akm-cli 0.9.0-rc.8 → 0.9.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +1063 -44
- package/README.md +51 -25
- package/SECURITY.md +14 -1
- package/STABILITY.md +497 -0
- package/dist/akm +148 -35
- package/dist/{akm-migrate-storage → akm-migrate} +6 -9
- package/dist/assets/hints/cli-hints-full.md +223 -95
- package/dist/assets/hints/cli-hints-short.md +85 -22
- package/dist/assets/improve-strategies/default.json +1 -1
- package/dist/assets/improve-strategies/reflect-distill.json +1 -1
- package/dist/assets/prompts/memory-infer-user.md +2 -3
- package/dist/assets/stash-skeleton/README.md +6 -5
- package/dist/assets/stash-skeleton/facts/conventions/assets/agent.md +2 -0
- package/dist/assets/stash-skeleton/facts/conventions/assets/command.md +2 -0
- package/dist/assets/stash-skeleton/facts/conventions/assets/fact.md +2 -0
- package/dist/assets/stash-skeleton/facts/conventions/assets/knowledge.md +2 -0
- package/dist/assets/stash-skeleton/facts/conventions/assets/lesson.md +2 -0
- package/dist/assets/stash-skeleton/facts/conventions/assets/memory.md +2 -0
- package/dist/assets/stash-skeleton/facts/conventions/assets/script.md +2 -0
- package/dist/assets/stash-skeleton/facts/conventions/assets/skill.md +2 -0
- package/dist/assets/stash-skeleton/facts/conventions/assets/workflow.md +2 -0
- package/dist/assets/stash-skeleton/facts/conventions/backlinks.md +2 -0
- package/dist/assets/stash-skeleton/facts/conventions/domains.md +2 -0
- package/dist/assets/stash-skeleton/facts/conventions/organization.md +20 -9
- package/dist/assets/tasks/core/extract.yml +1 -1
- package/dist/assets/tasks/core/version-check.yml +1 -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 +1 -3
- package/dist/assets/workflows/workflow-template.md +32 -15
- package/dist/cli/invocation.js +40 -15
- package/dist/cli/parse-args.js +0 -22
- package/dist/cli/retired-commands.js +121 -0
- package/dist/cli/shared.js +154 -22
- package/dist/cli/unknown-flags.js +236 -0
- package/dist/cli-node.mjs +2 -1
- package/dist/cli.js +696 -258
- package/dist/commands/agent/agent-dispatch.js +14 -3
- package/dist/commands/agent/contribute-cli.js +73 -88
- package/dist/commands/completions.js +79 -22
- package/dist/commands/config-cli.js +17 -150
- package/dist/commands/env/env-cli.js +59 -143
- package/dist/commands/env/env.js +12 -163
- package/dist/commands/env/marker-path.js +6 -0
- package/dist/commands/env/secret-cli.js +36 -66
- package/dist/commands/env/secret.js +24 -57
- package/dist/commands/feedback-cli.js +141 -87
- package/dist/commands/health/accept-rate.js +58 -0
- package/dist/commands/health/advisories.js +3 -4
- package/dist/commands/health/checks.js +85 -23
- package/dist/commands/health/html-report.js +7 -10
- package/dist/commands/health/improve-metrics.js +25 -83
- package/dist/commands/health/md-report.js +5 -9
- package/dist/commands/health/metrics.js +62 -20
- package/dist/commands/health/renderers.js +47 -0
- package/dist/commands/health/report-view-model.js +4 -5
- package/dist/commands/health/stash-exposure.js +1 -1
- package/dist/commands/health/surfaces.js +3 -48
- package/dist/commands/health/task-runs.js +3 -67
- package/dist/commands/health/types-improve.js +7 -0
- package/dist/commands/health.js +99 -28
- package/dist/commands/improve/anti-collapse.js +2 -2
- package/dist/commands/improve/autonomy-gate.js +68 -0
- package/dist/commands/improve/collapse-detector.js +41 -40
- package/dist/commands/improve/consolidate/eligibility.js +1 -23
- package/dist/commands/improve/consolidate/merge.js +4 -0
- package/dist/commands/improve/consolidate.js +140 -1000
- package/dist/commands/improve/distill/promote-memory.js +12 -12
- package/dist/commands/improve/distill/quality-gate.js +6 -6
- package/dist/commands/improve/distill.js +58 -69
- package/dist/commands/improve/eligibility.js +105 -57
- package/dist/commands/improve/extract-cli.js +14 -133
- package/dist/commands/improve/improve-cli.js +98 -114
- package/dist/commands/improve/improve-result-file.js +1 -28
- package/dist/commands/improve/improve-strategies.js +8 -5
- package/dist/commands/improve/improve.js +128 -91
- package/dist/commands/improve/loop-stages.js +182 -20
- package/dist/commands/improve/memory/derived-ref.js +45 -43
- package/dist/commands/improve/memory/memory-belief.js +1 -1
- package/dist/commands/improve/memory/memory-contradiction-detect.js +4 -12
- package/dist/commands/improve/memory/memory-improve.js +6 -5
- package/dist/commands/improve/outcome-loop.js +22 -65
- package/dist/commands/improve/preparation.js +114 -123
- package/dist/commands/improve/proactive-maintenance.js +2 -5
- package/dist/commands/improve/reflect.js +56 -160
- package/dist/commands/improve/salience.js +11 -122
- package/dist/commands/improve/source-identity.js +10 -38
- package/dist/commands/lint/base-linter.js +20 -124
- package/dist/commands/lint/env-key-rules.js +31 -47
- package/dist/commands/lint/index.js +249 -43
- package/dist/commands/{events.js → log.js} +33 -38
- package/dist/commands/migrate-cli.js +92 -12
- package/dist/commands/migration-tool.js +46 -0
- package/dist/commands/observability-cli.js +70 -209
- package/dist/commands/proposal/drain.js +101 -29
- package/dist/commands/proposal/proposal-cli.js +76 -48
- package/dist/commands/proposal/proposal.js +54 -18
- package/dist/commands/proposal/propose-cli.js +88 -0
- package/dist/commands/proposal/propose.js +23 -15
- package/dist/commands/proposal/repository.js +701 -278
- package/dist/commands/proposal/validators/proposal-quality-validators.js +2 -8
- package/dist/commands/proposal/validators/proposal-validators.js +55 -7
- package/dist/commands/proposal/validators/proposals.js +4 -7
- package/dist/commands/read/curate.js +34 -53
- package/dist/commands/read/knowledge.js +150 -95
- package/dist/commands/read/registry-search.js +2 -2
- package/dist/commands/read/remember-cli.js +42 -15
- package/dist/commands/read/search-cli.js +180 -78
- package/dist/commands/read/search.js +58 -43
- package/dist/commands/read/show.js +197 -141
- package/dist/commands/registry-cli.js +12 -51
- package/dist/commands/remember.js +14 -57
- package/dist/commands/sources/add-cli.js +100 -31
- package/dist/commands/sources/bundle-cli.js +166 -0
- package/dist/commands/sources/bundle-config-ops.js +7 -2
- package/dist/commands/sources/info.js +18 -5
- package/dist/commands/sources/init.js +12 -12
- package/dist/commands/sources/installed-stashes.js +382 -98
- package/dist/commands/sources/schema-repair.js +3 -2
- package/dist/commands/sources/self-update.js +131 -38
- package/dist/commands/sources/source-add.js +72 -17
- package/dist/commands/sources/source-clone.js +129 -45
- package/dist/commands/sources/source-manage.js +43 -23
- package/dist/commands/sources/sources-cli.js +57 -208
- package/dist/commands/sources/stash-cli.js +46 -53
- package/dist/commands/tasks/tasks-cli.js +91 -97
- package/dist/commands/tasks/tasks.js +276 -421
- package/dist/commands/workflow-cli.js +175 -450
- package/dist/core/adapter/adapters/akm-adapter.js +47 -28
- package/dist/core/adapter/adapters/akm-lint.js +42 -27
- package/dist/core/adapter/adapters/akm-metadata.js +15 -44
- package/dist/core/adapter/adapters/akm-task-adapter.js +15 -13
- package/dist/core/adapter/adapters/akm-workflow-adapter.js +55 -71
- package/dist/core/adapter/adapters/dotenv-adapter.js +1 -1
- package/dist/core/adapter/adapters/generic-files-adapter.js +2 -0
- package/dist/core/adapter/adapters/index.js +6 -6
- package/dist/core/adapter/adapters/llm-wiki-adapter.js +14 -8
- package/dist/core/adapter/adapters/okf-adapter.js +187 -19
- package/dist/core/adapter/adapters/shared.js +3 -19
- package/dist/core/adapter/adapters/tool-dir-shared.js +8 -3
- package/dist/core/adapter/adapters/website-snapshot-adapter.js +1 -0
- package/dist/core/adapter/detect-adapter.js +17 -0
- package/dist/core/adapter/recognize-match.js +6 -4
- package/dist/core/adapter/validate-context.js +214 -0
- package/dist/core/asset/akm-markdown.js +63 -0
- package/dist/core/asset/asset-placement.js +20 -6
- package/dist/core/asset/asset-ref.js +11 -9
- package/dist/core/asset/frontmatter-lint.js +30 -0
- package/dist/core/asset/frontmatter.js +37 -9
- package/dist/core/asset/markdown.js +40 -51
- package/dist/core/asset/resolve-ref.js +89 -18
- package/dist/core/asset/stash-meta.js +1 -1
- package/dist/core/bundle-id.js +51 -0
- package/dist/core/common.js +152 -38
- package/dist/core/config/config-io.js +12 -1
- package/dist/core/config/config-schema.js +35 -8
- package/dist/core/config/config-sources.js +55 -11
- package/dist/core/config/config-walker.js +25 -9
- package/dist/core/config/config.js +9 -48
- package/dist/core/config/experimental.js +21 -0
- package/dist/core/config/schema/embedding.js +5 -1
- package/dist/core/config/schema/experimental.js +30 -0
- package/dist/core/config/schema/improve-processes.js +0 -6
- package/dist/core/config/schema/improve.js +21 -3
- package/dist/core/config/schema/index-config.js +8 -15
- package/dist/core/config/schema/output.js +4 -1
- package/dist/core/config/schema/setup.js +9 -18
- package/dist/core/config/schema/sources-bundles.js +49 -33
- package/dist/core/config/schema/workflow.js +3 -3
- package/dist/core/env-secret-ref.js +76 -46
- package/dist/core/errors.js +18 -12
- package/dist/core/events.js +46 -128
- package/dist/core/file-change.js +6 -5
- package/dist/core/fs-txn.js +83 -7
- package/dist/core/git-message.js +2 -2
- package/dist/core/improve-result.js +1 -100
- package/dist/core/lesson-lint.js +1 -17
- package/dist/core/logs-db.js +2 -1
- package/dist/core/migration-operation.js +16 -0
- package/dist/core/mutation-target.js +78 -0
- package/dist/core/parse.js +4 -1
- package/dist/core/paths.js +17 -20
- package/dist/core/recognition-util.js +12 -14
- package/dist/core/redaction.js +34 -0
- package/dist/core/standards/resolve-standards-context.js +2 -14
- package/dist/core/standards/resolve-stash-standards.js +2 -2
- package/dist/core/standards/resolve-type-conventions.js +2 -2
- package/dist/core/state/migrations.js +41 -18
- package/dist/core/state-db.js +5 -14
- package/dist/core/structured.js +1 -1
- package/dist/core/subprocess.js +6 -4
- package/dist/core/text-truncation.js +9 -5
- package/dist/core/type-presentation.js +3 -3
- package/dist/core/warn.js +0 -3
- package/dist/core/write-source.js +771 -95
- package/dist/indexer/bundle-identity-guard.js +3 -2
- package/dist/indexer/db/graph-db.js +0 -24
- package/dist/indexer/ensure-index.js +1 -0
- package/dist/indexer/graph/graph-boost.js +9 -34
- package/dist/indexer/graph/graph-extraction.js +8 -5
- package/dist/indexer/index-writer-lock.js +53 -17
- package/dist/indexer/index-written-assets.js +16 -22
- package/dist/indexer/indexer.js +497 -239
- package/dist/indexer/installations.js +14 -96
- package/dist/indexer/passes/dir-staleness.js +16 -9
- package/dist/indexer/passes/memory-inference.js +11 -9
- package/dist/indexer/passes/metadata.js +113 -47
- package/dist/indexer/scan/doc-to-entry.js +38 -1
- package/dist/indexer/scan/drain-dir.js +13 -23
- package/dist/indexer/search/db-search.js +99 -54
- package/dist/indexer/search/fts-query.js +47 -24
- package/dist/indexer/search/ranking-contributors.js +42 -20
- package/dist/indexer/search/ranking.js +18 -99
- package/dist/indexer/search/search-fields.js +7 -2
- package/dist/indexer/search/search-source.js +82 -93
- package/dist/indexer/usage/usage-events.js +0 -89
- package/dist/indexer/walk/file-context.js +2 -1
- package/dist/indexer/walk/matchers.js +30 -43
- package/dist/indexer/walk/path-resolver.js +7 -2
- package/dist/indexer/walk/walker.js +38 -12
- package/dist/integrations/agent/builders.js +0 -6
- package/dist/integrations/agent/config.js +2 -2
- package/dist/integrations/agent/detect.js +49 -19
- package/dist/integrations/agent/engine-fallback.js +76 -0
- package/dist/integrations/agent/profiles.js +14 -0
- package/dist/integrations/agent/prompts.js +12 -8
- package/dist/integrations/agent/runner-dispatch.js +4 -2
- package/dist/integrations/agent/runner.js +0 -1
- package/dist/integrations/agent/spawn.js +5 -6
- package/dist/integrations/github.js +1 -1
- package/dist/integrations/harnesses/aider/agent-builder.js +6 -4
- package/dist/integrations/harnesses/amazonq/agent-builder.js +7 -4
- package/dist/integrations/harnesses/claude/session-log.js +0 -10
- package/dist/integrations/harnesses/codex/agent-builder.js +5 -2
- package/dist/integrations/harnesses/copilot/agent-builder.js +5 -3
- package/dist/integrations/harnesses/gemini/agent-builder.js +5 -3
- package/dist/integrations/harnesses/index.js +3 -7
- package/dist/integrations/harnesses/opencode/agent-builder.js +21 -2
- package/dist/integrations/harnesses/opencode/session-log.js +0 -15
- package/dist/integrations/harnesses/opencode-sdk/sdk-runner.js +13 -4
- package/dist/integrations/harnesses/openhands/agent-builder.js +9 -6
- package/dist/integrations/harnesses/pi/agent-builder.js +6 -4
- package/dist/integrations/lockfile.js +101 -6
- package/dist/integrations/session-logs/index.js +3 -28
- package/dist/llm/client.js +136 -100
- package/dist/llm/embedders/remote.js +13 -5
- package/dist/llm/feature-gate.js +4 -12
- package/dist/llm/graph-extract.js +5 -11
- package/dist/llm/memory-infer.js +144 -1
- package/dist/llm/metadata-enhance.js +5 -7
- package/dist/llm/structured-call.js +1 -1
- package/dist/llm/usage-persist.js +26 -5
- package/dist/llm/usage-telemetry.js +25 -2
- package/dist/output/cli-hints.js +1 -2
- package/dist/output/context.js +22 -7
- package/dist/output/format-exempt.js +80 -0
- package/dist/output/generic-render.js +259 -0
- package/dist/output/render-registry.js +57 -0
- package/dist/output/renderers.js +14 -36
- package/dist/output/shapes/curate.js +10 -1
- package/dist/output/shapes/events.js +12 -7
- package/dist/output/shapes/helpers.js +56 -83
- package/dist/output/shapes/migrate.js +8 -0
- package/dist/output/shapes/passthrough.js +7 -41
- package/dist/output/shapes/proposal/producer.js +15 -7
- package/dist/output/shapes.js +2 -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 +164 -96
- package/dist/output/text/env.js +1 -3
- package/dist/output/text/events.js +8 -7
- package/dist/output/text/health-format.js +103 -0
- package/dist/output/text/health.js +7 -0
- package/dist/output/text/helpers.js +10 -8
- package/dist/output/text/lint-format.js +43 -0
- package/dist/output/text/{save.js → lint.js} +2 -2
- package/dist/output/text/migrate.js +88 -0
- package/dist/output/text/proposal/producer.js +4 -2
- package/dist/output/text/proposal-format.js +44 -72
- package/dist/output/text/registry-commands.js +1 -2
- package/dist/output/text/show-directives.js +15 -7
- package/dist/output/text/status-list.js +32 -0
- package/dist/output/text/sync.js +5 -0
- package/dist/output/text/workflow-format.js +24 -203
- package/dist/output/text/workflow.js +1 -7
- package/dist/output/text.js +16 -17
- 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 +13 -23
- package/dist/registry/resolve.js +42 -7
- package/dist/registry/semver.js +34 -84
- package/dist/runtime.js +2 -23
- package/dist/scripts/akm-migrate-node.js +60290 -0
- package/dist/scripts/akm-migrate.js +59628 -0
- package/dist/setup/detect.js +42 -15
- package/dist/setup/registry-stash-loader.js +2 -2
- package/dist/setup/setup.js +236 -136
- package/dist/setup/steps/connection.js +7 -9
- package/dist/setup/steps/platforms.js +9 -9
- package/dist/setup/steps/semantic.js +15 -3
- package/dist/setup/steps/sources.js +12 -13
- package/dist/setup/steps/stashdir.js +2 -3
- package/dist/setup/steps/tasks.js +237 -120
- package/dist/sources/freshness.js +1 -1
- 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 +25 -23
- package/dist/sources/providers/git-stash.js +395 -106
- package/dist/sources/providers/git.js +2 -2
- package/dist/sources/providers/npm.js +16 -19
- package/dist/sources/providers/provider-utils.js +7 -4
- package/dist/sources/providers/sync-from-ref.js +3 -9
- package/dist/sources/providers/website.js +6 -1
- package/dist/sources/resolve.js +6 -5
- package/dist/sources/snapshot-fetchers/bluesky.js +146 -0
- package/dist/sources/snapshot-fetchers/content-extract.js +566 -0
- package/dist/sources/snapshot-fetchers/fetcher-util.js +41 -0
- package/dist/sources/snapshot-fetchers/github.js +100 -0
- package/dist/sources/snapshot-fetchers/host-guard.js +291 -0
- package/dist/sources/snapshot-fetchers/registry.js +17 -1
- package/dist/sources/snapshot-fetchers/robots.js +348 -0
- package/dist/sources/snapshot-fetchers/rss.js +282 -0
- package/dist/sources/snapshot-fetchers/secret-seam.js +42 -0
- package/dist/sources/snapshot-fetchers/website-ingest.js +566 -268
- package/dist/sources/snapshot-fetchers/x.js +910 -0
- package/dist/storage/database.js +7 -0
- package/dist/storage/engines/sqlite-migrations.js +23 -111
- package/dist/storage/managed-db.js +2 -2
- package/dist/storage/repositories/canaries-repository.js +1 -1
- package/dist/storage/repositories/events-repository.js +27 -11
- package/dist/storage/repositories/improve-runs-repository.js +6 -12
- package/dist/storage/repositories/index-connection.js +17 -6
- package/dist/storage/repositories/index-entries-repository.js +151 -240
- package/dist/storage/repositories/index-entry-mapper.js +15 -11
- package/dist/storage/repositories/index-fts-repository.js +5 -2
- package/dist/storage/repositories/index-llm-cache-repository.js +0 -1
- package/dist/storage/repositories/index-meta-repository.js +2 -3
- package/dist/storage/repositories/index-schema.js +10 -25
- package/dist/storage/repositories/index-utility-repository.js +15 -28
- package/dist/storage/repositories/index-vec-repository.js +6 -1
- package/dist/storage/repositories/outcome-repository.js +119 -0
- package/dist/storage/repositories/proposals-repository.js +296 -59
- package/dist/storage/repositories/registry-cache.js +19 -0
- package/dist/storage/repositories/salience-repository.js +172 -0
- package/dist/storage/repositories/task-history-repository.js +15 -13
- package/dist/storage/repositories/workflow-runs-repository.js +52 -40
- package/dist/tasks/backends/cron.js +105 -15
- package/dist/tasks/backends/index.js +1 -1
- package/dist/tasks/backends/launchd.js +85 -38
- package/dist/tasks/backends/schtasks.js +135 -15
- package/dist/tasks/embedded.js +56 -40
- package/dist/tasks/parser.js +7 -157
- package/dist/tasks/resolve-akm-bin.js +137 -59
- package/dist/tasks/runner.js +79 -42
- package/dist/tasks/scheduler-invocation.js +220 -10
- package/dist/tasks/schema.js +24 -1
- package/dist/tasks/task-id.js +1 -3
- package/dist/tasks/validator.js +20 -6
- package/dist/workflows/authoring/authoring.js +94 -143
- package/dist/workflows/authoring/scope-key.js +1 -1
- package/dist/workflows/exec/frozen-judge.js +28 -2
- package/dist/workflows/exec/native-executor.js +77 -57
- package/dist/workflows/exec/param-secrets.js +9 -9
- package/dist/workflows/exec/run-workflow.js +133 -79
- package/dist/workflows/exec/step-work.js +219 -346
- package/dist/{migrate-storage-node.mjs → workflows/exec/unit-dispatch.js} +1 -5
- package/dist/workflows/ir/compile.js +141 -270
- package/dist/workflows/ir/freeze.js +40 -30
- package/dist/workflows/ir/params.js +135 -11
- package/dist/workflows/ir/plan-hash.js +1 -1
- package/dist/workflows/ir/schema.js +25 -26
- package/dist/workflows/parser.js +872 -307
- package/dist/workflows/program/expressions.js +20 -208
- package/dist/workflows/program/schema.js +7 -10
- package/dist/workflows/renderer.js +95 -68
- package/dist/workflows/resource-limits.js +2 -0
- package/dist/workflows/runtime/checkin.js +3 -3
- package/dist/workflows/runtime/plan-classifier.js +16 -75
- package/dist/workflows/runtime/runs.js +186 -127
- package/dist/workflows/runtime/unit-checkin.js +1 -1
- package/dist/workflows/runtime/unit-phases.js +2 -2
- package/dist/workflows/runtime/workflow-asset-loader.js +232 -83
- package/dist/workflows/schema.js +1 -11
- package/dist/workflows/validate-summary.js +30 -36
- package/dist/workflows/validator.js +21 -62
- package/docs/README.md +68 -0
- package/docs/migration/README.md +8 -0
- package/docs/migration/release-notes/0.7.0.md +11 -11
- package/docs/migration/release-notes/0.9.0.md +208 -27
- package/docs/migration/v0.7-to-v0.8.md +46 -47
- package/docs/migration/v0.8-to-v0.9.md +564 -208
- package/docs/migration/v0.9.0-troubleshooting.md +561 -0
- package/docs/reference/README.md +12 -0
- package/docs/reference/cli.md +2253 -0
- package/docs/reference/configuration.md +358 -0
- package/docs/reference/data-and-telemetry.md +105 -42
- package/docs/reference/workflows.md +647 -0
- package/package.json +22 -11
- package/schemas/akm-asset-envelope.json +93 -0
- package/schemas/akm-config.json +81 -128
- package/schemas/akm-workflow.json +74 -73
- package/dist/assets/tasks/core/backup.yml +0 -5
- package/dist/assets/tasks/graph-refresh-weekly.yml +0 -10
- package/dist/cli/config-migrate.js +0 -1806
- package/dist/cli/config-validate.js +0 -41
- package/dist/commands/backup-cli.js +0 -56
- package/dist/commands/bundle/bundle-cli.js +0 -68
- package/dist/commands/bundle/bundle.js +0 -219
- package/dist/commands/graph/graph-cli.js +0 -124
- package/dist/commands/graph/graph.js +0 -489
- package/dist/commands/improve/extract-watch.js +0 -140
- package/dist/commands/mv-cli.js +0 -1221
- package/dist/commands/sources/history.js +0 -201
- package/dist/commands/tasks/default-tasks.js +0 -186
- package/dist/core/migration-backup.js +0 -1234
- package/dist/indexer/usage/unmigrated-vaults-guard.js +0 -95
- package/dist/llm/memory-infer-impl.js +0 -138
- package/dist/migrate/legacy/config-source-migration.js +0 -223
- package/dist/migrate/legacy/content-migration.js +0 -305
- package/dist/migrate/legacy/legacy-layout.js +0 -779
- package/dist/migrate/legacy/legacy-paths.js +0 -25
- package/dist/migrate/legacy/legacy-stash-json.js +0 -72
- package/dist/migrate/legacy/proposal-fs-import.js +0 -168
- package/dist/migrate/legacy/task-target-ref-migration.js +0 -272
- package/dist/migrate/legacy/three-db-cutover.js +0 -841
- package/dist/migrate/legacy/workflow-migrations-bodies.js +0 -52
- package/dist/migrate/legacy/workflow-migrations-frozen.js +0 -21
- package/dist/migrate/legacy-ref-grammar.js +0 -214
- 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/registry/build-index.js +0 -382
- package/dist/schemas/akm-config.json +0 -4704
- package/dist/schemas/akm-task.json +0 -87
- package/dist/schemas/akm-workflow.json +0 -372
- package/dist/scripts/migrate-storage.js +0 -3816
- package/dist/workflows/authoring/workflow-program-template.yaml +0 -31
- package/dist/workflows/cli.js +0 -53
- package/dist/workflows/exec/brief.js +0 -481
- package/dist/workflows/exec/report.js +0 -1460
- package/dist/workflows/exec/watch.js +0 -116
- package/dist/workflows/program/parser.js +0 -813
- package/dist/workflows/program/project.js +0 -104
|
@@ -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
|
+
}
|
|
@@ -1,19 +1,21 @@
|
|
|
1
1
|
// This Source Code Form is subject to the terms of the Mozilla Public
|
|
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
|
-
import { directoryMatcher, extensionMatcher, parentDirHintMatcher, smartMdMatcher
|
|
4
|
+
import { directoryMatcher, extensionMatcher, parentDirHintMatcher, smartMdMatcher } from "../../indexer/walk/matchers.js";
|
|
5
5
|
/**
|
|
6
|
-
* The
|
|
6
|
+
* The four builtin matchers, in registration order. The array index IS the
|
|
7
7
|
* registration index `runMatchers` uses for tie-breaking. (The `wiki` matcher
|
|
8
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.
|
|
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.)
|
|
10
13
|
*/
|
|
11
14
|
const AKM_MATCHERS = [
|
|
12
15
|
extensionMatcher,
|
|
13
16
|
directoryMatcher,
|
|
14
17
|
parentDirHintMatcher,
|
|
15
18
|
smartMdMatcher,
|
|
16
|
-
workflowProgramMatcher,
|
|
17
19
|
];
|
|
18
20
|
/**
|
|
19
21
|
* Synchronous reproduction of `file-context.ts#runMatchers`'s arbitration
|
|
@@ -0,0 +1,214 @@
|
|
|
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 ONE core {@link ValidateContext} implementation — akm 0.9.0 chunk-2/lint
|
|
6
|
+
* follow-up ("make `validate()` genuinely load-bearing").
|
|
7
|
+
*
|
|
8
|
+
* `BundleAdapter.validate()` (`./bundle-adapter.ts`) is a REQUIRED interface
|
|
9
|
+
* method whose contract is explicit and normative (`./bundle-adapter.ts:96-100`
|
|
10
|
+
* / the format-neutral spec §12.1): the adapter MUST NOT write and MUST NOT
|
|
11
|
+
* read the live filesystem — `ctx` serves the run's on-disk SNAPSHOT WITH the
|
|
12
|
+
* caller's pending {@link FileChange}s overlaid, plus a read-only `resolveRef`
|
|
13
|
+
* for link/xref existence. That overlay is meant to be built ONCE, centrally —
|
|
14
|
+
* "one core overlay implementation, not one per adapter" (the interface's own
|
|
15
|
+
* words) — so every `validate()` caller shares identical overlay semantics
|
|
16
|
+
* rather than each hand-rolling its own (as every adapter's own test suite has
|
|
17
|
+
* done up to now, see e.g. `tests/core/adapter/okf-adapter.test.ts`'s
|
|
18
|
+
* `overlayCtx`/`diskCtx`).
|
|
19
|
+
*
|
|
20
|
+
* The two production callers wired onto this factory:
|
|
21
|
+
* - `akm lint`'s non-akm adapter dispatch (`commands/lint/index.ts`) — a
|
|
22
|
+
* plain sweep with NO pending changes, so the overlay is empty and every
|
|
23
|
+
* read falls straight through to disk.
|
|
24
|
+
* - the proposal promotion preflight (`commands/proposal/repository.ts`
|
|
25
|
+
* `preflightProposalPromotion`) — the one proposal's own `changes` are the
|
|
26
|
+
* overlay, so `validate()` sees the bundle AS IT WOULD LOOK the instant
|
|
27
|
+
* after the transaction commits, without that transaction ever touching
|
|
28
|
+
* disk.
|
|
29
|
+
*
|
|
30
|
+
* ── resolveRef ──
|
|
31
|
+
*
|
|
32
|
+
* A bare (unqualified) `conceptId` is resolved as BOTH:
|
|
33
|
+
* 1. the AKM placement-derived path (`typeNameFromConceptId` + `stashDirFor`
|
|
34
|
+
* + `assetPathForName` — the exact derivation `commands/lint/base-linter.ts
|
|
35
|
+
* #refToRelPath` already uses for the akm-native `missing-ref` check); and
|
|
36
|
+
* 2. the DIRECT component-relative spelling (`<conceptId>.md`, and the bare
|
|
37
|
+
* `<conceptId>` for an extensionless asset) — the form every non-akm
|
|
38
|
+
* adapter's OWN conceptId already IS (OKF: path − `.md`; llm-wiki: same).
|
|
39
|
+
* A `bundle//conceptId`-qualified ref has its bundle prefix stripped before
|
|
40
|
+
* resolution — mirroring `base-linter.ts#classifyConceptRef`'s existing
|
|
41
|
+
* behavior (the legacy resolver has never scoped-by-bundle-name either; it
|
|
42
|
+
* searches every configured stash root for the bare conceptId). Existence is
|
|
43
|
+
* checked against the PRIMARY root (with the pending-changes overlay applied)
|
|
44
|
+
* first, then each extra root (disk only — a pending transaction never
|
|
45
|
+
* targets more than one bundle).
|
|
46
|
+
*/
|
|
47
|
+
import fs from "node:fs";
|
|
48
|
+
import path from "node:path";
|
|
49
|
+
import { assetPathForName, stashDirFor } from "../asset/asset-placement.js";
|
|
50
|
+
import { typeNameFromConceptId } from "../asset/resolve-ref.js";
|
|
51
|
+
function toPosix(p) {
|
|
52
|
+
return p.replace(/\\/g, "/");
|
|
53
|
+
}
|
|
54
|
+
/** Build the overlay map, keyed by POSIX path relative to `root`. */
|
|
55
|
+
function buildOverlay(root, changes) {
|
|
56
|
+
const overlay = new Map();
|
|
57
|
+
for (const change of changes) {
|
|
58
|
+
const relKey = toPosix(path.isAbsolute(change.path) ? path.relative(root, change.path) : change.path);
|
|
59
|
+
if (!relKey || relKey.startsWith(".."))
|
|
60
|
+
continue; // outside root — not this context's concern
|
|
61
|
+
if (change.op === "delete") {
|
|
62
|
+
overlay.set(relKey, null);
|
|
63
|
+
continue;
|
|
64
|
+
}
|
|
65
|
+
if (change.after !== undefined)
|
|
66
|
+
overlay.set(relKey, change.after);
|
|
67
|
+
}
|
|
68
|
+
return overlay;
|
|
69
|
+
}
|
|
70
|
+
function readDisk(absPath) {
|
|
71
|
+
try {
|
|
72
|
+
return fs.readFileSync(absPath, "utf8");
|
|
73
|
+
}
|
|
74
|
+
catch {
|
|
75
|
+
return null;
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
function existsOnDiskOrOverlay(relPath, root, overlay) {
|
|
79
|
+
const key = toPosix(relPath);
|
|
80
|
+
// Overlay entries are pending file contents by construction, so a present
|
|
81
|
+
// non-null entry is always a file.
|
|
82
|
+
if (overlay?.has(key))
|
|
83
|
+
return overlay.get(key) !== null;
|
|
84
|
+
try {
|
|
85
|
+
// `isFile()`, not `existsSync()`: a ref naming a DIRECTORY (e.g. a
|
|
86
|
+
// `pages/foo/` dir alongside no `pages/foo.md`) must not count as a
|
|
87
|
+
// resolved target — that would silently suppress a real
|
|
88
|
+
// `missing-ref`/`broken-xref` diagnostic.
|
|
89
|
+
return fs.statSync(path.join(root, relPath)).isFile();
|
|
90
|
+
}
|
|
91
|
+
catch {
|
|
92
|
+
return false;
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
/**
|
|
96
|
+
* Refs come from bundle CONTENT (link targets, xrefs, `sources:` entries), so
|
|
97
|
+
* they are untrusted input. A ref must stay inside the bundle root: reject
|
|
98
|
+
* absolute paths and any `..` segment before joining, so content can never
|
|
99
|
+
* probe for existence outside its own bundle (nor report a resolved `path`
|
|
100
|
+
* pointing there). Mirrors the `isWithin` containment rule the write path
|
|
101
|
+
* already enforces in `core/write-source.ts`.
|
|
102
|
+
*/
|
|
103
|
+
function refEscapesBundle(conceptId) {
|
|
104
|
+
if (path.isAbsolute(conceptId) || conceptId.startsWith("/"))
|
|
105
|
+
return true;
|
|
106
|
+
return toPosix(conceptId)
|
|
107
|
+
.split("/")
|
|
108
|
+
.some((segment) => segment === "..");
|
|
109
|
+
}
|
|
110
|
+
/**
|
|
111
|
+
* Every on-disk relative path a bare `conceptId` might resolve to: the AKM
|
|
112
|
+
* placement-derived path (when the leading segment names a known placement
|
|
113
|
+
* stash-subdir) and the direct component-relative spellings every non-akm
|
|
114
|
+
* adapter's own conceptId already uses. Order doesn't matter — the caller
|
|
115
|
+
* checks all of them.
|
|
116
|
+
*/
|
|
117
|
+
function candidateRelPaths(conceptId) {
|
|
118
|
+
const candidates = [];
|
|
119
|
+
const parts = typeNameFromConceptId(conceptId);
|
|
120
|
+
if (parts !== undefined) {
|
|
121
|
+
const typeDir = stashDirFor(parts.type);
|
|
122
|
+
if (typeDir !== undefined)
|
|
123
|
+
candidates.push(assetPathForName(parts.type, typeDir, parts.name));
|
|
124
|
+
}
|
|
125
|
+
candidates.push(`${conceptId}.md`);
|
|
126
|
+
candidates.push(conceptId);
|
|
127
|
+
return candidates;
|
|
128
|
+
}
|
|
129
|
+
/** Strip an optional `#fragment` (export selector) — never part of the on-disk identity. */
|
|
130
|
+
function stripFragment(ref) {
|
|
131
|
+
const hashIdx = ref.indexOf("#");
|
|
132
|
+
return hashIdx >= 0 ? ref.slice(0, hashIdx) : ref;
|
|
133
|
+
}
|
|
134
|
+
/**
|
|
135
|
+
* Strip an optional `bundle//` qualifier. Mirrors `base-linter.ts
|
|
136
|
+
* #classifyConceptRef`'s existing behavior: the legacy missing-ref checker has
|
|
137
|
+
* never resolved a qualifier against a NAMED bundle either — it searches every
|
|
138
|
+
* configured stash root for the bare conceptId. Keeping that same leniency
|
|
139
|
+
* here means this resolver's answers agree with today's `akm lint` for every
|
|
140
|
+
* ref shape that already worked.
|
|
141
|
+
*/
|
|
142
|
+
function stripBundlePrefix(ref) {
|
|
143
|
+
const boundary = ref.indexOf("//");
|
|
144
|
+
return boundary >= 0 ? ref.slice(boundary + 2) : ref;
|
|
145
|
+
}
|
|
146
|
+
/**
|
|
147
|
+
* Build the ONE core {@link ValidateContext}: reads/lookups served from the
|
|
148
|
+
* on-disk snapshot rooted at `options.root` (+ `options.extraRoots` for
|
|
149
|
+
* cross-bundle ref existence) WITH `options.changes` overlaid on `options.root`
|
|
150
|
+
* — never a write, never a live-FS read beyond that snapshot.
|
|
151
|
+
*/
|
|
152
|
+
export function createValidateContext(options) {
|
|
153
|
+
const root = options.root;
|
|
154
|
+
const extraRoots = options.extraRoots ?? [];
|
|
155
|
+
const overlay = buildOverlay(root, options.changes ?? []);
|
|
156
|
+
async function readFile(p) {
|
|
157
|
+
// An absolute candidate (e.g. a `stale-path` scan hit, which is always an
|
|
158
|
+
// absolute host path) bypasses the overlay — it can never name a pending
|
|
159
|
+
// change's stash-relative path, and reads straight from disk (the base
|
|
160
|
+
// checks' existing "does this literal absolute path exist" question).
|
|
161
|
+
if (path.isAbsolute(p))
|
|
162
|
+
return readDisk(p);
|
|
163
|
+
const key = toPosix(p);
|
|
164
|
+
if (overlay.has(key))
|
|
165
|
+
return overlay.get(key) ?? null;
|
|
166
|
+
return readDisk(path.join(root, p));
|
|
167
|
+
}
|
|
168
|
+
async function list(dir) {
|
|
169
|
+
const abs = path.isAbsolute(dir) ? dir : path.join(root, dir);
|
|
170
|
+
const relDir = toPosix(path.isAbsolute(dir) ? path.relative(root, dir) : dir);
|
|
171
|
+
const names = new Set();
|
|
172
|
+
try {
|
|
173
|
+
for (const entry of fs.readdirSync(abs))
|
|
174
|
+
names.add(entry);
|
|
175
|
+
}
|
|
176
|
+
catch {
|
|
177
|
+
// directory may not exist on disk yet — the overlay can still populate it
|
|
178
|
+
}
|
|
179
|
+
const prefix = relDir === "." || relDir === "" ? "" : `${relDir}/`;
|
|
180
|
+
for (const [key, value] of overlay) {
|
|
181
|
+
if (prefix.length > 0 && !key.startsWith(prefix))
|
|
182
|
+
continue;
|
|
183
|
+
const rest = prefix.length > 0 ? key.slice(prefix.length) : key;
|
|
184
|
+
if (rest.length === 0 || rest.includes("/"))
|
|
185
|
+
continue; // only direct children
|
|
186
|
+
if (value === null)
|
|
187
|
+
names.delete(rest);
|
|
188
|
+
else
|
|
189
|
+
names.add(rest);
|
|
190
|
+
}
|
|
191
|
+
return [...names];
|
|
192
|
+
}
|
|
193
|
+
async function resolveRef(ref) {
|
|
194
|
+
const conceptId = stripBundlePrefix(stripFragment(ref)).trim();
|
|
195
|
+
if (!conceptId)
|
|
196
|
+
return { exists: false };
|
|
197
|
+
if (refEscapesBundle(conceptId))
|
|
198
|
+
return { exists: false };
|
|
199
|
+
const candidates = candidateRelPaths(conceptId);
|
|
200
|
+
const roots = [
|
|
201
|
+
{ root, usesOverlay: true },
|
|
202
|
+
...extraRoots.map((r) => ({ root: r, usesOverlay: false })),
|
|
203
|
+
];
|
|
204
|
+
for (const { root: candidateRoot, usesOverlay } of roots) {
|
|
205
|
+
for (const relPath of candidates) {
|
|
206
|
+
if (existsOnDiskOrOverlay(relPath, candidateRoot, usesOverlay ? overlay : null)) {
|
|
207
|
+
return { exists: true, path: path.join(candidateRoot, relPath) };
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
return { exists: false };
|
|
212
|
+
}
|
|
213
|
+
return { readFile, list, resolveRef };
|
|
214
|
+
}
|
|
@@ -0,0 +1,63 @@
|
|
|
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 { localDateStamp } from "../common.js";
|
|
6
|
+
import { UsageError } from "../errors.js";
|
|
7
|
+
import { serializeFrontmatter } from "./asset-serialize.js";
|
|
8
|
+
import { parseFrontmatterBlock, spliceFrontmatterLine } from "./frontmatter.js";
|
|
9
|
+
/**
|
|
10
|
+
* Ensure an AKM-authored Markdown concept is also a conformant OKF concept.
|
|
11
|
+
*
|
|
12
|
+
* Stamps BOTH `type` and `updated`, because both are required of a conformant
|
|
13
|
+
* document and this is the one chokepoint every `.md` write passes through
|
|
14
|
+
* (`core/write-source.ts`). Without the `updated` stamp, every asset akm
|
|
15
|
+
* created for you — `akm remember`, `akm import`, accepted proposals,
|
|
16
|
+
* authored workflows — was immediately flagged `missing-updated` by akm's own
|
|
17
|
+
* `akm lint`, so the tool disagreed with itself about its own output.
|
|
18
|
+
*
|
|
19
|
+
* An existing `updated` is left alone: this fills a gap, it does not
|
|
20
|
+
* re-stamp on every write (which would churn timestamps and manufacture
|
|
21
|
+
* needless diffs in git-backed bundles).
|
|
22
|
+
*
|
|
23
|
+
* Source preservation: when the type already matches and the ONLY change is
|
|
24
|
+
* adding `updated`, the line is spliced into the original block textually —
|
|
25
|
+
* round-tripping through the YAML serializer would drop user-authored
|
|
26
|
+
* comments and normalize formatting just to contribute one field. Only a
|
|
27
|
+
* document whose `type` must actually be corrected takes the re-serialize
|
|
28
|
+
* path (as it always has).
|
|
29
|
+
*/
|
|
30
|
+
export function ensureAkmMarkdownType(content, type, now = new Date()) {
|
|
31
|
+
const block = parseFrontmatterBlock(content);
|
|
32
|
+
if (!block) {
|
|
33
|
+
return `---\n${serializeFrontmatter({ type, updated: localDateStamp(now) })}\n---\n${content}`;
|
|
34
|
+
}
|
|
35
|
+
let parsed;
|
|
36
|
+
try {
|
|
37
|
+
parsed = block.frontmatter.trim() ? parseYaml(block.frontmatter) : {};
|
|
38
|
+
}
|
|
39
|
+
catch {
|
|
40
|
+
throw new UsageError("AKM Markdown has malformed YAML frontmatter.", "INVALID_FLAG_VALUE");
|
|
41
|
+
}
|
|
42
|
+
if (parsed === null)
|
|
43
|
+
parsed = {};
|
|
44
|
+
if (typeof parsed !== "object" || Array.isArray(parsed)) {
|
|
45
|
+
throw new UsageError("AKM Markdown frontmatter must be a YAML mapping.", "INVALID_FLAG_VALUE");
|
|
46
|
+
}
|
|
47
|
+
const data = parsed;
|
|
48
|
+
const needsUpdated = !("updated" in data);
|
|
49
|
+
if (data.type === type) {
|
|
50
|
+
if (!needsUpdated)
|
|
51
|
+
return content;
|
|
52
|
+
const spliced = spliceFrontmatterLine(content, `updated: ${localDateStamp(now)}`);
|
|
53
|
+
if (spliced !== null)
|
|
54
|
+
return spliced;
|
|
55
|
+
// Unreachable in practice (parseFrontmatterBlock succeeded above), but a
|
|
56
|
+
// re-serialized document beats a non-conformant one.
|
|
57
|
+
}
|
|
58
|
+
const { type: _priorType, ...rest } = data;
|
|
59
|
+
const next = { type, ...rest };
|
|
60
|
+
if (needsUpdated)
|
|
61
|
+
next.updated = localDateStamp(now);
|
|
62
|
+
return `---\n${serializeFrontmatter(next)}\n---\n${block.content}`;
|
|
63
|
+
}
|
|
@@ -46,7 +46,7 @@ const workflowSpec = {
|
|
|
46
46
|
if (lower.endsWith(ext))
|
|
47
47
|
return path.join(typeRoot, name);
|
|
48
48
|
}
|
|
49
|
-
// Probe in priority order
|
|
49
|
+
// Probe in canonical extension priority order and fall back
|
|
50
50
|
// to the markdown path so error messages keep naming the canonical file.
|
|
51
51
|
for (const ext of WORKFLOW_EXTENSIONS) {
|
|
52
52
|
const candidate = path.join(typeRoot, `${name}${ext}`);
|
|
@@ -60,7 +60,7 @@ const markdownSpec = {
|
|
|
60
60
|
isRelevantFile: (fileName) => path.extname(fileName).toLowerCase() === ".md",
|
|
61
61
|
toCanonicalName: (typeRoot, filePath) => {
|
|
62
62
|
const rel = toPosix(path.relative(typeRoot, filePath));
|
|
63
|
-
// Strip .md extension from canonical names
|
|
63
|
+
// Strip .md extension from canonical names.
|
|
64
64
|
return rel.endsWith(".md") ? rel.slice(0, -3) : rel;
|
|
65
65
|
},
|
|
66
66
|
toAssetPath: (typeRoot, name) => {
|
|
@@ -74,7 +74,7 @@ const scriptSpec = {
|
|
|
74
74
|
toCanonicalName: (typeRoot, filePath) => toPosix(path.relative(typeRoot, filePath)),
|
|
75
75
|
toAssetPath: (typeRoot, name) => path.join(typeRoot, name),
|
|
76
76
|
};
|
|
77
|
-
const
|
|
77
|
+
const BUILTIN_PLACEMENT_SPECS = {
|
|
78
78
|
skill: {
|
|
79
79
|
stashDir: "skills",
|
|
80
80
|
isRelevantFile: (fileName) => fileName === "SKILL.md",
|
|
@@ -89,6 +89,18 @@ const PLACEMENT_SPECS = {
|
|
|
89
89
|
command: { stashDir: "commands", ...markdownSpec },
|
|
90
90
|
agent: { stashDir: "agents", ...markdownSpec },
|
|
91
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 },
|
|
92
104
|
workflow: { stashDir: "workflows", ...workflowSpec },
|
|
93
105
|
script: { stashDir: "scripts", ...scriptSpec },
|
|
94
106
|
memory: { stashDir: "memories", ...markdownSpec },
|
|
@@ -154,6 +166,9 @@ const PLACEMENT_SPECS = {
|
|
|
154
166
|
// project. A plain markdown spec; see docs/architecture/specs/fact-asset-type.md.
|
|
155
167
|
fact: { stashDir: "facts", ...markdownSpec },
|
|
156
168
|
};
|
|
169
|
+
const _placementKeysSubsetOfKnownTypes = true;
|
|
170
|
+
void _placementKeysSubsetOfKnownTypes;
|
|
171
|
+
const PLACEMENT_SPECS = { ...BUILTIN_PLACEMENT_SPECS };
|
|
157
172
|
/** Live placement spec for a type, or `undefined` for an unknown type. */
|
|
158
173
|
export function placementSpecFor(type) {
|
|
159
174
|
return PLACEMENT_SPECS[type];
|
|
@@ -174,9 +189,8 @@ export function stashDirFor(type) {
|
|
|
174
189
|
* Reverse of {@link stashDirFor}: the placement type owning a stash subdir, or
|
|
175
190
|
* `undefined` when no registered type places into it. The type→subdir map is a
|
|
176
191
|
* bijection over the built-in types (each type has a distinct subdir), so this
|
|
177
|
-
* is the well-defined inverse
|
|
178
|
-
*
|
|
179
|
-
* `type:name` predicate so NULL-`item_ref` rows stay findable by new refs.
|
|
192
|
+
* is the well-defined inverse used to project a path-based conceptId onto the
|
|
193
|
+
* asset-type metadata required by native adapters.
|
|
180
194
|
*/
|
|
181
195
|
export function typeForStashDir(stashDir) {
|
|
182
196
|
for (const [type, spec] of Object.entries(PLACEMENT_SPECS)) {
|
|
@@ -11,16 +11,19 @@ function validateName(name) {
|
|
|
11
11
|
throw new UsageError("Null byte in asset name.", "MISSING_REQUIRED_ARGUMENT");
|
|
12
12
|
if (/^[A-Za-z]:/.test(name))
|
|
13
13
|
throw new UsageError("Windows drive path in asset name.", "MISSING_REQUIRED_ARGUMENT");
|
|
14
|
-
const
|
|
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);
|
|
15
22
|
if (path.posix.isAbsolute(normalized))
|
|
16
23
|
throw new UsageError("Absolute path in asset name.", "MISSING_REQUIRED_ARGUMENT");
|
|
17
24
|
if (normalized === ".." || normalized.startsWith("../")) {
|
|
18
25
|
throw new UsageError("Path traversal in asset name.", "MISSING_REQUIRED_ARGUMENT");
|
|
19
26
|
}
|
|
20
|
-
const segments = normalized.split("/");
|
|
21
|
-
if (segments.some((seg) => seg === "." || seg === "..")) {
|
|
22
|
-
throw new UsageError("Asset name cannot contain relative path segments.", "MISSING_REQUIRED_ARGUMENT");
|
|
23
|
-
}
|
|
24
27
|
}
|
|
25
28
|
function normalizeName(name) {
|
|
26
29
|
return path.posix.normalize(name.replace(/\\/g, "/"));
|
|
@@ -34,9 +37,8 @@ function normalizeName(name) {
|
|
|
34
37
|
const BUNDLE_SLUG_RE = /^[^\s:.#/]+$/;
|
|
35
38
|
/**
|
|
36
39
|
* True when `s` is a legal bundle slug (spec §11.1 / D-R5 charset: non-empty,
|
|
37
|
-
* no `:`/`.`/`#`/`/` or whitespace). Exported
|
|
38
|
-
*
|
|
39
|
-
* is a legal bundle slug — the clean legacy-vs-new-grammar discriminator.
|
|
40
|
+
* no `:`/`.`/`#`/`/` or whitespace). Exported for input boundaries that need
|
|
41
|
+
* to distinguish bundle-qualified refs from source locators.
|
|
40
42
|
*/
|
|
41
43
|
export function isBundleSlug(s) {
|
|
42
44
|
return BUNDLE_SLUG_RE.test(s);
|
|
@@ -53,7 +55,7 @@ export function isBundleSlug(s) {
|
|
|
53
55
|
* boundary punctuation (brackets/parens/quotes/backtick/comma/angle) so a
|
|
54
56
|
* leading boundary char (e.g. the `[` of a markdown link) is not absorbed into
|
|
55
57
|
* the slug. The concept segment reuses the same terminator charset as the
|
|
56
|
-
*
|
|
58
|
+
* body-ref scan (whitespace/quotes/brackets/comma/nl), and admits `/`,
|
|
57
59
|
* `.`, and a trailing `#fragment`.
|
|
58
60
|
*/
|
|
59
61
|
export const BUNDLE_REF_RE = /(?:^|[\s`"'(,[])([^\s:.#/`"'()[\],<>]+\/\/[^\s"'`)\]>,\n]+)/gm;
|
|
@@ -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 { isScalar, parseDocument } from "yaml";
|
|
5
|
+
/**
|
|
6
|
+
* Report a colon-bearing description only when YAML parsed it as a plain
|
|
7
|
+
* scalar. Quoted and block scalars may span physical lines and remain valid.
|
|
8
|
+
*/
|
|
9
|
+
export function checkUnquotedDescriptionColon(frontmatterText) {
|
|
10
|
+
if (!frontmatterText)
|
|
11
|
+
return null;
|
|
12
|
+
const document = parseDocument(frontmatterText);
|
|
13
|
+
const description = document.get("description", true);
|
|
14
|
+
if (document.errors.length === 0 &&
|
|
15
|
+
isScalar(description) &&
|
|
16
|
+
description.type === "PLAIN" &&
|
|
17
|
+
typeof description.value === "string" &&
|
|
18
|
+
description.value.includes(":")) {
|
|
19
|
+
return `description value contains unquoted colon: ${description.value}`;
|
|
20
|
+
}
|
|
21
|
+
// Preserve the existing finding for malformed plain scalars that YAML cannot
|
|
22
|
+
// construct, while letting valid multiline quoted/block scalars pass above.
|
|
23
|
+
if (document.errors.length > 0) {
|
|
24
|
+
const line = frontmatterText.split(/\r?\n/).find((candidate) => candidate.startsWith("description:"));
|
|
25
|
+
const value = line?.slice("description:".length).trim();
|
|
26
|
+
if (value?.includes(":"))
|
|
27
|
+
return `description value contains unquoted colon: ${value}`;
|
|
28
|
+
}
|
|
29
|
+
return null;
|
|
30
|
+
}
|
|
@@ -51,15 +51,11 @@ export function parseFrontmatter(raw) {
|
|
|
51
51
|
};
|
|
52
52
|
}
|
|
53
53
|
/**
|
|
54
|
-
* Normalize YAML
|
|
54
|
+
* Normalize YAML dates to match expected AKM frontmatter types.
|
|
55
55
|
*
|
|
56
|
-
*
|
|
57
|
-
* 1. `Date` → YYYY-MM-DD string: the yaml "core" schema parses bare date
|
|
56
|
+
* `Date` → YYYY-MM-DD string: the yaml "core" schema parses bare date
|
|
58
57
|
* scalars like `2026-06-18` as JS Date instances. AKM frontmatter treats
|
|
59
58
|
* `updated:` and similar fields as plain strings.
|
|
60
|
-
* 2. `null` → `""`: the yaml library parses empty-value keys (`key:` with no
|
|
61
|
-
* value) as `null`, but AKM callers historically received `""` from the
|
|
62
|
-
* hand-rolled parser. Convert to preserve backward compatibility.
|
|
63
59
|
*/
|
|
64
60
|
function normalizeYamlValues(value) {
|
|
65
61
|
if (value instanceof Date) {
|
|
@@ -68,11 +64,9 @@ function normalizeYamlValues(value) {
|
|
|
68
64
|
const d = String(value.getUTCDate()).padStart(2, "0");
|
|
69
65
|
return `${y}-${m}-${d}`;
|
|
70
66
|
}
|
|
71
|
-
if (value === null)
|
|
72
|
-
return "";
|
|
73
67
|
if (Array.isArray(value))
|
|
74
68
|
return value.map(normalizeYamlValues);
|
|
75
|
-
if (typeof value === "object") {
|
|
69
|
+
if (value !== null && typeof value === "object") {
|
|
76
70
|
return Object.fromEntries(Object.entries(value).map(([k, v]) => [k, normalizeYamlValues(v)]));
|
|
77
71
|
}
|
|
78
72
|
return value;
|
|
@@ -130,6 +124,18 @@ function parseFrontmatterLenient(frontmatter) {
|
|
|
130
124
|
export function mutateFrontmatter(filePath, mutator) {
|
|
131
125
|
const raw = fs.readFileSync(filePath, "utf8");
|
|
132
126
|
const parsed = parseFrontmatter(raw);
|
|
127
|
+
if (parsed.frontmatter?.trim()) {
|
|
128
|
+
let strict;
|
|
129
|
+
try {
|
|
130
|
+
strict = yamlParse(parsed.frontmatter);
|
|
131
|
+
}
|
|
132
|
+
catch {
|
|
133
|
+
throw new Error(`Cannot mutate malformed YAML frontmatter in ${filePath}.`);
|
|
134
|
+
}
|
|
135
|
+
if (strict === null || typeof strict !== "object" || Array.isArray(strict)) {
|
|
136
|
+
throw new Error(`Cannot mutate non-mapping YAML frontmatter in ${filePath}.`);
|
|
137
|
+
}
|
|
138
|
+
}
|
|
133
139
|
const nextFrontmatter = mutator(parsed);
|
|
134
140
|
if (nextFrontmatter === null)
|
|
135
141
|
return false;
|
|
@@ -170,6 +176,28 @@ function countLines(text) {
|
|
|
170
176
|
return 0;
|
|
171
177
|
return text.split(/\r?\n/).length - 1;
|
|
172
178
|
}
|
|
179
|
+
/**
|
|
180
|
+
* Insert one `key: value` line just before the closing `---` of an existing
|
|
181
|
+
* frontmatter block, leaving every other byte — YAML comments, quoting, key
|
|
182
|
+
* order, line endings — untouched. Returns null when `raw` has no well-formed
|
|
183
|
+
* block, so callers can fall back to a parse-and-serialize path.
|
|
184
|
+
*
|
|
185
|
+
* This is the source-preserving way to ADD a field to user-authored
|
|
186
|
+
* frontmatter: round-tripping the mapping through the YAML serializer drops
|
|
187
|
+
* comments and normalizes formatting, which is unacceptable for a write that
|
|
188
|
+
* only needs to contribute one line. Shared by `ensureAkmMarkdownType`
|
|
189
|
+
* (stamping `updated:` on write) and lint's `--fix` for `missing-updated`.
|
|
190
|
+
*/
|
|
191
|
+
export function spliceFrontmatterLine(raw, line) {
|
|
192
|
+
const lines = raw.split(/\r?\n/);
|
|
193
|
+
if (lines[0]?.trim() !== "---")
|
|
194
|
+
return null;
|
|
195
|
+
const closeIdx = lines.findIndex((l, i) => i > 0 && l.trim() === "---");
|
|
196
|
+
if (closeIdx === -1)
|
|
197
|
+
return null;
|
|
198
|
+
lines.splice(closeIdx, 0, line);
|
|
199
|
+
return lines.join("\n");
|
|
200
|
+
}
|
|
173
201
|
/**
|
|
174
202
|
* Parse a YAML scalar value (string, boolean, or number).
|
|
175
203
|
*
|
|
@@ -2,6 +2,34 @@
|
|
|
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 { parseFrontmatter } from "./frontmatter.js";
|
|
5
|
+
/** Stable GitHub-style selector for a Markdown heading. */
|
|
6
|
+
export function markdownHeadingSlug(heading) {
|
|
7
|
+
return heading
|
|
8
|
+
.trim()
|
|
9
|
+
.toLowerCase()
|
|
10
|
+
.replace(/<[^>]*>/g, "")
|
|
11
|
+
.replace(/[^\p{L}\p{N}\s_-]+/gu, "-")
|
|
12
|
+
.replace(/[\s_]+/g, "-")
|
|
13
|
+
.replace(/-+/g, "-")
|
|
14
|
+
.replace(/^-|-$/g, "");
|
|
15
|
+
}
|
|
16
|
+
export function markdownFragmentSlugs(content) {
|
|
17
|
+
return uniqueHeadingSlugs(parseMarkdownToc(content).headings).filter(Boolean);
|
|
18
|
+
}
|
|
19
|
+
function uniqueHeadingSlugs(headings) {
|
|
20
|
+
const used = new Set();
|
|
21
|
+
return headings.map((heading) => {
|
|
22
|
+
const base = markdownHeadingSlug(heading.text);
|
|
23
|
+
if (!base)
|
|
24
|
+
return "";
|
|
25
|
+
let slug = base;
|
|
26
|
+
let suffix = 0;
|
|
27
|
+
while (used.has(slug))
|
|
28
|
+
slug = `${base}-${++suffix}`;
|
|
29
|
+
used.add(slug);
|
|
30
|
+
return slug;
|
|
31
|
+
});
|
|
32
|
+
}
|
|
5
33
|
// ── Parsing ─────────────────────────────────────────────────────────────────
|
|
6
34
|
export function parseMarkdownToc(content) {
|
|
7
35
|
const lines = content.split(/\r?\n/);
|
|
@@ -31,61 +59,22 @@ export function parseMarkdownToc(content) {
|
|
|
31
59
|
// ── Extraction ──────────────────────────────────────────────────────────────
|
|
32
60
|
export function extractSection(content, heading) {
|
|
33
61
|
const lines = content.split(/\r?\n/);
|
|
34
|
-
const
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
continue;
|
|
41
|
-
const text = match[2].replace(/\s+#+\s*$/, "").trim();
|
|
42
|
-
if (text.toLowerCase() === target && startIdx === -1) {
|
|
43
|
-
startIdx = i;
|
|
44
|
-
startLevel = match[1].length;
|
|
45
|
-
}
|
|
46
|
-
else if (startIdx !== -1 && match[1].length <= startLevel) {
|
|
47
|
-
return {
|
|
48
|
-
content: lines.slice(startIdx, i).join("\n"),
|
|
49
|
-
startLine: startIdx + 1,
|
|
50
|
-
endLine: i,
|
|
51
|
-
};
|
|
52
|
-
}
|
|
53
|
-
}
|
|
54
|
-
if (startIdx === -1)
|
|
62
|
+
const headings = parseMarkdownToc(content).headings;
|
|
63
|
+
const fragment = heading.trim();
|
|
64
|
+
const slugIndex = uniqueHeadingSlugs(headings).indexOf(fragment);
|
|
65
|
+
const exact = slugIndex < 0 ? headings.find((candidate) => candidate.text.toLowerCase() === fragment.toLowerCase()) : undefined;
|
|
66
|
+
const selected = slugIndex >= 0 ? headings[slugIndex] : exact;
|
|
67
|
+
if (!selected)
|
|
55
68
|
return null;
|
|
69
|
+
const next = headings.find((candidate) => candidate.line > selected.line && candidate.level <= selected.level);
|
|
70
|
+
const startIdx = selected.line - 1;
|
|
71
|
+
const endIdx = next ? next.line - 1 : lines.length;
|
|
56
72
|
return {
|
|
57
|
-
content: lines.slice(startIdx).join("\n"),
|
|
58
|
-
startLine:
|
|
59
|
-
endLine:
|
|
73
|
+
content: lines.slice(startIdx, endIdx).join("\n"),
|
|
74
|
+
startLine: selected.line,
|
|
75
|
+
endLine: endIdx,
|
|
60
76
|
};
|
|
61
77
|
}
|
|
62
|
-
export function extractLineRange(content, start, end) {
|
|
63
|
-
const lines = content.split(/\r?\n/);
|
|
64
|
-
if (end < start)
|
|
65
|
-
return "";
|
|
66
|
-
const s = Math.max(1, Math.min(start, lines.length));
|
|
67
|
-
const e = Math.min(end, lines.length);
|
|
68
|
-
return lines.slice(s - 1, e).join("\n");
|
|
69
|
-
}
|
|
70
|
-
export function extractFrontmatterOnly(content) {
|
|
71
|
-
const parsed = parseFrontmatter(content);
|
|
72
|
-
return parsed.frontmatter;
|
|
73
|
-
}
|
|
74
|
-
// ── Formatting ──────────────────────────────────────────────────────────────
|
|
75
|
-
export function formatToc(toc) {
|
|
76
|
-
if (toc.headings.length === 0) {
|
|
77
|
-
return `(no headings found — ${toc.totalLines} lines total)`;
|
|
78
|
-
}
|
|
79
|
-
const lineWidth = String(toc.totalLines).length;
|
|
80
|
-
const parts = toc.headings.map((h) => {
|
|
81
|
-
const lineNum = `L${String(h.line).padStart(lineWidth)}`;
|
|
82
|
-
const indent = " ".repeat(h.level - 1);
|
|
83
|
-
const prefix = "#".repeat(h.level);
|
|
84
|
-
return `${lineNum} ${indent}${prefix} ${h.text}`;
|
|
85
|
-
});
|
|
86
|
-
parts.push(`\n${toc.totalLines} lines total`);
|
|
87
|
-
return parts.join("\n");
|
|
88
|
-
}
|
|
89
78
|
// ── Fence stripping ──────────────────────────────────────────────────────────
|
|
90
79
|
/**
|
|
91
80
|
* Best-effort fence stripping. Strips `<think>` reasoning blocks emitted by
|