akm-cli 0.9.0-rc.0 → 0.9.0-rc.13
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 +1283 -22
- package/README.md +62 -37
- package/SECURITY.md +46 -31
- package/dist/akm +162 -38
- package/dist/akm-migrate +44 -0
- package/dist/assets/backends/schtasks-template.xml +2 -1
- package/dist/assets/hints/cli-hints-full.md +268 -118
- package/dist/assets/hints/cli-hints-short.md +87 -24
- package/dist/assets/{profiles → improve-strategies}/catchup.json +3 -1
- package/dist/assets/{profiles → improve-strategies}/consolidate.json +3 -1
- package/dist/assets/{profiles → improve-strategies}/default.json +6 -7
- package/dist/assets/improve-strategies/frequent.json +15 -0
- package/dist/assets/{profiles → improve-strategies}/graph-refresh.json +4 -2
- package/dist/assets/{profiles → improve-strategies}/memory-focus.json +4 -1
- package/dist/assets/{profiles → improve-strategies}/proactive-maintenance.json +5 -5
- package/dist/assets/{profiles → improve-strategies}/quick.json +4 -2
- package/dist/assets/improve-strategies/reflect-distill.json +30 -0
- package/dist/assets/{profiles → improve-strategies}/thorough.json +1 -1
- package/dist/assets/prompts/consolidate-system.md +5 -5
- package/dist/assets/prompts/extract-session.md +2 -6
- package/dist/assets/prompts/memory-infer-user.md +2 -3
- package/dist/assets/prompts/reflect-llm-framed-contract.md +11 -0
- package/dist/assets/prompts/reflect-llm-schema-contract.md +3 -0
- package/dist/assets/prompts/reflect-output-repair.md +3 -0
- package/dist/assets/prompts/workflow-unit-preamble.md +26 -0
- package/dist/assets/stash-skeleton/README.md +38 -10
- package/dist/assets/stash-skeleton/facts/conventions/assets/agent.md +8 -0
- package/dist/assets/stash-skeleton/facts/conventions/assets/command.md +8 -0
- package/dist/assets/stash-skeleton/facts/conventions/assets/fact.md +14 -1
- package/dist/assets/stash-skeleton/facts/conventions/assets/knowledge.md +13 -1
- package/dist/assets/stash-skeleton/facts/conventions/assets/lesson.md +9 -1
- package/dist/assets/stash-skeleton/facts/conventions/assets/memory.md +11 -0
- package/dist/assets/stash-skeleton/facts/conventions/assets/script.md +9 -0
- package/dist/assets/stash-skeleton/facts/conventions/assets/skill.md +9 -0
- package/dist/assets/stash-skeleton/facts/conventions/assets/workflow.md +8 -0
- package/dist/assets/stash-skeleton/facts/conventions/backlinks.md +100 -0
- package/dist/assets/stash-skeleton/facts/conventions/domains.md +64 -0
- package/dist/assets/stash-skeleton/facts/conventions/organization.md +136 -0
- package/dist/assets/tasks/core/extract.yml +3 -2
- package/dist/assets/tasks/core/improve.yml +2 -1
- package/dist/assets/tasks/core/index-refresh.yml +1 -0
- package/dist/assets/tasks/core/sync.yml +1 -0
- package/dist/assets/tasks/core/version-check.yml +2 -1
- package/dist/assets/tasks/improve/akm-graph-refresh-weekly.yml +5 -0
- package/dist/assets/tasks/improve/akm-improve-catchup.yml +8 -0
- package/dist/assets/tasks/improve/akm-improve-consolidate.yml +5 -0
- package/dist/assets/tasks/improve/akm-improve-frequent.yml +5 -0
- package/dist/assets/tasks/improve/akm-improve-nightly.yml +5 -0
- package/dist/assets/templates/html/health.html +5 -4
- package/dist/assets/workflows/workflow-template.md +31 -15
- package/dist/cli/invocation.js +279 -0
- package/dist/cli/parse-args.js +5 -90
- package/dist/cli/retired-commands.js +78 -0
- package/dist/cli/shared.js +158 -48
- package/dist/cli-node.mjs +2 -1
- package/dist/cli.js +747 -293
- package/dist/commands/agent/agent-dispatch.js +19 -18
- package/dist/commands/agent/agent-support.js +0 -24
- package/dist/commands/agent/contribute-cli.js +43 -97
- package/dist/commands/completions.js +80 -23
- package/dist/commands/config-cli.js +44 -281
- package/dist/commands/env/env-binding.js +99 -0
- package/dist/commands/env/env-cli.js +84 -224
- package/dist/commands/env/env.js +12 -163
- package/dist/commands/env/marker-path.js +6 -0
- package/dist/commands/env/secret-cli.js +45 -61
- package/dist/commands/env/secret.js +32 -62
- package/dist/commands/feedback-cli.js +179 -85
- package/dist/commands/health/accept-rate.js +58 -0
- package/dist/commands/health/advisories.js +7 -8
- package/dist/commands/health/checks.js +279 -94
- package/dist/commands/health/html-report.js +197 -578
- package/dist/commands/health/improve-metrics.js +277 -246
- package/dist/commands/health/llm-usage.js +19 -19
- package/dist/commands/health/md-report.js +16 -7
- package/dist/commands/health/metrics.js +67 -32
- package/dist/commands/health/renderers.js +47 -0
- package/dist/commands/health/report-view-model.js +508 -0
- package/dist/commands/health/stash-exposure.js +1 -1
- package/dist/commands/health/surfaces.js +16 -56
- package/dist/commands/health/task-runs.js +3 -67
- package/dist/{migrate-storage-node.mjs → commands/health/types-checks.js} +1 -5
- package/dist/commands/health/types-improve.js +29 -0
- package/dist/{output/text/save.js → commands/health/types-metrics.js} +1 -2
- package/dist/commands/health/types-result.js +7 -0
- package/dist/commands/health/types-runs.js +4 -0
- package/dist/commands/health/types-session-log.js +4 -0
- package/dist/commands/health/types-windows.js +4 -0
- package/dist/commands/health/types.js +26 -21
- package/dist/commands/health/windows.js +2 -3
- package/dist/commands/health.js +296 -167
- package/dist/commands/improve/anti-collapse.js +5 -5
- package/dist/commands/improve/autonomy-gate.js +68 -0
- package/dist/commands/improve/collapse-detector.js +65 -52
- package/dist/commands/improve/consolidate/chunking.js +9 -7
- package/dist/commands/improve/consolidate/eligibility.js +1 -23
- package/dist/commands/improve/consolidate/merge.js +4 -0
- package/dist/commands/improve/consolidate.js +454 -1354
- package/dist/commands/improve/content-hash.js +39 -0
- package/dist/commands/improve/distill/content-repair.js +4 -10
- package/dist/commands/improve/distill/promote-memory.js +89 -64
- package/dist/commands/improve/distill/quality-gate.js +118 -42
- package/dist/commands/improve/distill-guards.js +1 -1
- package/dist/commands/improve/distill-promotion-policy.js +33 -888
- package/dist/commands/improve/distill.js +607 -363
- package/dist/commands/improve/eligibility.js +165 -79
- package/dist/commands/improve/extract-cli.js +35 -126
- package/dist/commands/improve/extract-prompt.js +6 -35
- package/dist/commands/improve/extract.js +640 -391
- package/dist/commands/improve/feedback-valence.js +2 -12
- package/dist/commands/improve/improve-cli.js +134 -135
- package/dist/commands/improve/improve-result-file.js +30 -50
- package/dist/commands/improve/improve-run-types.js +4 -0
- package/dist/commands/improve/improve-strategies.js +135 -0
- package/dist/commands/improve/improve.js +904 -701
- package/dist/commands/improve/locks.js +64 -111
- package/dist/commands/improve/loop-stages.js +1110 -923
- package/dist/commands/improve/memory/derived-ref.js +124 -0
- package/dist/commands/improve/memory/memory-belief.js +79 -7
- package/dist/commands/improve/memory/memory-contradiction-detect.js +49 -52
- package/dist/commands/improve/memory/memory-improve.js +25 -37
- package/dist/commands/improve/outcome-loop.js +25 -88
- package/dist/commands/improve/preparation.js +1034 -813
- package/dist/commands/improve/proactive-maintenance.js +34 -9
- package/dist/commands/improve/proposal-envelope.js +31 -0
- package/dist/commands/improve/reflect.js +983 -794
- package/dist/commands/improve/run-context.js +119 -0
- package/dist/commands/improve/salience.js +24 -127
- package/dist/commands/improve/session-asset.js +7 -3
- package/dist/commands/improve/shared.js +14 -34
- package/dist/commands/improve/source-identity.js +28 -0
- package/dist/commands/improve/triage.js +20 -17
- package/dist/commands/lint/base-linter.js +340 -313
- package/dist/commands/lint/env-key-rules.js +31 -47
- package/dist/commands/lint/index.js +185 -30
- package/dist/commands/{events.js → log.js} +28 -38
- package/dist/commands/migrate-cli.js +54 -0
- package/dist/commands/migration-tool.js +55 -0
- package/dist/commands/observability-cli.js +70 -208
- package/dist/commands/proposal/diff-format.js +50 -0
- package/dist/commands/proposal/drain-policies.js +0 -6
- package/dist/commands/proposal/drain.js +91 -40
- package/dist/commands/proposal/proposal-cli.js +134 -132
- package/dist/commands/proposal/proposal-types.js +56 -0
- package/dist/commands/proposal/proposal.js +83 -65
- package/dist/commands/proposal/propose-cli.js +88 -0
- package/dist/commands/proposal/propose.js +105 -88
- package/dist/commands/proposal/repository.js +1303 -278
- package/dist/commands/proposal/validators/proposal-quality-validators.js +16 -6
- package/dist/commands/proposal/validators/proposal-validators.js +61 -12
- package/dist/commands/proposal/validators/proposals.js +6 -8
- package/dist/commands/read/curate.js +78 -73
- package/dist/commands/read/knowledge.js +510 -13
- package/dist/commands/read/registry-search.js +2 -2
- package/dist/commands/read/remember-cli.js +84 -15
- package/dist/commands/read/search-cli.js +203 -96
- package/dist/commands/read/search.js +126 -94
- package/dist/commands/read/show.js +226 -250
- package/dist/commands/registry-cli.js +34 -60
- package/dist/commands/remember.js +18 -57
- package/dist/commands/sources/add-cli.js +104 -49
- package/dist/commands/sources/bundle-cli.js +166 -0
- package/dist/commands/sources/bundle-config-ops.js +63 -0
- package/dist/commands/sources/info.js +27 -15
- package/dist/commands/sources/init.js +30 -40
- package/dist/commands/sources/installed-stashes.js +469 -172
- package/dist/commands/sources/migration-help.js +7 -4
- package/dist/commands/sources/schema-repair.js +10 -9
- package/dist/commands/sources/self-update.js +182 -121
- package/dist/commands/sources/source-add.js +169 -178
- package/dist/commands/sources/source-clone.js +144 -41
- package/dist/commands/sources/source-manage.js +94 -59
- package/dist/commands/sources/sources-cli.js +64 -205
- package/dist/commands/sources/stash-cli.js +91 -54
- package/dist/commands/sources/stash-skeleton.js +1 -1
- package/dist/commands/tasks/tasks-cli.js +106 -104
- package/dist/commands/tasks/tasks.js +445 -262
- package/dist/commands/workflow-cli.js +232 -121
- package/dist/core/action-contributors.js +1 -1
- package/dist/core/activation-policy.js +49 -0
- package/dist/core/adapter/adapters/agent-skills-adapter.js +181 -0
- package/dist/core/adapter/adapters/akm-adapter.js +528 -0
- package/dist/core/adapter/adapters/akm-lint.js +392 -0
- package/dist/core/adapter/adapters/akm-metadata.js +387 -0
- package/dist/core/adapter/adapters/akm-task-adapter.js +149 -0
- package/dist/core/adapter/adapters/akm-workflow-adapter.js +180 -0
- package/dist/core/adapter/adapters/claude-adapter.js +61 -0
- package/dist/core/adapter/adapters/dotenv-adapter.js +187 -0
- package/dist/core/adapter/adapters/generic-files-adapter.js +119 -0
- package/dist/core/adapter/adapters/index.js +80 -0
- package/dist/core/adapter/adapters/llm-wiki-adapter.js +419 -0
- package/dist/core/adapter/adapters/okf-adapter.js +391 -0
- package/dist/core/adapter/adapters/opencode-adapter.js +68 -0
- package/dist/core/adapter/adapters/shared.js +286 -0
- package/dist/core/adapter/adapters/tool-dir-shared.js +217 -0
- package/dist/core/adapter/adapters/website-snapshot-adapter.js +155 -0
- package/dist/core/adapter/bundle-adapter.js +4 -0
- package/dist/core/adapter/detect-adapter.js +17 -0
- package/dist/core/adapter/recognize-match.js +44 -0
- package/dist/core/adapter/registry.js +56 -0
- package/dist/core/adapter/types.js +4 -0
- package/dist/core/asset/akm-markdown.js +30 -0
- package/dist/core/asset/asset-placement.js +243 -0
- package/dist/core/asset/asset-ref.js +110 -79
- package/dist/core/asset/asset-serialize.js +20 -0
- package/dist/core/asset/frontmatter.js +28 -12
- package/dist/core/asset/markdown.js +40 -51
- package/dist/core/asset/resolve-ref.js +274 -0
- package/dist/core/asset/stash-meta.js +2 -2
- package/dist/core/bundle-id.js +51 -0
- package/dist/core/common.js +281 -86
- package/dist/core/config/config-io.js +42 -128
- package/dist/core/config/config-schema.js +233 -834
- package/dist/core/config/config-sources.js +162 -39
- package/dist/core/config/config-types.js +16 -11
- package/dist/core/config/config-version.js +29 -0
- package/dist/core/config/config-walker.js +126 -37
- package/dist/core/config/config.js +154 -331
- package/dist/core/config/deep-merge.js +41 -0
- package/dist/core/config/engine-semantics.js +28 -0
- package/dist/core/config/experimental.js +21 -0
- package/dist/core/config/schema/embedding.js +38 -0
- package/dist/core/config/schema/engines.js +116 -0
- package/dist/core/config/schema/experimental.js +47 -0
- package/dist/core/config/schema/feedback.js +31 -0
- package/dist/core/config/schema/improve-processes.js +389 -0
- package/dist/core/config/schema/improve.js +94 -0
- package/dist/core/config/schema/index-config.js +176 -0
- package/dist/core/config/schema/output.js +18 -0
- package/dist/core/config/schema/primitives.js +94 -0
- package/dist/core/config/schema/search.js +30 -0
- package/dist/core/config/schema/setup.js +18 -0
- package/dist/core/config/schema/sources-bundles.js +169 -0
- package/dist/core/config/schema/workflow.js +29 -0
- package/dist/core/env-secret-ref.js +155 -20
- package/dist/core/errors.js +17 -15
- package/dist/core/events-types.js +4 -0
- package/dist/core/events.js +46 -128
- package/dist/core/extra-params.js +62 -0
- package/dist/core/file-change.js +17 -0
- package/dist/core/file-lock.js +202 -57
- package/dist/core/fs-txn.js +392 -0
- package/dist/core/git-message.js +59 -0
- package/dist/core/improve-result.js +167 -0
- package/dist/core/json-schema.js +142 -0
- package/dist/core/lesson-lint.js +1 -17
- package/dist/core/logs-db.js +1 -1
- package/dist/core/maintenance-barrier.js +135 -0
- package/dist/core/migration-operation.js +44 -0
- package/dist/core/mutation-target.js +78 -0
- package/dist/core/paths.js +22 -25
- package/dist/core/platform.js +10 -0
- package/dist/core/recognition-util.js +128 -0
- package/dist/core/redaction.js +392 -0
- package/dist/core/standards/resolve-standards-context.js +36 -65
- package/dist/core/standards/resolve-stash-standards.js +2 -2
- package/dist/core/standards/resolve-type-conventions.js +5 -5
- package/dist/core/state/migrations.js +242 -11
- package/dist/core/state-db.js +98 -10
- package/dist/core/structured.js +1 -1
- package/dist/core/subprocess.js +303 -0
- package/dist/core/text-truncation.js +9 -5
- package/dist/core/time.js +20 -0
- package/dist/core/type-presentation.js +130 -0
- package/dist/core/warn.js +0 -3
- package/dist/core/write-source.js +834 -118
- package/dist/indexer/bundle-identity-guard.js +92 -0
- package/dist/indexer/db/graph-db.js +1 -25
- package/dist/indexer/db/llm-cache.js +1 -1
- package/dist/indexer/ensure-index.js +30 -9
- package/dist/indexer/graph/graph-boost.js +9 -30
- package/dist/indexer/graph/graph-extraction.js +41 -27
- package/dist/indexer/graph/graph-types.js +4 -0
- package/dist/indexer/index-writer-lock.js +93 -49
- package/dist/indexer/index-written-assets.js +100 -53
- package/dist/indexer/indexer.js +746 -329
- package/dist/indexer/init.js +18 -25
- package/dist/indexer/installations.js +142 -0
- package/dist/indexer/passes/dir-staleness.js +18 -10
- package/dist/indexer/passes/memory-inference.js +25 -15
- package/dist/indexer/passes/metadata.js +412 -243
- package/dist/indexer/scan/doc-to-entry.js +160 -0
- package/dist/indexer/scan/drain-dir.js +134 -0
- package/dist/indexer/search/db-search.js +292 -108
- package/dist/indexer/search/fts-query.js +64 -0
- package/dist/indexer/search/ranking-contributors.js +145 -25
- package/dist/indexer/search/ranking-types.js +4 -0
- package/dist/indexer/search/ranking.js +28 -71
- package/dist/indexer/search/search-attribution.js +67 -0
- package/dist/indexer/search/search-fields.js +18 -3
- package/dist/indexer/search/search-hit-enrichers.js +30 -40
- package/dist/indexer/search/search-source.js +157 -111
- package/dist/indexer/search/semantic-status.js +4 -1
- package/dist/indexer/usage/usage-events.js +10 -30
- package/dist/indexer/walk/file-context.js +3 -45
- package/dist/indexer/walk/matchers.js +42 -34
- package/dist/indexer/walk/path-resolver.js +11 -5
- package/dist/indexer/walk/walker.js +42 -14
- package/dist/integrations/agent/builder-shared.js +7 -0
- package/dist/integrations/agent/builders.js +5 -56
- package/dist/integrations/agent/config.js +3 -143
- package/dist/integrations/agent/detect.js +17 -2
- package/dist/integrations/agent/engine-resolution.js +231 -0
- package/dist/integrations/agent/index.js +1 -2
- package/dist/integrations/agent/model-aliases.js +16 -2
- package/dist/integrations/agent/profiles.js +36 -62
- package/dist/integrations/agent/prompts.js +46 -18
- package/dist/integrations/agent/runner-dispatch.js +93 -4
- package/dist/integrations/agent/runner.js +76 -208
- package/dist/integrations/agent/spawn.js +88 -196
- package/dist/integrations/harnesses/aider/agent-builder.js +114 -0
- package/dist/integrations/harnesses/aider/index.js +48 -0
- package/dist/integrations/harnesses/aider/result-extractor.js +53 -0
- package/dist/integrations/harnesses/amazonq/agent-builder.js +147 -0
- package/dist/integrations/harnesses/amazonq/index.js +45 -0
- package/dist/integrations/harnesses/amazonq/result-extractor.js +48 -0
- package/dist/integrations/harnesses/claude/agent-builder.js +46 -8
- package/dist/integrations/harnesses/claude/config-import.js +1 -3
- package/dist/integrations/harnesses/claude/index.js +24 -35
- package/dist/integrations/harnesses/claude/result-extractor.js +52 -0
- package/dist/integrations/harnesses/claude/session-log.js +27 -75
- package/dist/integrations/harnesses/codex/agent-builder.js +138 -0
- package/dist/integrations/harnesses/codex/index.js +52 -0
- package/dist/integrations/harnesses/codex/result-extractor.js +73 -0
- package/dist/integrations/harnesses/copilot/agent-builder.js +122 -0
- package/dist/integrations/harnesses/copilot/index.js +48 -0
- package/dist/integrations/harnesses/copilot/result-extractor.js +151 -0
- package/dist/integrations/harnesses/gemini/agent-builder.js +120 -0
- package/dist/integrations/harnesses/gemini/index.js +48 -0
- package/dist/integrations/harnesses/gemini/result-extractor.js +121 -0
- package/dist/integrations/harnesses/ids.js +24 -0
- package/dist/integrations/harnesses/index.js +54 -34
- package/dist/integrations/harnesses/opencode/agent-builder.js +23 -5
- package/dist/integrations/harnesses/opencode/config-import.js +1 -3
- package/dist/integrations/harnesses/opencode/index.js +14 -32
- package/dist/integrations/harnesses/opencode/session-log.js +67 -125
- package/dist/integrations/harnesses/opencode-sdk/harness.js +51 -0
- package/dist/integrations/harnesses/opencode-sdk/sdk-runner.js +681 -108
- package/dist/integrations/harnesses/openhands/agent-builder.js +128 -0
- package/dist/integrations/harnesses/openhands/index.js +48 -0
- package/dist/integrations/harnesses/openhands/result-extractor.js +103 -0
- package/dist/integrations/harnesses/pi/agent-builder.js +97 -0
- package/dist/integrations/harnesses/pi/index.js +45 -0
- package/dist/integrations/harnesses/pi/result-extractor.js +135 -0
- package/dist/integrations/harnesses/shared.js +17 -0
- package/dist/integrations/harnesses/types.js +43 -32
- package/dist/integrations/lockfile.js +211 -24
- package/dist/integrations/session-logs/index.js +36 -39
- package/dist/integrations/session-logs/provider-base.js +113 -0
- package/dist/llm/client.js +182 -110
- package/dist/llm/embedders/deterministic.js +2 -2
- package/dist/llm/embedders/remote.js +21 -9
- package/dist/llm/feature-gate.js +17 -57
- package/dist/llm/graph-extract.js +12 -13
- package/dist/llm/index-passes.js +8 -42
- package/dist/llm/memory-infer.js +144 -1
- package/dist/llm/metadata-enhance.js +45 -30
- package/dist/llm/structured-call.js +16 -8
- package/dist/llm/usage-persist.js +30 -5
- package/dist/llm/usage-telemetry.js +59 -6
- package/dist/output/cli-hints.js +1 -2
- package/dist/output/command-registry.js +27 -0
- package/dist/output/context.js +22 -7
- package/dist/output/format-exempt.js +80 -0
- package/dist/output/generic-render.js +251 -0
- package/dist/output/html-render.js +11 -16
- package/dist/output/render-registry.js +57 -0
- package/dist/output/renderers.js +14 -279
- package/dist/output/shapes/curate.js +10 -1
- package/dist/output/shapes/events.js +12 -7
- package/dist/output/shapes/helpers.js +58 -84
- package/dist/output/shapes/passthrough.js +11 -39
- package/dist/output/shapes/proposal/producer.js +15 -7
- package/dist/output/shapes/registry.js +12 -6
- package/dist/output/shapes.js +0 -9
- package/dist/output/text/{init.js → bundle-create.js} +3 -1
- package/dist/output/text/bundle-show.js +7 -0
- package/dist/output/text/command-format.js +562 -0
- package/dist/output/text/env.js +1 -3
- package/dist/output/text/events.js +8 -7
- package/dist/output/text/helpers.js +15 -1164
- package/dist/output/text/proposal/producer.js +4 -2
- package/dist/output/text/proposal-format.js +202 -0
- package/dist/output/text/registry-commands.js +1 -2
- package/dist/output/text/registry.js +12 -6
- package/dist/output/text/show-directives.js +117 -0
- package/dist/output/text/show-format.js +103 -0
- package/dist/output/text/sync.js +5 -0
- package/dist/output/text/workflow-format.js +332 -0
- package/dist/output/text/workflow.js +3 -2
- package/dist/output/text.js +10 -19
- package/dist/registry/factory.js +4 -6
- package/dist/registry/origin-resolve.js +16 -27
- package/dist/registry/providers/skills-sh.js +3 -3
- package/dist/registry/providers/static-index.js +15 -25
- package/dist/registry/resolve.js +43 -94
- package/dist/registry/semver.js +43 -0
- package/dist/runtime.js +81 -12
- package/dist/scripts/akm-migrate.js +35529 -0
- package/dist/setup/detect.js +5 -7
- package/dist/setup/detected-engines.js +136 -0
- package/dist/setup/engine-config.js +100 -0
- package/dist/setup/registry-stash-loader.js +3 -3
- package/dist/setup/semantic-assets.js +12 -9
- package/dist/setup/setup.js +444 -208
- package/dist/setup/steps/connection-shared.js +120 -0
- package/dist/setup/steps/connection.js +108 -305
- package/dist/setup/steps/platforms.js +13 -12
- package/dist/setup/steps/semantic.js +15 -3
- package/dist/setup/steps/sources.js +21 -15
- package/dist/setup/steps/stashdir.js +6 -4
- package/dist/setup/steps/tasks.js +236 -119
- package/dist/setup/steps.js +3 -2
- package/dist/sources/freshness.js +39 -0
- package/dist/sources/provider-factory.js +11 -17
- package/dist/sources/providers/filesystem.js +2 -3
- package/dist/sources/providers/git-install.js +278 -34
- package/dist/sources/providers/git-provider.js +54 -56
- package/dist/sources/providers/git-stash.js +420 -91
- package/dist/sources/providers/git.js +2 -2
- package/dist/sources/providers/npm.js +16 -19
- package/dist/sources/providers/provider-utils.js +47 -22
- package/dist/sources/providers/sync-from-ref.js +3 -9
- package/dist/sources/providers/website.js +2 -2
- package/dist/sources/resolve.js +11 -10
- package/dist/sources/snapshot-fetchers/types.js +4 -0
- package/dist/sources/{website-ingest.js → snapshot-fetchers/website-ingest.js} +110 -41
- package/dist/storage/database.js +60 -4
- package/dist/storage/engines/sqlite-migrations.js +156 -5
- package/dist/storage/locations.js +1 -2
- package/dist/storage/repositories/canaries-repository.js +1 -1
- package/dist/storage/repositories/events-repository.js +51 -11
- package/dist/storage/repositories/improve-runs-repository.js +6 -32
- package/dist/storage/repositories/index-connection.js +79 -0
- package/dist/storage/repositories/index-db.js +4 -3
- package/dist/storage/repositories/index-entries-repository.js +863 -0
- package/dist/{indexer/db/entry-mapper.js → storage/repositories/index-entry-mapper.js} +19 -2
- package/dist/storage/repositories/index-entry-types.js +4 -0
- package/dist/storage/repositories/index-fts-repository.js +167 -0
- package/dist/storage/repositories/index-llm-cache-repository.js +108 -0
- package/dist/storage/repositories/index-meta-repository.js +49 -0
- package/dist/{indexer/db/schema.js → storage/repositories/index-schema.js} +226 -100
- package/dist/storage/repositories/index-sql.js +12 -0
- package/dist/storage/repositories/index-utility-repository.js +356 -0
- package/dist/storage/repositories/index-vec-repository.js +250 -0
- package/dist/storage/repositories/outcome-repository.js +119 -0
- package/dist/storage/repositories/proposals-repository.js +317 -75
- package/dist/storage/repositories/registry-cache.js +1 -1
- package/dist/storage/repositories/salience-repository.js +172 -0
- package/dist/storage/repositories/task-history-repository.js +110 -3
- package/dist/storage/repositories/workflow-runs-repository.js +240 -19
- package/dist/tasks/backends/cron.js +169 -46
- package/dist/tasks/backends/exec-utils.js +76 -3
- package/dist/tasks/backends/index.js +6 -9
- package/dist/tasks/backends/launchd.js +292 -55
- package/dist/tasks/backends/schtasks.js +557 -70
- package/dist/tasks/backends/types.js +4 -0
- package/dist/tasks/command-executable.js +93 -0
- package/dist/tasks/embedded.js +56 -38
- package/dist/tasks/parser.js +156 -64
- package/dist/tasks/resolve-akm-bin.js +144 -51
- package/dist/tasks/runner.js +377 -209
- package/dist/tasks/schedule.js +108 -19
- package/dist/tasks/scheduler-invocation.js +296 -0
- package/dist/tasks/schema.js +1 -1
- package/dist/tasks/task-id.js +35 -0
- package/dist/tasks/validator.js +30 -16
- package/dist/text-import-hook.mjs +1 -1
- package/dist/workflows/authoring/authoring.js +104 -43
- package/dist/workflows/authoring/scope-key.js +1 -1
- package/dist/workflows/cli.js +0 -16
- package/dist/workflows/concurrency-policy.js +15 -0
- package/dist/workflows/exec/brief.js +450 -0
- package/dist/workflows/exec/frozen-judge.js +47 -0
- package/dist/workflows/exec/native-executor.js +1038 -0
- package/dist/workflows/exec/param-secrets.js +115 -0
- package/dist/workflows/exec/report.js +1460 -0
- package/dist/workflows/exec/run-workflow.js +602 -0
- package/dist/workflows/exec/scheduler.js +71 -0
- package/dist/workflows/exec/step-work.js +1190 -0
- package/dist/workflows/exec/unit-writer.js +23 -0
- package/dist/workflows/exec/workflow-engine-gate.js +67 -0
- package/dist/workflows/exec/worktree.js +171 -0
- package/dist/workflows/ir/compile.js +246 -0
- package/dist/workflows/ir/freeze.js +233 -0
- package/dist/workflows/ir/params.js +54 -0
- package/dist/workflows/ir/plan-hash.js +68 -0
- package/dist/workflows/ir/schema.js +540 -0
- package/dist/workflows/parser.js +878 -304
- package/dist/workflows/program/expressions.js +181 -0
- package/dist/workflows/program/schema.js +51 -0
- package/dist/workflows/renderer.js +100 -45
- package/dist/workflows/resource-limits.js +22 -0
- package/dist/workflows/runtime/agent-identity.js +59 -14
- package/dist/workflows/runtime/checkin.js +1 -1
- package/dist/workflows/runtime/plan-classifier.js +131 -0
- package/dist/workflows/runtime/runs.js +376 -119
- package/dist/workflows/runtime/unit-checkin.js +45 -0
- package/dist/workflows/runtime/unit-phases.js +20 -0
- package/dist/workflows/runtime/workflow-asset-loader.js +241 -40
- package/dist/workflows/schema.js +1 -11
- package/dist/workflows/validate-summary.js +2 -3
- package/dist/workflows/validator.js +52 -30
- package/docs/README.md +42 -78
- package/docs/migration/README.md +8 -0
- package/docs/migration/release-notes/0.6.0.md +1 -1
- package/docs/migration/release-notes/0.7.0.md +9 -8
- package/docs/migration/release-notes/0.9.0.md +158 -14
- package/docs/migration/v0.7-to-v0.8.md +46 -47
- package/docs/migration/v0.8-to-v0.9.md +844 -0
- package/docs/reference/README.md +12 -0
- package/docs/reference/data-and-telemetry.md +333 -0
- package/package.json +21 -17
- package/schemas/akm-asset-envelope.json +93 -0
- package/schemas/akm-config.json +4636 -0
- package/schemas/akm-task.json +87 -0
- package/schemas/akm-workflow.json +373 -0
- package/dist/akm-migrate-storage +0 -38
- package/dist/assets/help/help-accept.md +0 -12
- package/dist/assets/help/help-improve.md +0 -84
- package/dist/assets/help/help-proposals.md +0 -17
- package/dist/assets/help/help-propose.md +0 -17
- package/dist/assets/help/help-reject.md +0 -11
- package/dist/assets/profiles/frequent.json +0 -13
- package/dist/assets/profiles/recombine-only.json +0 -21
- package/dist/assets/profiles/reflect-distill.json +0 -30
- package/dist/assets/profiles/synthesize.json +0 -15
- package/dist/assets/prompts/procedural-system.md +0 -44
- package/dist/assets/prompts/recombine-system.md +0 -40
- package/dist/assets/prompts/staleness-detect-system.md +0 -6
- package/dist/assets/tasks/core/backup.yml +0 -4
- package/dist/assets/tasks/graph-refresh-weekly.yml +0 -10
- package/dist/assets/templates/html/default.html +0 -78
- package/dist/assets/templates/html/vendor/echarts.min.js +0 -45
- package/dist/assets/wiki/index-template.md +0 -12
- package/dist/assets/wiki/ingest-workflow-template.md +0 -83
- package/dist/assets/wiki/log-template.md +0 -8
- package/dist/assets/wiki/schema-template.md +0 -61
- package/dist/cli/config-migrate.js +0 -150
- package/dist/cli/config-validate.js +0 -39
- package/dist/commands/graph/graph-cli.js +0 -124
- package/dist/commands/graph/graph.js +0 -487
- package/dist/commands/improve/calibration.js +0 -161
- package/dist/commands/improve/dedup.js +0 -482
- package/dist/commands/improve/extract-watch.js +0 -140
- package/dist/commands/improve/hot-probation.js +0 -45
- package/dist/commands/improve/improve-auto-accept.js +0 -276
- package/dist/commands/improve/improve-profiles.js +0 -168
- package/dist/commands/improve/procedural.js +0 -398
- package/dist/commands/improve/recombine.js +0 -818
- package/dist/commands/improve/schema-similarity-gate.js +0 -168
- package/dist/commands/lint/agent-linter.js +0 -44
- package/dist/commands/lint/command-linter.js +0 -44
- package/dist/commands/lint/default-linter.js +0 -16
- package/dist/commands/lint/fact-linter.js +0 -39
- package/dist/commands/lint/knowledge-linter.js +0 -16
- package/dist/commands/lint/memory-linter.js +0 -61
- package/dist/commands/lint/registry.js +0 -41
- package/dist/commands/lint/skill-linter.js +0 -45
- package/dist/commands/lint/task-linter.js +0 -50
- package/dist/commands/lint/workflow-linter.js +0 -81
- package/dist/commands/proposal/legacy-import.js +0 -115
- package/dist/commands/sources/history.js +0 -196
- package/dist/commands/tasks/default-tasks.js +0 -186
- package/dist/commands/wiki-cli.js +0 -292
- package/dist/core/asset/asset-registry.js +0 -76
- package/dist/core/asset/asset-spec.js +0 -259
- package/dist/core/config/config-migration.js +0 -602
- package/dist/core/deep-merge.js +0 -38
- package/dist/core/eval/rank-metrics.js +0 -113
- package/dist/core/ripgrep/install.js +0 -163
- package/dist/core/ripgrep/resolve.js +0 -81
- package/dist/indexer/db/db.js +0 -1413
- package/dist/indexer/manifest.js +0 -170
- package/dist/indexer/passes/metadata-contributors.js +0 -31
- package/dist/indexer/usage/unmigrated-vaults-guard.js +0 -94
- package/dist/integrations/harnesses/opencode-sdk/index.js +0 -49
- package/dist/llm/call-ai.js +0 -62
- package/dist/llm/memory-infer-impl.js +0 -138
- package/dist/output/shapes/distill.js +0 -14
- package/dist/output/shapes/history.js +0 -11
- package/dist/output/text/distill.js +0 -6
- package/dist/output/text/enable-disable.js +0 -8
- package/dist/output/text/history.js +0 -6
- package/dist/output/text/wiki.js +0 -16
- package/dist/registry/build-index.js +0 -386
- package/dist/scripts/migrate-storage.js +0 -19108
- package/dist/scripts/migrations/import-fs-improve-runs-to-db.js +0 -9411
- package/dist/scripts/migrations/v16-to-v17.js +0 -141
- package/dist/setup/legacy-config.js +0 -106
- package/dist/storage/repositories/consolidation-repository.js +0 -38
- package/dist/storage/repositories/recombine-repository.js +0 -213
- package/dist/wiki/wiki-templates.js +0 -15
- package/dist/wiki/wiki.js +0 -1012
- package/dist/workflows/db.js +0 -215
- package/docs/data-and-telemetry.md +0 -226
- /package/dist/sources/{wiki-fetchers → snapshot-fetchers}/registry.js +0 -0
- /package/dist/sources/{wiki-fetchers → snapshot-fetchers}/youtube.js +0 -0
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
// This Source Code Form is subject to the terms of the Mozilla Public
|
|
2
|
+
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
3
|
+
// file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
|
4
|
+
import { getAdapters } from "./registry.js";
|
|
5
|
+
/** Select the first built-in adapter whose ordered root probe claims `root`. */
|
|
6
|
+
export function detectAdapterId(root, fallback = "akm") {
|
|
7
|
+
for (const adapter of getAdapters()) {
|
|
8
|
+
try {
|
|
9
|
+
if (adapter.looksLikeRoot?.(root) === true)
|
|
10
|
+
return adapter.id;
|
|
11
|
+
}
|
|
12
|
+
catch {
|
|
13
|
+
// An unreadable or racing probe does not claim the bundle.
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
return fallback;
|
|
17
|
+
}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
// This Source Code Form is subject to the terms of the Mozilla Public
|
|
2
|
+
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
3
|
+
// file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
|
4
|
+
import { directoryMatcher, extensionMatcher, parentDirHintMatcher, smartMdMatcher } from "../../indexer/walk/matchers.js";
|
|
5
|
+
/**
|
|
6
|
+
* The four builtin matchers, in registration order. The array index IS the
|
|
7
|
+
* registration index `runMatchers` uses for tie-breaking. (The `wiki` matcher
|
|
8
|
+
* was removed in chunk 4 — the wiki asset-type is retired; LLM Wiki content is
|
|
9
|
+
* served by the first-class `llm-wiki` adapter, not the akm adapter. The YAML
|
|
10
|
+
* workflow-program matcher was removed by workflow-format-unification — one
|
|
11
|
+
* workflow format now, recognized by frontmatter `type: workflow` or
|
|
12
|
+
* residence under `workflows/`, both already covered by the remaining four.)
|
|
13
|
+
*/
|
|
14
|
+
const AKM_MATCHERS = [
|
|
15
|
+
extensionMatcher,
|
|
16
|
+
directoryMatcher,
|
|
17
|
+
parentDirHintMatcher,
|
|
18
|
+
smartMdMatcher,
|
|
19
|
+
];
|
|
20
|
+
/**
|
|
21
|
+
* Synchronous reproduction of `file-context.ts#runMatchers`'s arbitration
|
|
22
|
+
* (`:242-265`), minus its `ensureBuiltinsRegistered()` dynamic import. Runs
|
|
23
|
+
* every builtin matcher in registration order, collects the non-null
|
|
24
|
+
* `MatchResult`s, and returns the highest-specificity one (ties broken by the
|
|
25
|
+
* later-registered matcher — higher index — winning). Returns null when no
|
|
26
|
+
* matcher claims the file.
|
|
27
|
+
*/
|
|
28
|
+
export function recognizeMatch(file) {
|
|
29
|
+
const hits = [];
|
|
30
|
+
for (let i = 0; i < AKM_MATCHERS.length; i++) {
|
|
31
|
+
const result = AKM_MATCHERS[i](file);
|
|
32
|
+
if (result !== null)
|
|
33
|
+
hits.push({ result, index: i });
|
|
34
|
+
}
|
|
35
|
+
if (hits.length === 0)
|
|
36
|
+
return null;
|
|
37
|
+
hits.sort((a, b) => {
|
|
38
|
+
const specDiff = b.result.specificity - a.result.specificity;
|
|
39
|
+
if (specDiff !== 0)
|
|
40
|
+
return specDiff;
|
|
41
|
+
return b.index - a.index;
|
|
42
|
+
});
|
|
43
|
+
return hits[0].result;
|
|
44
|
+
}
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
// This Source Code Form is subject to the terms of the Mozilla Public
|
|
2
|
+
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
3
|
+
// file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
|
4
|
+
/**
|
|
5
|
+
* The format-family adapter registry — akm 0.9.0 (§4 / normative §12.6).
|
|
6
|
+
*
|
|
7
|
+
* A STATIC, FROZEN registry per normative §12.6: the ordered built-in adapter
|
|
8
|
+
* list ({@link BUILTIN_ADAPTERS}) is defined in the built-in barrel
|
|
9
|
+
* (`./adapters`) and this module exposes read-only lookups over it. There is NO
|
|
10
|
+
* mutable registration step and NO load-order dependency — `getAdapters()` and
|
|
11
|
+
* `adapterForId()` are populated at MODULE LOAD, so every production call site
|
|
12
|
+
* sees the full set without anyone first calling a registration function. This
|
|
13
|
+
* replaces the earlier mutable module-level singleton, whose "someone must
|
|
14
|
+
* register the built-ins first" contract left the registry empty in production
|
|
15
|
+
* (every source then fell back to `akm`).
|
|
16
|
+
*
|
|
17
|
+
* PRODUCTION CONSUMERS (all install/index-time, never query-time): the ordered
|
|
18
|
+
* `looksLikeRoot` probe in `installations.ts#detectAdapterId`, and the
|
|
19
|
+
* bundle-root probe in `provider-utils.ts#detectStashRoot`.
|
|
20
|
+
*
|
|
21
|
+
* KEYED BY `adapter.id` ONLY. Adapters are FORMAT FAMILIES (§0.2): one adapter
|
|
22
|
+
* per component root, and `recognize()` is the single source of truth for what
|
|
23
|
+
* a file IS. The open OKF `type` lives on the emitted `IndexDocument`, never on
|
|
24
|
+
* the adapter — so there is deliberately NO per-`type` → adapter mapping here.
|
|
25
|
+
*
|
|
26
|
+
* QUERY-TIME SAFETY (normative §14.3 / D11 — "adapters/registry never run at
|
|
27
|
+
* query time"): this module is imported only from install/index-time modules;
|
|
28
|
+
* the search path (`indexer/search/**`) does not import it, and importing the
|
|
29
|
+
* frozen list pulls the concrete adapters into no query-time graph.
|
|
30
|
+
*/
|
|
31
|
+
import { BUILTIN_ADAPTERS } from "./adapters/index.js";
|
|
32
|
+
export { BUILTIN_ADAPTERS } from "./adapters/index.js";
|
|
33
|
+
/** `adapter.id -> adapter`, frozen at module load from the static built-in list. */
|
|
34
|
+
const BY_ID = new Map(BUILTIN_ADAPTERS.map((a) => [a.id, a]));
|
|
35
|
+
/**
|
|
36
|
+
* Every built-in adapter, in the §1.2 install-time probe order (array order ==
|
|
37
|
+
* probe precedence; see `./adapters` for the ordering rationale). Returns a
|
|
38
|
+
* fresh array each call so callers may sort/filter it without disturbing the
|
|
39
|
+
* frozen registry.
|
|
40
|
+
*/
|
|
41
|
+
export function getAdapters() {
|
|
42
|
+
return [...BUILTIN_ADAPTERS];
|
|
43
|
+
}
|
|
44
|
+
/** Look up an adapter by its own `id` (matches `BundleComponent.adapter`); `undefined` for an unknown id (spec §4 — caller skips + warns). */
|
|
45
|
+
export function adapterForId(id) {
|
|
46
|
+
return BY_ID.get(id);
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* DEPRECATED no-op retained for test back-compat only. The registry is a static
|
|
50
|
+
* frozen map (normative §12.6) populated at module load, so there is nothing to
|
|
51
|
+
* reset — `getAdapters()` / `adapterForId()` are always the full built-in set.
|
|
52
|
+
* New code MUST NOT depend on this.
|
|
53
|
+
*/
|
|
54
|
+
export function resetAdapterRegistryForTests() {
|
|
55
|
+
// intentionally empty — the registry is static; see the doc comment above.
|
|
56
|
+
}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
// This Source Code Form is subject to the terms of the Mozilla Public
|
|
2
|
+
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
3
|
+
// file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
|
4
|
+
import { parse as parseYaml } from "yaml";
|
|
5
|
+
import { UsageError } from "../errors.js";
|
|
6
|
+
import { serializeFrontmatter } from "./asset-serialize.js";
|
|
7
|
+
import { parseFrontmatterBlock } from "./frontmatter.js";
|
|
8
|
+
/** Ensure an AKM-authored Markdown concept is also a conformant OKF concept. */
|
|
9
|
+
export function ensureAkmMarkdownType(content, type) {
|
|
10
|
+
const block = parseFrontmatterBlock(content);
|
|
11
|
+
if (!block)
|
|
12
|
+
return `---\ntype: ${type}\n---\n${content}`;
|
|
13
|
+
let parsed;
|
|
14
|
+
try {
|
|
15
|
+
parsed = block.frontmatter.trim() ? parseYaml(block.frontmatter) : {};
|
|
16
|
+
}
|
|
17
|
+
catch {
|
|
18
|
+
throw new UsageError("AKM Markdown has malformed YAML frontmatter.", "INVALID_FLAG_VALUE");
|
|
19
|
+
}
|
|
20
|
+
if (parsed === null)
|
|
21
|
+
parsed = {};
|
|
22
|
+
if (typeof parsed !== "object" || Array.isArray(parsed)) {
|
|
23
|
+
throw new UsageError("AKM Markdown frontmatter must be a YAML mapping.", "INVALID_FLAG_VALUE");
|
|
24
|
+
}
|
|
25
|
+
const data = parsed;
|
|
26
|
+
if (data.type === type)
|
|
27
|
+
return content;
|
|
28
|
+
const { type: _priorType, ...rest } = data;
|
|
29
|
+
return `---\n${serializeFrontmatter({ type, ...rest })}\n---\n${block.content}`;
|
|
30
|
+
}
|
|
@@ -0,0 +1,243 @@
|
|
|
1
|
+
// This Source Code Form is subject to the terms of the Mozilla Public
|
|
2
|
+
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
3
|
+
// file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
|
4
|
+
/**
|
|
5
|
+
* Pure recognition/placement layer for the built-in akm asset types — akm
|
|
6
|
+
* 0.9.0 chunk-3 (the taxonomy-cutover leaf).
|
|
7
|
+
*
|
|
8
|
+
* This module is the SINGLE SOURCE OF TRUTH for the `AssetSpec` filesystem
|
|
9
|
+
* surface (`stashDir`/`isRelevantFile`/`toCanonicalName`/`toAssetPath`), the
|
|
10
|
+
* per-type stash-subdir map, and the derive/resolve helpers built on them.
|
|
11
|
+
* Chunk-3 deleted the mutable taxonomy globals that used to expose this data
|
|
12
|
+
* as ambient registries; the surface is now reached through the small typed
|
|
13
|
+
* accessors below (`stashDirFor`/`stashDirNames`/`placementTypes`/
|
|
14
|
+
* `assetPathForName`/`placementSpecFor`), which the `akm` bundle adapter
|
|
15
|
+
* (`adapter/adapters/akm-adapter.ts`) consumes for placement.
|
|
16
|
+
*
|
|
17
|
+
* ── Cycle-safety ──
|
|
18
|
+
*
|
|
19
|
+
* This leaf imports only Node builtins and the `core/recognition-util` pure
|
|
20
|
+
* sink, so it is NOT an import-cycle (SCC) participant. Depending on it from
|
|
21
|
+
* the `akm` adapter keeps the adapter a leaf. Renderer NAMES and action
|
|
22
|
+
* builders for the built-in types live in the static `type-presentation.ts`
|
|
23
|
+
* table (`TYPE_PRESENTATION`); they are intentionally absent here — placement
|
|
24
|
+
* is a filesystem concern, presentation is a rendering concern.
|
|
25
|
+
*/
|
|
26
|
+
import fs from "node:fs";
|
|
27
|
+
import path from "node:path";
|
|
28
|
+
import { SCRIPT_EXTENSIONS, WORKFLOW_EXTENSIONS } from "../recognition-util.js";
|
|
29
|
+
function toPosix(input) {
|
|
30
|
+
return input.replace(/\\/g, "/");
|
|
31
|
+
}
|
|
32
|
+
const workflowSpec = {
|
|
33
|
+
isRelevantFile: (fileName) => WORKFLOW_EXTENSIONS.includes(path.extname(fileName).toLowerCase()),
|
|
34
|
+
toCanonicalName: (typeRoot, filePath) => {
|
|
35
|
+
const rel = toPosix(path.relative(typeRoot, filePath));
|
|
36
|
+
for (const ext of WORKFLOW_EXTENSIONS) {
|
|
37
|
+
if (rel.toLowerCase().endsWith(ext))
|
|
38
|
+
return rel.slice(0, -ext.length);
|
|
39
|
+
}
|
|
40
|
+
return rel;
|
|
41
|
+
},
|
|
42
|
+
toAssetPath: (typeRoot, name) => {
|
|
43
|
+
// Explicit extension wins (accepts refs like "release/ship.yaml").
|
|
44
|
+
const lower = name.toLowerCase();
|
|
45
|
+
for (const ext of WORKFLOW_EXTENSIONS) {
|
|
46
|
+
if (lower.endsWith(ext))
|
|
47
|
+
return path.join(typeRoot, name);
|
|
48
|
+
}
|
|
49
|
+
// Probe in canonical extension priority order and fall back
|
|
50
|
+
// to the markdown path so error messages keep naming the canonical file.
|
|
51
|
+
for (const ext of WORKFLOW_EXTENSIONS) {
|
|
52
|
+
const candidate = path.join(typeRoot, `${name}${ext}`);
|
|
53
|
+
if (fs.existsSync(candidate))
|
|
54
|
+
return candidate;
|
|
55
|
+
}
|
|
56
|
+
return path.join(typeRoot, `${name}.md`);
|
|
57
|
+
},
|
|
58
|
+
};
|
|
59
|
+
const markdownSpec = {
|
|
60
|
+
isRelevantFile: (fileName) => path.extname(fileName).toLowerCase() === ".md",
|
|
61
|
+
toCanonicalName: (typeRoot, filePath) => {
|
|
62
|
+
const rel = toPosix(path.relative(typeRoot, filePath));
|
|
63
|
+
// Strip .md extension from canonical names.
|
|
64
|
+
return rel.endsWith(".md") ? rel.slice(0, -3) : rel;
|
|
65
|
+
},
|
|
66
|
+
toAssetPath: (typeRoot, name) => {
|
|
67
|
+
// Accept both with and without .md extension
|
|
68
|
+
const withExt = name.endsWith(".md") ? name : `${name}.md`;
|
|
69
|
+
return path.join(typeRoot, withExt);
|
|
70
|
+
},
|
|
71
|
+
};
|
|
72
|
+
const scriptSpec = {
|
|
73
|
+
isRelevantFile: (fileName) => SCRIPT_EXTENSIONS.has(path.extname(fileName).toLowerCase()),
|
|
74
|
+
toCanonicalName: (typeRoot, filePath) => toPosix(path.relative(typeRoot, filePath)),
|
|
75
|
+
toAssetPath: (typeRoot, name) => path.join(typeRoot, name),
|
|
76
|
+
};
|
|
77
|
+
const BUILTIN_PLACEMENT_SPECS = {
|
|
78
|
+
skill: {
|
|
79
|
+
stashDir: "skills",
|
|
80
|
+
isRelevantFile: (fileName) => fileName === "SKILL.md",
|
|
81
|
+
toCanonicalName: (typeRoot, filePath) => {
|
|
82
|
+
const relDir = toPosix(path.dirname(path.relative(typeRoot, filePath)));
|
|
83
|
+
if (!relDir || relDir === ".")
|
|
84
|
+
return undefined;
|
|
85
|
+
return relDir;
|
|
86
|
+
},
|
|
87
|
+
toAssetPath: (typeRoot, name) => path.join(typeRoot, name, "SKILL.md"),
|
|
88
|
+
},
|
|
89
|
+
command: { stashDir: "commands", ...markdownSpec },
|
|
90
|
+
agent: { stashDir: "agents", ...markdownSpec },
|
|
91
|
+
knowledge: { stashDir: "knowledge", ...markdownSpec },
|
|
92
|
+
// R-045 / Q-18 second half (owner ruling 11, EXECUTE NOW) — `instruction` as
|
|
93
|
+
// a real stash-resident type, mirroring `knowledge`'s plain markdown spec.
|
|
94
|
+
// This is distinct from the ADAPTER-OWNED instruction docs emitted by
|
|
95
|
+
// format-family adapters (root CLAUDE.md/AGENTS.md via `tool-dir-shared.ts`):
|
|
96
|
+
// those carry `document.ownsPresentation === true` and are routed straight
|
|
97
|
+
// to their adapter's own projection by `rendererForIndexedEntry`
|
|
98
|
+
// (`src/commands/read/show.ts`), which checks `ownsPresentation` BEFORE
|
|
99
|
+
// ever consulting a type/renderer mapping. A stash-resident `instructions/`
|
|
100
|
+
// asset never sets that marker, so it always renders via the `knowledge-md`
|
|
101
|
+
// renderer (`TYPE_PRESENTATION.instruction`, `src/core/type-presentation.ts`)
|
|
102
|
+
// like any other placement type — no collision with the adapter-owned path.
|
|
103
|
+
instruction: { stashDir: "instructions", ...markdownSpec },
|
|
104
|
+
workflow: { stashDir: "workflows", ...workflowSpec },
|
|
105
|
+
script: { stashDir: "scripts", ...scriptSpec },
|
|
106
|
+
memory: { stashDir: "memories", ...markdownSpec },
|
|
107
|
+
// Environment assets — whole `.env` files sourced/injected wholesale. Replaced
|
|
108
|
+
// the deprecated `vault` type (removed in 0.9.0). Only key NAMES are surfaced
|
|
109
|
+
// as metadata; values and comment text are never read for indexing (comments
|
|
110
|
+
// routinely contain commented-out credentials).
|
|
111
|
+
env: {
|
|
112
|
+
stashDir: "env",
|
|
113
|
+
isRelevantFile: (fileName) => fileName === ".env" || fileName.endsWith(".env"),
|
|
114
|
+
toCanonicalName: (typeRoot, filePath) => {
|
|
115
|
+
const rel = toPosix(path.relative(typeRoot, filePath));
|
|
116
|
+
const fileName = path.basename(rel);
|
|
117
|
+
// Treat ".env" as the "default" env; "<name>.env" → "<name>"
|
|
118
|
+
if (fileName === ".env") {
|
|
119
|
+
const dir = path.dirname(rel);
|
|
120
|
+
return dir === "." || dir === "" ? "default" : `${dir}/default`;
|
|
121
|
+
}
|
|
122
|
+
const stripped = rel.endsWith(".env") ? rel.slice(0, -4) : rel;
|
|
123
|
+
return stripped;
|
|
124
|
+
},
|
|
125
|
+
toAssetPath: (typeRoot, name) => {
|
|
126
|
+
if (name === "default")
|
|
127
|
+
return path.join(typeRoot, ".env");
|
|
128
|
+
return path.join(typeRoot, name.endsWith(".env") ? name : `${name}.env`);
|
|
129
|
+
},
|
|
130
|
+
},
|
|
131
|
+
// Secrets — a single sensitive value used on its own for authentication (a
|
|
132
|
+
// PEM key, API token, TLS cert). Unlike `env` (a group of related .env
|
|
133
|
+
// configuration), the ENTIRE file is the one secret value — there is no safe
|
|
134
|
+
// region to parse, so only the filename is ever surfaced as metadata. A
|
|
135
|
+
// secret is any regular file under `secrets/` except `.lock`/`.sensitive`
|
|
136
|
+
// sidecars; the canonical name preserves the natural filename.
|
|
137
|
+
secret: {
|
|
138
|
+
stashDir: "secrets",
|
|
139
|
+
isRelevantFile: (fileName) => !fileName.endsWith(".lock") && !fileName.endsWith(".sensitive"),
|
|
140
|
+
toCanonicalName: (typeRoot, filePath) => toPosix(path.relative(typeRoot, filePath)),
|
|
141
|
+
toAssetPath: (typeRoot, name) => path.join(typeRoot, name),
|
|
142
|
+
},
|
|
143
|
+
// v1 spec §13 — `lesson` asset type. Required frontmatter fields are
|
|
144
|
+
// `description` and `when_to_use`; lint enforces both.
|
|
145
|
+
lesson: { stashDir: "lessons", ...markdownSpec },
|
|
146
|
+
// Scheduled tasks. A task file pairs a cron-style schedule with a target
|
|
147
|
+
// (workflow ref, prompt, or command). Stored as pure YAML under
|
|
148
|
+
// <stash>/tasks/<id>.yml.
|
|
149
|
+
task: {
|
|
150
|
+
stashDir: "tasks",
|
|
151
|
+
isRelevantFile: (fileName) => path.extname(fileName).toLowerCase() === ".yml",
|
|
152
|
+
toCanonicalName: (typeRoot, filePath) => {
|
|
153
|
+
const rel = toPosix(path.relative(typeRoot, filePath));
|
|
154
|
+
return rel.endsWith(".yml") ? rel.slice(0, -4) : rel;
|
|
155
|
+
},
|
|
156
|
+
toAssetPath: (typeRoot, name) => {
|
|
157
|
+
const withExt = name.endsWith(".yml") ? name : `${name}.yml`;
|
|
158
|
+
return path.join(typeRoot, withExt);
|
|
159
|
+
},
|
|
160
|
+
},
|
|
161
|
+
// #561 — agent sessions indexed as a first-class searchable asset type.
|
|
162
|
+
// Generated markdown written by the `extract` pass to
|
|
163
|
+
// `sessions/<harness>/<session-id>.md`.
|
|
164
|
+
session: { stashDir: "sessions", ...markdownSpec },
|
|
165
|
+
// Durable stash-level semantic knowledge — facts about the user, team, or
|
|
166
|
+
// project. A plain markdown spec; see docs/architecture/specs/fact-asset-type.md.
|
|
167
|
+
fact: { stashDir: "facts", ...markdownSpec },
|
|
168
|
+
};
|
|
169
|
+
const _placementKeysSubsetOfKnownTypes = true;
|
|
170
|
+
void _placementKeysSubsetOfKnownTypes;
|
|
171
|
+
const PLACEMENT_SPECS = { ...BUILTIN_PLACEMENT_SPECS };
|
|
172
|
+
/** Live placement spec for a type, or `undefined` for an unknown type. */
|
|
173
|
+
export function placementSpecFor(type) {
|
|
174
|
+
return PLACEMENT_SPECS[type];
|
|
175
|
+
}
|
|
176
|
+
/** Every registered placement spec (for whole-registry sweeps). */
|
|
177
|
+
export function placementSpecList() {
|
|
178
|
+
return Object.values(PLACEMENT_SPECS);
|
|
179
|
+
}
|
|
180
|
+
/** Every registered placement type key (the set of types that have a stash subdir). */
|
|
181
|
+
export function placementTypes() {
|
|
182
|
+
return Object.keys(PLACEMENT_SPECS);
|
|
183
|
+
}
|
|
184
|
+
/** The stash subdir a type places into, or `undefined` for an unknown type. */
|
|
185
|
+
export function stashDirFor(type) {
|
|
186
|
+
return PLACEMENT_SPECS[type]?.stashDir;
|
|
187
|
+
}
|
|
188
|
+
/**
|
|
189
|
+
* Reverse of {@link stashDirFor}: the placement type owning a stash subdir, or
|
|
190
|
+
* `undefined` when no registered type places into it. The type→subdir map is a
|
|
191
|
+
* bijection over the built-in types (each type has a distinct subdir), so this
|
|
192
|
+
* is the well-defined inverse used to project a path-based conceptId onto the
|
|
193
|
+
* asset-type metadata required by native adapters.
|
|
194
|
+
*/
|
|
195
|
+
export function typeForStashDir(stashDir) {
|
|
196
|
+
for (const [type, spec] of Object.entries(PLACEMENT_SPECS)) {
|
|
197
|
+
if (spec.stashDir === stashDir)
|
|
198
|
+
return type;
|
|
199
|
+
}
|
|
200
|
+
return undefined;
|
|
201
|
+
}
|
|
202
|
+
/** All stash subdir names across the registered types. */
|
|
203
|
+
export function stashDirNames() {
|
|
204
|
+
return Object.values(PLACEMENT_SPECS).map((spec) => spec.stashDir);
|
|
205
|
+
}
|
|
206
|
+
/**
|
|
207
|
+
* Mutate the placement registry to add/replace a type's spec. Used by the
|
|
208
|
+
* custom-asset-type registration surface (extension types); built-ins are
|
|
209
|
+
* baked into {@link PLACEMENT_SPECS} above.
|
|
210
|
+
*/
|
|
211
|
+
export function registerAssetSpec(type, spec) {
|
|
212
|
+
PLACEMENT_SPECS[type] = spec;
|
|
213
|
+
}
|
|
214
|
+
/** Remove a previously-registered type's spec. */
|
|
215
|
+
export function deregisterAssetSpec(type) {
|
|
216
|
+
delete PLACEMENT_SPECS[type];
|
|
217
|
+
}
|
|
218
|
+
export function isRelevantAssetFile(assetType, fileName) {
|
|
219
|
+
return PLACEMENT_SPECS[assetType]?.isRelevantFile(fileName) ?? false;
|
|
220
|
+
}
|
|
221
|
+
export function deriveCanonicalAssetName(assetType, typeRoot, filePath) {
|
|
222
|
+
return PLACEMENT_SPECS[assetType]?.toCanonicalName(typeRoot, filePath);
|
|
223
|
+
}
|
|
224
|
+
export function deriveCanonicalAssetNameFromStashRoot(assetType, stashRoot, filePath) {
|
|
225
|
+
const relPath = toPosix(path.relative(stashRoot, filePath));
|
|
226
|
+
const segments = relPath.split("/").filter(Boolean);
|
|
227
|
+
const firstSegment = segments[0];
|
|
228
|
+
// When the first segment matches the canonical type dir (e.g. "agents"),
|
|
229
|
+
// use it as the type root so canonical names are relative to it.
|
|
230
|
+
// Otherwise fall back to stashRoot — this preserves the full relative path
|
|
231
|
+
// as the canonical name, which is correct for installed stashes that live
|
|
232
|
+
// under custom directories (e.g. "tools/agents/svelte-file-editor").
|
|
233
|
+
const typeRoot = firstSegment !== undefined && firstSegment === stashDirFor(assetType)
|
|
234
|
+
? path.join(stashRoot, firstSegment)
|
|
235
|
+
: stashRoot;
|
|
236
|
+
return deriveCanonicalAssetName(assetType, typeRoot, filePath);
|
|
237
|
+
}
|
|
238
|
+
export function assetPathForName(assetType, typeRoot, name) {
|
|
239
|
+
const spec = PLACEMENT_SPECS[assetType];
|
|
240
|
+
if (!spec)
|
|
241
|
+
throw new Error(`Unknown asset type: "${assetType}"`);
|
|
242
|
+
return spec.toAssetPath(typeRoot, name);
|
|
243
|
+
}
|
|
@@ -2,104 +2,135 @@
|
|
|
2
2
|
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
3
3
|
// file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
|
4
4
|
import path from "node:path";
|
|
5
|
-
import { isAssetType } from "../common.js";
|
|
6
5
|
import { UsageError } from "../errors.js";
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
6
|
+
// ── Validation ──────────────────────────────────────────────────────────────
|
|
7
|
+
function validateName(name) {
|
|
8
|
+
if (!name)
|
|
9
|
+
throw new UsageError("Empty asset name.", "MISSING_REQUIRED_ARGUMENT");
|
|
10
|
+
if (name.includes("\0"))
|
|
11
|
+
throw new UsageError("Null byte in asset name.", "MISSING_REQUIRED_ARGUMENT");
|
|
12
|
+
if (/^[A-Za-z]:/.test(name))
|
|
13
|
+
throw new UsageError("Windows drive path in asset name.", "MISSING_REQUIRED_ARGUMENT");
|
|
14
|
+
const slashName = name.replace(/\\/g, "/");
|
|
15
|
+
if (slashName === ".." || slashName.startsWith("../")) {
|
|
16
|
+
throw new UsageError("Path traversal in asset name.", "MISSING_REQUIRED_ARGUMENT");
|
|
17
|
+
}
|
|
18
|
+
if (slashName.split("/").some((seg) => seg === "." || seg === "..")) {
|
|
19
|
+
throw new UsageError("Asset name cannot contain relative path segments.", "MISSING_REQUIRED_ARGUMENT");
|
|
20
|
+
}
|
|
21
|
+
const normalized = path.posix.normalize(slashName);
|
|
22
|
+
if (path.posix.isAbsolute(normalized))
|
|
23
|
+
throw new UsageError("Absolute path in asset name.", "MISSING_REQUIRED_ARGUMENT");
|
|
24
|
+
if (normalized === ".." || normalized.startsWith("../")) {
|
|
25
|
+
throw new UsageError("Path traversal in asset name.", "MISSING_REQUIRED_ARGUMENT");
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
function normalizeName(name) {
|
|
29
|
+
return path.posix.normalize(name.replace(/\\/g, "/"));
|
|
30
|
+
}
|
|
12
31
|
/**
|
|
13
|
-
*
|
|
32
|
+
* Bundle slug charset (spec §3.4): non-empty, and excluding `:`/`.`/`#`/`/`
|
|
33
|
+
* plus whitespace. Excluding `:`/`.` is what keeps `bundle//conceptId` lexically
|
|
34
|
+
* distinguishable from a URL in prose; excluding `/` is what makes the first
|
|
35
|
+
* `//` an unambiguous bundle boundary; excluding `#` reserves it for fragments.
|
|
36
|
+
*/
|
|
37
|
+
const BUNDLE_SLUG_RE = /^[^\s:.#/]+$/;
|
|
38
|
+
/**
|
|
39
|
+
* True when `s` is a legal bundle slug (spec §11.1 / D-R5 charset: non-empty,
|
|
40
|
+
* no `:`/`.`/`#`/`/` or whitespace). Exported for input boundaries that need
|
|
41
|
+
* to distinguish bundle-qualified refs from source locators.
|
|
42
|
+
*/
|
|
43
|
+
export function isBundleSlug(s) {
|
|
44
|
+
return BUNDLE_SLUG_RE.test(s);
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* Body-ref recognition (prose): the FULLY-QUALIFIED anchored form ONLY
|
|
48
|
+
* (`<bundle>//<concept-id>[#<fragment>]`, spec §11.1 — the short form is never
|
|
49
|
+
* recognized in prose). `g`/`m` for scanning; group 1 = the whole ref token.
|
|
14
50
|
*
|
|
15
|
-
*
|
|
16
|
-
*
|
|
17
|
-
*
|
|
18
|
-
*
|
|
19
|
-
*
|
|
20
|
-
*
|
|
21
|
-
*
|
|
22
|
-
*
|
|
23
|
-
*
|
|
51
|
+
* The leading `//` must be preceded by a bundle slug, so a URL (`https://…`,
|
|
52
|
+
* `//cdn.example.com/…`) never matches: a scheme carries a `:` before `//`
|
|
53
|
+
* (excluded from the slug), and a scheme-relative `//host` has no slug before
|
|
54
|
+
* its `//` at all. The bundle-slug charset here additionally excludes the prose
|
|
55
|
+
* boundary punctuation (brackets/parens/quotes/backtick/comma/angle) so a
|
|
56
|
+
* leading boundary char (e.g. the `[` of a markdown link) is not absorbed into
|
|
57
|
+
* the slug. The concept segment reuses the same terminator charset as the
|
|
58
|
+
* body-ref scan (whitespace/quotes/brackets/comma/nl), and admits `/`,
|
|
59
|
+
* `.`, and a trailing `#fragment`.
|
|
24
60
|
*/
|
|
25
|
-
export
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
const
|
|
29
|
-
if (
|
|
30
|
-
|
|
31
|
-
|
|
61
|
+
export const BUNDLE_REF_RE = /(?:^|[\s`"'(,[])([^\s:.#/`"'()[\],<>]+\/\/[^\s"'`)\]>,\n]+)/gm;
|
|
62
|
+
/** NFC-normalize + traversal/null-byte/drive-letter guard + path-normalize a concept id. */
|
|
63
|
+
function normalizeConceptId(raw) {
|
|
64
|
+
const nfc = raw.normalize("NFC");
|
|
65
|
+
if (nfc.includes("#")) {
|
|
66
|
+
throw new UsageError("`#` is reserved for the export fragment in a concept id.", "MISSING_REQUIRED_ARGUMENT");
|
|
67
|
+
}
|
|
68
|
+
validateName(nfc);
|
|
69
|
+
return normalizeName(nfc);
|
|
32
70
|
}
|
|
33
71
|
/**
|
|
34
|
-
*
|
|
35
|
-
* `[origin//]type:name` string form. The single formatter for refs — call
|
|
36
|
-
* this instead of hand-building `${type}:${name}` template strings so the
|
|
37
|
-
* serialization rules (origin prefix, name normalization) live in one place
|
|
38
|
-
* and stay in lockstep with {@link parseAssetRef}.
|
|
72
|
+
* Build a fully-qualified (or short) bundle ref string from its components.
|
|
39
73
|
*
|
|
40
|
-
*
|
|
41
|
-
*
|
|
74
|
+
* Examples:
|
|
75
|
+
* makeBundleRef(undefined, "knowledge/http-caching") → "knowledge/http-caching"
|
|
76
|
+
* makeBundleRef("core", "knowledge/http-caching") → "core//knowledge/http-caching"
|
|
77
|
+
* makeBundleRef("core", "skills/review", "usage") → "core//skills/review#usage"
|
|
42
78
|
*/
|
|
43
|
-
export function
|
|
44
|
-
|
|
79
|
+
export function makeBundleRef(bundle, conceptId, fragment) {
|
|
80
|
+
const normalized = normalizeConceptId(conceptId);
|
|
81
|
+
let out = normalized;
|
|
82
|
+
if (bundle) {
|
|
83
|
+
if (!BUNDLE_SLUG_RE.test(bundle)) {
|
|
84
|
+
throw new UsageError(`Invalid bundle slug "${bundle}". A bundle slug may not contain ':', '.', '#', '/', or whitespace.`, "MISSING_REQUIRED_ARGUMENT");
|
|
85
|
+
}
|
|
86
|
+
out = `${bundle}//${normalized}`;
|
|
87
|
+
}
|
|
88
|
+
if (fragment)
|
|
89
|
+
out = `${out}#${fragment}`;
|
|
90
|
+
return out;
|
|
45
91
|
}
|
|
46
|
-
// ── Parsing ─────────────────────────────────────────────────────────────────
|
|
47
92
|
/**
|
|
48
|
-
*
|
|
93
|
+
* Serialize a parsed {@link BundleRef} back to its canonical
|
|
94
|
+
* `[bundle//]conceptId[#fragment]` string form — the single formatter for the
|
|
95
|
+
* 0.9.0 grammar (call this instead of hand-building `${bundle}//${conceptId}`).
|
|
96
|
+
* `bundleRefToString(parseBundleRef(s))` round-trips for any `s` that
|
|
97
|
+
* `parseBundleRef` accepts.
|
|
49
98
|
*/
|
|
50
|
-
export function
|
|
99
|
+
export function bundleRefToString(ref) {
|
|
100
|
+
return makeBundleRef(ref.bundle, ref.conceptId, ref.fragment);
|
|
101
|
+
}
|
|
102
|
+
/**
|
|
103
|
+
* Parse a ref string in the 0.9.0 format `[<bundle>//]<concept-id>[#<fragment>]`.
|
|
104
|
+
* The bundle prefix and the export fragment are both optional; the short form
|
|
105
|
+
* (no `bundle//`) leaves `bundle` undefined for the caller to resolve against
|
|
106
|
+
* the containing bundle (§11.1).
|
|
107
|
+
*/
|
|
108
|
+
export function parseBundleRef(ref) {
|
|
51
109
|
const trimmed = ref.trim();
|
|
52
110
|
if (!trimmed)
|
|
53
111
|
throw new UsageError("Empty ref.", "MISSING_REQUIRED_ARGUMENT");
|
|
54
|
-
let
|
|
112
|
+
let bundle;
|
|
55
113
|
let body = trimmed;
|
|
56
114
|
const boundary = trimmed.indexOf("//");
|
|
57
115
|
if (boundary >= 0) {
|
|
58
|
-
|
|
116
|
+
bundle = trimmed.slice(0, boundary);
|
|
59
117
|
body = trimmed.slice(boundary + 2);
|
|
60
|
-
if (!
|
|
61
|
-
throw new UsageError("Empty
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
throw new UsageError(`Invalid ref "${trimmed}". Expected [origin//]type:name, e.g. skill:deploy or knowledge:guide.md`, "MISSING_REQUIRED_ARGUMENT");
|
|
118
|
+
if (!bundle)
|
|
119
|
+
throw new UsageError("Empty bundle in ref.", "MISSING_REQUIRED_ARGUMENT");
|
|
120
|
+
if (!BUNDLE_SLUG_RE.test(bundle)) {
|
|
121
|
+
throw new UsageError(`Invalid bundle slug "${bundle}" in ref "${trimmed}". A bundle slug may not contain ':', '.', '#', '/', or whitespace.`, "MISSING_REQUIRED_ARGUMENT");
|
|
122
|
+
}
|
|
66
123
|
}
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
}
|
|
74
|
-
// Type aliases: `environment:` is an accepted spelling of the canonical
|
|
75
|
-
// `env:` type.
|
|
76
|
-
const resolvedType = TYPE_ALIASES[rawType] ?? rawType;
|
|
77
|
-
if (!isAssetType(resolvedType)) {
|
|
78
|
-
throw new UsageError(`Invalid asset type: "${rawType}".`, "MISSING_REQUIRED_ARGUMENT");
|
|
79
|
-
}
|
|
80
|
-
validateName(rawName);
|
|
81
|
-
const name = normalizeName(rawName);
|
|
82
|
-
return { type: resolvedType, name, origin: origin || undefined };
|
|
83
|
-
}
|
|
84
|
-
// ── Validation ──────────────────────────────────────────────────────────────
|
|
85
|
-
function validateName(name) {
|
|
86
|
-
if (!name)
|
|
87
|
-
throw new UsageError("Empty asset name.", "MISSING_REQUIRED_ARGUMENT");
|
|
88
|
-
if (name.includes("\0"))
|
|
89
|
-
throw new UsageError("Null byte in asset name.", "MISSING_REQUIRED_ARGUMENT");
|
|
90
|
-
if (/^[A-Za-z]:/.test(name))
|
|
91
|
-
throw new UsageError("Windows drive path in asset name.", "MISSING_REQUIRED_ARGUMENT");
|
|
92
|
-
const normalized = path.posix.normalize(name.replace(/\\/g, "/"));
|
|
93
|
-
if (path.posix.isAbsolute(normalized))
|
|
94
|
-
throw new UsageError("Absolute path in asset name.", "MISSING_REQUIRED_ARGUMENT");
|
|
95
|
-
if (normalized === ".." || normalized.startsWith("../")) {
|
|
96
|
-
throw new UsageError("Path traversal in asset name.", "MISSING_REQUIRED_ARGUMENT");
|
|
124
|
+
// Export fragment: everything after the first `#` in the concept body.
|
|
125
|
+
let fragment;
|
|
126
|
+
const hash = body.indexOf("#");
|
|
127
|
+
if (hash >= 0) {
|
|
128
|
+
fragment = body.slice(hash + 1) || undefined;
|
|
129
|
+
body = body.slice(0, hash);
|
|
97
130
|
}
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
throw new UsageError("Asset name cannot contain relative path segments.", "MISSING_REQUIRED_ARGUMENT");
|
|
131
|
+
if (!body) {
|
|
132
|
+
throw new UsageError(`Invalid ref "${trimmed}". Expected [bundle//]conceptId, e.g. knowledge/guide or core//skills/review`, "MISSING_REQUIRED_ARGUMENT");
|
|
101
133
|
}
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
return path.posix.normalize(name.replace(/\\/g, "/"));
|
|
134
|
+
const conceptId = normalizeConceptId(body);
|
|
135
|
+
return { bundle: bundle || undefined, conceptId, fragment };
|
|
105
136
|
}
|
|
@@ -45,6 +45,26 @@ import { stringify as yamlStringify } from "yaml";
|
|
|
45
45
|
export function serializeFrontmatter(frontmatter) {
|
|
46
46
|
return yamlStringify(frontmatter).trimEnd();
|
|
47
47
|
}
|
|
48
|
+
/**
|
|
49
|
+
* Serialize a frontmatter object with every value `JSON.stringify`-quoted,
|
|
50
|
+
* without `---` fences and without a trailing newline.
|
|
51
|
+
*
|
|
52
|
+
* Use this instead of {@link serializeFrontmatter} when the input is a
|
|
53
|
+
* pre-validated LLM payload where `yaml.stringify` may emit shapes
|
|
54
|
+
* (`|`-block scalars, anchors, unquoted multiline) that the project's
|
|
55
|
+
* hand-rolled `parseFrontmatter` subset parser cannot read back. Every scalar
|
|
56
|
+
* becomes a quoted JSON literal; arrays become `[<json>, <json>]` (space after
|
|
57
|
+
* the comma). Field order is preserved from the input's insertion order.
|
|
58
|
+
*
|
|
59
|
+
* This is the single home for the "guaranteed-quoted scalars" serializer the
|
|
60
|
+
* module doc above anticipates; before it, `distill` (array-aware) and
|
|
61
|
+
* `distill/content-repair` (scalar-only) reimplemented it divergently.
|
|
62
|
+
*/
|
|
63
|
+
export function serializeFrontmatterQuoted(frontmatter) {
|
|
64
|
+
return Object.entries(frontmatter)
|
|
65
|
+
.map(([k, v]) => Array.isArray(v) ? `${k}: [${v.map((s) => JSON.stringify(s)).join(", ")}]` : `${k}: ${JSON.stringify(v)}`)
|
|
66
|
+
.join("\n");
|
|
67
|
+
}
|
|
48
68
|
/**
|
|
49
69
|
* Assemble a complete asset file string from a frontmatter object and a body.
|
|
50
70
|
*
|