akm-cli 0.9.0-rc.9 → 0.9.1-beta.1
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 +1770 -44
- package/README.md +38 -47
- package/SECURITY.md +14 -1
- package/STABILITY.md +499 -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 +74 -89
- 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 +148 -88
- 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/llm-usage.js +2 -1
- 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 +4 -122
- package/dist/commands/health/task-runs.js +3 -67
- package/dist/commands/health/types-improve.js +7 -0
- package/dist/commands/health.js +160 -35
- 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 +11 -6
- package/dist/commands/improve/distill.js +58 -69
- package/dist/commands/improve/eligibility.js +101 -55
- 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 +254 -101
- package/dist/commands/improve/locks.js +7 -0
- 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 +14 -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/run-context.js +5 -0
- package/dist/commands/improve/salience.js +11 -122
- package/dist/commands/improve/session-asset.js +4 -0
- package/dist/commands/improve/source-identity.js +10 -38
- package/dist/commands/lint/base-linter.js +51 -131
- package/dist/commands/lint/env-key-rules.js +31 -47
- package/dist/commands/lint/index.js +444 -88
- package/dist/commands/lint/types.js +22 -1
- 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 +718 -279
- 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 +30 -7
- package/dist/commands/sources/init.js +12 -12
- package/dist/commands/sources/installed-stashes.js +387 -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 +284 -423
- package/dist/commands/workflow-cli.js +181 -450
- package/dist/core/abort-deadline.js +28 -0
- package/dist/core/adapter/adapters/agent-skills-adapter.js +67 -5
- package/dist/core/adapter/adapters/akm-adapter.js +60 -38
- package/dist/core/adapter/adapters/akm-lint.js +109 -42
- package/dist/core/adapter/adapters/akm-metadata.js +15 -44
- package/dist/core/adapter/adapters/akm-task-adapter.js +54 -31
- 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 +13 -6
- 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 +29 -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 +218 -44
- package/dist/core/concurrent.js +32 -0
- 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 +14 -3
- package/dist/core/env-secret-ref.js +76 -46
- package/dist/core/errors.js +43 -12
- package/dist/core/events.js +76 -152
- package/dist/core/file-change.js +6 -5
- package/dist/core/file-lock.js +7 -1
- package/dist/core/fs-txn.js +83 -7
- package/dist/core/git-message.js +2 -2
- package/dist/core/improve-result.js +6 -100
- package/dist/core/json-schema.js +327 -9
- package/dist/core/lesson-lint.js +1 -17
- package/dist/core/logs-db.js +2 -1
- package/dist/core/loopback.js +89 -0
- package/dist/core/migration-operation.js +33 -2
- package/dist/core/mutation-target.js +78 -0
- package/dist/core/parse.js +4 -1
- package/dist/core/path-access.js +107 -0
- package/dist/core/paths.js +17 -20
- package/dist/core/recognition-util.js +12 -14
- package/dist/core/redaction.js +98 -17
- package/dist/core/spawn-env.js +234 -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-scope.js +134 -0
- package/dist/core/state-db.js +5 -14
- package/dist/core/structured.js +1 -1
- package/dist/core/subprocess.js +182 -36
- 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-provenance.js +85 -0
- package/dist/core/write-source.js +778 -95
- package/dist/indexer/bundle-identity-guard.js +3 -2
- package/dist/indexer/db/graph-db.js +17 -30
- package/dist/indexer/ensure-index.js +11 -3
- 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 +33 -24
- package/dist/indexer/indexer.js +551 -253
- package/dist/indexer/installations.js +14 -96
- package/dist/indexer/passes/dir-staleness.js +16 -9
- package/dist/indexer/passes/memory-inference.js +15 -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 +107 -69
- 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/engine-resolution.js +24 -11
- package/dist/integrations/agent/model-aliases.js +1 -1
- package/dist/integrations/agent/profiles.js +23 -1
- 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 +20 -93
- 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 +132 -8
- 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 +56 -0
- package/dist/{migrate-storage-node.mjs → output/text/lint.js} +2 -5
- 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/{save.js → sync.js} +2 -2
- 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 +60972 -0
- package/dist/scripts/akm-migrate.js +60310 -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 +414 -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 +686 -282
- package/dist/sources/snapshot-fetchers/x.js +910 -0
- package/dist/storage/database.js +13 -1
- package/dist/storage/engines/sqlite-migrations.js +23 -111
- package/dist/storage/managed-db.js +21 -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 +44 -8
- package/dist/storage/repositories/index-entries-repository.js +157 -241
- 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 +118 -53
- 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/log-redaction.js +156 -0
- package/dist/tasks/parser.js +87 -160
- package/dist/tasks/resolve-akm-bin.js +137 -59
- package/dist/tasks/runner.js +209 -44
- package/dist/tasks/scheduler-invocation.js +220 -10
- package/dist/tasks/schema.js +89 -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/concurrency-policy.js +95 -1
- package/dist/workflows/exec/dispatch-redaction.js +114 -0
- package/dist/workflows/exec/exec-unit.js +542 -0
- package/dist/workflows/exec/frozen-judge.js +130 -32
- package/dist/workflows/exec/native-executor.js +513 -274
- package/dist/workflows/exec/param-secrets.js +12 -11
- package/dist/workflows/exec/run-workflow.js +496 -237
- package/dist/workflows/exec/step-work.js +616 -412
- package/dist/workflows/exec/unit-dispatch.js +34 -0
- package/dist/workflows/exec/unit-writer.js +53 -13
- package/dist/workflows/exec/worktree.js +454 -41
- package/dist/workflows/ir/compile.js +166 -271
- package/dist/workflows/ir/freeze.js +119 -42
- package/dist/workflows/ir/params.js +135 -11
- package/dist/workflows/ir/plan-hash.js +1 -1
- package/dist/workflows/ir/schema.js +129 -45
- package/dist/workflows/parser.js +1070 -307
- package/dist/workflows/program/expressions.js +20 -208
- package/dist/workflows/program/schema.js +31 -10
- package/dist/workflows/renderer.js +123 -68
- package/dist/workflows/resource-limits.js +184 -0
- package/dist/workflows/runtime/checkin.js +3 -3
- package/dist/workflows/runtime/plan-classifier.js +16 -75
- package/dist/workflows/runtime/runs.js +331 -132
- 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 +47 -38
- package/dist/workflows/validator.js +21 -62
- package/docs/README.md +110 -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 +209 -27
- package/docs/migration/v0.7-to-v0.8.md +48 -48
- package/docs/migration/v0.8-to-v0.9.md +567 -209
- package/docs/migration/v0.9.0-troubleshooting.md +561 -0
- package/docs/reference/README.md +19 -0
- package/docs/reference/bundle-types.md +19 -0
- package/docs/reference/cli.md +2342 -0
- package/docs/reference/configuration.md +371 -0
- package/docs/reference/data-and-telemetry.md +129 -46
- package/docs/reference/supported-formats.md +50 -0
- package/docs/reference/workflow-schema.md +1014 -0
- package/docs/reference/workflows.md +51 -0
- package/package.json +31 -13
- package/schemas/akm-asset-envelope.json +93 -0
- package/schemas/akm-config.json +89 -128
- package/schemas/akm-task.json +27 -5
- package/schemas/akm-workflow.json +157 -81
- 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 -1878
- 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 -1231
- 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 -257
- package/dist/migrate/legacy/content-migration.js +0 -350
- 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 -80
- package/dist/migrate/legacy/proposal-fs-import.js +0 -168
- package/dist/migrate/legacy/task-target-ref-migration.js +0 -278
- package/dist/migrate/legacy/three-db-cutover.js +0 -845
- 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,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
|
+
}
|
|
@@ -10,6 +10,7 @@
|
|
|
10
10
|
*/
|
|
11
11
|
import fs from "node:fs";
|
|
12
12
|
import { parse as yamlParse, stringify as yamlStringify } from "yaml";
|
|
13
|
+
import { recordWrittenPath } from "../write-provenance.js";
|
|
13
14
|
import { assembleAsset, serializeFrontmatter } from "./asset-serialize.js";
|
|
14
15
|
/**
|
|
15
16
|
* Parse YAML frontmatter from a Markdown (or similar) string.
|
|
@@ -51,15 +52,11 @@ export function parseFrontmatter(raw) {
|
|
|
51
52
|
};
|
|
52
53
|
}
|
|
53
54
|
/**
|
|
54
|
-
* Normalize YAML
|
|
55
|
+
* Normalize YAML dates to match expected AKM frontmatter types.
|
|
55
56
|
*
|
|
56
|
-
*
|
|
57
|
-
* 1. `Date` → YYYY-MM-DD string: the yaml "core" schema parses bare date
|
|
57
|
+
* `Date` → YYYY-MM-DD string: the yaml "core" schema parses bare date
|
|
58
58
|
* scalars like `2026-06-18` as JS Date instances. AKM frontmatter treats
|
|
59
59
|
* `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
60
|
*/
|
|
64
61
|
function normalizeYamlValues(value) {
|
|
65
62
|
if (value instanceof Date) {
|
|
@@ -68,11 +65,9 @@ function normalizeYamlValues(value) {
|
|
|
68
65
|
const d = String(value.getUTCDate()).padStart(2, "0");
|
|
69
66
|
return `${y}-${m}-${d}`;
|
|
70
67
|
}
|
|
71
|
-
if (value === null)
|
|
72
|
-
return "";
|
|
73
68
|
if (Array.isArray(value))
|
|
74
69
|
return value.map(normalizeYamlValues);
|
|
75
|
-
if (typeof value === "object") {
|
|
70
|
+
if (value !== null && typeof value === "object") {
|
|
76
71
|
return Object.fromEntries(Object.entries(value).map(([k, v]) => [k, normalizeYamlValues(v)]));
|
|
77
72
|
}
|
|
78
73
|
return value;
|
|
@@ -149,6 +144,9 @@ export function mutateFrontmatter(filePath, mutator) {
|
|
|
149
144
|
? `---\n${serializeFrontmatter(nextFrontmatter)}\n---\n${parsed.content}`
|
|
150
145
|
: assembleAsset(nextFrontmatter, parsed.content);
|
|
151
146
|
fs.writeFileSync(filePath, next, "utf8");
|
|
147
|
+
// #652: in-place frontmatter stamps (belief state, contradiction markers,
|
|
148
|
+
// salience) are real asset mutations — journal them for the run's sync.
|
|
149
|
+
recordWrittenPath(filePath);
|
|
152
150
|
return true;
|
|
153
151
|
}
|
|
154
152
|
export function parseFrontmatterBlock(raw) {
|
|
@@ -182,6 +180,28 @@ function countLines(text) {
|
|
|
182
180
|
return 0;
|
|
183
181
|
return text.split(/\r?\n/).length - 1;
|
|
184
182
|
}
|
|
183
|
+
/**
|
|
184
|
+
* Insert one `key: value` line just before the closing `---` of an existing
|
|
185
|
+
* frontmatter block, leaving every other byte — YAML comments, quoting, key
|
|
186
|
+
* order, line endings — untouched. Returns null when `raw` has no well-formed
|
|
187
|
+
* block, so callers can fall back to a parse-and-serialize path.
|
|
188
|
+
*
|
|
189
|
+
* This is the source-preserving way to ADD a field to user-authored
|
|
190
|
+
* frontmatter: round-tripping the mapping through the YAML serializer drops
|
|
191
|
+
* comments and normalizes formatting, which is unacceptable for a write that
|
|
192
|
+
* only needs to contribute one line. Shared by `ensureAkmMarkdownType`
|
|
193
|
+
* (stamping `updated:` on write) and lint's `--fix` for `missing-updated`.
|
|
194
|
+
*/
|
|
195
|
+
export function spliceFrontmatterLine(raw, line) {
|
|
196
|
+
const lines = raw.split(/\r?\n/);
|
|
197
|
+
if (lines[0]?.trim() !== "---")
|
|
198
|
+
return null;
|
|
199
|
+
const closeIdx = lines.findIndex((l, i) => i > 0 && l.trim() === "---");
|
|
200
|
+
if (closeIdx === -1)
|
|
201
|
+
return null;
|
|
202
|
+
lines.splice(closeIdx, 0, line);
|
|
203
|
+
return lines.join("\n");
|
|
204
|
+
}
|
|
185
205
|
/**
|
|
186
206
|
* Parse a YAML scalar value (string, boolean, or number).
|
|
187
207
|
*
|
|
@@ -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
|
|
@@ -34,7 +34,7 @@
|
|
|
34
34
|
* grammar to bridge any more.
|
|
35
35
|
*/
|
|
36
36
|
import { NotFoundError, UsageError } from "../errors.js";
|
|
37
|
-
import { stashDirFor, typeForStashDir } from "./asset-placement.js";
|
|
37
|
+
import { deriveCanonicalAssetNameFromStashRoot, placementSpecFor, stashDirFor, typeForStashDir, } from "./asset-placement.js";
|
|
38
38
|
import { isBundleSlug, parseBundleRef } from "./asset-ref.js";
|
|
39
39
|
/**
|
|
40
40
|
* Resolve a maybe-short input ref to a fully-qualified {@link ResolvedRef}
|
|
@@ -82,10 +82,10 @@ function notFound(conceptId, triedForms, ctx) {
|
|
|
82
82
|
return new NotFoundError(`No bundle contains concept "${conceptId}"${scope} (tried ${forms}).`, "ASSET_NOT_FOUND");
|
|
83
83
|
}
|
|
84
84
|
/**
|
|
85
|
-
* D-R2 conceptId derivation from
|
|
85
|
+
* D-R2 conceptId derivation from an asset `type`/`name` pair
|
|
86
86
|
* (`stashDirFor(type)/name`; bare name for a foreign type with no placement
|
|
87
87
|
* stash-subdir). Kept self-contained so {@link displayRef} — a PERMANENT display
|
|
88
|
-
* rule
|
|
88
|
+
* rule remains independent of input parsing.
|
|
89
89
|
*
|
|
90
90
|
* Exported (Chunk-8 WI-8.5c) as the ONE conceptId derivation the improve
|
|
91
91
|
* correlation sites (`eligibility.ts` candidate refs, `salience.ts`
|
|
@@ -97,6 +97,19 @@ export function conceptIdFromTypeName(type, name) {
|
|
|
97
97
|
const stashDir = stashDirFor(type);
|
|
98
98
|
return stashDir !== undefined ? `${stashDir}/${name}` : name;
|
|
99
99
|
}
|
|
100
|
+
/**
|
|
101
|
+
* User-facing conceptId for a file on disk, derived through the placement
|
|
102
|
+
* spec's canonical-name rule — the ONE way a diagnostic should spell a ref it
|
|
103
|
+
* expects the user to paste into `akm show`. (The dangerous-env-key lint used
|
|
104
|
+
* to hand-build `env:<base>` colon refs the parser rejects; both its emission
|
|
105
|
+
* sites now route through here.) For a type with no placement spec — which no
|
|
106
|
+
* built-in caller passes — falls back to the raw name so the output is still
|
|
107
|
+
* informative rather than empty.
|
|
108
|
+
*/
|
|
109
|
+
export function conceptIdForStashFile(type, stashRoot, filePath) {
|
|
110
|
+
const name = deriveCanonicalAssetNameFromStashRoot(type, stashRoot, filePath);
|
|
111
|
+
return name === undefined ? filePath : conceptIdFromTypeName(type, name);
|
|
112
|
+
}
|
|
100
113
|
/**
|
|
101
114
|
* Build the USER-FACING / envelope ref string for an indexed item, applying the
|
|
102
115
|
* Chunk-5 flip F4b output-spelling rule (orchestrator decision; ref-grammar
|
|
@@ -116,19 +129,23 @@ export function conceptIdFromTypeName(type, name) {
|
|
|
116
129
|
* derived slug bundle id, never the retired `origin//type:name` spelling.
|
|
117
130
|
*/
|
|
118
131
|
export function displayRef(item, defaultBundleId) {
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
132
|
+
return displayRefForConceptId(item.conceptId ?? conceptIdFromTypeName(item.type, item.name), item.bundleId, defaultBundleId);
|
|
133
|
+
}
|
|
134
|
+
/**
|
|
135
|
+
* The F4b output-spelling flip itself, for a caller that already holds the
|
|
136
|
+
* conceptId (no `type`/`name` derivation needed — e.g. lint findings built
|
|
137
|
+
* from {@link conceptIdForStashFile}). {@link displayRef} delegates here, so
|
|
138
|
+
* the short-default / qualified-secondary rule still has exactly one home.
|
|
139
|
+
*/
|
|
140
|
+
export function displayRefForConceptId(conceptId, bundleId, defaultBundleId) {
|
|
141
|
+
// Default/primary bundle → SHORT conceptId (the flip).
|
|
142
|
+
if (bundleId === undefined || bundleId === defaultBundleId)
|
|
126
143
|
return conceptId;
|
|
127
144
|
// Non-default bundle → the new fully-qualified `bundle//conceptId` grammar.
|
|
128
145
|
return `${bundleId}//${conceptId}`;
|
|
129
146
|
}
|
|
130
147
|
/**
|
|
131
|
-
* Split a qualified conceptId (`<stash-subdir>/<name>`)
|
|
148
|
+
* Split a qualified conceptId (`<stash-subdir>/<name>`) into its asset
|
|
132
149
|
* `type`/`name`, or `undefined` when the leading segment is not a known stash
|
|
133
150
|
* subdir (a bare-name conceptId from a foreign type — no legacy predicate
|
|
134
151
|
* applies). The PERMANENT D-R2 reverse table: the input boundary uses it to map
|
|
@@ -144,6 +161,54 @@ export function typeNameFromConceptId(conceptId) {
|
|
|
144
161
|
return undefined;
|
|
145
162
|
return { type, name: conceptId.slice(slash + 1) };
|
|
146
163
|
}
|
|
164
|
+
/**
|
|
165
|
+
* D11 — the opaque-adapter-conceptId fallback. `typeNameFromConceptId` only
|
|
166
|
+
* ever answers for the PLACEMENT_SPECS stash-resident subset (D-R2); a
|
|
167
|
+
* conceptId whose leading segment is NOT a registered placement stashDir is
|
|
168
|
+
* still perfectly legal DATA per D11 — an OKF item (`tables/customers`), a
|
|
169
|
+
* website page, a wiki pageKind, an adapter `instruction` doc, … — and the
|
|
170
|
+
* ref-consuming commands must accept it rather than treat "not an AKM
|
|
171
|
+
* placement dir" as "malformed ref". This function draws the line: it accepts
|
|
172
|
+
* any well-formed `<segment>/<rest>` conceptId (so the shape is still
|
|
173
|
+
* anchored — a bare no-slash name stays the caller's job to pre-qualify with a
|
|
174
|
+
* default type, the existing env/secret/`akm mv` "bare name" convenience), and
|
|
175
|
+
* REJECTS anything shaped like the retired `type:name` colon grammar (Q-02):
|
|
176
|
+
* a `:` in the leading segment is that grammar smuggled through a conceptId
|
|
177
|
+
* string (e.g. `script:db/migrate/run.sh`, `workflow:release/train`), not a
|
|
178
|
+
* real adapter directory name, so it is refused rather than silently
|
|
179
|
+
* reinterpreted as opaque data.
|
|
180
|
+
*
|
|
181
|
+
* `name` deliberately carries the FULL original conceptId, not just the tail.
|
|
182
|
+
* This is what makes the pair round-trip through the UNCHANGED
|
|
183
|
+
* {@link conceptIdFromTypeName} (`stashDirFor(type)/name`, bare `name` when
|
|
184
|
+
* `type` has no placement stashDir): since an opaque `type` never owns a
|
|
185
|
+
* placement stashDir, `conceptIdFromTypeName(type, name)` falls to its bare-
|
|
186
|
+
* `name` branch and returns `name` verbatim — the original conceptId,
|
|
187
|
+
* unchanged — instead of losing the leading segment. `type` itself carries
|
|
188
|
+
* the leading segment (informational/behavioral use: `.type === "lesson"`
|
|
189
|
+
* checks correctly miss for opaque data) UNLESS that segment happens to
|
|
190
|
+
* collide with a real PLACEMENT_SPECS type key that merely uses a different
|
|
191
|
+
* stashDir spelling (e.g. a foreign top-level dir literally named "skill",
|
|
192
|
+
* singular) — that pathological case would make `stashDirFor` succeed on the
|
|
193
|
+
* "type" and corrupt the round-trip, so it falls back to the full conceptId as
|
|
194
|
+
* `type` too (never a placement key, since a placement key never contains
|
|
195
|
+
* `/`).
|
|
196
|
+
*
|
|
197
|
+
* This intentionally does NOT collapse the KNOWN_TYPES/PLACEMENT_SPECS split:
|
|
198
|
+
* `typeNameFromConceptId` (PLACEMENT_SPECS only) is untouched, and an opaque
|
|
199
|
+
* `type` returned here is never a {@link KnownType} — it is a passthrough
|
|
200
|
+
* label, not a claim that AKM recognizes or owns the concept.
|
|
201
|
+
*/
|
|
202
|
+
function opaqueRefParts(conceptId, allowRoot = false) {
|
|
203
|
+
const slash = conceptId.indexOf("/");
|
|
204
|
+
if (slash <= 0)
|
|
205
|
+
return allowRoot && !conceptId.includes(":") ? { type: conceptId, name: conceptId } : undefined;
|
|
206
|
+
const segment = conceptId.slice(0, slash);
|
|
207
|
+
if (segment.includes(":"))
|
|
208
|
+
return undefined; // retired `type:name` grammar, not opaque data (Q-02).
|
|
209
|
+
const type = placementSpecFor(segment) === undefined ? segment : conceptId;
|
|
210
|
+
return { type, name: conceptId };
|
|
211
|
+
}
|
|
147
212
|
/**
|
|
148
213
|
* Parse a RAW user / CLI / API ref string in the 0.9.0 `[bundle//]conceptId`
|
|
149
214
|
* grammar, returning it in today's {@link AssetRef} value-object shape
|
|
@@ -154,9 +219,14 @@ export function typeNameFromConceptId(conceptId) {
|
|
|
154
219
|
*
|
|
155
220
|
* Mapping (new grammar → {@link AssetRef}):
|
|
156
221
|
* - `conceptId` → `type`/`name` via {@link typeNameFromConceptId} (the D-R2
|
|
157
|
-
* static stash-subdir table)
|
|
158
|
-
*
|
|
159
|
-
*
|
|
222
|
+
* static stash-subdir table) when the leading segment is a known AKM
|
|
223
|
+
* placement stashDir, else via {@link opaqueRefParts} (D11 — the ref-parser
|
|
224
|
+
* seam accepts opaque adapter conceptIds, e.g. OKF items, website pages,
|
|
225
|
+
* wiki pageKinds, adapter `instruction` docs) when it is merely a well-
|
|
226
|
+
* formed but foreign `<segment>/<rest>` shape. A conceptId that is neither
|
|
227
|
+
* — no slash at all, or a retired colon-grammar shape smuggled through —
|
|
228
|
+
* has no type predicate: the same not-found outcome a genuine typo
|
|
229
|
+
* produces today.
|
|
160
230
|
* - `bundle` → `origin`. A new-grammar bundle slug is a registryId-shaped
|
|
161
231
|
* id by construction, so it flows straight into the legacy origin channel
|
|
162
232
|
* that `resolveSourcesForOrigin` matches on `registryId`. The SHORT form (no
|
|
@@ -168,11 +238,11 @@ export function parseRefInput(raw) {
|
|
|
168
238
|
if (ref.fragment !== undefined) {
|
|
169
239
|
throw new UsageError(`Export fragment "#${ref.fragment}" is not accepted here — drop it from "${raw.trim()}".`, "INVALID_FLAG_VALUE");
|
|
170
240
|
}
|
|
171
|
-
const
|
|
172
|
-
if (
|
|
241
|
+
const parts = typeNameFromConceptId(ref.conceptId) ?? opaqueRefParts(ref.conceptId, ref.bundle !== undefined);
|
|
242
|
+
if (parts === undefined) {
|
|
173
243
|
throw new NotFoundError(`Unrecognized asset ref "${raw.trim()}": conceptId "${ref.conceptId}" has no known asset-type prefix.`, "ASSET_NOT_FOUND");
|
|
174
244
|
}
|
|
175
|
-
return { type:
|
|
245
|
+
return { type: parts.type, name: parts.name, origin: ref.bundle };
|
|
176
246
|
}
|
|
177
247
|
/**
|
|
178
248
|
* Parse a CLI/API ref that MAY be qualified by a NON-slug origin — a registry
|
|
@@ -215,7 +285,8 @@ export function isFullRefInput(raw) {
|
|
|
215
285
|
if (!trimmed)
|
|
216
286
|
return false;
|
|
217
287
|
try {
|
|
218
|
-
|
|
288
|
+
const parsed = parseBundleRef(trimmed);
|
|
289
|
+
return parsed.bundle !== undefined || typeNameFromConceptId(parsed.conceptId) !== undefined;
|
|
219
290
|
}
|
|
220
291
|
catch {
|
|
221
292
|
return false;
|
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
* A stash may carry an optional, human-authored `.meta/` directory at its
|
|
8
8
|
* root holding orientation docs for the stash as a whole: purpose, key
|
|
9
9
|
* assets, conventions, maintainer info. Because `.meta/` is a dot-directory,
|
|
10
|
-
* the indexer's walker already skips it (see `src/indexer/walker.ts`), so
|
|
10
|
+
* the indexer's walker already skips it (see `src/indexer/walk/walker.ts`), so
|
|
11
11
|
* these files never pollute the search corpus. They are surfaced on demand
|
|
12
12
|
* via `akm show [<origin>//]meta[:<name>]`, which direct-reads the file
|
|
13
13
|
* rather than going through the index.
|
|
@@ -0,0 +1,51 @@
|
|
|
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 crypto from "node:crypto";
|
|
5
|
+
import path from "node:path";
|
|
6
|
+
import { isBundleSlug } from "./asset/asset-ref.js";
|
|
7
|
+
/** Deterministic, filesystem-safe bundle slug from a source path. */
|
|
8
|
+
export function slugForPath(sourcePath) {
|
|
9
|
+
const resolved = path.resolve(sourcePath);
|
|
10
|
+
const base = path
|
|
11
|
+
.basename(resolved)
|
|
12
|
+
.toLowerCase()
|
|
13
|
+
.replace(/[^a-z0-9]+/g, "-")
|
|
14
|
+
.replace(/^-+|-+$/g, "");
|
|
15
|
+
if (base.length > 0)
|
|
16
|
+
return base;
|
|
17
|
+
return `bundle-${shortHash(resolved)}`;
|
|
18
|
+
}
|
|
19
|
+
/** Derive one batch-unique bundle id. */
|
|
20
|
+
export function deriveBundleId(registryId, sourcePath, usedIds) {
|
|
21
|
+
const preferred = registryId && registryId.length > 0 && isBundleSlug(registryId) ? registryId : slugForPath(sourcePath);
|
|
22
|
+
const id = ensureUniqueId(preferred, sourcePath, usedIds);
|
|
23
|
+
usedIds.add(id);
|
|
24
|
+
return id;
|
|
25
|
+
}
|
|
26
|
+
/** Derive an ordered batch while reserving every explicit configured bundle id. */
|
|
27
|
+
export function deriveBundleIds(sources) {
|
|
28
|
+
const usedIds = new Set();
|
|
29
|
+
const reservedIds = new Set(sources.flatMap((source) => (source.registryId && isBundleSlug(source.registryId) ? [source.registryId] : [])));
|
|
30
|
+
return sources.map((source) => {
|
|
31
|
+
const id = source.registryId && isBundleSlug(source.registryId)
|
|
32
|
+
? deriveBundleId(source.registryId, source.path, usedIds)
|
|
33
|
+
: deriveBundleId(undefined, source.path, new Set([...usedIds, ...reservedIds]));
|
|
34
|
+
usedIds.add(id);
|
|
35
|
+
return id;
|
|
36
|
+
});
|
|
37
|
+
}
|
|
38
|
+
function ensureUniqueId(preferred, sourcePath, used) {
|
|
39
|
+
if (!used.has(preferred))
|
|
40
|
+
return preferred;
|
|
41
|
+
const suffixed = `${preferred}-${shortHash(path.resolve(sourcePath))}`;
|
|
42
|
+
if (!used.has(suffixed))
|
|
43
|
+
return suffixed;
|
|
44
|
+
let n = 2;
|
|
45
|
+
while (used.has(`${suffixed}-${n}`))
|
|
46
|
+
n++;
|
|
47
|
+
return `${suffixed}-${n}`;
|
|
48
|
+
}
|
|
49
|
+
function shortHash(input) {
|
|
50
|
+
return crypto.createHash("sha256").update(input).digest("hex").slice(0, 8);
|
|
51
|
+
}
|