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
|
@@ -25,12 +25,106 @@
|
|
|
25
25
|
* WARNING (consumers tolerate broken links). Reads go through
|
|
26
26
|
* `ctx.readFile`; ref existence via `ctx.resolveRef`. Never touches the live
|
|
27
27
|
* filesystem.
|
|
28
|
-
* -
|
|
28
|
+
* - directoryList / looksLikeRoot per §5 / §1.2. Authoring is deliberately
|
|
29
|
+
* absent; AKM-native writers fail before mutating an OKF bundle.
|
|
29
30
|
*/
|
|
30
31
|
import fs from "node:fs";
|
|
31
32
|
import path from "node:path";
|
|
32
33
|
import { parseFrontmatter } from "../../asset/frontmatter.js";
|
|
33
34
|
import { hashContent, nonEmptyString, readTags, runBaseValidateChecks } from "./shared.js";
|
|
35
|
+
/** v0.2 frontmatter keys consumed into first-class fields below (§0.1) — excluded from the generic `documentJson` extras fold alongside the v0.1 five, so nothing is duplicated between a first-class field and the opaque extras bag. */
|
|
36
|
+
const CONSUMED_FRONTMATTER_KEYS = [
|
|
37
|
+
"type",
|
|
38
|
+
"title",
|
|
39
|
+
"description",
|
|
40
|
+
"tags",
|
|
41
|
+
"timestamp",
|
|
42
|
+
"generated",
|
|
43
|
+
"verified",
|
|
44
|
+
"sources",
|
|
45
|
+
"status",
|
|
46
|
+
"stale_after",
|
|
47
|
+
"okf_version",
|
|
48
|
+
];
|
|
49
|
+
/** True for a plain (non-null, non-array) object — the shape every v0.2 mapping (`generated`, one `verified`/`sources` entry) must have. */
|
|
50
|
+
function isPlainObject(value) {
|
|
51
|
+
return value !== null && typeof value === "object" && !Array.isArray(value);
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* Parse one `verified:` actor mapping (`{by, at?}`). Tolerant: a missing/blank
|
|
55
|
+
* `by` yields `undefined` (the entry is dropped, never rejecting the document
|
|
56
|
+
* — OKF conformance leniency); `at` is independently optional.
|
|
57
|
+
*/
|
|
58
|
+
function parseActorMapping(value) {
|
|
59
|
+
if (!isPlainObject(value))
|
|
60
|
+
return undefined;
|
|
61
|
+
const by = nonEmptyString(value.by);
|
|
62
|
+
if (by === undefined)
|
|
63
|
+
return undefined;
|
|
64
|
+
const at = nonEmptyString(value.at);
|
|
65
|
+
return at !== undefined ? { by, at } : { by };
|
|
66
|
+
}
|
|
67
|
+
/**
|
|
68
|
+
* Parse the `verified:` family. v0.2 permits EITHER a list of `{by, at?}`
|
|
69
|
+
* mappings OR a single mapping written without the list dash (the shorthand
|
|
70
|
+
* form) — both normalize to a non-empty array here. Malformed entries are
|
|
71
|
+
* dropped individually; an entirely-empty/malformed result is `undefined`
|
|
72
|
+
* rather than `[]` (mirrors every other optional-field convention on this
|
|
73
|
+
* adapter: absent, not empty).
|
|
74
|
+
*/
|
|
75
|
+
function parseVerified(value) {
|
|
76
|
+
if (value === undefined || value === null)
|
|
77
|
+
return undefined;
|
|
78
|
+
const candidates = Array.isArray(value) ? value : [value];
|
|
79
|
+
const out = [];
|
|
80
|
+
for (const candidate of candidates) {
|
|
81
|
+
const parsed = parseActorMapping(candidate);
|
|
82
|
+
if (parsed)
|
|
83
|
+
out.push(parsed);
|
|
84
|
+
}
|
|
85
|
+
return out.length > 0 ? out : undefined;
|
|
86
|
+
}
|
|
87
|
+
/**
|
|
88
|
+
* Parse the `sources:` object-list family. Each entry needs at minimum a
|
|
89
|
+
* non-empty `resource`; entries failing that are dropped individually (never
|
|
90
|
+
* rejects the document). This is a DIFFERENT shape from the AKM-native
|
|
91
|
+
* `IndexDocument.sources: string[]` (wiki citations) — the two are never
|
|
92
|
+
* mixed, hence the caller lands this under `provenance.sources` instead.
|
|
93
|
+
*/
|
|
94
|
+
function parseOkfSources(value) {
|
|
95
|
+
if (!Array.isArray(value))
|
|
96
|
+
return undefined;
|
|
97
|
+
const out = [];
|
|
98
|
+
for (const item of value) {
|
|
99
|
+
if (!isPlainObject(item))
|
|
100
|
+
continue;
|
|
101
|
+
const resource = nonEmptyString(item.resource);
|
|
102
|
+
if (resource === undefined)
|
|
103
|
+
continue;
|
|
104
|
+
const entry = { resource };
|
|
105
|
+
const id = nonEmptyString(item.id);
|
|
106
|
+
if (id !== undefined)
|
|
107
|
+
entry.id = id;
|
|
108
|
+
const title = nonEmptyString(item.title);
|
|
109
|
+
if (title !== undefined)
|
|
110
|
+
entry.title = title;
|
|
111
|
+
const author = nonEmptyString(item.author);
|
|
112
|
+
if (author !== undefined)
|
|
113
|
+
entry.author = author;
|
|
114
|
+
if (typeof item.usage_count === "number" && Number.isFinite(item.usage_count)) {
|
|
115
|
+
entry.usage_count = item.usage_count;
|
|
116
|
+
}
|
|
117
|
+
const lastModified = nonEmptyString(item.last_modified);
|
|
118
|
+
if (lastModified !== undefined)
|
|
119
|
+
entry.last_modified = lastModified;
|
|
120
|
+
out.push(entry);
|
|
121
|
+
}
|
|
122
|
+
return out.length > 0 ? out : undefined;
|
|
123
|
+
}
|
|
124
|
+
/** Parse the `status:` lifecycle field — a strict whitelist; any other value (including a foreign/future status) is left unset rather than guessed at. */
|
|
125
|
+
function parseLifecycleStatus(value) {
|
|
126
|
+
return value === "draft" || value === "stable" || value === "deprecated" ? value : undefined;
|
|
127
|
+
}
|
|
34
128
|
/** Reserved OKF files (case-insensitive) — recognized, never indexed as concepts (§5, OKF §1.4). */
|
|
35
129
|
const RESERVED_FILES = new Set(["index.md", "log.md"]);
|
|
36
130
|
/** Upper bound on the bounded `content` FTS field (§3: "content: FTS 1 (bounded)"). Small fixtures are never truncated. */
|
|
@@ -59,13 +153,24 @@ function isReservedFileName(name) {
|
|
|
59
153
|
*/
|
|
60
154
|
export function resolveOkfLinks(body, fileRelPath) {
|
|
61
155
|
const dir = path.posix.dirname(toPosix(fileRelPath));
|
|
62
|
-
const
|
|
156
|
+
const definitions = new Map();
|
|
157
|
+
for (const match of body.matchAll(/^\s*\[([^\]]+)\]:\s*(\S+)/gm)) {
|
|
158
|
+
definitions.set(match[1].trim().toLowerCase(), match[2]);
|
|
159
|
+
}
|
|
160
|
+
const candidates = [];
|
|
161
|
+
for (const match of body.matchAll(/\[[^\]]*\]\(([^)]+)\)/g)) {
|
|
162
|
+
candidates.push({ index: match.index, target: match[1] });
|
|
163
|
+
}
|
|
164
|
+
for (const match of body.matchAll(/(?<!!)\[[^\]]*\]\[([^\]]+)\]/g)) {
|
|
165
|
+
const target = definitions.get(match[1].trim().toLowerCase());
|
|
166
|
+
if (target)
|
|
167
|
+
candidates.push({ index: match.index, target });
|
|
168
|
+
}
|
|
169
|
+
candidates.sort((a, b) => a.index - b.index);
|
|
63
170
|
const out = [];
|
|
64
171
|
const seen = new Set();
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
while ((match = linkRe.exec(body)) !== null) {
|
|
68
|
-
let target = match[1].trim();
|
|
172
|
+
for (const candidate of candidates) {
|
|
173
|
+
let target = candidate.target.trim();
|
|
69
174
|
// Drop an optional markdown link title: `[x](/a.md "Title")`.
|
|
70
175
|
const wsIdx = target.search(/\s/);
|
|
71
176
|
if (wsIdx >= 0)
|
|
@@ -127,8 +232,33 @@ function recognize(c, file) {
|
|
|
127
232
|
const name = nonEmptyString(data.title) ?? lastSegment;
|
|
128
233
|
const description = nonEmptyString(data.description);
|
|
129
234
|
const tags = readTags(data.tags);
|
|
130
|
-
const updated = nonEmptyString(data.timestamp);
|
|
131
235
|
const links = resolveOkfLinks(body, file.relPath);
|
|
236
|
+
// v0.2 trust/provenance/lifecycle families (§0.1, okf-support.md v0.2 note).
|
|
237
|
+
// `generated.at` — v0.2's replacement for `timestamp` — takes precedence;
|
|
238
|
+
// `timestamp` remains a fully valid fallback (the v0.2-permitted legacy
|
|
239
|
+
// reading, not merely tolerated). Both stay fully optional (never rejects).
|
|
240
|
+
const generatedMapping = isPlainObject(data.generated) ? data.generated : undefined;
|
|
241
|
+
const generatedAt = generatedMapping ? nonEmptyString(generatedMapping.at) : undefined;
|
|
242
|
+
const generatedBy = generatedMapping ? nonEmptyString(generatedMapping.by) : undefined;
|
|
243
|
+
const legacyTimestamp = nonEmptyString(data.timestamp);
|
|
244
|
+
const updated = generatedAt ?? legacyTimestamp;
|
|
245
|
+
const verified = parseVerified(data.verified);
|
|
246
|
+
const okfSources = parseOkfSources(data.sources);
|
|
247
|
+
const provenance = generatedBy !== undefined || generatedAt !== undefined || verified !== undefined || okfSources !== undefined
|
|
248
|
+
? {
|
|
249
|
+
...(generatedBy !== undefined ? { generatedBy } : {}),
|
|
250
|
+
...(generatedAt !== undefined ? { generatedAt } : {}),
|
|
251
|
+
...(verified !== undefined ? { verified } : {}),
|
|
252
|
+
...(okfSources !== undefined ? { sources: okfSources } : {}),
|
|
253
|
+
}
|
|
254
|
+
: undefined;
|
|
255
|
+
const lifecycleStatus = parseLifecycleStatus(data.status);
|
|
256
|
+
const staleAfter = nonEmptyString(data.stale_after);
|
|
257
|
+
// `okf_version` is upstream-declared only on a bundle-root `index.md` (never
|
|
258
|
+
// indexed as a concept, §5) — read defensively from any concept anyway
|
|
259
|
+
// (best-effort, conformance Rule 9); a producer that also tags ordinary
|
|
260
|
+
// concepts with it is neither rejected nor silently ignored.
|
|
261
|
+
const okfVersion = nonEmptyString(data.okf_version);
|
|
132
262
|
const doc = {
|
|
133
263
|
ref: `${c.id}//${conceptId}`,
|
|
134
264
|
bundle: c.id,
|
|
@@ -139,6 +269,7 @@ function recognize(c, file) {
|
|
|
139
269
|
// incrementality/fingerprints (`types.ts` hash doc comment).
|
|
140
270
|
hash: hashContent(raw),
|
|
141
271
|
adapterId: "okf",
|
|
272
|
+
ownsPresentation: true,
|
|
142
273
|
type,
|
|
143
274
|
name,
|
|
144
275
|
content: body.length > MAX_CONTENT_CHARS ? body.slice(0, MAX_CONTENT_CHARS) : body,
|
|
@@ -151,6 +282,17 @@ function recognize(c, file) {
|
|
|
151
282
|
doc.updated = updated;
|
|
152
283
|
if (links.length > 0)
|
|
153
284
|
doc.links = links;
|
|
285
|
+
if (provenance !== undefined)
|
|
286
|
+
doc.provenance = provenance;
|
|
287
|
+
if (lifecycleStatus !== undefined)
|
|
288
|
+
doc.lifecycleStatus = lifecycleStatus;
|
|
289
|
+
if (staleAfter !== undefined)
|
|
290
|
+
doc.staleAfter = staleAfter;
|
|
291
|
+
if (okfVersion !== undefined)
|
|
292
|
+
doc.okfVersion = okfVersion;
|
|
293
|
+
const extras = Object.fromEntries(Object.entries(data).filter(([key]) => !CONSUMED_FRONTMATTER_KEYS.includes(key)));
|
|
294
|
+
if (Object.keys(extras).length > 0)
|
|
295
|
+
doc.documentJson = extras;
|
|
154
296
|
return doc;
|
|
155
297
|
}
|
|
156
298
|
async function validate(c, changes, ctx) {
|
|
@@ -165,14 +307,11 @@ async function validate(c, changes, ctx) {
|
|
|
165
307
|
const fileName = relPath.split("/").pop() ?? relPath;
|
|
166
308
|
const reserved = isReservedFileName(fileName);
|
|
167
309
|
const parsed = parseFrontmatter(raw);
|
|
168
|
-
// Base checks
|
|
310
|
+
// Base checks shared with native AKM formats. OKF's timestamp is optional,
|
|
311
|
+
// so the AKM-specific freshness diagnostic is removed unconditionally.
|
|
169
312
|
const base = await runBaseValidateChecks(relPath, parsed, c.root, ctx);
|
|
170
|
-
// §0.1: for OKF content a `timestamp` satisfies the freshness requirement
|
|
171
|
-
// (the base-linter's `missing-updated` maps to `timestamp`). Suppress the
|
|
172
|
-
// `missing-updated` diagnostic when a non-empty `timestamp` is present.
|
|
173
|
-
const hasTimestamp = nonEmptyString(parsed.data.timestamp) !== undefined;
|
|
174
313
|
for (const diag of base) {
|
|
175
|
-
if (
|
|
314
|
+
if (diag.issue === "missing-updated")
|
|
176
315
|
continue;
|
|
177
316
|
diagnostics.push(diag);
|
|
178
317
|
}
|
|
@@ -208,16 +347,45 @@ export const okfAdapter = {
|
|
|
208
347
|
extensions: [".md"],
|
|
209
348
|
recognize,
|
|
210
349
|
validate,
|
|
211
|
-
/** `<c.root>/<conceptId>.md` (§5). */
|
|
212
|
-
placeNew(c, conceptId) {
|
|
213
|
-
return path.join(c.root, `${conceptId}.md`);
|
|
214
|
-
},
|
|
215
350
|
/** OKF concepts live anywhere under the component root (§5). */
|
|
216
351
|
directoryList(_c) {
|
|
217
352
|
return ["."];
|
|
218
353
|
},
|
|
219
|
-
/**
|
|
354
|
+
/**
|
|
355
|
+
* Install-time probe. A root index is sufficient; an index-less root is also
|
|
356
|
+
* OKF when it contains at least one conformant concept with a non-empty open
|
|
357
|
+
* `type`. More-specific native adapters run before this portable baseline.
|
|
358
|
+
*/
|
|
220
359
|
looksLikeRoot(root) {
|
|
221
|
-
|
|
360
|
+
if (fs.existsSync(path.join(root, "index.md")))
|
|
361
|
+
return true;
|
|
362
|
+
const stack = [root];
|
|
363
|
+
while (stack.length > 0) {
|
|
364
|
+
const current = stack.pop();
|
|
365
|
+
if (!current)
|
|
366
|
+
continue;
|
|
367
|
+
let entries;
|
|
368
|
+
try {
|
|
369
|
+
entries = fs.readdirSync(current, { withFileTypes: true });
|
|
370
|
+
}
|
|
371
|
+
catch {
|
|
372
|
+
continue;
|
|
373
|
+
}
|
|
374
|
+
for (const entry of entries) {
|
|
375
|
+
if (entry.isSymbolicLink() || entry.name === ".git")
|
|
376
|
+
continue;
|
|
377
|
+
const absolute = path.join(current, entry.name);
|
|
378
|
+
if (entry.isDirectory()) {
|
|
379
|
+
stack.push(absolute);
|
|
380
|
+
continue;
|
|
381
|
+
}
|
|
382
|
+
if (!entry.isFile() || !entry.name.toLowerCase().endsWith(".md") || isReservedFileName(entry.name))
|
|
383
|
+
continue;
|
|
384
|
+
const type = nonEmptyString(parseFrontmatter(fs.readFileSync(absolute, "utf8")).data.type);
|
|
385
|
+
if (type)
|
|
386
|
+
return true;
|
|
387
|
+
}
|
|
388
|
+
}
|
|
389
|
+
return false;
|
|
222
390
|
},
|
|
223
391
|
};
|
|
@@ -49,6 +49,7 @@
|
|
|
49
49
|
*/
|
|
50
50
|
import { createHash } from "node:crypto";
|
|
51
51
|
import { parseFrontmatter } from "../../asset/frontmatter.js";
|
|
52
|
+
import { checkUnquotedDescriptionColon } from "../../asset/frontmatter-lint.js";
|
|
52
53
|
// ── Small pure helpers, reused across the concrete adapters ──────────────────
|
|
53
54
|
/** Content hash feeding `IndexDocument.hash` (incrementality/fingerprints, `types.ts` doc comment). */
|
|
54
55
|
export function hashContent(content) {
|
|
@@ -69,23 +70,6 @@ export function readTags(value) {
|
|
|
69
70
|
return tags.length > 0 ? tags : undefined;
|
|
70
71
|
}
|
|
71
72
|
// ── Base validate checks (port of `BaseLinter.runBaseChecks`) ────────────────
|
|
72
|
-
function checkUnquotedColon(frontmatterText) {
|
|
73
|
-
if (!frontmatterText)
|
|
74
|
-
return null;
|
|
75
|
-
for (const line of frontmatterText.split(/\r?\n/)) {
|
|
76
|
-
const match = line.match(/^description:\s*(.*)/);
|
|
77
|
-
if (!match)
|
|
78
|
-
continue;
|
|
79
|
-
const value = match[1].trim();
|
|
80
|
-
if ((value.startsWith('"') && value.endsWith('"')) || (value.startsWith("'") && value.endsWith("'"))) {
|
|
81
|
-
return null;
|
|
82
|
-
}
|
|
83
|
-
if (value.includes(":")) {
|
|
84
|
-
return `description value contains unquoted colon: ${value}`;
|
|
85
|
-
}
|
|
86
|
-
}
|
|
87
|
-
return null;
|
|
88
|
-
}
|
|
89
73
|
function checkMissingUpdated(data, frontmatterText) {
|
|
90
74
|
return frontmatterText !== null && !("updated" in data);
|
|
91
75
|
}
|
|
@@ -207,7 +191,7 @@ function readRefStringOrArray(value) {
|
|
|
207
191
|
export async function runBaseValidateChecks(relPath, parsed, componentRoot, ctx) {
|
|
208
192
|
const diagnostics = [];
|
|
209
193
|
const { data, content: body, frontmatter } = parsed;
|
|
210
|
-
const unquotedColonDetail =
|
|
194
|
+
const unquotedColonDetail = checkUnquotedDescriptionColon(frontmatter);
|
|
211
195
|
if (unquotedColonDetail) {
|
|
212
196
|
diagnostics.push({ file: relPath, issue: "unquoted-colon", detail: unquotedColonDetail, fixed: false });
|
|
213
197
|
}
|
|
@@ -228,7 +212,7 @@ export async function runBaseValidateChecks(relPath, parsed, componentRoot, ctx)
|
|
|
228
212
|
if (found !== null)
|
|
229
213
|
continue;
|
|
230
214
|
const portableHint = candidate.startsWith(componentRoot)
|
|
231
|
-
? ` (portable form: $
|
|
215
|
+
? ` (portable form: $AKM_BUNDLE_DIR${candidate.slice(componentRoot.length)})`
|
|
232
216
|
: "";
|
|
233
217
|
diagnostics.push({
|
|
234
218
|
file: relPath,
|
|
@@ -41,9 +41,12 @@
|
|
|
41
41
|
* ── Cycle-safety ──
|
|
42
42
|
*
|
|
43
43
|
* Imported only by `claude-adapter.ts` / `opencode-adapter.ts` (themselves
|
|
44
|
-
* imported
|
|
45
|
-
*
|
|
46
|
-
*
|
|
44
|
+
* imported by the `adapters/index.ts` barrel — NOT test-only: `core/adapter/
|
|
45
|
+
* registry.ts:33` imports it in production for the frozen `BUILTIN_ADAPTERS`
|
|
46
|
+
* list). This leaf still can never gain an inbound edge from a cycle
|
|
47
|
+
* participant, since nothing on that import chain imports back into it. It
|
|
48
|
+
* value-imports only pure leaves (`shared`, `akm-lint`, `frontmatter`) plus
|
|
49
|
+
* Node builtins.
|
|
47
50
|
*/
|
|
48
51
|
import path from "node:path";
|
|
49
52
|
import { parseFrontmatter } from "../../asset/frontmatter.js";
|
|
@@ -121,6 +124,8 @@ export function recognizeToolDir(layout, c, file) {
|
|
|
121
124
|
name,
|
|
122
125
|
content: body.length > MAX_CONTENT_CHARS ? body.slice(0, MAX_CONTENT_CHARS) : body,
|
|
123
126
|
};
|
|
127
|
+
if (cls.type === "instruction")
|
|
128
|
+
doc.ownsPresentation = true;
|
|
124
129
|
if (description !== undefined)
|
|
125
130
|
doc.description = description;
|
|
126
131
|
if (tags !== undefined)
|
|
@@ -180,9 +185,11 @@ export async function validateToolDir(layout, c, changes, ctx) {
|
|
|
180
185
|
continue;
|
|
181
186
|
const relPath = toPosix(change.path);
|
|
182
187
|
// The one coded skill check (missing-skill-md) fires on ANY change under a
|
|
183
|
-
//
|
|
184
|
-
// mirrors the akm adapter's per-change SkillLinter.lintDirectory
|
|
185
|
-
|
|
188
|
+
// `<skillDir>/<name>/…` package (self-gated + deduped), even a bundled
|
|
189
|
+
// resource — mirrors the akm adapter's per-change SkillLinter.lintDirectory
|
|
190
|
+
// pass. `layout.skillDirs` is passed so opencode's singular `skill/` alias
|
|
191
|
+
// is checked identically to `skills/` (issue #774).
|
|
192
|
+
diagnostics.push(...(await skillDirectoryDiagnostics(relPath, seenSkillDirs, ctx, layout.skillDirs)));
|
|
186
193
|
const cls = classify(change.path, layout);
|
|
187
194
|
if (cls === null)
|
|
188
195
|
continue;
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
// This Source Code Form is subject to the terms of the Mozilla Public
|
|
2
|
+
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
3
|
+
// file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
|
4
|
+
import { getAdapters } from "./registry.js";
|
|
5
|
+
/** Select the first built-in adapter whose ordered root probe claims `root`. */
|
|
6
|
+
export function detectAdapterId(root, fallback = "akm") {
|
|
7
|
+
for (const adapter of getAdapters()) {
|
|
8
|
+
try {
|
|
9
|
+
if (adapter.looksLikeRoot?.(root) === true)
|
|
10
|
+
return adapter.id;
|
|
11
|
+
}
|
|
12
|
+
catch {
|
|
13
|
+
// An unreadable or racing probe does not claim the bundle.
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
return fallback;
|
|
17
|
+
}
|
|
@@ -1,19 +1,21 @@
|
|
|
1
1
|
// This Source Code Form is subject to the terms of the Mozilla Public
|
|
2
2
|
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
3
3
|
// file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
|
4
|
-
import { directoryMatcher, extensionMatcher, parentDirHintMatcher, smartMdMatcher
|
|
4
|
+
import { directoryMatcher, extensionMatcher, parentDirHintMatcher, smartMdMatcher } from "../../indexer/walk/matchers.js";
|
|
5
5
|
/**
|
|
6
|
-
* The
|
|
6
|
+
* The four builtin matchers, in registration order. The array index IS the
|
|
7
7
|
* registration index `runMatchers` uses for tie-breaking. (The `wiki` matcher
|
|
8
8
|
* was removed in chunk 4 — the wiki asset-type is retired; LLM Wiki content is
|
|
9
|
-
* served by the first-class `llm-wiki` adapter, not the akm adapter.
|
|
9
|
+
* served by the first-class `llm-wiki` adapter, not the akm adapter. The YAML
|
|
10
|
+
* workflow-program matcher was removed by workflow-format-unification — one
|
|
11
|
+
* workflow format now, recognized by frontmatter `type: workflow` or
|
|
12
|
+
* residence under `workflows/`, both already covered by the remaining four.)
|
|
10
13
|
*/
|
|
11
14
|
const AKM_MATCHERS = [
|
|
12
15
|
extensionMatcher,
|
|
13
16
|
directoryMatcher,
|
|
14
17
|
parentDirHintMatcher,
|
|
15
18
|
smartMdMatcher,
|
|
16
|
-
workflowProgramMatcher,
|
|
17
19
|
];
|
|
18
20
|
/**
|
|
19
21
|
* Synchronous reproduction of `file-context.ts#runMatchers`'s arbitration
|
|
@@ -0,0 +1,214 @@
|
|
|
1
|
+
// This Source Code Form is subject to the terms of the Mozilla Public
|
|
2
|
+
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
3
|
+
// file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
|
4
|
+
/**
|
|
5
|
+
* The ONE core {@link ValidateContext} implementation — akm 0.9.0 chunk-2/lint
|
|
6
|
+
* follow-up ("make `validate()` genuinely load-bearing").
|
|
7
|
+
*
|
|
8
|
+
* `BundleAdapter.validate()` (`./bundle-adapter.ts`) is a REQUIRED interface
|
|
9
|
+
* method whose contract is explicit and normative (`./bundle-adapter.ts:96-100`
|
|
10
|
+
* / the format-neutral spec §12.1): the adapter MUST NOT write and MUST NOT
|
|
11
|
+
* read the live filesystem — `ctx` serves the run's on-disk SNAPSHOT WITH the
|
|
12
|
+
* caller's pending {@link FileChange}s overlaid, plus a read-only `resolveRef`
|
|
13
|
+
* for link/xref existence. That overlay is meant to be built ONCE, centrally —
|
|
14
|
+
* "one core overlay implementation, not one per adapter" (the interface's own
|
|
15
|
+
* words) — so every `validate()` caller shares identical overlay semantics
|
|
16
|
+
* rather than each hand-rolling its own (as every adapter's own test suite has
|
|
17
|
+
* done up to now, see e.g. `tests/core/adapter/okf-adapter.test.ts`'s
|
|
18
|
+
* `overlayCtx`/`diskCtx`).
|
|
19
|
+
*
|
|
20
|
+
* The two production callers wired onto this factory:
|
|
21
|
+
* - `akm lint`'s non-akm adapter dispatch (`commands/lint/index.ts`) — a
|
|
22
|
+
* plain sweep with NO pending changes, so the overlay is empty and every
|
|
23
|
+
* read falls straight through to disk.
|
|
24
|
+
* - the proposal promotion preflight (`commands/proposal/repository.ts`
|
|
25
|
+
* `preflightProposalPromotion`) — the one proposal's own `changes` are the
|
|
26
|
+
* overlay, so `validate()` sees the bundle AS IT WOULD LOOK the instant
|
|
27
|
+
* after the transaction commits, without that transaction ever touching
|
|
28
|
+
* disk.
|
|
29
|
+
*
|
|
30
|
+
* ── resolveRef ──
|
|
31
|
+
*
|
|
32
|
+
* A bare (unqualified) `conceptId` is resolved as BOTH:
|
|
33
|
+
* 1. the AKM placement-derived path (`typeNameFromConceptId` + `stashDirFor`
|
|
34
|
+
* + `assetPathForName` — the exact derivation `commands/lint/base-linter.ts
|
|
35
|
+
* #refToRelPath` already uses for the akm-native `missing-ref` check); and
|
|
36
|
+
* 2. the DIRECT component-relative spelling (`<conceptId>.md`, and the bare
|
|
37
|
+
* `<conceptId>` for an extensionless asset) — the form every non-akm
|
|
38
|
+
* adapter's OWN conceptId already IS (OKF: path − `.md`; llm-wiki: same).
|
|
39
|
+
* A `bundle//conceptId`-qualified ref has its bundle prefix stripped before
|
|
40
|
+
* resolution — mirroring `base-linter.ts#classifyConceptRef`'s existing
|
|
41
|
+
* behavior (the legacy resolver has never scoped-by-bundle-name either; it
|
|
42
|
+
* searches every configured stash root for the bare conceptId). Existence is
|
|
43
|
+
* checked against the PRIMARY root (with the pending-changes overlay applied)
|
|
44
|
+
* first, then each extra root (disk only — a pending transaction never
|
|
45
|
+
* targets more than one bundle).
|
|
46
|
+
*/
|
|
47
|
+
import fs from "node:fs";
|
|
48
|
+
import path from "node:path";
|
|
49
|
+
import { assetPathForName, stashDirFor } from "../asset/asset-placement.js";
|
|
50
|
+
import { typeNameFromConceptId } from "../asset/resolve-ref.js";
|
|
51
|
+
function toPosix(p) {
|
|
52
|
+
return p.replace(/\\/g, "/");
|
|
53
|
+
}
|
|
54
|
+
/** Build the overlay map, keyed by POSIX path relative to `root`. */
|
|
55
|
+
function buildOverlay(root, changes) {
|
|
56
|
+
const overlay = new Map();
|
|
57
|
+
for (const change of changes) {
|
|
58
|
+
const relKey = toPosix(path.isAbsolute(change.path) ? path.relative(root, change.path) : change.path);
|
|
59
|
+
if (!relKey || relKey.startsWith(".."))
|
|
60
|
+
continue; // outside root — not this context's concern
|
|
61
|
+
if (change.op === "delete") {
|
|
62
|
+
overlay.set(relKey, null);
|
|
63
|
+
continue;
|
|
64
|
+
}
|
|
65
|
+
if (change.after !== undefined)
|
|
66
|
+
overlay.set(relKey, change.after);
|
|
67
|
+
}
|
|
68
|
+
return overlay;
|
|
69
|
+
}
|
|
70
|
+
function readDisk(absPath) {
|
|
71
|
+
try {
|
|
72
|
+
return fs.readFileSync(absPath, "utf8");
|
|
73
|
+
}
|
|
74
|
+
catch {
|
|
75
|
+
return null;
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
function existsOnDiskOrOverlay(relPath, root, overlay) {
|
|
79
|
+
const key = toPosix(relPath);
|
|
80
|
+
// Overlay entries are pending file contents by construction, so a present
|
|
81
|
+
// non-null entry is always a file.
|
|
82
|
+
if (overlay?.has(key))
|
|
83
|
+
return overlay.get(key) !== null;
|
|
84
|
+
try {
|
|
85
|
+
// `isFile()`, not `existsSync()`: a ref naming a DIRECTORY (e.g. a
|
|
86
|
+
// `pages/foo/` dir alongside no `pages/foo.md`) must not count as a
|
|
87
|
+
// resolved target — that would silently suppress a real
|
|
88
|
+
// `missing-ref`/`broken-xref` diagnostic.
|
|
89
|
+
return fs.statSync(path.join(root, relPath)).isFile();
|
|
90
|
+
}
|
|
91
|
+
catch {
|
|
92
|
+
return false;
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
/**
|
|
96
|
+
* Refs come from bundle CONTENT (link targets, xrefs, `sources:` entries), so
|
|
97
|
+
* they are untrusted input. A ref must stay inside the bundle root: reject
|
|
98
|
+
* absolute paths and any `..` segment before joining, so content can never
|
|
99
|
+
* probe for existence outside its own bundle (nor report a resolved `path`
|
|
100
|
+
* pointing there). Mirrors the `isWithin` containment rule the write path
|
|
101
|
+
* already enforces in `core/write-source.ts`.
|
|
102
|
+
*/
|
|
103
|
+
function refEscapesBundle(conceptId) {
|
|
104
|
+
if (path.isAbsolute(conceptId) || conceptId.startsWith("/"))
|
|
105
|
+
return true;
|
|
106
|
+
return toPosix(conceptId)
|
|
107
|
+
.split("/")
|
|
108
|
+
.some((segment) => segment === "..");
|
|
109
|
+
}
|
|
110
|
+
/**
|
|
111
|
+
* Every on-disk relative path a bare `conceptId` might resolve to: the AKM
|
|
112
|
+
* placement-derived path (when the leading segment names a known placement
|
|
113
|
+
* stash-subdir) and the direct component-relative spellings every non-akm
|
|
114
|
+
* adapter's own conceptId already uses. Order doesn't matter — the caller
|
|
115
|
+
* checks all of them.
|
|
116
|
+
*/
|
|
117
|
+
function candidateRelPaths(conceptId) {
|
|
118
|
+
const candidates = [];
|
|
119
|
+
const parts = typeNameFromConceptId(conceptId);
|
|
120
|
+
if (parts !== undefined) {
|
|
121
|
+
const typeDir = stashDirFor(parts.type);
|
|
122
|
+
if (typeDir !== undefined)
|
|
123
|
+
candidates.push(assetPathForName(parts.type, typeDir, parts.name));
|
|
124
|
+
}
|
|
125
|
+
candidates.push(`${conceptId}.md`);
|
|
126
|
+
candidates.push(conceptId);
|
|
127
|
+
return candidates;
|
|
128
|
+
}
|
|
129
|
+
/** Strip an optional `#fragment` (export selector) — never part of the on-disk identity. */
|
|
130
|
+
function stripFragment(ref) {
|
|
131
|
+
const hashIdx = ref.indexOf("#");
|
|
132
|
+
return hashIdx >= 0 ? ref.slice(0, hashIdx) : ref;
|
|
133
|
+
}
|
|
134
|
+
/**
|
|
135
|
+
* Strip an optional `bundle//` qualifier. Mirrors `base-linter.ts
|
|
136
|
+
* #classifyConceptRef`'s existing behavior: the legacy missing-ref checker has
|
|
137
|
+
* never resolved a qualifier against a NAMED bundle either — it searches every
|
|
138
|
+
* configured stash root for the bare conceptId. Keeping that same leniency
|
|
139
|
+
* here means this resolver's answers agree with today's `akm lint` for every
|
|
140
|
+
* ref shape that already worked.
|
|
141
|
+
*/
|
|
142
|
+
function stripBundlePrefix(ref) {
|
|
143
|
+
const boundary = ref.indexOf("//");
|
|
144
|
+
return boundary >= 0 ? ref.slice(boundary + 2) : ref;
|
|
145
|
+
}
|
|
146
|
+
/**
|
|
147
|
+
* Build the ONE core {@link ValidateContext}: reads/lookups served from the
|
|
148
|
+
* on-disk snapshot rooted at `options.root` (+ `options.extraRoots` for
|
|
149
|
+
* cross-bundle ref existence) WITH `options.changes` overlaid on `options.root`
|
|
150
|
+
* — never a write, never a live-FS read beyond that snapshot.
|
|
151
|
+
*/
|
|
152
|
+
export function createValidateContext(options) {
|
|
153
|
+
const root = options.root;
|
|
154
|
+
const extraRoots = options.extraRoots ?? [];
|
|
155
|
+
const overlay = buildOverlay(root, options.changes ?? []);
|
|
156
|
+
async function readFile(p) {
|
|
157
|
+
// An absolute candidate (e.g. a `stale-path` scan hit, which is always an
|
|
158
|
+
// absolute host path) bypasses the overlay — it can never name a pending
|
|
159
|
+
// change's stash-relative path, and reads straight from disk (the base
|
|
160
|
+
// checks' existing "does this literal absolute path exist" question).
|
|
161
|
+
if (path.isAbsolute(p))
|
|
162
|
+
return readDisk(p);
|
|
163
|
+
const key = toPosix(p);
|
|
164
|
+
if (overlay.has(key))
|
|
165
|
+
return overlay.get(key) ?? null;
|
|
166
|
+
return readDisk(path.join(root, p));
|
|
167
|
+
}
|
|
168
|
+
async function list(dir) {
|
|
169
|
+
const abs = path.isAbsolute(dir) ? dir : path.join(root, dir);
|
|
170
|
+
const relDir = toPosix(path.isAbsolute(dir) ? path.relative(root, dir) : dir);
|
|
171
|
+
const names = new Set();
|
|
172
|
+
try {
|
|
173
|
+
for (const entry of fs.readdirSync(abs))
|
|
174
|
+
names.add(entry);
|
|
175
|
+
}
|
|
176
|
+
catch {
|
|
177
|
+
// directory may not exist on disk yet — the overlay can still populate it
|
|
178
|
+
}
|
|
179
|
+
const prefix = relDir === "." || relDir === "" ? "" : `${relDir}/`;
|
|
180
|
+
for (const [key, value] of overlay) {
|
|
181
|
+
if (prefix.length > 0 && !key.startsWith(prefix))
|
|
182
|
+
continue;
|
|
183
|
+
const rest = prefix.length > 0 ? key.slice(prefix.length) : key;
|
|
184
|
+
if (rest.length === 0 || rest.includes("/"))
|
|
185
|
+
continue; // only direct children
|
|
186
|
+
if (value === null)
|
|
187
|
+
names.delete(rest);
|
|
188
|
+
else
|
|
189
|
+
names.add(rest);
|
|
190
|
+
}
|
|
191
|
+
return [...names];
|
|
192
|
+
}
|
|
193
|
+
async function resolveRef(ref) {
|
|
194
|
+
const conceptId = stripBundlePrefix(stripFragment(ref)).trim();
|
|
195
|
+
if (!conceptId)
|
|
196
|
+
return { exists: false };
|
|
197
|
+
if (refEscapesBundle(conceptId))
|
|
198
|
+
return { exists: false };
|
|
199
|
+
const candidates = candidateRelPaths(conceptId);
|
|
200
|
+
const roots = [
|
|
201
|
+
{ root, usesOverlay: true },
|
|
202
|
+
...extraRoots.map((r) => ({ root: r, usesOverlay: false })),
|
|
203
|
+
];
|
|
204
|
+
for (const { root: candidateRoot, usesOverlay } of roots) {
|
|
205
|
+
for (const relPath of candidates) {
|
|
206
|
+
if (existsOnDiskOrOverlay(relPath, candidateRoot, usesOverlay ? overlay : null)) {
|
|
207
|
+
return { exists: true, path: path.join(candidateRoot, relPath) };
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
return { exists: false };
|
|
212
|
+
}
|
|
213
|
+
return { readFile, list, resolveRef };
|
|
214
|
+
}
|