akm-cli 0.9.0-rc.9 → 0.9.1-beta.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (477) hide show
  1. package/CHANGELOG.md +1770 -44
  2. package/README.md +38 -47
  3. package/SECURITY.md +14 -1
  4. package/STABILITY.md +499 -0
  5. package/dist/akm +148 -35
  6. package/dist/{akm-migrate-storage → akm-migrate} +6 -9
  7. package/dist/assets/hints/cli-hints-full.md +223 -95
  8. package/dist/assets/hints/cli-hints-short.md +85 -22
  9. package/dist/assets/improve-strategies/default.json +1 -1
  10. package/dist/assets/improve-strategies/reflect-distill.json +1 -1
  11. package/dist/assets/prompts/memory-infer-user.md +2 -3
  12. package/dist/assets/stash-skeleton/README.md +6 -5
  13. package/dist/assets/stash-skeleton/facts/conventions/assets/agent.md +2 -0
  14. package/dist/assets/stash-skeleton/facts/conventions/assets/command.md +2 -0
  15. package/dist/assets/stash-skeleton/facts/conventions/assets/fact.md +2 -0
  16. package/dist/assets/stash-skeleton/facts/conventions/assets/knowledge.md +2 -0
  17. package/dist/assets/stash-skeleton/facts/conventions/assets/lesson.md +2 -0
  18. package/dist/assets/stash-skeleton/facts/conventions/assets/memory.md +2 -0
  19. package/dist/assets/stash-skeleton/facts/conventions/assets/script.md +2 -0
  20. package/dist/assets/stash-skeleton/facts/conventions/assets/skill.md +2 -0
  21. package/dist/assets/stash-skeleton/facts/conventions/assets/workflow.md +2 -0
  22. package/dist/assets/stash-skeleton/facts/conventions/backlinks.md +2 -0
  23. package/dist/assets/stash-skeleton/facts/conventions/domains.md +2 -0
  24. package/dist/assets/stash-skeleton/facts/conventions/organization.md +20 -9
  25. package/dist/assets/tasks/core/extract.yml +1 -1
  26. package/dist/assets/tasks/core/version-check.yml +1 -1
  27. package/dist/assets/tasks/improve/akm-graph-refresh-weekly.yml +5 -0
  28. package/dist/assets/tasks/improve/akm-improve-catchup.yml +8 -0
  29. package/dist/assets/tasks/improve/akm-improve-consolidate.yml +5 -0
  30. package/dist/assets/tasks/improve/akm-improve-frequent.yml +5 -0
  31. package/dist/assets/tasks/improve/akm-improve-nightly.yml +5 -0
  32. package/dist/assets/templates/html/health.html +1 -3
  33. package/dist/assets/workflows/workflow-template.md +32 -15
  34. package/dist/cli/invocation.js +40 -15
  35. package/dist/cli/parse-args.js +0 -22
  36. package/dist/cli/retired-commands.js +121 -0
  37. package/dist/cli/shared.js +154 -22
  38. package/dist/cli/unknown-flags.js +236 -0
  39. package/dist/cli-node.mjs +2 -1
  40. package/dist/cli.js +696 -258
  41. package/dist/commands/agent/agent-dispatch.js +14 -3
  42. package/dist/commands/agent/contribute-cli.js +74 -89
  43. package/dist/commands/completions.js +79 -22
  44. package/dist/commands/config-cli.js +17 -150
  45. package/dist/commands/env/env-cli.js +59 -143
  46. package/dist/commands/env/env.js +12 -163
  47. package/dist/commands/env/marker-path.js +6 -0
  48. package/dist/commands/env/secret-cli.js +36 -66
  49. package/dist/commands/env/secret.js +24 -57
  50. package/dist/commands/feedback-cli.js +148 -88
  51. package/dist/commands/health/accept-rate.js +58 -0
  52. package/dist/commands/health/advisories.js +3 -4
  53. package/dist/commands/health/checks.js +85 -23
  54. package/dist/commands/health/html-report.js +7 -10
  55. package/dist/commands/health/improve-metrics.js +25 -83
  56. package/dist/commands/health/llm-usage.js +2 -1
  57. package/dist/commands/health/md-report.js +5 -9
  58. package/dist/commands/health/metrics.js +62 -20
  59. package/dist/commands/health/renderers.js +47 -0
  60. package/dist/commands/health/report-view-model.js +4 -5
  61. package/dist/commands/health/stash-exposure.js +1 -1
  62. package/dist/commands/health/surfaces.js +4 -122
  63. package/dist/commands/health/task-runs.js +3 -67
  64. package/dist/commands/health/types-improve.js +7 -0
  65. package/dist/commands/health.js +160 -35
  66. package/dist/commands/improve/anti-collapse.js +2 -2
  67. package/dist/commands/improve/autonomy-gate.js +68 -0
  68. package/dist/commands/improve/collapse-detector.js +41 -40
  69. package/dist/commands/improve/consolidate/eligibility.js +1 -23
  70. package/dist/commands/improve/consolidate/merge.js +4 -0
  71. package/dist/commands/improve/consolidate.js +140 -1000
  72. package/dist/commands/improve/distill/promote-memory.js +12 -12
  73. package/dist/commands/improve/distill/quality-gate.js +11 -6
  74. package/dist/commands/improve/distill.js +58 -69
  75. package/dist/commands/improve/eligibility.js +101 -55
  76. package/dist/commands/improve/extract-cli.js +14 -133
  77. package/dist/commands/improve/improve-cli.js +98 -114
  78. package/dist/commands/improve/improve-result-file.js +1 -28
  79. package/dist/commands/improve/improve-strategies.js +8 -5
  80. package/dist/commands/improve/improve.js +254 -101
  81. package/dist/commands/improve/locks.js +7 -0
  82. package/dist/commands/improve/loop-stages.js +182 -20
  83. package/dist/commands/improve/memory/derived-ref.js +45 -43
  84. package/dist/commands/improve/memory/memory-belief.js +1 -1
  85. package/dist/commands/improve/memory/memory-contradiction-detect.js +4 -12
  86. package/dist/commands/improve/memory/memory-improve.js +14 -5
  87. package/dist/commands/improve/outcome-loop.js +22 -65
  88. package/dist/commands/improve/preparation.js +114 -123
  89. package/dist/commands/improve/proactive-maintenance.js +2 -5
  90. package/dist/commands/improve/reflect.js +56 -160
  91. package/dist/commands/improve/run-context.js +5 -0
  92. package/dist/commands/improve/salience.js +11 -122
  93. package/dist/commands/improve/session-asset.js +4 -0
  94. package/dist/commands/improve/source-identity.js +10 -38
  95. package/dist/commands/lint/base-linter.js +51 -131
  96. package/dist/commands/lint/env-key-rules.js +31 -47
  97. package/dist/commands/lint/index.js +444 -88
  98. package/dist/commands/lint/types.js +22 -1
  99. package/dist/commands/{events.js → log.js} +33 -38
  100. package/dist/commands/migrate-cli.js +92 -12
  101. package/dist/commands/migration-tool.js +46 -0
  102. package/dist/commands/observability-cli.js +70 -209
  103. package/dist/commands/proposal/drain.js +101 -29
  104. package/dist/commands/proposal/proposal-cli.js +76 -48
  105. package/dist/commands/proposal/proposal.js +54 -18
  106. package/dist/commands/proposal/propose-cli.js +88 -0
  107. package/dist/commands/proposal/propose.js +23 -15
  108. package/dist/commands/proposal/repository.js +718 -279
  109. package/dist/commands/proposal/validators/proposal-quality-validators.js +2 -8
  110. package/dist/commands/proposal/validators/proposal-validators.js +55 -7
  111. package/dist/commands/proposal/validators/proposals.js +4 -7
  112. package/dist/commands/read/curate.js +34 -53
  113. package/dist/commands/read/knowledge.js +150 -95
  114. package/dist/commands/read/registry-search.js +2 -2
  115. package/dist/commands/read/remember-cli.js +42 -15
  116. package/dist/commands/read/search-cli.js +180 -78
  117. package/dist/commands/read/search.js +58 -43
  118. package/dist/commands/read/show.js +197 -141
  119. package/dist/commands/registry-cli.js +12 -51
  120. package/dist/commands/remember.js +14 -57
  121. package/dist/commands/sources/add-cli.js +100 -31
  122. package/dist/commands/sources/bundle-cli.js +166 -0
  123. package/dist/commands/sources/bundle-config-ops.js +7 -2
  124. package/dist/commands/sources/info.js +30 -7
  125. package/dist/commands/sources/init.js +12 -12
  126. package/dist/commands/sources/installed-stashes.js +387 -98
  127. package/dist/commands/sources/schema-repair.js +3 -2
  128. package/dist/commands/sources/self-update.js +131 -38
  129. package/dist/commands/sources/source-add.js +72 -17
  130. package/dist/commands/sources/source-clone.js +129 -45
  131. package/dist/commands/sources/source-manage.js +43 -23
  132. package/dist/commands/sources/sources-cli.js +57 -208
  133. package/dist/commands/sources/stash-cli.js +46 -53
  134. package/dist/commands/tasks/tasks-cli.js +91 -97
  135. package/dist/commands/tasks/tasks.js +284 -423
  136. package/dist/commands/workflow-cli.js +181 -450
  137. package/dist/core/abort-deadline.js +28 -0
  138. package/dist/core/adapter/adapters/agent-skills-adapter.js +67 -5
  139. package/dist/core/adapter/adapters/akm-adapter.js +60 -38
  140. package/dist/core/adapter/adapters/akm-lint.js +109 -42
  141. package/dist/core/adapter/adapters/akm-metadata.js +15 -44
  142. package/dist/core/adapter/adapters/akm-task-adapter.js +54 -31
  143. package/dist/core/adapter/adapters/akm-workflow-adapter.js +55 -71
  144. package/dist/core/adapter/adapters/dotenv-adapter.js +1 -1
  145. package/dist/core/adapter/adapters/generic-files-adapter.js +2 -0
  146. package/dist/core/adapter/adapters/index.js +6 -6
  147. package/dist/core/adapter/adapters/llm-wiki-adapter.js +14 -8
  148. package/dist/core/adapter/adapters/okf-adapter.js +187 -19
  149. package/dist/core/adapter/adapters/shared.js +3 -19
  150. package/dist/core/adapter/adapters/tool-dir-shared.js +13 -6
  151. package/dist/core/adapter/adapters/website-snapshot-adapter.js +1 -0
  152. package/dist/core/adapter/detect-adapter.js +17 -0
  153. package/dist/core/adapter/recognize-match.js +6 -4
  154. package/dist/core/adapter/validate-context.js +214 -0
  155. package/dist/core/asset/akm-markdown.js +63 -0
  156. package/dist/core/asset/asset-placement.js +20 -6
  157. package/dist/core/asset/asset-ref.js +11 -9
  158. package/dist/core/asset/frontmatter-lint.js +30 -0
  159. package/dist/core/asset/frontmatter.js +29 -9
  160. package/dist/core/asset/markdown.js +40 -51
  161. package/dist/core/asset/resolve-ref.js +89 -18
  162. package/dist/core/asset/stash-meta.js +1 -1
  163. package/dist/core/bundle-id.js +51 -0
  164. package/dist/core/common.js +218 -44
  165. package/dist/core/concurrent.js +32 -0
  166. package/dist/core/config/config-io.js +12 -1
  167. package/dist/core/config/config-schema.js +35 -8
  168. package/dist/core/config/config-sources.js +55 -11
  169. package/dist/core/config/config-walker.js +25 -9
  170. package/dist/core/config/config.js +9 -48
  171. package/dist/core/config/experimental.js +21 -0
  172. package/dist/core/config/schema/embedding.js +5 -1
  173. package/dist/core/config/schema/experimental.js +30 -0
  174. package/dist/core/config/schema/improve-processes.js +0 -6
  175. package/dist/core/config/schema/improve.js +21 -3
  176. package/dist/core/config/schema/index-config.js +8 -15
  177. package/dist/core/config/schema/output.js +4 -1
  178. package/dist/core/config/schema/setup.js +9 -18
  179. package/dist/core/config/schema/sources-bundles.js +49 -33
  180. package/dist/core/config/schema/workflow.js +14 -3
  181. package/dist/core/env-secret-ref.js +76 -46
  182. package/dist/core/errors.js +43 -12
  183. package/dist/core/events.js +76 -152
  184. package/dist/core/file-change.js +6 -5
  185. package/dist/core/file-lock.js +7 -1
  186. package/dist/core/fs-txn.js +83 -7
  187. package/dist/core/git-message.js +2 -2
  188. package/dist/core/improve-result.js +6 -100
  189. package/dist/core/json-schema.js +327 -9
  190. package/dist/core/lesson-lint.js +1 -17
  191. package/dist/core/logs-db.js +2 -1
  192. package/dist/core/loopback.js +89 -0
  193. package/dist/core/migration-operation.js +33 -2
  194. package/dist/core/mutation-target.js +78 -0
  195. package/dist/core/parse.js +4 -1
  196. package/dist/core/path-access.js +107 -0
  197. package/dist/core/paths.js +17 -20
  198. package/dist/core/recognition-util.js +12 -14
  199. package/dist/core/redaction.js +98 -17
  200. package/dist/core/spawn-env.js +234 -0
  201. package/dist/core/standards/resolve-standards-context.js +2 -14
  202. package/dist/core/standards/resolve-stash-standards.js +2 -2
  203. package/dist/core/standards/resolve-type-conventions.js +2 -2
  204. package/dist/core/state/migrations.js +41 -18
  205. package/dist/core/state-db-scope.js +134 -0
  206. package/dist/core/state-db.js +5 -14
  207. package/dist/core/structured.js +1 -1
  208. package/dist/core/subprocess.js +182 -36
  209. package/dist/core/text-truncation.js +9 -5
  210. package/dist/core/type-presentation.js +3 -3
  211. package/dist/core/warn.js +0 -3
  212. package/dist/core/write-provenance.js +85 -0
  213. package/dist/core/write-source.js +778 -95
  214. package/dist/indexer/bundle-identity-guard.js +3 -2
  215. package/dist/indexer/db/graph-db.js +17 -30
  216. package/dist/indexer/ensure-index.js +11 -3
  217. package/dist/indexer/graph/graph-boost.js +9 -34
  218. package/dist/indexer/graph/graph-extraction.js +8 -5
  219. package/dist/indexer/index-writer-lock.js +53 -17
  220. package/dist/indexer/index-written-assets.js +33 -24
  221. package/dist/indexer/indexer.js +551 -253
  222. package/dist/indexer/installations.js +14 -96
  223. package/dist/indexer/passes/dir-staleness.js +16 -9
  224. package/dist/indexer/passes/memory-inference.js +15 -9
  225. package/dist/indexer/passes/metadata.js +113 -47
  226. package/dist/indexer/scan/doc-to-entry.js +38 -1
  227. package/dist/indexer/scan/drain-dir.js +13 -23
  228. package/dist/indexer/search/db-search.js +107 -69
  229. package/dist/indexer/search/fts-query.js +47 -24
  230. package/dist/indexer/search/ranking-contributors.js +42 -20
  231. package/dist/indexer/search/ranking.js +18 -99
  232. package/dist/indexer/search/search-fields.js +7 -2
  233. package/dist/indexer/search/search-source.js +82 -93
  234. package/dist/indexer/usage/usage-events.js +0 -89
  235. package/dist/indexer/walk/file-context.js +2 -1
  236. package/dist/indexer/walk/matchers.js +30 -43
  237. package/dist/indexer/walk/path-resolver.js +7 -2
  238. package/dist/indexer/walk/walker.js +38 -12
  239. package/dist/integrations/agent/builders.js +0 -6
  240. package/dist/integrations/agent/config.js +2 -2
  241. package/dist/integrations/agent/detect.js +49 -19
  242. package/dist/integrations/agent/engine-fallback.js +76 -0
  243. package/dist/integrations/agent/engine-resolution.js +24 -11
  244. package/dist/integrations/agent/model-aliases.js +1 -1
  245. package/dist/integrations/agent/profiles.js +23 -1
  246. package/dist/integrations/agent/prompts.js +12 -8
  247. package/dist/integrations/agent/runner-dispatch.js +4 -2
  248. package/dist/integrations/agent/runner.js +0 -1
  249. package/dist/integrations/agent/spawn.js +20 -93
  250. package/dist/integrations/github.js +1 -1
  251. package/dist/integrations/harnesses/aider/agent-builder.js +6 -4
  252. package/dist/integrations/harnesses/amazonq/agent-builder.js +7 -4
  253. package/dist/integrations/harnesses/claude/session-log.js +0 -10
  254. package/dist/integrations/harnesses/codex/agent-builder.js +5 -2
  255. package/dist/integrations/harnesses/copilot/agent-builder.js +5 -3
  256. package/dist/integrations/harnesses/gemini/agent-builder.js +5 -3
  257. package/dist/integrations/harnesses/index.js +3 -7
  258. package/dist/integrations/harnesses/opencode/agent-builder.js +21 -2
  259. package/dist/integrations/harnesses/opencode/session-log.js +0 -15
  260. package/dist/integrations/harnesses/opencode-sdk/sdk-runner.js +13 -4
  261. package/dist/integrations/harnesses/openhands/agent-builder.js +9 -6
  262. package/dist/integrations/harnesses/pi/agent-builder.js +6 -4
  263. package/dist/integrations/lockfile.js +132 -8
  264. package/dist/integrations/session-logs/index.js +3 -28
  265. package/dist/llm/client.js +136 -100
  266. package/dist/llm/embedders/remote.js +13 -5
  267. package/dist/llm/feature-gate.js +4 -12
  268. package/dist/llm/graph-extract.js +5 -11
  269. package/dist/llm/memory-infer.js +144 -1
  270. package/dist/llm/metadata-enhance.js +5 -7
  271. package/dist/llm/structured-call.js +1 -1
  272. package/dist/llm/usage-persist.js +26 -5
  273. package/dist/llm/usage-telemetry.js +25 -2
  274. package/dist/output/cli-hints.js +1 -2
  275. package/dist/output/context.js +22 -7
  276. package/dist/output/format-exempt.js +80 -0
  277. package/dist/output/generic-render.js +259 -0
  278. package/dist/output/render-registry.js +57 -0
  279. package/dist/output/renderers.js +14 -36
  280. package/dist/output/shapes/curate.js +10 -1
  281. package/dist/output/shapes/events.js +12 -7
  282. package/dist/output/shapes/helpers.js +56 -83
  283. package/dist/output/shapes/migrate.js +8 -0
  284. package/dist/output/shapes/passthrough.js +7 -41
  285. package/dist/output/shapes/proposal/producer.js +15 -7
  286. package/dist/output/shapes.js +2 -9
  287. package/dist/output/text/{init.js → bundle-create.js} +3 -1
  288. package/dist/output/text/bundle-show.js +7 -0
  289. package/dist/output/text/command-format.js +164 -96
  290. package/dist/output/text/env.js +1 -3
  291. package/dist/output/text/events.js +8 -7
  292. package/dist/output/text/health-format.js +103 -0
  293. package/dist/output/text/health.js +7 -0
  294. package/dist/output/text/helpers.js +10 -8
  295. package/dist/output/text/lint-format.js +56 -0
  296. package/dist/{migrate-storage-node.mjs → output/text/lint.js} +2 -5
  297. package/dist/output/text/migrate.js +88 -0
  298. package/dist/output/text/proposal/producer.js +4 -2
  299. package/dist/output/text/proposal-format.js +44 -72
  300. package/dist/output/text/registry-commands.js +1 -2
  301. package/dist/output/text/show-directives.js +15 -7
  302. package/dist/output/text/status-list.js +32 -0
  303. package/dist/output/text/{save.js → sync.js} +2 -2
  304. package/dist/output/text/workflow-format.js +24 -203
  305. package/dist/output/text/workflow.js +1 -7
  306. package/dist/output/text.js +16 -17
  307. package/dist/registry/factory.js +4 -6
  308. package/dist/registry/origin-resolve.js +16 -27
  309. package/dist/registry/providers/skills-sh.js +3 -3
  310. package/dist/registry/providers/static-index.js +13 -23
  311. package/dist/registry/resolve.js +42 -7
  312. package/dist/registry/semver.js +34 -84
  313. package/dist/runtime.js +2 -23
  314. package/dist/scripts/akm-migrate-node.js +60972 -0
  315. package/dist/scripts/akm-migrate.js +60310 -0
  316. package/dist/setup/detect.js +42 -15
  317. package/dist/setup/registry-stash-loader.js +2 -2
  318. package/dist/setup/setup.js +236 -136
  319. package/dist/setup/steps/connection.js +7 -9
  320. package/dist/setup/steps/platforms.js +9 -9
  321. package/dist/setup/steps/semantic.js +15 -3
  322. package/dist/setup/steps/sources.js +12 -13
  323. package/dist/setup/steps/stashdir.js +2 -3
  324. package/dist/setup/steps/tasks.js +237 -120
  325. package/dist/sources/freshness.js +1 -1
  326. package/dist/sources/provider-factory.js +11 -17
  327. package/dist/sources/providers/filesystem.js +2 -3
  328. package/dist/sources/providers/git-install.js +278 -34
  329. package/dist/sources/providers/git-provider.js +25 -23
  330. package/dist/sources/providers/git-stash.js +414 -106
  331. package/dist/sources/providers/git.js +2 -2
  332. package/dist/sources/providers/npm.js +16 -19
  333. package/dist/sources/providers/provider-utils.js +7 -4
  334. package/dist/sources/providers/sync-from-ref.js +3 -9
  335. package/dist/sources/providers/website.js +6 -1
  336. package/dist/sources/resolve.js +6 -5
  337. package/dist/sources/snapshot-fetchers/bluesky.js +146 -0
  338. package/dist/sources/snapshot-fetchers/content-extract.js +566 -0
  339. package/dist/sources/snapshot-fetchers/fetcher-util.js +41 -0
  340. package/dist/sources/snapshot-fetchers/github.js +100 -0
  341. package/dist/sources/snapshot-fetchers/host-guard.js +291 -0
  342. package/dist/sources/snapshot-fetchers/registry.js +17 -1
  343. package/dist/sources/snapshot-fetchers/robots.js +348 -0
  344. package/dist/sources/snapshot-fetchers/rss.js +282 -0
  345. package/dist/sources/snapshot-fetchers/secret-seam.js +42 -0
  346. package/dist/sources/snapshot-fetchers/website-ingest.js +686 -282
  347. package/dist/sources/snapshot-fetchers/x.js +910 -0
  348. package/dist/storage/database.js +13 -1
  349. package/dist/storage/engines/sqlite-migrations.js +23 -111
  350. package/dist/storage/managed-db.js +21 -2
  351. package/dist/storage/repositories/canaries-repository.js +1 -1
  352. package/dist/storage/repositories/events-repository.js +27 -11
  353. package/dist/storage/repositories/improve-runs-repository.js +6 -12
  354. package/dist/storage/repositories/index-connection.js +44 -8
  355. package/dist/storage/repositories/index-entries-repository.js +157 -241
  356. package/dist/storage/repositories/index-entry-mapper.js +15 -11
  357. package/dist/storage/repositories/index-fts-repository.js +5 -2
  358. package/dist/storage/repositories/index-llm-cache-repository.js +0 -1
  359. package/dist/storage/repositories/index-meta-repository.js +2 -3
  360. package/dist/storage/repositories/index-schema.js +10 -25
  361. package/dist/storage/repositories/index-utility-repository.js +15 -28
  362. package/dist/storage/repositories/index-vec-repository.js +6 -1
  363. package/dist/storage/repositories/outcome-repository.js +119 -0
  364. package/dist/storage/repositories/proposals-repository.js +296 -59
  365. package/dist/storage/repositories/registry-cache.js +19 -0
  366. package/dist/storage/repositories/salience-repository.js +172 -0
  367. package/dist/storage/repositories/task-history-repository.js +15 -13
  368. package/dist/storage/repositories/workflow-runs-repository.js +118 -53
  369. package/dist/tasks/backends/cron.js +105 -15
  370. package/dist/tasks/backends/index.js +1 -1
  371. package/dist/tasks/backends/launchd.js +85 -38
  372. package/dist/tasks/backends/schtasks.js +135 -15
  373. package/dist/tasks/embedded.js +56 -40
  374. package/dist/tasks/log-redaction.js +156 -0
  375. package/dist/tasks/parser.js +87 -160
  376. package/dist/tasks/resolve-akm-bin.js +137 -59
  377. package/dist/tasks/runner.js +209 -44
  378. package/dist/tasks/scheduler-invocation.js +220 -10
  379. package/dist/tasks/schema.js +89 -1
  380. package/dist/tasks/task-id.js +1 -3
  381. package/dist/tasks/validator.js +20 -6
  382. package/dist/workflows/authoring/authoring.js +94 -143
  383. package/dist/workflows/authoring/scope-key.js +1 -1
  384. package/dist/workflows/concurrency-policy.js +95 -1
  385. package/dist/workflows/exec/dispatch-redaction.js +114 -0
  386. package/dist/workflows/exec/exec-unit.js +542 -0
  387. package/dist/workflows/exec/frozen-judge.js +130 -32
  388. package/dist/workflows/exec/native-executor.js +513 -274
  389. package/dist/workflows/exec/param-secrets.js +12 -11
  390. package/dist/workflows/exec/run-workflow.js +496 -237
  391. package/dist/workflows/exec/step-work.js +616 -412
  392. package/dist/workflows/exec/unit-dispatch.js +34 -0
  393. package/dist/workflows/exec/unit-writer.js +53 -13
  394. package/dist/workflows/exec/worktree.js +454 -41
  395. package/dist/workflows/ir/compile.js +166 -271
  396. package/dist/workflows/ir/freeze.js +119 -42
  397. package/dist/workflows/ir/params.js +135 -11
  398. package/dist/workflows/ir/plan-hash.js +1 -1
  399. package/dist/workflows/ir/schema.js +129 -45
  400. package/dist/workflows/parser.js +1070 -307
  401. package/dist/workflows/program/expressions.js +20 -208
  402. package/dist/workflows/program/schema.js +31 -10
  403. package/dist/workflows/renderer.js +123 -68
  404. package/dist/workflows/resource-limits.js +184 -0
  405. package/dist/workflows/runtime/checkin.js +3 -3
  406. package/dist/workflows/runtime/plan-classifier.js +16 -75
  407. package/dist/workflows/runtime/runs.js +331 -132
  408. package/dist/workflows/runtime/unit-checkin.js +1 -1
  409. package/dist/workflows/runtime/unit-phases.js +2 -2
  410. package/dist/workflows/runtime/workflow-asset-loader.js +232 -83
  411. package/dist/workflows/schema.js +1 -11
  412. package/dist/workflows/validate-summary.js +47 -38
  413. package/dist/workflows/validator.js +21 -62
  414. package/docs/README.md +110 -0
  415. package/docs/migration/README.md +8 -0
  416. package/docs/migration/release-notes/0.7.0.md +11 -11
  417. package/docs/migration/release-notes/0.9.0.md +209 -27
  418. package/docs/migration/v0.7-to-v0.8.md +48 -48
  419. package/docs/migration/v0.8-to-v0.9.md +567 -209
  420. package/docs/migration/v0.9.0-troubleshooting.md +561 -0
  421. package/docs/reference/README.md +19 -0
  422. package/docs/reference/bundle-types.md +19 -0
  423. package/docs/reference/cli.md +2342 -0
  424. package/docs/reference/configuration.md +371 -0
  425. package/docs/reference/data-and-telemetry.md +129 -46
  426. package/docs/reference/supported-formats.md +50 -0
  427. package/docs/reference/workflow-schema.md +1014 -0
  428. package/docs/reference/workflows.md +51 -0
  429. package/package.json +31 -13
  430. package/schemas/akm-asset-envelope.json +93 -0
  431. package/schemas/akm-config.json +89 -128
  432. package/schemas/akm-task.json +27 -5
  433. package/schemas/akm-workflow.json +157 -81
  434. package/dist/assets/tasks/core/backup.yml +0 -5
  435. package/dist/assets/tasks/graph-refresh-weekly.yml +0 -10
  436. package/dist/cli/config-migrate.js +0 -1878
  437. package/dist/cli/config-validate.js +0 -41
  438. package/dist/commands/backup-cli.js +0 -56
  439. package/dist/commands/bundle/bundle-cli.js +0 -68
  440. package/dist/commands/bundle/bundle.js +0 -219
  441. package/dist/commands/graph/graph-cli.js +0 -124
  442. package/dist/commands/graph/graph.js +0 -489
  443. package/dist/commands/improve/extract-watch.js +0 -140
  444. package/dist/commands/mv-cli.js +0 -1221
  445. package/dist/commands/sources/history.js +0 -201
  446. package/dist/commands/tasks/default-tasks.js +0 -186
  447. package/dist/core/migration-backup.js +0 -1231
  448. package/dist/indexer/usage/unmigrated-vaults-guard.js +0 -95
  449. package/dist/llm/memory-infer-impl.js +0 -138
  450. package/dist/migrate/legacy/config-source-migration.js +0 -257
  451. package/dist/migrate/legacy/content-migration.js +0 -350
  452. package/dist/migrate/legacy/legacy-layout.js +0 -779
  453. package/dist/migrate/legacy/legacy-paths.js +0 -25
  454. package/dist/migrate/legacy/legacy-stash-json.js +0 -80
  455. package/dist/migrate/legacy/proposal-fs-import.js +0 -168
  456. package/dist/migrate/legacy/task-target-ref-migration.js +0 -278
  457. package/dist/migrate/legacy/three-db-cutover.js +0 -845
  458. package/dist/migrate/legacy/workflow-migrations-bodies.js +0 -52
  459. package/dist/migrate/legacy/workflow-migrations-frozen.js +0 -21
  460. package/dist/migrate/legacy-ref-grammar.js +0 -214
  461. package/dist/output/shapes/distill.js +0 -14
  462. package/dist/output/shapes/history.js +0 -11
  463. package/dist/output/text/distill.js +0 -6
  464. package/dist/output/text/enable-disable.js +0 -8
  465. package/dist/output/text/history.js +0 -6
  466. package/dist/registry/build-index.js +0 -382
  467. package/dist/schemas/akm-config.json +0 -4704
  468. package/dist/schemas/akm-task.json +0 -87
  469. package/dist/schemas/akm-workflow.json +0 -372
  470. package/dist/scripts/migrate-storage.js +0 -3816
  471. package/dist/workflows/authoring/workflow-program-template.yaml +0 -31
  472. package/dist/workflows/cli.js +0 -53
  473. package/dist/workflows/exec/brief.js +0 -481
  474. package/dist/workflows/exec/report.js +0 -1460
  475. package/dist/workflows/exec/watch.js +0 -116
  476. package/dist/workflows/program/parser.js +0 -813
  477. package/dist/workflows/program/project.js +0 -104
@@ -25,12 +25,106 @@
25
25
  * WARNING (consumers tolerate broken links). Reads go through
26
26
  * `ctx.readFile`; ref existence via `ctx.resolveRef`. Never touches the live
27
27
  * filesystem.
28
- * - placeNew / directoryList / looksLikeRoot per §5 / §1.2.
28
+ * - directoryList / looksLikeRoot per §5 / §1.2. Authoring is deliberately
29
+ * absent; AKM-native writers fail before mutating an OKF bundle.
29
30
  */
30
31
  import fs from "node:fs";
31
32
  import path from "node:path";
32
33
  import { parseFrontmatter } from "../../asset/frontmatter.js";
33
34
  import { hashContent, nonEmptyString, readTags, runBaseValidateChecks } from "./shared.js";
35
+ /** v0.2 frontmatter keys consumed into first-class fields below (§0.1) — excluded from the generic `documentJson` extras fold alongside the v0.1 five, so nothing is duplicated between a first-class field and the opaque extras bag. */
36
+ const CONSUMED_FRONTMATTER_KEYS = [
37
+ "type",
38
+ "title",
39
+ "description",
40
+ "tags",
41
+ "timestamp",
42
+ "generated",
43
+ "verified",
44
+ "sources",
45
+ "status",
46
+ "stale_after",
47
+ "okf_version",
48
+ ];
49
+ /** True for a plain (non-null, non-array) object — the shape every v0.2 mapping (`generated`, one `verified`/`sources` entry) must have. */
50
+ function isPlainObject(value) {
51
+ return value !== null && typeof value === "object" && !Array.isArray(value);
52
+ }
53
+ /**
54
+ * Parse one `verified:` actor mapping (`{by, at?}`). Tolerant: a missing/blank
55
+ * `by` yields `undefined` (the entry is dropped, never rejecting the document
56
+ * — OKF conformance leniency); `at` is independently optional.
57
+ */
58
+ function parseActorMapping(value) {
59
+ if (!isPlainObject(value))
60
+ return undefined;
61
+ const by = nonEmptyString(value.by);
62
+ if (by === undefined)
63
+ return undefined;
64
+ const at = nonEmptyString(value.at);
65
+ return at !== undefined ? { by, at } : { by };
66
+ }
67
+ /**
68
+ * Parse the `verified:` family. v0.2 permits EITHER a list of `{by, at?}`
69
+ * mappings OR a single mapping written without the list dash (the shorthand
70
+ * form) — both normalize to a non-empty array here. Malformed entries are
71
+ * dropped individually; an entirely-empty/malformed result is `undefined`
72
+ * rather than `[]` (mirrors every other optional-field convention on this
73
+ * adapter: absent, not empty).
74
+ */
75
+ function parseVerified(value) {
76
+ if (value === undefined || value === null)
77
+ return undefined;
78
+ const candidates = Array.isArray(value) ? value : [value];
79
+ const out = [];
80
+ for (const candidate of candidates) {
81
+ const parsed = parseActorMapping(candidate);
82
+ if (parsed)
83
+ out.push(parsed);
84
+ }
85
+ return out.length > 0 ? out : undefined;
86
+ }
87
+ /**
88
+ * Parse the `sources:` object-list family. Each entry needs at minimum a
89
+ * non-empty `resource`; entries failing that are dropped individually (never
90
+ * rejects the document). This is a DIFFERENT shape from the AKM-native
91
+ * `IndexDocument.sources: string[]` (wiki citations) — the two are never
92
+ * mixed, hence the caller lands this under `provenance.sources` instead.
93
+ */
94
+ function parseOkfSources(value) {
95
+ if (!Array.isArray(value))
96
+ return undefined;
97
+ const out = [];
98
+ for (const item of value) {
99
+ if (!isPlainObject(item))
100
+ continue;
101
+ const resource = nonEmptyString(item.resource);
102
+ if (resource === undefined)
103
+ continue;
104
+ const entry = { resource };
105
+ const id = nonEmptyString(item.id);
106
+ if (id !== undefined)
107
+ entry.id = id;
108
+ const title = nonEmptyString(item.title);
109
+ if (title !== undefined)
110
+ entry.title = title;
111
+ const author = nonEmptyString(item.author);
112
+ if (author !== undefined)
113
+ entry.author = author;
114
+ if (typeof item.usage_count === "number" && Number.isFinite(item.usage_count)) {
115
+ entry.usage_count = item.usage_count;
116
+ }
117
+ const lastModified = nonEmptyString(item.last_modified);
118
+ if (lastModified !== undefined)
119
+ entry.last_modified = lastModified;
120
+ out.push(entry);
121
+ }
122
+ return out.length > 0 ? out : undefined;
123
+ }
124
+ /** Parse the `status:` lifecycle field — a strict whitelist; any other value (including a foreign/future status) is left unset rather than guessed at. */
125
+ function parseLifecycleStatus(value) {
126
+ return value === "draft" || value === "stable" || value === "deprecated" ? value : undefined;
127
+ }
34
128
  /** Reserved OKF files (case-insensitive) — recognized, never indexed as concepts (§5, OKF §1.4). */
35
129
  const RESERVED_FILES = new Set(["index.md", "log.md"]);
36
130
  /** Upper bound on the bounded `content` FTS field (§3: "content: FTS 1 (bounded)"). Small fixtures are never truncated. */
@@ -59,13 +153,24 @@ function isReservedFileName(name) {
59
153
  */
60
154
  export function resolveOkfLinks(body, fileRelPath) {
61
155
  const dir = path.posix.dirname(toPosix(fileRelPath));
62
- const linkRe = /\[[^\]]*\]\(([^)]+)\)/g;
156
+ const definitions = new Map();
157
+ for (const match of body.matchAll(/^\s*\[([^\]]+)\]:\s*(\S+)/gm)) {
158
+ definitions.set(match[1].trim().toLowerCase(), match[2]);
159
+ }
160
+ const candidates = [];
161
+ for (const match of body.matchAll(/\[[^\]]*\]\(([^)]+)\)/g)) {
162
+ candidates.push({ index: match.index, target: match[1] });
163
+ }
164
+ for (const match of body.matchAll(/(?<!!)\[[^\]]*\]\[([^\]]+)\]/g)) {
165
+ const target = definitions.get(match[1].trim().toLowerCase());
166
+ if (target)
167
+ candidates.push({ index: match.index, target });
168
+ }
169
+ candidates.sort((a, b) => a.index - b.index);
63
170
  const out = [];
64
171
  const seen = new Set();
65
- let match;
66
- // biome-ignore lint/suspicious/noAssignInExpressions: idiomatic regex loop
67
- while ((match = linkRe.exec(body)) !== null) {
68
- let target = match[1].trim();
172
+ for (const candidate of candidates) {
173
+ let target = candidate.target.trim();
69
174
  // Drop an optional markdown link title: `[x](/a.md "Title")`.
70
175
  const wsIdx = target.search(/\s/);
71
176
  if (wsIdx >= 0)
@@ -127,8 +232,33 @@ function recognize(c, file) {
127
232
  const name = nonEmptyString(data.title) ?? lastSegment;
128
233
  const description = nonEmptyString(data.description);
129
234
  const tags = readTags(data.tags);
130
- const updated = nonEmptyString(data.timestamp);
131
235
  const links = resolveOkfLinks(body, file.relPath);
236
+ // v0.2 trust/provenance/lifecycle families (§0.1, okf-support.md v0.2 note).
237
+ // `generated.at` — v0.2's replacement for `timestamp` — takes precedence;
238
+ // `timestamp` remains a fully valid fallback (the v0.2-permitted legacy
239
+ // reading, not merely tolerated). Both stay fully optional (never rejects).
240
+ const generatedMapping = isPlainObject(data.generated) ? data.generated : undefined;
241
+ const generatedAt = generatedMapping ? nonEmptyString(generatedMapping.at) : undefined;
242
+ const generatedBy = generatedMapping ? nonEmptyString(generatedMapping.by) : undefined;
243
+ const legacyTimestamp = nonEmptyString(data.timestamp);
244
+ const updated = generatedAt ?? legacyTimestamp;
245
+ const verified = parseVerified(data.verified);
246
+ const okfSources = parseOkfSources(data.sources);
247
+ const provenance = generatedBy !== undefined || generatedAt !== undefined || verified !== undefined || okfSources !== undefined
248
+ ? {
249
+ ...(generatedBy !== undefined ? { generatedBy } : {}),
250
+ ...(generatedAt !== undefined ? { generatedAt } : {}),
251
+ ...(verified !== undefined ? { verified } : {}),
252
+ ...(okfSources !== undefined ? { sources: okfSources } : {}),
253
+ }
254
+ : undefined;
255
+ const lifecycleStatus = parseLifecycleStatus(data.status);
256
+ const staleAfter = nonEmptyString(data.stale_after);
257
+ // `okf_version` is upstream-declared only on a bundle-root `index.md` (never
258
+ // indexed as a concept, §5) — read defensively from any concept anyway
259
+ // (best-effort, conformance Rule 9); a producer that also tags ordinary
260
+ // concepts with it is neither rejected nor silently ignored.
261
+ const okfVersion = nonEmptyString(data.okf_version);
132
262
  const doc = {
133
263
  ref: `${c.id}//${conceptId}`,
134
264
  bundle: c.id,
@@ -139,6 +269,7 @@ function recognize(c, file) {
139
269
  // incrementality/fingerprints (`types.ts` hash doc comment).
140
270
  hash: hashContent(raw),
141
271
  adapterId: "okf",
272
+ ownsPresentation: true,
142
273
  type,
143
274
  name,
144
275
  content: body.length > MAX_CONTENT_CHARS ? body.slice(0, MAX_CONTENT_CHARS) : body,
@@ -151,6 +282,17 @@ function recognize(c, file) {
151
282
  doc.updated = updated;
152
283
  if (links.length > 0)
153
284
  doc.links = links;
285
+ if (provenance !== undefined)
286
+ doc.provenance = provenance;
287
+ if (lifecycleStatus !== undefined)
288
+ doc.lifecycleStatus = lifecycleStatus;
289
+ if (staleAfter !== undefined)
290
+ doc.staleAfter = staleAfter;
291
+ if (okfVersion !== undefined)
292
+ doc.okfVersion = okfVersion;
293
+ const extras = Object.fromEntries(Object.entries(data).filter(([key]) => !CONSUMED_FRONTMATTER_KEYS.includes(key)));
294
+ if (Object.keys(extras).length > 0)
295
+ doc.documentJson = extras;
154
296
  return doc;
155
297
  }
156
298
  async function validate(c, changes, ctx) {
@@ -165,14 +307,11 @@ async function validate(c, changes, ctx) {
165
307
  const fileName = relPath.split("/").pop() ?? relPath;
166
308
  const reserved = isReservedFileName(fileName);
167
309
  const parsed = parseFrontmatter(raw);
168
- // Base checks (unquoted-colon / missing-updated / stale-path / missing-ref).
310
+ // Base checks shared with native AKM formats. OKF's timestamp is optional,
311
+ // so the AKM-specific freshness diagnostic is removed unconditionally.
169
312
  const base = await runBaseValidateChecks(relPath, parsed, c.root, ctx);
170
- // §0.1: for OKF content a `timestamp` satisfies the freshness requirement
171
- // (the base-linter's `missing-updated` maps to `timestamp`). Suppress the
172
- // `missing-updated` diagnostic when a non-empty `timestamp` is present.
173
- const hasTimestamp = nonEmptyString(parsed.data.timestamp) !== undefined;
174
313
  for (const diag of base) {
175
- if (hasTimestamp && diag.issue === "missing-updated")
314
+ if (diag.issue === "missing-updated")
176
315
  continue;
177
316
  diagnostics.push(diag);
178
317
  }
@@ -208,16 +347,45 @@ export const okfAdapter = {
208
347
  extensions: [".md"],
209
348
  recognize,
210
349
  validate,
211
- /** `<c.root>/<conceptId>.md` (§5). */
212
- placeNew(c, conceptId) {
213
- return path.join(c.root, `${conceptId}.md`);
214
- },
215
350
  /** OKF concepts live anywhere under the component root (§5). */
216
351
  directoryList(_c) {
217
352
  return ["."];
218
353
  },
219
- /** Install-time probe: a root is an OKF bundle when it has a root `index.md` (§1.2; `okf_version` NOT required). */
354
+ /**
355
+ * Install-time probe. A root index is sufficient; an index-less root is also
356
+ * OKF when it contains at least one conformant concept with a non-empty open
357
+ * `type`. More-specific native adapters run before this portable baseline.
358
+ */
220
359
  looksLikeRoot(root) {
221
- return fs.existsSync(path.join(root, "index.md"));
360
+ if (fs.existsSync(path.join(root, "index.md")))
361
+ return true;
362
+ const stack = [root];
363
+ while (stack.length > 0) {
364
+ const current = stack.pop();
365
+ if (!current)
366
+ continue;
367
+ let entries;
368
+ try {
369
+ entries = fs.readdirSync(current, { withFileTypes: true });
370
+ }
371
+ catch {
372
+ continue;
373
+ }
374
+ for (const entry of entries) {
375
+ if (entry.isSymbolicLink() || entry.name === ".git")
376
+ continue;
377
+ const absolute = path.join(current, entry.name);
378
+ if (entry.isDirectory()) {
379
+ stack.push(absolute);
380
+ continue;
381
+ }
382
+ if (!entry.isFile() || !entry.name.toLowerCase().endsWith(".md") || isReservedFileName(entry.name))
383
+ continue;
384
+ const type = nonEmptyString(parseFrontmatter(fs.readFileSync(absolute, "utf8")).data.type);
385
+ if (type)
386
+ return true;
387
+ }
388
+ }
389
+ return false;
222
390
  },
223
391
  };
@@ -49,6 +49,7 @@
49
49
  */
50
50
  import { createHash } from "node:crypto";
51
51
  import { parseFrontmatter } from "../../asset/frontmatter.js";
52
+ import { checkUnquotedDescriptionColon } from "../../asset/frontmatter-lint.js";
52
53
  // ── Small pure helpers, reused across the concrete adapters ──────────────────
53
54
  /** Content hash feeding `IndexDocument.hash` (incrementality/fingerprints, `types.ts` doc comment). */
54
55
  export function hashContent(content) {
@@ -69,23 +70,6 @@ export function readTags(value) {
69
70
  return tags.length > 0 ? tags : undefined;
70
71
  }
71
72
  // ── Base validate checks (port of `BaseLinter.runBaseChecks`) ────────────────
72
- function checkUnquotedColon(frontmatterText) {
73
- if (!frontmatterText)
74
- return null;
75
- for (const line of frontmatterText.split(/\r?\n/)) {
76
- const match = line.match(/^description:\s*(.*)/);
77
- if (!match)
78
- continue;
79
- const value = match[1].trim();
80
- if ((value.startsWith('"') && value.endsWith('"')) || (value.startsWith("'") && value.endsWith("'"))) {
81
- return null;
82
- }
83
- if (value.includes(":")) {
84
- return `description value contains unquoted colon: ${value}`;
85
- }
86
- }
87
- return null;
88
- }
89
73
  function checkMissingUpdated(data, frontmatterText) {
90
74
  return frontmatterText !== null && !("updated" in data);
91
75
  }
@@ -207,7 +191,7 @@ function readRefStringOrArray(value) {
207
191
  export async function runBaseValidateChecks(relPath, parsed, componentRoot, ctx) {
208
192
  const diagnostics = [];
209
193
  const { data, content: body, frontmatter } = parsed;
210
- const unquotedColonDetail = checkUnquotedColon(frontmatter);
194
+ const unquotedColonDetail = checkUnquotedDescriptionColon(frontmatter);
211
195
  if (unquotedColonDetail) {
212
196
  diagnostics.push({ file: relPath, issue: "unquoted-colon", detail: unquotedColonDetail, fixed: false });
213
197
  }
@@ -228,7 +212,7 @@ export async function runBaseValidateChecks(relPath, parsed, componentRoot, ctx)
228
212
  if (found !== null)
229
213
  continue;
230
214
  const portableHint = candidate.startsWith(componentRoot)
231
- ? ` (portable form: $AKM_STASH_DIR${candidate.slice(componentRoot.length)})`
215
+ ? ` (portable form: $AKM_BUNDLE_DIR${candidate.slice(componentRoot.length)})`
232
216
  : "";
233
217
  diagnostics.push({
234
218
  file: relPath,
@@ -41,9 +41,12 @@
41
41
  * ── Cycle-safety ──
42
42
  *
43
43
  * Imported only by `claude-adapter.ts` / `opencode-adapter.ts` (themselves
44
- * imported 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.
44
+ * imported by the `adapters/index.ts` barrel — NOT test-only: `core/adapter/
45
+ * registry.ts:33` imports it in production for the frozen `BUILTIN_ADAPTERS`
46
+ * list). This leaf still can never gain an inbound edge from a cycle
47
+ * participant, since nothing on that import chain imports back into it. It
48
+ * value-imports only pure leaves (`shared`, `akm-lint`, `frontmatter`) plus
49
+ * Node builtins.
47
50
  */
48
51
  import path from "node:path";
49
52
  import { parseFrontmatter } from "../../asset/frontmatter.js";
@@ -121,6 +124,8 @@ export function recognizeToolDir(layout, c, file) {
121
124
  name,
122
125
  content: body.length > MAX_CONTENT_CHARS ? body.slice(0, MAX_CONTENT_CHARS) : body,
123
126
  };
127
+ if (cls.type === "instruction")
128
+ doc.ownsPresentation = true;
124
129
  if (description !== undefined)
125
130
  doc.description = description;
126
131
  if (tags !== undefined)
@@ -180,9 +185,11 @@ export async function validateToolDir(layout, c, changes, ctx) {
180
185
  continue;
181
186
  const relPath = toPosix(change.path);
182
187
  // The one coded skill check (missing-skill-md) fires on ANY change under a
183
- // `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)));
188
+ // `<skillDir>/<name>/…` package (self-gated + deduped), even a bundled
189
+ // resource — mirrors the akm adapter's per-change SkillLinter.lintDirectory
190
+ // pass. `layout.skillDirs` is passed so opencode's singular `skill/` alias
191
+ // is checked identically to `skills/` (issue #774).
192
+ diagnostics.push(...(await skillDirectoryDiagnostics(relPath, seenSkillDirs, ctx, layout.skillDirs)));
186
193
  const cls = classify(change.path, layout);
187
194
  if (cls === null)
188
195
  continue;
@@ -95,6 +95,7 @@ function recognize(c, file) {
95
95
  path: file.absPath,
96
96
  hash: hashContent(raw),
97
97
  adapterId: "website-snapshot",
98
+ ownsPresentation: true,
98
99
  type: "website",
99
100
  name,
100
101
  content: body.length > MAX_CONTENT_CHARS ? body.slice(0, MAX_CONTENT_CHARS) : body,
@@ -0,0 +1,17 @@
1
+ // This Source Code Form is subject to the terms of the Mozilla Public
2
+ // License, v. 2.0. If a copy of the MPL was not distributed with this
3
+ // file, You can obtain one at https://mozilla.org/MPL/2.0/.
4
+ import { getAdapters } from "./registry.js";
5
+ /** Select the first built-in adapter whose ordered root probe claims `root`. */
6
+ export function detectAdapterId(root, fallback = "akm") {
7
+ for (const adapter of getAdapters()) {
8
+ try {
9
+ if (adapter.looksLikeRoot?.(root) === true)
10
+ return adapter.id;
11
+ }
12
+ catch {
13
+ // An unreadable or racing probe does not claim the bundle.
14
+ }
15
+ }
16
+ return fallback;
17
+ }
@@ -1,19 +1,21 @@
1
1
  // This Source Code Form is subject to the terms of the Mozilla Public
2
2
  // License, v. 2.0. If a copy of the MPL was not distributed with this
3
3
  // file, You can obtain one at https://mozilla.org/MPL/2.0/.
4
- import { directoryMatcher, extensionMatcher, parentDirHintMatcher, smartMdMatcher, workflowProgramMatcher, } from "../../indexer/walk/matchers.js";
4
+ import { directoryMatcher, extensionMatcher, parentDirHintMatcher, smartMdMatcher } from "../../indexer/walk/matchers.js";
5
5
  /**
6
- * The five builtin matchers, in registration order. The array index IS the
6
+ * The four builtin matchers, in registration order. The array index IS the
7
7
  * registration index `runMatchers` uses for tie-breaking. (The `wiki` matcher
8
8
  * was removed in chunk 4 — the wiki asset-type is retired; LLM Wiki content is
9
- * served by the first-class `llm-wiki` adapter, not the akm adapter.)
9
+ * served by the first-class `llm-wiki` adapter, not the akm adapter. The YAML
10
+ * workflow-program matcher was removed by workflow-format-unification — one
11
+ * workflow format now, recognized by frontmatter `type: workflow` or
12
+ * residence under `workflows/`, both already covered by the remaining four.)
10
13
  */
11
14
  const AKM_MATCHERS = [
12
15
  extensionMatcher,
13
16
  directoryMatcher,
14
17
  parentDirHintMatcher,
15
18
  smartMdMatcher,
16
- workflowProgramMatcher,
17
19
  ];
18
20
  /**
19
21
  * Synchronous reproduction of `file-context.ts#runMatchers`'s arbitration
@@ -0,0 +1,214 @@
1
+ // This Source Code Form is subject to the terms of the Mozilla Public
2
+ // License, v. 2.0. If a copy of the MPL was not distributed with this
3
+ // file, You can obtain one at https://mozilla.org/MPL/2.0/.
4
+ /**
5
+ * The ONE core {@link ValidateContext} implementation — akm 0.9.0 chunk-2/lint
6
+ * follow-up ("make `validate()` genuinely load-bearing").
7
+ *
8
+ * `BundleAdapter.validate()` (`./bundle-adapter.ts`) is a REQUIRED interface
9
+ * method whose contract is explicit and normative (`./bundle-adapter.ts:96-100`
10
+ * / the format-neutral spec §12.1): the adapter MUST NOT write and MUST NOT
11
+ * read the live filesystem — `ctx` serves the run's on-disk SNAPSHOT WITH the
12
+ * caller's pending {@link FileChange}s overlaid, plus a read-only `resolveRef`
13
+ * for link/xref existence. That overlay is meant to be built ONCE, centrally —
14
+ * "one core overlay implementation, not one per adapter" (the interface's own
15
+ * words) — so every `validate()` caller shares identical overlay semantics
16
+ * rather than each hand-rolling its own (as every adapter's own test suite has
17
+ * done up to now, see e.g. `tests/core/adapter/okf-adapter.test.ts`'s
18
+ * `overlayCtx`/`diskCtx`).
19
+ *
20
+ * The two production callers wired onto this factory:
21
+ * - `akm lint`'s non-akm adapter dispatch (`commands/lint/index.ts`) — a
22
+ * plain sweep with NO pending changes, so the overlay is empty and every
23
+ * read falls straight through to disk.
24
+ * - the proposal promotion preflight (`commands/proposal/repository.ts`
25
+ * `preflightProposalPromotion`) — the one proposal's own `changes` are the
26
+ * overlay, so `validate()` sees the bundle AS IT WOULD LOOK the instant
27
+ * after the transaction commits, without that transaction ever touching
28
+ * disk.
29
+ *
30
+ * ── resolveRef ──
31
+ *
32
+ * A bare (unqualified) `conceptId` is resolved as BOTH:
33
+ * 1. the AKM placement-derived path (`typeNameFromConceptId` + `stashDirFor`
34
+ * + `assetPathForName` — the exact derivation `commands/lint/base-linter.ts
35
+ * #refToRelPath` already uses for the akm-native `missing-ref` check); and
36
+ * 2. the DIRECT component-relative spelling (`<conceptId>.md`, and the bare
37
+ * `<conceptId>` for an extensionless asset) — the form every non-akm
38
+ * adapter's OWN conceptId already IS (OKF: path − `.md`; llm-wiki: same).
39
+ * A `bundle//conceptId`-qualified ref has its bundle prefix stripped before
40
+ * resolution — mirroring `base-linter.ts#classifyConceptRef`'s existing
41
+ * behavior (the legacy resolver has never scoped-by-bundle-name either; it
42
+ * searches every configured stash root for the bare conceptId). Existence is
43
+ * checked against the PRIMARY root (with the pending-changes overlay applied)
44
+ * first, then each extra root (disk only — a pending transaction never
45
+ * targets more than one bundle).
46
+ */
47
+ import fs from "node:fs";
48
+ import path from "node:path";
49
+ import { assetPathForName, stashDirFor } from "../asset/asset-placement.js";
50
+ import { typeNameFromConceptId } from "../asset/resolve-ref.js";
51
+ function toPosix(p) {
52
+ return p.replace(/\\/g, "/");
53
+ }
54
+ /** Build the overlay map, keyed by POSIX path relative to `root`. */
55
+ function buildOverlay(root, changes) {
56
+ const overlay = new Map();
57
+ for (const change of changes) {
58
+ const relKey = toPosix(path.isAbsolute(change.path) ? path.relative(root, change.path) : change.path);
59
+ if (!relKey || relKey.startsWith(".."))
60
+ continue; // outside root — not this context's concern
61
+ if (change.op === "delete") {
62
+ overlay.set(relKey, null);
63
+ continue;
64
+ }
65
+ if (change.after !== undefined)
66
+ overlay.set(relKey, change.after);
67
+ }
68
+ return overlay;
69
+ }
70
+ function readDisk(absPath) {
71
+ try {
72
+ return fs.readFileSync(absPath, "utf8");
73
+ }
74
+ catch {
75
+ return null;
76
+ }
77
+ }
78
+ function existsOnDiskOrOverlay(relPath, root, overlay) {
79
+ const key = toPosix(relPath);
80
+ // Overlay entries are pending file contents by construction, so a present
81
+ // non-null entry is always a file.
82
+ if (overlay?.has(key))
83
+ return overlay.get(key) !== null;
84
+ try {
85
+ // `isFile()`, not `existsSync()`: a ref naming a DIRECTORY (e.g. a
86
+ // `pages/foo/` dir alongside no `pages/foo.md`) must not count as a
87
+ // resolved target — that would silently suppress a real
88
+ // `missing-ref`/`broken-xref` diagnostic.
89
+ return fs.statSync(path.join(root, relPath)).isFile();
90
+ }
91
+ catch {
92
+ return false;
93
+ }
94
+ }
95
+ /**
96
+ * Refs come from bundle CONTENT (link targets, xrefs, `sources:` entries), so
97
+ * they are untrusted input. A ref must stay inside the bundle root: reject
98
+ * absolute paths and any `..` segment before joining, so content can never
99
+ * probe for existence outside its own bundle (nor report a resolved `path`
100
+ * pointing there). Mirrors the `isWithin` containment rule the write path
101
+ * already enforces in `core/write-source.ts`.
102
+ */
103
+ function refEscapesBundle(conceptId) {
104
+ if (path.isAbsolute(conceptId) || conceptId.startsWith("/"))
105
+ return true;
106
+ return toPosix(conceptId)
107
+ .split("/")
108
+ .some((segment) => segment === "..");
109
+ }
110
+ /**
111
+ * Every on-disk relative path a bare `conceptId` might resolve to: the AKM
112
+ * placement-derived path (when the leading segment names a known placement
113
+ * stash-subdir) and the direct component-relative spellings every non-akm
114
+ * adapter's own conceptId already uses. Order doesn't matter — the caller
115
+ * checks all of them.
116
+ */
117
+ function candidateRelPaths(conceptId) {
118
+ const candidates = [];
119
+ const parts = typeNameFromConceptId(conceptId);
120
+ if (parts !== undefined) {
121
+ const typeDir = stashDirFor(parts.type);
122
+ if (typeDir !== undefined)
123
+ candidates.push(assetPathForName(parts.type, typeDir, parts.name));
124
+ }
125
+ candidates.push(`${conceptId}.md`);
126
+ candidates.push(conceptId);
127
+ return candidates;
128
+ }
129
+ /** Strip an optional `#fragment` (export selector) — never part of the on-disk identity. */
130
+ function stripFragment(ref) {
131
+ const hashIdx = ref.indexOf("#");
132
+ return hashIdx >= 0 ? ref.slice(0, hashIdx) : ref;
133
+ }
134
+ /**
135
+ * Strip an optional `bundle//` qualifier. Mirrors `base-linter.ts
136
+ * #classifyConceptRef`'s existing behavior: the legacy missing-ref checker has
137
+ * never resolved a qualifier against a NAMED bundle either — it searches every
138
+ * configured stash root for the bare conceptId. Keeping that same leniency
139
+ * here means this resolver's answers agree with today's `akm lint` for every
140
+ * ref shape that already worked.
141
+ */
142
+ function stripBundlePrefix(ref) {
143
+ const boundary = ref.indexOf("//");
144
+ return boundary >= 0 ? ref.slice(boundary + 2) : ref;
145
+ }
146
+ /**
147
+ * Build the ONE core {@link ValidateContext}: reads/lookups served from the
148
+ * on-disk snapshot rooted at `options.root` (+ `options.extraRoots` for
149
+ * cross-bundle ref existence) WITH `options.changes` overlaid on `options.root`
150
+ * — never a write, never a live-FS read beyond that snapshot.
151
+ */
152
+ export function createValidateContext(options) {
153
+ const root = options.root;
154
+ const extraRoots = options.extraRoots ?? [];
155
+ const overlay = buildOverlay(root, options.changes ?? []);
156
+ async function readFile(p) {
157
+ // An absolute candidate (e.g. a `stale-path` scan hit, which is always an
158
+ // absolute host path) bypasses the overlay — it can never name a pending
159
+ // change's stash-relative path, and reads straight from disk (the base
160
+ // checks' existing "does this literal absolute path exist" question).
161
+ if (path.isAbsolute(p))
162
+ return readDisk(p);
163
+ const key = toPosix(p);
164
+ if (overlay.has(key))
165
+ return overlay.get(key) ?? null;
166
+ return readDisk(path.join(root, p));
167
+ }
168
+ async function list(dir) {
169
+ const abs = path.isAbsolute(dir) ? dir : path.join(root, dir);
170
+ const relDir = toPosix(path.isAbsolute(dir) ? path.relative(root, dir) : dir);
171
+ const names = new Set();
172
+ try {
173
+ for (const entry of fs.readdirSync(abs))
174
+ names.add(entry);
175
+ }
176
+ catch {
177
+ // directory may not exist on disk yet — the overlay can still populate it
178
+ }
179
+ const prefix = relDir === "." || relDir === "" ? "" : `${relDir}/`;
180
+ for (const [key, value] of overlay) {
181
+ if (prefix.length > 0 && !key.startsWith(prefix))
182
+ continue;
183
+ const rest = prefix.length > 0 ? key.slice(prefix.length) : key;
184
+ if (rest.length === 0 || rest.includes("/"))
185
+ continue; // only direct children
186
+ if (value === null)
187
+ names.delete(rest);
188
+ else
189
+ names.add(rest);
190
+ }
191
+ return [...names];
192
+ }
193
+ async function resolveRef(ref) {
194
+ const conceptId = stripBundlePrefix(stripFragment(ref)).trim();
195
+ if (!conceptId)
196
+ return { exists: false };
197
+ if (refEscapesBundle(conceptId))
198
+ return { exists: false };
199
+ const candidates = candidateRelPaths(conceptId);
200
+ const roots = [
201
+ { root, usesOverlay: true },
202
+ ...extraRoots.map((r) => ({ root: r, usesOverlay: false })),
203
+ ];
204
+ for (const { root: candidateRoot, usesOverlay } of roots) {
205
+ for (const relPath of candidates) {
206
+ if (existsOnDiskOrOverlay(relPath, candidateRoot, usesOverlay ? overlay : null)) {
207
+ return { exists: true, path: path.join(candidateRoot, relPath) };
208
+ }
209
+ }
210
+ }
211
+ return { exists: false };
212
+ }
213
+ return { readFile, list, resolveRef };
214
+ }