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
|
@@ -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
|
+
}
|
package/dist/core/common.js
CHANGED
|
@@ -80,60 +80,105 @@ export function readTextFileWithLimit(filePath, maxBytes, label = "File") {
|
|
|
80
80
|
* 2. fsync the parent directory after rename, so the directory entry change
|
|
81
81
|
* is durable too. Some filesystems (FAT, certain FUSE mounts) don't
|
|
82
82
|
* support directory fsync; we ignore EINVAL/ENOTSUP so atomic writes
|
|
83
|
-
* don't fail on
|
|
83
|
+
* don't fail on those mounts. Windows does not support opening a
|
|
84
|
+
* directory for fsync, so the directory-sync step is skipped there.
|
|
84
85
|
*/
|
|
85
86
|
export function writeFileAtomic(target, content, mode) {
|
|
86
87
|
const tmp = `${target}.tmp.${process.pid}.${crypto.randomBytes(8).toString("hex")}`;
|
|
87
|
-
const
|
|
88
|
+
const data = typeof content === "string" ? Buffer.from(content) : content;
|
|
89
|
+
const fileMode = mode ?? 0o600;
|
|
90
|
+
let fd;
|
|
91
|
+
let tempOwned = false;
|
|
92
|
+
let renamed = false;
|
|
93
|
+
let failed = false;
|
|
94
|
+
let failure;
|
|
88
95
|
try {
|
|
89
|
-
fs.
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
96
|
+
fd = fs.openSync(tmp, "wx", fileMode);
|
|
97
|
+
tempOwned = true;
|
|
98
|
+
if (process.platform !== "win32")
|
|
99
|
+
fs.fchmodSync(fd, fileMode);
|
|
100
|
+
let offset = 0;
|
|
101
|
+
while (offset < data.byteLength) {
|
|
102
|
+
const written = fs.writeSync(fd, data, offset, data.byteLength - offset);
|
|
103
|
+
if (written <= 0)
|
|
104
|
+
throw new Error(`Could not make progress writing atomic temp file ${tmp}.`);
|
|
105
|
+
offset += written;
|
|
97
106
|
}
|
|
107
|
+
fs.fdatasyncSync(fd);
|
|
108
|
+
fs.closeSync(fd);
|
|
109
|
+
fd = undefined;
|
|
110
|
+
fs.renameSync(tmp, target);
|
|
111
|
+
renamed = true;
|
|
112
|
+
}
|
|
113
|
+
catch (error) {
|
|
114
|
+
failed = true;
|
|
115
|
+
failure = error;
|
|
98
116
|
}
|
|
99
117
|
finally {
|
|
100
|
-
|
|
118
|
+
if (fd !== undefined) {
|
|
119
|
+
try {
|
|
120
|
+
fs.closeSync(fd);
|
|
121
|
+
}
|
|
122
|
+
catch (error) {
|
|
123
|
+
if (!failed) {
|
|
124
|
+
failed = true;
|
|
125
|
+
failure = error;
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
if (tempOwned && !renamed) {
|
|
130
|
+
try {
|
|
131
|
+
fs.unlinkSync(tmp);
|
|
132
|
+
}
|
|
133
|
+
catch (error) {
|
|
134
|
+
if (!failed && !hasErrnoCode(error, "ENOENT")) {
|
|
135
|
+
failed = true;
|
|
136
|
+
failure = error;
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
}
|
|
101
140
|
}
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
141
|
+
if (failed)
|
|
142
|
+
throw failure;
|
|
143
|
+
if (process.platform !== "win32") {
|
|
144
|
+
let dirFd;
|
|
105
145
|
try {
|
|
106
|
-
fs.
|
|
146
|
+
dirFd = fs.openSync(path.dirname(target), "r");
|
|
147
|
+
}
|
|
148
|
+
catch (error) {
|
|
149
|
+
if (hasErrnoCode(error, "EINVAL") || hasErrnoCode(error, "ENOTSUP"))
|
|
150
|
+
return;
|
|
151
|
+
throw error;
|
|
152
|
+
}
|
|
153
|
+
try {
|
|
154
|
+
try {
|
|
155
|
+
fs.fsyncSync(dirFd);
|
|
156
|
+
}
|
|
157
|
+
catch (error) {
|
|
158
|
+
if (!hasErrnoCode(error, "EINVAL") && !hasErrnoCode(error, "ENOTSUP"))
|
|
159
|
+
throw error;
|
|
160
|
+
}
|
|
107
161
|
}
|
|
108
162
|
finally {
|
|
109
163
|
fs.closeSync(dirFd);
|
|
110
164
|
}
|
|
111
165
|
}
|
|
112
|
-
catch {
|
|
113
|
-
// Directory fsync is unsupported on FAT, some FUSE mounts, and Windows
|
|
114
|
-
// (where directories cannot be opened for read like POSIX). Silently
|
|
115
|
-
// ignore so writeFileAtomic remains portable.
|
|
116
|
-
}
|
|
117
166
|
}
|
|
118
167
|
/**
|
|
119
168
|
* Resolve the stash directory using a three-level fallback chain:
|
|
120
|
-
* 1.
|
|
121
|
-
* 2.
|
|
169
|
+
* 1. AKM_BUNDLE_DIR environment variable (override for CI/scripts)
|
|
170
|
+
* 2. The configured default bundle path
|
|
122
171
|
* 3. Platform default (~/akm or ~/Documents/akm on Windows)
|
|
123
172
|
*
|
|
124
|
-
* Pure read: never writes to disk. The legacy `readOnly` option is accepted
|
|
125
|
-
* (and ignored) for one release cycle so older callers continue to compile;
|
|
126
|
-
* it can be removed in the next minor bump.
|
|
127
|
-
*
|
|
128
173
|
* Throws if no valid stash directory is found.
|
|
129
174
|
*/
|
|
130
|
-
export function resolveStashDir(
|
|
175
|
+
export function resolveStashDir(env = process.env) {
|
|
131
176
|
// 1. Env var override (for CI, scripts, testing)
|
|
132
|
-
const envDir = env.
|
|
177
|
+
const envDir = env.AKM_BUNDLE_DIR?.trim();
|
|
133
178
|
if (envDir) {
|
|
134
179
|
return validateStashDir(envDir);
|
|
135
180
|
}
|
|
136
|
-
// 2.
|
|
181
|
+
// 2. Configured default bundle path
|
|
137
182
|
const configStashDir = readStashDirFromConfig();
|
|
138
183
|
if (configStashDir)
|
|
139
184
|
return validateStashDir(configStashDir);
|
|
@@ -142,7 +187,7 @@ export function resolveStashDir(_options, env = process.env) {
|
|
|
142
187
|
if (isValidDirectory(defaultDir)) {
|
|
143
188
|
return defaultDir;
|
|
144
189
|
}
|
|
145
|
-
throw new ConfigError(`No
|
|
190
|
+
throw new ConfigError(`No bundle directory found. Run "akm bundle create" to create one at ${defaultDir}.`, "STASH_DIR_NOT_FOUND");
|
|
146
191
|
}
|
|
147
192
|
function validateStashDir(raw) {
|
|
148
193
|
const stashDir = path.resolve(raw);
|
|
@@ -151,10 +196,10 @@ function validateStashDir(raw) {
|
|
|
151
196
|
stat = fs.statSync(stashDir);
|
|
152
197
|
}
|
|
153
198
|
catch {
|
|
154
|
-
throw new ConfigError(`Unable to read
|
|
199
|
+
throw new ConfigError(`Unable to read bundle directory at "${stashDir}".`, "STASH_DIR_UNREADABLE");
|
|
155
200
|
}
|
|
156
201
|
if (!stat.isDirectory()) {
|
|
157
|
-
throw new ConfigError(`
|
|
202
|
+
throw new ConfigError(`Bundle path must point to a directory: "${stashDir}".`, "STASH_DIR_NOT_A_DIRECTORY");
|
|
158
203
|
}
|
|
159
204
|
return stashDir;
|
|
160
205
|
}
|
|
@@ -172,7 +217,7 @@ function isValidDirectory(dir) {
|
|
|
172
217
|
*
|
|
173
218
|
* Reads ONLY the 0.9.0 `bundles`/`defaultBundle` shape. A config still carrying
|
|
174
219
|
* the retired `stashDir`/`sources`/`installed` keys (with no usable bundles
|
|
175
|
-
* path) is an unmigrated config: this refuses it with the same `akm
|
|
220
|
+
* path) is an unmigrated config: this refuses it with the same `akm-migrate
|
|
176
221
|
* apply` hint the schema hard-reject uses (config-schema.ts), rather than
|
|
177
222
|
* silently honouring the retired key — so every `resolveStashDir` caller gets
|
|
178
223
|
* the coherent migrate posture instead of split-brain success.
|
|
@@ -197,14 +242,37 @@ function readStashDirFromConfig() {
|
|
|
197
242
|
typeof bundles[defaultBundle] === "object" &&
|
|
198
243
|
typeof bundles[defaultBundle].path === "string" &&
|
|
199
244
|
bundles[defaultBundle].path.trim()) {
|
|
200
|
-
|
|
245
|
+
const bundle = bundles[defaultBundle];
|
|
246
|
+
const bundlePath = bundle.path.trim();
|
|
247
|
+
if (bundle.components !== undefined) {
|
|
248
|
+
if (typeof bundle.components !== "object" || bundle.components === null) {
|
|
249
|
+
throw new ConfigError("A bundle components map must contain exactly one component.", "INVALID_CONFIG_FILE");
|
|
250
|
+
}
|
|
251
|
+
const components = Object.values(bundle.components);
|
|
252
|
+
if (components.length !== 1) {
|
|
253
|
+
throw new ConfigError("A bundle components map must contain exactly one component.", "INVALID_CONFIG_FILE");
|
|
254
|
+
}
|
|
255
|
+
const component = components[0];
|
|
256
|
+
if (typeof component === "object" && component !== null) {
|
|
257
|
+
const componentConfig = component;
|
|
258
|
+
if (typeof componentConfig.root !== "string")
|
|
259
|
+
return bundlePath;
|
|
260
|
+
const bundleRoot = path.resolve(bundlePath);
|
|
261
|
+
const componentRoot = path.resolve(bundleRoot, componentConfig.root);
|
|
262
|
+
if (!isWithin(componentRoot, bundleRoot)) {
|
|
263
|
+
throw new ConfigError(`Component root "${componentConfig.root}" escapes bundle "${defaultBundle}".`, "INVALID_CONFIG_FILE");
|
|
264
|
+
}
|
|
265
|
+
return componentRoot;
|
|
266
|
+
}
|
|
267
|
+
}
|
|
268
|
+
return bundlePath;
|
|
201
269
|
}
|
|
202
270
|
// Retired pre-cutover shape with no usable bundles path: refuse with the
|
|
203
271
|
// migrate hint (matches the schema's hard-reject, config-schema.ts) instead
|
|
204
272
|
// of silently resolving the old key.
|
|
205
273
|
for (const key of ["stashDir", "sources", "installed"]) {
|
|
206
274
|
if (key in raw && raw[key] !== undefined) {
|
|
207
|
-
throw new ConfigError(`${key} is the retired pre-cutover source shape; run \`akm
|
|
275
|
+
throw new ConfigError(`${key} is the retired pre-cutover source shape; run \`akm-migrate apply\` to convert it to bundles`, "INVALID_CONFIG_FILE");
|
|
208
276
|
}
|
|
209
277
|
}
|
|
210
278
|
}
|
|
@@ -308,6 +376,8 @@ export async function fetchWithTimeout(url, opts, timeoutMs = 30_000, signal) {
|
|
|
308
376
|
}
|
|
309
377
|
}
|
|
310
378
|
try {
|
|
379
|
+
if (controller.signal.aborted)
|
|
380
|
+
throw controller.signal.reason ?? new Error(`Request aborted: ${url}`);
|
|
311
381
|
return await fetch(url, { ...opts, signal: controller.signal });
|
|
312
382
|
}
|
|
313
383
|
catch (err) {
|
|
@@ -337,11 +407,12 @@ export async function fetchWithRetry(url, init, options) {
|
|
|
337
407
|
const timeout = options?.timeout ?? 30_000;
|
|
338
408
|
for (let attempt = 0; attempt <= maxRetries; attempt++) {
|
|
339
409
|
try {
|
|
340
|
-
const response = await fetchWithTimeout(url, init, timeout);
|
|
410
|
+
const response = await fetchWithTimeout(url, init, timeout, init?.signal ?? undefined);
|
|
341
411
|
if (attempt < maxRetries && shouldRetry(response.status)) {
|
|
342
412
|
const retryAfter = parseRetryAfter(response);
|
|
343
413
|
const delay = retryAfter ?? baseDelay * 2 ** attempt * (0.5 + Math.random() * 0.5);
|
|
344
|
-
await
|
|
414
|
+
await response.body?.cancel().catch(() => undefined);
|
|
415
|
+
await abortableDelay(delay, init?.signal);
|
|
345
416
|
continue;
|
|
346
417
|
}
|
|
347
418
|
return response;
|
|
@@ -349,12 +420,41 @@ export async function fetchWithRetry(url, init, options) {
|
|
|
349
420
|
catch (err) {
|
|
350
421
|
if (attempt >= maxRetries)
|
|
351
422
|
throw err;
|
|
423
|
+
// A caller-supplied abort is terminal: never keep retrying past it.
|
|
424
|
+
if (init?.signal?.aborted)
|
|
425
|
+
throw err;
|
|
352
426
|
const delay = baseDelay * 2 ** attempt * (0.5 + Math.random() * 0.5);
|
|
353
|
-
await
|
|
427
|
+
await abortableDelay(delay, init?.signal);
|
|
354
428
|
}
|
|
355
429
|
}
|
|
356
430
|
throw new Error("fetchWithRetry: unreachable");
|
|
357
431
|
}
|
|
432
|
+
/**
|
|
433
|
+
* Sleep, but wake immediately if `signal` aborts.
|
|
434
|
+
*
|
|
435
|
+
* A server-supplied `Retry-After` is honored verbatim and can be arbitrarily
|
|
436
|
+
* large. Sleeping it out with a bare `setTimeout` ignored the caller's abort
|
|
437
|
+
* signal entirely, so a single `429` could park an operation far past any
|
|
438
|
+
* deadline its caller believed it had imposed — the request timeout bounds
|
|
439
|
+
* only the request, never the wait between attempts.
|
|
440
|
+
*/
|
|
441
|
+
function abortableDelay(ms, signal) {
|
|
442
|
+
if (!signal)
|
|
443
|
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
444
|
+
if (signal.aborted)
|
|
445
|
+
return Promise.reject(signal.reason ?? new Error("Aborted"));
|
|
446
|
+
return new Promise((resolve, reject) => {
|
|
447
|
+
const onAbort = () => {
|
|
448
|
+
clearTimeout(timer);
|
|
449
|
+
reject(signal.reason ?? new Error("Aborted"));
|
|
450
|
+
};
|
|
451
|
+
const timer = setTimeout(() => {
|
|
452
|
+
signal.removeEventListener("abort", onAbort);
|
|
453
|
+
resolve();
|
|
454
|
+
}, ms);
|
|
455
|
+
signal.addEventListener("abort", onAbort, { once: true });
|
|
456
|
+
});
|
|
457
|
+
}
|
|
358
458
|
function shouldRetry(status) {
|
|
359
459
|
return status === 429 || status >= 500;
|
|
360
460
|
}
|
|
@@ -560,6 +660,20 @@ export function toErrorMessage(error) {
|
|
|
560
660
|
export function todayIso() {
|
|
561
661
|
return new Date().toISOString().slice(0, 10);
|
|
562
662
|
}
|
|
663
|
+
/**
|
|
664
|
+
* `YYYY-MM-DD` in LOCAL time — deliberately not {@link todayIso}, which is
|
|
665
|
+
* UTC and can differ near midnight. This is the spelling the `updated:`
|
|
666
|
+
* frontmatter stampers share (`core/asset/akm-markdown.ts` on write,
|
|
667
|
+
* `commands/lint/base-linter.ts` on `--fix`), so the field's format has one
|
|
668
|
+
* definition even though the two stampers pick different instants (now vs
|
|
669
|
+
* file mtime).
|
|
670
|
+
*/
|
|
671
|
+
export function localDateStamp(d) {
|
|
672
|
+
const y = d.getFullYear();
|
|
673
|
+
const m = String(d.getMonth() + 1).padStart(2, "0");
|
|
674
|
+
const day = String(d.getDate()).padStart(2, "0");
|
|
675
|
+
return `${y}-${m}-${day}`;
|
|
676
|
+
}
|
|
563
677
|
/**
|
|
564
678
|
* Return a filesystem-safe timestamp string derived from the current instant.
|
|
565
679
|
* Colons and dots are replaced with hyphens so the result is safe as a
|
|
@@ -156,7 +156,18 @@ function pruneOldBackups(backupDir) {
|
|
|
156
156
|
export function getConfigLockPath() {
|
|
157
157
|
return path.join(getConfigDir(), "config.json.lck");
|
|
158
158
|
}
|
|
159
|
-
|
|
159
|
+
// Total wait budget for a CONTENDED lock: retries × delay. An abandoned lock
|
|
160
|
+
// is not covered by this budget — it is reclaimed by the stale-lock probe
|
|
161
|
+
// below — so this only bounds how long a caller queues behind live writers.
|
|
162
|
+
//
|
|
163
|
+
// 10 × 50ms (500ms) was too thin: N concurrent `akm config set` processes
|
|
164
|
+
// serialize on this lock, so the last one in line waits for all N-1 holders.
|
|
165
|
+
// On a loaded machine (CI runs four test shards in parallel) eight contenders
|
|
166
|
+
// exhausted the budget and one exited with "Timed out waiting for config
|
|
167
|
+
// lock", which is a spurious failure — the lock was healthy and simply busy.
|
|
168
|
+
// 2s gives real concurrent use headroom while still failing promptly against a
|
|
169
|
+
// genuinely wedged (but non-stale) holder.
|
|
170
|
+
const CONFIG_LOCK_MAX_RETRIES = 40;
|
|
160
171
|
const CONFIG_LOCK_RETRY_DELAY_MS = 50;
|
|
161
172
|
/**
|
|
162
173
|
* Block the current thread for `ms` without busy-spinning (H8). Delegates to
|
|
@@ -46,6 +46,7 @@ import { z } from "zod";
|
|
|
46
46
|
import { BUILTIN_IMPROVE_STRATEGY_NAMES, IMPROVE_PROCESS_ENGINE_CAPABILITIES } from "./engine-semantics.js";
|
|
47
47
|
import { EmbeddingConnectionConfigSchema } from "./schema/embedding.js";
|
|
48
48
|
import { EnginesSchema } from "./schema/engines.js";
|
|
49
|
+
import { ExperimentalConfigSchema } from "./schema/experimental.js";
|
|
49
50
|
import { FeedbackConfigSchema } from "./schema/feedback.js";
|
|
50
51
|
import { ImproveConfigSchema } from "./schema/improve.js";
|
|
51
52
|
import { IndexConfigSchema } from "./schema/index-config.js";
|
|
@@ -58,6 +59,7 @@ import { WorkflowConfigSchema } from "./schema/workflow.js";
|
|
|
58
59
|
// ── Section re-exports (keep every `./config-schema` import path working) ────
|
|
59
60
|
export { EmbeddingConnectionConfigSchema } from "./schema/embedding.js";
|
|
60
61
|
export { EngineConfigSchema, EnginesSchema, LlmConnectionConfigSchema, LlmProfileConfigSchema } from "./schema/engines.js";
|
|
62
|
+
export { ExperimentalConfigSchema } from "./schema/experimental.js";
|
|
61
63
|
export { FEEDBACK_FAILURE_MODES, FeedbackConfigSchema } from "./schema/feedback.js";
|
|
62
64
|
export { ImproveConfigSchema } from "./schema/improve.js";
|
|
63
65
|
export { ConsolidateProcessConfigSchema, DistillProcessConfigSchema, ExtractProcessConfigSchema, GraphExtractionProcessConfigSchema, ImproveProcessConfigSchema, ImproveProfileConfigSchema, MemoryInferenceProcessConfigSchema, ProactiveMaintenanceProcessConfigSchema, ReflectProcessConfigSchema, TriageProcessConfigSchema, ValidationProcessConfigSchema, } from "./schema/improve-processes.js";
|
|
@@ -65,8 +67,8 @@ export { IndexConfigSchema, IndexPassConfigSchema } from "./schema/index-config.
|
|
|
65
67
|
export { OutputConfigSchema } from "./schema/output.js";
|
|
66
68
|
export { CURRENT_CONFIG_VERSION, LlmInvocationOverridesSchema } from "./schema/primitives.js";
|
|
67
69
|
export { SearchConfigSchema } from "./schema/search.js";
|
|
68
|
-
export { SetupConfigSchema
|
|
69
|
-
export { BundleConfigEntrySchema, BundlesConfigSchema,
|
|
70
|
+
export { SetupConfigSchema } from "./schema/setup.js";
|
|
71
|
+
export { BundleConfigEntrySchema, BundlesConfigSchema, RegistryConfigEntrySchema, SourceConfigEntrySchema, } from "./schema/sources-bundles.js";
|
|
70
72
|
export { WorkflowConfigSchema } from "./schema/workflow.js";
|
|
71
73
|
// ── Defaults ───────────────────────────────────────────────────────────────
|
|
72
74
|
export const DefaultsSchema = z
|
|
@@ -100,7 +102,7 @@ export const AkmConfigShape = {
|
|
|
100
102
|
// custom profile's name for the default builder) — unknown keys are inert.
|
|
101
103
|
// Precedence: profile modelAliases > this table > built-in aliases.
|
|
102
104
|
modelAliases: GlobalModelAliasesSchema.optional(),
|
|
103
|
-
semanticSearchMode: z.enum(["off", "auto"]).default("
|
|
105
|
+
semanticSearchMode: z.enum(["off", "auto"]).default("off"),
|
|
104
106
|
embedding: EmbeddingConnectionConfigSchema.optional(),
|
|
105
107
|
index: IndexConfigSchema.optional(),
|
|
106
108
|
registries: z.array(RegistryConfigEntrySchema).optional(),
|
|
@@ -109,13 +111,10 @@ export const AkmConfigShape = {
|
|
|
109
111
|
// trio is hard-rejected at load (see the top-level superRefine). The migrator
|
|
110
112
|
// ({@link migrateConfigSourcesToBundles}) converts a pre-cutover config to this
|
|
111
113
|
// shape before validation. `defaultBundle` names the primary bundle (spec
|
|
112
|
-
// §11.1 short-ref resolution / D-R4).
|
|
113
|
-
// `InstalledStashEntrySchema` remain EXPORTED (the migrator + transitional
|
|
114
|
-
// readers consume them) but are no longer top-level config fields.
|
|
114
|
+
// §11.1 short-ref resolution / D-R4).
|
|
115
115
|
bundles: BundlesConfigSchema.optional(),
|
|
116
116
|
defaultBundle: nonEmptyString.optional(),
|
|
117
117
|
output: OutputConfigSchema.optional(),
|
|
118
|
-
writable: z.boolean().optional(),
|
|
119
118
|
defaultWriteTarget: nonEmptyString.optional(),
|
|
120
119
|
search: SearchConfigSchema.optional(),
|
|
121
120
|
feedback: FeedbackConfigSchema.optional(),
|
|
@@ -123,8 +122,20 @@ export const AkmConfigShape = {
|
|
|
123
122
|
improve: ImproveConfigSchema.optional(),
|
|
124
123
|
workflow: WorkflowConfigSchema.optional(),
|
|
125
124
|
setup: SetupConfigSchema.optional(),
|
|
125
|
+
// D8 — explicit opt-ins for behaviour outside the stability contract. Every
|
|
126
|
+
// key defaults to OFF; see `src/core/config/experimental.ts` for the readers.
|
|
127
|
+
experimental: ExperimentalConfigSchema.optional(),
|
|
126
128
|
};
|
|
127
129
|
export const AkmConfigBaseSchema = z.object(AkmConfigShape).passthrough();
|
|
130
|
+
/**
|
|
131
|
+
* Per-key overrides for the retired `stashDir`/`sources`/`installed` source-
|
|
132
|
+
* shape rejection below. Keys without an entry fall back to the shared
|
|
133
|
+
* "retired pre-cutover source shape" message, which already names the
|
|
134
|
+
* replacement (bundles) and the migration command.
|
|
135
|
+
*/
|
|
136
|
+
const RETIRED_SOURCE_SHAPE_KEY_MESSAGES = {
|
|
137
|
+
stashDir: "stashDir is retired in 0.9; the stash path now comes from `bundles`. Run `akm-migrate apply` to convert a pre-0.9 config, or see `akm config path --all` / `akm info`.",
|
|
138
|
+
};
|
|
128
139
|
export const AkmConfigSchema = AkmConfigBaseSchema.superRefine((config, ctx) => {
|
|
129
140
|
const raw = config;
|
|
130
141
|
for (const key of ["profiles", "llm", "agent", "features", "stashes"]) {
|
|
@@ -157,10 +168,18 @@ export const AkmConfigSchema = AkmConfigBaseSchema.superRefine((config, ctx) =>
|
|
|
157
168
|
ctx.addIssue({
|
|
158
169
|
code: z.ZodIssueCode.custom,
|
|
159
170
|
path: [key],
|
|
160
|
-
message:
|
|
171
|
+
message: RETIRED_SOURCE_SHAPE_KEY_MESSAGES[key] ??
|
|
172
|
+
`${key} is the retired pre-cutover source shape; run \`akm-migrate apply\` to convert it to bundles`,
|
|
161
173
|
});
|
|
162
174
|
}
|
|
163
175
|
}
|
|
176
|
+
if ("writable" in raw) {
|
|
177
|
+
ctx.addIssue({
|
|
178
|
+
code: z.ZodIssueCode.custom,
|
|
179
|
+
path: ["writable"],
|
|
180
|
+
message: "top-level writable is not supported; configure bundles.<id>.writable instead",
|
|
181
|
+
});
|
|
182
|
+
}
|
|
164
183
|
// `defaultBundle`, when present, must name a configured bundle.
|
|
165
184
|
if (config.defaultBundle !== undefined) {
|
|
166
185
|
if (config.bundles === undefined) {
|
|
@@ -215,6 +234,14 @@ export const AkmConfigSchema = AkmConfigBaseSchema.superRefine((config, ctx) =>
|
|
|
215
234
|
message: "llmEngine must name an LLM engine",
|
|
216
235
|
});
|
|
217
236
|
}
|
|
237
|
+
const workflowJudge = config.workflow?.judgeEngine;
|
|
238
|
+
if (workflowJudge && !config.engines?.[workflowJudge]) {
|
|
239
|
+
ctx.addIssue({
|
|
240
|
+
code: z.ZodIssueCode.custom,
|
|
241
|
+
path: ["workflow", "judgeEngine"],
|
|
242
|
+
message: "judgeEngine does not name a configured engine",
|
|
243
|
+
});
|
|
244
|
+
}
|
|
218
245
|
const defaultStrategy = config.defaults?.improveStrategy;
|
|
219
246
|
if (defaultStrategy &&
|
|
220
247
|
!BUILTIN_IMPROVE_STRATEGY_NAMES.includes(defaultStrategy) &&
|