akm-cli 0.6.0 → 0.7.0-rc1
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 +66 -0
- package/dist/{cli.js → src/cli.js} +672 -29
- package/dist/{commands → src/commands}/config-cli.js +5 -4
- package/dist/src/commands/distill.js +283 -0
- package/dist/src/commands/events.js +108 -0
- package/dist/src/commands/history.js +120 -0
- package/dist/{commands → src/commands}/installed-stashes.js +28 -2
- package/dist/src/commands/proposal.js +119 -0
- package/dist/src/commands/propose.js +171 -0
- package/dist/src/commands/reflect.js +193 -0
- package/dist/{commands → src/commands}/registry-search.js +2 -1
- package/dist/{commands → src/commands}/remember.js +12 -0
- package/dist/{commands → src/commands}/search.js +74 -1
- package/dist/{commands → src/commands}/self-update.js +4 -3
- package/dist/{commands → src/commands}/show.js +67 -2
- package/dist/{core → src/core}/asset-ref.js +5 -5
- package/dist/{core → src/core}/asset-spec.js +12 -0
- package/dist/{core → src/core}/common.js +1 -1
- package/dist/{core → src/core}/config.js +175 -121
- package/dist/{core → src/core}/errors.js +4 -0
- package/dist/src/core/events.js +239 -0
- package/dist/src/core/lesson-lint.js +86 -0
- package/dist/src/core/proposals.js +406 -0
- package/dist/src/core/warn.js +72 -0
- package/dist/{core → src/core}/write-source.js +80 -5
- package/dist/{indexer → src/indexer}/db-search.js +119 -27
- package/dist/{indexer → src/indexer}/db.js +76 -23
- package/dist/{indexer → src/indexer}/file-context.js +0 -3
- package/dist/src/indexer/graph-boost.js +179 -0
- package/dist/src/indexer/graph-extraction.js +212 -0
- package/dist/{indexer → src/indexer}/indexer.js +73 -6
- package/dist/src/indexer/memory-inference.js +263 -0
- package/dist/{indexer → src/indexer}/metadata.js +114 -11
- package/dist/src/integrations/agent/config.js +292 -0
- package/dist/src/integrations/agent/detect.js +94 -0
- package/dist/src/integrations/agent/index.js +17 -0
- package/dist/src/integrations/agent/profiles.js +65 -0
- package/dist/src/integrations/agent/prompts.js +167 -0
- package/dist/src/integrations/agent/spawn.js +221 -0
- package/dist/{integrations → src/integrations}/lockfile.js +0 -26
- package/dist/{llm → src/llm}/client.js +33 -2
- package/dist/src/llm/feature-gate.js +108 -0
- package/dist/src/llm/graph-extract.js +107 -0
- package/dist/src/llm/index-passes.js +35 -0
- package/dist/src/llm/memory-infer.js +86 -0
- package/dist/{output → src/output}/renderers.js +60 -1
- package/dist/src/output/shapes.js +516 -0
- package/dist/{output → src/output}/text.js +447 -4
- package/dist/{registry → src/registry}/build-index.js +14 -4
- package/dist/{registry → src/registry}/factory.js +0 -8
- package/dist/{registry → src/registry}/providers/static-index.js +3 -2
- package/dist/{registry → src/registry}/resolve.js +68 -2
- package/dist/{setup → src/setup}/setup.js +43 -5
- package/dist/{sources → src/sources}/providers/git.js +7 -15
- package/dist/{wiki → src/wiki}/wiki.js +9 -11
- package/dist/tests/add-website-source.test.js +119 -0
- package/dist/tests/agent/agent-config-loader.test.js +70 -0
- package/dist/tests/agent/agent-config.test.js +221 -0
- package/dist/tests/agent/agent-detect.test.js +100 -0
- package/dist/tests/agent/agent-spawn.test.js +234 -0
- package/dist/tests/agent-output.test.js +186 -0
- package/dist/tests/architecture/agent-no-llm-sdk-guard.test.js +103 -0
- package/dist/tests/architecture/agent-spawn-seam.test.js +193 -0
- package/dist/tests/architecture/llm-stateless-seam.test.js +112 -0
- package/dist/tests/asset-ref.test.js +192 -0
- package/dist/tests/asset-registry.test.js +103 -0
- package/dist/tests/asset-spec.test.js +241 -0
- package/dist/tests/bench/attribution.test.js +995 -0
- package/dist/tests/bench/cleanup-sigint.test.js +83 -0
- package/dist/tests/bench/cleanup.js +203 -0
- package/dist/tests/bench/cleanup.test.js +166 -0
- package/dist/tests/bench/cli.js +683 -0
- package/dist/tests/bench/cli.test.js +177 -0
- package/dist/tests/bench/compare.test.js +556 -0
- package/dist/tests/bench/corpus.js +314 -0
- package/dist/tests/bench/corpus.test.js +258 -0
- package/dist/tests/bench/driver.js +346 -0
- package/dist/tests/bench/driver.test.js +443 -0
- package/dist/tests/bench/evolve-metrics.js +179 -0
- package/dist/tests/bench/evolve-metrics.test.js +187 -0
- package/dist/tests/bench/evolve.js +580 -0
- package/dist/tests/bench/evolve.test.js +616 -0
- package/dist/tests/bench/failure-modes.test.js +300 -0
- package/dist/tests/bench/feedback-integrity.test.js +456 -0
- package/dist/tests/bench/leakage.test.js +125 -0
- package/dist/tests/bench/learning-curve.test.js +133 -0
- package/dist/tests/bench/metrics.js +2319 -0
- package/dist/tests/bench/metrics.test.js +1144 -0
- package/dist/tests/bench/no-os-tmpdir-invariant.test.js +43 -0
- package/dist/tests/bench/report.js +1821 -0
- package/dist/tests/bench/report.test.js +989 -0
- package/dist/tests/bench/runner.js +536 -0
- package/dist/tests/bench/runner.test.js +958 -0
- package/dist/tests/bench/search-bridge.test.js +331 -0
- package/dist/tests/bench/tmp.js +41 -0
- package/dist/tests/bench/trajectory.js +116 -0
- package/dist/tests/bench/trajectory.test.js +127 -0
- package/dist/tests/bench/verifier.js +109 -0
- package/dist/tests/bench/verifier.test.js +118 -0
- package/dist/tests/bench/workflow-evaluator.js +557 -0
- package/dist/tests/bench/workflow-evaluator.test.js +421 -0
- package/dist/tests/bench/workflow-spec.js +358 -0
- package/dist/tests/bench/workflow-spec.test.js +363 -0
- package/dist/tests/bench/workflow-trace.js +438 -0
- package/dist/tests/bench/workflow-trace.test.js +254 -0
- package/dist/tests/benchmark-search-quality.js +536 -0
- package/dist/tests/benchmark-suite.js +1441 -0
- package/dist/tests/capture-cli.test.js +112 -0
- package/dist/tests/cli-errors.test.js +203 -0
- package/dist/tests/commands/events.test.js +370 -0
- package/dist/tests/commands/history.test.js +223 -0
- package/dist/tests/commands/import.test.js +103 -0
- package/dist/tests/commands/proposal-cli.test.js +209 -0
- package/dist/tests/commands/reflect-propose-cli.test.js +333 -0
- package/dist/tests/commands/remember.test.js +97 -0
- package/dist/tests/commands/scope-flags.test.js +300 -0
- package/dist/tests/commands/search.test.js +537 -0
- package/dist/tests/commands/show-indexer-parity.test.js +117 -0
- package/dist/tests/commands/show.test.js +294 -0
- package/dist/tests/common.test.js +266 -0
- package/dist/tests/completions.test.js +142 -0
- package/dist/tests/config-cli.test.js +193 -0
- package/dist/tests/config-llm-features.test.js +139 -0
- package/dist/tests/config.test.js +544 -0
- package/dist/tests/contracts/migration-baseline.test.js +43 -0
- package/dist/tests/contracts/reflect-propose-envelope.test.js +139 -0
- package/dist/tests/contracts/spec-helpers.js +46 -0
- package/dist/tests/contracts/v1-spec-section-11-proposal-queue.test.js +228 -0
- package/dist/tests/contracts/v1-spec-section-12-agent-config.test.js +56 -0
- package/dist/tests/contracts/v1-spec-section-13-lesson-type.test.js +34 -0
- package/dist/tests/contracts/v1-spec-section-14-llm-features.test.js +94 -0
- package/dist/tests/contracts/v1-spec-section-4-1-asset-types.test.js +39 -0
- package/dist/tests/contracts/v1-spec-section-4-2-quality-rules.test.js +44 -0
- package/dist/tests/contracts/v1-spec-section-5-configuration.test.js +47 -0
- package/dist/tests/contracts/v1-spec-section-6-orchestration.test.js +40 -0
- package/dist/tests/contracts/v1-spec-section-7-module-layout.test.js +58 -0
- package/dist/tests/contracts/v1-spec-section-8-extension-points.test.js +34 -0
- package/dist/tests/contracts/v1-spec-section-9-4-cli-surface.test.js +75 -0
- package/dist/tests/contracts/v1-spec-section-9-7-llm-agent-boundary.test.js +36 -0
- package/dist/tests/core/write-source.test.js +366 -0
- package/dist/tests/curate-command.test.js +87 -0
- package/dist/tests/db-scoring.test.js +201 -0
- package/dist/tests/db.test.js +654 -0
- package/dist/tests/distill-cli-flag.test.js +208 -0
- package/dist/tests/distill.test.js +515 -0
- package/dist/tests/docker-install.test.js +120 -0
- package/dist/tests/e2e.test.js +1398 -0
- package/dist/tests/embedder.test.js +340 -0
- package/dist/tests/embedding-model-config.test.js +379 -0
- package/dist/tests/feedback-command.test.js +172 -0
- package/dist/tests/file-context.test.js +552 -0
- package/dist/tests/fixtures/scripts/git/summarize-diff.js +9 -0
- package/dist/tests/fixtures/scripts/lint/eslint-check.js +7 -0
- package/dist/tests/fixtures/stashes/load.js +166 -0
- package/dist/tests/fixtures/stashes/load.test.js +88 -0
- package/dist/tests/fixtures/stashes/ranking-baseline/scripts/mem0-search.js +12 -0
- package/dist/tests/frontmatter.test.js +190 -0
- package/dist/tests/fts-field-weighting.test.js +254 -0
- package/dist/tests/fuzzy-search.test.js +230 -0
- package/dist/tests/git-provider-clone.test.js +45 -0
- package/dist/tests/github.test.js +161 -0
- package/dist/tests/graph-boost-ranking.test.js +305 -0
- package/dist/tests/graph-extraction.test.js +282 -0
- package/dist/tests/helpers/usage-events.js +8 -0
- package/dist/tests/index-pass-llm.test.js +161 -0
- package/dist/tests/indexer.test.js +559 -0
- package/dist/tests/info-command.test.js +166 -0
- package/dist/tests/init.test.js +69 -0
- package/dist/tests/install-script.test.js +246 -0
- package/dist/tests/integration/agent-real-profile.test.js +94 -0
- package/dist/tests/issue-36-repro.test.js +304 -0
- package/dist/tests/issues-191-194.test.js +160 -0
- package/dist/tests/lesson-lint.test.js +111 -0
- package/dist/tests/llm-client.test.js +115 -0
- package/dist/tests/llm-feature-gate.test.js +151 -0
- package/dist/tests/llm.test.js +139 -0
- package/dist/tests/lockfile.test.js +216 -0
- package/dist/tests/manifest.test.js +205 -0
- package/dist/tests/markdown.test.js +126 -0
- package/dist/tests/matchers-unit.test.js +189 -0
- package/dist/tests/memory-inference.test.js +299 -0
- package/dist/tests/merge-scoring.test.js +136 -0
- package/dist/tests/metadata.test.js +313 -0
- package/dist/tests/migration-help.test.js +89 -0
- package/dist/tests/origin-resolve.test.js +124 -0
- package/dist/tests/output-baseline.test.js +217 -0
- package/dist/tests/output-shapes-unit.test.js +476 -0
- package/dist/tests/parallel-search.test.js +272 -0
- package/dist/tests/parameter-metadata.test.js +365 -0
- package/dist/tests/paths.test.js +177 -0
- package/dist/tests/progressive-disclosure.test.js +280 -0
- package/dist/tests/proposals.test.js +279 -0
- package/dist/tests/proposed-quality.test.js +271 -0
- package/dist/tests/provider-registry.test.js +32 -0
- package/dist/tests/ranking-regression.test.js +548 -0
- package/dist/tests/reflect-propose.test.js +455 -0
- package/dist/tests/registry-build-index.test.js +378 -0
- package/dist/tests/registry-cli.test.js +290 -0
- package/dist/tests/registry-index-v2.test.js +430 -0
- package/dist/tests/registry-install.test.js +728 -0
- package/dist/tests/registry-providers/parity.test.js +189 -0
- package/dist/tests/registry-providers/skills-sh.test.js +309 -0
- package/dist/tests/registry-providers/static-index.test.js +204 -0
- package/dist/tests/registry-resolve.test.js +126 -0
- package/dist/tests/registry-search.test.js +723 -0
- package/dist/tests/remember-frontmatter.test.js +380 -0
- package/dist/tests/remember-unit.test.js +123 -0
- package/dist/tests/ripgrep-install.test.js +251 -0
- package/dist/tests/ripgrep-resolve.test.js +108 -0
- package/dist/tests/ripgrep.test.js +163 -0
- package/dist/tests/save-command.test.js +94 -0
- package/dist/tests/save-trust-qa-fixes.test.js +270 -0
- package/dist/tests/scoring-pipeline.test.js +648 -0
- package/dist/tests/search-include-proposed-cli.test.js +118 -0
- package/dist/tests/self-update.test.js +442 -0
- package/dist/tests/semantic-search-e2e.test.js +512 -0
- package/dist/tests/semantic-status.test.js +471 -0
- package/dist/tests/setup-run.integration.js +877 -0
- package/dist/tests/setup-wizard.test.js +198 -0
- package/dist/tests/setup.test.js +131 -0
- package/dist/tests/source-add.test.js +11 -0
- package/dist/tests/source-clone.test.js +254 -0
- package/dist/tests/source-manage.test.js +366 -0
- package/dist/tests/source-providers/filesystem.test.js +82 -0
- package/dist/tests/source-providers/git.test.js +252 -0
- package/dist/tests/source-providers/website.test.js +128 -0
- package/dist/tests/source-qa-fixes.test.js +268 -0
- package/dist/tests/source-registry.test.js +350 -0
- package/dist/tests/source-resolve.test.js +100 -0
- package/dist/tests/source-source.test.js +221 -0
- package/dist/tests/source.test.js +533 -0
- package/dist/tests/tar-utils-scan.test.js +73 -0
- package/dist/tests/toggle-components.test.js +73 -0
- package/dist/tests/usage-telemetry.test.js +265 -0
- package/dist/tests/utility-scoring.test.js +558 -0
- package/dist/tests/vault-load-error.test.js +78 -0
- package/dist/tests/vault-qa-fixes.test.js +194 -0
- package/dist/tests/vault.test.js +429 -0
- package/dist/tests/vector-search.test.js +608 -0
- package/dist/tests/walker.test.js +252 -0
- package/dist/tests/wave2-cluster-bc.test.js +228 -0
- package/dist/tests/wave2-cluster-d.test.js +180 -0
- package/dist/tests/wave2-cluster-e.test.js +179 -0
- package/dist/tests/wiki-qa-fixes.test.js +270 -0
- package/dist/tests/wiki.test.js +529 -0
- package/dist/tests/workflow-cli.test.js +271 -0
- package/dist/tests/workflow-markdown.test.js +171 -0
- package/dist/tests/workflow-path-escape.test.js +132 -0
- package/dist/tests/workflow-qa-fixes.test.js +377 -0
- package/dist/tests/workflows/indexer-rejection.test.js +213 -0
- package/docs/README.md +8 -0
- package/docs/migration/release-notes/0.7.0.md +244 -0
- package/package.json +2 -2
- package/dist/core/warn.js +0 -27
- package/dist/output/shapes.js +0 -212
- /package/dist/{commands → src/commands}/completions.js +0 -0
- /package/dist/{commands → src/commands}/curate.js +0 -0
- /package/dist/{commands → src/commands}/info.js +0 -0
- /package/dist/{commands → src/commands}/init.js +0 -0
- /package/dist/{commands → src/commands}/install-audit.js +0 -0
- /package/dist/{commands → src/commands}/migration-help.js +0 -0
- /package/dist/{commands → src/commands}/source-add.js +0 -0
- /package/dist/{commands → src/commands}/source-clone.js +0 -0
- /package/dist/{commands → src/commands}/source-manage.js +0 -0
- /package/dist/{commands → src/commands}/vault.js +0 -0
- /package/dist/{core → src/core}/asset-registry.js +0 -0
- /package/dist/{core → src/core}/frontmatter.js +0 -0
- /package/dist/{core → src/core}/markdown.js +0 -0
- /package/dist/{core → src/core}/paths.js +0 -0
- /package/dist/{indexer → src/indexer}/manifest.js +0 -0
- /package/dist/{indexer → src/indexer}/matchers.js +0 -0
- /package/dist/{indexer → src/indexer}/search-fields.js +0 -0
- /package/dist/{indexer → src/indexer}/search-source.js +0 -0
- /package/dist/{indexer → src/indexer}/semantic-status.js +0 -0
- /package/dist/{indexer → src/indexer}/usage-events.js +0 -0
- /package/dist/{indexer → src/indexer}/walker.js +0 -0
- /package/dist/{integrations → src/integrations}/github.js +0 -0
- /package/dist/{llm → src/llm}/embedder.js +0 -0
- /package/dist/{llm → src/llm}/embedders/cache.js +0 -0
- /package/dist/{llm → src/llm}/embedders/local.js +0 -0
- /package/dist/{llm → src/llm}/embedders/remote.js +0 -0
- /package/dist/{llm → src/llm}/embedders/types.js +0 -0
- /package/dist/{llm → src/llm}/metadata-enhance.js +0 -0
- /package/dist/{output → src/output}/cli-hints.js +0 -0
- /package/dist/{output → src/output}/context.js +0 -0
- /package/dist/{registry → src/registry}/create-provider-registry.js +0 -0
- /package/dist/{registry → src/registry}/origin-resolve.js +0 -0
- /package/dist/{registry → src/registry}/providers/index.js +0 -0
- /package/dist/{registry → src/registry}/providers/skills-sh.js +0 -0
- /package/dist/{registry → src/registry}/providers/types.js +0 -0
- /package/dist/{registry → src/registry}/types.js +0 -0
- /package/dist/{setup → src/setup}/detect.js +0 -0
- /package/dist/{setup → src/setup}/ripgrep-install.js +0 -0
- /package/dist/{setup → src/setup}/ripgrep-resolve.js +0 -0
- /package/dist/{setup → src/setup}/steps.js +0 -0
- /package/dist/{sources → src/sources}/include.js +0 -0
- /package/dist/{sources → src/sources}/provider-factory.js +0 -0
- /package/dist/{sources → src/sources}/provider.js +0 -0
- /package/dist/{sources → src/sources}/providers/filesystem.js +0 -0
- /package/dist/{sources → src/sources}/providers/index.js +0 -0
- /package/dist/{sources → src/sources}/providers/install-types.js +0 -0
- /package/dist/{sources → src/sources}/providers/npm.js +0 -0
- /package/dist/{sources → src/sources}/providers/provider-utils.js +0 -0
- /package/dist/{sources → src/sources}/providers/sync-from-ref.js +0 -0
- /package/dist/{sources → src/sources}/providers/tar-utils.js +0 -0
- /package/dist/{sources → src/sources}/providers/website.js +0 -0
- /package/dist/{sources → src/sources}/resolve.js +0 -0
- /package/dist/{sources → src/sources}/types.js +0 -0
- /package/dist/{templates → src/templates}/wiki-templates.js +0 -0
- /package/dist/{version.js → src/version.js} +0 -0
- /package/dist/{workflows → src/workflows}/authoring.js +0 -0
- /package/dist/{workflows → src/workflows}/cli.js +0 -0
- /package/dist/{workflows → src/workflows}/db.js +0 -0
- /package/dist/{workflows → src/workflows}/document-cache.js +0 -0
- /package/dist/{workflows → src/workflows}/parser.js +0 -0
- /package/dist/{workflows → src/workflows}/renderer.js +0 -0
- /package/dist/{workflows → src/workflows}/runs.js +0 -0
- /package/dist/{workflows → src/workflows}/schema.js +0 -0
- /package/dist/{workflows → src/workflows}/validator.js +0 -0
|
@@ -182,10 +182,446 @@ export function formatPlain(command, result, detail) {
|
|
|
182
182
|
const over = r.overwritten ? " (overwritten)" : "";
|
|
183
183
|
return `Cloned${remote} → ${dst}${over}`;
|
|
184
184
|
}
|
|
185
|
+
// Output shape registration for `akm history` — paired with the shape function in shapes.ts.
|
|
186
|
+
case "history": {
|
|
187
|
+
return formatHistoryPlain(r);
|
|
188
|
+
}
|
|
189
|
+
// Output shape registration for `akm events list` / `akm events tail`
|
|
190
|
+
// (#204). Both share a renderer; `events-tail` is also called per-event
|
|
191
|
+
// by the streaming code path via `formatEventLine`.
|
|
192
|
+
case "events-list":
|
|
193
|
+
case "events-tail": {
|
|
194
|
+
return formatEventsPlain(r);
|
|
195
|
+
}
|
|
196
|
+
// Output shape registration for `akm proposal *` (#225).
|
|
197
|
+
case "proposal-list": {
|
|
198
|
+
return formatProposalListPlain(r);
|
|
199
|
+
}
|
|
200
|
+
case "proposal-show": {
|
|
201
|
+
return formatProposalShowPlain(r);
|
|
202
|
+
}
|
|
203
|
+
case "proposal-accept": {
|
|
204
|
+
return formatProposalAcceptPlain(r);
|
|
205
|
+
}
|
|
206
|
+
case "proposal-reject": {
|
|
207
|
+
return formatProposalRejectPlain(r);
|
|
208
|
+
}
|
|
209
|
+
case "proposal-diff": {
|
|
210
|
+
return formatProposalDiffPlain(r);
|
|
211
|
+
}
|
|
212
|
+
// Output shape registration for `akm reflect` / `akm propose` (#226).
|
|
213
|
+
case "reflect":
|
|
214
|
+
case "propose": {
|
|
215
|
+
return formatProposalProducerPlain(command, r);
|
|
216
|
+
}
|
|
217
|
+
// Output shape registration for `akm distill <ref>` (#228). Three branches
|
|
218
|
+
// mirror the three terminal `outcome` values.
|
|
219
|
+
case "distill": {
|
|
220
|
+
return formatDistillPlain(r);
|
|
221
|
+
}
|
|
222
|
+
case "info":
|
|
223
|
+
return formatInfoPlain(r);
|
|
224
|
+
case "config":
|
|
225
|
+
return formatConfigPlain(r);
|
|
226
|
+
case "feedback":
|
|
227
|
+
return formatFeedbackPlain(r);
|
|
228
|
+
case "remember":
|
|
229
|
+
return formatRememberPlain(r);
|
|
230
|
+
case "import":
|
|
231
|
+
return formatImportPlain(r);
|
|
232
|
+
case "save":
|
|
233
|
+
return formatSavePlain(r);
|
|
234
|
+
case "enable":
|
|
235
|
+
case "disable":
|
|
236
|
+
return formatToggleComponentPlain(command, r);
|
|
237
|
+
case "registry-list":
|
|
238
|
+
return formatRegistryListPlain(r);
|
|
239
|
+
case "registry-add":
|
|
240
|
+
return formatRegistryAddPlain(r);
|
|
241
|
+
case "registry-remove":
|
|
242
|
+
return formatRegistryRemovePlain(r);
|
|
243
|
+
case "registry-search":
|
|
244
|
+
return formatRegistrySearchPlain(r, detail);
|
|
245
|
+
case "registry-build-index":
|
|
246
|
+
return formatRegistryBuildIndexPlain(r);
|
|
247
|
+
case "vault-list":
|
|
248
|
+
return formatVaultListPlain(r);
|
|
249
|
+
case "vault-create":
|
|
250
|
+
return `Created vault ${String(r.ref ?? "?")} at ${String(r.path ?? "?")}`;
|
|
251
|
+
case "vault-set":
|
|
252
|
+
return `Set ${String(r.key ?? "?")} in ${String(r.ref ?? "?")} (value not displayed)`;
|
|
253
|
+
case "vault-unset": {
|
|
254
|
+
const removed = r.removed === true;
|
|
255
|
+
const head = removed
|
|
256
|
+
? `Removed ${String(r.key ?? "?")} from ${String(r.ref ?? "?")}`
|
|
257
|
+
: `Key ${String(r.key ?? "?")} was not present in ${String(r.ref ?? "?")}`;
|
|
258
|
+
return head;
|
|
259
|
+
}
|
|
260
|
+
case "wiki-register":
|
|
261
|
+
return formatWikiRegisterPlain(r);
|
|
262
|
+
case "workflow-resume":
|
|
263
|
+
return formatWorkflowStatusPlain(r) ?? `Resumed workflow run ${String(r.id ?? r.runId ?? "?")}`;
|
|
264
|
+
case "workflow-validate":
|
|
265
|
+
return formatWorkflowValidatePlain(r);
|
|
185
266
|
default:
|
|
186
267
|
return null; // fall through to YAML
|
|
187
268
|
}
|
|
188
269
|
}
|
|
270
|
+
export function formatInfoPlain(r) {
|
|
271
|
+
const lines = [];
|
|
272
|
+
if (r.version)
|
|
273
|
+
lines.push(`version: ${String(r.version)}`);
|
|
274
|
+
if (r.stashDir)
|
|
275
|
+
lines.push(`stashDir: ${String(r.stashDir)}`);
|
|
276
|
+
if (r.configPath)
|
|
277
|
+
lines.push(`configPath: ${String(r.configPath)}`);
|
|
278
|
+
if (r.cacheDir)
|
|
279
|
+
lines.push(`cacheDir: ${String(r.cacheDir)}`);
|
|
280
|
+
if (r.dbPath)
|
|
281
|
+
lines.push(`dbPath: ${String(r.dbPath)}`);
|
|
282
|
+
const capabilities = r.capabilities;
|
|
283
|
+
if (capabilities) {
|
|
284
|
+
lines.push("capabilities:");
|
|
285
|
+
for (const [k, v] of Object.entries(capabilities)) {
|
|
286
|
+
lines.push(` ${k}: ${typeof v === "object" ? JSON.stringify(v) : String(v)}`);
|
|
287
|
+
}
|
|
288
|
+
}
|
|
289
|
+
const indexStats = r.index;
|
|
290
|
+
if (indexStats) {
|
|
291
|
+
lines.push("index:");
|
|
292
|
+
for (const [k, v] of Object.entries(indexStats)) {
|
|
293
|
+
lines.push(` ${k}: ${typeof v === "object" ? JSON.stringify(v) : String(v)}`);
|
|
294
|
+
}
|
|
295
|
+
}
|
|
296
|
+
if (lines.length === 0)
|
|
297
|
+
return JSON.stringify(r, null, 2);
|
|
298
|
+
return lines.join("\n");
|
|
299
|
+
}
|
|
300
|
+
export function formatConfigPlain(r) {
|
|
301
|
+
// Recursive flattener: prints `key=value` lines, and nested objects as
|
|
302
|
+
// `parent.child=value`. Arrays render as JSON for compactness.
|
|
303
|
+
const lines = [];
|
|
304
|
+
const walk = (obj, prefix) => {
|
|
305
|
+
for (const [k, v] of Object.entries(obj)) {
|
|
306
|
+
const path = prefix ? `${prefix}.${k}` : k;
|
|
307
|
+
if (v === null || v === undefined) {
|
|
308
|
+
lines.push(`${path}=`);
|
|
309
|
+
}
|
|
310
|
+
else if (Array.isArray(v)) {
|
|
311
|
+
lines.push(`${path}=${JSON.stringify(v)}`);
|
|
312
|
+
}
|
|
313
|
+
else if (typeof v === "object") {
|
|
314
|
+
walk(v, path);
|
|
315
|
+
}
|
|
316
|
+
else {
|
|
317
|
+
lines.push(`${path}=${String(v)}`);
|
|
318
|
+
}
|
|
319
|
+
}
|
|
320
|
+
};
|
|
321
|
+
walk(r, "");
|
|
322
|
+
if (lines.length === 0)
|
|
323
|
+
return "(empty config)";
|
|
324
|
+
return lines.join("\n");
|
|
325
|
+
}
|
|
326
|
+
export function formatFeedbackPlain(r) {
|
|
327
|
+
const ref = String(r.ref ?? "?");
|
|
328
|
+
const signal = String(r.signal ?? "?");
|
|
329
|
+
const note = typeof r.note === "string" && r.note ? ` — ${r.note}` : "";
|
|
330
|
+
return `Recorded ${signal} feedback for ${ref}${note}`;
|
|
331
|
+
}
|
|
332
|
+
export function formatRememberPlain(r) {
|
|
333
|
+
const ref = String(r.ref ?? "?");
|
|
334
|
+
const pathValue = String(r.path ?? "?");
|
|
335
|
+
return `Saved ${ref} at ${pathValue}`;
|
|
336
|
+
}
|
|
337
|
+
export function formatImportPlain(r) {
|
|
338
|
+
const ref = String(r.ref ?? "?");
|
|
339
|
+
const source = String(r.source ?? "?");
|
|
340
|
+
const pathValue = String(r.path ?? "?");
|
|
341
|
+
return `Imported ${source} → ${ref} at ${pathValue}`;
|
|
342
|
+
}
|
|
343
|
+
export function formatSavePlain(r) {
|
|
344
|
+
if (r.ok === false) {
|
|
345
|
+
const reason = typeof r.reason === "string" ? r.reason : "unknown";
|
|
346
|
+
return `save: failed (${reason})`;
|
|
347
|
+
}
|
|
348
|
+
const name = typeof r.name === "string" ? r.name : "primary stash";
|
|
349
|
+
const committed = r.committed === true;
|
|
350
|
+
const pushed = r.pushed === true;
|
|
351
|
+
const parts = [`save: ${name}`];
|
|
352
|
+
parts.push(committed ? "committed" : "no changes");
|
|
353
|
+
if (pushed)
|
|
354
|
+
parts.push("pushed");
|
|
355
|
+
return parts.join(" — ");
|
|
356
|
+
}
|
|
357
|
+
export function formatToggleComponentPlain(command, r) {
|
|
358
|
+
const verb = command === "enable" ? "Enabled" : "Disabled";
|
|
359
|
+
const component = String(r.component ?? "?");
|
|
360
|
+
const changed = r.changed === true;
|
|
361
|
+
return changed ? `${verb} ${component}` : `${component} was already ${command}d`;
|
|
362
|
+
}
|
|
363
|
+
export function formatRegistryListPlain(r) {
|
|
364
|
+
const registries = Array.isArray(r.registries) ? r.registries : [];
|
|
365
|
+
if (registries.length === 0) {
|
|
366
|
+
return "No registries configured. Add one with `akm registry add <url>`.";
|
|
367
|
+
}
|
|
368
|
+
const lines = [];
|
|
369
|
+
for (const reg of registries) {
|
|
370
|
+
const url = String(reg.url ?? "?");
|
|
371
|
+
const name = typeof reg.name === "string" ? reg.name : "";
|
|
372
|
+
const provider = typeof reg.provider === "string" ? ` (${reg.provider})` : "";
|
|
373
|
+
const enabled = reg.enabled === false ? " [disabled]" : "";
|
|
374
|
+
const head = name ? `${name}: ${url}` : url;
|
|
375
|
+
lines.push(`${head}${provider}${enabled}`);
|
|
376
|
+
}
|
|
377
|
+
return lines.join("\n");
|
|
378
|
+
}
|
|
379
|
+
export function formatRegistryAddPlain(r) {
|
|
380
|
+
if (r.added === false) {
|
|
381
|
+
return typeof r.message === "string" ? r.message : "Registry already configured.";
|
|
382
|
+
}
|
|
383
|
+
const registries = Array.isArray(r.registries) ? r.registries.length : 0;
|
|
384
|
+
return `Registry added (${registries} total).`;
|
|
385
|
+
}
|
|
386
|
+
export function formatRegistryRemovePlain(r) {
|
|
387
|
+
if (r.removed === false) {
|
|
388
|
+
return typeof r.message === "string" ? r.message : "No matching registry found.";
|
|
389
|
+
}
|
|
390
|
+
const entry = r.entry;
|
|
391
|
+
const url = entry ? String(entry.url ?? entry.name ?? "?") : "?";
|
|
392
|
+
return `Removed registry ${url}`;
|
|
393
|
+
}
|
|
394
|
+
export function formatRegistrySearchPlain(r, detail) {
|
|
395
|
+
// Reuse the same renderer as `search` — both share `hits` / `registryHits`.
|
|
396
|
+
return formatSearchPlain(r, detail);
|
|
397
|
+
}
|
|
398
|
+
export function formatRegistryBuildIndexPlain(r) {
|
|
399
|
+
const outPath = String(r.outPath ?? "?");
|
|
400
|
+
const total = typeof r.totalKits === "number" ? r.totalKits : 0;
|
|
401
|
+
const version = typeof r.version === "number" ? `v${r.version}` : "";
|
|
402
|
+
return `Wrote registry index ${version} (${total} kits) → ${outPath}`.replace(/\s+/g, " ").trim();
|
|
403
|
+
}
|
|
404
|
+
export function formatVaultListPlain(r) {
|
|
405
|
+
// Single-vault listing: { ref, path, entries: [{ key, comment? }, ...] }
|
|
406
|
+
if (typeof r.ref === "string" && Array.isArray(r.entries)) {
|
|
407
|
+
const ref = r.ref;
|
|
408
|
+
const entries = r.entries;
|
|
409
|
+
if (entries.length === 0) {
|
|
410
|
+
return `No keys in ${ref}. Set one with \`akm vault set ${ref} KEY=VALUE\`.`;
|
|
411
|
+
}
|
|
412
|
+
const lines = [ref];
|
|
413
|
+
for (const e of entries) {
|
|
414
|
+
const key = String(e.key ?? "?");
|
|
415
|
+
const comment = typeof e.comment === "string" && e.comment ? ` # ${e.comment}` : "";
|
|
416
|
+
lines.push(` ${key}${comment}`);
|
|
417
|
+
}
|
|
418
|
+
return lines.join("\n");
|
|
419
|
+
}
|
|
420
|
+
// Multi-vault listing: { vaults: [{ ref, path, keyCount }, ...] }
|
|
421
|
+
const vaults = Array.isArray(r.vaults) ? r.vaults : [];
|
|
422
|
+
if (vaults.length === 0) {
|
|
423
|
+
return "No vaults. Create one with `akm vault create <name>` then `akm vault set vault:<name> KEY=VALUE`.";
|
|
424
|
+
}
|
|
425
|
+
const lines = [];
|
|
426
|
+
for (const v of vaults) {
|
|
427
|
+
const ref = String(v.ref ?? "?");
|
|
428
|
+
const keyCount = typeof v.keyCount === "number" ? v.keyCount : 0;
|
|
429
|
+
lines.push(`${ref}\t${keyCount} key(s)`);
|
|
430
|
+
}
|
|
431
|
+
return lines.join("\n");
|
|
432
|
+
}
|
|
433
|
+
export function formatWikiRegisterPlain(r) {
|
|
434
|
+
const name = String(r.name ?? r.wiki ?? "?");
|
|
435
|
+
const ref = String(r.ref ?? r.path ?? r.url ?? "?");
|
|
436
|
+
return `Registered wiki ${name} → ${ref}`;
|
|
437
|
+
}
|
|
438
|
+
export function formatWorkflowValidatePlain(r) {
|
|
439
|
+
const ok = r.ok !== false;
|
|
440
|
+
const pathValue = String(r.path ?? "?");
|
|
441
|
+
if (!ok)
|
|
442
|
+
return `workflow validate: failed (${pathValue})`;
|
|
443
|
+
const title = typeof r.title === "string" ? r.title : "";
|
|
444
|
+
const stepCount = typeof r.stepCount === "number" ? r.stepCount : 0;
|
|
445
|
+
return `workflow validate: ok — ${title || pathValue} (${stepCount} step(s))`;
|
|
446
|
+
}
|
|
447
|
+
export function formatProposalProducerPlain(command, r) {
|
|
448
|
+
if (r.ok === false) {
|
|
449
|
+
const reason = String(r.reason ?? "unknown");
|
|
450
|
+
const error = typeof r.error === "string" ? r.error : "";
|
|
451
|
+
const lines = [`${command}: failed (${reason})`];
|
|
452
|
+
if (error)
|
|
453
|
+
lines.push(` error: ${error}`);
|
|
454
|
+
if (r.ref)
|
|
455
|
+
lines.push(` ref: ${String(r.ref)}`);
|
|
456
|
+
if (r.exitCode !== undefined && r.exitCode !== null) {
|
|
457
|
+
lines.push(` exitCode: ${String(r.exitCode)}`);
|
|
458
|
+
}
|
|
459
|
+
return lines.join("\n");
|
|
460
|
+
}
|
|
461
|
+
const proposal = r.proposal ?? {};
|
|
462
|
+
const id = String(proposal.id ?? "?");
|
|
463
|
+
const ref = String(r.ref ?? proposal.ref ?? "?");
|
|
464
|
+
const status = String(proposal.status ?? "pending");
|
|
465
|
+
return `${command}: queued proposal ${id} (${ref}) [${status}]`;
|
|
466
|
+
}
|
|
467
|
+
export function formatProposalListPlain(r) {
|
|
468
|
+
const proposals = Array.isArray(r.proposals) ? r.proposals : [];
|
|
469
|
+
const total = typeof r.totalCount === "number" ? r.totalCount : proposals.length;
|
|
470
|
+
if (proposals.length === 0) {
|
|
471
|
+
return `${total} proposal(s).\nNo proposals.\nGenerate one with \`akm reflect <ref>\`, \`akm propose <type> <name> --task ...\`, or \`akm distill <ref>\`.`;
|
|
472
|
+
}
|
|
473
|
+
const lines = [`${total} proposal(s)`, ""];
|
|
474
|
+
for (const p of proposals) {
|
|
475
|
+
const id = String(p.id ?? "?");
|
|
476
|
+
const ref = String(p.ref ?? "?");
|
|
477
|
+
const status = String(p.status ?? "?");
|
|
478
|
+
const source = String(p.source ?? "?");
|
|
479
|
+
const created = String(p.createdAt ?? "?");
|
|
480
|
+
lines.push(`${id} [${status}] ${ref} source=${source} ${created}`);
|
|
481
|
+
}
|
|
482
|
+
return lines.join("\n").trimEnd();
|
|
483
|
+
}
|
|
484
|
+
export function formatProposalShowPlain(r) {
|
|
485
|
+
const p = r.proposal ?? {};
|
|
486
|
+
const lines = [];
|
|
487
|
+
lines.push(`# proposal ${String(p.id ?? "?")}`);
|
|
488
|
+
lines.push(`ref: ${String(p.ref ?? "?")}`);
|
|
489
|
+
lines.push(`status: ${String(p.status ?? "?")}`);
|
|
490
|
+
lines.push(`source: ${String(p.source ?? "?")}`);
|
|
491
|
+
if (p.sourceRun)
|
|
492
|
+
lines.push(`sourceRun: ${String(p.sourceRun)}`);
|
|
493
|
+
if (p.createdAt)
|
|
494
|
+
lines.push(`createdAt: ${String(p.createdAt)}`);
|
|
495
|
+
if (p.updatedAt)
|
|
496
|
+
lines.push(`updatedAt: ${String(p.updatedAt)}`);
|
|
497
|
+
const review = p.review;
|
|
498
|
+
if (review) {
|
|
499
|
+
lines.push(`review.outcome: ${String(review.outcome ?? "?")}`);
|
|
500
|
+
if (review.reason)
|
|
501
|
+
lines.push(`review.reason: ${String(review.reason)}`);
|
|
502
|
+
if (review.decidedAt)
|
|
503
|
+
lines.push(`review.decidedAt: ${String(review.decidedAt)}`);
|
|
504
|
+
}
|
|
505
|
+
const validation = r.validation;
|
|
506
|
+
if (validation) {
|
|
507
|
+
const ok = validation.ok === true;
|
|
508
|
+
const findings = Array.isArray(validation.findings) ? validation.findings : [];
|
|
509
|
+
lines.push("");
|
|
510
|
+
lines.push(`validation: ${ok ? "ok" : `${findings.length} finding(s)`}`);
|
|
511
|
+
for (const f of findings) {
|
|
512
|
+
lines.push(` - [${String(f.kind)}] ${String(f.message)}`);
|
|
513
|
+
}
|
|
514
|
+
}
|
|
515
|
+
const payload = p.payload;
|
|
516
|
+
if (payload && typeof payload.content === "string") {
|
|
517
|
+
lines.push("");
|
|
518
|
+
lines.push("payload:");
|
|
519
|
+
lines.push(payload.content);
|
|
520
|
+
}
|
|
521
|
+
return lines.join("\n").trimEnd();
|
|
522
|
+
}
|
|
523
|
+
export function formatProposalAcceptPlain(r) {
|
|
524
|
+
return `Accepted proposal ${String(r.id ?? "?")} → ${String(r.ref ?? "?")} at ${String(r.assetPath ?? "?")}`;
|
|
525
|
+
}
|
|
526
|
+
export function formatProposalRejectPlain(r) {
|
|
527
|
+
const reason = r.reason ? ` (${String(r.reason)})` : "";
|
|
528
|
+
return `Rejected proposal ${String(r.id ?? "?")} (${String(r.ref ?? "?")})${reason}`;
|
|
529
|
+
}
|
|
530
|
+
export function formatDistillPlain(r) {
|
|
531
|
+
const outcome = String(r.outcome ?? "unknown");
|
|
532
|
+
const inputRef = String(r.inputRef ?? "?");
|
|
533
|
+
const lessonRef = String(r.lessonRef ?? "?");
|
|
534
|
+
if (outcome === "queued") {
|
|
535
|
+
const id = String(r.proposalId ?? "?");
|
|
536
|
+
return `Distilled ${inputRef} → proposal ${id} (${lessonRef}). Run \`akm proposal show ${id}\` to review.`;
|
|
537
|
+
}
|
|
538
|
+
if (outcome === "validation_failed") {
|
|
539
|
+
const findings = Array.isArray(r.findings) ? r.findings : [];
|
|
540
|
+
const lines = [`Distillation produced an invalid lesson for ${inputRef}; no proposal queued.`];
|
|
541
|
+
for (const f of findings) {
|
|
542
|
+
lines.push(` - ${String(f.message ?? f.kind ?? "validation finding")}`);
|
|
543
|
+
}
|
|
544
|
+
return lines.join("\n");
|
|
545
|
+
}
|
|
546
|
+
// skipped
|
|
547
|
+
const message = typeof r.message === "string" ? r.message : "feature disabled or LLM unavailable";
|
|
548
|
+
return `Distill skipped for ${inputRef}: ${message}`;
|
|
549
|
+
}
|
|
550
|
+
export function formatProposalDiffPlain(r) {
|
|
551
|
+
const header = r.isNew
|
|
552
|
+
? `# proposal ${String(r.id ?? "?")} (new asset: ${String(r.ref ?? "?")})`
|
|
553
|
+
: `# proposal ${String(r.id ?? "?")} (update: ${String(r.ref ?? "?")})`;
|
|
554
|
+
const unified = typeof r.unified === "string" ? r.unified : "";
|
|
555
|
+
if (!unified)
|
|
556
|
+
return `${header}\n(no changes)`;
|
|
557
|
+
return `${header}\n${unified}`;
|
|
558
|
+
}
|
|
559
|
+
export function formatEventsPlain(r) {
|
|
560
|
+
const events = Array.isArray(r.events) ? r.events : [];
|
|
561
|
+
const headerParts = [];
|
|
562
|
+
if (typeof r.ref === "string" && r.ref)
|
|
563
|
+
headerParts.push(`ref: ${r.ref}`);
|
|
564
|
+
if (typeof r.type === "string" && r.type)
|
|
565
|
+
headerParts.push(`type: ${r.type}`);
|
|
566
|
+
if (typeof r.since === "string" && r.since)
|
|
567
|
+
headerParts.push(`since: ${r.since}`);
|
|
568
|
+
const totalCount = typeof r.totalCount === "number" ? r.totalCount : events.length;
|
|
569
|
+
headerParts.push(`${totalCount} event(s)`);
|
|
570
|
+
const header = headerParts.join(" ");
|
|
571
|
+
if (events.length === 0) {
|
|
572
|
+
return `${header}\nNo events.`;
|
|
573
|
+
}
|
|
574
|
+
const lines = [header, ""];
|
|
575
|
+
for (const event of events) {
|
|
576
|
+
lines.push(formatEventLine(event));
|
|
577
|
+
}
|
|
578
|
+
return lines.join("\n").trimEnd();
|
|
579
|
+
}
|
|
580
|
+
export function formatEventLine(event) {
|
|
581
|
+
const ts = String(event.ts ?? "?");
|
|
582
|
+
const eventType = String(event.eventType ?? "?");
|
|
583
|
+
const ref = event.ref ? String(event.ref) : null;
|
|
584
|
+
const head = ref ? `${ts} [${eventType}] ${ref}` : `${ts} [${eventType}]`;
|
|
585
|
+
if (event.metadata != null && event.metadata !== "") {
|
|
586
|
+
const meta = typeof event.metadata === "string" ? event.metadata : JSON.stringify(event.metadata);
|
|
587
|
+
return `${head}\n metadata: ${meta}`;
|
|
588
|
+
}
|
|
589
|
+
return head;
|
|
590
|
+
}
|
|
591
|
+
export function formatHistoryPlain(r) {
|
|
592
|
+
const entries = Array.isArray(r.entries) ? r.entries : [];
|
|
593
|
+
const headerParts = [];
|
|
594
|
+
if (typeof r.ref === "string" && r.ref)
|
|
595
|
+
headerParts.push(`ref: ${r.ref}`);
|
|
596
|
+
if (typeof r.since === "string" && r.since)
|
|
597
|
+
headerParts.push(`since: ${r.since}`);
|
|
598
|
+
const totalCount = typeof r.totalCount === "number" ? r.totalCount : entries.length;
|
|
599
|
+
headerParts.push(`${totalCount} event(s)`);
|
|
600
|
+
const header = headerParts.join(" ");
|
|
601
|
+
if (entries.length === 0) {
|
|
602
|
+
const scope = typeof r.ref === "string" && r.ref ? ` for ${r.ref}` : "";
|
|
603
|
+
return `${header}\nNo history${scope}.`;
|
|
604
|
+
}
|
|
605
|
+
const lines = [header, ""];
|
|
606
|
+
for (const entry of entries) {
|
|
607
|
+
const created = String(entry.createdAt ?? "?");
|
|
608
|
+
const eventType = String(entry.eventType ?? "?");
|
|
609
|
+
const ref = entry.ref ? String(entry.ref) : null;
|
|
610
|
+
const signal = entry.signal ? String(entry.signal) : null;
|
|
611
|
+
const query = entry.query ? String(entry.query) : null;
|
|
612
|
+
const head = ref ? `${created} [${eventType}] ${ref}` : `${created} [${eventType}]`;
|
|
613
|
+
lines.push(head);
|
|
614
|
+
if (signal)
|
|
615
|
+
lines.push(` signal: ${signal}`);
|
|
616
|
+
if (query)
|
|
617
|
+
lines.push(` query: ${query}`);
|
|
618
|
+
if (entry.metadata != null && entry.metadata !== "") {
|
|
619
|
+
const meta = typeof entry.metadata === "string" ? entry.metadata : JSON.stringify(entry.metadata);
|
|
620
|
+
lines.push(` metadata: ${meta}`);
|
|
621
|
+
}
|
|
622
|
+
}
|
|
623
|
+
return lines.join("\n").trimEnd();
|
|
624
|
+
}
|
|
189
625
|
function formatShowPlain(r, detail) {
|
|
190
626
|
const lines = [];
|
|
191
627
|
if (r.type || r.name) {
|
|
@@ -260,8 +696,9 @@ function formatShowPlain(r, detail) {
|
|
|
260
696
|
}
|
|
261
697
|
export function formatWorkflowListPlain(result) {
|
|
262
698
|
const runs = Array.isArray(result.runs) ? result.runs : [];
|
|
263
|
-
if (runs.length === 0)
|
|
264
|
-
return "No workflow runs
|
|
699
|
+
if (runs.length === 0) {
|
|
700
|
+
return "No workflow runs. Start one with `akm workflow next workflow:<name>` or author one with `akm workflow create <name>`.";
|
|
701
|
+
}
|
|
265
702
|
return runs
|
|
266
703
|
.map((run) => {
|
|
267
704
|
const id = typeof run.id === "string" ? run.id : "unknown";
|
|
@@ -351,8 +788,14 @@ export function formatSearchPlain(r, detail) {
|
|
|
351
788
|
lines.push(` run: ${String(hit.run)}`);
|
|
352
789
|
if (Array.isArray(hit.tags) && hit.tags.length > 0)
|
|
353
790
|
lines.push(` tags: ${hit.tags.join(", ")}`);
|
|
354
|
-
|
|
355
|
-
|
|
791
|
+
// Optional v1 spec §4.2 quality marker (e.g. "curated" / "proposed").
|
|
792
|
+
if (typeof hit.quality === "string" && hit.quality)
|
|
793
|
+
lines.push(` quality: ${hit.quality}`);
|
|
794
|
+
// Surface optional hit-level warnings (v1 spec §4.2). The legacy
|
|
795
|
+
// `curated` boolean was removed in v1.
|
|
796
|
+
if (Array.isArray(hit.warnings) && hit.warnings.length > 0) {
|
|
797
|
+
lines.push(` warnings: ${hit.warnings.join("; ")}`);
|
|
798
|
+
}
|
|
356
799
|
if (detail === "full") {
|
|
357
800
|
if (hit.path)
|
|
358
801
|
lines.push(` path: ${String(hit.path)}`);
|
|
@@ -8,9 +8,9 @@
|
|
|
8
8
|
* updated together with this builder.
|
|
9
9
|
*/
|
|
10
10
|
import fs from "node:fs";
|
|
11
|
-
import os from "node:os";
|
|
12
11
|
import path from "node:path";
|
|
13
12
|
import { fetchWithRetry, jsonWithByteCap } from "../core/common";
|
|
13
|
+
import { getCacheDir } from "../core/paths";
|
|
14
14
|
import { generateMetadataFlat, loadStashFile } from "../indexer/metadata";
|
|
15
15
|
import { walkStashFlat } from "../indexer/walker";
|
|
16
16
|
import { asRecord, asString, GITHUB_API_BASE, githubHeaders } from "../integrations/github";
|
|
@@ -173,7 +173,16 @@ async function scanGithub(githubApiBase) {
|
|
|
173
173
|
return stashes;
|
|
174
174
|
}
|
|
175
175
|
async function inspectArchive(url, headers) {
|
|
176
|
-
|
|
176
|
+
// Route registry-build scratch space under the akm cache dir instead of
|
|
177
|
+
// the system temp directory. Long-running registry builds that crash
|
|
178
|
+
// mid-flight previously left orphan dirs under `/tmp`, which can fill
|
|
179
|
+
// the OS partition. Pinning to `${getCacheDir()}/registry-build/` keeps
|
|
180
|
+
// a single `rm -rf` of that directory sufficient to reclaim the space
|
|
181
|
+
// and leaves the operator's `/tmp` untouched. See #276 for the
|
|
182
|
+
// bench-tmp precedent and #284 for this non-bench rollout.
|
|
183
|
+
const tmpRoot = path.join(getCacheDir(), "registry-build");
|
|
184
|
+
fs.mkdirSync(tmpRoot, { recursive: true });
|
|
185
|
+
const tempDir = fs.mkdtempSync(path.join(tmpRoot, "build-"));
|
|
177
186
|
const archivePath = path.join(tempDir, "archive.tgz");
|
|
178
187
|
const extractDir = path.join(tempDir, "extract");
|
|
179
188
|
try {
|
|
@@ -283,7 +292,9 @@ async function loadManualEntries(manualEntriesPath) {
|
|
|
283
292
|
const parsed = parseRegistryIndex({ version: 3, updatedAt: new Date().toISOString(), stashes: candidateKits });
|
|
284
293
|
if (!parsed)
|
|
285
294
|
return [];
|
|
286
|
-
|
|
295
|
+
// Legacy `curated` flag on input entries is ignored (v1 spec §4.2). The
|
|
296
|
+
// builder no longer emits it on the resulting index.
|
|
297
|
+
return parsed.stashes.map((stash) => normalizeStash(stash));
|
|
287
298
|
}
|
|
288
299
|
catch {
|
|
289
300
|
return [];
|
|
@@ -327,7 +338,6 @@ function mergeEntries(a, b) {
|
|
|
327
338
|
author: a.author ?? b.author,
|
|
328
339
|
license: a.license ?? b.license,
|
|
329
340
|
latestVersion: a.latestVersion ?? b.latestVersion,
|
|
330
|
-
curated: a.curated || b.curated || undefined,
|
|
331
341
|
});
|
|
332
342
|
}
|
|
333
343
|
function mergeAssets(a, b) {
|
|
@@ -23,11 +23,3 @@ export function registerProvider(type, factory) {
|
|
|
23
23
|
export function resolveProviderFactory(type) {
|
|
24
24
|
return registry.resolve(type);
|
|
25
25
|
}
|
|
26
|
-
/**
|
|
27
|
-
* Iterate over all registered registry providers. Used by the orchestrator
|
|
28
|
-
* (`src/commands/registry-search.ts`) to fan out queries through the same
|
|
29
|
-
* `RegistryProvider` interface regardless of provider kind.
|
|
30
|
-
*/
|
|
31
|
-
export function listProviderTypes() {
|
|
32
|
-
return registry.list();
|
|
33
|
-
}
|
|
@@ -213,6 +213,9 @@ function parseStashEntry(raw) {
|
|
|
213
213
|
const source = asSource(obj.source);
|
|
214
214
|
if (!id || !name || !ref || !source)
|
|
215
215
|
return null;
|
|
216
|
+
// The legacy registry boolean `curated` is removed in v1. Legacy index JSON
|
|
217
|
+
// containing a `curated` key parses normally; the key is silently ignored
|
|
218
|
+
// (v1 spec §4.2, docs/migration/v1.md).
|
|
216
219
|
return {
|
|
217
220
|
id,
|
|
218
221
|
name,
|
|
@@ -226,7 +229,6 @@ function parseStashEntry(raw) {
|
|
|
226
229
|
author: asString(obj.author),
|
|
227
230
|
license: asString(obj.license),
|
|
228
231
|
latestVersion: asString(obj.latestVersion),
|
|
229
|
-
curated: obj.curated === true ? true : undefined,
|
|
230
232
|
};
|
|
231
233
|
}
|
|
232
234
|
// ── Scoring ─────────────────────────────────────────────────────────────────
|
|
@@ -294,7 +296,6 @@ function toSearchHit(stash, score, registryName) {
|
|
|
294
296
|
homepage: stash.homepage,
|
|
295
297
|
score: Math.round(score * 1000) / 1000,
|
|
296
298
|
metadata,
|
|
297
|
-
curated: stash.curated,
|
|
298
299
|
registryName,
|
|
299
300
|
};
|
|
300
301
|
}
|
|
@@ -4,7 +4,7 @@ import os from "node:os";
|
|
|
4
4
|
import path from "node:path";
|
|
5
5
|
import { fileURLToPath, pathToFileURL } from "node:url";
|
|
6
6
|
import { fetchWithRetry, jsonWithByteCap } from "../core/common";
|
|
7
|
-
import { UsageError } from "../core/errors";
|
|
7
|
+
import { NotFoundError, UsageError } from "../core/errors";
|
|
8
8
|
import { asRecord, asString, GITHUB_API_BASE, githubHeaders } from "../integrations/github";
|
|
9
9
|
/**
|
|
10
10
|
* Validate that a URL is safe to pass to git.
|
|
@@ -200,7 +200,7 @@ function tryParseLocalRef(rawRef, explicitPath) {
|
|
|
200
200
|
catch {
|
|
201
201
|
// Explicit paths (./foo, ../bar, /abs) should throw on missing
|
|
202
202
|
if (explicitPath) {
|
|
203
|
-
throw new
|
|
203
|
+
throw new NotFoundError(`Local path not found: ${resolvedPath}`, "FILE_NOT_FOUND", "Check the path exists and is readable.");
|
|
204
204
|
}
|
|
205
205
|
// Bare names that don't exist on disk — let caller fall through to npm/github
|
|
206
206
|
return undefined;
|
|
@@ -231,6 +231,71 @@ function isPathLikeRef(ref) {
|
|
|
231
231
|
}
|
|
232
232
|
return ref.includes("/") || ref.includes("\\");
|
|
233
233
|
}
|
|
234
|
+
/** Default public npm registry host. */
|
|
235
|
+
const DEFAULT_NPM_REGISTRY_HOST = "registry.npmjs.org";
|
|
236
|
+
/**
|
|
237
|
+
* Typed error raised when the npm registry returns a tarball URL on a host
|
|
238
|
+
* that is not the public registry or the operator-configured mirror. Carries
|
|
239
|
+
* a stable `.code` so callers (and JSON envelope output) can branch on it
|
|
240
|
+
* without parsing the message string.
|
|
241
|
+
*/
|
|
242
|
+
export class UntrustedNpmTarballError extends Error {
|
|
243
|
+
code = "UNTRUSTED_NPM_TARBALL";
|
|
244
|
+
_hint;
|
|
245
|
+
constructor(msg, hint) {
|
|
246
|
+
super(msg);
|
|
247
|
+
this.name = "UntrustedNpmTarballError";
|
|
248
|
+
this._hint = hint;
|
|
249
|
+
Object.setPrototypeOf(this, new.target.prototype);
|
|
250
|
+
}
|
|
251
|
+
hint() {
|
|
252
|
+
return (this._hint ??
|
|
253
|
+
"Set AKM_NPM_REGISTRY to your private npm mirror's base URL if you install from a non-default registry.");
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
/**
|
|
257
|
+
* Resolve the set of npm registry hosts whose tarballs are considered trusted.
|
|
258
|
+
* Always includes the public npm registry, plus the host of an operator-set
|
|
259
|
+
* `AKM_NPM_REGISTRY` environment variable (if it parses to a valid URL).
|
|
260
|
+
*/
|
|
261
|
+
export function trustedNpmTarballHosts() {
|
|
262
|
+
const hosts = new Set([DEFAULT_NPM_REGISTRY_HOST]);
|
|
263
|
+
const override = process.env.AKM_NPM_REGISTRY?.trim();
|
|
264
|
+
if (override) {
|
|
265
|
+
try {
|
|
266
|
+
const overrideHost = new URL(override).hostname.toLowerCase();
|
|
267
|
+
if (overrideHost)
|
|
268
|
+
hosts.add(overrideHost);
|
|
269
|
+
}
|
|
270
|
+
catch {
|
|
271
|
+
// Ignore unparseable overrides; the default host still applies.
|
|
272
|
+
}
|
|
273
|
+
}
|
|
274
|
+
return hosts;
|
|
275
|
+
}
|
|
276
|
+
/**
|
|
277
|
+
* Validate that an npm tarball URL points at a trusted registry host. A
|
|
278
|
+
* compromised mirror could otherwise return an attacker-controlled
|
|
279
|
+
* `dist.tarball` field, redirecting installs to a third-party host.
|
|
280
|
+
*/
|
|
281
|
+
export function validateNpmTarballUrl(tarballUrl, packageRef) {
|
|
282
|
+
let url;
|
|
283
|
+
try {
|
|
284
|
+
url = new URL(tarballUrl);
|
|
285
|
+
}
|
|
286
|
+
catch {
|
|
287
|
+
throw new UntrustedNpmTarballError(`npm package ${packageRef} returned an invalid tarball URL: ${tarballUrl}`);
|
|
288
|
+
}
|
|
289
|
+
if (url.protocol !== "https:" && url.protocol !== "http:") {
|
|
290
|
+
throw new UntrustedNpmTarballError(`npm package ${packageRef} returned a tarball with disallowed scheme "${url.protocol}".`);
|
|
291
|
+
}
|
|
292
|
+
const trusted = trustedNpmTarballHosts();
|
|
293
|
+
const host = url.hostname.toLowerCase();
|
|
294
|
+
if (!trusted.has(host)) {
|
|
295
|
+
const allowed = Array.from(trusted).join(", ");
|
|
296
|
+
throw new UntrustedNpmTarballError(`npm package ${packageRef} returned a tarball URL on untrusted host "${host}" (allowed: ${allowed}).`);
|
|
297
|
+
}
|
|
298
|
+
}
|
|
234
299
|
async function resolveNpmArtifact(parsed) {
|
|
235
300
|
const encodedName = encodeURIComponent(parsed.packageName);
|
|
236
301
|
const metadata = await fetchJson(`https://registry.npmjs.org/${encodedName}`);
|
|
@@ -262,6 +327,7 @@ async function resolveNpmArtifact(parsed) {
|
|
|
262
327
|
if (!tarballUrl) {
|
|
263
328
|
throw new Error(`npm package ${parsed.packageName}@${resolvedVersion} does not expose a tarball URL.`);
|
|
264
329
|
}
|
|
330
|
+
validateNpmTarballUrl(tarballUrl, `${parsed.packageName}@${resolvedVersion}`);
|
|
265
331
|
const resolvedRevision = asString(dist.shasum) ?? asString(dist.integrity);
|
|
266
332
|
return {
|
|
267
333
|
id: parsed.id,
|