{"version":3,"file":"cli-commands.d.ts","sourceRoot":"","sources":["../src/cli-commands.ts"],"names":[],"mappings":"AA0BA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AAE3C,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,2BAA2B,CAAC;AAI7D,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAC/C,OAAO,KAAK,EAA2B,QAAQ,EAAwB,MAAM,YAAY,CAAC;AAE1F;;;;;GAKG;AACH,MAAM,WAAW,mBAAmB;IACnC,0DAA0D;IAC1D,aAAa,EAAE,MAAM,CAAC;IACtB,8DAA8D;IAC9D,GAAG,EAAE,MAAM,CAAC;IACZ,iEAAiE;IACjE,KAAK,CAAC,UAAU,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,CAAC;IACpF,qCAAqC;IACrC,MAAM,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;IAC9B,+EAA+E;IAC/E,OAAO,CAAC,KAAK,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;CAC1D;AAED,sFAAsF;AACtF,MAAM,WAAW,uBAAuB;IACvC,KAAK,EAAE,QAAQ,CAAC;IAChB,OAAO,EAAE,UAAU,CAAC;IACpB,MAAM,EAAE,WAAW,CAAC;IACpB,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,KAAK,CAAC,EAAE,MAAM,CAAC;CACf;AAED,wBAAgB,eAAe,CAAC,OAAO,EAAE,uBAAuB,EAAE,MAAM,CAAC,EAAE,WAAW;;;;;;;EASrF;AAED,wBAAgB,UAAU,CAAC,KAAK,EAAE,MAAM,GAAG;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,CAKzE;AAED,wBAAgB,YAAY,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,MAAM,CAGjE;AAED,wBAAsB,yBAAyB,CAAC,OAAO,EAAE;IAAE,OAAO,EAAE,UAAU,CAAA;CAAE,GAAG,OAAO,CAAC,MAAM,CAAC,CAIjG;AAoCD,wBAAsB,kBAAkB,CAAC,KAAK,EAAE,QAAQ,EAAE,QAAQ,EAAE,QAAQ,GAAG,OAAO,CAAC,MAAM,CAAC,CAiE7F;AA4CD,MAAM,WAAW,8BAA8B;IAC9C,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;IAChB,WAAW,EAAE,MAAM,CAAC;IACpB,sFAAsF;IACtF,KAAK,EAAE,WAAW,GAAG,QAAQ,GAAG,SAAS,CAAC;IAC1C,iBAAiB,CAAC,EAAE,MAAM,CAAC;CAC3B;AAED,+EAA+E;AAC/E,wBAAsB,gCAAgC,CACrD,OAAO,EAAE,uBAAuB,GAC9B,OAAO,CAAC,8BAA8B,EAAE,CAAC,CA2B3C;AAID,kEAAkE;AAClE,wBAAgB,0BAA0B,CAAC,SAAS,EAAE,SAAS,8BAA8B,EAAE,GAAG,MAAM,CAUvG;AAED,8EAA8E;AAC9E,wBAAsB,8BAA8B,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAMlF;AAqZD;;;GAGG;AACH,wBAAsB,mBAAmB,CACxC,OAAO,EAAE,uBAAuB,EAChC,OAAO,EAAE,MAAM,EACf,IAAI,EAAE,MAAM,EACZ,SAAS,EAAE,mBAAmB,GAC5B,OAAO,CAAC,OAAO,CAAC,CAKlB","sourcesContent":["import { mkdtemp, readdir, readFile } from \"node:fs/promises\";\nimport { tmpdir } from \"node:os\";\nimport { join, resolve } from \"node:path\";\nimport { loadCompiledBundle } from \"./bundle/compile.ts\";\nimport { renderTrialComparisonMarkdown, type TrialComparison } from \"./comparison.ts\";\nimport { EvoCapabilityBroker } from \"./components/capabilities/broker.ts\";\nimport {\n\tpauseBackgroundEvolution,\n\tremoveBackgroundEvolution,\n\tresumeBackgroundEvolution,\n\tstartBackgroundEvolution,\n} from \"./evolve/background.ts\";\nimport {\n\treadEvoControlConfig,\n\treadEvolutionWorkflow,\n\tresetEvolutionWorkflow,\n\tupdateEvoControlConfigValue,\n} from \"./evolve/config.ts\";\nimport { runEvolutionCycle } from \"./evolve/cycle.ts\";\nimport { getSessionTriageStatus, runSessionTriage } from \"./evolve/triage.ts\";\nimport { readInboxEntry, readInboxLifecycleStates } from \"./inbox.ts\";\nimport { readBundlePreferenceMemory } from \"./memory/preferences.ts\";\nimport { DEEP_RESEARCH_TRIGGER, writeDeepResearchPack } from \"./pack/templates/deep-research.ts\";\nimport { DEEP_REVIEW_TRIGGER, writeDeepReviewPack } from \"./pack/templates/deep-review.ts\";\nimport { DEEPCODE_TRIGGER, writeDeepcodePack } from \"./pack/templates/deepcode.ts\";\nimport type { WrittenWorkflowPack } from \"./pack/templates/write-pack.ts\";\nimport type { EvoPaths } from \"./paths.ts\";\nimport { readEvaluationArtifact } from \"./proposal-artifacts.ts\";\nimport type { ModelRunner } from \"./reflect/model-runner.ts\";\nimport { runRetrospective } from \"./reflect/retrospective.ts\";\nimport { renderModelUsageSummary, summarizeModelUsage } from \"./reflect/usage.ts\";\nimport { runConfiguredImprove, type ScheduledImproveResult } from \"./scheduler.ts\";\nimport type { EvoService } from \"./service.ts\";\nimport type { EvoStatus, HistoryEntry, Proposal, ProposalArtifactKind } from \"./types.ts\";\n\n/**\n * Presentation seam between the two command surfaces: the in-session /evo\n * extension (cards + notifications + UI confirms) and the standalone evo-pi\n * CLI (stdout + readline confirms). Shared handlers below are written once\n * against this interface; each dispatcher supplies its own implementation.\n */\nexport interface EvoCommandPresenter {\n\t/** \"/evo\" or \"evo-pi\"; only used inside usage strings. */\n\tcommandPrefix: string;\n\t/** Resolved working directory for spawned background work. */\n\tcwd: string;\n\t/** Full-content result (extension: custom card; CLI: stdout). */\n\tprint(customType: string, content: string, details?: Record<string, unknown>): void;\n\t/** Short one-line status message. */\n\tnotify(message: string): void;\n\t/** Yes/no confirmation gating a state change; rejects when non-interactive. */\n\tconfirm(title: string, message: string): Promise<boolean>;\n}\n\n/** The dependency slice shared handlers need; EvoCommandDependencies satisfies it. */\nexport interface EvoSharedCommandContext {\n\tpaths: EvoPaths;\n\tservice: EvoService;\n\trunner: ModelRunner;\n\tcwd?: string;\n\tagentDir?: string;\n\tmodel?: string;\n}\n\nexport function modelRunOptions(context: EvoSharedCommandContext, signal?: AbortSignal) {\n\treturn {\n\t\tpaths: context.paths,\n\t\trunner: context.runner,\n\t\t...(context.cwd ? { cwd: context.cwd } : {}),\n\t\t...(context.agentDir ? { agentDir: context.agentDir } : {}),\n\t\t...(context.model ? { model: context.model } : {}),\n\t\t...(signal ? { signal } : {}),\n\t};\n}\n\nexport function splitFirst(value: string): { first: string; rest: string } {\n\tconst trimmed = value.trim();\n\tconst separator = trimmed.search(/\\s/);\n\tif (separator === -1) return { first: trimmed, rest: \"\" };\n\treturn { first: trimmed.slice(0, separator), rest: trimmed.slice(separator).trim() };\n}\n\nexport function requireValue(value: string, usage: string): string {\n\tif (!value.trim()) throw new Error(`Usage: ${usage}`);\n\treturn value.trim();\n}\n\nexport async function requireActiveBundleDigest(context: { service: EvoService }): Promise<string> {\n\tconst digest = await context.service.registry.readStableDigest();\n\tif (!digest) throw new Error(\"Evo-Pi is not initialized; run evo-pi init first\");\n\treturn digest;\n}\n\nfunction formatStatus(status: EvoStatus): string {\n\treturn [\n\t\t`initialized: ${String(status.initialized)}`,\n\t\t`stable: ${status.stableDigest ?? \"none\"}`,\n\t\t`trial: ${status.trial ? `${status.trial.proposalId} (${status.trial.digest})` : \"none\"}`,\n\t\t`pending proposals: ${status.pendingProposals}`,\n\t\t`deferred proposals: ${status.deferredProposals}`,\n\t\t`paused: ${String(status.paused)}`,\n\t].join(\"\\n\");\n}\n\nfunction formatProposalSummary(proposal: Proposal): string {\n\treturn `${proposal.id}  r${proposal.revision}  ${proposal.status}  ${proposal.tier}/${proposal.kind}  ${proposal.motivation}`;\n}\n\nasync function readProposalArtifact(\n\tpaths: EvoPaths,\n\tproposal: Proposal,\n\tkind: ProposalArtifactKind,\n): Promise<string | undefined> {\n\tconst reference = proposal.artifacts[kind];\n\tif (!reference) return undefined;\n\treturn (\n\t\tawait readEvaluationArtifact({\n\t\t\tpaths,\n\t\t\tproposalId: proposal.id,\n\t\t\trevision: proposal.revision,\n\t\t\tdiffDigest: proposal.diffDigest,\n\t\t\tkind,\n\t\t\treference,\n\t\t})\n\t).trim();\n}\n\nexport async function formatProposalCard(paths: EvoPaths, proposal: Proposal): Promise<string> {\n\tconst evidence = proposal.evidence.length\n\t\t? proposal.evidence\n\t\t\t\t.map(\n\t\t\t\t\t(reference) =>\n\t\t\t\t\t\t`- ${reference.sessionId}:${reference.sequence}${reference.quote ? ` — ${reference.quote}` : \"\"}`,\n\t\t\t\t)\n\t\t\t\t.join(\"\\n\")\n\t\t: \"- none\";\n\tconst review = await readProposalArtifact(paths, proposal, \"review\");\n\tconst replay = await readProposalArtifact(paths, proposal, \"replay\");\n\tconst validation = await readProposalArtifact(paths, proposal, \"validation\");\n\tconst comparisonJson = await readProposalArtifact(paths, proposal, \"comparison\");\n\tconst comparison = comparisonJson\n\t\t? renderTrialComparisonMarkdown(JSON.parse(comparisonJson) as TrialComparison)\n\t\t: undefined;\n\tconst retrospective = await readProposalArtifact(paths, proposal, \"retrospective\");\n\treturn [\n\t\t`# Evo proposal ${proposal.id}`,\n\t\t\"\",\n\t\t`Status: ${proposal.status}`,\n\t\t`Revision: ${proposal.revision}`,\n\t\t`Tier: ${proposal.tier}`,\n\t\t`Kind: ${proposal.kind}`,\n\t\t`Parent bundle: ${proposal.parentBundleDigest}`,\n\t\t`Final diff digest: ${proposal.diffDigest}`,\n\t\t...(proposal.codeWorkspace\n\t\t\t? [\n\t\t\t\t\t`Approval context digest: ${proposal.approvalDigest}`,\n\t\t\t\t\t`Repository root: ${proposal.codeWorkspace.repositoryRoot}`,\n\t\t\t\t\t`Repository identity: ${proposal.codeWorkspace.repositoryId}`,\n\t\t\t\t\t`Base commit: ${proposal.codeWorkspace.baseCommit}`,\n\t\t\t\t\t`Worktree: ${proposal.codeWorkspace.worktreePath}`,\n\t\t\t\t\t`Branch: ${proposal.codeWorkspace.branch}`,\n\t\t\t\t]\n\t\t\t: []),\n\t\t\"\",\n\t\t\"## Motivation\",\n\t\tproposal.motivation,\n\t\t\"\",\n\t\t\"## Evidence\",\n\t\tevidence,\n\t\t\"\",\n\t\t\"## Diff\",\n\t\t\"```diff\",\n\t\tproposal.diff,\n\t\t\"```\",\n\t\t\"\",\n\t\t\"## Expected effect\",\n\t\tproposal.expectedEffect,\n\t\t\"\",\n\t\t\"## Risk\",\n\t\tproposal.risk,\n\t\t\"\",\n\t\t\"## Verification plan\",\n\t\tproposal.verifyPlan,\n\t\t\"\",\n\t\t\"## Trial plan\",\n\t\tproposal.trialPlan,\n\t\t...(validation ? [\"\", \"## L1 validation\", validation] : []),\n\t\t...(review ? [\"\", \"## Critic review\", review] : []),\n\t\t...(replay ? [\"\", \"## Counterfactual replay\", replay] : []),\n\t\t...(comparison ? [\"\", comparison] : []),\n\t\t...(retrospective ? [\"\", \"## Retrospective\", retrospective] : []),\n\t].join(\"\\n\");\n}\n\nfunction proposalCardDetails(proposal: Proposal): Record<string, unknown> {\n\treturn {\n\t\tproposalId: proposal.id,\n\t\tstatus: proposal.status,\n\t\ttier: proposal.tier,\n\t\tapprovalDigest: proposal.approvalDigest,\n\t};\n}\n\nasync function formatActivePreferences(context: EvoSharedCommandContext): Promise<string> {\n\tconst digest = await context.service.registry.readStableDigest();\n\tif (!digest) return \"No active Evo-Pi preferences\";\n\tconst memory = await readBundlePreferenceMemory(await loadCompiledBundle(context.paths, digest));\n\tif (memory.preferences.length === 0) return \"No active Evo-Pi preferences\";\n\treturn [\n\t\t\"# Active Evo-Pi preferences\",\n\t\t\"\",\n\t\t...memory.preferences.map((preference) => `- ${preference.id}: ${preference.instruction}`),\n\t].join(\"\\n\");\n}\n\nconst WORKFLOW_PACK_TEMPLATES: Record<\n\tstring,\n\t{ trigger: string; description: string; write(directory: string): Promise<WrittenWorkflowPack> }\n> = {\n\t\"deep-review\": {\n\t\ttrigger: DEEP_REVIEW_TRIGGER,\n\t\tdescription: \"Voting-style code review: per-file reviewers plus adversarial verification\",\n\t\twrite: writeDeepReviewPack,\n\t},\n\t\"deep-research\": {\n\t\ttrigger: DEEP_RESEARCH_TRIGGER,\n\t\tdescription: \"Multi-source research: parallel searchers, adversarial claim verification, cited report\",\n\t\twrite: writeDeepResearchPack,\n\t},\n\tdeepcode: {\n\t\ttrigger: DEEPCODE_TRIGGER,\n\t\tdescription: \"Multi-agent coding: parallel explorers, frozen step plan, serial implementation, verify-fix loop\",\n\t\twrite: writeDeepcodePack,\n\t},\n};\n\nexport interface EvoInstallableWorkflowTemplate {\n\tname: string;\n\ttrigger: string;\n\tdescription: string;\n\t/** active: already in the stable bundle; pending: staged proposal awaits approval. */\n\tstate: \"available\" | \"active\" | \"pending\";\n\tpendingProposalId?: string;\n}\n\n/** The bundled workflow templates annotated with their local install state. */\nexport async function listInstallableWorkflowTemplates(\n\tcontext: EvoSharedCommandContext,\n): Promise<EvoInstallableWorkflowTemplate[]> {\n\tconst digest = await context.service.registry.readStableDigest();\n\tconst activeTriggers = new Set<string>();\n\tif (digest) {\n\t\tconst bundle = await loadCompiledBundle(context.paths, digest);\n\t\tfor (const selection of bundle.policy.workflows ?? []) activeTriggers.add(selection.trigger);\n\t}\n\tconst proposals = await context.service.listProposals();\n\treturn Object.entries(WORKFLOW_PACK_TEMPLATES).map(([name, template]) => {\n\t\tconst pending = proposals.find(\n\t\t\t(proposal) =>\n\t\t\t\tproposal.status === \"pending\" &&\n\t\t\t\tproposal.targetAbi === \"workflow/v1\" &&\n\t\t\t\tproposal.motivation.includes(`optimization pack \"${name}\"`),\n\t\t);\n\t\treturn {\n\t\t\tname,\n\t\t\ttrigger: template.trigger,\n\t\t\tdescription: template.description,\n\t\t\tstate: activeTriggers.has(template.trigger)\n\t\t\t\t? (\"active\" as const)\n\t\t\t\t: pending\n\t\t\t\t\t? (\"pending\" as const)\n\t\t\t\t\t: (\"available\" as const),\n\t\t\t...(pending ? { pendingProposalId: pending.id } : {}),\n\t\t};\n\t});\n}\n\nconst TEMPLATE_STATE_LABELS = { available: \"可安装\", active: \"已激活\", pending: \"待批准\" } as const;\n\n/** Numbered listing used by the parameterless `import` wizard. */\nexport function formatInstallableTemplates(templates: readonly EvoInstallableWorkflowTemplate[]): string {\n\treturn [\n\t\t\"可安装的 workflow 模板:\",\n\t\t...templates.map(\n\t\t\t(template, index) =>\n\t\t\t\t`${index + 1}. ${template.name} (${template.trigger}) [${TEMPLATE_STATE_LABELS[template.state]}] — ${template.description}`,\n\t\t),\n\t\t\"\",\n\t\t\"选择一个模板可自动完成 生成 → 导入 → 批准;外部优化包用 'import <directory>'。\",\n\t].join(\"\\n\");\n}\n\n/** Write a bundled template into a throwaway staging directory for import. */\nexport async function writeWorkflowTemplateToStaging(name: string): Promise<string> {\n\tconst template = WORKFLOW_PACK_TEMPLATES[name];\n\tif (!template) throw new Error(`Unknown pack template: ${name}`);\n\tconst directory = await mkdtemp(join(tmpdir(), `evo-pack-${name}-`));\n\tawait template.write(directory);\n\treturn directory;\n}\n\nfunction formatWorkflowPackTemplates(): string {\n\treturn [\n\t\t\"Bundled workflow pack templates:\",\n\t\t...Object.entries(WORKFLOW_PACK_TEMPLATES).map(\n\t\t\t([name, template]) => `- ${name} (${template.trigger}): ${template.description}`,\n\t\t),\n\t\t\"\",\n\t\t\"Write one with 'packs init <template> [directory]', then import the directory to stage it.\",\n\t].join(\"\\n\");\n}\n\nasync function initWorkflowPackTemplate(name: string, directory: string | undefined): Promise<string> {\n\tconst template = WORKFLOW_PACK_TEMPLATES[name];\n\tif (!template) {\n\t\tthrow new Error(\n\t\t\t`Unknown pack template: ${name || \"(none)\"}. Available: ${Object.keys(WORKFLOW_PACK_TEMPLATES).join(\", \")}`,\n\t\t);\n\t}\n\tconst target = resolve(directory ?? `${name}-pack`);\n\tconst written = await template.write(target);\n\treturn [\n\t\t`Wrote ${written.manifest.name}@${written.manifest.version} (${template.trigger}) to ${target}`,\n\t\t`Integrity: ${written.integrity}`,\n\t\t`Next: run 'import ${target}' to stage the workflow proposal.`,\n\t].join(\"\\n\");\n}\n\nasync function formatActiveWorkflows(context: EvoSharedCommandContext): Promise<string> {\n\tconst digest = await requireActiveBundleDigest(context);\n\tconst bundle = await loadCompiledBundle(context.paths, digest);\n\tconst workflows = bundle.policy.workflows ?? [];\n\tif (workflows.length === 0) {\n\t\treturn \"No workflow components are active in the current bundle. Use 'packs' to see bundled templates.\";\n\t}\n\tconst broker = await new EvoCapabilityBroker({ paths: context.paths }).getState();\n\tconst lines = workflows.map((selection) => {\n\t\tconst spawn = (selection.grants ?? []).find((grant) => grant.capability === \"spawn-agent\");\n\t\tconst grantLabel =\n\t\t\tspawn && \"models\" in spawn\n\t\t\t\t? `spawn-agent ≤${spawn.maxCalls} calls · ${spawn.models.join(\",\")}`\n\t\t\t\t: \"no spawn-agent grant\";\n\t\tconst usage = broker.components\n\t\t\t.find((component) => component.id === selection.id && component.artifactDigest === selection.artifactDigest)\n\t\t\t?.usage.find((entry) => entry.capability === \"spawn-agent\");\n\t\tconst usageLabel = usage ? ` · used ${usage.calls} call(s)` : \"\";\n\t\treturn `- ${selection.trigger} → ${selection.id} (${selection.artifactDigest.slice(0, 12)}) · ${grantLabel}${usageLabel}`;\n\t});\n\treturn [`Active workflows in bundle ${digest.slice(0, 12)}:`, ...lines].join(\"\\n\");\n}\n\nasync function formatCapabilityGrants(paths: EvoPaths): Promise<string> {\n\tconst state = await new EvoCapabilityBroker({ paths }).getState();\n\tif (state.components.length === 0) {\n\t\treturn \"No components hold capability grants. Grants are persisted when an imported pack's proposal is staged.\";\n\t}\n\tconst lines = [`Capability grants (${state.components.length} component(s)):`];\n\tfor (const component of state.components) {\n\t\tlines.push(`- ${component.id} (${component.artifactDigest.slice(0, 12)})`);\n\t\tfor (const grant of component.grants) {\n\t\t\tconst usage = component.usage.find((entry) => entry.capability === grant.capability);\n\t\t\tconst calls = usage?.calls ?? 0;\n\t\t\tif (\"models\" in grant) {\n\t\t\t\tconst cost = usage?.costUsd ?? 0;\n\t\t\t\tconst tokens = usage?.totalTokens ?? 0;\n\t\t\t\tconst tools = grant.tools ? ` · ${grant.tools.length} tool(s)` : \"\";\n\t\t\t\tlines.push(\n\t\t\t\t\t`    ${grant.capability}: ${calls}/${grant.maxCalls} calls · $${cost.toFixed(4)}/$${grant.maxCostUsd} · ${tokens}/${grant.maxTotalTokens} total tokens · models ${grant.models.join(\",\")}${tools}`,\n\t\t\t\t);\n\t\t\t} else {\n\t\t\t\tlines.push(`    ${grant.capability}: ${calls}/${grant.maxCalls} calls`);\n\t\t\t}\n\t\t}\n\t}\n\tif (state.operations.length > 0 || state.reservations.length > 0) {\n\t\tlines.push(`In-flight: ${state.operations.length} operation(s), ${state.reservations.length} reservation(s)`);\n\t}\n\treturn lines.join(\"\\n\");\n}\n\nconst DEFAULT_HISTORY_LIMIT = 20;\n\nfunction shortDigest(digest: string | undefined): string {\n\treturn digest ? digest.slice(0, 12) : \"none\";\n}\n\nasync function formatBundleHistory(paths: EvoPaths, rest: string, prefix: string): Promise<string> {\n\tconst trimmed = rest.trim();\n\tconst match = /^([1-9][0-9]*)$/.exec(trimmed);\n\tif (trimmed && !match) throw new Error(`Usage: ${prefix} history [<count>]`);\n\tconst limit = match ? Number(match[1]) : DEFAULT_HISTORY_LIMIT;\n\tlet content: string;\n\ttry {\n\t\tcontent = await readFile(paths.history, \"utf8\");\n\t} catch (error) {\n\t\tif (typeof error === \"object\" && error !== null && \"code\" in error && error.code === \"ENOENT\") {\n\t\t\treturn \"No bundle transitions recorded yet.\";\n\t\t}\n\t\tthrow error;\n\t}\n\tconst entries: HistoryEntry[] = [];\n\tfor (const line of content.split(\"\\n\")) {\n\t\tif (!line.trim()) continue;\n\t\ttry {\n\t\t\tconst entry = JSON.parse(line) as HistoryEntry;\n\t\t\tif (typeof entry.timestamp === \"string\" && typeof entry.action === \"string\") entries.push(entry);\n\t\t} catch {\n\t\t\t// A torn final line from a crashed writer is repaired by the registry\n\t\t\t// on its next transaction; the read-only view just skips it.\n\t\t}\n\t}\n\tif (entries.length === 0) return \"No bundle transitions recorded yet.\";\n\tconst shown = entries.slice(-limit);\n\tconst lines = shown.map((entry) => {\n\t\tconst digests =\n\t\t\tentry.fromDigest || entry.toDigest ? ` ${shortDigest(entry.fromDigest)} → ${shortDigest(entry.toDigest)}` : \"\";\n\t\tconst proposal = entry.proposalId ? ` · ${entry.proposalId}` : \"\";\n\t\treturn `- ${entry.timestamp} ${entry.action} (${entry.actor})${digests}${proposal}\\n    ${entry.reason}`;\n\t});\n\treturn [`Bundle history (showing ${shown.length} of ${entries.length} transition(s), oldest first):`, ...lines].join(\n\t\t\"\\n\",\n\t);\n}\n\nasync function formatInboxListing(paths: EvoPaths): Promise<string> {\n\tconst states = await readInboxLifecycleStates(paths);\n\tconst files = (await readdir(paths.inbox)).filter((file) => file.endsWith(\".json\")).sort();\n\tif (files.length === 0) return \"Inbox is empty.\";\n\tconst lines: string[] = [];\n\tfor (const file of files) {\n\t\ttry {\n\t\t\tconst entry = await readInboxEntry(paths, file);\n\t\t\tconst state = states.get(file);\n\t\t\tconst preview = entry.text.replaceAll(\"\\n\", \" \").slice(0, 96);\n\t\t\tlines.push(`- [${state?.status ?? \"open\"}/${state?.kind ?? entry.kind ?? \"unclassified\"}] ${file}`);\n\t\t\tlines.push(`    ${preview}`);\n\t\t} catch {\n\t\t\tlines.push(`- [unreadable] ${file}`);\n\t\t}\n\t}\n\treturn [`Inbox entries (${files.length}):`, ...lines].join(\"\\n\");\n}\n\nasync function formatUsageSummary(paths: EvoPaths, rest: string): Promise<string> {\n\tconst trimmed = rest.trim();\n\tconst match = /^([1-9][0-9]*)d$/.exec(trimmed);\n\tif (trimmed && !match) throw new Error(\"Usage: usage [<n>d]\");\n\tconst days = match ? Number(match[1]) : 7;\n\tconst summary = await summarizeModelUsage(paths, {\n\t\tsince: new Date(Date.now() - days * 24 * 60 * 60 * 1_000),\n\t});\n\treturn [`Model usage over the last ${days}d:`, \"\", renderModelUsageSummary(summary)].join(\"\\n\");\n}\n\nasync function formatControlConfig(paths: EvoPaths): Promise<string> {\n\tconst config = await readEvoControlConfig(paths);\n\treturn [`Config file: ${paths.config}`, \"\", JSON.stringify(config, undefined, 2)].join(\"\\n\");\n}\n\nfunction describeImproveSkip(result: Extract<ScheduledImproveResult<unknown>, { status: \"skipped\" }>): string {\n\tconst detail =\n\t\tresult.reason === \"interval-not-elapsed\" && result.nextEligibleDay\n\t\t\t? ` (next eligible ${result.nextEligibleDay})`\n\t\t\t: \"\";\n\treturn `Scheduled improve skipped: ${result.reason}${detail}`;\n}\n\n/** One guarded, cadence-gated evolution attempt (the old `scheduled-improve`). */\nasync function runScheduledImprove(context: EvoSharedCommandContext, presenter: EvoCommandPresenter): Promise<void> {\n\tconst scheduled = await runConfiguredImprove({\n\t\tpaths: context.paths,\n\t\timprove: (signal) =>\n\t\t\trunEvolutionCycle({\n\t\t\t\tpaths: context.paths,\n\t\t\t\tservice: context.service,\n\t\t\t\trunner: context.runner,\n\t\t\t\tcwd: context.cwd,\n\t\t\t\tagentDir: context.agentDir,\n\t\t\t\ttrigger: \"scheduled\",\n\t\t\t\tsignal,\n\t\t\t}),\n\t});\n\tif (scheduled.status === \"skipped\") {\n\t\tpresenter.notify(describeImproveSkip(scheduled));\n\t\treturn;\n\t}\n\tpresenter.notify(`Evolution run ${scheduled.value.run.id} completed`);\n\tfor (const proposal of scheduled.value.proposals) {\n\t\tpresenter.print(\"evo.proposal\", await formatProposalCard(context.paths, proposal), proposalCardDetails(proposal));\n\t}\n}\n\nasync function runTriageCommand(\n\tcontext: EvoSharedCommandContext,\n\trest: string,\n\tpresenter: EvoCommandPresenter,\n): Promise<void> {\n\tif (rest && rest !== \"now\") throw new Error(`Usage: ${presenter.commandPrefix} triage [now]`);\n\tconst config = await readEvoControlConfig(context.paths);\n\tif (rest !== \"now\") {\n\t\tconst status = await getSessionTriageStatus(context.paths);\n\t\tpresenter.print(\n\t\t\t\"evo.triage\",\n\t\t\t[\n\t\t\t\t`Last triage run: ${status.lastRunAt ?? \"never\"}`,\n\t\t\t\t`New complete sessions since then: ${status.newSessions} (auto-triage runs at ${config.triage.everyNSessions})`,\n\t\t\t\t`Triage model: ${config.models.triage.model}`,\n\t\t\t\t`Run '${presenter.commandPrefix} triage now' to scan the new sessions immediately.`,\n\t\t\t].join(\"\\n\"),\n\t\t\t{},\n\t\t);\n\t\treturn;\n\t}\n\tconst outcome = await runSessionTriage({\n\t\tpaths: context.paths,\n\t\trunner: context.runner,\n\t\tmodel: config.models.triage.model,\n\t\t...(config.models.triage.thinkingLevel ? { thinkingLevel: config.models.triage.thinkingLevel } : {}),\n\t\teveryNSessions: 1,\n\t\tcwd: presenter.cwd,\n\t\t...(context.agentDir ? { agentDir: context.agentDir } : {}),\n\t});\n\tif (!outcome.ran) {\n\t\tpresenter.notify(\"Triage skipped: no new complete sessions since the last run\");\n\t\treturn;\n\t}\n\tif (outcome.hypotheses.length === 0) {\n\t\tpresenter.notify(`Triage scanned ${outcome.newSessions} new session(s); no hypotheses were filed`);\n\t\treturn;\n\t}\n\tpresenter.print(\n\t\t\"evo.triage\",\n\t\t[\n\t\t\t`Triage filed ${outcome.hypotheses.length} hypothesis(es) from ${outcome.newSessions} new session(s):`,\n\t\t\t...outcome.hypotheses.map((hypothesis) => `- ${hypothesis.direction}: ${hypothesis.summary}`),\n\t\t\t\"\",\n\t\t\t`Inbox files: ${outcome.inboxFiles.join(\", \")}`,\n\t\t\t\"The next research run consumes these as pre-triaged evidence.\",\n\t\t].join(\"\\n\"),\n\t\t{ hypotheses: outcome.hypotheses.length },\n\t);\n}\n\ntype SharedCommandHandler = (\n\tcontext: EvoSharedCommandContext,\n\trest: string,\n\tpresenter: EvoCommandPresenter,\n) => Promise<void>;\n\nasync function runPlaybookCommand(\n\tcontext: EvoSharedCommandContext,\n\trest: string,\n\tpresenter: EvoCommandPresenter,\n): Promise<void> {\n\tif (rest === \"reset\") await resetEvolutionWorkflow(context.paths);\n\telse if (rest) throw new Error(`Usage: ${presenter.commandPrefix} playbook [reset]`);\n\tconst playbook = await readEvolutionWorkflow(context.paths);\n\tpresenter.print(\"evo.playbook\", `Playbook: ${context.paths.workflow}\\n\\n${playbook}`, {\n\t\tpath: context.paths.workflow,\n\t});\n}\n\nconst SHARED_COMMAND_HANDLERS: Record<string, SharedCommandHandler> = {\n\tstatus: async (context, _rest, presenter) => {\n\t\tpresenter.notify(formatStatus(await context.service.status()));\n\t},\n\tgo: async (context, rest, presenter) => {\n\t\tif (rest === \"--scheduled\") {\n\t\t\tawait runScheduledImprove(context, presenter);\n\t\t\treturn;\n\t\t}\n\t\tif (rest.startsWith(\"--\")) throw new Error(`Usage: ${presenter.commandPrefix} go [request | --scheduled]`);\n\t\tconst run = await startBackgroundEvolution({\n\t\t\tpaths: context.paths,\n\t\t\tcwd: presenter.cwd,\n\t\t\t...(rest ? { request: rest } : {}),\n\t\t});\n\t\tpresenter.notify(`Evolution task ${run.id} started in the background`);\n\t},\n\t// Deprecated alias for `go --scheduled`.\n\t\"scheduled-improve\": async (context, _rest, presenter) => {\n\t\tawait runScheduledImprove(context, presenter);\n\t},\n\tpause: async (context, rest, presenter) => {\n\t\tconst id = requireValue(rest, `${presenter.commandPrefix} pause <run-id>`);\n\t\tawait pauseBackgroundEvolution(context.paths, id);\n\t\tpresenter.notify(`Evolution task ${id} paused`);\n\t},\n\tresume: async (context, rest, presenter) => {\n\t\tconst id = requireValue(rest, `${presenter.commandPrefix} resume <run-id>`);\n\t\tawait resumeBackgroundEvolution(context.paths, id);\n\t\tpresenter.notify(`Evolution task ${id} resumed`);\n\t},\n\tdelete: async (context, rest, presenter) => {\n\t\tconst id = requireValue(rest, `${presenter.commandPrefix} delete <run-id>`);\n\t\tif (!(await presenter.confirm(\"Delete evolution task\", `Permanently delete ${id}?`))) {\n\t\t\tpresenter.notify(\"Delete cancelled\");\n\t\t\treturn;\n\t\t}\n\t\tawait removeBackgroundEvolution(context.paths, id);\n\t\tpresenter.notify(`Evolution task ${id} deleted`);\n\t},\n\t// \"workflow\" is a deprecated alias: the evolution playbook is unrelated to\n\t// workflow components, which live under \"workflows\" and \"packs\".\n\tworkflow: runPlaybookCommand,\n\tplaybook: runPlaybookCommand,\n\tworkflows: async (context, _rest, presenter) => {\n\t\tpresenter.print(\"evo.workflows\", await formatActiveWorkflows(context), {});\n\t},\n\tpacks: async (_context, rest, presenter) => {\n\t\tconst parts = rest.split(/\\s+/).filter(Boolean);\n\t\tif (parts.length === 0) {\n\t\t\tpresenter.print(\"evo.packs\", formatWorkflowPackTemplates(), {});\n\t\t\treturn;\n\t\t}\n\t\tif (parts[0] !== \"init\") throw new Error(`Usage: ${presenter.commandPrefix} packs [init <template> [directory]]`);\n\t\tpresenter.print(\"evo.packs\", await initWorkflowPackTemplate(parts[1] ?? \"\", parts[2]), { template: parts[1] });\n\t},\n\tconfig: async (context, rest, presenter) => {\n\t\tconst parts = rest.split(/\\s+/).filter(Boolean);\n\t\tif (parts[0] === \"set\") {\n\t\t\tconst key = parts[1];\n\t\t\tconst value = parts.slice(2).join(\" \");\n\t\t\tif (!key || !value) throw new Error(`Usage: ${presenter.commandPrefix} config set <key> <value>`);\n\t\t\tawait updateEvoControlConfigValue(context.paths, key, value);\n\t\t\tpresenter.notify(`Config updated: ${key} = ${value}`);\n\t\t\treturn;\n\t\t}\n\t\tif (parts.length > 0) throw new Error(`Usage: ${presenter.commandPrefix} config [set <key> <value>]`);\n\t\tpresenter.print(\"evo.config\", await formatControlConfig(context.paths), {});\n\t},\n\tgrants: async (context, rest, presenter) => {\n\t\tif (rest) throw new Error(`Usage: ${presenter.commandPrefix} grants`);\n\t\tpresenter.print(\"evo.grants\", await formatCapabilityGrants(context.paths), {});\n\t},\n\thistory: async (context, rest, presenter) => {\n\t\tpresenter.print(\"evo.history\", await formatBundleHistory(context.paths, rest, presenter.commandPrefix), {});\n\t},\n\ttriage: runTriageCommand,\n\tinbox: async (context, _rest, presenter) => {\n\t\tpresenter.print(\"evo.inbox\", await formatInboxListing(context.paths), {});\n\t},\n\tusage: async (context, rest, presenter) => {\n\t\tpresenter.print(\"evo.usage\", await formatUsageSummary(context.paths, rest), {});\n\t},\n\tlist: async (context, _rest, presenter) => {\n\t\tconst proposals = await context.service.listProposals();\n\t\tif (proposals.length === 0) {\n\t\t\tpresenter.notify(\"No Evo-Pi proposals\");\n\t\t\treturn;\n\t\t}\n\t\tpresenter.print(\"evo.proposal-list\", proposals.map(formatProposalSummary).join(\"\\n\"), {\n\t\t\tproposalIds: proposals.map((proposal) => proposal.id),\n\t\t});\n\t},\n\tshow: async (context, rest, presenter) => {\n\t\tconst { first: id } = splitFirst(rest);\n\t\trequireValue(id, `${presenter.commandPrefix} show <proposal-id>`);\n\t\tconst proposal = await context.service.getProposal(id);\n\t\tpresenter.print(\"evo.proposal\", await formatProposalCard(context.paths, proposal), proposalCardDetails(proposal));\n\t},\n\tpreferences: async (context, rest, presenter) => {\n\t\tif (rest) throw new Error(`Usage: ${presenter.commandPrefix} preferences`);\n\t\tpresenter.print(\"evo.preferences\", await formatActivePreferences(context), {});\n\t},\n\tresolve: async (context, rest, presenter) => {\n\t\tconst { first: file, rest: reason } = splitFirst(rest);\n\t\trequireValue(file, `${presenter.commandPrefix} resolve <inbox-file> [reason]`);\n\t\tif (!(await presenter.confirm(\"Resolve Evo-Pi input\", `Mark ${file} fulfilled and collect it?`))) {\n\t\t\tpresenter.notify(\"Resolve cancelled\");\n\t\t\treturn;\n\t\t}\n\t\tawait context.service.resolveInbox(file, reason || \"User confirmed the input was fulfilled externally\");\n\t\tpresenter.notify(`Resolved ${file}`);\n\t},\n\tgc: async (context, rest, presenter) => {\n\t\tif (rest && rest !== \"--dry-run\") throw new Error(`Usage: ${presenter.commandPrefix} gc [--dry-run]`);\n\t\tconst dryRun = rest === \"--dry-run\";\n\t\tif (!dryRun && !(await presenter.confirm(\"Collect Evo-Pi inbox\", \"Delete terminal inbox payloads?\"))) {\n\t\t\tpresenter.notify(\"GC cancelled\");\n\t\t\treturn;\n\t\t}\n\t\tconst result = await context.service.gcInbox(dryRun);\n\t\tpresenter.notify(`${dryRun ? \"GC would collect\" : \"GC collected\"} ${result.files.length} inbox payloads`);\n\t},\n\treject: async (context, rest, presenter) => {\n\t\tconst { first: id, rest: reason } = splitFirst(rest);\n\t\trequireValue(id, `${presenter.commandPrefix} reject <proposal-id> <reason>`);\n\t\trequireValue(reason, `${presenter.commandPrefix} reject <proposal-id> <reason>`);\n\t\tif (!(await presenter.confirm(`Reject ${id}`, `Reject proposal ${id}?`))) {\n\t\t\tpresenter.notify(\"Rejection cancelled\");\n\t\t\treturn;\n\t\t}\n\t\tawait context.service.reject(id, reason);\n\t\tpresenter.notify(`Rejected ${id}`);\n\t},\n\tretrospect: async (context, _rest, presenter) => {\n\t\tconst retrospective = await runRetrospective(modelRunOptions(context));\n\t\tpresenter.print(\"evo.retrospective\", retrospective.retrospectiveMarkdown, {\n\t\t\tproposalId: retrospective.proposal.id,\n\t\t});\n\t},\n};\n\n/**\n * Run `command` when it is one of the surface-independent commands; returns\n * false when the command is surface-specific and the caller must dispatch it.\n */\nexport async function runSharedEvoCommand(\n\tcontext: EvoSharedCommandContext,\n\tcommand: string,\n\trest: string,\n\tpresenter: EvoCommandPresenter,\n): Promise<boolean> {\n\tconst handler = SHARED_COMMAND_HANDLERS[command];\n\tif (!handler) return false;\n\tawait handler(context, rest, presenter);\n\treturn true;\n}\n"]}