akm-cli 0.9.0-rc.4 → 0.9.0-rc.6
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 +122 -26
- package/README.md +7 -7
- package/SECURITY.md +11 -10
- package/dist/akm +2 -2
- package/dist/akm-migrate-storage +2 -2
- package/dist/assets/hints/cli-hints-full.md +60 -73
- package/dist/assets/hints/cli-hints-short.md +8 -12
- package/dist/assets/improve-strategies/default.json +5 -7
- package/dist/assets/improve-strategies/frequent.json +2 -2
- package/dist/assets/improve-strategies/proactive-maintenance.json +2 -2
- package/dist/assets/improve-strategies/quick.json +1 -1
- package/dist/assets/improve-strategies/reflect-distill.json +3 -3
- package/dist/assets/improve-strategies/thorough.json +1 -1
- package/dist/assets/prompts/consolidate-system.md +5 -5
- package/dist/assets/prompts/extract-session.md +2 -6
- package/dist/assets/prompts/reflect-llm-framed-contract.md +11 -0
- package/dist/assets/prompts/reflect-llm-schema-contract.md +3 -0
- package/dist/assets/prompts/reflect-output-repair.md +3 -0
- package/dist/assets/stash-skeleton/README.md +10 -11
- package/dist/assets/stash-skeleton/facts/conventions/assets/agent.md +1 -1
- package/dist/assets/stash-skeleton/facts/conventions/assets/fact.md +1 -1
- package/dist/assets/stash-skeleton/facts/conventions/assets/knowledge.md +1 -1
- package/dist/assets/stash-skeleton/facts/conventions/assets/lesson.md +2 -2
- package/dist/assets/stash-skeleton/facts/conventions/assets/memory.md +2 -2
- package/dist/assets/stash-skeleton/facts/conventions/assets/script.md +1 -1
- package/dist/assets/stash-skeleton/facts/conventions/assets/skill.md +2 -2
- package/dist/assets/stash-skeleton/facts/conventions/assets/workflow.md +1 -1
- package/dist/assets/stash-skeleton/facts/conventions/backlinks.md +18 -18
- package/dist/assets/stash-skeleton/facts/conventions/domains.md +8 -9
- package/dist/assets/stash-skeleton/facts/conventions/organization.md +13 -15
- package/dist/assets/tasks/core/extract.yml +2 -2
- package/dist/assets/tasks/core/improve.yml +1 -1
- package/dist/assets/templates/html/health.html +2 -3
- package/dist/cli/config-migrate.js +1328 -101
- package/dist/cli/invocation.js +255 -0
- package/dist/cli/parse-args.js +20 -83
- package/dist/cli/shared.js +15 -37
- package/dist/cli.js +18 -49
- package/dist/commands/agent/agent-dispatch.js +2 -2
- package/dist/commands/agent/contribute-cli.js +5 -4
- package/dist/commands/bundle/bundle-cli.js +68 -0
- package/dist/commands/bundle/bundle.js +219 -0
- package/dist/commands/completions.js +2 -2
- package/dist/commands/config-cli.js +6 -3
- package/dist/commands/env/env-binding.js +13 -9
- package/dist/commands/env/env-cli.js +49 -48
- package/dist/commands/env/secret-cli.js +24 -10
- package/dist/commands/events.js +2 -2
- package/dist/commands/feedback-cli.js +59 -33
- package/dist/commands/graph/graph.js +6 -4
- package/dist/commands/health/advisories.js +4 -4
- package/dist/commands/health/html-report.js +190 -593
- package/dist/commands/health/improve-metrics.js +240 -188
- package/dist/commands/health/metrics.js +9 -17
- package/dist/commands/health/report-view-model.js +509 -0
- package/dist/commands/health/surfaces.js +6 -3
- package/dist/commands/health/types-checks.js +4 -0
- package/dist/commands/health/types-improve.js +22 -0
- package/dist/commands/health/types-metrics.js +4 -0
- package/dist/commands/health/types-result.js +7 -0
- package/dist/commands/health/types-runs.js +4 -0
- package/dist/commands/health/types-session-log.js +4 -0
- package/dist/commands/health/types-windows.js +4 -0
- package/dist/commands/health/types.js +26 -21
- package/dist/commands/health/windows.js +1 -2
- package/dist/commands/health.js +219 -161
- package/dist/commands/improve/collapse-detector.js +25 -14
- package/dist/commands/improve/consolidate/chunking.js +7 -5
- package/dist/commands/improve/consolidate.js +640 -740
- package/dist/commands/improve/content-hash.js +39 -0
- package/dist/commands/improve/distill/content-repair.js +4 -10
- package/dist/commands/improve/distill/promote-memory.js +79 -49
- package/dist/commands/improve/distill/quality-gate.js +113 -35
- package/dist/commands/improve/distill-promotion-policy.js +24 -885
- package/dist/commands/improve/distill.js +565 -337
- package/dist/commands/improve/eligibility.js +75 -36
- package/dist/commands/improve/extract-cli.js +6 -6
- package/dist/commands/improve/extract-prompt.js +5 -34
- package/dist/commands/improve/extract.js +526 -357
- package/dist/commands/improve/feedback-valence.js +2 -12
- package/dist/commands/improve/improve-cli.js +11 -20
- package/dist/commands/improve/improve-result-file.js +7 -6
- package/dist/commands/improve/improve-run-types.js +4 -0
- package/dist/commands/improve/improve-strategies.js +3 -9
- package/dist/commands/improve/improve.js +822 -727
- package/dist/commands/improve/locks.js +27 -95
- package/dist/commands/improve/loop-stages.js +941 -963
- package/dist/commands/improve/memory/derived-ref.js +122 -0
- package/dist/commands/improve/memory/memory-contradiction-detect.js +35 -38
- package/dist/commands/improve/memory/memory-improve.js +24 -37
- package/dist/commands/improve/outcome-loop.js +13 -33
- package/dist/commands/improve/preparation.js +889 -691
- package/dist/commands/improve/proactive-maintenance.js +37 -9
- package/dist/commands/improve/proposal-envelope.js +31 -0
- package/dist/commands/improve/reflect.js +974 -588
- package/dist/commands/improve/run-context.js +119 -0
- package/dist/commands/improve/salience.js +8 -4
- package/dist/commands/improve/session-asset.js +7 -3
- package/dist/commands/improve/shared.js +14 -40
- package/dist/commands/improve/source-identity.js +39 -26
- package/dist/commands/improve/triage.js +20 -17
- package/dist/commands/lint/base-linter.js +288 -295
- package/dist/commands/lint/index.js +132 -19
- package/dist/commands/mv-cli.js +257 -234
- package/dist/commands/observability-cli.js +2 -1
- package/dist/commands/proposal/diff-format.js +50 -0
- package/dist/commands/proposal/drain-policies.js +0 -6
- package/dist/commands/proposal/drain.js +17 -16
- package/dist/commands/proposal/proposal-cli.js +40 -77
- package/dist/commands/proposal/proposal-types.js +56 -0
- package/dist/commands/proposal/proposal.js +39 -1
- package/dist/commands/proposal/propose.js +41 -19
- package/dist/commands/proposal/repository.js +566 -486
- package/dist/commands/proposal/validators/proposal-quality-validators.js +22 -6
- package/dist/commands/proposal/validators/proposal-validators.js +6 -5
- package/dist/commands/proposal/validators/proposals.js +5 -4
- package/dist/commands/read/curate.js +50 -28
- package/dist/commands/read/knowledge.js +25 -16
- package/dist/commands/read/remember-cli.js +12 -4
- package/dist/commands/read/search-cli.js +33 -33
- package/dist/commands/read/search.js +68 -55
- package/dist/commands/read/show.js +54 -135
- package/dist/commands/remember.js +7 -5
- package/dist/commands/sources/add-cli.js +10 -24
- package/dist/commands/sources/bundle-config-ops.js +58 -0
- package/dist/commands/sources/history.js +13 -8
- package/dist/commands/sources/info.js +10 -11
- package/dist/commands/sources/init.js +12 -21
- package/dist/commands/sources/installed-stashes.js +151 -138
- package/dist/commands/sources/schema-repair.js +5 -3
- package/dist/commands/sources/self-update.js +9 -7
- package/dist/commands/sources/source-add.js +116 -154
- package/dist/commands/sources/source-clone.js +33 -14
- package/dist/commands/sources/source-manage.js +43 -32
- package/dist/commands/sources/sources-cli.js +12 -7
- package/dist/commands/sources/stash-cli.js +12 -5
- package/dist/commands/sources/stash-skeleton.js +1 -1
- package/dist/commands/tasks/default-tasks.js +13 -13
- package/dist/commands/tasks/tasks-cli.js +3 -3
- package/dist/commands/tasks/tasks.js +71 -31
- package/dist/commands/workflow-cli.js +22 -26
- package/dist/core/action-contributors.js +1 -1
- package/dist/core/activation-policy.js +49 -0
- package/dist/core/adapter/adapters/agent-skills-adapter.js +181 -0
- package/dist/core/adapter/adapters/akm-adapter.js +509 -0
- package/dist/core/adapter/adapters/akm-lint.js +381 -0
- package/dist/core/adapter/adapters/akm-metadata.js +416 -0
- package/dist/core/adapter/adapters/akm-task-adapter.js +150 -0
- package/dist/core/adapter/adapters/akm-workflow-adapter.js +196 -0
- package/dist/core/adapter/adapters/claude-adapter.js +61 -0
- package/dist/core/adapter/adapters/dotenv-adapter.js +187 -0
- package/dist/core/adapter/adapters/generic-files-adapter.js +117 -0
- package/dist/core/adapter/adapters/index.js +80 -0
- package/dist/core/adapter/adapters/llm-wiki-adapter.js +413 -0
- package/dist/core/adapter/adapters/okf-adapter.js +223 -0
- package/dist/core/adapter/adapters/opencode-adapter.js +68 -0
- package/dist/core/adapter/adapters/shared.js +286 -0
- package/dist/core/adapter/adapters/tool-dir-shared.js +212 -0
- package/dist/core/adapter/adapters/website-snapshot-adapter.js +154 -0
- package/dist/core/adapter/bundle-adapter.js +4 -0
- package/dist/core/adapter/recognize-match.js +42 -0
- package/dist/core/adapter/registry.js +56 -0
- package/dist/core/adapter/types.js +4 -0
- package/dist/core/asset/asset-placement.js +229 -0
- package/dist/core/asset/asset-ref.js +107 -78
- package/dist/core/asset/asset-serialize.js +20 -0
- package/dist/core/asset/frontmatter.js +1 -1
- package/dist/core/asset/resolve-ref.js +223 -0
- package/dist/core/asset/stash-meta.js +1 -1
- package/dist/core/common.js +141 -49
- package/dist/core/config/config-io.js +0 -108
- package/dist/core/config/config-schema.js +94 -1039
- package/dist/core/config/config-sources.js +115 -36
- package/dist/core/config/config-types.js +16 -11
- package/dist/core/config/config-walker.js +4 -2
- package/dist/core/config/config.js +21 -26
- package/dist/core/config/engine-semantics.js +0 -4
- package/dist/core/config/schema/embedding.js +38 -0
- package/dist/core/config/schema/engines.js +116 -0
- package/dist/core/config/schema/feedback.js +31 -0
- package/dist/core/config/schema/improve-processes.js +395 -0
- package/dist/core/config/schema/improve.js +76 -0
- package/dist/core/config/schema/index-config.js +183 -0
- package/dist/core/config/schema/output.js +15 -0
- package/dist/core/config/schema/primitives.js +94 -0
- package/dist/core/config/schema/search.js +30 -0
- package/dist/core/config/schema/setup.js +27 -0
- package/dist/core/config/schema/sources-bundles.js +164 -0
- package/dist/core/config/schema/workflow.js +29 -0
- package/dist/core/env-secret-ref.js +113 -8
- package/dist/core/errors.js +1 -1
- package/dist/core/events-types.js +4 -0
- package/dist/core/extra-params.js +1 -0
- package/dist/core/file-change.js +16 -0
- package/dist/core/fs-txn.js +316 -0
- package/dist/core/git-message.js +59 -0
- package/dist/core/improve-result.js +9 -1
- package/dist/core/logs-db.js +1 -1
- package/dist/core/maintenance-barrier.js +16 -0
- package/dist/core/migration-backup.js +332 -160
- package/dist/core/paths.js +3 -6
- package/dist/core/platform.js +10 -0
- package/dist/core/recognition-util.js +130 -0
- package/dist/core/redaction.js +2 -2
- package/dist/core/standards/resolve-standards-context.js +47 -64
- package/dist/core/standards/resolve-type-conventions.js +3 -3
- package/dist/core/state/migrations.js +201 -8
- package/dist/core/state-db.js +38 -2
- package/dist/core/subprocess.js +303 -0
- package/dist/core/time.js +20 -0
- package/dist/core/type-presentation.js +130 -0
- package/dist/core/write-source.js +39 -67
- package/dist/indexer/bundle-identity-guard.js +91 -0
- package/dist/indexer/db/graph-db.js +1 -1
- package/dist/indexer/db/llm-cache.js +1 -1
- package/dist/indexer/ensure-index.js +29 -9
- package/dist/indexer/graph/graph-boost.js +28 -24
- package/dist/indexer/graph/graph-extraction.js +5 -6
- package/dist/indexer/graph/graph-types.js +4 -0
- package/dist/indexer/index-written-assets.js +44 -17
- package/dist/indexer/indexer.js +289 -153
- package/dist/indexer/init.js +18 -25
- package/dist/indexer/installations.js +224 -0
- package/dist/indexer/passes/dir-staleness.js +2 -1
- package/dist/indexer/passes/memory-inference.js +8 -4
- package/dist/indexer/passes/metadata.js +96 -243
- package/dist/indexer/scan/doc-to-entry.js +123 -0
- package/dist/indexer/scan/drain-dir.js +144 -0
- package/dist/indexer/search/db-search.js +171 -99
- package/dist/indexer/search/fts-query.js +1 -1
- package/dist/indexer/search/ranking-contributors.js +57 -7
- package/dist/indexer/search/ranking-types.js +4 -0
- package/dist/indexer/search/ranking.js +31 -6
- package/dist/indexer/search/search-attribution.js +67 -0
- package/dist/indexer/search/search-hit-enrichers.js +30 -40
- package/dist/indexer/search/search-source.js +109 -52
- package/dist/indexer/search/semantic-status.js +4 -1
- package/dist/indexer/usage/unmigrated-vaults-guard.js +2 -1
- package/dist/indexer/usage/usage-events.js +72 -9
- package/dist/indexer/walk/file-context.js +1 -44
- package/dist/indexer/walk/matchers.js +16 -32
- package/dist/indexer/walk/path-resolver.js +6 -5
- package/dist/indexer/walk/walker.js +4 -2
- package/dist/integrations/agent/engine-resolution.js +22 -0
- package/dist/integrations/agent/model-aliases.js +1 -1
- package/dist/integrations/agent/prompts.js +35 -11
- package/dist/integrations/agent/spawn.js +41 -270
- package/dist/integrations/harnesses/aider/agent-builder.js +2 -2
- package/dist/integrations/harnesses/aider/index.js +2 -11
- package/dist/integrations/harnesses/amazonq/agent-builder.js +1 -9
- package/dist/integrations/harnesses/amazonq/index.js +3 -16
- package/dist/integrations/harnesses/claude/config-import.js +1 -3
- package/dist/integrations/harnesses/claude/index.js +1 -12
- package/dist/integrations/harnesses/claude/session-log.js +27 -65
- package/dist/integrations/harnesses/codex/agent-builder.js +1 -2
- package/dist/integrations/harnesses/codex/index.js +2 -12
- package/dist/integrations/harnesses/copilot/agent-builder.js +2 -3
- package/dist/integrations/harnesses/copilot/index.js +1 -12
- package/dist/integrations/harnesses/gemini/agent-builder.js +1 -3
- package/dist/integrations/harnesses/gemini/index.js +1 -12
- package/dist/integrations/harnesses/ids.js +24 -0
- package/dist/integrations/harnesses/index.js +27 -2
- package/dist/integrations/harnesses/opencode/config-import.js +1 -3
- package/dist/integrations/harnesses/opencode/index.js +1 -12
- package/dist/integrations/harnesses/opencode/session-log.js +67 -110
- package/dist/integrations/harnesses/opencode-sdk/harness.js +2 -11
- package/dist/integrations/harnesses/openhands/agent-builder.js +3 -3
- package/dist/integrations/harnesses/openhands/index.js +2 -11
- package/dist/integrations/harnesses/pi/agent-builder.js +3 -11
- package/dist/integrations/harnesses/pi/index.js +3 -15
- package/dist/integrations/harnesses/shared.js +17 -0
- package/dist/integrations/harnesses/types.js +37 -2
- package/dist/integrations/lockfile.js +36 -0
- package/dist/integrations/session-logs/index.js +21 -12
- package/dist/integrations/session-logs/provider-base.js +113 -0
- package/dist/llm/client.js +7 -5
- package/dist/llm/embedders/deterministic.js +2 -2
- package/dist/llm/embedders/remote.js +8 -4
- package/dist/llm/graph-extract.js +7 -2
- package/dist/llm/metadata-enhance.js +43 -26
- package/dist/llm/structured-call.js +15 -7
- package/dist/migrate/legacy/config-source-migration.js +223 -0
- package/dist/migrate/legacy/content-migration.js +305 -0
- package/dist/migrate/legacy/legacy-layout.js +779 -0
- package/dist/migrate/legacy/legacy-paths.js +25 -0
- package/dist/migrate/legacy/legacy-stash-json.js +72 -0
- package/dist/migrate/legacy/proposal-fs-import.js +168 -0
- package/dist/migrate/legacy/task-target-ref-migration.js +272 -0
- package/dist/migrate/legacy/three-db-cutover.js +840 -0
- package/dist/migrate/legacy/workflow-migrations-bodies.js +52 -0
- package/dist/migrate/legacy/workflow-migrations-frozen.js +21 -0
- package/dist/migrate/legacy-ref-grammar.js +209 -0
- package/dist/output/command-registry.js +27 -0
- package/dist/output/html-render.js +11 -16
- package/dist/output/renderers.js +32 -276
- package/dist/output/shapes/passthrough.js +3 -9
- package/dist/output/shapes/registry.js +12 -6
- package/dist/output/text/command-format.js +547 -0
- package/dist/output/text/helpers.js +15 -1377
- package/dist/output/text/proposal-format.js +230 -0
- package/dist/output/text/registry.js +12 -6
- package/dist/output/text/show-directives.js +107 -0
- package/dist/output/text/show-format.js +103 -0
- package/dist/output/text/workflow-format.js +345 -0
- package/dist/output/text.js +1 -3
- package/dist/registry/build-index.js +13 -17
- package/dist/registry/providers/static-index.js +2 -2
- package/dist/registry/resolve.js +4 -90
- package/dist/registry/semver.js +93 -0
- package/dist/runtime.js +90 -0
- package/dist/schemas/akm-config.json +2956 -14890
- package/dist/schemas/akm-task.json +2 -2
- package/dist/schemas/akm-workflow.json +2 -2
- package/dist/scripts/migrate-storage.js +2312 -19642
- package/dist/setup/detect.js +5 -7
- package/dist/setup/engine-config.js +2 -1
- package/dist/setup/registry-stash-loader.js +1 -1
- package/dist/setup/semantic-assets.js +12 -9
- package/dist/setup/setup.js +109 -39
- package/dist/setup/steps/connection-shared.js +120 -0
- package/dist/setup/steps/connection.js +50 -274
- package/dist/setup/steps/platforms.js +1 -0
- package/dist/setup/steps/sources.js +13 -6
- package/dist/setup/steps/stashdir.js +5 -2
- package/dist/sources/freshness.js +39 -0
- package/dist/sources/providers/git-provider.js +29 -33
- package/dist/sources/providers/git-stash.js +10 -5
- package/dist/sources/providers/provider-utils.js +40 -18
- package/dist/sources/providers/website.js +1 -1
- package/dist/sources/resolve.js +5 -5
- package/dist/sources/snapshot-fetchers/types.js +4 -0
- package/dist/sources/{website-ingest.js → snapshot-fetchers/website-ingest.js} +105 -39
- package/dist/storage/database.js +47 -3
- package/dist/storage/engines/sqlite-migrations.js +34 -16
- package/dist/storage/locations.js +1 -2
- package/dist/storage/repositories/improve-runs-repository.js +0 -20
- package/dist/storage/repositories/index-connection.js +80 -0
- package/dist/storage/repositories/index-db.js +4 -3
- package/dist/storage/repositories/index-entries-repository.js +952 -0
- package/dist/{indexer/db/entry-mapper.js → storage/repositories/index-entry-mapper.js} +15 -2
- package/dist/storage/repositories/index-entry-types.js +4 -0
- package/dist/storage/repositories/index-fts-repository.js +164 -0
- package/dist/storage/repositories/index-llm-cache-repository.js +109 -0
- package/dist/storage/repositories/index-meta-repository.js +50 -0
- package/dist/{indexer/db/schema.js → storage/repositories/index-schema.js} +241 -100
- package/dist/storage/repositories/index-sql.js +12 -0
- package/dist/storage/repositories/index-utility-repository.js +369 -0
- package/dist/storage/repositories/index-vec-repository.js +245 -0
- package/dist/storage/repositories/proposals-repository.js +41 -50
- package/dist/storage/repositories/registry-cache.js +1 -1
- package/dist/storage/repositories/workflow-runs-repository.js +17 -11
- package/dist/tasks/backends/cron.js +5 -5
- package/dist/tasks/backends/exec-utils.js +21 -0
- package/dist/tasks/backends/index.js +6 -9
- package/dist/tasks/backends/launchd.js +16 -17
- package/dist/tasks/backends/schtasks.js +33 -41
- package/dist/tasks/backends/types.js +4 -0
- package/dist/tasks/parser.js +25 -5
- package/dist/tasks/runner.js +33 -39
- package/dist/tasks/validator.js +5 -6
- package/dist/workflows/authoring/authoring.js +8 -11
- package/dist/workflows/exec/brief.js +10 -7
- package/dist/workflows/exec/report.js +261 -156
- package/dist/workflows/exec/run-workflow.js +2 -1
- package/dist/workflows/exec/step-work.js +3 -9
- package/dist/workflows/program/parser.js +4 -60
- package/dist/workflows/renderer.js +11 -60
- package/dist/workflows/runtime/checkin.js +1 -1
- package/dist/workflows/runtime/plan-classifier.js +7 -4
- package/dist/workflows/runtime/runs.js +11 -10
- package/dist/workflows/runtime/unit-checkin.js +1 -1
- package/dist/workflows/runtime/unit-phases.js +20 -0
- package/dist/workflows/runtime/workflow-asset-loader.js +27 -16
- package/dist/workflows/validate-summary.js +2 -2
- package/dist/workflows/validator.js +18 -2
- package/docs/migration/release-notes/0.9.0.md +45 -3
- package/docs/migration/v0.8-to-v0.9.md +466 -262
- package/docs/{data-and-telemetry.md → reference/data-and-telemetry.md} +61 -17
- package/package.json +10 -9
- package/schemas/akm-config.json +2956 -14890
- package/schemas/akm-task.json +2 -2
- package/schemas/akm-workflow.json +2 -2
- package/dist/assets/help/help-accept.md +0 -12
- package/dist/assets/help/help-improve.md +0 -84
- package/dist/assets/help/help-proposals.md +0 -17
- package/dist/assets/help/help-propose.md +0 -17
- package/dist/assets/help/help-reject.md +0 -11
- package/dist/assets/improve-strategies/recombine-only.json +0 -23
- package/dist/assets/improve-strategies/synthesize.json +0 -17
- package/dist/assets/prompts/procedural-system.md +0 -44
- package/dist/assets/prompts/recombine-system.md +0 -40
- package/dist/assets/prompts/staleness-detect-system.md +0 -6
- package/dist/assets/templates/html/default.html +0 -78
- package/dist/assets/templates/html/vendor/echarts.min.js +0 -45
- package/dist/assets/wiki/index-template.md +0 -12
- package/dist/assets/wiki/ingest-workflow-template.md +0 -83
- package/dist/assets/wiki/log-template.md +0 -8
- package/dist/assets/wiki/schema-template.md +0 -61
- package/dist/commands/improve/calibration.js +0 -161
- package/dist/commands/improve/dedup.js +0 -482
- package/dist/commands/improve/hot-probation.js +0 -45
- package/dist/commands/improve/improve-auto-accept.js +0 -262
- package/dist/commands/improve/procedural.js +0 -403
- package/dist/commands/improve/recombine.js +0 -843
- package/dist/commands/improve/schema-similarity-gate.js +0 -168
- package/dist/commands/lint/agent-linter.js +0 -44
- package/dist/commands/lint/command-linter.js +0 -44
- package/dist/commands/lint/default-linter.js +0 -16
- package/dist/commands/lint/fact-linter.js +0 -39
- package/dist/commands/lint/knowledge-linter.js +0 -16
- package/dist/commands/lint/memory-linter.js +0 -61
- package/dist/commands/lint/registry.js +0 -41
- package/dist/commands/lint/skill-linter.js +0 -45
- package/dist/commands/lint/task-linter.js +0 -50
- package/dist/commands/lint/workflow-linter.js +0 -81
- package/dist/commands/proposal/legacy-import.js +0 -115
- package/dist/commands/wiki-cli.js +0 -291
- package/dist/core/asset/asset-registry.js +0 -76
- package/dist/core/asset/asset-spec.js +0 -316
- package/dist/core/eval/rank-metrics.js +0 -113
- package/dist/core/ripgrep/install.js +0 -163
- package/dist/core/ripgrep/resolve.js +0 -81
- package/dist/indexer/db/db.js +0 -1585
- package/dist/indexer/manifest.js +0 -170
- package/dist/indexer/passes/metadata-contributors.js +0 -31
- package/dist/integrations/harnesses/opencode-sdk/index.js +0 -25
- package/dist/output/text/wiki.js +0 -16
- package/dist/scripts/migrations/import-fs-improve-runs-to-db.js +0 -18228
- package/dist/scripts/migrations/v16-to-v17.js +0 -141
- package/dist/storage/repositories/consolidation-repository.js +0 -38
- package/dist/storage/repositories/recombine-repository.js +0 -213
- package/dist/wiki/wiki-templates.js +0 -15
- package/dist/wiki/wiki.js +0 -1013
- package/dist/workflows/db.js +0 -413
- package/docs/README.md +0 -103
- package/docs/migration/release-notes/0.9.0-beta.60.md +0 -19
- /package/dist/sources/{wiki-fetchers → snapshot-fetchers}/registry.js +0 -0
- /package/dist/sources/{wiki-fetchers → snapshot-fetchers}/youtube.js +0 -0
|
@@ -0,0 +1,68 @@
|
|
|
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 `opencode` tool-directory adapter — akm 0.9.0 format-family work item
|
|
6
|
+
* (#46).
|
|
7
|
+
*
|
|
8
|
+
* Recognizes an OpenCode `.opencode` layout (spec §6/§7, real-world:
|
|
9
|
+
* https://opencode.ai/docs/{commands,agents,skills,rules}/): a root `AGENTS.md`
|
|
10
|
+
* → `instruction`, `commands/*.md` → `command`, `agents/*.md` → `agent`,
|
|
11
|
+
* `skills/<name>/SKILL.md` → `skill`, and ABSTAINS on `opencode.json` runtime
|
|
12
|
+
* config. Per open-question-6 (RESOLVED, accept BOTH forms) the SINGULAR
|
|
13
|
+
* `command/`/`agent/`/`skill/` dirs are accepted as backwards-compat aliases on
|
|
14
|
+
* READ (the conceptId preserves the on-disk spelling, e.g. `command/legacy`);
|
|
15
|
+
* WRITES normalize to the canonical plural (handled in the shared codec).
|
|
16
|
+
*
|
|
17
|
+
* All behavior lives in the shared tool-dir codec ({@link makeToolDirAdapter});
|
|
18
|
+
* this module supplies only the `.opencode` layout + the install-time probe.
|
|
19
|
+
*
|
|
20
|
+
* Conformance oracle (authored, DO NOT modify): fixture
|
|
21
|
+
* `tests/fixtures/bundles/opencode/` + goldens
|
|
22
|
+
* `tests/fixtures/format-family-goldens/opencode/{recognition,placement,lint,renderer}.json`.
|
|
23
|
+
*/
|
|
24
|
+
import fs from "node:fs";
|
|
25
|
+
import path from "node:path";
|
|
26
|
+
import { makeToolDirAdapter } from "./tool-dir-shared.js";
|
|
27
|
+
const LAYOUT = {
|
|
28
|
+
adapterId: "opencode",
|
|
29
|
+
componentId: ".opencode",
|
|
30
|
+
instructionFile: "AGENTS.md",
|
|
31
|
+
instructionConceptId: "AGENTS",
|
|
32
|
+
commandDirs: new Set(["commands", "command"]),
|
|
33
|
+
agentDirs: new Set(["agents", "agent"]),
|
|
34
|
+
skillDirs: new Set(["skills", "skill"]),
|
|
35
|
+
};
|
|
36
|
+
const CONFIG_FILES = ["opencode.json", "opencode.jsonc"];
|
|
37
|
+
const TOOL_DIRS = ["commands", "command", "agents", "agent", "skills", "skill"];
|
|
38
|
+
function fileExists(p) {
|
|
39
|
+
try {
|
|
40
|
+
return fs.existsSync(p);
|
|
41
|
+
}
|
|
42
|
+
catch {
|
|
43
|
+
return false;
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
function dirExists(p) {
|
|
47
|
+
try {
|
|
48
|
+
return fs.statSync(p).isDirectory();
|
|
49
|
+
}
|
|
50
|
+
catch {
|
|
51
|
+
return false;
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* Install-time probe (§1.2): a root is an `.opencode` tool dir when it carries
|
|
56
|
+
* an `opencode.json`/`opencode.jsonc` config, OR a root `AGENTS.md` plus at
|
|
57
|
+
* least one tool dir. Neither marker exists on an akm/okf/wiki root, so the
|
|
58
|
+
* probe is unambiguous; registered ahead of `akm` so a `.opencode` root (whose
|
|
59
|
+
* plural tool dirs also look like akm stash subdirs) is claimed by `opencode`.
|
|
60
|
+
*/
|
|
61
|
+
function opencodeLooksLikeRoot(root) {
|
|
62
|
+
if (CONFIG_FILES.some((f) => fileExists(path.join(root, f))))
|
|
63
|
+
return true;
|
|
64
|
+
if (!fileExists(path.join(root, "AGENTS.md")))
|
|
65
|
+
return false;
|
|
66
|
+
return TOOL_DIRS.some((d) => dirExists(path.join(root, d)));
|
|
67
|
+
}
|
|
68
|
+
export const opencodeAdapter = makeToolDirAdapter(LAYOUT, opencodeLooksLikeRoot);
|
|
@@ -0,0 +1,286 @@
|
|
|
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
|
+
* Shared helpers for the concrete `BundleAdapter` implementations under
|
|
6
|
+
* `src/core/adapter/adapters/` — akm 0.9.0 chunk-2, WI-A (the `okf` adapter).
|
|
7
|
+
*
|
|
8
|
+
* ── Cycle-safety (chunk-2 "cycle-safety watch") ──
|
|
9
|
+
*
|
|
10
|
+
* The base-check logic below is a PORT, not an import, of
|
|
11
|
+
* `src/commands/lint/base-linter.ts`'s `runBaseChecks` (the 4 checks every
|
|
12
|
+
* non-`DefaultLinter` class inherits: unquoted-colon, missing-updated,
|
|
13
|
+
* stale-path, missing-ref). Importing `base-linter.ts` directly would add a
|
|
14
|
+
* new `src/core/adapter` -> `src/commands/lint` edge; `base-linter.ts`
|
|
15
|
+
* itself imports `core/asset/asset-spec.ts` (a baseline cycle participant,
|
|
16
|
+
* `scripts/lint-import-cycles.ts`) and `commands/lint/markdown-insertion.ts`
|
|
17
|
+
* — pulling either into `src/core/adapter/`'s import graph risks adding a
|
|
18
|
+
* 19th cycle participant against the zero-tolerance ratchet (baseline 18).
|
|
19
|
+
* Per the brief's explicit instruction ("copy the pure logic to a leaf ...
|
|
20
|
+
* do NOT use dynamic import to launder"), the pure regex/string logic is
|
|
21
|
+
* duplicated here, adapted to `Diagnostic`/`ValidateContext`. Verified
|
|
22
|
+
* cycle-safe: `bun scripts/lint-import-cycles.ts` stays at 18 with this file
|
|
23
|
+
* present (see the WI-A report).
|
|
24
|
+
*
|
|
25
|
+
* ── What did NOT get ported ──
|
|
26
|
+
*
|
|
27
|
+
* `bundle-adapter.ts`'s own doc comment is explicit that `validate()`
|
|
28
|
+
* "MUST NOT write and MUST NOT read the live filesystem" — reads go through
|
|
29
|
+
* `ValidateContext`, which serves the run snapshot WITH pending changes
|
|
30
|
+
* overlaid. `stale-path`'s legacy check called `fs.existsSync` directly on
|
|
31
|
+
* arbitrary absolute paths found in content; here it goes through
|
|
32
|
+
* `ctx.readFile(candidate)` instead (a `null` result means "does not
|
|
33
|
+
* exist") — the interface-compliant translation of the same check, not a
|
|
34
|
+
* behavior change for any path that genuinely exists or doesn't.
|
|
35
|
+
*
|
|
36
|
+
* The `refs:`-frontmatter-array authoritative-list carve-out (session-
|
|
37
|
+
* checkpoint memories; `base-linter.ts`'s `extractFrontmatterRefs`) is NOT
|
|
38
|
+
* ported here — it is memory/session-specific and no OKF concept uses it. A
|
|
39
|
+
* later memory/note adapter should extend `extractProseRefTokens`'s caller with
|
|
40
|
+
* that carve-out when it ports memory/session.
|
|
41
|
+
*
|
|
42
|
+
* The fence-strip (`stripFencedBlocksSimple` below) is a simplified,
|
|
43
|
+
* same-INTENT reimplementation of `markdown-insertion.ts`'s
|
|
44
|
+
* `findFenceRegions` (which is also table/HTML-region aware, because it
|
|
45
|
+
* additionally powers safe line INSERTION, a concern this module doesn't
|
|
46
|
+
* have) — not byte-identical on every edge case, but sufficient for "don't
|
|
47
|
+
* flag a ref/path inside a ``` example," which is the only thing this
|
|
48
|
+
* module needs the strip for.
|
|
49
|
+
*/
|
|
50
|
+
import { createHash } from "node:crypto";
|
|
51
|
+
import { parseFrontmatter } from "../../asset/frontmatter.js";
|
|
52
|
+
// ── Small pure helpers, reused across the concrete adapters ──────────────────
|
|
53
|
+
/** Content hash feeding `IndexDocument.hash` (incrementality/fingerprints, `types.ts` doc comment). */
|
|
54
|
+
export function hashContent(content) {
|
|
55
|
+
return createHash("sha256").update(content, "utf8").digest("hex");
|
|
56
|
+
}
|
|
57
|
+
/** Trim a possibly-non-string frontmatter value down to a non-empty string or `undefined` (mirrors `core/common.ts#asNonEmptyString`, duplicated locally to avoid importing a cycle-participant module for a 4-line helper). */
|
|
58
|
+
export function nonEmptyString(value) {
|
|
59
|
+
if (typeof value !== "string")
|
|
60
|
+
return undefined;
|
|
61
|
+
const trimmed = value.trim();
|
|
62
|
+
return trimmed.length > 0 ? trimmed : undefined;
|
|
63
|
+
}
|
|
64
|
+
/** Read a frontmatter `tags:` array down to its non-empty string entries, or `undefined` when there are none (mirrors `output/renderers.ts#readFrontmatterTags`). */
|
|
65
|
+
export function readTags(value) {
|
|
66
|
+
if (!Array.isArray(value))
|
|
67
|
+
return undefined;
|
|
68
|
+
const tags = value.filter((tag) => typeof tag === "string" && tag.trim().length > 0);
|
|
69
|
+
return tags.length > 0 ? tags : undefined;
|
|
70
|
+
}
|
|
71
|
+
// ── 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
|
+
function checkMissingUpdated(data, frontmatterText) {
|
|
90
|
+
return frontmatterText !== null && !("updated" in data);
|
|
91
|
+
}
|
|
92
|
+
function findStalePathCandidates(body) {
|
|
93
|
+
const pathRe = /(?:\/home\/|\/tmp\/|\/var\/|\/root\/|\/opt\/)[^\s"'`)\]>,\n]+/g;
|
|
94
|
+
const out = [];
|
|
95
|
+
let match;
|
|
96
|
+
// biome-ignore lint/suspicious/noAssignInExpressions: idiomatic regex loop
|
|
97
|
+
while ((match = pathRe.exec(body)) !== null)
|
|
98
|
+
out.push(match[0]);
|
|
99
|
+
return out;
|
|
100
|
+
}
|
|
101
|
+
/** See file header: same intent as `markdown-insertion.ts#findFenceRegions`, not byte-identical. */
|
|
102
|
+
function stripFencedBlocksSimple(body) {
|
|
103
|
+
const lines = body.split(/\r?\n/);
|
|
104
|
+
let inFence = false;
|
|
105
|
+
const out = lines.map((line) => {
|
|
106
|
+
if (/^\s*```/.test(line)) {
|
|
107
|
+
const wasInFence = inFence;
|
|
108
|
+
inFence = !inFence;
|
|
109
|
+
return wasInFence || inFence ? "" : line;
|
|
110
|
+
}
|
|
111
|
+
return inFence ? "" : line;
|
|
112
|
+
});
|
|
113
|
+
return out.join("\n");
|
|
114
|
+
}
|
|
115
|
+
// Ref-token grammar — copied from `base-linter.ts`'s `BUNDLE_REF_RE`
|
|
116
|
+
// (`core/asset/asset-ref.ts`, spec §11.1 / ref-grammar decision D-R3) as a VALUE,
|
|
117
|
+
// not an import (same cycle-avoidance rationale as the file header — the source
|
|
118
|
+
// string is duplicated so no new `core/adapter` -> `core/asset` edge is added for
|
|
119
|
+
// this scan). Recognizes the 0.9.0 FULLY-QUALIFIED `<bundle>//<concept-id>
|
|
120
|
+
// [#fragment]` prose body-ref anchored form ONLY. Per D-R3, a bare short
|
|
121
|
+
// conceptId in prose is NOT a ref, so it is never flagged here (it is a ref only
|
|
122
|
+
// in the ref-LIST channels — {@link refTokenFromListValue}). The pre-0.9.0
|
|
123
|
+
// `<type>:<slug>` colon grammar is gone from every recognition surface.
|
|
124
|
+
const BUNDLE_BODY_REF_RE_SRC = "(?:^|[\\s`\"'(,[])([^\\s:.#/`\"'()[\\],<>]+\\/\\/[^\\s\"'`)\\]>,\\n]+)";
|
|
125
|
+
/**
|
|
126
|
+
* A conceptId that is a lint false positive rather than a real ref — mirrors the
|
|
127
|
+
* `isNonRefName` guards `base-linter.ts` applies deep in its existence check:
|
|
128
|
+
* template placeholders (`skills/<name>`) and degenerate/incomplete tokens.
|
|
129
|
+
*/
|
|
130
|
+
function isNonRefConceptId(conceptId) {
|
|
131
|
+
const id = conceptId.split("#", 1)[0]; // drop the export #fragment selector
|
|
132
|
+
if (!id || id.length <= 1 || id === "**")
|
|
133
|
+
return true;
|
|
134
|
+
if (id.includes("<"))
|
|
135
|
+
return true; // template placeholder, e.g. `skills/<name>`
|
|
136
|
+
return false;
|
|
137
|
+
}
|
|
138
|
+
/**
|
|
139
|
+
* Extract the fully-qualified `bundle//conceptId` refs from PROSE — the flipped
|
|
140
|
+
* `base-linter.ts#checkMissingRefs` semantics (`BUNDLE_REF_RE`, qualified-only;
|
|
141
|
+
* bare short conceptIds are NOT refs, D-R3). Fenced code is stripped first so
|
|
142
|
+
* example refs inside ``` are not flagged; shell-substitution (`$(`/`${`) and
|
|
143
|
+
* ACP (`::`) false positives are dropped.
|
|
144
|
+
*/
|
|
145
|
+
function extractProseRefTokens(text) {
|
|
146
|
+
const re = new RegExp(BUNDLE_BODY_REF_RE_SRC, "gm");
|
|
147
|
+
const scanBody = stripFencedBlocksSimple(text);
|
|
148
|
+
const refs = [];
|
|
149
|
+
let match;
|
|
150
|
+
// biome-ignore lint/suspicious/noAssignInExpressions: idiomatic regex loop
|
|
151
|
+
while ((match = re.exec(scanBody)) !== null) {
|
|
152
|
+
const token = match[1];
|
|
153
|
+
if (token.includes("$(") || token.includes("${") || token.includes("::"))
|
|
154
|
+
continue;
|
|
155
|
+
if (isNonRefConceptId(token.slice(token.indexOf("//") + 2)))
|
|
156
|
+
continue;
|
|
157
|
+
refs.push(token);
|
|
158
|
+
}
|
|
159
|
+
return refs;
|
|
160
|
+
}
|
|
161
|
+
/**
|
|
162
|
+
* Recognize a single frontmatter ref-LIST value (`xrefs:`/`supersededBy:`/
|
|
163
|
+
* `contradictedBy:`) as a whole ref — the flipped `base-linter.ts#
|
|
164
|
+
* checkMissingRefsInList` semantics. Unlike prose, a bare short `conceptId` IS a
|
|
165
|
+
* ref here (the value's whole purpose is to name one asset). Returns the ref
|
|
166
|
+
* token, or `null` for a false positive or a legacy `origin//type:name` value
|
|
167
|
+
* (the colon-grammar is retired — D-R3).
|
|
168
|
+
*/
|
|
169
|
+
function refTokenFromListValue(value) {
|
|
170
|
+
const trimmed = value.trim();
|
|
171
|
+
if (!trimmed || trimmed.includes("$(") || trimmed.includes("${") || trimmed.includes("::"))
|
|
172
|
+
return null;
|
|
173
|
+
const boundary = trimmed.indexOf("//");
|
|
174
|
+
// Qualified `bundle//conceptId`: a colon in the tail marks a legacy/remote
|
|
175
|
+
// `origin//type:name` — not the 0.9.0 grammar — so skip it. Un-prefixed values
|
|
176
|
+
// are a 0.9.0 short conceptId; a colon there is likewise the retired grammar.
|
|
177
|
+
const conceptId = boundary >= 0 ? trimmed.slice(boundary + 2) : trimmed;
|
|
178
|
+
if (conceptId.includes(":") || isNonRefConceptId(conceptId))
|
|
179
|
+
return null;
|
|
180
|
+
return trimmed;
|
|
181
|
+
}
|
|
182
|
+
const XREF_FRONTMATTER_KEYS = ["xrefs", "supersededBy", "contradictedBy"];
|
|
183
|
+
function readRefStringOrArray(value) {
|
|
184
|
+
if (typeof value === "string") {
|
|
185
|
+
const trimmed = value.trim();
|
|
186
|
+
return trimmed ? [trimmed] : null;
|
|
187
|
+
}
|
|
188
|
+
if (!Array.isArray(value))
|
|
189
|
+
return null;
|
|
190
|
+
const out = value.filter((v) => typeof v === "string" && v.trim().length > 0).map((v) => v.trim());
|
|
191
|
+
return out.length > 0 ? out : null;
|
|
192
|
+
}
|
|
193
|
+
/**
|
|
194
|
+
* Port of `BaseLinter.runBaseChecks`, adapted to the read-only
|
|
195
|
+
* `ValidateContext`/`Diagnostic` shapes (adapter spec §12.1). Reproduces
|
|
196
|
+
* unquoted-colon / missing-updated / stale-path / missing-ref for ONE file's
|
|
197
|
+
* already-parsed content.
|
|
198
|
+
*
|
|
199
|
+
* `fixed` is always `false`: `BundleAdapter.validate` MUST NOT write the
|
|
200
|
+
* filesystem, so there is no fix-apply mechanism at this layer — a documented,
|
|
201
|
+
* intentional behavior narrowing from the legacy `--fix`-capable linter.
|
|
202
|
+
*
|
|
203
|
+
* `componentRoot` is `BundleComponent.root` — used only for the stale-path
|
|
204
|
+
* "portable form" hint (mirrors legacy `ctx.stashRoot`-relative substitution;
|
|
205
|
+
* no I/O).
|
|
206
|
+
*/
|
|
207
|
+
export async function runBaseValidateChecks(relPath, parsed, componentRoot, ctx) {
|
|
208
|
+
const diagnostics = [];
|
|
209
|
+
const { data, content: body, frontmatter } = parsed;
|
|
210
|
+
const unquotedColonDetail = checkUnquotedColon(frontmatter);
|
|
211
|
+
if (unquotedColonDetail) {
|
|
212
|
+
diagnostics.push({ file: relPath, issue: "unquoted-colon", detail: unquotedColonDetail, fixed: false });
|
|
213
|
+
}
|
|
214
|
+
if (checkMissingUpdated(data, frontmatter)) {
|
|
215
|
+
diagnostics.push({
|
|
216
|
+
file: relPath,
|
|
217
|
+
issue: "missing-updated",
|
|
218
|
+
detail: "no updated field in frontmatter",
|
|
219
|
+
fixed: false,
|
|
220
|
+
});
|
|
221
|
+
}
|
|
222
|
+
const staleCandidates = [
|
|
223
|
+
...findStalePathCandidates(body),
|
|
224
|
+
...(frontmatter ? findStalePathCandidates(frontmatter) : []),
|
|
225
|
+
];
|
|
226
|
+
for (const candidate of staleCandidates) {
|
|
227
|
+
const found = await ctx.readFile(candidate);
|
|
228
|
+
if (found !== null)
|
|
229
|
+
continue;
|
|
230
|
+
const portableHint = candidate.startsWith(componentRoot)
|
|
231
|
+
? ` (portable form: $AKM_STASH_DIR${candidate.slice(componentRoot.length)})`
|
|
232
|
+
: "";
|
|
233
|
+
diagnostics.push({
|
|
234
|
+
file: relPath,
|
|
235
|
+
issue: "stale-path",
|
|
236
|
+
detail: `nonexistent path: ${candidate}${portableHint}`,
|
|
237
|
+
fixed: false,
|
|
238
|
+
});
|
|
239
|
+
}
|
|
240
|
+
for (const ref of extractProseRefTokens(body)) {
|
|
241
|
+
const { exists } = await ctx.resolveRef(ref);
|
|
242
|
+
if (!exists)
|
|
243
|
+
diagnostics.push({ file: relPath, issue: "missing-ref", detail: `missing ref: ${ref}`, fixed: false });
|
|
244
|
+
}
|
|
245
|
+
if (frontmatter !== null) {
|
|
246
|
+
for (const key of XREF_FRONTMATTER_KEYS) {
|
|
247
|
+
const values = readRefStringOrArray(data[key]);
|
|
248
|
+
if (values === null)
|
|
249
|
+
continue;
|
|
250
|
+
for (const value of values) {
|
|
251
|
+
const ref = refTokenFromListValue(value);
|
|
252
|
+
if (ref === null)
|
|
253
|
+
continue;
|
|
254
|
+
const { exists } = await ctx.resolveRef(ref);
|
|
255
|
+
if (!exists) {
|
|
256
|
+
diagnostics.push({
|
|
257
|
+
file: relPath,
|
|
258
|
+
issue: "missing-ref",
|
|
259
|
+
detail: `missing ref: ${ref} (frontmatter ${key})`,
|
|
260
|
+
fixed: false,
|
|
261
|
+
});
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
return diagnostics;
|
|
267
|
+
}
|
|
268
|
+
/**
|
|
269
|
+
* Shared per-change loop: for every non-delete change with readable content,
|
|
270
|
+
* parse frontmatter and run {@link runBaseValidateChecks}. Adapters with extra
|
|
271
|
+
* per-type checks (e.g. `okf`'s missing-type / OKF-link warnings) run their own
|
|
272
|
+
* loop and call {@link runBaseValidateChecks} directly instead.
|
|
273
|
+
*/
|
|
274
|
+
export async function validateChangesWithBaseChecks(c, changes, ctx) {
|
|
275
|
+
const diagnostics = [];
|
|
276
|
+
for (const change of changes) {
|
|
277
|
+
if (change.op === "delete")
|
|
278
|
+
continue;
|
|
279
|
+
const raw = change.after ?? (await ctx.readFile(change.path));
|
|
280
|
+
if (typeof raw !== "string")
|
|
281
|
+
continue;
|
|
282
|
+
const parsed = parseFrontmatter(raw);
|
|
283
|
+
diagnostics.push(...(await runBaseValidateChecks(change.path, parsed, c.root, ctx)));
|
|
284
|
+
}
|
|
285
|
+
return diagnostics;
|
|
286
|
+
}
|
|
@@ -0,0 +1,212 @@
|
|
|
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
|
+
* Shared recognition / placement / validation for the two AGENT TOOL-DIRECTORY
|
|
6
|
+
* adapters — `claude` (`.claude`) and `opencode` (`.opencode`) — akm 0.9.0
|
|
7
|
+
* format-family work item (#46). Both translate a foreign tool layout into the
|
|
8
|
+
* open AKM `type` vocabulary (spec §6/§7): a root instruction file
|
|
9
|
+
* (CLAUDE.md / AGENTS.md) → `instruction`, `commands/*.md` → `command`,
|
|
10
|
+
* `agents/*.md` → `agent`, `skills/<name>/SKILL.md` → `skill` (item = the dir).
|
|
11
|
+
* They differ only in three parameters carried on {@link ToolDirLayout}:
|
|
12
|
+
* - the instruction basename + its concept id,
|
|
13
|
+
* - the accepted subdir spellings (opencode accepts the SINGULAR
|
|
14
|
+
* `command/`/`agent/`/`skill/` aliases per open-question-6, claude does not),
|
|
15
|
+
* - the adapter id + component id.
|
|
16
|
+
*
|
|
17
|
+
* The SKILL.md codec is shared with the `agent-skills` adapter as functions
|
|
18
|
+
* (spec §8): a skill's conceptId is its DIRECTORY, and its `name` is the
|
|
19
|
+
* frontmatter `name` (== the dir name per the Agent Skills §4.5 hard rule).
|
|
20
|
+
*
|
|
21
|
+
* ── D-R6 (reserved files) ──
|
|
22
|
+
*
|
|
23
|
+
* `index.md` / `log.md` are OKF structural files at EVERY depth — never a
|
|
24
|
+
* concept. `classify` excludes them up front so a stray `commands/index.md`
|
|
25
|
+
* never becomes a `command` (mirrors okf/akm/llm-wiki `RESERVED_FILES`).
|
|
26
|
+
*
|
|
27
|
+
* ── validate leniency (spec §6 skill row) ──
|
|
28
|
+
*
|
|
29
|
+
* The tool-dir adapters TOLERATE the tools' native frontmatter (argument-hint,
|
|
30
|
+
* allowed-tools, tools, model, mode, temperature, agent, …) — the strict
|
|
31
|
+
* unknown-frontmatter behavior belongs to `agent-skills`, not here. So `command`
|
|
32
|
+
* / `agent` get a LENIENT name-or-signal check (never the akm `CommandLinter`'s
|
|
33
|
+
* frontmatter `name`+`type` requirement, which these tool files do not carry),
|
|
34
|
+
* `skill` gets the coded `missing-skill-md` directory check
|
|
35
|
+
* ({@link skillDirectoryDiagnostics}), and `instruction` runs the shared base
|
|
36
|
+
* checks (a no-op on a frontmatter-free CLAUDE.md/AGENTS.md). Notably NO base
|
|
37
|
+
* checks run on command/agent/skill: their tool frontmatter carries no `updated`
|
|
38
|
+
* field, so `missing-updated` would fire on every file and contradict the lint
|
|
39
|
+
* golden's clean result.
|
|
40
|
+
*
|
|
41
|
+
* ── Cycle-safety ──
|
|
42
|
+
*
|
|
43
|
+
* Imported only by `claude-adapter.ts` / `opencode-adapter.ts` (themselves
|
|
44
|
+
* imported only by the test-only `adapters/index.ts` barrel), so this leaf can
|
|
45
|
+
* never gain an inbound edge from a cycle participant. It value-imports only
|
|
46
|
+
* pure leaves (`shared`, `akm-lint`, `frontmatter`) plus Node builtins.
|
|
47
|
+
*/
|
|
48
|
+
import path from "node:path";
|
|
49
|
+
import { parseFrontmatter } from "../../asset/frontmatter.js";
|
|
50
|
+
import { skillDirectoryDiagnostics } from "./akm-lint.js";
|
|
51
|
+
import { hashContent, nonEmptyString, readTags, runBaseValidateChecks } from "./shared.js";
|
|
52
|
+
/** OKF reserved structural files (D-R6) — excluded at every depth, case-insensitive. */
|
|
53
|
+
const RESERVED_FILES = new Set(["index.md", "log.md"]);
|
|
54
|
+
/** Upper bound on the bounded `content` FTS field (mirrors okf-adapter). */
|
|
55
|
+
const MAX_CONTENT_CHARS = 100_000;
|
|
56
|
+
/** The canonical (plural) subdir spellings writes normalize to (open-question-6). */
|
|
57
|
+
const CANONICAL_COMMAND_DIR = "commands";
|
|
58
|
+
const CANONICAL_AGENT_DIR = "agents";
|
|
59
|
+
const CANONICAL_SKILL_DIR = "skills";
|
|
60
|
+
const SKILL_MANIFEST = "SKILL.md";
|
|
61
|
+
function toPosix(p) {
|
|
62
|
+
return p.replace(/\\/g, "/");
|
|
63
|
+
}
|
|
64
|
+
function isReserved(base) {
|
|
65
|
+
return RESERVED_FILES.has(base.toLowerCase());
|
|
66
|
+
}
|
|
67
|
+
/** Classify one component-root-relative file into a tool-dir `type` + conceptId, or abstain (null). */
|
|
68
|
+
function classify(relPath, layout) {
|
|
69
|
+
const posix = toPosix(relPath);
|
|
70
|
+
const segs = posix.split("/").filter((s) => s.length > 0);
|
|
71
|
+
if (segs.length === 0)
|
|
72
|
+
return null;
|
|
73
|
+
const base = segs[segs.length - 1];
|
|
74
|
+
if (isReserved(base))
|
|
75
|
+
return null;
|
|
76
|
+
// Root instruction file.
|
|
77
|
+
if (segs.length === 1 && base === layout.instructionFile) {
|
|
78
|
+
return { type: "instruction", conceptId: layout.instructionConceptId, name: layout.instructionConceptId };
|
|
79
|
+
}
|
|
80
|
+
const head = segs[0];
|
|
81
|
+
const ext = path.extname(base).toLowerCase();
|
|
82
|
+
// skill: <skillDir>/<name>/SKILL.md — the item is the DIRECTORY. Any other
|
|
83
|
+
// file under a skill dir (bundled resources) is part of the item, not a concept.
|
|
84
|
+
if (layout.skillDirs.has(head)) {
|
|
85
|
+
if (segs.length >= 3 && base === SKILL_MANIFEST) {
|
|
86
|
+
return { type: "skill", conceptId: `${segs[0]}/${segs[1]}`, name: segs[1] };
|
|
87
|
+
}
|
|
88
|
+
return null;
|
|
89
|
+
}
|
|
90
|
+
if (layout.commandDirs.has(head) && ext === ".md" && segs.length >= 2) {
|
|
91
|
+
return { type: "command", conceptId: posix.replace(/\.md$/i, ""), name: base.replace(/\.md$/i, "") };
|
|
92
|
+
}
|
|
93
|
+
if (layout.agentDirs.has(head) && ext === ".md" && segs.length >= 2) {
|
|
94
|
+
return { type: "agent", conceptId: posix.replace(/\.md$/i, ""), name: base.replace(/\.md$/i, "") };
|
|
95
|
+
}
|
|
96
|
+
return null;
|
|
97
|
+
}
|
|
98
|
+
/** recognize() for a tool-dir adapter: derive the open `type`, project the OKF-shaped fields. */
|
|
99
|
+
export function recognizeToolDir(layout, c, file) {
|
|
100
|
+
const cls = classify(file.relPath, layout);
|
|
101
|
+
if (cls === null)
|
|
102
|
+
return null;
|
|
103
|
+
const raw = file.content();
|
|
104
|
+
const parsed = parseFrontmatter(raw);
|
|
105
|
+
const data = parsed.data;
|
|
106
|
+
const body = parsed.content;
|
|
107
|
+
// For a skill, `name` is the frontmatter `name` (== dir name); otherwise the
|
|
108
|
+
// basename-derived concept name.
|
|
109
|
+
const name = cls.type === "skill" ? (nonEmptyString(data.name) ?? cls.name) : cls.name;
|
|
110
|
+
const description = nonEmptyString(data.description);
|
|
111
|
+
const tags = readTags(data.tags);
|
|
112
|
+
const doc = {
|
|
113
|
+
ref: `${c.id}//${cls.conceptId}`,
|
|
114
|
+
bundle: c.id,
|
|
115
|
+
component: layout.componentId,
|
|
116
|
+
conceptId: cls.conceptId,
|
|
117
|
+
path: file.absPath,
|
|
118
|
+
hash: hashContent(raw),
|
|
119
|
+
adapterId: layout.adapterId,
|
|
120
|
+
type: cls.type,
|
|
121
|
+
name,
|
|
122
|
+
content: body.length > MAX_CONTENT_CHARS ? body.slice(0, MAX_CONTENT_CHARS) : body,
|
|
123
|
+
};
|
|
124
|
+
if (description !== undefined)
|
|
125
|
+
doc.description = description;
|
|
126
|
+
if (tags !== undefined)
|
|
127
|
+
doc.tags = tags;
|
|
128
|
+
return doc;
|
|
129
|
+
}
|
|
130
|
+
/**
|
|
131
|
+
* placeNew() for a tool-dir adapter (spec §7: "AKM workspace layout IS the tool
|
|
132
|
+
* dir minus the prefix"). Writes NORMALIZE to the canonical plural subdir
|
|
133
|
+
* (open-question-6): a `command/foo` concept still places at `commands/foo.md`.
|
|
134
|
+
* The instruction file is fixed at the component root.
|
|
135
|
+
*/
|
|
136
|
+
export function placeNewToolDir(layout, c, conceptId) {
|
|
137
|
+
const posix = toPosix(conceptId);
|
|
138
|
+
if (posix === layout.instructionConceptId)
|
|
139
|
+
return path.join(c.root, layout.instructionFile);
|
|
140
|
+
const segs = posix.split("/").filter((s) => s.length > 0);
|
|
141
|
+
const head = segs[0];
|
|
142
|
+
const rest = segs.slice(1).join("/");
|
|
143
|
+
if (rest.length > 0) {
|
|
144
|
+
if (layout.skillDirs.has(head))
|
|
145
|
+
return path.join(c.root, CANONICAL_SKILL_DIR, rest, SKILL_MANIFEST);
|
|
146
|
+
if (layout.commandDirs.has(head))
|
|
147
|
+
return path.join(c.root, CANONICAL_COMMAND_DIR, `${rest}.md`);
|
|
148
|
+
if (layout.agentDirs.has(head))
|
|
149
|
+
return path.join(c.root, CANONICAL_AGENT_DIR, `${rest}.md`);
|
|
150
|
+
}
|
|
151
|
+
return path.join(c.root, `${posix}.md`);
|
|
152
|
+
}
|
|
153
|
+
/** LENIENT command/agent check: a diagnostic only when NEITHER a name/description NOR a type-shaped signal is present. */
|
|
154
|
+
function nameOrSignalDiagnostics(type, relPath, data, body) {
|
|
155
|
+
if (nonEmptyString(data.name) !== undefined || nonEmptyString(data.description) !== undefined)
|
|
156
|
+
return [];
|
|
157
|
+
// command-shaped body/frontmatter signals (spec §6 command row): $ARGUMENTS / $1 / an `agent` frontmatter key.
|
|
158
|
+
if (type === "command" &&
|
|
159
|
+
(/\$ARGUMENTS\b/.test(body) || /\$\d/.test(body) || nonEmptyString(data.agent) !== undefined)) {
|
|
160
|
+
return [];
|
|
161
|
+
}
|
|
162
|
+
return [
|
|
163
|
+
{
|
|
164
|
+
file: relPath,
|
|
165
|
+
issue: "missing-name-or-type",
|
|
166
|
+
detail: `${type} has neither a name/description nor a ${type}-shaped signal`,
|
|
167
|
+
fixed: false,
|
|
168
|
+
},
|
|
169
|
+
];
|
|
170
|
+
}
|
|
171
|
+
/** validate() for a tool-dir adapter — see file header for the leniency contract. */
|
|
172
|
+
export async function validateToolDir(layout, c, changes, ctx) {
|
|
173
|
+
const diagnostics = [];
|
|
174
|
+
const seenSkillDirs = new Set();
|
|
175
|
+
for (const change of changes) {
|
|
176
|
+
if (change.op === "delete")
|
|
177
|
+
continue;
|
|
178
|
+
const raw = change.after ?? (await ctx.readFile(change.path));
|
|
179
|
+
if (typeof raw !== "string")
|
|
180
|
+
continue;
|
|
181
|
+
const relPath = toPosix(change.path);
|
|
182
|
+
// The one coded skill check (missing-skill-md) fires on ANY change under a
|
|
183
|
+
// `skills/<name>/…` package (self-gated + deduped), even a bundled resource —
|
|
184
|
+
// mirrors the akm adapter's per-change SkillLinter.lintDirectory pass.
|
|
185
|
+
diagnostics.push(...(await skillDirectoryDiagnostics(relPath, seenSkillDirs, ctx)));
|
|
186
|
+
const cls = classify(change.path, layout);
|
|
187
|
+
if (cls === null)
|
|
188
|
+
continue;
|
|
189
|
+
if (cls.type === "instruction") {
|
|
190
|
+
diagnostics.push(...(await runBaseValidateChecks(relPath, parseFrontmatter(raw), c.root, ctx)));
|
|
191
|
+
}
|
|
192
|
+
else if (cls.type === "command" || cls.type === "agent") {
|
|
193
|
+
const parsed = parseFrontmatter(raw);
|
|
194
|
+
diagnostics.push(...nameOrSignalDiagnostics(cls.type, relPath, parsed.data, parsed.content));
|
|
195
|
+
}
|
|
196
|
+
// skill: covered by skillDirectoryDiagnostics above; no additional per-file check.
|
|
197
|
+
}
|
|
198
|
+
return diagnostics;
|
|
199
|
+
}
|
|
200
|
+
/** Build the concrete `BundleAdapter` from a layout + a `looksLikeRoot` probe (claude/opencode share everything else). */
|
|
201
|
+
export function makeToolDirAdapter(layout, looksLikeRoot) {
|
|
202
|
+
return {
|
|
203
|
+
id: layout.adapterId,
|
|
204
|
+
version: "0.9.0",
|
|
205
|
+
extensions: [".md"],
|
|
206
|
+
recognize: (c, file) => recognizeToolDir(layout, c, file),
|
|
207
|
+
validate: (c, changes, ctx) => validateToolDir(layout, c, changes, ctx),
|
|
208
|
+
placeNew: (c, conceptId) => placeNewToolDir(layout, c, conceptId),
|
|
209
|
+
directoryList: () => [CANONICAL_COMMAND_DIR, CANONICAL_AGENT_DIR, CANONICAL_SKILL_DIR],
|
|
210
|
+
looksLikeRoot,
|
|
211
|
+
};
|
|
212
|
+
}
|