kairo-mcp 1.3.0
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.
- package/CHANGELOG.md +1122 -0
- package/LICENSE +21 -0
- package/README.md +662 -0
- package/dist/cli/cli.d.ts +3 -0
- package/dist/cli/cli.d.ts.map +1 -0
- package/dist/cli/cli.js +226 -0
- package/dist/cli/cli.js.map +1 -0
- package/dist/cli/commands.d.ts +10 -0
- package/dist/cli/commands.d.ts.map +1 -0
- package/dist/cli/commands.js +938 -0
- package/dist/cli/commands.js.map +1 -0
- package/dist/cli/flags.d.ts +30 -0
- package/dist/cli/flags.d.ts.map +1 -0
- package/dist/cli/flags.js +90 -0
- package/dist/cli/flags.js.map +1 -0
- package/dist/cli/output.d.ts +69 -0
- package/dist/cli/output.d.ts.map +1 -0
- package/dist/cli/output.js +166 -0
- package/dist/cli/output.js.map +1 -0
- package/dist/cli/types.d.ts +34 -0
- package/dist/cli/types.d.ts.map +1 -0
- package/dist/cli/types.js +2 -0
- package/dist/cli/types.js.map +1 -0
- package/dist/contracts/migrations.d.ts +31 -0
- package/dist/contracts/migrations.d.ts.map +1 -0
- package/dist/contracts/migrations.js +71 -0
- package/dist/contracts/migrations.js.map +1 -0
- package/dist/contracts/schemas.d.ts +53 -0
- package/dist/contracts/schemas.d.ts.map +1 -0
- package/dist/contracts/schemas.js +27 -0
- package/dist/contracts/schemas.js.map +1 -0
- package/dist/contracts/stability.d.ts +32 -0
- package/dist/contracts/stability.d.ts.map +1 -0
- package/dist/contracts/stability.js +140 -0
- package/dist/contracts/stability.js.map +1 -0
- package/dist/contracts/zodSchemas.d.ts +811 -0
- package/dist/contracts/zodSchemas.d.ts.map +1 -0
- package/dist/contracts/zodSchemas.js +139 -0
- package/dist/contracts/zodSchemas.js.map +1 -0
- package/dist/core/brief/budget.d.ts +21 -0
- package/dist/core/brief/budget.d.ts.map +1 -0
- package/dist/core/brief/budget.js +42 -0
- package/dist/core/brief/budget.js.map +1 -0
- package/dist/core/checkpoint/checkpointManager.d.ts +29 -0
- package/dist/core/checkpoint/checkpointManager.d.ts.map +1 -0
- package/dist/core/checkpoint/checkpointManager.js +49 -0
- package/dist/core/checkpoint/checkpointManager.js.map +1 -0
- package/dist/core/compaction/compactor.d.ts +40 -0
- package/dist/core/compaction/compactor.d.ts.map +1 -0
- package/dist/core/compaction/compactor.js +168 -0
- package/dist/core/compaction/compactor.js.map +1 -0
- package/dist/core/continuation/continuationBuilder.d.ts +19 -0
- package/dist/core/continuation/continuationBuilder.d.ts.map +1 -0
- package/dist/core/continuation/continuationBuilder.js +184 -0
- package/dist/core/continuation/continuationBuilder.js.map +1 -0
- package/dist/core/coordination/coordinationManager.d.ts +34 -0
- package/dist/core/coordination/coordinationManager.d.ts.map +1 -0
- package/dist/core/coordination/coordinationManager.js +253 -0
- package/dist/core/coordination/coordinationManager.js.map +1 -0
- package/dist/core/coordination/types.d.ts +52 -0
- package/dist/core/coordination/types.d.ts.map +1 -0
- package/dist/core/coordination/types.js +6 -0
- package/dist/core/coordination/types.js.map +1 -0
- package/dist/core/github/changelog.d.ts +9 -0
- package/dist/core/github/changelog.d.ts.map +1 -0
- package/dist/core/github/changelog.js +41 -0
- package/dist/core/github/changelog.js.map +1 -0
- package/dist/core/github/commitMessage.d.ts +4 -0
- package/dist/core/github/commitMessage.d.ts.map +1 -0
- package/dist/core/github/commitMessage.js +125 -0
- package/dist/core/github/commitMessage.js.map +1 -0
- package/dist/core/github/gitContext.d.ts +3 -0
- package/dist/core/github/gitContext.d.ts.map +1 -0
- package/dist/core/github/gitContext.js +67 -0
- package/dist/core/github/gitContext.js.map +1 -0
- package/dist/core/github/releasePlan.d.ts +8 -0
- package/dist/core/github/releasePlan.d.ts.map +1 -0
- package/dist/core/github/releasePlan.js +56 -0
- package/dist/core/github/releasePlan.js.map +1 -0
- package/dist/core/github/semver.d.ts +11 -0
- package/dist/core/github/semver.d.ts.map +1 -0
- package/dist/core/github/semver.js +23 -0
- package/dist/core/github/semver.js.map +1 -0
- package/dist/core/github/types.d.ts +36 -0
- package/dist/core/github/types.d.ts.map +1 -0
- package/dist/core/github/types.js +2 -0
- package/dist/core/github/types.js.map +1 -0
- package/dist/core/graph/derived.d.ts +6 -0
- package/dist/core/graph/derived.d.ts.map +1 -0
- package/dist/core/graph/derived.js +133 -0
- package/dist/core/graph/derived.js.map +1 -0
- package/dist/core/graph/graphEngine.d.ts +11 -0
- package/dist/core/graph/graphEngine.d.ts.map +1 -0
- package/dist/core/graph/graphEngine.js +28 -0
- package/dist/core/graph/graphEngine.js.map +1 -0
- package/dist/core/graph/imports.d.ts +19 -0
- package/dist/core/graph/imports.d.ts.map +1 -0
- package/dist/core/graph/imports.js +0 -0
- package/dist/core/graph/imports.js.map +1 -0
- package/dist/core/graph/mermaid.d.ts +6 -0
- package/dist/core/graph/mermaid.d.ts.map +1 -0
- package/dist/core/graph/mermaid.js +40 -0
- package/dist/core/graph/mermaid.js.map +1 -0
- package/dist/core/graph/moduleGraph.d.ts +27 -0
- package/dist/core/graph/moduleGraph.d.ts.map +1 -0
- package/dist/core/graph/moduleGraph.js +186 -0
- package/dist/core/graph/moduleGraph.js.map +1 -0
- package/dist/core/graph/types.d.ts +32 -0
- package/dist/core/graph/types.d.ts.map +1 -0
- package/dist/core/graph/types.js +2 -0
- package/dist/core/graph/types.js.map +1 -0
- package/dist/core/query/queryEngine.d.ts +26 -0
- package/dist/core/query/queryEngine.d.ts.map +1 -0
- package/dist/core/query/queryEngine.js +258 -0
- package/dist/core/query/queryEngine.js.map +1 -0
- package/dist/core/query/types.d.ts +70 -0
- package/dist/core/query/types.d.ts.map +1 -0
- package/dist/core/query/types.js +6 -0
- package/dist/core/query/types.js.map +1 -0
- package/dist/core/repo/fingerprint.d.ts +0 -0
- package/dist/core/repo/fingerprint.d.ts.map +1 -0
- package/dist/core/repo/fingerprint.js +17 -0
- package/dist/core/repo/fingerprint.js.map +1 -0
- package/dist/core/repo/frameworkDetectors.d.ts +14 -0
- package/dist/core/repo/frameworkDetectors.d.ts.map +1 -0
- package/dist/core/repo/frameworkDetectors.js +151 -0
- package/dist/core/repo/frameworkDetectors.js.map +1 -0
- package/dist/core/repo/repoScanner.d.ts +20 -0
- package/dist/core/repo/repoScanner.d.ts.map +1 -0
- package/dist/core/repo/repoScanner.js +290 -0
- package/dist/core/repo/repoScanner.js.map +1 -0
- package/dist/core/repo/summary.d.ts +7 -0
- package/dist/core/repo/summary.d.ts.map +1 -0
- package/dist/core/repo/summary.js +34 -0
- package/dist/core/repo/summary.js.map +1 -0
- package/dist/core/repo/types.d.ts +62 -0
- package/dist/core/repo/types.d.ts.map +1 -0
- package/dist/core/repo/types.js +10 -0
- package/dist/core/repo/types.js.map +1 -0
- package/dist/core/risk/guardrail.d.ts +3 -0
- package/dist/core/risk/guardrail.d.ts.map +1 -0
- package/dist/core/risk/guardrail.js +47 -0
- package/dist/core/risk/guardrail.js.map +1 -0
- package/dist/core/risk/riskEngine.d.ts +8 -0
- package/dist/core/risk/riskEngine.d.ts.map +1 -0
- package/dist/core/risk/riskEngine.js +112 -0
- package/dist/core/risk/riskEngine.js.map +1 -0
- package/dist/core/risk/riskHeuristics.d.ts +4 -0
- package/dist/core/risk/riskHeuristics.d.ts.map +1 -0
- package/dist/core/risk/riskHeuristics.js +36 -0
- package/dist/core/risk/riskHeuristics.js.map +1 -0
- package/dist/core/salience/config.d.ts +14 -0
- package/dist/core/salience/config.d.ts.map +1 -0
- package/dist/core/salience/config.js +87 -0
- package/dist/core/salience/config.js.map +1 -0
- package/dist/core/salience/salienceEngine.d.ts +16 -0
- package/dist/core/salience/salienceEngine.d.ts.map +1 -0
- package/dist/core/salience/salienceEngine.js +64 -0
- package/dist/core/salience/salienceEngine.js.map +1 -0
- package/dist/core/salience/signals.d.ts +4 -0
- package/dist/core/salience/signals.d.ts.map +1 -0
- package/dist/core/salience/signals.js +143 -0
- package/dist/core/salience/signals.js.map +1 -0
- package/dist/core/salience/types.d.ts +69 -0
- package/dist/core/salience/types.d.ts.map +1 -0
- package/dist/core/salience/types.js +7 -0
- package/dist/core/salience/types.js.map +1 -0
- package/dist/core/session/eventPayloads.d.ts +92 -0
- package/dist/core/session/eventPayloads.d.ts.map +1 -0
- package/dist/core/session/eventPayloads.js +2 -0
- package/dist/core/session/eventPayloads.js.map +1 -0
- package/dist/core/session/reducer.d.ts +14 -0
- package/dist/core/session/reducer.d.ts.map +1 -0
- package/dist/core/session/reducer.js +181 -0
- package/dist/core/session/reducer.js.map +1 -0
- package/dist/core/session/sessionManager.d.ts +252 -0
- package/dist/core/session/sessionManager.d.ts.map +1 -0
- package/dist/core/session/sessionManager.js +655 -0
- package/dist/core/session/sessionManager.js.map +1 -0
- package/dist/core/telemetry/analytics.d.ts +24 -0
- package/dist/core/telemetry/analytics.d.ts.map +1 -0
- package/dist/core/telemetry/analytics.js +167 -0
- package/dist/core/telemetry/analytics.js.map +1 -0
- package/dist/core/telemetry/exporter.d.ts +17 -0
- package/dist/core/telemetry/exporter.d.ts.map +1 -0
- package/dist/core/telemetry/exporter.js +31 -0
- package/dist/core/telemetry/exporter.js.map +1 -0
- package/dist/core/telemetry/recorder.d.ts +19 -0
- package/dist/core/telemetry/recorder.d.ts.map +1 -0
- package/dist/core/telemetry/recorder.js +43 -0
- package/dist/core/telemetry/recorder.js.map +1 -0
- package/dist/core/telemetry/reports.d.ts +6 -0
- package/dist/core/telemetry/reports.d.ts.map +1 -0
- package/dist/core/telemetry/reports.js +84 -0
- package/dist/core/telemetry/reports.js.map +1 -0
- package/dist/core/telemetry/types.d.ts +82 -0
- package/dist/core/telemetry/types.d.ts.map +1 -0
- package/dist/core/telemetry/types.js +9 -0
- package/dist/core/telemetry/types.js.map +1 -0
- package/dist/core/vector/chunking/memoryChunker.d.ts +10 -0
- package/dist/core/vector/chunking/memoryChunker.d.ts.map +1 -0
- package/dist/core/vector/chunking/memoryChunker.js +202 -0
- package/dist/core/vector/chunking/memoryChunker.js.map +1 -0
- package/dist/core/vector/compression/architectureDigest.d.ts +12 -0
- package/dist/core/vector/compression/architectureDigest.d.ts.map +1 -0
- package/dist/core/vector/compression/architectureDigest.js +37 -0
- package/dist/core/vector/compression/architectureDigest.js.map +1 -0
- package/dist/core/vector/embedding/deterministicEmbedder.d.ts +10 -0
- package/dist/core/vector/embedding/deterministicEmbedder.d.ts.map +1 -0
- package/dist/core/vector/embedding/deterministicEmbedder.js +84 -0
- package/dist/core/vector/embedding/deterministicEmbedder.js.map +1 -0
- package/dist/core/vector/memory/memoryEngine.d.ts +56 -0
- package/dist/core/vector/memory/memoryEngine.d.ts.map +1 -0
- package/dist/core/vector/memory/memoryEngine.js +204 -0
- package/dist/core/vector/memory/memoryEngine.js.map +1 -0
- package/dist/core/vector/memory/memoryFingerprint.d.ts +12 -0
- package/dist/core/vector/memory/memoryFingerprint.d.ts.map +1 -0
- package/dist/core/vector/memory/memoryFingerprint.js +33 -0
- package/dist/core/vector/memory/memoryFingerprint.js.map +1 -0
- package/dist/core/vector/providers/deterministicProvider.d.ts +14 -0
- package/dist/core/vector/providers/deterministicProvider.d.ts.map +1 -0
- package/dist/core/vector/providers/deterministicProvider.js +22 -0
- package/dist/core/vector/providers/deterministicProvider.js.map +1 -0
- package/dist/core/vector/providers/httpEmbeddingProvider.d.ts +32 -0
- package/dist/core/vector/providers/httpEmbeddingProvider.d.ts.map +1 -0
- package/dist/core/vector/providers/httpEmbeddingProvider.js +93 -0
- package/dist/core/vector/providers/httpEmbeddingProvider.js.map +1 -0
- package/dist/core/vector/providers/registry.d.ts +9 -0
- package/dist/core/vector/providers/registry.d.ts.map +1 -0
- package/dist/core/vector/providers/registry.js +79 -0
- package/dist/core/vector/providers/registry.js.map +1 -0
- package/dist/core/vector/providers/types.d.ts +23 -0
- package/dist/core/vector/providers/types.d.ts.map +1 -0
- package/dist/core/vector/providers/types.js +2 -0
- package/dist/core/vector/providers/types.js.map +1 -0
- package/dist/core/vector/retrieval/hybridRetriever.d.ts +37 -0
- package/dist/core/vector/retrieval/hybridRetriever.d.ts.map +1 -0
- package/dist/core/vector/retrieval/hybridRetriever.js +131 -0
- package/dist/core/vector/retrieval/hybridRetriever.js.map +1 -0
- package/dist/core/vector/types.d.ts +87 -0
- package/dist/core/vector/types.d.ts.map +1 -0
- package/dist/core/vector/types.js +6 -0
- package/dist/core/vector/types.js.map +1 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +30 -0
- package/dist/index.js.map +1 -0
- package/dist/inspect/cli.d.ts +3 -0
- package/dist/inspect/cli.d.ts.map +1 -0
- package/dist/inspect/cli.js +40 -0
- package/dist/inspect/cli.js.map +1 -0
- package/dist/inspect/projections.d.ts +141 -0
- package/dist/inspect/projections.d.ts.map +1 -0
- package/dist/inspect/projections.js +311 -0
- package/dist/inspect/projections.js.map +1 -0
- package/dist/inspect/render.d.ts +27 -0
- package/dist/inspect/render.d.ts.map +1 -0
- package/dist/inspect/render.js +325 -0
- package/dist/inspect/render.js.map +1 -0
- package/dist/inspect/server.d.ts +18 -0
- package/dist/inspect/server.d.ts.map +1 -0
- package/dist/inspect/server.js +112 -0
- package/dist/inspect/server.js.map +1 -0
- package/dist/perf/index.d.ts +24 -0
- package/dist/perf/index.d.ts.map +1 -0
- package/dist/perf/index.js +38 -0
- package/dist/perf/index.js.map +1 -0
- package/dist/perf/report.d.ts +8 -0
- package/dist/perf/report.d.ts.map +1 -0
- package/dist/perf/report.js +52 -0
- package/dist/perf/report.js.map +1 -0
- package/dist/perf/runner.d.ts +29 -0
- package/dist/perf/runner.d.ts.map +1 -0
- package/dist/perf/runner.js +74 -0
- package/dist/perf/runner.js.map +1 -0
- package/dist/perf/scenarios.d.ts +8 -0
- package/dist/perf/scenarios.d.ts.map +1 -0
- package/dist/perf/scenarios.js +119 -0
- package/dist/perf/scenarios.js.map +1 -0
- package/dist/perf/types.d.ts +32 -0
- package/dist/perf/types.d.ts.map +1 -0
- package/dist/perf/types.js +6 -0
- package/dist/perf/types.js.map +1 -0
- package/dist/plugins/loader.d.ts +78 -0
- package/dist/plugins/loader.d.ts.map +1 -0
- package/dist/plugins/loader.js +193 -0
- package/dist/plugins/loader.js.map +1 -0
- package/dist/plugins/types.d.ts +38 -0
- package/dist/plugins/types.d.ts.map +1 -0
- package/dist/plugins/types.js +10 -0
- package/dist/plugins/types.js.map +1 -0
- package/dist/pressure/pressureModel.d.ts +6 -0
- package/dist/pressure/pressureModel.d.ts.map +1 -0
- package/dist/pressure/pressureModel.js +102 -0
- package/dist/pressure/pressureModel.js.map +1 -0
- package/dist/prompts/continuityPrompt.d.ts +7 -0
- package/dist/prompts/continuityPrompt.d.ts.map +1 -0
- package/dist/prompts/continuityPrompt.js +43 -0
- package/dist/prompts/continuityPrompt.js.map +1 -0
- package/dist/sdk/index.d.ts +53 -0
- package/dist/sdk/index.d.ts.map +1 -0
- package/dist/sdk/index.js +130 -0
- package/dist/sdk/index.js.map +1 -0
- package/dist/security/patterns.d.ts +16 -0
- package/dist/security/patterns.d.ts.map +1 -0
- package/dist/security/patterns.js +61 -0
- package/dist/security/patterns.js.map +1 -0
- package/dist/security/redactor.d.ts +20 -0
- package/dist/security/redactor.d.ts.map +1 -0
- package/dist/security/redactor.js +58 -0
- package/dist/security/redactor.js.map +1 -0
- package/dist/server/createServer.d.ts +10 -0
- package/dist/server/createServer.d.ts.map +1 -0
- package/dist/server/createServer.js +60 -0
- package/dist/server/createServer.js.map +1 -0
- package/dist/server/registerTools.d.ts +4 -0
- package/dist/server/registerTools.d.ts.map +1 -0
- package/dist/server/registerTools.js +1086 -0
- package/dist/server/registerTools.js.map +1 -0
- package/dist/server/responses.d.ts +17 -0
- package/dist/server/responses.d.ts.map +1 -0
- package/dist/server/responses.js +21 -0
- package/dist/server/responses.js.map +1 -0
- package/dist/snapshot/export.d.ts +26 -0
- package/dist/snapshot/export.d.ts.map +1 -0
- package/dist/snapshot/export.js +164 -0
- package/dist/snapshot/export.js.map +1 -0
- package/dist/snapshot/import.d.ts +34 -0
- package/dist/snapshot/import.d.ts.map +1 -0
- package/dist/snapshot/import.js +114 -0
- package/dist/snapshot/import.js.map +1 -0
- package/dist/snapshot/types.d.ts +63 -0
- package/dist/snapshot/types.d.ts.map +1 -0
- package/dist/snapshot/types.js +2 -0
- package/dist/snapshot/types.js.map +1 -0
- package/dist/storage/faultAdapter.d.ts +64 -0
- package/dist/storage/faultAdapter.d.ts.map +1 -0
- package/dist/storage/faultAdapter.js +134 -0
- package/dist/storage/faultAdapter.js.map +1 -0
- package/dist/storage/fileStorageAdapter.d.ts +55 -0
- package/dist/storage/fileStorageAdapter.d.ts.map +1 -0
- package/dist/storage/fileStorageAdapter.js +219 -0
- package/dist/storage/fileStorageAdapter.js.map +1 -0
- package/dist/storage/paths.d.ts +30 -0
- package/dist/storage/paths.d.ts.map +1 -0
- package/dist/storage/paths.js +32 -0
- package/dist/storage/paths.js.map +1 -0
- package/dist/storage/quarantine.d.ts +36 -0
- package/dist/storage/quarantine.d.ts.map +1 -0
- package/dist/storage/quarantine.js +26 -0
- package/dist/storage/quarantine.js.map +1 -0
- package/dist/storage/redactingAdapter.d.ts +13 -0
- package/dist/storage/redactingAdapter.d.ts.map +1 -0
- package/dist/storage/redactingAdapter.js +91 -0
- package/dist/storage/redactingAdapter.js.map +1 -0
- package/dist/storage/storageAdapter.d.ts +46 -0
- package/dist/storage/storageAdapter.d.ts.map +1 -0
- package/dist/storage/storageAdapter.js +2 -0
- package/dist/storage/storageAdapter.js.map +1 -0
- package/dist/types/domain.d.ts +139 -0
- package/dist/types/domain.d.ts.map +1 -0
- package/dist/types/domain.js +9 -0
- package/dist/types/domain.js.map +1 -0
- package/dist/types/events.d.ts +28 -0
- package/dist/types/events.d.ts.map +1 -0
- package/dist/types/events.js +7 -0
- package/dist/types/events.js.map +1 -0
- package/dist/utils/errors.d.ts +6 -0
- package/dist/utils/errors.d.ts.map +1 -0
- package/dist/utils/errors.js +10 -0
- package/dist/utils/errors.js.map +1 -0
- package/dist/utils/ids.d.ts +7 -0
- package/dist/utils/ids.d.ts.map +1 -0
- package/dist/utils/ids.js +22 -0
- package/dist/utils/ids.js.map +1 -0
- package/dist/utils/logger.d.ts +7 -0
- package/dist/utils/logger.d.ts.map +1 -0
- package/dist/utils/logger.js +26 -0
- package/dist/utils/logger.js.map +1 -0
- package/dist/utils/time.d.ts +8 -0
- package/dist/utils/time.d.ts.map +1 -0
- package/dist/utils/time.js +11 -0
- package/dist/utils/time.js.map +1 -0
- package/package.json +79 -0
|
@@ -0,0 +1,938 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Kairo CLI commands (v1.1.0, ADR-0016).
|
|
3
|
+
*
|
|
4
|
+
* All commands here. Each is a thin wrapper over existing SDK / projection
|
|
5
|
+
* / snapshot / compaction / benchmark functionality. No new business
|
|
6
|
+
* logic, no new persisted state. The CLI is a developer-facing
|
|
7
|
+
* presentation layer.
|
|
8
|
+
*/
|
|
9
|
+
import { readFile, writeFile, readdir } from 'node:fs/promises';
|
|
10
|
+
import { existsSync } from 'node:fs';
|
|
11
|
+
import { join, resolve } from 'node:path';
|
|
12
|
+
import { spawn } from 'node:child_process';
|
|
13
|
+
import { KairoClient } from '../sdk/index.js';
|
|
14
|
+
import { kairoPaths } from '../storage/paths.js';
|
|
15
|
+
import { exportSnapshot } from '../snapshot/export.js';
|
|
16
|
+
import { importSnapshot } from '../snapshot/import.js';
|
|
17
|
+
import { compact } from '../core/compaction/compactor.js';
|
|
18
|
+
import { runBenchmark } from '../perf/index.js';
|
|
19
|
+
import { SessionManager } from '../core/session/sessionManager.js';
|
|
20
|
+
import { FileStorageAdapter } from '../storage/fileStorageAdapter.js';
|
|
21
|
+
import { withRedaction } from '../storage/redactingAdapter.js';
|
|
22
|
+
import { systemClock } from '../utils/time.js';
|
|
23
|
+
import { startInspectServer } from '../inspect/server.js';
|
|
24
|
+
import { loadPlugins } from '../plugins/loader.js';
|
|
25
|
+
import { STABILITY, stabilityOf } from '../contracts/stability.js';
|
|
26
|
+
import { SERVER_VERSION } from '../server/createServer.js';
|
|
27
|
+
import { parse as parseFlagsImpl } from './flags.js';
|
|
28
|
+
const NO_KAIRO_HINT = 'Run `kairo init` to wire Kairo into this project.';
|
|
29
|
+
function requireKairo(ctx) {
|
|
30
|
+
const client = new KairoClient({ projectRoot: ctx.projectRoot });
|
|
31
|
+
if (client.hasKairo())
|
|
32
|
+
return true;
|
|
33
|
+
ctx.out.error(`No .kairo/ in ${ctx.projectRoot}`);
|
|
34
|
+
ctx.out.hint(NO_KAIRO_HINT);
|
|
35
|
+
return false;
|
|
36
|
+
}
|
|
37
|
+
function shortId(id, n = 12) {
|
|
38
|
+
if (!id)
|
|
39
|
+
return '-';
|
|
40
|
+
return id.length > n ? id.slice(0, n) + '…' : id;
|
|
41
|
+
}
|
|
42
|
+
// ── version ────────────────────────────────────────────────────────────
|
|
43
|
+
const versionCmd = {
|
|
44
|
+
name: 'version',
|
|
45
|
+
aliases: ['--version', '-V'],
|
|
46
|
+
summary: 'Print kairo version.',
|
|
47
|
+
help: 'Prints the installed kairo build version.',
|
|
48
|
+
run(ctx) {
|
|
49
|
+
if (ctx.out.maybeJson({ version: SERVER_VERSION }))
|
|
50
|
+
return Promise.resolve({ exitCode: 0 });
|
|
51
|
+
ctx.out.line(SERVER_VERSION);
|
|
52
|
+
return Promise.resolve({ exitCode: 0 });
|
|
53
|
+
},
|
|
54
|
+
};
|
|
55
|
+
// ── status ─────────────────────────────────────────────────────────────
|
|
56
|
+
const statusCmd = {
|
|
57
|
+
name: 'status',
|
|
58
|
+
summary: "One-screen overview of the project's .kairo/ state.",
|
|
59
|
+
help: 'Shows event/telemetry/session/checkpoint counts, the latest session and ' +
|
|
60
|
+
'checkpoint, repo intelligence summary, and any quarantine count.',
|
|
61
|
+
examples: ['kairo status', 'kairo status --json'],
|
|
62
|
+
async run(ctx) {
|
|
63
|
+
if (!requireKairo(ctx))
|
|
64
|
+
return { exitCode: 3 };
|
|
65
|
+
const k = new KairoClient({ projectRoot: ctx.projectRoot });
|
|
66
|
+
const o = await k.overview();
|
|
67
|
+
if (ctx.out.maybeJson(o))
|
|
68
|
+
return { exitCode: 0 };
|
|
69
|
+
ctx.out.heading('Project');
|
|
70
|
+
ctx.out.kv([
|
|
71
|
+
['root', o.projectRoot],
|
|
72
|
+
['events', String(o.eventCount)],
|
|
73
|
+
['telemetry', String(o.telemetryCount)],
|
|
74
|
+
['sessions', String(o.sessionCount)],
|
|
75
|
+
['checkpoints', String(o.checkpointCount)],
|
|
76
|
+
[
|
|
77
|
+
'quarantine',
|
|
78
|
+
o.quarantineCount === 0 ? ctx.out.dim('0') : ctx.out.yellow(String(o.quarantineCount)),
|
|
79
|
+
],
|
|
80
|
+
['latest session', shortId(o.latestSessionId)],
|
|
81
|
+
['latest checkpoint', shortId(o.latestCheckpointId)],
|
|
82
|
+
]);
|
|
83
|
+
if (o.intelligence) {
|
|
84
|
+
ctx.out.heading('Intelligence');
|
|
85
|
+
ctx.out.kv([
|
|
86
|
+
['schema', `v${o.intelligence.schema}`],
|
|
87
|
+
['files', String(o.intelligence.files)],
|
|
88
|
+
['frameworks', o.intelligence.frameworks.join(', ') || ctx.out.dim('(none)')],
|
|
89
|
+
['languages', o.intelligence.languages.join(', ') || ctx.out.dim('(none)')],
|
|
90
|
+
]);
|
|
91
|
+
}
|
|
92
|
+
if (o.quarantineCount > 0) {
|
|
93
|
+
ctx.out.hint(`${o.quarantineCount} quarantined record(s) — inspect .kairo/quarantine/`);
|
|
94
|
+
}
|
|
95
|
+
return { exitCode: 0 };
|
|
96
|
+
},
|
|
97
|
+
};
|
|
98
|
+
// ── brief / continue ───────────────────────────────────────────────────
|
|
99
|
+
const briefCmd = {
|
|
100
|
+
name: 'brief',
|
|
101
|
+
summary: 'Print the latest continuation brief.',
|
|
102
|
+
flags: [
|
|
103
|
+
{ name: 'tiny', type: 'boolean', help: 'Use tiny mode (~1500 chars).' },
|
|
104
|
+
{ name: 'normal', type: 'boolean', help: 'Use normal mode (default, ~4000 chars).' },
|
|
105
|
+
{ name: 'deep', type: 'boolean', help: 'Use deep mode (~20000 chars).' },
|
|
106
|
+
{ name: 'max-chars', type: 'number', help: 'Override the char budget.' },
|
|
107
|
+
],
|
|
108
|
+
help: 'Generates a continuation brief from the latest checkpoint and prints the ' +
|
|
109
|
+
'markdown to stdout. Modes follow ADR-0010 budgets. With --json, the body ' +
|
|
110
|
+
'is in `markdown` and the count is in `chars`.',
|
|
111
|
+
examples: ['kairo brief --tiny', 'kairo brief --deep --max-chars 8000'],
|
|
112
|
+
async run(ctx) {
|
|
113
|
+
if (!requireKairo(ctx))
|
|
114
|
+
return { exitCode: 3 };
|
|
115
|
+
const { values } = parseFlags(ctx.argv, briefCmd.flags ?? []);
|
|
116
|
+
let mode = 'normal';
|
|
117
|
+
if (values.tiny)
|
|
118
|
+
mode = 'tiny';
|
|
119
|
+
else if (values.deep)
|
|
120
|
+
mode = 'deep';
|
|
121
|
+
const adapter = withRedaction(new FileStorageAdapter(ctx.projectRoot), systemClock);
|
|
122
|
+
const sessions = new SessionManager(adapter, systemClock);
|
|
123
|
+
const opts = { mode };
|
|
124
|
+
const maxChars = values['max-chars'];
|
|
125
|
+
if (typeof maxChars === 'number' && maxChars > 0) {
|
|
126
|
+
opts.maxChars = maxChars;
|
|
127
|
+
}
|
|
128
|
+
const b = await sessions.buildBrief(opts);
|
|
129
|
+
if (!b) {
|
|
130
|
+
if (ctx.out.maybeJson({ ok: false, reason: 'no-checkpoint' }))
|
|
131
|
+
return { exitCode: 0 };
|
|
132
|
+
ctx.out.info(ctx.out.dim('No checkpoint yet.'));
|
|
133
|
+
return { exitCode: 0 };
|
|
134
|
+
}
|
|
135
|
+
if (ctx.out.maybeJson({ mode: b.mode, chars: b.chars, markdown: b.markdown })) {
|
|
136
|
+
return { exitCode: 0 };
|
|
137
|
+
}
|
|
138
|
+
ctx.out.write(b.markdown);
|
|
139
|
+
if (!b.markdown.endsWith('\n'))
|
|
140
|
+
ctx.out.line();
|
|
141
|
+
return { exitCode: 0 };
|
|
142
|
+
},
|
|
143
|
+
};
|
|
144
|
+
const continueCmd = {
|
|
145
|
+
name: 'continue',
|
|
146
|
+
summary: 'Alias for `brief --normal`.',
|
|
147
|
+
help: 'Prints the latest continuation brief in normal mode.',
|
|
148
|
+
examples: ['kairo continue'],
|
|
149
|
+
run(ctx) {
|
|
150
|
+
return briefCmd.run({ ...ctx, argv: ['--normal', ...ctx.argv] });
|
|
151
|
+
},
|
|
152
|
+
};
|
|
153
|
+
// ── sessions ───────────────────────────────────────────────────────────
|
|
154
|
+
const sessionsCmd = {
|
|
155
|
+
name: 'sessions',
|
|
156
|
+
summary: 'List sessions, or show one.',
|
|
157
|
+
args: '[<id>]',
|
|
158
|
+
help: 'Without an argument: list every session. With an id: print its full state.',
|
|
159
|
+
examples: ['kairo sessions', 'kairo sessions 01HX...', 'kairo sessions --json'],
|
|
160
|
+
async run(ctx) {
|
|
161
|
+
if (!requireKairo(ctx))
|
|
162
|
+
return { exitCode: 3 };
|
|
163
|
+
const k = new KairoClient({ projectRoot: ctx.projectRoot });
|
|
164
|
+
const { positional } = parseFlags(ctx.argv, []);
|
|
165
|
+
const id = positional[0];
|
|
166
|
+
if (id) {
|
|
167
|
+
const s = await k.session(id);
|
|
168
|
+
if (!s) {
|
|
169
|
+
if (ctx.out.maybeJson({ ok: false, reason: 'not-found' }))
|
|
170
|
+
return { exitCode: 4 };
|
|
171
|
+
ctx.out.error(`Session ${id} not found`);
|
|
172
|
+
return { exitCode: 4 };
|
|
173
|
+
}
|
|
174
|
+
if (ctx.out.maybeJson(s))
|
|
175
|
+
return { exitCode: 0 };
|
|
176
|
+
ctx.out.heading(`Session ${s.id}`);
|
|
177
|
+
ctx.out.kv([
|
|
178
|
+
['agent', s.agent],
|
|
179
|
+
['task', s.task],
|
|
180
|
+
['status', s.status],
|
|
181
|
+
['started', s.startedAt],
|
|
182
|
+
['last activity', s.lastActivityAt],
|
|
183
|
+
['changed files', String(Object.keys(s.changedFiles).length)],
|
|
184
|
+
['decisions', String(s.decisions.length)],
|
|
185
|
+
['errors', String(s.errors.length)],
|
|
186
|
+
['heartbeats', String(s.heartbeats)],
|
|
187
|
+
]);
|
|
188
|
+
return { exitCode: 0 };
|
|
189
|
+
}
|
|
190
|
+
const list = await k.sessions();
|
|
191
|
+
if (ctx.out.maybeJson({ sessions: list }))
|
|
192
|
+
return { exitCode: 0 };
|
|
193
|
+
ctx.out.heading(`Sessions (${list.length})`);
|
|
194
|
+
ctx.out.table(['Started', 'ID', 'Agent', 'Status', 'Files', 'Errors', 'Task'], list
|
|
195
|
+
.slice()
|
|
196
|
+
.reverse()
|
|
197
|
+
.map((s) => [
|
|
198
|
+
s.startedAt.slice(0, 19),
|
|
199
|
+
shortId(s.id, 14),
|
|
200
|
+
s.agent,
|
|
201
|
+
s.status,
|
|
202
|
+
String(s.changedFiles),
|
|
203
|
+
String(s.errors),
|
|
204
|
+
s.task.length > 40 ? s.task.slice(0, 37) + '…' : s.task,
|
|
205
|
+
]));
|
|
206
|
+
return { exitCode: 0 };
|
|
207
|
+
},
|
|
208
|
+
};
|
|
209
|
+
// ── checkpoints ────────────────────────────────────────────────────────
|
|
210
|
+
const checkpointsCmd = {
|
|
211
|
+
name: 'checkpoints',
|
|
212
|
+
summary: 'List checkpoints, or show one with lineage.',
|
|
213
|
+
args: '[<id>]',
|
|
214
|
+
help: 'Without an argument: list every checkpoint. With an id: show details + lineage chain.',
|
|
215
|
+
examples: ['kairo checkpoints', 'kairo checkpoints 01HX...'],
|
|
216
|
+
async run(ctx) {
|
|
217
|
+
if (!requireKairo(ctx))
|
|
218
|
+
return { exitCode: 3 };
|
|
219
|
+
const k = new KairoClient({ projectRoot: ctx.projectRoot });
|
|
220
|
+
const { positional } = parseFlags(ctx.argv, []);
|
|
221
|
+
const id = positional[0];
|
|
222
|
+
if (id) {
|
|
223
|
+
const cp = await k.checkpoint(id);
|
|
224
|
+
if (!cp) {
|
|
225
|
+
if (ctx.out.maybeJson({ ok: false, reason: 'not-found' }))
|
|
226
|
+
return { exitCode: 4 };
|
|
227
|
+
ctx.out.error(`Checkpoint ${id} not found`);
|
|
228
|
+
return { exitCode: 4 };
|
|
229
|
+
}
|
|
230
|
+
if (ctx.out.maybeJson(cp))
|
|
231
|
+
return { exitCode: 0 };
|
|
232
|
+
ctx.out.heading(`Checkpoint ${cp.id}`);
|
|
233
|
+
ctx.out.kv([
|
|
234
|
+
['session', cp.sessionId],
|
|
235
|
+
['agent', cp.agent],
|
|
236
|
+
['created', cp.createdAt],
|
|
237
|
+
['reason', cp.reason],
|
|
238
|
+
['task', cp.task],
|
|
239
|
+
['risk', `${cp.risk.level.toUpperCase()} (${cp.risk.score.toFixed(2)})`],
|
|
240
|
+
['brief', cp.continuationRef],
|
|
241
|
+
]);
|
|
242
|
+
return { exitCode: 0 };
|
|
243
|
+
}
|
|
244
|
+
const list = await k.checkpoints();
|
|
245
|
+
if (ctx.out.maybeJson({ checkpoints: list }))
|
|
246
|
+
return { exitCode: 0 };
|
|
247
|
+
ctx.out.heading(`Checkpoints (${list.length})`);
|
|
248
|
+
ctx.out.table(['Created', 'ID', 'Reason', 'Risk', 'Task'], list
|
|
249
|
+
.slice()
|
|
250
|
+
.reverse()
|
|
251
|
+
.map((c) => [
|
|
252
|
+
c.createdAt.slice(0, 19),
|
|
253
|
+
shortId(c.id, 14),
|
|
254
|
+
c.reason,
|
|
255
|
+
`${c.riskLevel}(${c.riskScore.toFixed(2)})`,
|
|
256
|
+
c.task.length > 40 ? c.task.slice(0, 37) + '…' : c.task,
|
|
257
|
+
]));
|
|
258
|
+
return { exitCode: 0 };
|
|
259
|
+
},
|
|
260
|
+
};
|
|
261
|
+
// ── graph ──────────────────────────────────────────────────────────────
|
|
262
|
+
const graphCmd = {
|
|
263
|
+
name: 'graph',
|
|
264
|
+
summary: 'List graphs, or print one (Mermaid).',
|
|
265
|
+
args: '[<kind>]',
|
|
266
|
+
help: 'Without an argument: list available graph kinds. With a kind ' +
|
|
267
|
+
'(module/architecture/service/pipeline): print the Mermaid source.',
|
|
268
|
+
examples: ['kairo graph', 'kairo graph module'],
|
|
269
|
+
async run(ctx) {
|
|
270
|
+
if (!requireKairo(ctx))
|
|
271
|
+
return { exitCode: 3 };
|
|
272
|
+
const k = new KairoClient({ projectRoot: ctx.projectRoot });
|
|
273
|
+
const { positional } = parseFlags(ctx.argv, []);
|
|
274
|
+
const kind = positional[0];
|
|
275
|
+
if (!kind) {
|
|
276
|
+
const list = await k.graphs();
|
|
277
|
+
if (ctx.out.maybeJson({ graphs: list }))
|
|
278
|
+
return { exitCode: 0 };
|
|
279
|
+
ctx.out.heading('Graphs');
|
|
280
|
+
if (list.length === 0)
|
|
281
|
+
ctx.out.info(ctx.out.dim(' (none)'));
|
|
282
|
+
else
|
|
283
|
+
for (const g of list)
|
|
284
|
+
ctx.out.line(` ${g}`);
|
|
285
|
+
return { exitCode: 0 };
|
|
286
|
+
}
|
|
287
|
+
const g = await k.graph(kind);
|
|
288
|
+
if (!g) {
|
|
289
|
+
if (ctx.out.maybeJson({ ok: false, reason: 'not-found' }))
|
|
290
|
+
return { exitCode: 4 };
|
|
291
|
+
ctx.out.error(`Graph "${kind}" not found`);
|
|
292
|
+
return { exitCode: 4 };
|
|
293
|
+
}
|
|
294
|
+
if (ctx.out.maybeJson(g))
|
|
295
|
+
return { exitCode: 0 };
|
|
296
|
+
ctx.out.write(g.mermaid);
|
|
297
|
+
if (!g.mermaid.endsWith('\n'))
|
|
298
|
+
ctx.out.line();
|
|
299
|
+
return { exitCode: 0 };
|
|
300
|
+
},
|
|
301
|
+
};
|
|
302
|
+
// ── search ─────────────────────────────────────────────────────────────
|
|
303
|
+
const searchCmd = {
|
|
304
|
+
name: 'search',
|
|
305
|
+
summary: 'Semantic memory search.',
|
|
306
|
+
args: '<query>',
|
|
307
|
+
flags: [{ name: 'limit', type: 'number', default: 5, help: 'Max results (default 5).' }],
|
|
308
|
+
help: "Searches the vector index using the project's configured embedder. " +
|
|
309
|
+
'Results are compact (kind, locator, score, short why) — for the full ' +
|
|
310
|
+
'reasoning use the MCP tool kairo_memory_search.',
|
|
311
|
+
examples: ['kairo search "auth middleware"', 'kairo search payment --limit 10'],
|
|
312
|
+
async run(ctx) {
|
|
313
|
+
if (!requireKairo(ctx))
|
|
314
|
+
return { exitCode: 3 };
|
|
315
|
+
const { values, positional } = parseFlags(ctx.argv, searchCmd.flags ?? []);
|
|
316
|
+
const query = positional.join(' ').trim();
|
|
317
|
+
if (!query) {
|
|
318
|
+
ctx.out.error('Missing query.');
|
|
319
|
+
ctx.out.info('Usage: kairo search <query>');
|
|
320
|
+
return { exitCode: 2 };
|
|
321
|
+
}
|
|
322
|
+
const adapter = withRedaction(new FileStorageAdapter(ctx.projectRoot), systemClock);
|
|
323
|
+
const sessions = new SessionManager(adapter, systemClock);
|
|
324
|
+
const results = await sessions.searchMemory({ text: query, limit: Number(values.limit) });
|
|
325
|
+
if (ctx.out.maybeJson({ query, results }))
|
|
326
|
+
return { exitCode: 0 };
|
|
327
|
+
if (results.length === 0) {
|
|
328
|
+
ctx.out.info(ctx.out.dim('No results. Try kairo_memory_index first.'));
|
|
329
|
+
return { exitCode: 0 };
|
|
330
|
+
}
|
|
331
|
+
ctx.out.heading(`Memory: "${query}" (${results.length})`);
|
|
332
|
+
for (let i = 0; i < results.length; i++) {
|
|
333
|
+
const r = results[i];
|
|
334
|
+
const why = r.why.length > 100 ? r.why.slice(0, 97) + '…' : r.why;
|
|
335
|
+
ctx.out.line(` ${ctx.out.dim(i + 1 + '.')} [${ctx.out.cyan(r.chunk.kind)}] ${r.chunk.locator} ` +
|
|
336
|
+
ctx.out.dim(`(score ${r.score.toFixed(3)}) — ${why}`));
|
|
337
|
+
}
|
|
338
|
+
return { exitCode: 0 };
|
|
339
|
+
},
|
|
340
|
+
};
|
|
341
|
+
// ── stability ──────────────────────────────────────────────────────────
|
|
342
|
+
const stabilityCmd = {
|
|
343
|
+
name: 'stability',
|
|
344
|
+
summary: 'Lookup the stability tier of a Kairo surface.',
|
|
345
|
+
args: '[<id>]',
|
|
346
|
+
help: 'With an id (tool name, route, schema name): print its stability tier. ' +
|
|
347
|
+
'Without an id: dump the full registry.',
|
|
348
|
+
examples: ['kairo stability kairo_session_start', 'kairo stability --json'],
|
|
349
|
+
run(ctx) {
|
|
350
|
+
const { positional } = parseFlags(ctx.argv, []);
|
|
351
|
+
const id = positional[0];
|
|
352
|
+
if (!id) {
|
|
353
|
+
if (ctx.out.maybeJson({ entries: STABILITY }))
|
|
354
|
+
return Promise.resolve({ exitCode: 0 });
|
|
355
|
+
ctx.out.heading(`Stability registry (${STABILITY.length})`);
|
|
356
|
+
ctx.out.table(['Tier', 'Surface', 'ID', 'Since'], STABILITY.slice()
|
|
357
|
+
.sort((a, b) => (a.surface + a.id).localeCompare(b.surface + b.id))
|
|
358
|
+
.map((e) => [tierStyle(ctx, e.tier), e.surface, e.id, e.since]));
|
|
359
|
+
return Promise.resolve({ exitCode: 0 });
|
|
360
|
+
}
|
|
361
|
+
const entry = stabilityOf(id);
|
|
362
|
+
if (!entry) {
|
|
363
|
+
if (ctx.out.maybeJson({ id, entry: null }))
|
|
364
|
+
return Promise.resolve({ exitCode: 0 });
|
|
365
|
+
ctx.out.warn(`"${id}" is not in the registry — treat as internal.`);
|
|
366
|
+
return Promise.resolve({ exitCode: 0 });
|
|
367
|
+
}
|
|
368
|
+
if (ctx.out.maybeJson(entry))
|
|
369
|
+
return Promise.resolve({ exitCode: 0 });
|
|
370
|
+
ctx.out.kv([
|
|
371
|
+
['id', entry.id],
|
|
372
|
+
['surface', entry.surface],
|
|
373
|
+
['tier', tierStyle(ctx, entry.tier)],
|
|
374
|
+
['since', entry.since],
|
|
375
|
+
...(entry.note ? [['note', entry.note]] : []),
|
|
376
|
+
]);
|
|
377
|
+
return Promise.resolve({ exitCode: 0 });
|
|
378
|
+
},
|
|
379
|
+
};
|
|
380
|
+
function tierStyle(ctx, tier) {
|
|
381
|
+
if (tier === 'stable')
|
|
382
|
+
return ctx.out.green(tier);
|
|
383
|
+
if (tier === 'experimental')
|
|
384
|
+
return ctx.out.yellow(tier);
|
|
385
|
+
if (tier === 'deprecated')
|
|
386
|
+
return ctx.out.red(tier);
|
|
387
|
+
return ctx.out.dim(tier);
|
|
388
|
+
}
|
|
389
|
+
// ── plugins ────────────────────────────────────────────────────────────
|
|
390
|
+
const pluginsCmd = {
|
|
391
|
+
name: 'plugins',
|
|
392
|
+
summary: 'List plugin manifests under .kairo/plugins/.',
|
|
393
|
+
help: 'Reads .kairo/plugins/*.json and .kairo/plugins.json. Validates each ' +
|
|
394
|
+
'manifest. NO code is executed in-process (ADR-0015).',
|
|
395
|
+
examples: ['kairo plugins', 'kairo plugins --json'],
|
|
396
|
+
async run(ctx) {
|
|
397
|
+
const plugins = await loadPlugins(ctx.projectRoot);
|
|
398
|
+
if (ctx.out.maybeJson({ plugins }))
|
|
399
|
+
return { exitCode: 0 };
|
|
400
|
+
ctx.out.heading(`Plugins (${plugins.length})`);
|
|
401
|
+
if (plugins.length === 0) {
|
|
402
|
+
ctx.out.info(ctx.out.dim(' (none)'));
|
|
403
|
+
return { exitCode: 0 };
|
|
404
|
+
}
|
|
405
|
+
ctx.out.table(['Name', 'Version', 'Compat', 'Capabilities'], plugins.map((p) => [
|
|
406
|
+
p.compatible ? p.manifest.name : ctx.out.yellow(p.manifest.name),
|
|
407
|
+
p.manifest.version,
|
|
408
|
+
p.compatible ? ctx.out.green('ok') : ctx.out.yellow('mismatch'),
|
|
409
|
+
p.manifest.capabilities.join(', '),
|
|
410
|
+
]));
|
|
411
|
+
return { exitCode: 0 };
|
|
412
|
+
},
|
|
413
|
+
};
|
|
414
|
+
// ── inspect ────────────────────────────────────────────────────────────
|
|
415
|
+
const inspectCmd = {
|
|
416
|
+
name: 'inspect',
|
|
417
|
+
summary: 'Launch the local web inspector.',
|
|
418
|
+
flags: [
|
|
419
|
+
{ name: 'port', short: 'p', type: 'number', default: 4173, help: 'Port (default 4173).' },
|
|
420
|
+
{ name: 'host', type: 'string', default: '127.0.0.1', help: 'Host (default 127.0.0.1).' },
|
|
421
|
+
],
|
|
422
|
+
help: 'Loopback-only, read-only HTML inspector over .kairo/. Press Ctrl+C to stop.',
|
|
423
|
+
examples: ['kairo inspect', 'kairo inspect --port 5050'],
|
|
424
|
+
async run(ctx) {
|
|
425
|
+
if (!requireKairo(ctx))
|
|
426
|
+
return { exitCode: 3 };
|
|
427
|
+
const { values } = parseFlags(ctx.argv, inspectCmd.flags ?? []);
|
|
428
|
+
const handle = await startInspectServer({
|
|
429
|
+
projectRoot: ctx.projectRoot,
|
|
430
|
+
port: Number(values.port),
|
|
431
|
+
host: String(values.host),
|
|
432
|
+
});
|
|
433
|
+
if (ctx.out.maybeJson({ url: handle.url, port: handle.port })) {
|
|
434
|
+
// In JSON mode, keep the server up but exit-on-signal still works.
|
|
435
|
+
}
|
|
436
|
+
else {
|
|
437
|
+
ctx.out.info(`${ctx.out.green('ready')} ${handle.url}`);
|
|
438
|
+
ctx.out.info(ctx.out.dim(` project: ${ctx.projectRoot}`));
|
|
439
|
+
ctx.out.info(ctx.out.dim(' read-only · no network · Ctrl+C to stop'));
|
|
440
|
+
}
|
|
441
|
+
return new Promise((resolve) => {
|
|
442
|
+
const stop = () => {
|
|
443
|
+
void handle.close().finally(() => resolve({ exitCode: 0 }));
|
|
444
|
+
};
|
|
445
|
+
process.on('SIGINT', stop);
|
|
446
|
+
process.on('SIGTERM', stop);
|
|
447
|
+
});
|
|
448
|
+
},
|
|
449
|
+
};
|
|
450
|
+
// ── serve ──────────────────────────────────────────────────────────────
|
|
451
|
+
const serveCmd = {
|
|
452
|
+
name: 'serve',
|
|
453
|
+
summary: 'Run the MCP server on stdio.',
|
|
454
|
+
help: 'Same as the `kairo-mcp` bin. The MCP host launches it; you rarely run it by hand.',
|
|
455
|
+
examples: ['kairo serve'],
|
|
456
|
+
run(ctx) {
|
|
457
|
+
// Delegate to the existing index.js bin so we don't duplicate startup.
|
|
458
|
+
const here = new URL(import.meta.url).pathname;
|
|
459
|
+
// Climb from dist/cli/commands.js to dist/index.js
|
|
460
|
+
const idx = resolve(here, '..', '..', 'index.js');
|
|
461
|
+
const child = spawn(process.execPath, [idx], {
|
|
462
|
+
stdio: 'inherit',
|
|
463
|
+
env: { ...process.env, KAIRO_PROJECT_ROOT: ctx.projectRoot },
|
|
464
|
+
});
|
|
465
|
+
return new Promise((res) => {
|
|
466
|
+
child.on('exit', (code) => res({ exitCode: code ?? 0 }));
|
|
467
|
+
});
|
|
468
|
+
},
|
|
469
|
+
};
|
|
470
|
+
// ── snapshot ───────────────────────────────────────────────────────────
|
|
471
|
+
const snapshotCmd = {
|
|
472
|
+
name: 'snapshot',
|
|
473
|
+
summary: 'Export or import a .kairo/ snapshot.',
|
|
474
|
+
args: '<export|import> [args]',
|
|
475
|
+
help: 'Subcommands:\n' +
|
|
476
|
+
' export [<path>] Write .kairo/ to a single JSON file.\n' +
|
|
477
|
+
' import <path> [--force] Read a snapshot into the current project.',
|
|
478
|
+
examples: [
|
|
479
|
+
'kairo snapshot export',
|
|
480
|
+
'kairo snapshot export ./my-backup.json',
|
|
481
|
+
'kairo snapshot import ./team-snapshot.json --force',
|
|
482
|
+
],
|
|
483
|
+
async run(ctx) {
|
|
484
|
+
const sub = ctx.argv[0];
|
|
485
|
+
const rest = ctx.argv.slice(1);
|
|
486
|
+
if (sub === 'export') {
|
|
487
|
+
if (!requireKairo(ctx))
|
|
488
|
+
return { exitCode: 3 };
|
|
489
|
+
const { positional } = parseFlags(rest, []);
|
|
490
|
+
const path = positional[0];
|
|
491
|
+
const r = await exportSnapshot(ctx.projectRoot, path ? { path } : {});
|
|
492
|
+
if (ctx.out.maybeJson({
|
|
493
|
+
path: r.path,
|
|
494
|
+
bytes: r.bytes,
|
|
495
|
+
sha256: r.contentSha256,
|
|
496
|
+
manifest: r.snapshot.manifest,
|
|
497
|
+
})) {
|
|
498
|
+
return { exitCode: 0 };
|
|
499
|
+
}
|
|
500
|
+
ctx.out.kv([
|
|
501
|
+
['path', r.path],
|
|
502
|
+
['bytes', r.bytes.toLocaleString('en-US')],
|
|
503
|
+
['sha256', r.contentSha256],
|
|
504
|
+
['events', String(r.snapshot.manifest.counts.events)],
|
|
505
|
+
['checkpoints', String(r.snapshot.manifest.counts.checkpoints)],
|
|
506
|
+
['sessions', String(r.snapshot.manifest.counts.sessions)],
|
|
507
|
+
]);
|
|
508
|
+
return { exitCode: 0 };
|
|
509
|
+
}
|
|
510
|
+
if (sub === 'import') {
|
|
511
|
+
const { positional, values } = parseFlags(rest, [
|
|
512
|
+
{ name: 'force', type: 'boolean', help: 'Overwrite a non-empty .kairo/.' },
|
|
513
|
+
]);
|
|
514
|
+
const path = positional[0];
|
|
515
|
+
if (!path) {
|
|
516
|
+
ctx.out.error('Missing snapshot path.');
|
|
517
|
+
ctx.out.info('Usage: kairo snapshot import <path> [--force]');
|
|
518
|
+
return { exitCode: 2 };
|
|
519
|
+
}
|
|
520
|
+
const r = await importSnapshot(ctx.projectRoot, path, { force: Boolean(values.force) });
|
|
521
|
+
if (ctx.out.maybeJson(r))
|
|
522
|
+
return { exitCode: 0 };
|
|
523
|
+
ctx.out.kv([
|
|
524
|
+
['target', r.targetProjectRoot],
|
|
525
|
+
['events', String(r.ingested.events)],
|
|
526
|
+
['sessions', String(r.ingested.sessions)],
|
|
527
|
+
['checkpoints', String(r.ingested.checkpoints)],
|
|
528
|
+
['continuations', String(r.ingested.continuations)],
|
|
529
|
+
['intelligence', String(r.ingested.intelligence)],
|
|
530
|
+
['vector', String(r.ingested.vectorIndex)],
|
|
531
|
+
]);
|
|
532
|
+
if (r.warnings.length > 0)
|
|
533
|
+
for (const w of r.warnings)
|
|
534
|
+
ctx.out.warn(w);
|
|
535
|
+
return { exitCode: 0 };
|
|
536
|
+
}
|
|
537
|
+
ctx.out.error(`Unknown subcommand: snapshot ${sub ?? ''}`);
|
|
538
|
+
ctx.out.info(snapshotCmd.help);
|
|
539
|
+
return { exitCode: 2 };
|
|
540
|
+
},
|
|
541
|
+
};
|
|
542
|
+
// ── compact ────────────────────────────────────────────────────────────
|
|
543
|
+
const compactCmd = {
|
|
544
|
+
name: 'compact',
|
|
545
|
+
summary: 'Archive stale events (dry-run by default).',
|
|
546
|
+
flags: [
|
|
547
|
+
{
|
|
548
|
+
name: 'dry-run',
|
|
549
|
+
type: 'boolean',
|
|
550
|
+
default: true,
|
|
551
|
+
help: 'Default. Pass --no-dry-run to apply.',
|
|
552
|
+
},
|
|
553
|
+
{
|
|
554
|
+
name: 'days',
|
|
555
|
+
type: 'number',
|
|
556
|
+
default: 90,
|
|
557
|
+
help: 'Sessions ended longer ago than this are candidates.',
|
|
558
|
+
},
|
|
559
|
+
],
|
|
560
|
+
help: 'Conservative compaction (ADR-0014). Archive — never delete — events from ' +
|
|
561
|
+
'ended sessions older than --days. Lineage-protected: events referenced by ' +
|
|
562
|
+
'any existing checkpoint are never archived. Default is --dry-run.',
|
|
563
|
+
examples: ['kairo compact', 'kairo compact --days 30', 'kairo compact --no-dry-run'],
|
|
564
|
+
async run(ctx) {
|
|
565
|
+
if (!requireKairo(ctx))
|
|
566
|
+
return { exitCode: 3 };
|
|
567
|
+
const { values } = parseFlags(ctx.argv, compactCmd.flags ?? []);
|
|
568
|
+
const r = await compact(ctx.projectRoot, {
|
|
569
|
+
dryRun: Boolean(values['dry-run']),
|
|
570
|
+
olderThanDays: Number(values.days),
|
|
571
|
+
});
|
|
572
|
+
if (ctx.out.maybeJson(r))
|
|
573
|
+
return { exitCode: 0 };
|
|
574
|
+
ctx.out.kv([
|
|
575
|
+
['mode', r.applied ? ctx.out.green('applied') : ctx.out.yellow('dry-run')],
|
|
576
|
+
['candidates', String(r.plan.candidateEvents)],
|
|
577
|
+
['retained', String(r.plan.retainedEvents)],
|
|
578
|
+
['sessions', String(r.plan.candidateSessionIds.length)],
|
|
579
|
+
['report', r.plan.reportPath],
|
|
580
|
+
...(r.applied ? [['archive', r.plan.archivePath]] : []),
|
|
581
|
+
]);
|
|
582
|
+
if (!r.applied && r.plan.candidateEvents > 0) {
|
|
583
|
+
ctx.out.hint('re-run with `--no-dry-run` to apply.');
|
|
584
|
+
}
|
|
585
|
+
return { exitCode: 0 };
|
|
586
|
+
},
|
|
587
|
+
};
|
|
588
|
+
// ── benchmark ──────────────────────────────────────────────────────────
|
|
589
|
+
const benchmarkCmd = {
|
|
590
|
+
name: 'benchmark',
|
|
591
|
+
summary: 'Run the deterministic benchmark suite.',
|
|
592
|
+
flags: [
|
|
593
|
+
{
|
|
594
|
+
name: 'iterations',
|
|
595
|
+
short: 'n',
|
|
596
|
+
type: 'number',
|
|
597
|
+
default: 5,
|
|
598
|
+
help: 'Iterations per scenario.',
|
|
599
|
+
},
|
|
600
|
+
{ name: 'only', type: 'string', help: 'Comma-separated subset of scenario names.' },
|
|
601
|
+
],
|
|
602
|
+
help: 'Runs the benchmark scenarios over the current project and writes ' +
|
|
603
|
+
'.kairo/reports/PERFORMANCE.md. Wall-clock timings are host-dependent — ' +
|
|
604
|
+
'use for relative comparison and regression detection.',
|
|
605
|
+
examples: [
|
|
606
|
+
'kairo benchmark',
|
|
607
|
+
'kairo benchmark --iterations 10',
|
|
608
|
+
'kairo benchmark --only repo.cold-scan,repo.warm-scan',
|
|
609
|
+
],
|
|
610
|
+
async run(ctx) {
|
|
611
|
+
if (!requireKairo(ctx))
|
|
612
|
+
return { exitCode: 3 };
|
|
613
|
+
const { values } = parseFlags(ctx.argv, benchmarkCmd.flags ?? []);
|
|
614
|
+
const adapter = withRedaction(new FileStorageAdapter(ctx.projectRoot), systemClock);
|
|
615
|
+
const sessions = new SessionManager(adapter, systemClock);
|
|
616
|
+
await sessions.init();
|
|
617
|
+
const opts = {
|
|
618
|
+
iterations: Number(values.iterations),
|
|
619
|
+
};
|
|
620
|
+
if (typeof values.only === 'string' && values.only.length > 0) {
|
|
621
|
+
opts.only = String(values.only)
|
|
622
|
+
.split(',')
|
|
623
|
+
.map((s) => s.trim())
|
|
624
|
+
.filter(Boolean);
|
|
625
|
+
}
|
|
626
|
+
const r = await runBenchmark(sessions, ctx.projectRoot, opts);
|
|
627
|
+
if (ctx.out.maybeJson({ reportPath: r.reportPath, report: r.report }))
|
|
628
|
+
return { exitCode: 0 };
|
|
629
|
+
ctx.out.heading(`Benchmark (${r.report.scenarios.length} scenarios)`);
|
|
630
|
+
ctx.out.table(['Scenario', 'n', 'min', 'median', 'p95', 'max'], r.report.scenarios.map((s) => s.skipped
|
|
631
|
+
? [s.name, '-', ctx.out.dim('skipped'), '-', '-', '-']
|
|
632
|
+
: [
|
|
633
|
+
s.name,
|
|
634
|
+
String(s.stats.iterations),
|
|
635
|
+
s.stats.min.toFixed(1),
|
|
636
|
+
s.stats.median.toFixed(1),
|
|
637
|
+
s.stats.p95.toFixed(1),
|
|
638
|
+
s.stats.max.toFixed(1),
|
|
639
|
+
]));
|
|
640
|
+
ctx.out.line();
|
|
641
|
+
ctx.out.info(ctx.out.dim(` sum-of-medians: ${r.report.totalMs.toFixed(1)} ms`));
|
|
642
|
+
ctx.out.hint(`report at ${r.reportPath}`);
|
|
643
|
+
return { exitCode: 0 };
|
|
644
|
+
},
|
|
645
|
+
};
|
|
646
|
+
// ── init ───────────────────────────────────────────────────────────────
|
|
647
|
+
const initCmd = {
|
|
648
|
+
name: 'init',
|
|
649
|
+
summary: 'Wire Kairo into the current project (.mcp.json + .gitignore).',
|
|
650
|
+
flags: [
|
|
651
|
+
{ name: 'force', type: 'boolean', help: 'Overwrite an existing .mcp.json kairo entry.' },
|
|
652
|
+
{ name: 'host', type: 'string', default: 'auto', help: 'MCP host: claude | auto | none.' },
|
|
653
|
+
],
|
|
654
|
+
help: 'Idempotent. Writes .mcp.json (merging if present) so Claude Code (or any ' +
|
|
655
|
+
'MCP host) launches the kairo-mcp server when opened in this project. ' +
|
|
656
|
+
'Appends `.kairo/` to .gitignore if not already present.',
|
|
657
|
+
examples: ['kairo init', 'kairo init --force'],
|
|
658
|
+
async run(ctx) {
|
|
659
|
+
const { values } = parseFlags(ctx.argv, initCmd.flags ?? []);
|
|
660
|
+
const force = Boolean(values.force);
|
|
661
|
+
const result = { projectRoot: ctx.projectRoot };
|
|
662
|
+
// 1. .mcp.json
|
|
663
|
+
const mcpPath = join(ctx.projectRoot, '.mcp.json');
|
|
664
|
+
let mcp = {};
|
|
665
|
+
if (existsSync(mcpPath)) {
|
|
666
|
+
try {
|
|
667
|
+
mcp = JSON.parse(await readFile(mcpPath, 'utf8'));
|
|
668
|
+
}
|
|
669
|
+
catch {
|
|
670
|
+
ctx.out.warn(`Existing .mcp.json is not valid JSON; rewriting.`);
|
|
671
|
+
}
|
|
672
|
+
}
|
|
673
|
+
mcp.mcpServers ??= {};
|
|
674
|
+
if (mcp.mcpServers.kairo && !force) {
|
|
675
|
+
ctx.out.info(ctx.out.dim(' .mcp.json already declares kairo — pass --force to overwrite.'));
|
|
676
|
+
result.mcpJson = 'skipped';
|
|
677
|
+
}
|
|
678
|
+
else {
|
|
679
|
+
mcp.mcpServers.kairo = {
|
|
680
|
+
command: 'node',
|
|
681
|
+
args: ['./node_modules/kairo-mcp/dist/index.js'],
|
|
682
|
+
env: { KAIRO_PROJECT_ROOT: '.' },
|
|
683
|
+
};
|
|
684
|
+
await writeFile(mcpPath, JSON.stringify(mcp, null, 2) + '\n', 'utf8');
|
|
685
|
+
result.mcpJson = 'written';
|
|
686
|
+
}
|
|
687
|
+
// 2. .gitignore
|
|
688
|
+
const giPath = join(ctx.projectRoot, '.gitignore');
|
|
689
|
+
let gi = '';
|
|
690
|
+
try {
|
|
691
|
+
gi = await readFile(giPath, 'utf8');
|
|
692
|
+
}
|
|
693
|
+
catch {
|
|
694
|
+
/* no .gitignore yet — create one */
|
|
695
|
+
}
|
|
696
|
+
if (gi.split(/\r?\n/).some((l) => l.trim() === '.kairo/' || l.trim() === '.kairo')) {
|
|
697
|
+
result.gitignore = 'skipped';
|
|
698
|
+
}
|
|
699
|
+
else {
|
|
700
|
+
const next = gi.length > 0 && !gi.endsWith('\n') ? gi + '\n.kairo/\n' : gi + '.kairo/\n';
|
|
701
|
+
await writeFile(giPath, next, 'utf8');
|
|
702
|
+
result.gitignore = 'appended';
|
|
703
|
+
}
|
|
704
|
+
// 3. Detect MCP host (best-effort — purely informational; no IO at runtime).
|
|
705
|
+
const host = detectMcpHost();
|
|
706
|
+
result.detectedHost = host;
|
|
707
|
+
if (ctx.out.maybeJson(result))
|
|
708
|
+
return { exitCode: 0 };
|
|
709
|
+
ctx.out.heading('Initialised');
|
|
710
|
+
ctx.out.kv([
|
|
711
|
+
['.mcp.json', String(result.mcpJson)],
|
|
712
|
+
['.gitignore', String(result.gitignore)],
|
|
713
|
+
['mcp host', host === 'none' ? ctx.out.dim('not detected') : ctx.out.green(host)],
|
|
714
|
+
]);
|
|
715
|
+
ctx.out.line();
|
|
716
|
+
ctx.out.line(ctx.out.bold('Next steps'));
|
|
717
|
+
if (host === 'claude') {
|
|
718
|
+
ctx.out.line(` ${ctx.out.dim('1.')} Open Claude Code in this project: ${ctx.out.cyan('claude')}`);
|
|
719
|
+
ctx.out.line(` ${ctx.out.dim('2.')} Inside the session, run: ${ctx.out.cyan('/mcp')}`);
|
|
720
|
+
ctx.out.line(` ${ctx.out.dim('→ you should see kairo · connected · 41 tools')}`);
|
|
721
|
+
ctx.out.line(` ${ctx.out.dim('3.')} If anything looks off, run: ${ctx.out.cyan('kairo doctor')}`);
|
|
722
|
+
}
|
|
723
|
+
else {
|
|
724
|
+
ctx.out.line(` ${ctx.out.dim('1.')} Open your MCP host (Claude Code / Cursor / Claude Desktop) in this project.`);
|
|
725
|
+
ctx.out.line(` ${ctx.out.dim('2.')} It should auto-load .mcp.json and connect Kairo on next session.`);
|
|
726
|
+
ctx.out.line(` ${ctx.out.dim('3.')} Verify with: ${ctx.out.cyan('kairo doctor')}`);
|
|
727
|
+
}
|
|
728
|
+
return { exitCode: 0 };
|
|
729
|
+
},
|
|
730
|
+
};
|
|
731
|
+
/** Best-effort detection of an MCP host on PATH. Synchronous & cross-platform. */
|
|
732
|
+
function detectMcpHost() {
|
|
733
|
+
// Avoid spawning anything; just check whether `claude` resolves on PATH.
|
|
734
|
+
// Node's `process.env.PATH` parsing is the simplest cross-platform check.
|
|
735
|
+
const pathDirs = (process.env.PATH ?? '').split(process.platform === 'win32' ? ';' : ':');
|
|
736
|
+
const exts = process.platform === 'win32' ? ['.cmd', '.exe', '.bat', ''] : [''];
|
|
737
|
+
for (const dir of pathDirs) {
|
|
738
|
+
if (!dir)
|
|
739
|
+
continue;
|
|
740
|
+
for (const ext of exts) {
|
|
741
|
+
if (existsSync(join(dir, `claude${ext}`)))
|
|
742
|
+
return 'claude';
|
|
743
|
+
}
|
|
744
|
+
}
|
|
745
|
+
return 'none';
|
|
746
|
+
}
|
|
747
|
+
// ── doctor ─────────────────────────────────────────────────────────────
|
|
748
|
+
const doctorCmd = {
|
|
749
|
+
name: 'doctor',
|
|
750
|
+
summary: "Health-check the project's Kairo install.",
|
|
751
|
+
help: 'Runs a fast sequence of checks: project root, kairo-mcp install, dist/ ' +
|
|
752
|
+
'present, .mcp.json wired, .kairo/ readable, quarantine empty, server ' +
|
|
753
|
+
'version match. Reports the first failing check with a fix.',
|
|
754
|
+
examples: ['kairo doctor', 'kairo doctor --json'],
|
|
755
|
+
async run(ctx) {
|
|
756
|
+
const root = ctx.projectRoot;
|
|
757
|
+
const checks = [];
|
|
758
|
+
const pkgJson = join(root, 'package.json');
|
|
759
|
+
checks.push({
|
|
760
|
+
name: 'project root',
|
|
761
|
+
ok: existsSync(pkgJson),
|
|
762
|
+
detail: existsSync(pkgJson) ? root : `no package.json at ${root}`,
|
|
763
|
+
});
|
|
764
|
+
// Consumer install path OR "I am running doctor from inside the kairo-mcp
|
|
765
|
+
// dev repo itself" (where dist/ is at the root, not under node_modules).
|
|
766
|
+
const consumerDist = join(root, 'node_modules', 'kairo-mcp', 'dist', 'index.js');
|
|
767
|
+
const selfDist = join(root, 'dist', 'index.js');
|
|
768
|
+
let mcpDist;
|
|
769
|
+
if (existsSync(consumerDist))
|
|
770
|
+
mcpDist = consumerDist;
|
|
771
|
+
else if (existsSync(selfDist))
|
|
772
|
+
mcpDist = selfDist;
|
|
773
|
+
checks.push({
|
|
774
|
+
name: 'kairo-mcp installed',
|
|
775
|
+
ok: mcpDist !== undefined,
|
|
776
|
+
detail: mcpDist ??
|
|
777
|
+
`run \`npm install github:sandy001-kki/Kairo#v${SERVER_VERSION}\` in this project`,
|
|
778
|
+
});
|
|
779
|
+
const mcpJsonPath = join(root, '.mcp.json');
|
|
780
|
+
let mcpWired = false;
|
|
781
|
+
if (existsSync(mcpJsonPath)) {
|
|
782
|
+
try {
|
|
783
|
+
const parsed = JSON.parse(await readFile(mcpJsonPath, 'utf8'));
|
|
784
|
+
mcpWired = Boolean(parsed.mcpServers?.kairo);
|
|
785
|
+
}
|
|
786
|
+
catch {
|
|
787
|
+
mcpWired = false;
|
|
788
|
+
}
|
|
789
|
+
}
|
|
790
|
+
checks.push({
|
|
791
|
+
name: '.mcp.json wires kairo',
|
|
792
|
+
ok: mcpWired,
|
|
793
|
+
detail: mcpWired ? mcpJsonPath : 'run `kairo init`',
|
|
794
|
+
});
|
|
795
|
+
const paths = kairoPaths(root);
|
|
796
|
+
const hasKairo = existsSync(paths.base);
|
|
797
|
+
checks.push({
|
|
798
|
+
name: '.kairo/ present',
|
|
799
|
+
ok: hasKairo,
|
|
800
|
+
detail: hasKairo ? paths.base : '(none yet — first MCP session creates it)',
|
|
801
|
+
});
|
|
802
|
+
let quarantineCount = 0;
|
|
803
|
+
try {
|
|
804
|
+
const qfiles = await readdir(paths.quarantineDir);
|
|
805
|
+
for (const f of qfiles) {
|
|
806
|
+
if (!f.endsWith('.jsonl'))
|
|
807
|
+
continue;
|
|
808
|
+
const raw = await readFile(join(paths.quarantineDir, f), 'utf8');
|
|
809
|
+
quarantineCount += raw.split('\n').filter((l) => l.trim()).length;
|
|
810
|
+
}
|
|
811
|
+
}
|
|
812
|
+
catch {
|
|
813
|
+
/* no quarantine dir is the happy case */
|
|
814
|
+
}
|
|
815
|
+
checks.push({
|
|
816
|
+
name: 'quarantine empty',
|
|
817
|
+
ok: quarantineCount === 0,
|
|
818
|
+
detail: quarantineCount === 0
|
|
819
|
+
? 'clean'
|
|
820
|
+
: `${quarantineCount} record(s) — inspect ${paths.quarantineDir}`,
|
|
821
|
+
});
|
|
822
|
+
// Version match — consumer install OR running from inside the kairo-mcp dev repo.
|
|
823
|
+
try {
|
|
824
|
+
const consumerPkg = join(root, 'node_modules', 'kairo-mcp', 'package.json');
|
|
825
|
+
const selfPkg = join(root, 'package.json');
|
|
826
|
+
let pkgPath;
|
|
827
|
+
if (existsSync(consumerPkg))
|
|
828
|
+
pkgPath = consumerPkg;
|
|
829
|
+
else if (existsSync(selfPkg)) {
|
|
830
|
+
// Only treat the project root's package.json as the kairo-mcp source of
|
|
831
|
+
// truth when it actually IS kairo-mcp (e.g. running doctor from the
|
|
832
|
+
// dev repo itself). Avoids confusing Flexdee's package.json with ours.
|
|
833
|
+
const own = JSON.parse(await readFile(selfPkg, 'utf8'));
|
|
834
|
+
if (own.name === 'kairo-mcp')
|
|
835
|
+
pkgPath = selfPkg;
|
|
836
|
+
}
|
|
837
|
+
if (!pkgPath) {
|
|
838
|
+
checks.push({
|
|
839
|
+
name: 'version match',
|
|
840
|
+
ok: false,
|
|
841
|
+
detail: 'kairo-mcp not installed in this project',
|
|
842
|
+
});
|
|
843
|
+
}
|
|
844
|
+
else {
|
|
845
|
+
const installed = JSON.parse(await readFile(pkgPath, 'utf8'))
|
|
846
|
+
.version;
|
|
847
|
+
checks.push({
|
|
848
|
+
name: 'version match',
|
|
849
|
+
ok: installed === SERVER_VERSION,
|
|
850
|
+
detail: `installed=${installed} cli=${SERVER_VERSION}`,
|
|
851
|
+
});
|
|
852
|
+
}
|
|
853
|
+
}
|
|
854
|
+
catch {
|
|
855
|
+
checks.push({
|
|
856
|
+
name: 'version match',
|
|
857
|
+
ok: false,
|
|
858
|
+
detail: 'cannot read installed package.json',
|
|
859
|
+
});
|
|
860
|
+
}
|
|
861
|
+
const failing = checks.filter((c) => !c.ok);
|
|
862
|
+
if (ctx.out.maybeJson({ ok: failing.length === 0, checks })) {
|
|
863
|
+
return { exitCode: failing.length === 0 ? 0 : 5 };
|
|
864
|
+
}
|
|
865
|
+
ctx.out.heading('Doctor');
|
|
866
|
+
for (const c of checks) {
|
|
867
|
+
const marker = c.ok ? ctx.out.green('ok ') : ctx.out.yellow('!! ');
|
|
868
|
+
ctx.out.line(` ${marker} ${c.name.padEnd(22)} ${ctx.out.dim(c.detail)}`);
|
|
869
|
+
}
|
|
870
|
+
if (failing.length === 0) {
|
|
871
|
+
ctx.out.hint(ctx.out.green('all checks passed.'));
|
|
872
|
+
return { exitCode: 0 };
|
|
873
|
+
}
|
|
874
|
+
ctx.out.hint(`${failing.length} check(s) need attention.`);
|
|
875
|
+
return { exitCode: 5 };
|
|
876
|
+
},
|
|
877
|
+
};
|
|
878
|
+
// ── completion ─────────────────────────────────────────────────────────
|
|
879
|
+
const completionCmd = {
|
|
880
|
+
name: 'completion',
|
|
881
|
+
summary: 'Print a shell completion script.',
|
|
882
|
+
args: '<bash|zsh|pwsh>',
|
|
883
|
+
help: 'Emits a deterministic completion script for the requested shell. ' +
|
|
884
|
+
'Completes top-level subcommand names.',
|
|
885
|
+
examples: ['kairo completion bash >> ~/.bashrc', 'kairo completion pwsh > $PROFILE.d/kairo.ps1'],
|
|
886
|
+
run(ctx) {
|
|
887
|
+
const shell = ctx.argv[0];
|
|
888
|
+
const names = ALL_COMMAND_NAMES.join(' ');
|
|
889
|
+
if (shell === 'bash') {
|
|
890
|
+
ctx.out.write(`_kairo() { local cur="\${COMP_WORDS[COMP_CWORD]}"; COMPREPLY=($(compgen -W "${names}" -- "$cur")); }\ncomplete -F _kairo kairo\n`);
|
|
891
|
+
return Promise.resolve({ exitCode: 0 });
|
|
892
|
+
}
|
|
893
|
+
if (shell === 'zsh') {
|
|
894
|
+
ctx.out.write(`#compdef kairo\n_kairo() { compadd ${names} }\n`);
|
|
895
|
+
return Promise.resolve({ exitCode: 0 });
|
|
896
|
+
}
|
|
897
|
+
if (shell === 'pwsh' || shell === 'powershell') {
|
|
898
|
+
ctx.out.write(`Register-ArgumentCompleter -CommandName kairo -ScriptBlock { param($wordToComplete) @(${names
|
|
899
|
+
.split(' ')
|
|
900
|
+
.map((n) => `'${n}'`)
|
|
901
|
+
.join(',')}) | Where-Object { $_ -like "$wordToComplete*" } | ForEach-Object { [System.Management.Automation.CompletionResult]::new($_) } }\n`);
|
|
902
|
+
return Promise.resolve({ exitCode: 0 });
|
|
903
|
+
}
|
|
904
|
+
ctx.out.error(`Unknown shell: ${shell ?? ''}. Use bash, zsh, or pwsh.`);
|
|
905
|
+
return Promise.resolve({ exitCode: 2 });
|
|
906
|
+
},
|
|
907
|
+
};
|
|
908
|
+
// ── registry ───────────────────────────────────────────────────────────
|
|
909
|
+
export const COMMANDS = [
|
|
910
|
+
initCmd,
|
|
911
|
+
statusCmd,
|
|
912
|
+
briefCmd,
|
|
913
|
+
continueCmd,
|
|
914
|
+
sessionsCmd,
|
|
915
|
+
checkpointsCmd,
|
|
916
|
+
graphCmd,
|
|
917
|
+
searchCmd,
|
|
918
|
+
inspectCmd,
|
|
919
|
+
serveCmd,
|
|
920
|
+
snapshotCmd,
|
|
921
|
+
compactCmd,
|
|
922
|
+
benchmarkCmd,
|
|
923
|
+
doctorCmd,
|
|
924
|
+
stabilityCmd,
|
|
925
|
+
pluginsCmd,
|
|
926
|
+
completionCmd,
|
|
927
|
+
versionCmd,
|
|
928
|
+
];
|
|
929
|
+
const ALL_COMMAND_NAMES = COMMANDS.map((c) => c.name);
|
|
930
|
+
export function findCommand(name) {
|
|
931
|
+
return COMMANDS.find((c) => c.name === name || c.aliases?.includes(name));
|
|
932
|
+
}
|
|
933
|
+
// ── tiny parser used inside commands ──────────────────────────────────
|
|
934
|
+
function parseFlags(argv, specs) {
|
|
935
|
+
return parseFlagsImpl(argv, specs);
|
|
936
|
+
}
|
|
937
|
+
export { parseFlags };
|
|
938
|
+
//# sourceMappingURL=commands.js.map
|