knodin 0.7.4 → 0.7.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (104) hide show
  1. package/README.md +4 -1
  2. package/dist/bin/cli.js +108 -1859
  3. package/dist/bin/launcher.js +3 -10
  4. package/dist/chunks/chunk-DMQAGX77.js +654 -0
  5. package/dist/chunks/chunk-F4Z3Z766.js +4 -0
  6. package/dist/chunks/chunk-SIJAQVSX.js +3 -0
  7. package/dist/chunks/chunk-X6M4HUUE.js +2 -0
  8. package/dist/chunks/chunk-YPRMY2LP.js +8 -0
  9. package/dist/chunks/pure-compression-cli-4TA2TQD5.js +5 -0
  10. package/dist/chunks/server-7EDF4CBY.js +14 -0
  11. package/dist/chunks/structural-fast-path-KD5KQSPX.js +4 -0
  12. package/dist/src/init-progress-worker.js +2 -106
  13. package/dist/src/repair-progress-worker.js +2 -120
  14. package/docs/INSTALLATION.md +17 -1
  15. package/docs/releases/0.7.6.md +25 -0
  16. package/package.json +1 -110
  17. package/dist/src/agent-integration.js +0 -238
  18. package/dist/src/artifact-refresh.js +0 -81
  19. package/dist/src/cli-args.js +0 -274
  20. package/dist/src/cli-model.js +0 -358
  21. package/dist/src/codeflow-replay.js +0 -80
  22. package/dist/src/compact-structural.js +0 -96
  23. package/dist/src/competitive-cold-mcp.js +0 -40
  24. package/dist/src/competitive-constraints.js +0 -20
  25. package/dist/src/competitive-manifest.js +0 -411
  26. package/dist/src/competitive-measurement.js +0 -183
  27. package/dist/src/competitive-runner.js +0 -487
  28. package/dist/src/competitive-sandbox.js +0 -108
  29. package/dist/src/context-export.js +0 -422
  30. package/dist/src/context.js +0 -102
  31. package/dist/src/diagnostics.js +0 -449
  32. package/dist/src/docs-sections.js +0 -141
  33. package/dist/src/doctor.js +0 -380
  34. package/dist/src/engine/ann-hnsw.js +0 -271
  35. package/dist/src/engine/embeddings.js +0 -193
  36. package/dist/src/engine/file-walker.js +0 -43
  37. package/dist/src/engine/git-history.js +0 -289
  38. package/dist/src/engine/index.js +0 -13377
  39. package/dist/src/engine/perf.js +0 -115
  40. package/dist/src/engine/prune.js +0 -112
  41. package/dist/src/engine/scip-import.js +0 -408
  42. package/dist/src/engine/source-policy.js +0 -69
  43. package/dist/src/engine/sqlite.js +0 -71
  44. package/dist/src/engine/symbol-delete.js +0 -58
  45. package/dist/src/execution-profile.js +0 -203
  46. package/dist/src/failure-diagnosis.js +0 -649
  47. package/dist/src/fleet.js +0 -7
  48. package/dist/src/git-executable.js +0 -31
  49. package/dist/src/graph-query-health.js +0 -115
  50. package/dist/src/hook-manager-integration.js +0 -156
  51. package/dist/src/index-activity.js +0 -125
  52. package/dist/src/init-progress.js +0 -155
  53. package/dist/src/init.js +0 -1254
  54. package/dist/src/lifecycle-health.js +0 -250
  55. package/dist/src/lsp-readonly.js +0 -217
  56. package/dist/src/output-compression.js +0 -629
  57. package/dist/src/output-telemetry.js +0 -363
  58. package/dist/src/pr-triage.js +0 -638
  59. package/dist/src/progressive-evidence.js +0 -473
  60. package/dist/src/pure-compression-cli.js +0 -101
  61. package/dist/src/relationship-adapters.js +0 -370
  62. package/dist/src/release-attestation.js +0 -533
  63. package/dist/src/release-preflight.js +0 -510
  64. package/dist/src/repair-progress.js +0 -262
  65. package/dist/src/repository-init-process.js +0 -173
  66. package/dist/src/repository-management.js +0 -1231
  67. package/dist/src/response-budget.js +0 -194
  68. package/dist/src/server.js +0 -73
  69. package/dist/src/structural-fast-path.js +0 -338
  70. package/dist/src/structural-snapshot.js +0 -33
  71. package/dist/src/system-config.js +0 -615
  72. package/dist/src/terminal-help.js +0 -83
  73. package/dist/src/tools/knodin-tools.js +0 -1529
  74. package/dist/src/update-ceremony.js +0 -158
  75. package/dist/src/update-policy.js +0 -944
  76. package/dist/src/update-trust.js +0 -503
  77. package/dist/src/version.js +0 -13
  78. package/dist/src/visualization.js +0 -162
  79. package/dist/src/wait-for-fresh.js +0 -98
  80. package/dist/src/worktree-lifecycle.js +0 -231
  81. package/docs/CONTAINED-EXECUTION.md +0 -77
  82. package/docs/DIAGNOSTICS.md +0 -45
  83. package/docs/GIT-HISTORY-REVIEW.md +0 -39
  84. package/docs/PROGRESSIVE-EVIDENCE.md +0 -37
  85. package/docs/PT-ACCESS-RECOMMENDATION.md +0 -91
  86. package/docs/RELEASE-0.3-EVIDENCE.md +0 -73
  87. package/docs/SCIP-IMPORT.md +0 -57
  88. package/docs/SIGNED-UPDATES.md +0 -151
  89. package/docs/TELEMETRY.md +0 -46
  90. package/docs/assets/knodin-favicon.svg +0 -4
  91. package/docs/releases/0.3.0.md +0 -46
  92. package/docs/releases/0.4.0.md +0 -68
  93. package/docs/releases/0.4.1.md +0 -28
  94. package/docs/releases/0.4.2.md +0 -27
  95. package/docs/releases/0.4.3.md +0 -23
  96. package/docs/releases/0.5.0.md +0 -29
  97. package/docs/releases/0.5.1.md +0 -17
  98. package/docs/releases/0.6.0.md +0 -18
  99. package/docs/releases/0.7.0.md +0 -24
  100. package/docs/releases/0.7.1.md +0 -21
  101. package/docs/releases/0.7.2.md +0 -21
  102. package/docs/releases/0.7.3.md +0 -23
  103. package/docs/releases/0.7.4.md +0 -17
  104. package/schemas/release-attestation-v1.schema.json +0 -210
package/dist/bin/cli.js CHANGED
@@ -1,1860 +1,109 @@
1
1
  #!/usr/bin/env node
2
- /**
3
- * `knodin` CLI the daily driver.
4
- *
5
- * knodin explain <symbol> [minimal] edit-ready source + direct call paths + blast radius
6
- * knodin review [base] risk-scored context with explicit git diff scopes
7
- * knodin map subsystems + confidence-tagged edges
8
- * knodin wiki [--force] write .knodin/wiki/ (index.md + per-community pages)
9
- * knodin serve run the MCP server on stdio
10
- *
11
- * The same engine backs both the CLI and the MCP server, so any assistant and
12
- * a human at a terminal see identical results.
13
- */
14
- import { spawn, spawnSync } from "node:child_process";
15
- import fs from "node:fs";
16
- import path from "node:path";
17
- import readline from "node:readline/promises";
18
- import { fileURLToPath } from "node:url";
19
- import { detectSupportedAgents, parseInitScope, } from "../src/agent-integration.js";
20
- import { refreshExternalGraphArtifacts, writeArtifactRefreshRecord, } from "../src/artifact-refresh.js";
21
- import { checkIndexed, extractPositionals, extractRepoFlag, parseReviewArgs, planIndex, resolveCliRuntimeCommand, resolveRepo, } from "../src/cli-args.js";
22
- import { helpCommandPath, parseCliInvocation, renderCliHelp } from "../src/cli-model.js";
23
- import { buildKnodinContext } from "../src/context.js";
24
- import { exportContext, grepPackedArtifact, readPackedArtifact } from "../src/context-export.js";
25
- import { clearDiagnostics, collectDiagnostics, diagnosticsStatus, disableDiagnostics, enableDiagnostics, inspectDiagnosticsBundle, recordDiagnosticFailure, } from "../src/diagnostics.js";
26
- import { getDocSection, listDocTopics } from "../src/docs-sections.js";
27
- import { diagnoseInstallation } from "../src/doctor.js";
28
- import { createEngine, REPO_WIDE_QUERY_PATTERNS, } from "../src/engine/index.js";
29
- import { diagnoseFailure, } from "../src/failure-diagnosis.js";
30
- import { gitExecutable } from "../src/git-executable.js";
31
- import { decorateGraphQueryResult, inspectGraphQueryHealth } from "../src/graph-query-health.js";
32
- import { createIndexActivityReporter } from "../src/index-activity.js";
33
- import { detectTrackedTeamIntegration, InitializationHealthError, initializeRepository, inspectRepositoryIntegrationStatus, readRepositoryIntegrationConfig, refreshFromGitEvent, } from "../src/init.js";
34
- import { createInitProgressRenderer } from "../src/init-progress.js";
35
- import { attachLifecycleHealth, attachRepairLifecycle } from "../src/lifecycle-health.js";
36
- import { compressOutput, compressOutputFile, deleteOutputArtifact, readOutputArtifact, } from "../src/output-compression.js";
37
- import { clearTelemetry, exportTelemetry, readTelemetryRecords, telemetryStatus, writeTelemetryReport, } from "../src/output-telemetry.js";
38
- import { auditPullRequests } from "../src/pr-triage.js";
39
- import { deliverProgressiveEvidence, } from "../src/progressive-evidence.js";
40
- import { createRepairPlan, createRepairProgressRenderer, parseRepairCliArgs, resolveRepairProgressMode, serializeRepairJsonlRecord, } from "../src/repair-progress.js";
41
- import { runRepositoryInitializationProcess } from "../src/repository-init-process.js";
42
- import { detectRepositorySignals, discoverRepositories, formatRepositoryHuman, initializeRepositories, inventoryRepository, parseFleetInitArgs, parseRepositoryCommandArgs, repositorySignalInspectionLimit, searchRepositories, withRepositorySignals, } from "../src/repository-management.js";
43
- import { applyResponseBudget } from "../src/response-budget.js";
44
- import { enrichSystemRelationships, incorporateSystemQueryEvidence, indexModeForPath, loadSystemConfiguration, queryConfiguredSystem, systemMembershipsForPath, validateSystemHealth, } from "../src/system-config.js";
45
- import { applyTrustedUpdate, checkTrustedUpdate, claimScheduledUpdateCheck, detectUpdateInstallMethod, explainTrustedUpdate, releaseScheduledUpdateCheck, rollbackTrustedUpdate, trustedUpdateStatus, } from "../src/update-policy.js";
46
- import { KNODIN_VERSION } from "../src/version.js";
47
- import { writeVisualization } from "../src/visualization.js";
48
- import { waitForFresh } from "../src/wait-for-fresh.js";
49
- import { inspectWorktrees, reconcileWorktrees, removeManagedWorktree, } from "../src/worktree-lifecycle.js";
50
- function explicitScope(args) {
51
- const index = args.indexOf("--scope");
52
- if (index >= 0) {
53
- const value = args[index + 1];
54
- if (!value)
55
- throw new Error("knodin: --scope requires a value");
56
- return parseInitScope(value);
57
- }
58
- const equals = args.find((argument) => argument.startsWith("--scope="));
59
- return equals ? parseInitScope(equals.slice("--scope=".length)) : null;
60
- }
61
- async function chooseInitScope(args, repo) {
62
- const selected = explicitScope(args);
63
- if (selected)
64
- return selected;
65
- if (detectTrackedTeamIntegration(repo)) {
66
- process.stderr.write("[init:scope] Detected tracked knodin team integration; preserving team scope\n");
67
- return "team";
68
- }
69
- if (!process.stdin.isTTY || !process.stderr.isTTY)
70
- return "personal";
71
- const prompt = readline.createInterface({
72
- input: process.stdin,
73
- output: process.stderr,
74
- });
75
- try {
76
- const answer = await prompt.question([
77
- "How should knodin integrate with coding agents?",
78
- " 1. Personal (recommended) all detected agents; Git stays clean",
79
- " 2. Team create commit-ready shared configuration",
80
- " 3. CLI-only agents will not discover or invoke knodin automatically",
81
- "Select [1]: ",
82
- ].join("\n"));
83
- if (!answer.trim() || answer.trim() === "1")
84
- return "personal";
85
- if (answer.trim() === "2")
86
- return "team";
87
- if (answer.trim() === "3") {
88
- const confirmation = await prompt.question("CLI-only requires manual knodin commands. Continue? [y/N] ");
89
- if (!/^y(?:es)?$/i.test(confirmation.trim()))
90
- throw new Error("knodin init: CLI-only selection cancelled");
91
- return "cli-only";
92
- }
93
- return parseInitScope(answer.trim());
94
- }
95
- finally {
96
- prompt.close();
97
- }
98
- }
99
- function integrationAgents(repo) {
100
- const previous = readRepositoryIntegrationConfig(repo)?.agents ?? [];
101
- const repositoryDetected = inspectRepositoryIntegrationStatus(repo)?.agents ?? [];
102
- return [...new Set([...detectSupportedAgents(), ...previous, ...repositoryDetected])];
103
- }
104
- function formatInitHuman(result) {
105
- const agents = result.paths.scope === "cli-only"
106
- ? "CLI-only AI agents are not configured to discover knodin"
107
- : result.paths.agentIntegration.configured.length > 0
108
- ? `${result.paths.scope} ${result.paths.agentIntegration.configured.join(", ")}`
109
- : `${result.paths.scope} no supported coding agents detected`;
110
- const failures = result.paths.agentIntegration.failed
111
- .map(({ agent, message }) => `\nAgent warning (${agent}): ${message}`)
112
- .join("");
113
- const refresh = result.paths.lifecycleRefresh.state === "fresh"
114
- ? "fresh"
115
- : `still running (${result.paths.lifecycleRefresh.queuedEvents} queued event(s)); run \`knodin wait --fresh\``;
116
- return `${result.message}\nGraph: ${result.paths.database}\nGit refresh: ${result.paths.gitHooks.length} lifecycle hooks installed; ${refresh}\nAgent integration: ${agents}${failures}\nBackground indexer: ${result.paths.backgroundIndexer}\n`;
117
- }
118
- function formatConfigureStatusHuman(result) {
119
- if (result.scope === "unconfigured")
120
- return "Agent integration: unconfigured.\nWarning: AI agents will not discover or invoke knodin automatically. Run `knodin configure --scope personal`.\n";
121
- if (result.scope === "cli-only")
122
- return "Agent integration: CLI-only.\nWarning: AI agents will not discover or invoke knodin automatically. Run `knodin configure --scope personal` or `--scope team` to enable them.\n";
123
- if (result.scope === "repository-detected")
124
- return `Agent integration: repository-detected${result.agents.length > 0 ? ` (${result.agents.join(", ")})` : ""}.\nLocal scope receipt: missing; managed repository integration is present.\n`;
125
- return `Agent integration: ${result.scope}${result.agents.length > 0 ? ` (${result.agents.join(", ")})` : ""}.\n`;
126
- }
127
- function formatConfigureHuman(result) {
128
- let configured = "none detected";
129
- if (result.paths.agentIntegration.configured.length > 0) {
130
- configured = result.paths.agentIntegration.configured.join(", ");
131
- }
132
- else if (result.paths.scope === "cli-only") {
133
- configured = "none (CLI-only)";
134
- }
135
- const failures = result.paths.agentIntegration.failed
136
- .map(({ agent, message }) => `\nAgent warning (${agent}): ${message}`)
137
- .join("");
138
- const refresh = result.paths.lifecycleRefresh.state === "fresh"
139
- ? "fresh"
140
- : `still running (${result.paths.lifecycleRefresh.queuedEvents} queued event(s))`;
141
- const filesystemMutationLines = result.paths.filesystemChanges
142
- .map(({ path: changedPath, action }) => `\nFilesystem ${action}: ${changedPath}`)
143
- .join("");
144
- const externalOutcomeLines = result.paths.externalConfigurationOutcomes
145
- .map(({ system, state }) => `\nExternal configuration outcome: ${system} ${state} (external mutation not locally observable)`)
146
- .join("");
147
- return `${result.message}\nAgent integration: ${result.paths.scope} — ${configured}${failures}${filesystemMutationLines}${externalOutcomeLines}\nGraph initialization: unchanged\nLifecycle refresh: ${refresh}\nNext: ${result.nextAction}\n`;
148
- }
149
- function formatRepairHuman(result) {
150
- const coverage = result.after.coverage;
151
- if (result.cancelled) {
152
- return `Repair paused: ${result.remaining ?? 0} file(s) remaining. Run \`knodin repair\` again to finish.\n`;
153
- }
154
- if (result.verified) {
155
- if (result.lifecycle?.status === "degraded")
156
- return `Repair verified: graph is healthy (${coverage.indexedFiles} indexed files, ${coverage.filesWithSymbols} files with symbols), but lifecycle routing is degraded. Run \`knodin init\`, then \`knodin status\`.\n`;
157
- return `Repair verified: graph is healthy (${coverage.indexedFiles} indexed files, ${coverage.filesWithSymbols} files with symbols).\n`;
158
- }
159
- return `Repair finished with remaining issues. Run \`knodin status --deep\` for details.\n`;
160
- }
161
- function formatIndexHuman(result) {
162
- if (result.indexed.length === 0 && result.unchanged.length > 0) {
163
- const noun = result.unchanged.length === 1 ? "file" : "files";
164
- return `Graph already current: ${result.unchanged.length.toLocaleString()} requested ${noun} needed no work; health verified.\n`;
165
- }
166
- const unchanged = result.unchanged.length > 0
167
- ? `; ${result.unchanged.length.toLocaleString()} already current`
168
- : "";
169
- const noun = result.indexed.length === 1 ? "file" : "files";
170
- return `Index complete: ${result.indexed.length.toLocaleString()} ${noun} indexed${unchanged}; graph health verified.\n`;
171
- }
172
- function formatIndexVerificationError(result) {
173
- const firstIssue = result.verification.missing.files[0] ?? result.verification.missing.records[0];
174
- const detail = firstIssue ? ` First issue: ${firstIssue}.` : "";
175
- return `knodin index: requested work completed, but ${result.verification.issueCount.toLocaleString()} graph issue(s) remain.${detail} Run \`knodin repair\`.\n`;
176
- }
177
- function formatStatusHuman(result) {
178
- const coverage = `${result.coverage.sourceFiles} source files, ${result.coverage.indexedFiles} indexed files, ${result.coverage.filesWithSymbols} files with symbols`;
179
- if (result.status === "indexing" && result.activity) {
180
- const count = result.activity.phaseTotal === undefined
181
- ? ""
182
- : ` ${result.activity.phaseCompleted}/${result.activity.phaseTotal}`;
183
- const elapsed = Math.max(0, Math.floor((Date.now() - Date.parse(result.activity.startedAt)) / 1_000));
184
- return `Graph update in progress: ${result.activity.phase}${count} — ${result.activity.message} (${elapsed}s elapsed; ${coverage}).\n`;
185
- }
186
- const integration = result.integration;
187
- const integrationLine = integration
188
- ? `Agent integration: ${integration.scope}${integration.agents.length > 0 ? ` (${integration.agents.join(", ")})` : ""}.\n`
189
- : "Agent integration: unconfigured. AI agents will not discover knodin automatically; run `knodin configure --scope personal`.\n";
190
- const lifecycleLine = result.lifecycle
191
- ? result.lifecycle.status === "healthy"
192
- ? "Hooks: installed and executable.\n"
193
- : `Lifecycle refresh: ${result.lifecycle.status}; ${result.lifecycle.issues[0] ?? "refresh capability is not verified"}. Run \`knodin init\`.\n`
194
- : "";
195
- const head = (value) => value?.slice(0, 12) ?? "unknown";
196
- const distance = result.freshness.commitDistance === null
197
- ? ""
198
- : ` by ${result.freshness.commitDistance.toLocaleString()} commit(s)`;
199
- const freshnessLine = `Freshness: ${result.freshness.state}; indexed ${head(result.freshness.indexedHead)}, ` +
200
- `current ${head(result.freshness.currentHead)} (${result.freshness.commitRelation}${distance}); ` +
201
- `${result.freshness.workingTree.pendingPaths ?? "unknown"} pending path(s).\n` +
202
- `Last successful refresh: ${result.freshness.lastSuccessfulRefresh ?? "never"}.\n`;
203
- if (result.status === "healthy")
204
- return `Graph content is healthy: ${coverage} (knodin ${result.version}; ${result.verification.mode}).\n${freshnessLine}${lifecycleLine}${integrationLine}`;
205
- if (result.status === "stale")
206
- return `Graph content is intact but evidence is stale (${coverage}).\n${freshnessLine}${lifecycleLine}${integrationLine}Run \`knodin wait --fresh\` or issue a graph query to reconcile bounded drift.\n`;
207
- const outstanding = result.missing.files.length + result.missing.records.length;
208
- const firstIssue = result.missing.files[0] ?? result.missing.records[0];
209
- const detail = firstIssue ? ` First issue: ${firstIssue}.` : "";
210
- const repairCommand = result.lifecycle?.status === "degraded" &&
211
- result.missing.records.every((record) => result.lifecycle?.issues.includes(record))
212
- ? "Run `knodin init`."
213
- : "Run `knodin repair`.";
214
- return `Graph or lifecycle needs repair: ${outstanding} issue(s) found (${coverage}).${detail} ${repairCommand}\n${lifecycleLine}${integrationLine}`;
215
- }
216
- function humanLabel(key) {
217
- return key.replace(/([a-z])([A-Z])/g, "$1 $2");
218
- }
219
- /** Render bounded CLI data for a terminal without turning it back into JSON. */
220
- function formatHumanValue(value, indent = "", label) {
221
- if (value === null || typeof value !== "object") {
222
- return [`${indent}${label ? `${humanLabel(label)}: ` : ""}${String(value)}`];
223
- }
224
- if (Array.isArray(value)) {
225
- if (value.length === 0)
226
- return label ? [] : [`${indent}(none)`];
227
- const lines = label ? [`${indent}${humanLabel(label)}:`] : [];
228
- for (const item of value) {
229
- if (item === null || typeof item !== "object")
230
- lines.push(`${indent} - ${String(item)}`);
231
- else
232
- lines.push(...formatHumanValue(item, `${indent} - `));
233
- }
234
- return lines;
235
- }
236
- const entries = Object.entries(value).filter(([key]) => key !== "responseBudget");
237
- const lines = label ? [`${indent}${humanLabel(label)}:`] : [];
238
- for (const [key, child] of entries) {
239
- if (child === null || typeof child !== "object") {
240
- lines.push(`${indent}${label ? " " : ""}${humanLabel(key)}: ${String(child)}`);
241
- }
242
- else
243
- lines.push(...formatHumanValue(child, `${indent}${label ? " " : ""}`, key));
244
- }
245
- return lines;
246
- }
247
- function formatGenericHuman(cmd, result) {
248
- return `${[`${humanLabel(cmd)}:`, ...formatHumanValue(result, " ")].join("\n")}\n`;
249
- }
250
- function formatCompressionHuman(result) {
251
- const omitted = result.omittedRanges.reduce((total, range) => total + range.lineCount, 0);
252
- let fidelity = `Compressed output; ${omitted} line(s) omitted.`;
253
- if (result.status === "insufficient-budget")
254
- fidelity = `INSUFFICIENT BUDGET: ${result.fidelity.unpreservedSignals.length} detected signal line(s) are available only through retained drill-down.`;
255
- else if (result.complete)
256
- fidelity = "Complete output; nothing omitted.";
257
- const artifact = result.artifact.retained
258
- ? ` Retained artifact: ${result.artifact.id} (${result.artifact.path}).`
259
- : " Raw retention disabled; omitted regions cannot be retrieved.";
260
- const content = result.content ? `${result.content}\n` : "";
261
- return (`${content}---\n${fidelity} ` +
262
- `${result.output.lines}/${result.input.lines} lines, ${result.output.bytes}/${result.input.bytes} bytes; ` +
263
- `exit=${result.exit.code ?? "unknown"}, signal=${result.exit.signal ?? "none"}.${artifact}\n`);
264
- }
265
- function formatCompressionReadHuman(result) {
266
- const content = result.content ? `${result.content}\n` : "";
267
- const redactionStatus = result.raw
268
- ? "UNREDACTED raw view"
269
- : `${result.secretRedactions} secret(s) redacted`;
270
- return (`${content}---\nArtifact ${result.artifactId}, lines ${result.range.startLine}-${result.range.endLine}` +
271
- ` of ${result.range.totalLines}; ${result.bytes}/${result.byteBudget} bytes; ` +
272
- `${redactionStatus}.\n`);
273
- }
274
- function formatFailureRelations(label, relations) {
275
- if (relations.length === 0)
276
- return `${label}: none`;
277
- const formatted = relations.map(({ symbol, file, line }) => {
278
- const location = file ? ` (${file}:${line ?? "?"})` : "";
279
- return `${symbol}${location}`;
280
- });
281
- return `${label}: ${formatted.join(", ")}`;
282
- }
283
- function formatFailureDiagnosisHuman(result) {
284
- const lines = [
285
- `Failure diagnosis: ${result.status}; ${result.diagnostics.length} resolved, ${result.unresolved.length} unresolved.`,
286
- `Freshness: ${result.freshness.state}; indexed=${result.freshness.indexedHead ?? "unknown"}; current=${result.freshness.currentHead ?? "unknown"}.`,
287
- ];
288
- for (const diagnostic of result.diagnostics) {
289
- const location = `${diagnostic.file}:${diagnostic.reference.line ?? "?"}`;
290
- const identity = diagnostic.owner?.identity ? ` [${diagnostic.owner.identity}]` : "";
291
- const owner = `${diagnostic.owner?.symbol ?? "no owning symbol"}${identity}`;
292
- const packageDescription = diagnostic.package
293
- ? `${diagnostic.package.name ?? "unnamed"} (${diagnostic.package.kind}, ${diagnostic.package.manifest})`
294
- : "none";
295
- lines.push("", `${location} -> ${owner}`, `Package: ${packageDescription}`, formatFailureRelations("Tests", diagnostic.tests), formatFailureRelations("Upstream", diagnostic.upstream), formatFailureRelations("Downstream", diagnostic.downstream));
296
- const snippet = result.contextBundle.snippets.find(({ file }) => file === diagnostic.file);
297
- if (snippet)
298
- lines.push(snippet.content);
299
- }
300
- for (const unresolved of result.unresolved) {
301
- const candidates = unresolved.candidates ? ` (${unresolved.candidates.join(", ")})` : "";
302
- lines.push("", `Unresolved ${unresolved.reference.path}: ${unresolved.reason}${candidates}`);
303
- }
304
- lines.push("", ...result.limitations.map((limitation) => `Limitation: ${limitation}`));
305
- return `${lines.join("\n")}\n`;
306
- }
307
- async function readBoundedStdin(maxBytes) {
308
- const chunks = [];
309
- let bytes = 0;
310
- for await (const chunk of process.stdin) {
311
- const buffer = Buffer.isBuffer(chunk) ? chunk : Buffer.from(String(chunk));
312
- bytes += buffer.byteLength;
313
- if (bytes > maxBytes)
314
- throw new Error(`knodin compress: input exceeded bounded retention limit of ${maxBytes} bytes`);
315
- chunks.push(buffer);
316
- }
317
- return Buffer.concat(chunks).toString("utf-8");
318
- }
319
- const CLEAR_TERMINAL_LINE = "\r\x1b[2K";
320
- const PROGRESS_WORKER_CLOSE_TIMEOUT_MS = 2_000;
321
- function createProgressWorkerRenderer(workerName, startMessage) {
322
- const extension = fileURLToPath(import.meta.url).endsWith(".ts") ? ".ts" : ".js";
323
- const workerPath = path.resolve(path.dirname(fileURLToPath(import.meta.url)), `../src/${workerName}${extension}`);
324
- const workerArgs = extension === ".ts" && path.basename(process.execPath).startsWith("node")
325
- ? [...process.execArgv, workerPath]
326
- : [workerPath];
327
- const worker = spawn(process.execPath, workerArgs, {
328
- stdio: ["pipe", "ignore", "inherit"],
329
- env: { ...process.env, KNODIN_FORCE_PROGRESS_TTY: "1" },
330
- });
331
- let failed = false;
332
- const closed = new Promise((resolve) => {
333
- worker.once("close", () => resolve());
334
- worker.once("error", () => resolve());
335
- });
336
- worker.once("error", () => {
337
- failed = true;
338
- });
339
- worker.stdin.once("error", () => {
340
- failed = true;
341
- });
342
- const send = (message) => {
343
- if (failed || worker.stdin.destroyed)
344
- return;
345
- try {
346
- worker.stdin.write(`${JSON.stringify(message)}\n`);
347
- }
348
- catch {
349
- failed = true;
350
- }
351
- };
352
- return {
353
- start: () => send(startMessage),
354
- onProgress: (event) => send({ type: "progress", event }),
355
- stop: async () => {
356
- send({ type: "stop" });
357
- worker.stdin.end();
358
- const waitForClose = (timeoutMs) => new Promise((resolve) => {
359
- let settled = false;
360
- const finish = (closedCleanly) => {
361
- if (settled)
362
- return;
363
- settled = true;
364
- clearTimeout(timer);
365
- resolve(closedCleanly);
366
- };
367
- const timer = setTimeout(() => finish(false), timeoutMs);
368
- timer.unref();
369
- void closed.then(() => finish(true));
370
- });
371
- if (!(await waitForClose(PROGRESS_WORKER_CLOSE_TIMEOUT_MS))) {
372
- // Rendering is observational. A wedged terminal renderer must never
373
- // hold graph work or its completion summary hostage.
374
- worker.kill("SIGKILL");
375
- if (!(await waitForClose(500)))
376
- worker.unref();
377
- }
378
- process.stderr.write(CLEAR_TERMINAL_LINE);
379
- },
380
- };
381
- }
382
- function createInitRenderer(operation = "init") {
383
- if (process.stderr.isTTY !== true) {
384
- const renderer = createInitProgressRenderer({
385
- stderr: process.stderr,
386
- operation,
387
- });
388
- return {
389
- start: () => renderer.start(),
390
- onProgress: (event) => renderer.onProgress(event),
391
- stop: async () => renderer.stop(),
392
- };
393
- }
394
- return createProgressWorkerRenderer("init-progress-worker", {
395
- type: "start",
396
- operation,
397
- });
398
- }
399
- async function main() {
400
- const argv = process.argv.slice(2);
401
- if (argv[0] === "__repository-init-worker") {
402
- if (argv.length !== 2 || !path.isAbsolute(argv[1]))
403
- throw new Error("knodin internal repository init worker requires one absolute path");
404
- const repository = await fs.promises.realpath(argv[1]);
405
- const interval = setInterval(() => {
406
- process.send?.({ type: "rss", rssBytes: process.memoryUsage().rss });
407
- }, 50);
408
- interval.unref();
409
- process.send?.({ type: "rss", rssBytes: process.memoryUsage().rss });
410
- const engine = createEngine();
411
- try {
412
- const systemConfig = loadSystemConfiguration(repository);
413
- const summary = await initializeRepositories([repository], {
414
- // A portfolio worker owns exactly one already-discovered worktree.
415
- // Do not recursively initialize nested repositories inside the same
416
- // process, which would evade the per-repository memory boundary.
417
- depth: 0,
418
- include: [repository],
419
- command: resolveCliRuntimeCommand(process),
420
- index: (target) => engine.index(target),
421
- status: (target) => engine.status(target),
422
- agents: detectSupportedAgents(),
423
- indexMode: (target) => indexModeForPath(systemConfig, target),
424
- });
425
- const result = summary.results.find(({ repository: target }) => target === repository);
426
- if (!result)
427
- throw new Error("repository init worker produced no repository result");
428
- process.send?.({ type: "rss", rssBytes: process.memoryUsage().rss });
429
- process.send?.({ type: "result", result });
430
- if (result.status === "failed")
431
- process.exitCode = 1;
432
- }
433
- finally {
434
- clearInterval(interval);
435
- await engine.close();
436
- }
437
- return;
438
- }
439
- // Keep a normalized argv for compatibility dispatch while Commander remains
440
- // the authoritative grammar, option parser, validator, and help source.
441
- const { rest: cleaned } = extractRepoFlag(argv);
442
- const [cmd, ...rawRest] = cleaned;
443
- if (!cmd || cmd === "-h" || cmd === "--help") {
444
- process.stdout.write(renderCliHelp([], process.stdout.isTTY ? process.stdout.columns : undefined));
445
- return;
446
- }
447
- if (cmd === "-v" || cmd === "--version" || cmd === "version") {
448
- process.stdout.write(`${KNODIN_VERSION}\n`);
449
- return;
450
- }
451
- if (argv.some((argument) => argument === "-h" || argument === "--help")) {
452
- process.stdout.write(renderCliHelp(helpCommandPath(argv), process.stdout.isTTY ? process.stdout.columns : undefined));
453
- return;
454
- }
455
- const invocation = parseCliInvocation(argv);
456
- const optionKey = (flag) => flag.slice(2).replace(/-([a-z])/g, (_match, letter) => letter.toUpperCase());
457
- const selectorValue = (flag) => {
458
- const value = invocation.options[optionKey(flag)];
459
- return typeof value === "string" || typeof value === "number" ? String(value) : undefined;
460
- };
461
- const repoFlag = selectorValue("--repo");
462
- const runtimeCommand = resolveCliRuntimeCommand(process);
463
- // hook-refresh is an internal machine-to-machine command. Keep it JSON even
464
- // when an older installed hook predates the explicit --json argument.
465
- const jsonOutput = invocation.options.json === true || cmd === "hook-refresh";
466
- // `--json` is a shared output flag. Repair owns its richer --json/--jsonl
467
- // parser; all other commands receive their original arguments minus it.
468
- const rest = cmd === "repair" ? rawRest : rawRest.filter((argument) => argument !== "--json");
469
- const selector = {
470
- identity: selectorValue("--identity"),
471
- file: selectorValue("--file"),
472
- kind: selectorValue("--kind"),
473
- toIdentity: selectorValue("--to-identity"),
474
- toFile: selectorValue("--to-file"),
475
- toKind: selectorValue("--to-kind"),
476
- };
477
- const responseBudget = {
478
- bytes: selectorValue("--bytes") ? Number(selectorValue("--bytes")) : undefined,
479
- tokens: selectorValue("--tokens") ? Number(selectorValue("--tokens")) : undefined,
480
- items: selectorValue("--items") ? Number(selectorValue("--items")) : undefined,
481
- };
482
- for (const [flag, value, minimum] of [
483
- ["--bytes", responseBudget.bytes, 256],
484
- ["--tokens", responseBudget.tokens, 64],
485
- ["--items", responseBudget.items, 1],
486
- ]) {
487
- if (value !== undefined && (!Number.isInteger(value) || value < minimum))
488
- throw new Error(`knodin: ${flag} must be an integer >= ${minimum}`);
489
- }
490
- if (cmd === "init") {
491
- explicitScope(rawRest);
492
- const unsupported = rawRest.filter((argument, index) => {
493
- if (argument === "--json" || argument.startsWith("--scope="))
494
- return false;
495
- if (argument === "--scope" || rawRest[index - 1] === "--scope")
496
- return false;
497
- return true;
498
- });
499
- if (unsupported.length > 0)
500
- throw new Error(`knodin init: unknown option: ${unsupported[0]}`);
501
- }
502
- if (cmd === "configure") {
503
- const status = rawRest.includes("--status");
504
- const scope = explicitScope(rawRest);
505
- const unsupported = rawRest.filter((argument, index) => {
506
- if (argument === "--json" || argument === "--status" || argument.startsWith("--scope="))
507
- return false;
508
- if (argument === "--scope" || rawRest[index - 1] === "--scope")
509
- return false;
510
- return true;
511
- });
512
- if (unsupported.length > 0)
513
- throw new Error(`knodin configure: unknown option: ${unsupported[0]}`);
514
- if (status && scope)
515
- throw new Error("knodin configure: --status and --scope are mutually exclusive");
516
- if (!status && !scope)
517
- throw new Error("knodin configure requires --scope personal|team|cli-only or --status");
518
- }
519
- if (cmd === "serve") {
520
- const { startServer } = await import("../src/server.js");
521
- await startServer();
522
- return;
523
- }
524
- if (cmd === "fleet") {
525
- if (repoFlag !== undefined) {
526
- throw new Error("knodin fleet init accepts discovery roots, not --repo");
527
- }
528
- const plan = parseFleetInitArgs(rawRest, process.cwd());
529
- if (!plan.json) {
530
- process.stderr.write("warning: `knodin fleet init` is deprecated; use `knodin repos init --linked-worktrees=skip|include` (alias retained for two minor releases)\n");
531
- }
532
- const engine = createEngine();
533
- const systemConfig = loadSystemConfiguration(plan.roots[0] ?? process.cwd());
534
- const summary = await initializeRepositories(plan.roots, {
535
- command: runtimeCommand,
536
- depth: plan.depth,
537
- dryRun: plan.dryRun,
538
- worktrees: plan.worktrees,
539
- index: (target) => engine.index(target),
540
- status: (target) => engine.status(target),
541
- agents: detectSupportedAgents(),
542
- indexMode: (target) => indexModeForPath(systemConfig, target),
543
- isolatedInitialize: (target) => runRepositoryInitializationProcess({ repository: target, command: runtimeCommand }),
544
- });
545
- await engine.close();
546
- process.stdout.write(plan.json ? `${JSON.stringify(summary)}\n` : formatRepositoryHuman(summary));
547
- process.exitCode = summary.exitCode;
548
- return;
549
- }
550
- if (cmd === "repos") {
551
- if (repoFlag !== undefined) {
552
- throw new Error("knodin repos accepts discovery roots, not --repo");
553
- }
554
- const plan = parseRepositoryCommandArgs(rawRest, process.cwd());
555
- if (plan.command === "discover") {
556
- const discovery = await discoverRepositories(plan.roots, {
557
- depth: plan.depth,
558
- linkedWorktrees: plan.linkedWorktrees,
559
- });
560
- const engine = createEngine();
561
- const systemConfig = loadSystemConfiguration(plan.roots[0] ?? process.cwd());
562
- const repositories = [];
563
- const discoveryRecords = plan.signals
564
- ? discovery.repositories.slice(0, repositorySignalInspectionLimit(plan))
565
- : discovery.repositories;
566
- for (const record of discoveryRecords) {
567
- const inventory = await inventoryRepository(record, {
568
- status: async (target) => attachLifecycleHealth(target, await engine.status(target, { audit: "cached" })),
569
- systemMemberships: (target) => systemMembershipsForPath(systemConfig, target),
570
- });
571
- repositories.push(withRepositorySignals(inventory, plan.signals, plan.signals ? await detectRepositorySignals(record.path) : {}));
572
- }
573
- await engine.close();
574
- const output = {
575
- schemaVersion: 1,
576
- command: "discover",
577
- linkedWorktrees: plan.linkedWorktrees,
578
- ...discovery,
579
- repositories,
580
- };
581
- const boundedOutput = plan.signals
582
- ? applyResponseBudget(output, "repositories:discover", { bytes: plan.byteBudget, tokens: plan.tokenBudget, items: plan.itemBudget }, { bytes: 65_536, tokens: 16_384, items: 100 })
583
- : output;
584
- process.stdout.write(`${JSON.stringify(boundedOutput, null, plan.json ? 0 : 2)}\n`);
585
- process.exitCode = discovery.issues.length > 0 ? 1 : 0;
586
- return;
587
- }
588
- if (plan.command === "init") {
589
- const systemConfig = loadSystemConfiguration(plan.roots[0] ?? process.cwd());
590
- if (plan.dryRun) {
591
- const summary = await initializeRepositories(plan.roots, {
592
- command: runtimeCommand,
593
- depth: plan.depth,
594
- dryRun: true,
595
- worktrees: plan.linkedWorktrees,
596
- manifestPath: plan.manifestPath,
597
- include: plan.include,
598
- exclude: plan.exclude,
599
- index: async () => undefined,
600
- status: async () => {
601
- throw new Error("dry-run must not inspect graph databases");
602
- },
603
- agents: detectSupportedAgents(),
604
- indexMode: (target) => indexModeForPath(systemConfig, target),
605
- });
606
- const planned = summary.results.reduce((counts, result) => {
607
- const key = result.plannedStatus ?? result.status;
608
- counts[key] = (counts[key] ?? 0) + 1;
609
- return counts;
610
- }, {});
611
- const output = {
612
- schemaVersion: 1,
613
- command: "init",
614
- dryRun: true,
615
- graphDatabasesOpened: 0,
616
- modelsLoaded: 0,
617
- selectedRepositories: summary.results.filter(({ message }) => message.startsWith("dry-run:")).length,
618
- planned,
619
- ...summary,
620
- repositories: [],
621
- };
622
- process.stdout.write(plan.json ? `${JSON.stringify(output)}\n` : formatRepositoryHuman(summary));
623
- process.exitCode = summary.exitCode;
624
- return;
625
- }
626
- const engine = createEngine();
627
- const summary = await initializeRepositories(plan.roots, {
628
- command: runtimeCommand,
629
- depth: plan.depth,
630
- dryRun: false,
631
- worktrees: plan.linkedWorktrees,
632
- manifestPath: plan.manifestPath,
633
- include: plan.include,
634
- exclude: plan.exclude,
635
- index: (target) => engine.index(target),
636
- status: (target) => engine.status(target),
637
- agents: detectSupportedAgents(),
638
- indexMode: (target) => indexModeForPath(systemConfig, target),
639
- isolatedInitialize: (target) => runRepositoryInitializationProcess({ repository: target, command: runtimeCommand }),
640
- });
641
- const discovery = await discoverRepositories(plan.roots, {
642
- depth: plan.depth,
643
- linkedWorktrees: plan.linkedWorktrees,
644
- });
645
- const repositories = [];
646
- const selectedPaths = new Set(summary.results.map(({ repository }) => repository));
647
- for (const record of discovery.repositories.filter(({ path }) => selectedPaths.has(path))) {
648
- repositories.push(await inventoryRepository(record, {
649
- status: async (target) => attachLifecycleHealth(target, await engine.status(target, { audit: "cached" })),
650
- systemMemberships: (target) => systemMembershipsForPath(systemConfig, target),
651
- }));
652
- }
653
- await engine.close();
654
- const output = { schemaVersion: 1, command: "init", ...summary, repositories };
655
- process.stdout.write(plan.json ? `${JSON.stringify(output)}\n` : formatRepositoryHuman(summary));
656
- process.exitCode = summary.exitCode;
657
- return;
658
- }
659
- if (plan.command === "search") {
660
- const engine = createEngine();
661
- const systemConfig = loadSystemConfiguration(plan.roots[0] ?? process.cwd());
662
- const output = await searchRepositories(plan.roots, plan.query ?? "", {
663
- depth: plan.depth,
664
- linkedWorktrees: plan.linkedWorktrees,
665
- include: plan.include,
666
- exclude: plan.exclude,
667
- allowPartial: plan.allowPartial,
668
- itemBudget: plan.itemBudget,
669
- byteBudget: plan.byteBudget,
670
- tokenBudget: plan.tokenBudget,
671
- cursor: plan.cursor,
672
- status: async (target) => attachLifecycleHealth(target, await engine.status(target, { audit: "cached" })),
673
- search: (query, target, limit, offset) => engine.search(query, target, limit, {
674
- offset,
675
- includeSource: true,
676
- federate: false,
677
- }),
678
- systemMemberships: (target) => systemMembershipsForPath(systemConfig, target),
679
- });
680
- await engine.close();
681
- process.stdout.write(`${JSON.stringify(output, null, plan.json ? 0 : 2)}\n`);
682
- process.exitCode = output.status === "unavailable" ? 1 : 0;
683
- return;
684
- }
685
- const discovery = await discoverRepositories(plan.roots, {
686
- depth: plan.depth,
687
- linkedWorktrees: plan.linkedWorktrees,
688
- });
689
- const engine = createEngine();
690
- const systemConfig = loadSystemConfiguration(plan.roots[0] ?? process.cwd());
691
- const repositories = [];
692
- for (const repository of discovery.repositories) {
693
- try {
694
- if (plan.command === "doctor") {
695
- const graph = attachLifecycleHealth(repository.path, await engine.status(repository.path, { audit: "deep" }));
696
- const inventory = await inventoryRepository(repository, {
697
- status: async () => graph,
698
- systemMemberships: (target) => systemMembershipsForPath(systemConfig, target),
699
- });
700
- repositories.push({
701
- ...inventory,
702
- diagnosis: await diagnoseInstallation(repository.path, {
703
- currentVersion: KNODIN_VERSION,
704
- runtimeCommand,
705
- graph,
706
- }),
707
- });
708
- }
709
- else {
710
- repositories.push(await inventoryRepository(repository, {
711
- status: async (target) => attachLifecycleHealth(target, await engine.status(target, { audit: "cached" })),
712
- systemMemberships: (target) => systemMembershipsForPath(systemConfig, target),
713
- }));
714
- }
715
- }
716
- catch (error) {
717
- repositories.push({
718
- path: repository.path,
719
- status: "unknown",
720
- error: error instanceof Error ? error.message : String(error),
721
- });
722
- }
723
- }
724
- await engine.close();
725
- const output = {
726
- schemaVersion: 1,
727
- command: plan.command,
728
- linkedWorktrees: plan.linkedWorktrees,
729
- repositories,
730
- issues: discovery.issues,
731
- emptyRoots: discovery.emptyRoots,
732
- staleLinkedWorktrees: discovery.staleLinkedWorktrees,
733
- };
734
- process.stdout.write(`${JSON.stringify(output, null, plan.json ? 0 : 2)}\n`);
735
- process.exitCode =
736
- discovery.issues.length > 0 ||
737
- repositories.some((entry) => ("health" in entry && entry.health !== "healthy") ||
738
- ("status" in entry && entry.status === "unknown"))
739
- ? 1
740
- : 0;
741
- return;
742
- }
743
- if (cmd === "docs") {
744
- if (repoFlag !== undefined)
745
- throw new Error("knodin docs does not accept --repo");
746
- const [topic, ...unsupported] = rawRest;
747
- if (unsupported.length > 0)
748
- throw new Error(`knodin docs: unknown argument ${unsupported[0]}`);
749
- if (!topic || topic === "list") {
750
- process.stdout.write(`${listDocTopics().join("\n")}\n`);
751
- return;
752
- }
753
- const content = getDocSection(topic);
754
- if (!content)
755
- throw new Error(`knodin docs: unknown topic ${topic}`);
756
- process.stdout.write(content.endsWith("\n") ? content : `${content}\n`);
757
- return;
758
- }
759
- if (cmd === "update") {
760
- if (repoFlag !== undefined)
761
- throw new Error("knodin update does not accept --repo");
762
- const [action, ...unsupported] = rest;
763
- if (!action || !["status", "check", "explain", "apply", "rollback"].includes(action)) {
764
- throw new Error("knodin update requires status, check, explain, apply, or rollback");
765
- }
766
- if (unsupported.length > 0)
767
- throw new Error(`knodin update ${action}: unknown argument ${unsupported[0]}`);
768
- const method = detectUpdateInstallMethod(runtimeCommand);
769
- const runManager = async (argv) => {
770
- const [executable, ...arguments_] = argv;
771
- if (!executable)
772
- return { status: 1, stdout: "", stderr: "missing manager command" };
773
- const outcome = spawnSync(executable, arguments_, {
774
- encoding: "utf-8",
775
- timeout: 120_000,
776
- maxBuffer: 4 * 1_024 * 1_024,
777
- stdio: ["ignore", "pipe", "pipe"],
778
- });
779
- return {
780
- status: outcome.status,
781
- stdout: outcome.stdout ?? "",
782
- stderr: outcome.stderr ?? outcome.error?.message ?? "",
783
- };
784
- };
785
- const healthCheck = async () => {
786
- const [executable, ...arguments_] = runtimeCommand;
787
- if (!executable)
788
- return false;
789
- const outcome = spawnSync(executable, [...arguments_, "--version"], {
790
- encoding: "utf-8",
791
- timeout: 10_000,
792
- stdio: ["ignore", "pipe", "pipe"],
793
- });
794
- return outcome.status === 0 && /^\d+\.\d+\.\d+/.test(outcome.stdout.trim());
795
- };
796
- const options = {
797
- currentVersion: KNODIN_VERSION,
798
- installMethod: method,
799
- env: process.env,
800
- runManager,
801
- healthCheck,
802
- };
803
- let output;
804
- if (action === "status")
805
- output = trustedUpdateStatus(options);
806
- else if (action === "check") {
807
- try {
808
- output = await checkTrustedUpdate(options);
809
- }
810
- finally {
811
- if (process.env.KNODIN_UPDATE_BACKGROUND === "1")
812
- releaseScheduledUpdateCheck();
813
- }
814
- }
815
- else if (action === "explain")
816
- output = explainTrustedUpdate(options);
817
- else if (action === "apply")
818
- output = await applyTrustedUpdate(options);
819
- else
820
- output = await rollbackTrustedUpdate(options);
821
- process.stdout.write(jsonOutput ? `${JSON.stringify(output)}\n` : formatGenericHuman("update", output));
822
- return;
823
- }
824
- if (process.stdout.isTTY &&
825
- claimScheduledUpdateCheck({
826
- currentVersion: KNODIN_VERSION,
827
- installMethod: detectUpdateInstallMethod(runtimeCommand),
828
- env: process.env,
829
- })) {
830
- const [executable, ...arguments_] = runtimeCommand;
831
- if (executable) {
832
- const background = spawn(executable, [...arguments_, "update", "check", "--json"], {
833
- detached: true,
834
- stdio: "ignore",
835
- env: { ...process.env, KNODIN_UPDATE_BACKGROUND: "1" },
836
- });
837
- background.unref();
838
- }
839
- }
840
- const resolved = resolveRepo(repoFlag, process.cwd());
841
- if (!resolved.ok) {
842
- process.stderr.write(`${resolved.error}\n`);
843
- process.exit(1);
844
- }
845
- const repo = resolved.repo;
846
- if (cmd === "doctor") {
847
- const gitProbe = spawnSync(gitExecutable(), ["rev-parse", "--is-inside-work-tree"], {
848
- cwd: repo,
849
- encoding: "utf-8",
850
- stdio: ["ignore", "pipe", "ignore"],
851
- });
852
- if (gitProbe.status !== 0 || gitProbe.stdout.trim() !== "true") {
853
- const portfolio = {
854
- schemaVersion: 1,
855
- status: "portfolio-root",
856
- path: repo,
857
- graph: "not-inspected",
858
- warning: "Target is not a Git worktree; treating a portfolio parent as one repository would be misleading.",
859
- remediation: [`knodin repos doctor ${JSON.stringify(repo)}`],
860
- };
861
- process.stdout.write(jsonOutput ? `${JSON.stringify(portfolio)}\n` : formatGenericHuman("doctor", portfolio));
862
- process.exitCode = 1;
863
- return;
864
- }
865
- }
866
- if (cmd === "compress") {
867
- const valueFlags = new Set([
868
- "--strategy",
869
- "--adapter",
870
- "--lines",
871
- "--max-output-bytes",
872
- "--context",
873
- "--exit-code",
874
- "--signal",
875
- "--max-input-bytes",
876
- "--start",
877
- "--end",
878
- "--limit",
879
- "--offset",
880
- ]);
881
- const positionals = rest.filter((argument, index) => !argument.startsWith("--") && !valueFlags.has(rest[index - 1]));
882
- const integer = (flag, fallback, minimum, maximum) => {
883
- const raw = selectorValue(flag);
884
- const value = raw === undefined ? fallback : Number(raw);
885
- if (!Number.isInteger(value) || value < minimum || value > maximum)
886
- throw new Error(`knodin compress: ${flag} must be an integer from ${minimum} to ${maximum}`);
887
- return value;
888
- };
889
- const action = positionals[0] === "read" || positionals[0] === "diagnose" || positionals[0] === "delete"
890
- ? positionals[0]
891
- : "create";
892
- const artifactId = action === "create" ? undefined : positionals[1];
893
- if (action !== "create" && !artifactId)
894
- throw new Error(`knodin compress ${action} requires an artifact id`);
895
- let compressionResult;
896
- let diagnosisUnavailable = false;
897
- if (action === "diagnose") {
898
- const diagnosisEngine = createEngine();
899
- try {
900
- const health = await inspectGraphQueryHealth(repo, (target) => diagnosisEngine.status(target, { audit: "cached" }));
901
- if (health.available) {
902
- compressionResult = await diagnoseFailure(diagnosisEngine, repo, {
903
- artifactId: artifactId ?? "",
904
- maxDiagnostics: integer("--limit", 10, 1, 50),
905
- diagnosticOffset: integer("--offset", 0, 0, 1_000_000),
906
- contextLines: integer("--context", 2, 0, 10),
907
- contextByteBudget: integer("--max-output-bytes", 16_384, 256, 128 * 1024),
908
- });
909
- }
910
- else {
911
- compressionResult = health;
912
- diagnosisUnavailable = true;
913
- process.exitCode = 1;
914
- }
915
- }
916
- finally {
917
- await diagnosisEngine.close();
918
- }
919
- }
920
- else if (action === "read") {
921
- compressionResult = readOutputArtifact(repo, artifactId ?? "", {
922
- startLine: integer("--start", 1, 1, Number.MAX_SAFE_INTEGER),
923
- endLine: selectorValue("--end")
924
- ? integer("--end", 200, 1, Number.MAX_SAFE_INTEGER)
925
- : undefined,
926
- byteBudget: integer("--max-output-bytes", 16_384, 256, 4 * 1024 * 1024),
927
- raw: rest.includes("--raw"),
928
- });
929
- }
930
- else if (action === "delete") {
931
- compressionResult = deleteOutputArtifact(repo, artifactId ?? "");
932
- }
933
- else {
934
- const strategy = selectorValue("--strategy") ?? "smart";
935
- if (!["smart", "head-tail", "errors-only"].includes(strategy))
936
- throw new Error("knodin compress: invalid --strategy");
937
- const adapter = selectorValue("--adapter") ?? "auto";
938
- if (![
939
- "auto",
940
- "generic",
941
- "vitest",
942
- "jest",
943
- "pytest",
944
- "go-test",
945
- "maven",
946
- "gradle",
947
- "dotnet",
948
- "cargo",
949
- ].includes(adapter))
950
- throw new Error("knodin compress: invalid --adapter");
951
- const maxInputBytes = integer("--max-input-bytes", 16 * 1024 * 1024, 1, 64 * 1024 * 1024);
952
- const request = {
953
- exitCode: selectorValue("--exit-code") ? integer("--exit-code", 0, 0, 255) : undefined,
954
- signal: selectorValue("--signal"),
955
- strategy: strategy,
956
- adapter: adapter,
957
- lineBudget: integer("--lines", 200, 1, 10_000),
958
- byteBudget: integer("--max-output-bytes", 16_384, 256, 4 * 1024 * 1024),
959
- contextLines: integer("--context", 1, 0, 10),
960
- maxInputBytes,
961
- retain: !rest.includes("--no-retain"),
962
- redactSecrets: !rest.includes("--no-redact"),
963
- };
964
- const input = positionals[0] === "create" ? (positionals[1] ?? "-") : (positionals[0] ?? "-");
965
- compressionResult =
966
- input === "-"
967
- ? compressOutput(repo, { ...request, text: await readBoundedStdin(maxInputBytes) })
968
- : compressOutputFile(repo, input, request);
969
- }
970
- if (action === "diagnose" &&
971
- (responseBudget.bytes !== undefined ||
972
- responseBudget.tokens !== undefined ||
973
- responseBudget.items !== undefined)) {
974
- compressionResult = applyResponseBudget(compressionResult, "compress:diagnose", responseBudget, { bytes: 65_536, tokens: 16_384, items: 50 });
975
- }
976
- if (jsonOutput)
977
- process.stdout.write(`${JSON.stringify(compressionResult)}\n`);
978
- else if (action === "diagnose" && !diagnosisUnavailable)
979
- process.stdout.write(formatFailureDiagnosisHuman(compressionResult));
980
- else if (action === "diagnose")
981
- process.stdout.write(formatGenericHuman("compress diagnose", compressionResult));
982
- else if (action === "read")
983
- process.stdout.write(formatCompressionReadHuman(compressionResult));
984
- else if (action === "create")
985
- process.stdout.write(formatCompressionHuman(compressionResult));
986
- else
987
- process.stdout.write(formatGenericHuman("compress delete", compressionResult));
988
- return;
989
- }
990
- const engine = createEngine();
991
- let result;
992
- let repairOutput;
993
- let repairWasPlan = false;
994
- let repairExitCode = 0;
995
- let statusWasWatched = false;
996
- const graphRead = async (run) => {
997
- const health = await inspectGraphQueryHealth(repo, async (target) => attachLifecycleHealth(target, await engine.status(target, { audit: "cached" })));
998
- if (!health.available) {
999
- process.exitCode = 1;
1000
- return health;
1001
- }
1002
- const value = await run();
1003
- const verified = await inspectGraphQueryHealth(repo, async (target) => attachLifecycleHealth(target, await engine.status(target, { audit: "cached" })));
1004
- if (!verified.available) {
1005
- process.exitCode = 1;
1006
- return verified;
1007
- }
1008
- return decorateGraphQueryResult(value, verified.state, verified.graph.freshness);
1009
- };
1010
- switch (cmd) {
1011
- case "doctor": {
1012
- const client = selectorValue("--client");
1013
- if (client !== undefined && !["claude", "codex", "gemini", "antigravity"].includes(client))
1014
- throw new Error("knodin doctor: --client must be claude, codex, gemini, or antigravity");
1015
- const unsupported = rest.filter((argument, index) => argument !== "--client" && rest[index - 1] !== "--client");
1016
- if (unsupported.length > 0)
1017
- throw new Error(`knodin doctor: unknown option ${unsupported[0]}`);
1018
- const diagnosis = await diagnoseInstallation(repo, {
1019
- currentVersion: KNODIN_VERSION,
1020
- runtimeCommand: [...runtimeCommand, "serve"],
1021
- graph: await engine.status(repo, { audit: "deep" }),
1022
- client,
1023
- });
1024
- const manager = diagnosis.manager.name;
1025
- diagnosis.update = await checkTrustedUpdate({
1026
- currentVersion: KNODIN_VERSION,
1027
- installMethod: ["npm", "mise", "volta", "nvm", "fnm", "asdf", "homebrew"].includes(manager ?? "")
1028
- ? manager
1029
- : "unknown",
1030
- env: process.env,
1031
- });
1032
- result = diagnosis;
1033
- break;
1034
- }
1035
- case "system": {
1036
- const allowPartial = rest.includes("--allow-partial");
1037
- const [action, systemId, ...unsupported] = rest.filter((argument) => argument !== "--allow-partial");
1038
- if (!action || !["list", "show", "validate", "query"].includes(action)) {
1039
- throw new Error("knodin system requires list, show, validate, or query");
1040
- }
1041
- if (allowPartial && action !== "query")
1042
- throw new Error(`knodin system ${action}: --allow-partial applies only to query`);
1043
- if (unsupported.length > 0)
1044
- throw new Error(`knodin system ${action}: unknown argument ${unsupported[0]}`);
1045
- const config = await enrichSystemRelationships(loadSystemConfiguration(repo));
1046
- if (action === "list") {
1047
- if (systemId)
1048
- throw new Error("knodin system list accepts no system id");
1049
- result = {
1050
- schemaVersion: config.schemaVersion,
1051
- systems: config.systems.map(({ id, components }) => ({
1052
- id,
1053
- componentCount: components.length,
1054
- })),
1055
- };
1056
- break;
1057
- }
1058
- if (!systemId)
1059
- throw new Error(`knodin system ${action} requires <system-id>`);
1060
- const system = config.systems.find(({ id }) => id === systemId);
1061
- if (!system) {
1062
- result = {
1063
- status: "not-found",
1064
- systemId,
1065
- available: config.systems.map(({ id }) => id),
1066
- };
1067
- process.exitCode = 1;
1068
- break;
1069
- }
1070
- if (action === "show") {
1071
- result = { status: "ok", system, repositories: config.repositories };
1072
- }
1073
- else if (action === "validate") {
1074
- const validation = await validateSystemHealth(config, systemId, async (target) => attachLifecycleHealth(target, await engine.status(target, { audit: "cached" })), repo);
1075
- result = { systemId, ...validation };
1076
- if (!validation.valid)
1077
- process.exitCode = 1;
1078
- }
1079
- else {
1080
- const validation = await validateSystemHealth(config, systemId, async (target) => attachLifecycleHealth(target, await engine.status(target, { audit: "cached" })), repo);
1081
- result = queryConfiguredSystem(config, systemId, allowPartial, validation);
1082
- if (result.status === "unavailable")
1083
- process.exitCode = 1;
1084
- }
1085
- break;
1086
- }
1087
- case "hook-refresh": {
1088
- const [kind, first, second] = rest;
1089
- let event;
1090
- if (kind === "commit")
1091
- event = { kind };
1092
- else if (kind === "checkout" && first && second) {
1093
- event = { kind, before: first, after: second };
1094
- }
1095
- else if (kind === "merge" && first && second) {
1096
- event = { kind, before: first, after: second };
1097
- }
1098
- else if (kind === "rewrite" && first) {
1099
- event = { kind, inputPath: first };
1100
- }
1101
- else {
1102
- throw new Error("knodin hook-refresh: invalid lifecycle event");
1103
- }
1104
- const indexed = await refreshFromGitEvent(repo, event, (target, files) => engine.index(target, files));
1105
- result = { indexed };
1106
- break;
1107
- }
1108
- case "init": {
1109
- const scope = await chooseInitScope(rawRest, repo);
1110
- const agents = scope === "team" ? [] : integrationAgents(repo);
1111
- const renderer = createInitRenderer();
1112
- const activity = createIndexActivityReporter(repo);
1113
- activity.start();
1114
- renderer.start();
1115
- let paths;
1116
- try {
1117
- try {
1118
- paths = await initializeRepository(repo, {
1119
- command: runtimeCommand,
1120
- index: (target, options) => engine.index(target, undefined, false, options),
1121
- scope,
1122
- agents,
1123
- onProgress: (event) => {
1124
- activity.update(event);
1125
- renderer.onProgress(event);
1126
- },
1127
- });
1128
- }
1129
- finally {
1130
- await renderer.stop();
1131
- activity.stop();
1132
- }
1133
- }
1134
- catch (error) {
1135
- if (!(error instanceof InitializationHealthError))
1136
- throw error;
1137
- process.stderr.write(`${error.message}\n`);
1138
- await engine.close();
1139
- process.exitCode = 1;
1140
- return;
1141
- }
1142
- result = {
1143
- status: "success",
1144
- message: "knodin initialized successfully. Git lifecycle hooks configured.",
1145
- paths,
1146
- };
1147
- break;
1148
- }
1149
- case "configure": {
1150
- if (rawRest.includes("--status")) {
1151
- result = inspectRepositoryIntegrationStatus(repo) ?? {
1152
- scope: "unconfigured",
1153
- agents: [],
1154
- warning: "AI agents are not configured by knodin. Run `knodin configure --scope personal`.",
1155
- };
1156
- break;
1157
- }
1158
- const scope = explicitScope(rawRest);
1159
- if (!scope)
1160
- throw new Error("knodin configure: missing --scope");
1161
- if (!fs.existsSync(path.join(repo, ".knodin", "db.sqlite"))) {
1162
- throw new Error("knodin configure changes agent integration only; this repository is not initialized. Run `knodin init` first.");
1163
- }
1164
- const agents = scope === "team" ? [] : integrationAgents(repo);
1165
- const paths = await initializeRepository(repo, {
1166
- command: runtimeCommand,
1167
- index: async () => undefined,
1168
- scope,
1169
- agents,
1170
- allowTrackedTransition: true,
1171
- auditConfigurationChanges: true,
1172
- });
1173
- result = {
1174
- status: "success",
1175
- message: `knodin agent integration changed to ${scope}.`,
1176
- graphInitialization: "unchanged",
1177
- nextAction: "run `knodin status` and reload the configured client",
1178
- paths,
1179
- };
1180
- break;
1181
- }
1182
- case "index": {
1183
- // Target the repo unambiguously and never report success for a no-op:
1184
- // a lone directory positional means "index this repo", file positionals
1185
- // must resolve inside the repo, and a run touching zero files fails loud.
1186
- const positionals = extractPositionals(rest);
1187
- const plan = planIndex(repoFlag, positionals, process.cwd());
1188
- if (!plan.ok) {
1189
- process.stderr.write(`${plan.error}\n`);
1190
- await engine.close();
1191
- process.exit(1);
1192
- }
1193
- const clean = rest.includes("--clean") || rest.includes("--force");
1194
- const scipPath = selectorValue("--scip");
1195
- const renderer = createInitRenderer("index");
1196
- const activity = createIndexActivityReporter(plan.repo);
1197
- activity.start();
1198
- renderer.start();
1199
- let indexResult;
1200
- try {
1201
- indexResult = await engine.index(plan.repo, plan.files, clean, {
1202
- scip: scipPath ? { path: scipPath } : undefined,
1203
- onProgress: (event) => {
1204
- activity.update(event);
1205
- renderer.onProgress(event);
1206
- },
1207
- });
1208
- }
1209
- finally {
1210
- await renderer.stop();
1211
- activity.stop();
1212
- }
1213
- result = indexResult;
1214
- const indexedCheck = checkIndexed([...indexResult.indexed, ...indexResult.unchanged], plan.repo);
1215
- if (!indexedCheck.ok) {
1216
- process.stderr.write(`${indexedCheck.error}\n`);
1217
- await engine.close();
1218
- process.exit(1);
1219
- }
1220
- if (indexResult.verification.status !== "healthy") {
1221
- process.stderr.write(formatIndexVerificationError(indexResult));
1222
- await engine.close();
1223
- process.exit(1);
1224
- }
1225
- break;
1226
- }
1227
- case "status": {
1228
- const snapshot = async () => ({
1229
- ...attachLifecycleHealth(repo, await engine.status(repo, {
1230
- audit: rest.includes("--deep") ? "deep" : "cached",
1231
- })),
1232
- integration: inspectRepositoryIntegrationStatus(repo),
1233
- update: trustedUpdateStatus({
1234
- currentVersion: KNODIN_VERSION,
1235
- installMethod: detectUpdateInstallMethod(runtimeCommand),
1236
- env: process.env,
1237
- }),
1238
- });
1239
- if (rest.includes("--watch")) {
1240
- const intervalRaw = selectorValue("--interval");
1241
- const intervalSeconds = intervalRaw === undefined ? 1 : Number(intervalRaw);
1242
- if (!Number.isFinite(intervalSeconds) || intervalSeconds < 0.1 || intervalSeconds > 60)
1243
- throw new Error("knodin status: --interval must be between 0.1 and 60 seconds");
1244
- let watching = true;
1245
- const stop = () => {
1246
- watching = false;
1247
- };
1248
- process.once("SIGINT", stop);
1249
- process.once("SIGTERM", stop);
1250
- while (watching) {
1251
- const observed = await snapshot();
1252
- process.stdout.write(`${JSON.stringify({ observedAt: new Date().toISOString(), ...observed })}\n`);
1253
- await new Promise((resolve) => setTimeout(resolve, intervalSeconds * 1_000));
1254
- }
1255
- process.removeListener("SIGINT", stop);
1256
- process.removeListener("SIGTERM", stop);
1257
- statusWasWatched = true;
1258
- result = null;
1259
- }
1260
- else
1261
- result = await snapshot();
1262
- break;
1263
- }
1264
- case "wait": {
1265
- if (!rest.includes("--fresh"))
1266
- throw new Error("knodin wait requires --fresh");
1267
- const timeoutRaw = selectorValue("--timeout");
1268
- const timeoutSeconds = timeoutRaw === undefined ? 30 : Number(timeoutRaw);
1269
- if (!Number.isFinite(timeoutSeconds) || timeoutSeconds < 0 || timeoutSeconds > 300)
1270
- throw new Error("knodin wait: --timeout must be between 0 and 300 seconds");
1271
- result = await waitForFresh(engine, repo, Math.round(timeoutSeconds * 1_000));
1272
- if (result.status !== "fresh")
1273
- process.exitCode = 1;
1274
- break;
1275
- }
1276
- case "repair": {
1277
- const options = parseRepairCliArgs(rest);
1278
- if (options.plan) {
1279
- result = createRepairPlan(await engine.status(repo, { audit: "deep" }));
1280
- repairOutput = options.output;
1281
- repairWasPlan = true;
1282
- break;
1283
- }
1284
- const progressMode = resolveRepairProgressMode(options, process.env, process.stderr.isTTY);
1285
- const renderer = progressMode === "tty"
1286
- ? createProgressWorkerRenderer("repair-progress-worker", { type: "start" })
1287
- : (() => {
1288
- const direct = createRepairProgressRenderer({
1289
- mode: progressMode,
1290
- intervalMs: options.progressIntervalMs,
1291
- stdout: process.stdout,
1292
- stderr: process.stderr,
1293
- });
1294
- return {
1295
- start: () => direct.start(),
1296
- onProgress: (event) => direct.onProgress(event),
1297
- stop: async () => direct.stop(),
1298
- };
1299
- })();
1300
- const controller = new AbortController();
1301
- const abortRepair = () => controller.abort();
1302
- process.once("SIGINT", abortRepair);
1303
- renderer.start();
1304
- try {
1305
- result = attachRepairLifecycle(repo, await engine.repair(repo, {
1306
- signal: controller.signal,
1307
- onProgress: (event) => renderer.onProgress(event),
1308
- }));
1309
- }
1310
- finally {
1311
- process.removeListener("SIGINT", abortRepair);
1312
- await renderer.stop();
1313
- }
1314
- repairOutput = options.output;
1315
- if (result.cancelled)
1316
- repairExitCode = 130;
1317
- break;
1318
- }
1319
- case "refresh-artifacts": {
1320
- const event = rest[0] ?? "code-change";
1321
- if (event !== "checkout" && event !== "merge" && event !== "code-change") {
1322
- process.stderr.write("knodin refresh-artifacts accepts checkout, merge, or code-change\n");
1323
- process.exit(1);
1324
- }
1325
- const refreshResult = refreshExternalGraphArtifacts(repo, event);
1326
- writeArtifactRefreshRecord(repo, refreshResult);
1327
- result = refreshResult;
1328
- break;
1329
- }
1330
- case "explain": {
1331
- const symbol = rest[0];
1332
- if (!symbol) {
1333
- process.stderr.write("knodin explain requires a <symbol>\n");
1334
- process.exit(1);
1335
- }
1336
- result = await graphRead(async () => {
1337
- const explained = await engine.explain(symbol, repo, rest[1] === "minimal" ? "minimal" : "standard", selector);
1338
- return rest[1] === "source" && !explained.ambiguity
1339
- ? {
1340
- mode: "source",
1341
- identity: explained.identity,
1342
- symbol: explained.symbol,
1343
- source: explained.source,
1344
- staleness: explained.staleness,
1345
- }
1346
- : explained;
1347
- });
1348
- break;
1349
- }
1350
- case "review": {
1351
- const plan = parseReviewArgs(rest);
1352
- result = await graphRead(() => engine.review(plan.base, repo, plan.detailLevel, plan.options));
1353
- break;
1354
- }
1355
- case "map":
1356
- result = await graphRead(() => engine.map(repo, rest.includes("--standard") ? "standard" : "minimal", {
1357
- topN: selectorValue("--top") ? Number(selectorValue("--top")) : undefined,
1358
- sort: selectorValue("--sort"),
1359
- relationKinds: selectorValue("--relations")?.split(",").filter(Boolean),
1360
- }));
1361
- break;
1362
- case "wiki": {
1363
- const health = await inspectGraphQueryHealth(repo, async (target) => attachLifecycleHealth(target, await engine.status(target, { audit: "cached" })));
1364
- if (!health.available) {
1365
- result = health;
1366
- process.exitCode = 1;
1367
- break;
1368
- }
1369
- const force = rest.includes("--force");
1370
- const summary = await engine.wiki(repo, force);
1371
- await engine.close();
1372
- process.stdout.write(`wrote ${summary.written.length} pages, skipped ${summary.skipped.length}\n`);
1373
- process.exitCode = 0;
1374
- return;
1375
- }
1376
- case "visualize": {
1377
- const entry = rest.find((value, index) => !value.startsWith("--") && !rest[index - 1]?.startsWith("--"));
1378
- const outputPath = selectorValue("--output");
1379
- if (!entry)
1380
- throw new Error("knodin visualize requires an <entry> selector");
1381
- if (!outputPath)
1382
- throw new Error("knodin visualize requires --output <path.html>");
1383
- result = await graphRead(() => writeVisualization(engine, repo, {
1384
- entry,
1385
- outputPath,
1386
- depth: selectorValue("--depth") ? Number(selectorValue("--depth")) : undefined,
1387
- byteBudget: selectorValue("--max-bytes")
1388
- ? Number(selectorValue("--max-bytes"))
1389
- : undefined,
1390
- selector: {
1391
- identity: selector.identity,
1392
- file: selector.file,
1393
- kind: selector.kind,
1394
- },
1395
- }));
1396
- break;
1397
- }
1398
- case "search": {
1399
- const query = rest[0];
1400
- if (!query) {
1401
- process.stderr.write("knodin search requires a <query>\n");
1402
- process.exit(1);
1403
- }
1404
- const positionalLimit = rest[1] && !rest[1].startsWith("--") ? rest[1] : undefined;
1405
- const limit = Number(selectorValue("--limit") ?? positionalLimit ?? 5);
1406
- if (!Number.isInteger(limit) || limit < 1)
1407
- throw new Error("knodin search: limit must be a positive integer");
1408
- result = await graphRead(() => engine.search(query, repo, limit, {
1409
- languages: selectorValue("--languages")?.split(",").filter(Boolean),
1410
- extensions: selectorValue("--extensions")?.split(",").filter(Boolean),
1411
- kinds: selectorValue("--kinds")?.split(",").filter(Boolean),
1412
- path: selectorValue("--path"),
1413
- testScope: rest.includes("--tests-only")
1414
- ? "test"
1415
- : rest.includes("--production-only")
1416
- ? "production"
1417
- : "all",
1418
- includeSource: !rest.includes("--no-source"),
1419
- offset: selectorValue("--offset") ? Number(selectorValue("--offset")) : 0,
1420
- }));
1421
- break;
1422
- }
1423
- case "pack": {
1424
- const action = rest[0];
1425
- const diffScope = selectorValue("--diff-scope");
1426
- if (diffScope && !["unstaged", "staged", "all", "compare"].includes(diffScope))
1427
- throw new Error("knodin pack: invalid --diff-scope");
1428
- if (action === "read") {
1429
- if (!rest[1])
1430
- throw new Error("knodin pack read requires an artifact path");
1431
- result = readPackedArtifact(repo, rest[1], Number(selectorValue("--start") ?? 1), Number(selectorValue("--end") ?? 200), Number(selectorValue("--bytes") ?? 16_384));
1432
- }
1433
- else if (action === "grep") {
1434
- if (!rest[1] || !rest[2])
1435
- throw new Error("knodin pack grep requires an artifact path and regex");
1436
- result = grepPackedArtifact(repo, rest[1], rest[2], selectorValue("--flags") ?? "", Number(selectorValue("--limit") ?? 100));
1437
- }
1438
- else {
1439
- const policies = {};
1440
- for (const assignment of selectorValue("--policy")?.split(",") ?? []) {
1441
- const [glob, policy] = assignment.split("=");
1442
- if (!glob || !["full", "summary", "structure-only"].includes(policy))
1443
- throw new Error("knodin pack: invalid --policy assignment");
1444
- policies[glob] = policy;
1445
- }
1446
- result = exportContext(repo, {
1447
- format: selectorValue("--format") ?? "markdown",
1448
- include: selectorValue("--include")?.split(",").filter(Boolean),
1449
- exclude: selectorValue("--exclude")?.split(",").filter(Boolean),
1450
- policies,
1451
- alreadyPresent: selectorValue("--already-present")?.split(",").filter(Boolean),
1452
- chatFiles: selectorValue("--chat-files")?.split(",").filter(Boolean),
1453
- lineNumbers: rest.includes("--line-numbers"),
1454
- includeTree: rest.includes("--tree"),
1455
- byteBudget: Number(selectorValue("--bytes") ?? 65_536),
1456
- tokenBudget: Number(selectorValue("--tokens") ?? 16_384),
1457
- outputPath: selectorValue("--output"),
1458
- git: diffScope || selectorValue("--log")
1459
- ? {
1460
- diffScope: diffScope,
1461
- from: selectorValue("--from"),
1462
- to: selectorValue("--to"),
1463
- log: selectorValue("--log") ? Number(selectorValue("--log")) : undefined,
1464
- }
1465
- : undefined,
1466
- });
1467
- }
1468
- break;
1469
- }
1470
- case "evidence": {
1471
- const level = rest[0];
1472
- const file = rest[1];
1473
- if (!file || !["locate", "outline", "evidence", "expand"].includes(level))
1474
- throw new Error("knodin evidence requires locate|outline|evidence|expand <file>");
1475
- result = deliverProgressiveEvidence({
1476
- repo,
1477
- file,
1478
- level,
1479
- continuation: selectorValue("--continuation"),
1480
- baselineHash: selectorValue("--baseline-hash"),
1481
- baselineBytes: selectorValue("--baseline-bytes")
1482
- ? Number(selectorValue("--baseline-bytes"))
1483
- : undefined,
1484
- startLine: selectorValue("--start") ? Number(selectorValue("--start")) : undefined,
1485
- endLine: selectorValue("--end") ? Number(selectorValue("--end")) : undefined,
1486
- byteLimit: responseBudget.bytes,
1487
- tokenLimit: responseBudget.tokens,
1488
- itemLimit: responseBudget.items,
1489
- });
1490
- break;
1491
- }
1492
- case "query": {
1493
- const pattern = rest[0];
1494
- const repoWide = REPO_WIDE_QUERY_PATTERNS.includes(pattern);
1495
- // architecture_overview's only positional is the detail level (minimal|
1496
- // standard), not a target — keep its target empty so it's not misreported.
1497
- const target = pattern === "architecture_overview" ||
1498
- pattern === "import_cycles" ||
1499
- rest[1]?.startsWith("--")
1500
- ? ""
1501
- : (rest[1] ?? "");
1502
- if (!pattern) {
1503
- process.stderr.write("knodin query requires a <pattern> (lsp_diagnostics|lsp_definitions|lsp_declarations|lsp_implementations|callers_of|callees_of|imports_of|importers_of|import_cycles|file_summary|batch_outline|project_overview|shortest_path|inheritors_of|structural_implementations_of|tests_for|impact|dead_code|large_functions|large_files|rename_preview|flows|flow_of|stats|traverse|feature_path|flow_analysis|knowledge_gaps|surprising_connections|suggested_questions|architecture_overview|community|triggers_of|publishers_of|listeners_of|handlers_of|endpoints_for|consumers_of|children_of|federated_repos|mcp_tools|api_contract_mismatches)\n");
1504
- process.exit(1);
1505
- }
1506
- const directionValue = selectorValue("--direction");
1507
- if (directionValue && !["upstream", "downstream", "both"].includes(directionValue)) {
1508
- process.stderr.write("knodin query: --direction must be upstream, downstream, or both\n");
1509
- process.exit(1);
1510
- }
1511
- const facetsValue = selectorValue("--facets")?.split(",").filter(Boolean);
1512
- if (facetsValue?.some((facet) => !["packages", "layers", "boundaries", "hotspots", "entryPoints", "languages"].includes(facet))) {
1513
- process.stderr.write("knodin query: --facets contains an unknown architecture facet\n");
1514
- process.exit(1);
1515
- }
1516
- if (!target && !repoWide) {
1517
- process.stderr.write(`knodin query ${pattern} requires a <target>\n`);
1518
- process.exit(1);
1519
- }
1520
- // shortest_path: `knodin query shortest_path <from> <to>`
1521
- // rename_preview: `knodin query rename_preview <old> <new>`
1522
- const to = pattern === "shortest_path" || pattern === "rename_preview" ? rest[2] : undefined;
1523
- if (pattern === "rename_preview" && !to) {
1524
- process.stderr.write("knodin query rename_preview requires <old> and <new>\n");
1525
- process.exit(1);
1526
- }
1527
- // traverse: `knodin query traverse <symbol> [--depth n | <n>]`. Depth is
1528
- // clamped (1-6) in the engine, so a raw value passes straight through.
1529
- let depth;
1530
- if (pattern === "traverse" || pattern === "feature_path" || pattern === "impact") {
1531
- const flagIdx = rest.indexOf("--depth");
1532
- const raw = flagIdx >= 0 ? rest[flagIdx + 1] : rest[2];
1533
- if (raw !== undefined && raw !== "" && Number.isFinite(Number(raw)))
1534
- depth = Number(raw);
1535
- }
1536
- // architecture_overview: `knodin query architecture_overview [minimal|standard]`.
1537
- // Reuses the same detail knob as explain/review; minimal is the default.
1538
- let detailLevel;
1539
- if (pattern === "architecture_overview" && rest.includes("standard")) {
1540
- detailLevel = "standard";
1541
- }
1542
- const queryLimitRaw = selectorValue("--limit");
1543
- if (queryLimitRaw !== undefined &&
1544
- (!Number.isInteger(Number(queryLimitRaw)) || Number(queryLimitRaw) < 1)) {
1545
- process.stderr.write("knodin query: --limit must be a positive integer\n");
1546
- process.exit(1);
1547
- }
1548
- const queryHealth = pattern.startsWith("lsp_")
1549
- ? null
1550
- : await inspectGraphQueryHealth(repo, async (target) => attachLifecycleHealth(target, await engine.status(target, { audit: "cached" })));
1551
- if (queryHealth && !queryHealth.available) {
1552
- result = queryHealth;
1553
- process.exitCode = 1;
1554
- break;
1555
- }
1556
- result = await engine.query(pattern, target, repo, to, queryLimitRaw ? Number(queryLimitRaw) : undefined, depth, detailLevel, selector, pattern === "impact"
1557
- ? {
1558
- mode: selectorValue("--impact-mode") === "file" ? "file" : "symbol",
1559
- direction: ["upstream", "downstream", "both"].includes(selectorValue("--direction") ?? "")
1560
- ? selectorValue("--direction")
1561
- : undefined,
1562
- relationKinds: selectorValue("--relations")
1563
- ?.split(",")
1564
- .map((kind) => kind.trim())
1565
- .filter(Boolean),
1566
- minConfidence: selectorValue("--min-confidence")
1567
- ? Number(selectorValue("--min-confidence"))
1568
- : undefined,
1569
- includeTests: !rest.includes("--exclude-tests"),
1570
- includeDataFlow: rest.includes("--data-flow"),
1571
- }
1572
- : undefined, {
1573
- minLines: selectorValue("--min-lines") ? Number(selectorValue("--min-lines")) : undefined,
1574
- minComplexity: selectorValue("--min-complexity")
1575
- ? Number(selectorValue("--min-complexity"))
1576
- : undefined,
1577
- kinds: selectorValue("--kinds")?.split(",").filter(Boolean),
1578
- path: selectorValue("--path"),
1579
- direction: pattern === "traverse" &&
1580
- ["upstream", "downstream", "both"].includes(selectorValue("--direction") ?? "")
1581
- ? selectorValue("--direction")
1582
- : undefined,
1583
- includeDataFlow: pattern === "traverse" ? rest.includes("--data-flow") : undefined,
1584
- flowVariable: pattern === "flow_analysis" ? selectorValue("--variable") : undefined,
1585
- architectureFacets: facetsValue,
1586
- topN: selectorValue("--top") ? Number(selectorValue("--top")) : undefined,
1587
- sort: selectorValue("--sort"),
1588
- relationKinds: pattern === "impact"
1589
- ? undefined
1590
- : selectorValue("--relations")?.split(",").filter(Boolean),
1591
- detailLevel,
1592
- });
1593
- if (pattern === "impact" || pattern === "dead_code") {
1594
- const config = await enrichSystemRelationships(loadSystemConfiguration(repo));
1595
- result = incorporateSystemQueryEvidence(config, repo, pattern, target, result);
1596
- }
1597
- if (queryHealth?.available) {
1598
- const verifiedQueryHealth = await inspectGraphQueryHealth(repo, async (target) => attachLifecycleHealth(target, await engine.status(target, { audit: "cached" })));
1599
- if (!verifiedQueryHealth.available) {
1600
- result = verifiedQueryHealth;
1601
- process.exitCode = 1;
1602
- break;
1603
- }
1604
- result = decorateGraphQueryResult(result, verifiedQueryHealth.state, verifiedQueryHealth.graph.freshness);
1605
- }
1606
- break;
1607
- }
1608
- case "rename": {
1609
- // knodin rename <old> <new> [--apply] [--no-verify]
1610
- const positional = rest.filter((a) => !a.startsWith("--"));
1611
- const oldName = positional[0];
1612
- const newName = positional[1];
1613
- const apply = rest.includes("--apply");
1614
- // Post-apply typecheck is on by default; --no-verify opts out.
1615
- const verify = !rest.includes("--no-verify");
1616
- if (!oldName || !newName) {
1617
- process.stderr.write("knodin rename requires <old> and <new>\n");
1618
- process.exit(1);
1619
- }
1620
- result = await graphRead(() => engine.rename(oldName, newName, repo, apply, verify, selector));
1621
- break;
1622
- }
1623
- case "prs": {
1624
- try {
1625
- const args = rest[0] === "audit" ? rest.slice(1) : rest;
1626
- const flag = (name) => {
1627
- const index = args.indexOf(name);
1628
- return index >= 0 ? args[index + 1] : undefined;
1629
- };
1630
- const state = flag("--state");
1631
- const limitValue = flag("--limit");
1632
- const limit = limitValue === undefined ? 50 : Number(limitValue);
1633
- if (!Number.isInteger(limit) || limit < 1)
1634
- throw new Error("knodin prs: --limit must be a positive integer");
1635
- result = await auditPullRequests(repo, engine, {
1636
- state,
1637
- limit,
1638
- branches: flag("--branches"),
1639
- range: flag("--range"),
1640
- base: flag("--base"),
1641
- head: flag("--head"),
1642
- expectedLogin: flag("--expected-login"),
1643
- });
1644
- }
1645
- catch (err) {
1646
- await engine.close();
1647
- process.stderr.write(`${err.message}\n`);
1648
- process.exit(1);
1649
- }
1650
- break;
1651
- }
1652
- case "worktrees": {
1653
- const action = rest[0] ?? "status";
1654
- if (action === "status") {
1655
- result = await inspectWorktrees(repo, (worktree) => engine.status(worktree, { audit: "cached" }));
1656
- }
1657
- else if (action === "reconcile") {
1658
- result = reconcileWorktrees(repo);
1659
- }
1660
- else if (action === "remove") {
1661
- const target = rest[1];
1662
- if (!target)
1663
- throw new Error("knodin worktrees remove requires <path>");
1664
- result = removeManagedWorktree(repo, target, rest.includes("--dry-run"));
1665
- }
1666
- else {
1667
- throw new Error(`knodin worktrees: unknown action ${action}`);
1668
- }
1669
- break;
1670
- }
1671
- case "telemetry": {
1672
- const action = rest[0];
1673
- const rawRetention = selectorValue("--retention-days");
1674
- const retentionDays = rawRetention === undefined ? 30 : Number(rawRetention);
1675
- if (!Number.isInteger(retentionDays) || retentionDays < 1 || retentionDays > 3650)
1676
- throw new Error("knodin telemetry: --retention-days must be an integer from 1 to 3650");
1677
- const input = selectorValue("--input");
1678
- if (action === "status")
1679
- result = telemetryStatus(repo, input, retentionDays);
1680
- else if (action === "report")
1681
- result = writeTelemetryReport(repo, readTelemetryRecords(repo, input, retentionDays), selectorValue("--output"));
1682
- else if (action === "export")
1683
- result = exportTelemetry(repo, readTelemetryRecords(repo, input, retentionDays), selectorValue("--output"));
1684
- else if (action === "clear")
1685
- result = clearTelemetry(repo, input);
1686
- else
1687
- throw new Error("knodin telemetry requires status, report, export, or clear");
1688
- break;
1689
- }
1690
- case "diagnostics": {
1691
- const [action, bundlePath] = invocation.positionals;
1692
- const rawRetention = selectorValue("--retention-days");
1693
- const sinceOption = selectorValue("--since");
1694
- const outputOption = selectorValue("--output");
1695
- const retentionDays = rawRetention === undefined ? 14 : Number(rawRetention);
1696
- if (!Number.isInteger(retentionDays) || retentionDays < 1 || retentionDays > 365)
1697
- throw new Error("knodin diagnostics: --retention-days must be an integer from 1 to 365");
1698
- if (bundlePath && action !== "inspect")
1699
- throw new Error(`knodin diagnostics ${action}: unexpected bundle argument`);
1700
- if (rawRetention !== undefined && action !== "enable")
1701
- throw new Error(`knodin diagnostics ${action}: --retention-days applies only to enable`);
1702
- if (sinceOption !== undefined && action !== "collect")
1703
- throw new Error(`knodin diagnostics ${action}: --since applies only to collect`);
1704
- if (outputOption !== undefined && action !== "collect")
1705
- throw new Error(`knodin diagnostics ${action}: --output applies only to collect`);
1706
- if (action === "enable")
1707
- result = enableDiagnostics(repo, retentionDays);
1708
- else if (action === "status")
1709
- result = diagnosticsStatus(repo);
1710
- else if (action === "disable")
1711
- result = disableDiagnostics(repo);
1712
- else if (action === "clear")
1713
- result = clearDiagnostics(repo);
1714
- else if (action === "inspect") {
1715
- if (!bundlePath)
1716
- throw new Error("knodin diagnostics inspect requires <bundle>");
1717
- result = inspectDiagnosticsBundle(repo, bundlePath);
1718
- }
1719
- else if (action === "collect") {
1720
- const since = sinceOption ?? "24h";
1721
- const match = /^(\d+)(h|d)$/.exec(since);
1722
- if (!match)
1723
- throw new Error("knodin diagnostics collect: --since must be hours or days, such as 24h or 7d");
1724
- const sinceHours = Number(match[1]) * (match[2] === "d" ? 24 : 1);
1725
- const graph = attachLifecycleHealth(repo, await engine.status(repo, { audit: "deep" }));
1726
- const doctor = await diagnoseInstallation(repo, {
1727
- currentVersion: KNODIN_VERSION,
1728
- runtimeCommand: [...runtimeCommand, "serve"],
1729
- graph,
1730
- });
1731
- result = collectDiagnostics(repo, {
1732
- sinceHours,
1733
- outputPath: outputOption,
1734
- doctor,
1735
- graph,
1736
- telemetry: readTelemetryRecords(repo, undefined, Math.max(1, Math.ceil(sinceHours / 24))),
1737
- knodinVersion: KNODIN_VERSION,
1738
- });
1739
- }
1740
- else {
1741
- throw new Error("knodin diagnostics requires enable, status, collect, inspect, clear, or disable");
1742
- }
1743
- break;
1744
- }
1745
- case "context": {
1746
- // knodin context "<task>" [base]
1747
- const task = rest[0];
1748
- if (!task) {
1749
- process.stderr.write('knodin context requires a "<task>" description\n');
1750
- process.exit(1);
1751
- }
1752
- result = await graphRead(() => buildKnodinContext(engine, task, repo, rest[1]));
1753
- break;
1754
- }
1755
- default:
1756
- process.stderr.write(`unknown command: ${cmd}\n\n${renderCliHelp([], process.stderr.isTTY ? process.stderr.columns : undefined)}`);
1757
- process.exit(1);
1758
- }
1759
- await engine.close();
1760
- if (statusWasWatched)
1761
- return;
1762
- const boundedResult = applyResponseBudget(result, cmd, responseBudget, {
1763
- bytes: 65_536,
1764
- tokens: 16_384,
1765
- items: 100,
1766
- });
1767
- const finalExitCode = Math.max(Number(process.exitCode ?? 0), repairExitCode);
1768
- if (cmd === "init" && !jsonOutput) {
1769
- process.stdout.write(formatInitHuman(boundedResult));
1770
- process.exitCode = finalExitCode;
1771
- return;
1772
- }
1773
- if (cmd === "configure" && !jsonOutput && !rawRest.includes("--status")) {
1774
- process.stdout.write(formatConfigureHuman(boundedResult));
1775
- return;
1776
- }
1777
- if (cmd === "configure" && !jsonOutput && rawRest.includes("--status")) {
1778
- process.stdout.write(formatConfigureStatusHuman(boundedResult));
1779
- return;
1780
- }
1781
- if (cmd === "repair" && repairOutput === "human" && !repairWasPlan) {
1782
- process.stdout.write(formatRepairHuman(boundedResult));
1783
- process.exitCode = finalExitCode;
1784
- return;
1785
- }
1786
- if (cmd === "index" && !jsonOutput) {
1787
- // Human counts must describe the actual operation. The response budget is
1788
- // a JSON transport constraint and may truncate large `indexed` arrays.
1789
- process.stdout.write(formatIndexHuman(result));
1790
- process.exitCode = finalExitCode;
1791
- return;
1792
- }
1793
- if (cmd === "status" && !jsonOutput) {
1794
- process.stdout.write(formatStatusHuman(boundedResult));
1795
- process.exitCode = finalExitCode;
1796
- return;
1797
- }
1798
- if (!jsonOutput && repairOutput !== "jsonl") {
1799
- process.stdout.write(formatGenericHuman(cmd, boundedResult));
1800
- process.exitCode = finalExitCode;
1801
- return;
1802
- }
1803
- // Budget accounting is over the exact compact serialization written here.
1804
- process.stdout.write(repairOutput === "jsonl"
1805
- ? serializeRepairJsonlRecord("result", boundedResult)
1806
- : `${JSON.stringify(boundedResult)}\n`);
1807
- process.exitCode = finalExitCode;
1808
- }
1809
- main().catch((err) => {
1810
- const argv = process.argv.slice(2);
1811
- const repoIndex = argv.indexOf("--repo");
1812
- const equalsRepo = argv.find((argument) => argument.startsWith("--repo="));
1813
- const candidate = repoIndex >= 0 && argv[repoIndex + 1]
1814
- ? argv[repoIndex + 1]
1815
- : equalsRepo
1816
- ? equalsRepo.slice("--repo=".length)
1817
- : process.cwd();
1818
- const command = argv.find((argument, index) => {
1819
- if (argument.startsWith("-"))
1820
- return false;
1821
- return !(index > 0 && argv[index - 1] === "--repo") && argument !== candidate;
1822
- });
1823
- const knownCommands = new Set([
1824
- "init",
1825
- "configure",
1826
- "index",
1827
- "doctor",
1828
- "status",
1829
- "wait",
1830
- "repair",
1831
- "serve",
1832
- "context",
1833
- "explain",
1834
- "review",
1835
- "map",
1836
- "search",
1837
- "query",
1838
- "rename",
1839
- "wiki",
1840
- "visualize",
1841
- "pack",
1842
- "compress",
1843
- "prs",
1844
- "worktrees",
1845
- "telemetry",
1846
- "diagnostics",
1847
- "system",
1848
- "repos",
1849
- "update",
1850
- ]);
1851
- const diagnostic = recordDiagnosticFailure(candidate, {
1852
- surface: "cli",
1853
- operation: command && knownCommands.has(command) ? command : "unknown",
1854
- phase: "dispatch",
1855
- error: err,
1856
- });
1857
- const correlation = diagnostic.recorded ? ` [diagnostic ${diagnostic.correlationId}]` : "";
1858
- console.error(`${err instanceof Error ? err.message : String(err)}${correlation}`);
1859
- process.exit(1);
1860
- });
2
+ import{c as vt}from"../chunks/chunk-SIJAQVSX.js";import{$ as $t,A as at,Aa as Se,B as ct,Ba as Bt,C as ke,Ca as Ht,D as be,E as dt,F as lt,G as ut,H as ae,I as xe,J as F,K as pt,L as B,M as mt,N as ft,O as V,P as ve,Q as ht,R as gt,S as wt,T as yt,U as kt,V as bt,W as ce,X as xt,Y as $e,a as se,aa as de,b as Q,c as ye,ca as Et,d as Be,da as Rt,e as He,ea as St,f as De,g as ze,h as Ge,ha as Ct,i as Ue,ia as Ot,ja as Ee,k as Ve,ka as le,l as Ke,la as Pt,m as Je,ma as Lt,n as Ye,na as qt,o as Qe,oa as ue,p as Xe,pa as Mt,q as Ze,qa as Ft,r as et,ra as Z,s as tt,sa as jt,t as nt,ta as ee,u as ot,ua as pe,v as it,va as H,w as st,wa as Re,x as A,xa as Wt,y as rt,ya as X,z as re,za as te}from"../chunks/chunk-DMQAGX77.js";import{a as _t,b as It,c as Nt,e as Tt,f as At}from"../chunks/chunk-F4Z3Z766.js";import"../chunks/chunk-X6M4HUUE.js";import{a as Me,b as Fe,c as je,d as We}from"../chunks/chunk-YPRMY2LP.js";import{spawn as en,spawnSync as Ie}from"node:child_process";import Te from"node:fs";import W from"node:path";import $n from"node:readline/promises";import{fileURLToPath as En}from"node:url";import{spawnSync as Dt}from"node:child_process";import Ce from"node:fs";import ne from"node:path";var zt=3e4;function nn(){return{commandExists:e=>{let o=Dt(e,["--help"],{stdio:"ignore",timeout:5e3});return o.status!==null||o.error?.code!=="ENOENT"},run:(e,o)=>{let n=performance.now(),r=Dt(e[0],e.slice(1),{stdio:"ignore",timeout:zt,cwd:o});return{exitCode:r.status,elapsedMs:Math.round(performance.now()-n),timedOut:r.signal==="SIGTERM"||r.signal==="SIGKILL"}}}}function Gt(e,o,n=nn()){let r=ne.resolve(e),c=ne.join(r,".gitnexus","run.cjs"),i=[Ce.existsSync(c)?{artifact:"gitnexus",command:["node",c,"analyze"],requiredCommand:"node"}:{artifact:"gitnexus",command:["gitnexus","analyze",r],requiredCommand:"gitnexus"},{artifact:"graphify",command:["graphify","update",r],requiredCommand:"graphify"}].map(({artifact:d,command:O,requiredCommand:C})=>{if(!n.commandExists(C))return{artifact:d,state:"skipped",reason:`${d} is not installed`};let T=n.run(O,r);return T.exitCode===0?{artifact:d,state:"success",command:O,elapsedMs:T.elapsedMs}:{artifact:d,state:"failed",command:O,elapsedMs:T.elapsedMs,reason:T.timedOut?`timed out after ${zt}ms`:`exited ${T.exitCode??"unknown"}`}}),g=i.filter(d=>d.state==="success").length,b=i.filter(d=>d.state==="failed").length,$=b>0&&g===0?"failed":b>0||g>0?"partial":"skipped";return{event:o,status:$,artifacts:i}}function Ut(e,o){let n=ne.join(ne.resolve(e),".knodin");Ce.mkdirSync(n,{recursive:!0}),Ce.appendFileSync(ne.join(n,"artifact-refresh.jsonl"),`${JSON.stringify({at:new Date().toISOString(),...o})}
3
+ `,"utf8")}import{Argument as he,Command as rn,CommanderError as Kt,Option as an}from"commander";function fe(e){return e.length-e.trimStart().length}function on(e){let o=[],n=null;for(let r of e.replaceAll(" "," ").split(`
4
+ `)){if(r.trim()===""){n&&o.push(n),n=null,o.push(null);continue}if(n&&fe(r)>=4){n.continuations.push(r);continue}n&&o.push(n),n={first:r.trimEnd(),continuations:[]}}return n&&o.push(n),o}function me(e,o,n,r){let c=e.trim().split(/\s+/),l=[],i=o;for(let g of c){let b=i.trim().length>0?" ":"";i.length+b.length+g.length>r&&i.trim().length>0?(l.push(i.trimEnd()),i=`${n}${g}`):i+=`${b}${g}`}return i.trim().length>0&&l.push(i.trimEnd()),l}function sn(e,o){let n=" ".repeat(fe(e.first)),r=e.first.trim();if(e.continuations.length===0&&e.first.length<=o)return[e.first];let c=e.continuations.length>0?Math.min(...e.continuations.map(fe)):fe(e.first),l=[r,...e.continuations.map(g=>g.trim())].join(" ");if(e.continuations.length>0&&n.length+r.length>o*.55)return me(l,n,`${n} `,o);let i=/\s{2,}/.exec(r);if(i?.index!==void 0){let g=r.slice(0,i.index).trimEnd(),b=l.slice(i.index+i[0].length).trim(),$=Math.max(n.length+g.length+2,c);return me(b,`${n}${g}${" ".repeat(Math.max(2,$-n.length-g.length))}`," ".repeat($),o)}if(e.continuations.length>0){let g=n.length+r.length,b=Math.max(g+2,c),$=e.continuations.map(d=>d.trim()).join(" ");return me($,`${n}${r}${" ".repeat(Math.max(2,b-g))}`," ".repeat(b),o)}return me(r,n,n,o)}function Vt(e,o){let n=o===void 0||!Number.isFinite(o)?100:Math.max(60,Math.floor(o)-1),r=on(e).flatMap(c=>c===null?[""]:sn(c,n));for(;r.at(-1)==="";)r.pop();return`${r.join(`
5
+ `)}
6
+ `}function cn(e,o){let n=Number(e);if(!Number.isInteger(n))throw new Kt(1,"knodin.invalidNumber",`${o.split(" ")[0]} must be an integer`);return n}function dn(e,o){let n=Number(e);if(!Number.isFinite(n))throw new Kt(1,"knodin.invalidNumber",`${o.split(" ")[0]} must be a number`);return n}function ln(e,o){return[...o,e]}function k(e,o,n){let r=new an(e,o);return n==="integer"?r.argParser(c=>cn(c,e)):n==="number"?r.argParser(c=>dn(c,e)):n==="collect"&&r.argParser(ln).default([]),r}function S(e,o,n,r){let c=e.command(o).description(n).allowExcessArguments(!1);return c.action((...l)=>r(l.at(-1))),c}function un(e){e.option("-v, --version","print the installed knodin version").addOption(k("--repo <path>","target a repository instead of the current directory")).option("--json","emit stable JSON").addOption(k("--identity <id>","select a stable symbol identity")).addOption(k("--file <path>","select a repo-relative definition file")).addOption(k("--kind <kind>","select a symbol kind")).addOption(k("--to-identity <id>","select a destination identity")).addOption(k("--to-file <path>","select a destination file")).addOption(k("--to-kind <kind>","select a destination kind")).addOption(k("--bytes <count>","bound serialized response bytes","integer")).addOption(k("--tokens <count>","bound serialized response tokens","integer")).addOption(k("--items <count>","bound serialized response items","integer")).option("--impact-mode <mode>","symbol or file impact").option("--direction <direction>","upstream, downstream, or both").addOption(k("--depth <count>","bounded traversal depth","integer")).option("--relations <kinds>","comma-separated relationship kinds").addOption(k("--min-confidence <value>","minimum edge confidence","number")).option("--limit <count>","result limit").option("--exclude-tests","exclude tests from impact").option("--data-flow","include bounded data-flow evidence")}function pn(e,o){let n=e.command("repos").description("manage a portfolio of repositories");for(let c of["discover","init","status","doctor"]){let l=S(n,`${c} <roots...>`,`${c} repositories beneath one or more roots`,o).addOption(k("--depth <count>","maximum discovery depth","integer")).option("--linked-worktrees <mode>","skip or include linked worktrees");c==="init"&&l.addOption(k("--include <selector>","include a repository id or path","collect")).addOption(k("--exclude <selector>","exclude a repository id or path","collect")).option("--manifest <path>","write or resume a portfolio manifest").option("--dry-run","report actions without changing repositories"),c==="discover"&&l.option("--signals","include bounded repository applicability signals").addOption(k("--items <count>","bound returned repositories and signal arrays","integer")).addOption(k("--bytes <count>","bound serialized response bytes","integer")).addOption(k("--tokens <count>","bound estimated response tokens","integer")).addHelpText("after",`
7
+ --signals returns hookManager, markerFiles, sanitized remotes, ciProviders, agentConfigs, and aidevTrackReferenced when known; every returned repository has signals, including {}. Inspection uses fixed documented marker/hook allowlists, reads only bounded hook configuration, and performs no network, credential, or marker-content reads. Linked worktrees inspect their own checkout when --linked-worktrees include is selected. See docs/REPOSITORIES-AND-WORKTREES.md for omissions and limits.
8
+ `)}S(n,"search <query>","search selected repositories sequentially",o).addOption(k("--root <path>","portfolio root","collect")).addOption(k("--include <selector>","include a repository id or path","collect")).addOption(k("--exclude <selector>","exclude a repository id or path","collect")).option("--cursor <cursor>","resume from an opaque cursor").option("--allow-partial","return healthy repository results when another degrades");let r=e.command("fleet").description("deprecated repository-fleet compatibility");S(r,"init <roots...>","initialize a repository fleet",o).addOption(k("--depth <count>","maximum discovery depth","integer")).option("--worktrees <mode>","skip or include linked worktrees").option("--dry-run","report actions without mutation")}function mn(e,o){S(e,"context <task> [base]","build compact task orientation",o),S(e,"explain <symbol> [detail]","explain one ambiguity-safe symbol",o),S(e,"review [base] [detail]","review an explicit Git diff scope",o).option("--scope <scope>","unstaged, staged, all, or compare").option("--from <ref>","comparison start revision").option("--to <ref>","comparison end revision").option("--files <paths>","comma-separated repo-relative paths"),S(e,"map","show architecture communities and edges",o).option("--standard","include full map detail").addOption(k("--top <count>","maximum ranked communities","integer")).option("--sort <mode>","relevance, name, size, degree, or complexity").option("--relations <kinds>","comma-separated relationship kinds"),S(e,"wiki","write local architecture wiki pages",o).option("--force","rewrite unchanged pages"),S(e,"visualize <entry>","write a local architecture/call-flow HTML artifact",o).requiredOption("--output <path>","repo-relative HTML output path").addOption(k("--depth <count>","call-flow depth","integer")).addOption(k("--max-bytes <count>","hard artifact budget","integer")),S(e,"search <query> [limit]","hybrid symbol search",o).option("--languages <values>","comma-separated languages").option("--extensions <values>","comma-separated extensions").option("--kinds <values>","comma-separated symbol kinds").option("--path <prefix>","repo-relative path prefix").option("--tests-only","search test symbols only").option("--production-only","search production symbols only").option("--no-source","omit source snippets").addOption(k("--offset <count>","pagination offset","integer")).addOption(k("--limit <count>","result limit","integer")),S(e,"query <pattern> [targets...]","run one structured graph query",o).option("--impact-mode <mode>","symbol or file impact").option("--direction <direction>","upstream, downstream, or both").addOption(k("--depth <count>","bounded traversal depth","integer")).option("--relations <kinds>","comma-separated relationship kinds").addOption(k("--min-confidence <value>","minimum edge confidence","number")).option("--exclude-tests","exclude tests from impact").option("--data-flow","include bounded data-flow evidence").option("--limit <count>","result limit").addOption(k("--min-lines <count>","minimum line count","integer")).addOption(k("--min-complexity <count>","minimum complexity","integer")).option("--kinds <values>","comma-separated symbol kinds").option("--path <prefix>","repo-relative path prefix").option("--variable <name>","flow-analysis variable").option("--facets <values>","comma-separated architecture facets").addOption(k("--top <count>","maximum ranked results","integer")).option("--sort <mode>","result ordering"),S(e,"rename <old> <new>","preview or apply an ambiguity-safe rename",o).option("--apply","apply the verified edit").option("--no-verify","skip post-apply typecheck")}function fn(e,o){S(e,"evidence <level> <file>","deliver progressive source evidence",o).option("--continuation <handle>","resume an exact prior response").option("--baseline-hash <sha256>","complete baseline SHA-256").addOption(k("--baseline-bytes <count>","complete baseline UTF-8 bytes","integer")).addOption(k("--start <line>","first source line","integer")).addOption(k("--end <line>","last source line","integer"));let n=e.command("pack").description("export bounded portable context");n.argument("[input]").option("--format <format>","markdown, json, or xml").option("--include <globs>","comma-separated include globs").option("--exclude <globs>","comma-separated exclude globs").option("--policy <assignments>","comma-separated glob policies").option("--already-present <paths>","comma-separated paths already in context").option("--chat-files <paths>","comma-separated chat paths").option("--line-numbers","include source line numbers").option("--tree","include repository tree").option("--output <path>","write a repo-relative retained artifact").option("--diff-scope <scope>","unstaged, staged, all, or compare").option("--from <ref>","comparison start revision").option("--to <ref>","comparison end revision").addOption(k("--log <count>","include recent commits","integer")).action((...c)=>o(c.at(-1))),S(n,"read <artifact>","read a retained context artifact",o).addOption(k("--start <line>","first line","integer")).addOption(k("--end <line>","last line","integer")),S(n,"grep <artifact> <regex>","search an artifact with a linear-time regex",o).option("--flags <flags>","regular-expression flags").addOption(k("--limit <count>","match limit","integer"));let r=e.command("compress").description("compress already-produced output");r.argument("[input]").option("--strategy <strategy>","smart, head-tail, or errors-only").option("--adapter <adapter>","structured output adapter").addOption(k("--lines <count>","hard line budget","integer")).addOption(k("--max-output-bytes <count>","hard output byte budget","integer")).addOption(k("--context <count>","signal context lines","integer")).addOption(k("--exit-code <code>","source process exit code","integer")).option("--signal <name>","source process termination signal").addOption(k("--max-input-bytes <count>","hard input byte limit","integer")).option("--no-retain","do not retain raw drill-down data").option("--no-redact","disable secret redaction").action((...c)=>o(c.at(-1)));for(let c of["read","diagnose"])S(r,`${c} <artifact>`,`${c} a retained output artifact`,o).addOption(k("--start <line>","first line","integer")).addOption(k("--end <line>","last line","integer")).addOption(k("--max-output-bytes <count>","hard output byte budget","integer")).addOption(k("--context <count>","diagnostic context lines","integer")).addOption(k("--limit <count>","diagnostic result limit","integer")).addOption(k("--offset <count>","resume retained diagnostics at offset","integer")).option("--raw","return unredacted retained bytes");S(r,"delete <artifact>","delete a retained output artifact",o)}function Oe(e=()=>{}){let o=new rn("knodin").description("knodin \u2014 source-evidenced local code intelligence with known bounds").showHelpAfterError().showSuggestionAfterError().passThroughOptions(!1).allowExcessArguments(!1).exitOverride();o.configureOutput({writeErr:()=>{}}),un(o),o.addHelpText("after","\nMCP callers use the `prs` operation and corresponding operations on the single `knodin` gateway.\nknodin update status|check|explain|apply|rollback consumes only threshold-signed metadata.\n"),S(o,"init","initialize graph, lifecycle hooks, and agent integration",e).option("--scope <scope>","personal, team, or cli-only").addHelpText("after",`
9
+ Usage: knodin init [--scope personal|team|cli-only] [--json]
10
+
11
+ personal recommended; local/excluded agent adapters and a clean Git status
12
+ team commit-ready shared agent configuration
13
+ cli-only no agent discovery; AI agents will not know to invoke knodin
14
+
15
+ Change later with \`knodin configure --scope <scope>\`.
16
+ Tracked files are never added to Git exclude files.
17
+ `),S(o,"configure","change or inspect agent integration",e).option("--scope <scope>","personal, team, or cli-only").option("--status","inspect configuration without changing it"),S(o,"index [files...]","index a repository or selected files",e).option("--clean","rebuild selected index state").option("--force","force clean indexing").option("--scip <file>","opt in to a bounded local SCIP protobuf import"),S(o,"doctor","diagnose installation, clients, hooks, graph, and updates",e).option("--client <client>","claude, codex, gemini, or antigravity"),S(o,"status","report graph, lifecycle, integration, and update state",e).option("--deep","run a full graph audit").option("--watch","stream status snapshots").addOption(k("--interval <seconds>","watch interval","number")),S(o,"wait","wait for current graph evidence",e).option("--fresh","wait for a fresh graph").addOption(k("--timeout <seconds>","deadline","number")),S(o,"repair","audit and reconcile graph state",e).option("--plan","report the repair plan without mutation").option("--jsonl","stream JSONL progress").option("--progress <mode>","tty, jsonl, plain, or none").option("--progress-interval <duration>","progress interval such as 750ms, 30s, or 2m"),S(o,"serve","run the one-tool MCP gateway on stdio",e),S(o,"version","print the installed knodin version",e),S(o,"hook-refresh <kind> [values...]","internal Git lifecycle refresh",e),S(o,"refresh-artifacts [event]","refresh external graph artifacts",e),pn(o,e),mn(o,e),fn(o,e);let n=o.command("system").description("inspect declared multi-repository systems");S(n,"list","list configured systems",e);for(let i of["show","validate","query"]){let g=S(n,`${i} <system-id>`,`${i} one configured system`,e);i==="query"&&g.option("--allow-partial","return healthy components")}return o.command("update").description("inspect or apply threshold-signed updates").allowExcessArguments(!1).addArgument(new he("<action>").choices(["status","check","explain","apply","rollback"])).action((...i)=>e(i.at(-1))),S(o,"docs <topic>","read canonical product documentation",e),S(o,"prs [action]","audit pull requests, reviews, and checks",e).option("--state <state>","open, merged, closed, or all").addOption(k("--limit <count>","pull-request limit","integer")).option("--branches <pattern>","branch-name pattern").option("--range <range>","pull-request number range").option("--base <ref>","audit base revision").option("--head <ref>","audit head revision").option("--expected-login <login>","expected GitHub identity"),S(o,"worktrees [action] [path]","inspect, reconcile, or remove managed worktrees",e).option("--dry-run","report removal without mutation"),o.command("telemetry").description("manage local opt-in ROI telemetry").allowExcessArguments(!1).addArgument(new he("<action>").choices(["status","report","export","clear"])).option("--input <path>","telemetry input path").option("--output <path>","dashboard or evidence-bundle output path").addOption(k("--retention-days <count>","retention window in days","integer")).action((...i)=>e(i.at(-1))),o.command("diagnostics").description("manage local troubleshooting diagnostics and support bundles").allowExcessArguments(!1).addArgument(new he("<action>").choices(["enable","status","collect","inspect","clear","disable"])).addArgument(new he("[bundle]")).addOption(k("--retention-days <count>","local event retention in days","integer")).option("--since <duration>","collection window, such as 24h or 7d").option("--output <path>","repository-contained .json.gz bundle path").action((...i)=>e(i.at(-1))),o}function hn(e){return e.flatMap(o=>Array.isArray(o)?o:[o]).filter(o=>typeof o=="string")}function Jt(e){let o,n=Oe(l=>{o=l});if(n.parse([process.execPath,"knodin",...e]),!o)return{commandPath:[],positionals:[],options:n.opts()};let r=o,c=[];for(let l=r;l?.parent;l=l.parent)c.unshift(l.name());return{commandPath:c,positionals:hn(r.processedArgs),options:r.optsWithGlobals()}}function ge(e,o){let r=Oe();for(let l of e){let i=r.commands.find(g=>g.name()===l);if(!i)throw new Error(`knodin: unknown help command ${e.join(" ")}`);r=i}r.configureHelp({helpWidth:Math.max(60,Math.floor(o??100)-1)});let c="";return r.configureOutput({writeOut:l=>c+=l,writeErr:()=>{}}),r.outputHelp(),Vt(c,o)}function Yt(e){let o=Oe(),n=[],r=o;for(let c=0;c<e.length;c++){let l=e[c];if(l==="-h"||l==="--help")break;if(l.startsWith("-")){let g=l.split("=",1)[0],b=r,$;for(;b&&!$;)$=b.options.find(d=>d.short===g||d.long===g),b=b.parent;$?.required&&!l.includes("=")&&c++;continue}let i=r.commands.find(g=>g.name()===l);if(!i)break;r=i,n.push(l)}return n}import{execFileSync as gn}from"node:child_process";import wn from"node:crypto";import K from"node:fs";import j from"node:path";var yn=32768,kn=4096,_e=20;function P(e){return e.replaceAll("&","&amp;").replaceAll("<","&lt;").replaceAll(">","&gt;").replaceAll('"',"&quot;")}function bn(e){try{return gn("git",["rev-parse","HEAD"],{cwd:e,encoding:"utf8",stdio:["ignore","pipe","ignore"]}).trim()}catch{return"unavailable"}}function xn(e,o){if(!o||j.extname(o).toLowerCase()!==".html")throw new Error("knodin visualize: --output must be a repository-relative .html path");let n=j.resolve(e,o);if(n===e||!n.startsWith(`${e}${j.sep}`))throw new Error("knodin visualize: output path must stay inside the repository");let r=K.lstatSync(n,{throwIfNoEntry:!1});if(r?.isSymbolicLink())throw new Error("knodin visualize: output path may not be a symlink");if(r&&!K.realpathSync(n).startsWith(`${e}${j.sep}`))throw new Error("knodin visualize: output path may not follow a symlink outside the repository");let c=j.dirname(n);for(;!K.existsSync(c)&&c!==e;)c=j.dirname(c);let l=K.realpathSync(c);if(l!==e&&!l.startsWith(`${e}${j.sep}`))throw new Error("knodin visualize: output path may not traverse a symlink outside the repository");return{absolute:n,relative:j.relative(e,n).replaceAll(j.sep,"/")}}function vn(e){let o=e.communities.map(c=>{let l=(c.files??[]).slice(0,12).join(", ")||"no file detail returned",i=(c.symbols??[]).slice(0,12).join(", ")||"no symbol detail returned";return`<details><summary>${P(c.name)} <span>${c.size} symbols</span></summary><p>Cohesion: ${c.cohesion.toFixed(3)}</p><p><strong>Files:</strong> ${P(l)}</p><p><strong>Symbols:</strong> ${P(i)}</p></details>`}).join(`
18
+ `),n=(c,l)=>l.map(i=>{let g="degree"in i?`degree ${i.degree}`:`betweenness ${i.betweenness.toFixed(3)}`;return`<li><strong>${c}</strong> ${P(i.symbol)} \u2014 ${P(g)}; ${P(i.filePath||"unresolved file")} (${P(i.communityId??"unassigned")})</li>`}).join(`
19
+ `),r=c=>c.map(l=>`<li data-evidence="${l.label}"><code>${P(l.from)}</code> \u2192 <code>${P(l.to)}</code> <span>${P(l.kind)}</span><br><small>${l.label==="exact"?"Source evidence":"Heuristic source evidence"}: ${P(l.evidence)}</small></li>`).join(`
20
+ `)||"<li>No bounded source-evidenced relationships were returned.</li>";return`<!doctype html>
21
+ <html lang="en"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width, initial-scale=1"><title>knodin architecture visualization</title><style>body{font:16px system-ui,sans-serif;margin:2rem auto;max-width:74rem;color:#172033;background:#f6f8fb;line-height:1.45}section{background:#fff;border:1px solid #d9dee7;border-radius:.55rem;padding:1rem 1.25rem;margin:1rem 0}details{border-top:1px solid #e5e9ef;padding:.6rem 0}summary{cursor:pointer;font-weight:650}summary span,small{color:#526170}li{margin:.55rem 0}code{background:#edf1f6;padding:.1rem .3rem;border-radius:.2rem;overflow-wrap:anywhere}[data-evidence=heuristic]{border-left:3px solid #d18a00;padding-left:.55rem}</style></head>
22
+ <body><h1>knodin local architecture visualization</h1><p>Generated locally from the persisted knodin graph. Source commit <code>${P(e.commit)}</code>; Index freshness <code>${P(e.freshness)}</code>; ${e.truncated?"some lists are bounded by the artifact budget.":"all displayed lists fit the requested artifact budget."}</p>
23
+ <section><h2>Subsystem drill-down</h2>${o||"<p>No communities were returned.</p>"}</section>
24
+ <section><h2>Hub and bridge inspection</h2><ul>${n("Hub",e.hubs)}${n("Bridge",e.bridges)}</ul></section>
25
+ <section><h2>Architecture relationships</h2><ul>${r(e.architectureEdges)}</ul></section>
26
+ <section><h2>Call-flow navigation</h2><p>Static downstream traversal from <code>${P(e.entry)}</code>, depth ${e.depth}; this is not a runtime trace.</p><ul>${r(e.callFlowEdges)}</ul></section>
27
+ </body></html>
28
+ `}async function Qt(e,o,n){let r=K.realpathSync(o),c=xn(r,n.outputPath),l=n.entry.trim();if(!l)throw new Error("knodin visualize: an entry selector is required");let i=n.depth??3;if(!Number.isInteger(i)||i<1||i>6)throw new Error("knodin visualize: --depth must be an integer from 1 through 6");let g=n.byteBudget??yn;if(!Number.isInteger(g)||g<kn||g>65536)throw new Error("knodin visualize: --max-bytes must be an integer from 4096 through 65536");let[b,$]=await Promise.all([e.map(r,"standard",{topN:12,sort:"name"}),e.query("traverse",l,r,void 0,_e,i,"standard",n.selector,void 0,{direction:"downstream",relationKinds:["call"],includeDataFlow:!0})]);if($.ambiguity||$.count===0)throw new Error("knodin visualize: entry selector did not resolve to a traversable symbol");let d=b.edges.filter(_=>!!_.sourceEvidence).slice(0,_e).map(_=>({from:_.from,to:_.to,kind:_.kind,evidence:_.sourceEvidence??"",label:_.confidenceLabel==="heuristic"||_.provenance!=="EXTRACTED"?"heuristic":"exact"})),O=($.edges??[]).slice(0,_e).map(_=>({from:_.from,to:_.to,kind:_.kind??"call",evidence:_.dataFlow?.evidence??`${_.fromFile??"unresolved file"}:${_.line??"unknown line"}`,label:_.dataFlow?.heuristic||_.provenance==="INFERRED"?"heuristic":"exact"})),C=b.communities.slice(0,12),T=b.hubs.slice(0,10),u=b.bridges.slice(0,10),x=d,w=O,L=!!$.truncated,D="";for(;D=vn({commit:bn(r),freshness:b.staleness??"unknown",communities:C,hubs:T,bridges:u,architectureEdges:x,callFlowEdges:w,entry:l,depth:i,truncated:L}),!(Buffer.byteLength(D)<=g);)if(L=!0,x.length)x=x.slice(0,-1);else if(w.length)w=w.slice(0,-1);else if(C.length)C=C.slice(0,-1);else if(T.length)T=T.slice(0,-1);else if(u.length)u=u.slice(0,-1);else throw new Error("knodin visualize: budget is too small for visualization metadata");return K.mkdirSync(j.dirname(c.absolute),{recursive:!0}),K.writeFileSync(c.absolute,D),{outputPath:c.relative,bytes:Buffer.byteLength(D),sha256:wn.createHash("sha256").update(D).digest("hex"),byteBudget:g,truncated:L,indexFreshness:b.staleness??"unknown",callFlow:{entry:l,depth:i,edgeCount:w.length,truncated:!!$.truncated}}}function we(e){let o=e.indexOf("--scope");if(o>=0){let r=e[o+1];if(!r)throw new Error("knodin: --scope requires a value");return se(r)}let n=e.find(r=>r.startsWith("--scope="));return n?se(n.slice(8)):null}async function Rn(e,o){let n=we(e);if(n)return n;if(xt(o))return process.stderr.write(`[init:scope] Detected tracked knodin team integration; preserving team scope
29
+ `),"team";if(!process.stdin.isTTY||!process.stderr.isTTY)return"personal";let r=$n.createInterface({input:process.stdin,output:process.stderr});try{let c=await r.question(["How should knodin integrate with coding agents?"," 1. Personal (recommended) \u2014 all detected agents; Git stays clean"," 2. Team \u2014 create commit-ready shared configuration"," 3. CLI-only \u2014 agents will not discover or invoke knodin automatically","Select [1]: "].join(`
30
+ `));if(!c.trim()||c.trim()==="1")return"personal";if(c.trim()==="2")return"team";if(c.trim()==="3"){let l=await r.question("CLI-only requires manual knodin commands. Continue? [y/N] ");if(!/^y(?:es)?$/i.test(l.trim()))throw new Error("knodin init: CLI-only selection cancelled");return"cli-only"}return se(c.trim())}finally{r.close()}}function Xt(e){let o=bt(e)?.agents??[],n=ce(e)?.agents??[];return[...new Set([...Q(),...o,...n])]}function Sn(e){let o=e.paths.scope==="cli-only"?"CLI-only \u2014 AI agents are not configured to discover knodin":e.paths.agentIntegration.configured.length>0?`${e.paths.scope} \u2014 ${e.paths.agentIntegration.configured.join(", ")}`:`${e.paths.scope} \u2014 no supported coding agents detected`,n=e.paths.agentIntegration.failed.map(({agent:c,message:l})=>`
31
+ Agent warning (${c}): ${l}`).join(""),r=e.paths.lifecycleRefresh.state==="fresh"?"fresh":`still running (${e.paths.lifecycleRefresh.queuedEvents} queued event(s)); run \`knodin wait --fresh\``;return`${e.message}
32
+ Graph: ${e.paths.database}
33
+ Git refresh: ${e.paths.gitHooks.length} lifecycle hooks installed; ${r}
34
+ Agent integration: ${o}${n}
35
+ Background indexer: ${e.paths.backgroundIndexer}
36
+ `}function Cn(e){return e.scope==="unconfigured"?"Agent integration: unconfigured.\nWarning: AI agents will not discover or invoke knodin automatically. Run `knodin configure --scope personal`.\n":e.scope==="cli-only"?"Agent integration: CLI-only.\nWarning: AI agents will not discover or invoke knodin automatically. Run `knodin configure --scope personal` or `--scope team` to enable them.\n":e.scope==="repository-detected"?`Agent integration: repository-detected${e.agents.length>0?` (${e.agents.join(", ")})`:""}.
37
+ Local scope receipt: missing; managed repository integration is present.
38
+ `:`Agent integration: ${e.scope}${e.agents.length>0?` (${e.agents.join(", ")})`:""}.
39
+ `}function On(e){let o="none detected";e.paths.agentIntegration.configured.length>0?o=e.paths.agentIntegration.configured.join(", "):e.paths.scope==="cli-only"&&(o="none (CLI-only)");let n=e.paths.agentIntegration.failed.map(({agent:i,message:g})=>`
40
+ Agent warning (${i}): ${g}`).join(""),r=e.paths.lifecycleRefresh.state==="fresh"?"fresh":`still running (${e.paths.lifecycleRefresh.queuedEvents} queued event(s))`,c=e.paths.filesystemChanges.map(({path:i,action:g})=>`
41
+ Filesystem ${g}: ${i}`).join(""),l=e.paths.externalConfigurationOutcomes.map(({system:i,state:g})=>`
42
+ External configuration outcome: ${i} ${g} (external mutation not locally observable)`).join("");return`${e.message}
43
+ Agent integration: ${e.paths.scope} \u2014 ${o}${n}${c}${l}
44
+ Graph initialization: unchanged
45
+ Lifecycle refresh: ${r}
46
+ Next: ${e.nextAction}
47
+ `}function _n(e){let o=e.after.coverage;return e.cancelled?`Repair paused: ${e.remaining??0} file(s) remaining. Run \`knodin repair\` again to finish.
48
+ `:e.verified?e.lifecycle?.status==="degraded"?`Repair verified: graph is healthy (${o.indexedFiles} indexed files, ${o.filesWithSymbols} files with symbols), but lifecycle routing is degraded. Run \`knodin init\`, then \`knodin status\`.
49
+ `:`Repair verified: graph is healthy (${o.indexedFiles} indexed files, ${o.filesWithSymbols} files with symbols).
50
+ `:"Repair finished with remaining issues. Run `knodin status --deep` for details.\n"}function In(e){if(e.indexed.length===0&&e.unchanged.length>0){let r=e.unchanged.length===1?"file":"files";return`Graph already current: ${e.unchanged.length.toLocaleString()} requested ${r} needed no work; health verified.
51
+ `}let o=e.unchanged.length>0?`; ${e.unchanged.length.toLocaleString()} already current`:"",n=e.indexed.length===1?"file":"files";return`Index complete: ${e.indexed.length.toLocaleString()} ${n} indexed${o}; graph health verified.
52
+ `}function Nn(e){let o=e.verification.missing.files[0]??e.verification.missing.records[0],n=o?` First issue: ${o}.`:"";return`knodin index: requested work completed, but ${e.verification.issueCount.toLocaleString()} graph issue(s) remain.${n} Run \`knodin repair\`.
53
+ `}function Tn(e){let o=`${e.coverage.sourceFiles} source files, ${e.coverage.indexedFiles} indexed files, ${e.coverage.filesWithSymbols} files with symbols`;if(e.status==="indexing"&&e.activity){let C=e.activity.phaseTotal===void 0?"":` ${e.activity.phaseCompleted}/${e.activity.phaseTotal}`,T=Math.max(0,Math.floor((Date.now()-Date.parse(e.activity.startedAt))/1e3));return`Graph update in progress: ${e.activity.phase}${C} \u2014 ${e.activity.message} (${T}s elapsed; ${o}).
54
+ `}let n=e.integration,r=n?`Agent integration: ${n.scope}${n.agents.length>0?` (${n.agents.join(", ")})`:""}.
55
+ `:"Agent integration: unconfigured. AI agents will not discover knodin automatically; run `knodin configure --scope personal`.\n",c=e.lifecycle?e.lifecycle.status==="healthy"?`Hooks: installed and executable.
56
+ `:`Lifecycle refresh: ${e.lifecycle.status}; ${e.lifecycle.issues[0]??"refresh capability is not verified"}. Run \`knodin init\`.
57
+ `:"",l=C=>C?.slice(0,12)??"unknown",i=e.freshness.commitDistance===null?"":` by ${e.freshness.commitDistance.toLocaleString()} commit(s)`,g=`Freshness: ${e.freshness.state}; indexed ${l(e.freshness.indexedHead)}, current ${l(e.freshness.currentHead)} (${e.freshness.commitRelation}${i}); ${e.freshness.workingTree.pendingPaths??"unknown"} pending path(s).
58
+ Last successful refresh: ${e.freshness.lastSuccessfulRefresh??"never"}.
59
+ `;if(e.status==="healthy")return`Graph content is healthy: ${o} (knodin ${e.version}; ${e.verification.mode}).
60
+ ${g}${c}${r}`;if(e.status==="stale")return`Graph content is intact but evidence is stale (${o}).
61
+ ${g}${c}${r}Run \`knodin wait --fresh\` or issue a graph query to reconcile bounded drift.
62
+ `;let b=e.missing.files.length+e.missing.records.length,$=e.missing.files[0]??e.missing.records[0],d=$?` First issue: ${$}.`:"",O=e.lifecycle?.status==="degraded"&&e.missing.records.every(C=>e.lifecycle?.issues.includes(C))?"Run `knodin init`.":"Run `knodin repair`.";return`Graph or lifecycle needs repair: ${b} issue(s) found (${o}).${d} ${O}
63
+ ${c}${r}`}function ie(e){return e.replace(/([a-z])([A-Z])/g,"$1 $2")}function Ae(e,o="",n){if(e===null||typeof e!="object")return[`${o}${n?`${ie(n)}: `:""}${String(e)}`];if(Array.isArray(e)){if(e.length===0)return n?[]:[`${o}(none)`];let l=n?[`${o}${ie(n)}:`]:[];for(let i of e)i===null||typeof i!="object"?l.push(`${o} - ${String(i)}`):l.push(...Ae(i,`${o} - `));return l}let r=Object.entries(e).filter(([l])=>l!=="responseBudget"),c=n?[`${o}${ie(n)}:`]:[];for(let[l,i]of r)i===null||typeof i!="object"?c.push(`${o}${n?" ":""}${ie(l)}: ${String(i)}`):c.push(...Ae(i,`${o}${n?" ":""}`,l));return c}function oe(e,o){return`${[`${ie(e)}:`,...Ae(o," ")].join(`
64
+ `)}
65
+ `}function An(e){let n=`Compressed output; ${e.omittedRanges.reduce((l,i)=>l+i.lineCount,0)} line(s) omitted.`;e.status==="insufficient-budget"?n=`INSUFFICIENT BUDGET: ${e.fidelity.unpreservedSignals.length} detected signal line(s) are available only through retained drill-down.`:e.complete&&(n="Complete output; nothing omitted.");let r=e.artifact.retained?` Retained artifact: ${e.artifact.id} (${e.artifact.path}).`:" Raw retention disabled; omitted regions cannot be retrieved.";return`${e.content?`${e.content}
66
+ `:""}---
67
+ ${n} ${e.output.lines}/${e.input.lines} lines, ${e.output.bytes}/${e.input.bytes} bytes; exit=${e.exit.code??"unknown"}, signal=${e.exit.signal??"none"}.${r}
68
+ `}function Pn(e){let o=e.content?`${e.content}
69
+ `:"",n=e.raw?"UNREDACTED raw view":`${e.secretRedactions} secret(s) redacted`;return`${o}---
70
+ Artifact ${e.artifactId}, lines ${e.range.startLine}-${e.range.endLine} of ${e.range.totalLines}; ${e.bytes}/${e.byteBudget} bytes; ${n}.
71
+ `}function Ne(e,o){if(o.length===0)return`${e}: none`;let n=o.map(({symbol:r,file:c,line:l})=>{let i=c?` (${c}:${l??"?"})`:"";return`${r}${i}`});return`${e}: ${n.join(", ")}`}function Ln(e){let o=[`Failure diagnosis: ${e.status}; ${e.diagnostics.length} resolved, ${e.unresolved.length} unresolved.`,`Freshness: ${e.freshness.state}; indexed=${e.freshness.indexedHead??"unknown"}; current=${e.freshness.currentHead??"unknown"}.`];for(let n of e.diagnostics){let r=`${n.file}:${n.reference.line??"?"}`,c=n.owner?.identity?` [${n.owner.identity}]`:"",l=`${n.owner?.symbol??"no owning symbol"}${c}`,i=n.package?`${n.package.name??"unnamed"} (${n.package.kind}, ${n.package.manifest})`:"none";o.push("",`${r} -> ${l}`,`Package: ${i}`,Ne("Tests",n.tests),Ne("Upstream",n.upstream),Ne("Downstream",n.downstream));let g=e.contextBundle.snippets.find(({file:b})=>b===n.file);g&&o.push(g.content)}for(let n of e.unresolved){let r=n.candidates?` (${n.candidates.join(", ")})`:"";o.push("",`Unresolved ${n.reference.path}: ${n.reason}${r}`)}return o.push("",...e.limitations.map(n=>`Limitation: ${n}`)),`${o.join(`
72
+ `)}
73
+ `}async function qn(e){let o=[],n=0;for await(let r of process.stdin){let c=Buffer.isBuffer(r)?r:Buffer.from(String(r));if(n+=c.byteLength,n>e)throw new Error(`knodin compress: input exceeded bounded retention limit of ${e} bytes`);o.push(c)}return Buffer.concat(o).toString("utf-8")}var Mn="\r\x1B[2K",Fn=2e3;function tn(e,o){let n=En(import.meta.url),r=n.endsWith(".ts")?".ts":".js",c=W.dirname(n);for(;!Te.existsSync(W.join(c,"package.json"))&&c!==W.parse(c).root;)c=W.dirname(c);let l=r===".ts"?W.resolve(W.dirname(n),`../src/${e}.ts`):W.resolve(c,`dist/src/${e}.js`),i=r===".ts"&&W.basename(process.execPath).startsWith("node")?[...process.execArgv,l]:[l],g=en(process.execPath,i,{stdio:["pipe","ignore","inherit"],env:{...process.env,KNODIN_FORCE_PROGRESS_TTY:"1"}}),b=!1,$=new Promise(O=>{g.once("close",()=>O()),g.once("error",()=>O())});g.once("error",()=>{b=!0}),g.stdin.once("error",()=>{b=!0});let d=O=>{if(!(b||g.stdin.destroyed))try{g.stdin.write(`${JSON.stringify(O)}
74
+ `)}catch{b=!0}};return{start:()=>d(o),onProgress:O=>d({type:"progress",event:O}),stop:async()=>{d({type:"stop"}),g.stdin.end();let O=C=>new Promise(T=>{let u=!1,x=L=>{u||(u=!0,clearTimeout(w),T(L))},w=setTimeout(()=>x(!1),C);w.unref(),$.then(()=>x(!0))});await O(Fn)||(g.kill("SIGKILL"),await O(500)||g.unref()),process.stderr.write(Mn)}}}function Zt(e="init"){if(process.stderr.isTTY!==!0){let o=vt({stderr:process.stderr,operation:e});return{start:()=>o.start(),onProgress:n=>o.onProgress(n),stop:async()=>o.stop()}}return tn("init-progress-worker",{type:"start",operation:e})}async function jn(){let e=process.argv.slice(2);if(e[0]==="__repository-init-worker"){if(e.length!==2||!W.isAbsolute(e[1]))throw new Error("knodin internal repository init worker requires one absolute path");let t=await Te.promises.realpath(e[1]),s=setInterval(()=>{process.send?.({type:"rss",rssBytes:process.memoryUsage().rss})},50);s.unref(),process.send?.({type:"rss",rssBytes:process.memoryUsage().rss});let a=B();try{let p=H(t),h=(await ee([t],{depth:0,include:[t],command:ye(process),index:m=>a.index(m),status:m=>a.status(m),agents:Q(),indexMode:m=>te(p,m)})).results.find(({repository:m})=>m===t);if(!h)throw new Error("repository init worker produced no repository result");process.send?.({type:"rss",rssBytes:process.memoryUsage().rss}),process.send?.({type:"result",result:h}),h.status==="failed"&&(process.exitCode=1)}finally{clearInterval(s),await a.close()}return}let{rest:o}=Be(e),[n,...r]=o;if(!n||n==="-h"||n==="--help"){process.stdout.write(ge([],process.stdout.isTTY?process.stdout.columns:void 0));return}if(n==="-v"||n==="--version"||n==="version"){process.stdout.write(`${F}
75
+ `);return}if(e.some(t=>t==="-h"||t==="--help")){process.stdout.write(ge(Yt(e),process.stdout.isTTY?process.stdout.columns:void 0));return}let c=Jt(e),l=t=>t.slice(2).replace(/-([a-z])/g,(s,a)=>a.toUpperCase()),i=t=>{let s=c.options[l(t)];return typeof s=="string"||typeof s=="number"?String(s):void 0},g=i("--repo"),b=ye(process),$=c.options.json===!0||n==="hook-refresh",d=n==="repair"?r:r.filter(t=>t!=="--json"),O={identity:i("--identity"),file:i("--file"),kind:i("--kind"),toIdentity:i("--to-identity"),toFile:i("--to-file"),toKind:i("--to-kind")},C={bytes:i("--bytes")?Number(i("--bytes")):void 0,tokens:i("--tokens")?Number(i("--tokens")):void 0,items:i("--items")?Number(i("--items")):void 0};for(let[t,s,a]of[["--bytes",C.bytes,256],["--tokens",C.tokens,64],["--items",C.items,1]])if(s!==void 0&&(!Number.isInteger(s)||s<a))throw new Error(`knodin: ${t} must be an integer >= ${a}`);if(n==="init"){we(r);let t=r.filter((s,a)=>!(s==="--json"||s.startsWith("--scope=")||s==="--scope"||r[a-1]==="--scope"));if(t.length>0)throw new Error(`knodin init: unknown option: ${t[0]}`)}if(n==="configure"){let t=r.includes("--status"),s=we(r),a=r.filter((p,f)=>!(p==="--json"||p==="--status"||p.startsWith("--scope=")||p==="--scope"||r[f-1]==="--scope"));if(a.length>0)throw new Error(`knodin configure: unknown option: ${a[0]}`);if(t&&s)throw new Error("knodin configure: --status and --scope are mutually exclusive");if(!t&&!s)throw new Error("knodin configure requires --scope personal|team|cli-only or --status")}if(n==="serve"){let{startServer:t}=await import("../chunks/server-7EDF4CBY.js");await t();return}if(n==="fleet"){if(g!==void 0)throw new Error("knodin fleet init accepts discovery roots, not --repo");let t=Ft(r,process.cwd());t.json||process.stderr.write("warning: `knodin fleet init` is deprecated; use `knodin repos init --linked-worktrees=skip|include` (alias retained for two minor releases)\n");let s=B(),a=H(t.roots[0]??process.cwd()),p=await ee(t.roots,{command:b,depth:t.depth,dryRun:t.dryRun,worktrees:t.worktrees,index:f=>s.index(f),status:f=>s.status(f),agents:Q(),indexMode:f=>te(a,f),isolatedInitialize:f=>Ee({repository:f,command:b})});await s.close(),process.stdout.write(t.json?`${JSON.stringify(p)}
76
+ `:pe(p)),process.exitCode=p.exitCode;return}if(n==="repos"){if(g!==void 0)throw new Error("knodin repos accepts discovery roots, not --repo");let t=Mt(r,process.cwd());if(t.command==="discover"){let m=await ue(t.roots,{depth:t.depth,linkedWorktrees:t.linkedWorktrees}),y=B(),v=H(t.roots[0]??process.cwd()),R=[],E=t.signals?m.repositories.slice(0,Pt(t)):m.repositories;for(let N of E){let M=await Z(N,{status:async Y=>A(Y,await y.status(Y,{audit:"cached"})),systemMemberships:Y=>X(v,Y)});R.push(qt(M,t.signals,t.signals?await Lt(N.path):{}))}await y.close();let I={schemaVersion:1,command:"discover",linkedWorktrees:t.linkedWorktrees,...m,repositories:R},q=t.signals?le(I,"repositories:discover",{bytes:t.byteBudget,tokens:t.tokenBudget,items:t.itemBudget},{bytes:65536,tokens:16384,items:100}):I;process.stdout.write(`${JSON.stringify(q,null,t.json?0:2)}
77
+ `),process.exitCode=m.issues.length>0?1:0;return}if(t.command==="init"){let m=H(t.roots[0]??process.cwd());if(t.dryRun){let N=await ee(t.roots,{command:b,depth:t.depth,dryRun:!0,worktrees:t.linkedWorktrees,manifestPath:t.manifestPath,include:t.include,exclude:t.exclude,index:async()=>{},status:async()=>{throw new Error("dry-run must not inspect graph databases")},agents:Q(),indexMode:U=>te(m,U)}),M=N.results.reduce((U,Le)=>{let qe=Le.plannedStatus??Le.status;return U[qe]=(U[qe]??0)+1,U},{}),Y={schemaVersion:1,command:"init",dryRun:!0,graphDatabasesOpened:0,modelsLoaded:0,selectedRepositories:N.results.filter(({message:U})=>U.startsWith("dry-run:")).length,planned:M,...N,repositories:[]};process.stdout.write(t.json?`${JSON.stringify(Y)}
78
+ `:pe(N)),process.exitCode=N.exitCode;return}let y=B(),v=await ee(t.roots,{command:b,depth:t.depth,dryRun:!1,worktrees:t.linkedWorktrees,manifestPath:t.manifestPath,include:t.include,exclude:t.exclude,index:N=>y.index(N),status:N=>y.status(N),agents:Q(),indexMode:N=>te(m,N),isolatedInitialize:N=>Ee({repository:N,command:b})}),R=await ue(t.roots,{depth:t.depth,linkedWorktrees:t.linkedWorktrees}),E=[],I=new Set(v.results.map(({repository:N})=>N));for(let N of R.repositories.filter(({path:M})=>I.has(M)))E.push(await Z(N,{status:async M=>A(M,await y.status(M,{audit:"cached"})),systemMemberships:M=>X(m,M)}));await y.close();let q={schemaVersion:1,command:"init",...v,repositories:E};process.stdout.write(t.json?`${JSON.stringify(q)}
79
+ `:pe(v)),process.exitCode=v.exitCode;return}if(t.command==="search"){let m=B(),y=H(t.roots[0]??process.cwd()),v=await jt(t.roots,t.query??"",{depth:t.depth,linkedWorktrees:t.linkedWorktrees,include:t.include,exclude:t.exclude,allowPartial:t.allowPartial,itemBudget:t.itemBudget,byteBudget:t.byteBudget,tokenBudget:t.tokenBudget,cursor:t.cursor,status:async R=>A(R,await m.status(R,{audit:"cached"})),search:(R,E,I,q)=>m.search(R,E,I,{offset:q,includeSource:!0,federate:!1}),systemMemberships:R=>X(y,R)});await m.close(),process.stdout.write(`${JSON.stringify(v,null,t.json?0:2)}
80
+ `),process.exitCode=v.status==="unavailable"?1:0;return}let s=await ue(t.roots,{depth:t.depth,linkedWorktrees:t.linkedWorktrees}),a=B(),p=H(t.roots[0]??process.cwd()),f=[];for(let m of s.repositories)try{if(t.command==="doctor"){let y=A(m.path,await a.status(m.path,{audit:"deep"})),v=await Z(m,{status:async()=>y,systemMemberships:R=>X(p,R)});f.push({...v,diagnosis:await ae(m.path,{currentVersion:F,runtimeCommand:b,graph:y})})}else f.push(await Z(m,{status:async y=>A(y,await a.status(y,{audit:"cached"})),systemMemberships:y=>X(p,y)}))}catch(y){f.push({path:m.path,status:"unknown",error:y instanceof Error?y.message:String(y)})}await a.close();let h={schemaVersion:1,command:t.command,linkedWorktrees:t.linkedWorktrees,repositories:f,issues:s.issues,emptyRoots:s.emptyRoots,staleLinkedWorktrees:s.staleLinkedWorktrees};process.stdout.write(`${JSON.stringify(h,null,t.json?0:2)}
81
+ `),process.exitCode=s.issues.length>0||f.some(m=>"health"in m&&m.health!=="healthy"||"status"in m&&m.status==="unknown")?1:0;return}if(n==="docs"){if(g!==void 0)throw new Error("knodin docs does not accept --repo");let[t,...s]=r;if(s.length>0)throw new Error(`knodin docs: unknown argument ${s[0]}`);if(!t||t==="list"){process.stdout.write(`${it().join(`
82
+ `)}
83
+ `);return}let a=st(t);if(!a)throw new Error(`knodin docs: unknown topic ${t}`);process.stdout.write(a.endsWith(`
84
+ `)?a:`${a}
85
+ `);return}if(n==="update"){if(g!==void 0)throw new Error("knodin update does not accept --repo");let[t,...s]=d;if(!t||!["status","check","explain","apply","rollback"].includes(t))throw new Error("knodin update requires status, check, explain, apply, or rollback");if(s.length>0)throw new Error(`knodin update ${t}: unknown argument ${s[0]}`);let a=re(b),p=async y=>{let[v,...R]=y;if(!v)return{status:1,stdout:"",stderr:"missing manager command"};let E=Ie(v,R,{encoding:"utf-8",timeout:12e4,maxBuffer:4*1024*1024,stdio:["ignore","pipe","pipe"]});return{status:E.status,stdout:E.stdout??"",stderr:E.stderr??E.error?.message??""}},f=async()=>{let[y,...v]=b;if(!y)return!1;let R=Ie(y,[...v,"--version"],{encoding:"utf-8",timeout:1e4,stdio:["ignore","pipe","pipe"]});return R.status===0&&/^\d+\.\d+\.\d+/.test(R.stdout.trim())},h={currentVersion:F,installMethod:a,env:process.env,runManager:p,healthCheck:f},m;if(t==="status")m=ke(h);else if(t==="check")try{m=await be(h)}finally{process.env.KNODIN_UPDATE_BACKGROUND==="1"&&ct()}else t==="explain"?m=dt(h):t==="apply"?m=await lt(h):m=await ut(h);process.stdout.write($?`${JSON.stringify(m)}
86
+ `:oe("update",m));return}if(process.stdout.isTTY&&at({currentVersion:F,installMethod:re(b),env:process.env})){let[t,...s]=b;t&&en(t,[...s,"update","check","--json"],{detached:!0,stdio:"ignore",env:{...process.env,KNODIN_UPDATE_BACKGROUND:"1"}}).unref()}let T=De(g,process.cwd());T.ok||(process.stderr.write(`${T.error}
87
+ `),process.exit(1));let u=T.repo;if(n==="doctor"){let t=Ie(mt(),["rev-parse","--is-inside-work-tree"],{cwd:u,encoding:"utf-8",stdio:["ignore","pipe","ignore"]});if(t.status!==0||t.stdout.trim()!=="true"){let s={schemaVersion:1,status:"portfolio-root",path:u,graph:"not-inspected",warning:"Target is not a Git worktree; treating a portfolio parent as one repository would be misleading.",remediation:[`knodin repos doctor ${JSON.stringify(u)}`]};process.stdout.write($?`${JSON.stringify(s)}
88
+ `:oe("doctor",s)),process.exitCode=1;return}}if(n==="compress"){let t=new Set(["--strategy","--adapter","--lines","--max-output-bytes","--context","--exit-code","--signal","--max-input-bytes","--start","--end","--limit","--offset"]),s=d.filter((y,v)=>!y.startsWith("--")&&!t.has(d[v-1])),a=(y,v,R,E)=>{let I=i(y),q=I===void 0?v:Number(I);if(!Number.isInteger(q)||q<R||q>E)throw new Error(`knodin compress: ${y} must be an integer from ${R} to ${E}`);return q},p=s[0]==="read"||s[0]==="diagnose"||s[0]==="delete"?s[0]:"create",f=p==="create"?void 0:s[1];if(p!=="create"&&!f)throw new Error(`knodin compress ${p} requires an artifact id`);let h,m=!1;if(p==="diagnose"){let y=B();try{let v=await V(u,R=>y.status(R,{audit:"cached"}));v.available?h=await ft(y,u,{artifactId:f??"",maxDiagnostics:a("--limit",10,1,50),diagnosticOffset:a("--offset",0,0,1e6),contextLines:a("--context",2,0,10),contextByteBudget:a("--max-output-bytes",16384,256,128*1024)}):(h=v,m=!0,process.exitCode=1)}finally{await y.close()}}else if(p==="read")h=je(u,f??"",{startLine:a("--start",1,1,Number.MAX_SAFE_INTEGER),endLine:i("--end")?a("--end",200,1,Number.MAX_SAFE_INTEGER):void 0,byteBudget:a("--max-output-bytes",16384,256,4*1024*1024),raw:d.includes("--raw")});else if(p==="delete")h=We(u,f??"");else{let y=i("--strategy")??"smart";if(!["smart","head-tail","errors-only"].includes(y))throw new Error("knodin compress: invalid --strategy");let v=i("--adapter")??"auto";if(!["auto","generic","vitest","jest","pytest","go-test","maven","gradle","dotnet","cargo"].includes(v))throw new Error("knodin compress: invalid --adapter");let R=a("--max-input-bytes",16*1024*1024,1,64*1024*1024),E={exitCode:i("--exit-code")?a("--exit-code",0,0,255):void 0,signal:i("--signal"),strategy:y,adapter:v,lineBudget:a("--lines",200,1,1e4),byteBudget:a("--max-output-bytes",16384,256,4*1024*1024),contextLines:a("--context",1,0,10),maxInputBytes:R,retain:!d.includes("--no-retain"),redactSecrets:!d.includes("--no-redact")},I=s[0]==="create"?s[1]??"-":s[0]??"-";h=I==="-"?Me(u,{...E,text:await qn(R)}):Fe(u,I,E)}p==="diagnose"&&(C.bytes!==void 0||C.tokens!==void 0||C.items!==void 0)&&(h=le(h,"compress:diagnose",C,{bytes:65536,tokens:16384,items:50})),$?process.stdout.write(`${JSON.stringify(h)}
89
+ `):p==="diagnose"&&!m?process.stdout.write(Ln(h)):p==="diagnose"?process.stdout.write(oe("compress diagnose",h)):p==="read"?process.stdout.write(Pn(h)):p==="create"?process.stdout.write(An(h)):process.stdout.write(oe("compress delete",h));return}let x=B(),w,L,D=!1,_=0,Pe=!1,G=async t=>{let s=await V(u,async f=>A(f,await x.status(f,{audit:"cached"})));if(!s.available)return process.exitCode=1,s;let a=await t(),p=await V(u,async f=>A(f,await x.status(f,{audit:"cached"})));return p.available?ve(a,p.state,p.graph.freshness):(process.exitCode=1,p)};switch(n){case"doctor":{let t=i("--client");if(t!==void 0&&!["claude","codex","gemini","antigravity"].includes(t))throw new Error("knodin doctor: --client must be claude, codex, gemini, or antigravity");let s=d.filter((f,h)=>f!=="--client"&&d[h-1]!=="--client");if(s.length>0)throw new Error(`knodin doctor: unknown option ${s[0]}`);let a=await ae(u,{currentVersion:F,runtimeCommand:[...b,"serve"],graph:await x.status(u,{audit:"deep"}),client:t}),p=a.manager.name;a.update=await be({currentVersion:F,installMethod:["npm","mise","volta","nvm","fnm","asdf","homebrew"].includes(p??"")?p:"unknown",env:process.env}),w=a;break}case"system":{let t=d.includes("--allow-partial"),[s,a,...p]=d.filter(m=>m!=="--allow-partial");if(!s||!["list","show","validate","query"].includes(s))throw new Error("knodin system requires list, show, validate, or query");if(t&&s!=="query")throw new Error(`knodin system ${s}: --allow-partial applies only to query`);if(p.length>0)throw new Error(`knodin system ${s}: unknown argument ${p[0]}`);let f=await Se(H(u));if(s==="list"){if(a)throw new Error("knodin system list accepts no system id");w={schemaVersion:f.schemaVersion,systems:f.systems.map(({id:m,components:y})=>({id:m,componentCount:y.length}))};break}if(!a)throw new Error(`knodin system ${s} requires <system-id>`);let h=f.systems.find(({id:m})=>m===a);if(!h){w={status:"not-found",systemId:a,available:f.systems.map(({id:m})=>m)},process.exitCode=1;break}if(s==="show")w={status:"ok",system:h,repositories:f.repositories};else if(s==="validate"){let m=await Re(f,a,async y=>A(y,await x.status(y,{audit:"cached"})),u);w={systemId:a,...m},m.valid||(process.exitCode=1)}else{let m=await Re(f,a,async y=>A(y,await x.status(y,{audit:"cached"})),u);w=Wt(f,a,t,m),w.status==="unavailable"&&(process.exitCode=1)}break}case"hook-refresh":{let[t,s,a]=d,p;if(t==="commit")p={kind:t};else if(t==="checkout"&&s&&a)p={kind:t,before:s,after:a};else if(t==="merge"&&s&&a)p={kind:t,before:s,after:a};else if(t==="rewrite"&&s)p={kind:t,inputPath:s};else throw new Error("knodin hook-refresh: invalid lifecycle event");w={indexed:await kt(u,p,(h,m)=>x.index(h,m))};break}case"init":{let t=await Rn(r,u),s=t==="team"?[]:Xt(u),a=Zt(),p=xe(u);p.start(),a.start();let f;try{try{f=await $e(u,{command:b,index:(h,m)=>x.index(h,void 0,!1,m),scope:t,agents:s,onProgress:h=>{p.update(h),a.onProgress(h)}})}finally{await a.stop(),p.stop()}}catch(h){if(!(h instanceof yt))throw h;process.stderr.write(`${h.message}
90
+ `),await x.close(),process.exitCode=1;return}w={status:"success",message:"knodin initialized successfully. Git lifecycle hooks configured.",paths:f};break}case"configure":{if(r.includes("--status")){w=ce(u)??{scope:"unconfigured",agents:[],warning:"AI agents are not configured by knodin. Run `knodin configure --scope personal`."};break}let t=we(r);if(!t)throw new Error("knodin configure: missing --scope");if(!Te.existsSync(W.join(u,".knodin","db.sqlite")))throw new Error("knodin configure changes agent integration only; this repository is not initialized. Run `knodin init` first.");let s=t==="team"?[]:Xt(u),a=await $e(u,{command:b,index:async()=>{},scope:t,agents:s,allowTrackedTransition:!0,auditConfigurationChanges:!0});w={status:"success",message:`knodin agent integration changed to ${t}.`,graphInitialization:"unchanged",nextAction:"run `knodin status` and reload the configured client",paths:a};break}case"index":{let t=He(d),s=ze(g,t,process.cwd());s.ok||(process.stderr.write(`${s.error}
91
+ `),await x.close(),process.exit(1));let a=d.includes("--clean")||d.includes("--force"),p=i("--scip"),f=Zt("index"),h=xe(s.repo);h.start(),f.start();let m;try{m=await x.index(s.repo,s.files,a,{scip:p?{path:p}:void 0,onProgress:v=>{h.update(v),f.onProgress(v)}})}finally{await f.stop(),h.stop()}w=m;let y=Ge([...m.indexed,...m.unchanged],s.repo);y.ok||(process.stderr.write(`${y.error}
92
+ `),await x.close(),process.exit(1)),m.verification.status!=="healthy"&&(process.stderr.write(Nn(m)),await x.close(),process.exit(1));break}case"status":{let t=async()=>({...A(u,await x.status(u,{audit:d.includes("--deep")?"deep":"cached"})),integration:ce(u),update:ke({currentVersion:F,installMethod:re(b),env:process.env})});if(d.includes("--watch")){let s=i("--interval"),a=s===void 0?1:Number(s);if(!Number.isFinite(a)||a<.1||a>60)throw new Error("knodin status: --interval must be between 0.1 and 60 seconds");let p=!0,f=()=>{p=!1};for(process.once("SIGINT",f),process.once("SIGTERM",f);p;){let h=await t();process.stdout.write(`${JSON.stringify({observedAt:new Date().toISOString(),...h})}
93
+ `),await new Promise(m=>setTimeout(m,a*1e3))}process.removeListener("SIGINT",f),process.removeListener("SIGTERM",f),Pe=!0,w=null}else w=await t();break}case"wait":{if(!d.includes("--fresh"))throw new Error("knodin wait requires --fresh");let t=i("--timeout"),s=t===void 0?30:Number(t);if(!Number.isFinite(s)||s<0||s>300)throw new Error("knodin wait: --timeout must be between 0 and 300 seconds");w=await Ht(x,u,Math.round(s*1e3)),w.status!=="fresh"&&(process.exitCode=1);break}case"repair":{let t=_t(d);if(t.plan){w=It(await x.status(u,{audit:"deep"})),L=t.output,D=!0;break}let s=Nt(t,process.env,process.stderr.isTTY),a=s==="tty"?tn("repair-progress-worker",{type:"start"}):(()=>{let h=Tt({mode:s,intervalMs:t.progressIntervalMs,stdout:process.stdout,stderr:process.stderr});return{start:()=>h.start(),onProgress:m=>h.onProgress(m),stop:async()=>h.stop()}})(),p=new AbortController,f=()=>p.abort();process.once("SIGINT",f),a.start();try{w=rt(u,await x.repair(u,{signal:p.signal,onProgress:h=>a.onProgress(h)}))}finally{process.removeListener("SIGINT",f),await a.stop()}L=t.output,w.cancelled&&(_=130);break}case"refresh-artifacts":{let t=d[0]??"code-change";t!=="checkout"&&t!=="merge"&&t!=="code-change"&&(process.stderr.write(`knodin refresh-artifacts accepts checkout, merge, or code-change
94
+ `),process.exit(1));let s=Gt(u,t);Ut(u,s),w=s;break}case"explain":{let t=d[0];t||(process.stderr.write(`knodin explain requires a <symbol>
95
+ `),process.exit(1)),w=await G(async()=>{let s=await x.explain(t,u,d[1]==="minimal"?"minimal":"standard",O);return d[1]==="source"&&!s.ambiguity?{mode:"source",identity:s.identity,symbol:s.symbol,source:s.source,staleness:s.staleness}:s});break}case"review":{let t=Ue(d);w=await G(()=>x.review(t.base,u,t.detailLevel,t.options));break}case"map":w=await G(()=>x.map(u,d.includes("--standard")?"standard":"minimal",{topN:i("--top")?Number(i("--top")):void 0,sort:i("--sort"),relationKinds:i("--relations")?.split(",").filter(Boolean)}));break;case"wiki":{let t=await V(u,async p=>A(p,await x.status(p,{audit:"cached"})));if(!t.available){w=t,process.exitCode=1;break}let s=d.includes("--force"),a=await x.wiki(u,s);await x.close(),process.stdout.write(`wrote ${a.written.length} pages, skipped ${a.skipped.length}
96
+ `),process.exitCode=0;return}case"visualize":{let t=d.find((a,p)=>!a.startsWith("--")&&!d[p-1]?.startsWith("--")),s=i("--output");if(!t)throw new Error("knodin visualize requires an <entry> selector");if(!s)throw new Error("knodin visualize requires --output <path.html>");w=await G(()=>Qt(x,u,{entry:t,outputPath:s,depth:i("--depth")?Number(i("--depth")):void 0,byteBudget:i("--max-bytes")?Number(i("--max-bytes")):void 0,selector:{identity:O.identity,file:O.file,kind:O.kind}}));break}case"search":{let t=d[0];t||(process.stderr.write(`knodin search requires a <query>
97
+ `),process.exit(1));let s=d[1]&&!d[1].startsWith("--")?d[1]:void 0,a=Number(i("--limit")??s??5);if(!Number.isInteger(a)||a<1)throw new Error("knodin search: limit must be a positive integer");w=await G(()=>x.search(t,u,a,{languages:i("--languages")?.split(",").filter(Boolean),extensions:i("--extensions")?.split(",").filter(Boolean),kinds:i("--kinds")?.split(",").filter(Boolean),path:i("--path"),testScope:d.includes("--tests-only")?"test":d.includes("--production-only")?"production":"all",includeSource:!d.includes("--no-source"),offset:i("--offset")?Number(i("--offset")):0}));break}case"pack":{let t=d[0],s=i("--diff-scope");if(s&&!["unstaged","staged","all","compare"].includes(s))throw new Error("knodin pack: invalid --diff-scope");if(t==="read"){if(!d[1])throw new Error("knodin pack read requires an artifact path");w=Je(u,d[1],Number(i("--start")??1),Number(i("--end")??200),Number(i("--bytes")??16384))}else if(t==="grep"){if(!d[1]||!d[2])throw new Error("knodin pack grep requires an artifact path and regex");w=Ye(u,d[1],d[2],i("--flags")??"",Number(i("--limit")??100))}else{let a={};for(let p of i("--policy")?.split(",")??[]){let[f,h]=p.split("=");if(!f||!["full","summary","structure-only"].includes(h))throw new Error("knodin pack: invalid --policy assignment");a[f]=h}w=Ke(u,{format:i("--format")??"markdown",include:i("--include")?.split(",").filter(Boolean),exclude:i("--exclude")?.split(",").filter(Boolean),policies:a,alreadyPresent:i("--already-present")?.split(",").filter(Boolean),chatFiles:i("--chat-files")?.split(",").filter(Boolean),lineNumbers:d.includes("--line-numbers"),includeTree:d.includes("--tree"),byteBudget:Number(i("--bytes")??65536),tokenBudget:Number(i("--tokens")??16384),outputPath:i("--output"),git:s||i("--log")?{diffScope:s,from:i("--from"),to:i("--to"),log:i("--log")?Number(i("--log")):void 0}:void 0})}break}case"evidence":{let t=d[0],s=d[1];if(!s||!["locate","outline","evidence","expand"].includes(t))throw new Error("knodin evidence requires locate|outline|evidence|expand <file>");w=Ot({repo:u,file:s,level:t,continuation:i("--continuation"),baselineHash:i("--baseline-hash"),baselineBytes:i("--baseline-bytes")?Number(i("--baseline-bytes")):void 0,startLine:i("--start")?Number(i("--start")):void 0,endLine:i("--end")?Number(i("--end")):void 0,byteLimit:C.bytes,tokenLimit:C.tokens,itemLimit:C.items});break}case"query":{let t=d[0],s=pt.includes(t),a=t==="architecture_overview"||t==="import_cycles"||d[1]?.startsWith("--")?"":d[1]??"";t||(process.stderr.write(`knodin query requires a <pattern> (lsp_diagnostics|lsp_definitions|lsp_declarations|lsp_implementations|callers_of|callees_of|imports_of|importers_of|import_cycles|file_summary|batch_outline|project_overview|shortest_path|inheritors_of|structural_implementations_of|tests_for|impact|dead_code|large_functions|large_files|rename_preview|flows|flow_of|stats|traverse|feature_path|flow_analysis|knowledge_gaps|surprising_connections|suggested_questions|architecture_overview|community|triggers_of|publishers_of|listeners_of|handlers_of|endpoints_for|consumers_of|children_of|federated_repos|mcp_tools|api_contract_mismatches)
98
+ `),process.exit(1));let p=i("--direction");p&&!["upstream","downstream","both"].includes(p)&&(process.stderr.write(`knodin query: --direction must be upstream, downstream, or both
99
+ `),process.exit(1));let f=i("--facets")?.split(",").filter(Boolean);f?.some(E=>!["packages","layers","boundaries","hotspots","entryPoints","languages"].includes(E))&&(process.stderr.write(`knodin query: --facets contains an unknown architecture facet
100
+ `),process.exit(1)),!a&&!s&&(process.stderr.write(`knodin query ${t} requires a <target>
101
+ `),process.exit(1));let h=t==="shortest_path"||t==="rename_preview"?d[2]:void 0;t==="rename_preview"&&!h&&(process.stderr.write(`knodin query rename_preview requires <old> and <new>
102
+ `),process.exit(1));let m;if(t==="traverse"||t==="feature_path"||t==="impact"){let E=d.indexOf("--depth"),I=E>=0?d[E+1]:d[2];I!==void 0&&I!==""&&Number.isFinite(Number(I))&&(m=Number(I))}let y;t==="architecture_overview"&&d.includes("standard")&&(y="standard");let v=i("--limit");v!==void 0&&(!Number.isInteger(Number(v))||Number(v)<1)&&(process.stderr.write(`knodin query: --limit must be a positive integer
103
+ `),process.exit(1));let R=t.startsWith("lsp_")?null:await V(u,async E=>A(E,await x.status(E,{audit:"cached"})));if(R&&!R.available){w=R,process.exitCode=1;break}if(w=await x.query(t,a,u,h,v?Number(v):void 0,m,y,O,t==="impact"?{mode:i("--impact-mode")==="file"?"file":"symbol",direction:["upstream","downstream","both"].includes(i("--direction")??"")?i("--direction"):void 0,relationKinds:i("--relations")?.split(",").map(E=>E.trim()).filter(Boolean),minConfidence:i("--min-confidence")?Number(i("--min-confidence")):void 0,includeTests:!d.includes("--exclude-tests"),includeDataFlow:d.includes("--data-flow")}:void 0,{minLines:i("--min-lines")?Number(i("--min-lines")):void 0,minComplexity:i("--min-complexity")?Number(i("--min-complexity")):void 0,kinds:i("--kinds")?.split(",").filter(Boolean),path:i("--path"),direction:t==="traverse"&&["upstream","downstream","both"].includes(i("--direction")??"")?i("--direction"):void 0,includeDataFlow:t==="traverse"?d.includes("--data-flow"):void 0,flowVariable:t==="flow_analysis"?i("--variable"):void 0,architectureFacets:f,topN:i("--top")?Number(i("--top")):void 0,sort:i("--sort"),relationKinds:t==="impact"?void 0:i("--relations")?.split(",").filter(Boolean),detailLevel:y}),t==="impact"||t==="dead_code"){let E=await Se(H(u));w=Bt(E,u,t,a,w)}if(R?.available){let E=await V(u,async I=>A(I,await x.status(I,{audit:"cached"})));if(!E.available){w=E,process.exitCode=1;break}w=ve(w,E.state,E.graph.freshness)}break}case"rename":{let t=d.filter(h=>!h.startsWith("--")),s=t[0],a=t[1],p=d.includes("--apply"),f=!d.includes("--no-verify");(!s||!a)&&(process.stderr.write(`knodin rename requires <old> and <new>
104
+ `),process.exit(1)),w=await G(()=>x.rename(s,a,u,p,f,O));break}case"prs":{try{let t=d[0]==="audit"?d.slice(1):d,s=h=>{let m=t.indexOf(h);return m>=0?t[m+1]:void 0},a=s("--state"),p=s("--limit"),f=p===void 0?50:Number(p);if(!Number.isInteger(f)||f<1)throw new Error("knodin prs: --limit must be a positive integer");w=await Ct(u,x,{state:a,limit:f,branches:s("--branches"),range:s("--range"),base:s("--base"),head:s("--head"),expectedLogin:s("--expected-login")})}catch(t){await x.close(),process.stderr.write(`${t.message}
105
+ `),process.exit(1)}break}case"worktrees":{let t=d[0]??"status";if(t==="status")w=await ht(u,s=>x.status(s,{audit:"cached"}));else if(t==="reconcile")w=gt(u);else if(t==="remove"){let s=d[1];if(!s)throw new Error("knodin worktrees remove requires <path>");w=wt(u,s,d.includes("--dry-run"))}else throw new Error(`knodin worktrees: unknown action ${t}`);break}case"telemetry":{let t=d[0],s=i("--retention-days"),a=s===void 0?30:Number(s);if(!Number.isInteger(a)||a<1||a>3650)throw new Error("knodin telemetry: --retention-days must be an integer from 1 to 3650");let p=i("--input");if(t==="status")w=Et(u,p,a);else if(t==="report")w=$t(u,de(u,p,a),i("--output"));else if(t==="export")w=Rt(u,de(u,p,a),i("--output"));else if(t==="clear")w=St(u,p);else throw new Error("knodin telemetry requires status, report, export, or clear");break}case"diagnostics":{let[t,s]=c.positionals,a=i("--retention-days"),p=i("--since"),f=i("--output"),h=a===void 0?14:Number(a);if(!Number.isInteger(h)||h<1||h>365)throw new Error("knodin diagnostics: --retention-days must be an integer from 1 to 365");if(s&&t!=="inspect")throw new Error(`knodin diagnostics ${t}: unexpected bundle argument`);if(a!==void 0&&t!=="enable")throw new Error(`knodin diagnostics ${t}: --retention-days applies only to enable`);if(p!==void 0&&t!=="collect")throw new Error(`knodin diagnostics ${t}: --since applies only to collect`);if(f!==void 0&&t!=="collect")throw new Error(`knodin diagnostics ${t}: --output applies only to collect`);if(t==="enable")w=Qe(u,h);else if(t==="status")w=Ze(u);else if(t==="disable")w=Xe(u);else if(t==="clear")w=tt(u);else if(t==="inspect"){if(!s)throw new Error("knodin diagnostics inspect requires <bundle>");w=ot(u,s)}else if(t==="collect"){let m=p??"24h",y=/^(\d+)(h|d)$/.exec(m);if(!y)throw new Error("knodin diagnostics collect: --since must be hours or days, such as 24h or 7d");let v=Number(y[1])*(y[2]==="d"?24:1),R=A(u,await x.status(u,{audit:"deep"})),E=await ae(u,{currentVersion:F,runtimeCommand:[...b,"serve"],graph:R});w=nt(u,{sinceHours:v,outputPath:f,doctor:E,graph:R,telemetry:de(u,void 0,Math.max(1,Math.ceil(v/24))),knodinVersion:F})}else throw new Error("knodin diagnostics requires enable, status, collect, inspect, clear, or disable");break}case"context":{let t=d[0];t||(process.stderr.write(`knodin context requires a "<task>" description
106
+ `),process.exit(1)),w=await G(()=>Ve(x,t,u,d[1]));break}default:process.stderr.write(`unknown command: ${n}
107
+
108
+ ${ge([],process.stderr.isTTY?process.stderr.columns:void 0)}`),process.exit(1)}if(await x.close(),Pe)return;let z=le(w,n,C,{bytes:65536,tokens:16384,items:100}),J=Math.max(Number(process.exitCode??0),_);if(n==="init"&&!$){process.stdout.write(Sn(z)),process.exitCode=J;return}if(n==="configure"&&!$&&!r.includes("--status")){process.stdout.write(On(z));return}if(n==="configure"&&!$&&r.includes("--status")){process.stdout.write(Cn(z));return}if(n==="repair"&&L==="human"&&!D){process.stdout.write(_n(z)),process.exitCode=J;return}if(n==="index"&&!$){process.stdout.write(In(w)),process.exitCode=J;return}if(n==="status"&&!$){process.stdout.write(Tn(z)),process.exitCode=J;return}if(!$&&L!=="jsonl"){process.stdout.write(oe(n,z)),process.exitCode=J;return}process.stdout.write(L==="jsonl"?At("result",z):`${JSON.stringify(z)}
109
+ `),process.exitCode=J}jn().catch(e=>{let o=process.argv.slice(2),n=o.indexOf("--repo"),r=o.find($=>$.startsWith("--repo=")),c=n>=0&&o[n+1]?o[n+1]:r?r.slice(7):process.cwd(),l=o.find(($,d)=>$.startsWith("-")?!1:!(d>0&&o[d-1]==="--repo")&&$!==c),g=et(c,{surface:"cli",operation:l&&new Set(["init","configure","index","doctor","status","wait","repair","serve","context","explain","review","map","search","query","rename","wiki","visualize","pack","compress","prs","worktrees","telemetry","diagnostics","system","repos","update"]).has(l)?l:"unknown",phase:"dispatch",error:e}),b=g.recorded?` [diagnostic ${g.correlationId}]`:"";console.error(`${e instanceof Error?e.message:String(e)}${b}`),process.exit(1)});