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.
Files changed (438) hide show
  1. package/CHANGELOG.md +122 -26
  2. package/README.md +7 -7
  3. package/SECURITY.md +11 -10
  4. package/dist/akm +2 -2
  5. package/dist/akm-migrate-storage +2 -2
  6. package/dist/assets/hints/cli-hints-full.md +60 -73
  7. package/dist/assets/hints/cli-hints-short.md +8 -12
  8. package/dist/assets/improve-strategies/default.json +5 -7
  9. package/dist/assets/improve-strategies/frequent.json +2 -2
  10. package/dist/assets/improve-strategies/proactive-maintenance.json +2 -2
  11. package/dist/assets/improve-strategies/quick.json +1 -1
  12. package/dist/assets/improve-strategies/reflect-distill.json +3 -3
  13. package/dist/assets/improve-strategies/thorough.json +1 -1
  14. package/dist/assets/prompts/consolidate-system.md +5 -5
  15. package/dist/assets/prompts/extract-session.md +2 -6
  16. package/dist/assets/prompts/reflect-llm-framed-contract.md +11 -0
  17. package/dist/assets/prompts/reflect-llm-schema-contract.md +3 -0
  18. package/dist/assets/prompts/reflect-output-repair.md +3 -0
  19. package/dist/assets/stash-skeleton/README.md +10 -11
  20. package/dist/assets/stash-skeleton/facts/conventions/assets/agent.md +1 -1
  21. package/dist/assets/stash-skeleton/facts/conventions/assets/fact.md +1 -1
  22. package/dist/assets/stash-skeleton/facts/conventions/assets/knowledge.md +1 -1
  23. package/dist/assets/stash-skeleton/facts/conventions/assets/lesson.md +2 -2
  24. package/dist/assets/stash-skeleton/facts/conventions/assets/memory.md +2 -2
  25. package/dist/assets/stash-skeleton/facts/conventions/assets/script.md +1 -1
  26. package/dist/assets/stash-skeleton/facts/conventions/assets/skill.md +2 -2
  27. package/dist/assets/stash-skeleton/facts/conventions/assets/workflow.md +1 -1
  28. package/dist/assets/stash-skeleton/facts/conventions/backlinks.md +18 -18
  29. package/dist/assets/stash-skeleton/facts/conventions/domains.md +8 -9
  30. package/dist/assets/stash-skeleton/facts/conventions/organization.md +13 -15
  31. package/dist/assets/tasks/core/extract.yml +2 -2
  32. package/dist/assets/tasks/core/improve.yml +1 -1
  33. package/dist/assets/templates/html/health.html +2 -3
  34. package/dist/cli/config-migrate.js +1328 -101
  35. package/dist/cli/invocation.js +255 -0
  36. package/dist/cli/parse-args.js +20 -83
  37. package/dist/cli/shared.js +15 -37
  38. package/dist/cli.js +18 -49
  39. package/dist/commands/agent/agent-dispatch.js +2 -2
  40. package/dist/commands/agent/contribute-cli.js +5 -4
  41. package/dist/commands/bundle/bundle-cli.js +68 -0
  42. package/dist/commands/bundle/bundle.js +219 -0
  43. package/dist/commands/completions.js +2 -2
  44. package/dist/commands/config-cli.js +6 -3
  45. package/dist/commands/env/env-binding.js +13 -9
  46. package/dist/commands/env/env-cli.js +49 -48
  47. package/dist/commands/env/secret-cli.js +24 -10
  48. package/dist/commands/events.js +2 -2
  49. package/dist/commands/feedback-cli.js +59 -33
  50. package/dist/commands/graph/graph.js +6 -4
  51. package/dist/commands/health/advisories.js +4 -4
  52. package/dist/commands/health/html-report.js +190 -593
  53. package/dist/commands/health/improve-metrics.js +240 -188
  54. package/dist/commands/health/metrics.js +9 -17
  55. package/dist/commands/health/report-view-model.js +509 -0
  56. package/dist/commands/health/surfaces.js +6 -3
  57. package/dist/commands/health/types-checks.js +4 -0
  58. package/dist/commands/health/types-improve.js +22 -0
  59. package/dist/commands/health/types-metrics.js +4 -0
  60. package/dist/commands/health/types-result.js +7 -0
  61. package/dist/commands/health/types-runs.js +4 -0
  62. package/dist/commands/health/types-session-log.js +4 -0
  63. package/dist/commands/health/types-windows.js +4 -0
  64. package/dist/commands/health/types.js +26 -21
  65. package/dist/commands/health/windows.js +1 -2
  66. package/dist/commands/health.js +219 -161
  67. package/dist/commands/improve/collapse-detector.js +25 -14
  68. package/dist/commands/improve/consolidate/chunking.js +7 -5
  69. package/dist/commands/improve/consolidate.js +640 -740
  70. package/dist/commands/improve/content-hash.js +39 -0
  71. package/dist/commands/improve/distill/content-repair.js +4 -10
  72. package/dist/commands/improve/distill/promote-memory.js +79 -49
  73. package/dist/commands/improve/distill/quality-gate.js +113 -35
  74. package/dist/commands/improve/distill-promotion-policy.js +24 -885
  75. package/dist/commands/improve/distill.js +565 -337
  76. package/dist/commands/improve/eligibility.js +75 -36
  77. package/dist/commands/improve/extract-cli.js +6 -6
  78. package/dist/commands/improve/extract-prompt.js +5 -34
  79. package/dist/commands/improve/extract.js +526 -357
  80. package/dist/commands/improve/feedback-valence.js +2 -12
  81. package/dist/commands/improve/improve-cli.js +11 -20
  82. package/dist/commands/improve/improve-result-file.js +7 -6
  83. package/dist/commands/improve/improve-run-types.js +4 -0
  84. package/dist/commands/improve/improve-strategies.js +3 -9
  85. package/dist/commands/improve/improve.js +822 -727
  86. package/dist/commands/improve/locks.js +27 -95
  87. package/dist/commands/improve/loop-stages.js +941 -963
  88. package/dist/commands/improve/memory/derived-ref.js +122 -0
  89. package/dist/commands/improve/memory/memory-contradiction-detect.js +35 -38
  90. package/dist/commands/improve/memory/memory-improve.js +24 -37
  91. package/dist/commands/improve/outcome-loop.js +13 -33
  92. package/dist/commands/improve/preparation.js +889 -691
  93. package/dist/commands/improve/proactive-maintenance.js +37 -9
  94. package/dist/commands/improve/proposal-envelope.js +31 -0
  95. package/dist/commands/improve/reflect.js +974 -588
  96. package/dist/commands/improve/run-context.js +119 -0
  97. package/dist/commands/improve/salience.js +8 -4
  98. package/dist/commands/improve/session-asset.js +7 -3
  99. package/dist/commands/improve/shared.js +14 -40
  100. package/dist/commands/improve/source-identity.js +39 -26
  101. package/dist/commands/improve/triage.js +20 -17
  102. package/dist/commands/lint/base-linter.js +288 -295
  103. package/dist/commands/lint/index.js +132 -19
  104. package/dist/commands/mv-cli.js +257 -234
  105. package/dist/commands/observability-cli.js +2 -1
  106. package/dist/commands/proposal/diff-format.js +50 -0
  107. package/dist/commands/proposal/drain-policies.js +0 -6
  108. package/dist/commands/proposal/drain.js +17 -16
  109. package/dist/commands/proposal/proposal-cli.js +40 -77
  110. package/dist/commands/proposal/proposal-types.js +56 -0
  111. package/dist/commands/proposal/proposal.js +39 -1
  112. package/dist/commands/proposal/propose.js +41 -19
  113. package/dist/commands/proposal/repository.js +566 -486
  114. package/dist/commands/proposal/validators/proposal-quality-validators.js +22 -6
  115. package/dist/commands/proposal/validators/proposal-validators.js +6 -5
  116. package/dist/commands/proposal/validators/proposals.js +5 -4
  117. package/dist/commands/read/curate.js +50 -28
  118. package/dist/commands/read/knowledge.js +25 -16
  119. package/dist/commands/read/remember-cli.js +12 -4
  120. package/dist/commands/read/search-cli.js +33 -33
  121. package/dist/commands/read/search.js +68 -55
  122. package/dist/commands/read/show.js +54 -135
  123. package/dist/commands/remember.js +7 -5
  124. package/dist/commands/sources/add-cli.js +10 -24
  125. package/dist/commands/sources/bundle-config-ops.js +58 -0
  126. package/dist/commands/sources/history.js +13 -8
  127. package/dist/commands/sources/info.js +10 -11
  128. package/dist/commands/sources/init.js +12 -21
  129. package/dist/commands/sources/installed-stashes.js +151 -138
  130. package/dist/commands/sources/schema-repair.js +5 -3
  131. package/dist/commands/sources/self-update.js +9 -7
  132. package/dist/commands/sources/source-add.js +116 -154
  133. package/dist/commands/sources/source-clone.js +33 -14
  134. package/dist/commands/sources/source-manage.js +43 -32
  135. package/dist/commands/sources/sources-cli.js +12 -7
  136. package/dist/commands/sources/stash-cli.js +12 -5
  137. package/dist/commands/sources/stash-skeleton.js +1 -1
  138. package/dist/commands/tasks/default-tasks.js +13 -13
  139. package/dist/commands/tasks/tasks-cli.js +3 -3
  140. package/dist/commands/tasks/tasks.js +71 -31
  141. package/dist/commands/workflow-cli.js +22 -26
  142. package/dist/core/action-contributors.js +1 -1
  143. package/dist/core/activation-policy.js +49 -0
  144. package/dist/core/adapter/adapters/agent-skills-adapter.js +181 -0
  145. package/dist/core/adapter/adapters/akm-adapter.js +509 -0
  146. package/dist/core/adapter/adapters/akm-lint.js +381 -0
  147. package/dist/core/adapter/adapters/akm-metadata.js +416 -0
  148. package/dist/core/adapter/adapters/akm-task-adapter.js +150 -0
  149. package/dist/core/adapter/adapters/akm-workflow-adapter.js +196 -0
  150. package/dist/core/adapter/adapters/claude-adapter.js +61 -0
  151. package/dist/core/adapter/adapters/dotenv-adapter.js +187 -0
  152. package/dist/core/adapter/adapters/generic-files-adapter.js +117 -0
  153. package/dist/core/adapter/adapters/index.js +80 -0
  154. package/dist/core/adapter/adapters/llm-wiki-adapter.js +413 -0
  155. package/dist/core/adapter/adapters/okf-adapter.js +223 -0
  156. package/dist/core/adapter/adapters/opencode-adapter.js +68 -0
  157. package/dist/core/adapter/adapters/shared.js +286 -0
  158. package/dist/core/adapter/adapters/tool-dir-shared.js +212 -0
  159. package/dist/core/adapter/adapters/website-snapshot-adapter.js +154 -0
  160. package/dist/core/adapter/bundle-adapter.js +4 -0
  161. package/dist/core/adapter/recognize-match.js +42 -0
  162. package/dist/core/adapter/registry.js +56 -0
  163. package/dist/core/adapter/types.js +4 -0
  164. package/dist/core/asset/asset-placement.js +229 -0
  165. package/dist/core/asset/asset-ref.js +107 -78
  166. package/dist/core/asset/asset-serialize.js +20 -0
  167. package/dist/core/asset/frontmatter.js +1 -1
  168. package/dist/core/asset/resolve-ref.js +223 -0
  169. package/dist/core/asset/stash-meta.js +1 -1
  170. package/dist/core/common.js +141 -49
  171. package/dist/core/config/config-io.js +0 -108
  172. package/dist/core/config/config-schema.js +94 -1039
  173. package/dist/core/config/config-sources.js +115 -36
  174. package/dist/core/config/config-types.js +16 -11
  175. package/dist/core/config/config-walker.js +4 -2
  176. package/dist/core/config/config.js +21 -26
  177. package/dist/core/config/engine-semantics.js +0 -4
  178. package/dist/core/config/schema/embedding.js +38 -0
  179. package/dist/core/config/schema/engines.js +116 -0
  180. package/dist/core/config/schema/feedback.js +31 -0
  181. package/dist/core/config/schema/improve-processes.js +395 -0
  182. package/dist/core/config/schema/improve.js +76 -0
  183. package/dist/core/config/schema/index-config.js +183 -0
  184. package/dist/core/config/schema/output.js +15 -0
  185. package/dist/core/config/schema/primitives.js +94 -0
  186. package/dist/core/config/schema/search.js +30 -0
  187. package/dist/core/config/schema/setup.js +27 -0
  188. package/dist/core/config/schema/sources-bundles.js +164 -0
  189. package/dist/core/config/schema/workflow.js +29 -0
  190. package/dist/core/env-secret-ref.js +113 -8
  191. package/dist/core/errors.js +1 -1
  192. package/dist/core/events-types.js +4 -0
  193. package/dist/core/extra-params.js +1 -0
  194. package/dist/core/file-change.js +16 -0
  195. package/dist/core/fs-txn.js +316 -0
  196. package/dist/core/git-message.js +59 -0
  197. package/dist/core/improve-result.js +9 -1
  198. package/dist/core/logs-db.js +1 -1
  199. package/dist/core/maintenance-barrier.js +16 -0
  200. package/dist/core/migration-backup.js +332 -160
  201. package/dist/core/paths.js +3 -6
  202. package/dist/core/platform.js +10 -0
  203. package/dist/core/recognition-util.js +130 -0
  204. package/dist/core/redaction.js +2 -2
  205. package/dist/core/standards/resolve-standards-context.js +47 -64
  206. package/dist/core/standards/resolve-type-conventions.js +3 -3
  207. package/dist/core/state/migrations.js +201 -8
  208. package/dist/core/state-db.js +38 -2
  209. package/dist/core/subprocess.js +303 -0
  210. package/dist/core/time.js +20 -0
  211. package/dist/core/type-presentation.js +130 -0
  212. package/dist/core/write-source.js +39 -67
  213. package/dist/indexer/bundle-identity-guard.js +91 -0
  214. package/dist/indexer/db/graph-db.js +1 -1
  215. package/dist/indexer/db/llm-cache.js +1 -1
  216. package/dist/indexer/ensure-index.js +29 -9
  217. package/dist/indexer/graph/graph-boost.js +28 -24
  218. package/dist/indexer/graph/graph-extraction.js +5 -6
  219. package/dist/indexer/graph/graph-types.js +4 -0
  220. package/dist/indexer/index-written-assets.js +44 -17
  221. package/dist/indexer/indexer.js +289 -153
  222. package/dist/indexer/init.js +18 -25
  223. package/dist/indexer/installations.js +224 -0
  224. package/dist/indexer/passes/dir-staleness.js +2 -1
  225. package/dist/indexer/passes/memory-inference.js +8 -4
  226. package/dist/indexer/passes/metadata.js +96 -243
  227. package/dist/indexer/scan/doc-to-entry.js +123 -0
  228. package/dist/indexer/scan/drain-dir.js +144 -0
  229. package/dist/indexer/search/db-search.js +171 -99
  230. package/dist/indexer/search/fts-query.js +1 -1
  231. package/dist/indexer/search/ranking-contributors.js +57 -7
  232. package/dist/indexer/search/ranking-types.js +4 -0
  233. package/dist/indexer/search/ranking.js +31 -6
  234. package/dist/indexer/search/search-attribution.js +67 -0
  235. package/dist/indexer/search/search-hit-enrichers.js +30 -40
  236. package/dist/indexer/search/search-source.js +109 -52
  237. package/dist/indexer/search/semantic-status.js +4 -1
  238. package/dist/indexer/usage/unmigrated-vaults-guard.js +2 -1
  239. package/dist/indexer/usage/usage-events.js +72 -9
  240. package/dist/indexer/walk/file-context.js +1 -44
  241. package/dist/indexer/walk/matchers.js +16 -32
  242. package/dist/indexer/walk/path-resolver.js +6 -5
  243. package/dist/indexer/walk/walker.js +4 -2
  244. package/dist/integrations/agent/engine-resolution.js +22 -0
  245. package/dist/integrations/agent/model-aliases.js +1 -1
  246. package/dist/integrations/agent/prompts.js +35 -11
  247. package/dist/integrations/agent/spawn.js +41 -270
  248. package/dist/integrations/harnesses/aider/agent-builder.js +2 -2
  249. package/dist/integrations/harnesses/aider/index.js +2 -11
  250. package/dist/integrations/harnesses/amazonq/agent-builder.js +1 -9
  251. package/dist/integrations/harnesses/amazonq/index.js +3 -16
  252. package/dist/integrations/harnesses/claude/config-import.js +1 -3
  253. package/dist/integrations/harnesses/claude/index.js +1 -12
  254. package/dist/integrations/harnesses/claude/session-log.js +27 -65
  255. package/dist/integrations/harnesses/codex/agent-builder.js +1 -2
  256. package/dist/integrations/harnesses/codex/index.js +2 -12
  257. package/dist/integrations/harnesses/copilot/agent-builder.js +2 -3
  258. package/dist/integrations/harnesses/copilot/index.js +1 -12
  259. package/dist/integrations/harnesses/gemini/agent-builder.js +1 -3
  260. package/dist/integrations/harnesses/gemini/index.js +1 -12
  261. package/dist/integrations/harnesses/ids.js +24 -0
  262. package/dist/integrations/harnesses/index.js +27 -2
  263. package/dist/integrations/harnesses/opencode/config-import.js +1 -3
  264. package/dist/integrations/harnesses/opencode/index.js +1 -12
  265. package/dist/integrations/harnesses/opencode/session-log.js +67 -110
  266. package/dist/integrations/harnesses/opencode-sdk/harness.js +2 -11
  267. package/dist/integrations/harnesses/openhands/agent-builder.js +3 -3
  268. package/dist/integrations/harnesses/openhands/index.js +2 -11
  269. package/dist/integrations/harnesses/pi/agent-builder.js +3 -11
  270. package/dist/integrations/harnesses/pi/index.js +3 -15
  271. package/dist/integrations/harnesses/shared.js +17 -0
  272. package/dist/integrations/harnesses/types.js +37 -2
  273. package/dist/integrations/lockfile.js +36 -0
  274. package/dist/integrations/session-logs/index.js +21 -12
  275. package/dist/integrations/session-logs/provider-base.js +113 -0
  276. package/dist/llm/client.js +7 -5
  277. package/dist/llm/embedders/deterministic.js +2 -2
  278. package/dist/llm/embedders/remote.js +8 -4
  279. package/dist/llm/graph-extract.js +7 -2
  280. package/dist/llm/metadata-enhance.js +43 -26
  281. package/dist/llm/structured-call.js +15 -7
  282. package/dist/migrate/legacy/config-source-migration.js +223 -0
  283. package/dist/migrate/legacy/content-migration.js +305 -0
  284. package/dist/migrate/legacy/legacy-layout.js +779 -0
  285. package/dist/migrate/legacy/legacy-paths.js +25 -0
  286. package/dist/migrate/legacy/legacy-stash-json.js +72 -0
  287. package/dist/migrate/legacy/proposal-fs-import.js +168 -0
  288. package/dist/migrate/legacy/task-target-ref-migration.js +272 -0
  289. package/dist/migrate/legacy/three-db-cutover.js +840 -0
  290. package/dist/migrate/legacy/workflow-migrations-bodies.js +52 -0
  291. package/dist/migrate/legacy/workflow-migrations-frozen.js +21 -0
  292. package/dist/migrate/legacy-ref-grammar.js +209 -0
  293. package/dist/output/command-registry.js +27 -0
  294. package/dist/output/html-render.js +11 -16
  295. package/dist/output/renderers.js +32 -276
  296. package/dist/output/shapes/passthrough.js +3 -9
  297. package/dist/output/shapes/registry.js +12 -6
  298. package/dist/output/text/command-format.js +547 -0
  299. package/dist/output/text/helpers.js +15 -1377
  300. package/dist/output/text/proposal-format.js +230 -0
  301. package/dist/output/text/registry.js +12 -6
  302. package/dist/output/text/show-directives.js +107 -0
  303. package/dist/output/text/show-format.js +103 -0
  304. package/dist/output/text/workflow-format.js +345 -0
  305. package/dist/output/text.js +1 -3
  306. package/dist/registry/build-index.js +13 -17
  307. package/dist/registry/providers/static-index.js +2 -2
  308. package/dist/registry/resolve.js +4 -90
  309. package/dist/registry/semver.js +93 -0
  310. package/dist/runtime.js +90 -0
  311. package/dist/schemas/akm-config.json +2956 -14890
  312. package/dist/schemas/akm-task.json +2 -2
  313. package/dist/schemas/akm-workflow.json +2 -2
  314. package/dist/scripts/migrate-storage.js +2312 -19642
  315. package/dist/setup/detect.js +5 -7
  316. package/dist/setup/engine-config.js +2 -1
  317. package/dist/setup/registry-stash-loader.js +1 -1
  318. package/dist/setup/semantic-assets.js +12 -9
  319. package/dist/setup/setup.js +109 -39
  320. package/dist/setup/steps/connection-shared.js +120 -0
  321. package/dist/setup/steps/connection.js +50 -274
  322. package/dist/setup/steps/platforms.js +1 -0
  323. package/dist/setup/steps/sources.js +13 -6
  324. package/dist/setup/steps/stashdir.js +5 -2
  325. package/dist/sources/freshness.js +39 -0
  326. package/dist/sources/providers/git-provider.js +29 -33
  327. package/dist/sources/providers/git-stash.js +10 -5
  328. package/dist/sources/providers/provider-utils.js +40 -18
  329. package/dist/sources/providers/website.js +1 -1
  330. package/dist/sources/resolve.js +5 -5
  331. package/dist/sources/snapshot-fetchers/types.js +4 -0
  332. package/dist/sources/{website-ingest.js → snapshot-fetchers/website-ingest.js} +105 -39
  333. package/dist/storage/database.js +47 -3
  334. package/dist/storage/engines/sqlite-migrations.js +34 -16
  335. package/dist/storage/locations.js +1 -2
  336. package/dist/storage/repositories/improve-runs-repository.js +0 -20
  337. package/dist/storage/repositories/index-connection.js +80 -0
  338. package/dist/storage/repositories/index-db.js +4 -3
  339. package/dist/storage/repositories/index-entries-repository.js +952 -0
  340. package/dist/{indexer/db/entry-mapper.js → storage/repositories/index-entry-mapper.js} +15 -2
  341. package/dist/storage/repositories/index-entry-types.js +4 -0
  342. package/dist/storage/repositories/index-fts-repository.js +164 -0
  343. package/dist/storage/repositories/index-llm-cache-repository.js +109 -0
  344. package/dist/storage/repositories/index-meta-repository.js +50 -0
  345. package/dist/{indexer/db/schema.js → storage/repositories/index-schema.js} +241 -100
  346. package/dist/storage/repositories/index-sql.js +12 -0
  347. package/dist/storage/repositories/index-utility-repository.js +369 -0
  348. package/dist/storage/repositories/index-vec-repository.js +245 -0
  349. package/dist/storage/repositories/proposals-repository.js +41 -50
  350. package/dist/storage/repositories/registry-cache.js +1 -1
  351. package/dist/storage/repositories/workflow-runs-repository.js +17 -11
  352. package/dist/tasks/backends/cron.js +5 -5
  353. package/dist/tasks/backends/exec-utils.js +21 -0
  354. package/dist/tasks/backends/index.js +6 -9
  355. package/dist/tasks/backends/launchd.js +16 -17
  356. package/dist/tasks/backends/schtasks.js +33 -41
  357. package/dist/tasks/backends/types.js +4 -0
  358. package/dist/tasks/parser.js +25 -5
  359. package/dist/tasks/runner.js +33 -39
  360. package/dist/tasks/validator.js +5 -6
  361. package/dist/workflows/authoring/authoring.js +8 -11
  362. package/dist/workflows/exec/brief.js +10 -7
  363. package/dist/workflows/exec/report.js +261 -156
  364. package/dist/workflows/exec/run-workflow.js +2 -1
  365. package/dist/workflows/exec/step-work.js +3 -9
  366. package/dist/workflows/program/parser.js +4 -60
  367. package/dist/workflows/renderer.js +11 -60
  368. package/dist/workflows/runtime/checkin.js +1 -1
  369. package/dist/workflows/runtime/plan-classifier.js +7 -4
  370. package/dist/workflows/runtime/runs.js +11 -10
  371. package/dist/workflows/runtime/unit-checkin.js +1 -1
  372. package/dist/workflows/runtime/unit-phases.js +20 -0
  373. package/dist/workflows/runtime/workflow-asset-loader.js +27 -16
  374. package/dist/workflows/validate-summary.js +2 -2
  375. package/dist/workflows/validator.js +18 -2
  376. package/docs/migration/release-notes/0.9.0.md +45 -3
  377. package/docs/migration/v0.8-to-v0.9.md +466 -262
  378. package/docs/{data-and-telemetry.md → reference/data-and-telemetry.md} +61 -17
  379. package/package.json +10 -9
  380. package/schemas/akm-config.json +2956 -14890
  381. package/schemas/akm-task.json +2 -2
  382. package/schemas/akm-workflow.json +2 -2
  383. package/dist/assets/help/help-accept.md +0 -12
  384. package/dist/assets/help/help-improve.md +0 -84
  385. package/dist/assets/help/help-proposals.md +0 -17
  386. package/dist/assets/help/help-propose.md +0 -17
  387. package/dist/assets/help/help-reject.md +0 -11
  388. package/dist/assets/improve-strategies/recombine-only.json +0 -23
  389. package/dist/assets/improve-strategies/synthesize.json +0 -17
  390. package/dist/assets/prompts/procedural-system.md +0 -44
  391. package/dist/assets/prompts/recombine-system.md +0 -40
  392. package/dist/assets/prompts/staleness-detect-system.md +0 -6
  393. package/dist/assets/templates/html/default.html +0 -78
  394. package/dist/assets/templates/html/vendor/echarts.min.js +0 -45
  395. package/dist/assets/wiki/index-template.md +0 -12
  396. package/dist/assets/wiki/ingest-workflow-template.md +0 -83
  397. package/dist/assets/wiki/log-template.md +0 -8
  398. package/dist/assets/wiki/schema-template.md +0 -61
  399. package/dist/commands/improve/calibration.js +0 -161
  400. package/dist/commands/improve/dedup.js +0 -482
  401. package/dist/commands/improve/hot-probation.js +0 -45
  402. package/dist/commands/improve/improve-auto-accept.js +0 -262
  403. package/dist/commands/improve/procedural.js +0 -403
  404. package/dist/commands/improve/recombine.js +0 -843
  405. package/dist/commands/improve/schema-similarity-gate.js +0 -168
  406. package/dist/commands/lint/agent-linter.js +0 -44
  407. package/dist/commands/lint/command-linter.js +0 -44
  408. package/dist/commands/lint/default-linter.js +0 -16
  409. package/dist/commands/lint/fact-linter.js +0 -39
  410. package/dist/commands/lint/knowledge-linter.js +0 -16
  411. package/dist/commands/lint/memory-linter.js +0 -61
  412. package/dist/commands/lint/registry.js +0 -41
  413. package/dist/commands/lint/skill-linter.js +0 -45
  414. package/dist/commands/lint/task-linter.js +0 -50
  415. package/dist/commands/lint/workflow-linter.js +0 -81
  416. package/dist/commands/proposal/legacy-import.js +0 -115
  417. package/dist/commands/wiki-cli.js +0 -291
  418. package/dist/core/asset/asset-registry.js +0 -76
  419. package/dist/core/asset/asset-spec.js +0 -316
  420. package/dist/core/eval/rank-metrics.js +0 -113
  421. package/dist/core/ripgrep/install.js +0 -163
  422. package/dist/core/ripgrep/resolve.js +0 -81
  423. package/dist/indexer/db/db.js +0 -1585
  424. package/dist/indexer/manifest.js +0 -170
  425. package/dist/indexer/passes/metadata-contributors.js +0 -31
  426. package/dist/integrations/harnesses/opencode-sdk/index.js +0 -25
  427. package/dist/output/text/wiki.js +0 -16
  428. package/dist/scripts/migrations/import-fs-improve-runs-to-db.js +0 -18228
  429. package/dist/scripts/migrations/v16-to-v17.js +0 -141
  430. package/dist/storage/repositories/consolidation-repository.js +0 -38
  431. package/dist/storage/repositories/recombine-repository.js +0 -213
  432. package/dist/wiki/wiki-templates.js +0 -15
  433. package/dist/wiki/wiki.js +0 -1013
  434. package/dist/workflows/db.js +0 -413
  435. package/docs/README.md +0 -103
  436. package/docs/migration/release-notes/0.9.0-beta.60.md +0 -19
  437. /package/dist/sources/{wiki-fetchers → snapshot-fetchers}/registry.js +0 -0
  438. /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
+ }