akm-cli 0.9.0-rc.8 → 0.9.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (450) hide show
  1. package/CHANGELOG.md +1063 -44
  2. package/README.md +51 -25
  3. package/SECURITY.md +14 -1
  4. package/STABILITY.md +497 -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 +73 -88
  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 +141 -87
  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/md-report.js +5 -9
  57. package/dist/commands/health/metrics.js +62 -20
  58. package/dist/commands/health/renderers.js +47 -0
  59. package/dist/commands/health/report-view-model.js +4 -5
  60. package/dist/commands/health/stash-exposure.js +1 -1
  61. package/dist/commands/health/surfaces.js +3 -48
  62. package/dist/commands/health/task-runs.js +3 -67
  63. package/dist/commands/health/types-improve.js +7 -0
  64. package/dist/commands/health.js +99 -28
  65. package/dist/commands/improve/anti-collapse.js +2 -2
  66. package/dist/commands/improve/autonomy-gate.js +68 -0
  67. package/dist/commands/improve/collapse-detector.js +41 -40
  68. package/dist/commands/improve/consolidate/eligibility.js +1 -23
  69. package/dist/commands/improve/consolidate/merge.js +4 -0
  70. package/dist/commands/improve/consolidate.js +140 -1000
  71. package/dist/commands/improve/distill/promote-memory.js +12 -12
  72. package/dist/commands/improve/distill/quality-gate.js +6 -6
  73. package/dist/commands/improve/distill.js +58 -69
  74. package/dist/commands/improve/eligibility.js +105 -57
  75. package/dist/commands/improve/extract-cli.js +14 -133
  76. package/dist/commands/improve/improve-cli.js +98 -114
  77. package/dist/commands/improve/improve-result-file.js +1 -28
  78. package/dist/commands/improve/improve-strategies.js +8 -5
  79. package/dist/commands/improve/improve.js +128 -91
  80. package/dist/commands/improve/loop-stages.js +182 -20
  81. package/dist/commands/improve/memory/derived-ref.js +45 -43
  82. package/dist/commands/improve/memory/memory-belief.js +1 -1
  83. package/dist/commands/improve/memory/memory-contradiction-detect.js +4 -12
  84. package/dist/commands/improve/memory/memory-improve.js +6 -5
  85. package/dist/commands/improve/outcome-loop.js +22 -65
  86. package/dist/commands/improve/preparation.js +114 -123
  87. package/dist/commands/improve/proactive-maintenance.js +2 -5
  88. package/dist/commands/improve/reflect.js +56 -160
  89. package/dist/commands/improve/salience.js +11 -122
  90. package/dist/commands/improve/source-identity.js +10 -38
  91. package/dist/commands/lint/base-linter.js +20 -124
  92. package/dist/commands/lint/env-key-rules.js +31 -47
  93. package/dist/commands/lint/index.js +249 -43
  94. package/dist/commands/{events.js → log.js} +33 -38
  95. package/dist/commands/migrate-cli.js +92 -12
  96. package/dist/commands/migration-tool.js +46 -0
  97. package/dist/commands/observability-cli.js +70 -209
  98. package/dist/commands/proposal/drain.js +101 -29
  99. package/dist/commands/proposal/proposal-cli.js +76 -48
  100. package/dist/commands/proposal/proposal.js +54 -18
  101. package/dist/commands/proposal/propose-cli.js +88 -0
  102. package/dist/commands/proposal/propose.js +23 -15
  103. package/dist/commands/proposal/repository.js +701 -278
  104. package/dist/commands/proposal/validators/proposal-quality-validators.js +2 -8
  105. package/dist/commands/proposal/validators/proposal-validators.js +55 -7
  106. package/dist/commands/proposal/validators/proposals.js +4 -7
  107. package/dist/commands/read/curate.js +34 -53
  108. package/dist/commands/read/knowledge.js +150 -95
  109. package/dist/commands/read/registry-search.js +2 -2
  110. package/dist/commands/read/remember-cli.js +42 -15
  111. package/dist/commands/read/search-cli.js +180 -78
  112. package/dist/commands/read/search.js +58 -43
  113. package/dist/commands/read/show.js +197 -141
  114. package/dist/commands/registry-cli.js +12 -51
  115. package/dist/commands/remember.js +14 -57
  116. package/dist/commands/sources/add-cli.js +100 -31
  117. package/dist/commands/sources/bundle-cli.js +166 -0
  118. package/dist/commands/sources/bundle-config-ops.js +7 -2
  119. package/dist/commands/sources/info.js +18 -5
  120. package/dist/commands/sources/init.js +12 -12
  121. package/dist/commands/sources/installed-stashes.js +382 -98
  122. package/dist/commands/sources/schema-repair.js +3 -2
  123. package/dist/commands/sources/self-update.js +131 -38
  124. package/dist/commands/sources/source-add.js +72 -17
  125. package/dist/commands/sources/source-clone.js +129 -45
  126. package/dist/commands/sources/source-manage.js +43 -23
  127. package/dist/commands/sources/sources-cli.js +57 -208
  128. package/dist/commands/sources/stash-cli.js +46 -53
  129. package/dist/commands/tasks/tasks-cli.js +91 -97
  130. package/dist/commands/tasks/tasks.js +276 -421
  131. package/dist/commands/workflow-cli.js +175 -450
  132. package/dist/core/adapter/adapters/akm-adapter.js +47 -28
  133. package/dist/core/adapter/adapters/akm-lint.js +42 -27
  134. package/dist/core/adapter/adapters/akm-metadata.js +15 -44
  135. package/dist/core/adapter/adapters/akm-task-adapter.js +15 -13
  136. package/dist/core/adapter/adapters/akm-workflow-adapter.js +55 -71
  137. package/dist/core/adapter/adapters/dotenv-adapter.js +1 -1
  138. package/dist/core/adapter/adapters/generic-files-adapter.js +2 -0
  139. package/dist/core/adapter/adapters/index.js +6 -6
  140. package/dist/core/adapter/adapters/llm-wiki-adapter.js +14 -8
  141. package/dist/core/adapter/adapters/okf-adapter.js +187 -19
  142. package/dist/core/adapter/adapters/shared.js +3 -19
  143. package/dist/core/adapter/adapters/tool-dir-shared.js +8 -3
  144. package/dist/core/adapter/adapters/website-snapshot-adapter.js +1 -0
  145. package/dist/core/adapter/detect-adapter.js +17 -0
  146. package/dist/core/adapter/recognize-match.js +6 -4
  147. package/dist/core/adapter/validate-context.js +214 -0
  148. package/dist/core/asset/akm-markdown.js +63 -0
  149. package/dist/core/asset/asset-placement.js +20 -6
  150. package/dist/core/asset/asset-ref.js +11 -9
  151. package/dist/core/asset/frontmatter-lint.js +30 -0
  152. package/dist/core/asset/frontmatter.js +37 -9
  153. package/dist/core/asset/markdown.js +40 -51
  154. package/dist/core/asset/resolve-ref.js +89 -18
  155. package/dist/core/asset/stash-meta.js +1 -1
  156. package/dist/core/bundle-id.js +51 -0
  157. package/dist/core/common.js +152 -38
  158. package/dist/core/config/config-io.js +12 -1
  159. package/dist/core/config/config-schema.js +35 -8
  160. package/dist/core/config/config-sources.js +55 -11
  161. package/dist/core/config/config-walker.js +25 -9
  162. package/dist/core/config/config.js +9 -48
  163. package/dist/core/config/experimental.js +21 -0
  164. package/dist/core/config/schema/embedding.js +5 -1
  165. package/dist/core/config/schema/experimental.js +30 -0
  166. package/dist/core/config/schema/improve-processes.js +0 -6
  167. package/dist/core/config/schema/improve.js +21 -3
  168. package/dist/core/config/schema/index-config.js +8 -15
  169. package/dist/core/config/schema/output.js +4 -1
  170. package/dist/core/config/schema/setup.js +9 -18
  171. package/dist/core/config/schema/sources-bundles.js +49 -33
  172. package/dist/core/config/schema/workflow.js +3 -3
  173. package/dist/core/env-secret-ref.js +76 -46
  174. package/dist/core/errors.js +18 -12
  175. package/dist/core/events.js +46 -128
  176. package/dist/core/file-change.js +6 -5
  177. package/dist/core/fs-txn.js +83 -7
  178. package/dist/core/git-message.js +2 -2
  179. package/dist/core/improve-result.js +1 -100
  180. package/dist/core/lesson-lint.js +1 -17
  181. package/dist/core/logs-db.js +2 -1
  182. package/dist/core/migration-operation.js +16 -0
  183. package/dist/core/mutation-target.js +78 -0
  184. package/dist/core/parse.js +4 -1
  185. package/dist/core/paths.js +17 -20
  186. package/dist/core/recognition-util.js +12 -14
  187. package/dist/core/redaction.js +34 -0
  188. package/dist/core/standards/resolve-standards-context.js +2 -14
  189. package/dist/core/standards/resolve-stash-standards.js +2 -2
  190. package/dist/core/standards/resolve-type-conventions.js +2 -2
  191. package/dist/core/state/migrations.js +41 -18
  192. package/dist/core/state-db.js +5 -14
  193. package/dist/core/structured.js +1 -1
  194. package/dist/core/subprocess.js +6 -4
  195. package/dist/core/text-truncation.js +9 -5
  196. package/dist/core/type-presentation.js +3 -3
  197. package/dist/core/warn.js +0 -3
  198. package/dist/core/write-source.js +771 -95
  199. package/dist/indexer/bundle-identity-guard.js +3 -2
  200. package/dist/indexer/db/graph-db.js +0 -24
  201. package/dist/indexer/ensure-index.js +1 -0
  202. package/dist/indexer/graph/graph-boost.js +9 -34
  203. package/dist/indexer/graph/graph-extraction.js +8 -5
  204. package/dist/indexer/index-writer-lock.js +53 -17
  205. package/dist/indexer/index-written-assets.js +16 -22
  206. package/dist/indexer/indexer.js +497 -239
  207. package/dist/indexer/installations.js +14 -96
  208. package/dist/indexer/passes/dir-staleness.js +16 -9
  209. package/dist/indexer/passes/memory-inference.js +11 -9
  210. package/dist/indexer/passes/metadata.js +113 -47
  211. package/dist/indexer/scan/doc-to-entry.js +38 -1
  212. package/dist/indexer/scan/drain-dir.js +13 -23
  213. package/dist/indexer/search/db-search.js +99 -54
  214. package/dist/indexer/search/fts-query.js +47 -24
  215. package/dist/indexer/search/ranking-contributors.js +42 -20
  216. package/dist/indexer/search/ranking.js +18 -99
  217. package/dist/indexer/search/search-fields.js +7 -2
  218. package/dist/indexer/search/search-source.js +82 -93
  219. package/dist/indexer/usage/usage-events.js +0 -89
  220. package/dist/indexer/walk/file-context.js +2 -1
  221. package/dist/indexer/walk/matchers.js +30 -43
  222. package/dist/indexer/walk/path-resolver.js +7 -2
  223. package/dist/indexer/walk/walker.js +38 -12
  224. package/dist/integrations/agent/builders.js +0 -6
  225. package/dist/integrations/agent/config.js +2 -2
  226. package/dist/integrations/agent/detect.js +49 -19
  227. package/dist/integrations/agent/engine-fallback.js +76 -0
  228. package/dist/integrations/agent/profiles.js +14 -0
  229. package/dist/integrations/agent/prompts.js +12 -8
  230. package/dist/integrations/agent/runner-dispatch.js +4 -2
  231. package/dist/integrations/agent/runner.js +0 -1
  232. package/dist/integrations/agent/spawn.js +5 -6
  233. package/dist/integrations/github.js +1 -1
  234. package/dist/integrations/harnesses/aider/agent-builder.js +6 -4
  235. package/dist/integrations/harnesses/amazonq/agent-builder.js +7 -4
  236. package/dist/integrations/harnesses/claude/session-log.js +0 -10
  237. package/dist/integrations/harnesses/codex/agent-builder.js +5 -2
  238. package/dist/integrations/harnesses/copilot/agent-builder.js +5 -3
  239. package/dist/integrations/harnesses/gemini/agent-builder.js +5 -3
  240. package/dist/integrations/harnesses/index.js +3 -7
  241. package/dist/integrations/harnesses/opencode/agent-builder.js +21 -2
  242. package/dist/integrations/harnesses/opencode/session-log.js +0 -15
  243. package/dist/integrations/harnesses/opencode-sdk/sdk-runner.js +13 -4
  244. package/dist/integrations/harnesses/openhands/agent-builder.js +9 -6
  245. package/dist/integrations/harnesses/pi/agent-builder.js +6 -4
  246. package/dist/integrations/lockfile.js +101 -6
  247. package/dist/integrations/session-logs/index.js +3 -28
  248. package/dist/llm/client.js +136 -100
  249. package/dist/llm/embedders/remote.js +13 -5
  250. package/dist/llm/feature-gate.js +4 -12
  251. package/dist/llm/graph-extract.js +5 -11
  252. package/dist/llm/memory-infer.js +144 -1
  253. package/dist/llm/metadata-enhance.js +5 -7
  254. package/dist/llm/structured-call.js +1 -1
  255. package/dist/llm/usage-persist.js +26 -5
  256. package/dist/llm/usage-telemetry.js +25 -2
  257. package/dist/output/cli-hints.js +1 -2
  258. package/dist/output/context.js +22 -7
  259. package/dist/output/format-exempt.js +80 -0
  260. package/dist/output/generic-render.js +259 -0
  261. package/dist/output/render-registry.js +57 -0
  262. package/dist/output/renderers.js +14 -36
  263. package/dist/output/shapes/curate.js +10 -1
  264. package/dist/output/shapes/events.js +12 -7
  265. package/dist/output/shapes/helpers.js +56 -83
  266. package/dist/output/shapes/migrate.js +8 -0
  267. package/dist/output/shapes/passthrough.js +7 -41
  268. package/dist/output/shapes/proposal/producer.js +15 -7
  269. package/dist/output/shapes.js +2 -9
  270. package/dist/output/text/{init.js → bundle-create.js} +3 -1
  271. package/dist/output/text/bundle-show.js +7 -0
  272. package/dist/output/text/command-format.js +164 -96
  273. package/dist/output/text/env.js +1 -3
  274. package/dist/output/text/events.js +8 -7
  275. package/dist/output/text/health-format.js +103 -0
  276. package/dist/output/text/health.js +7 -0
  277. package/dist/output/text/helpers.js +10 -8
  278. package/dist/output/text/lint-format.js +43 -0
  279. package/dist/output/text/{save.js → lint.js} +2 -2
  280. package/dist/output/text/migrate.js +88 -0
  281. package/dist/output/text/proposal/producer.js +4 -2
  282. package/dist/output/text/proposal-format.js +44 -72
  283. package/dist/output/text/registry-commands.js +1 -2
  284. package/dist/output/text/show-directives.js +15 -7
  285. package/dist/output/text/status-list.js +32 -0
  286. package/dist/output/text/sync.js +5 -0
  287. package/dist/output/text/workflow-format.js +24 -203
  288. package/dist/output/text/workflow.js +1 -7
  289. package/dist/output/text.js +16 -17
  290. package/dist/registry/factory.js +4 -6
  291. package/dist/registry/origin-resolve.js +16 -27
  292. package/dist/registry/providers/skills-sh.js +3 -3
  293. package/dist/registry/providers/static-index.js +13 -23
  294. package/dist/registry/resolve.js +42 -7
  295. package/dist/registry/semver.js +34 -84
  296. package/dist/runtime.js +2 -23
  297. package/dist/scripts/akm-migrate-node.js +60290 -0
  298. package/dist/scripts/akm-migrate.js +59628 -0
  299. package/dist/setup/detect.js +42 -15
  300. package/dist/setup/registry-stash-loader.js +2 -2
  301. package/dist/setup/setup.js +236 -136
  302. package/dist/setup/steps/connection.js +7 -9
  303. package/dist/setup/steps/platforms.js +9 -9
  304. package/dist/setup/steps/semantic.js +15 -3
  305. package/dist/setup/steps/sources.js +12 -13
  306. package/dist/setup/steps/stashdir.js +2 -3
  307. package/dist/setup/steps/tasks.js +237 -120
  308. package/dist/sources/freshness.js +1 -1
  309. package/dist/sources/provider-factory.js +11 -17
  310. package/dist/sources/providers/filesystem.js +2 -3
  311. package/dist/sources/providers/git-install.js +278 -34
  312. package/dist/sources/providers/git-provider.js +25 -23
  313. package/dist/sources/providers/git-stash.js +395 -106
  314. package/dist/sources/providers/git.js +2 -2
  315. package/dist/sources/providers/npm.js +16 -19
  316. package/dist/sources/providers/provider-utils.js +7 -4
  317. package/dist/sources/providers/sync-from-ref.js +3 -9
  318. package/dist/sources/providers/website.js +6 -1
  319. package/dist/sources/resolve.js +6 -5
  320. package/dist/sources/snapshot-fetchers/bluesky.js +146 -0
  321. package/dist/sources/snapshot-fetchers/content-extract.js +566 -0
  322. package/dist/sources/snapshot-fetchers/fetcher-util.js +41 -0
  323. package/dist/sources/snapshot-fetchers/github.js +100 -0
  324. package/dist/sources/snapshot-fetchers/host-guard.js +291 -0
  325. package/dist/sources/snapshot-fetchers/registry.js +17 -1
  326. package/dist/sources/snapshot-fetchers/robots.js +348 -0
  327. package/dist/sources/snapshot-fetchers/rss.js +282 -0
  328. package/dist/sources/snapshot-fetchers/secret-seam.js +42 -0
  329. package/dist/sources/snapshot-fetchers/website-ingest.js +566 -268
  330. package/dist/sources/snapshot-fetchers/x.js +910 -0
  331. package/dist/storage/database.js +7 -0
  332. package/dist/storage/engines/sqlite-migrations.js +23 -111
  333. package/dist/storage/managed-db.js +2 -2
  334. package/dist/storage/repositories/canaries-repository.js +1 -1
  335. package/dist/storage/repositories/events-repository.js +27 -11
  336. package/dist/storage/repositories/improve-runs-repository.js +6 -12
  337. package/dist/storage/repositories/index-connection.js +17 -6
  338. package/dist/storage/repositories/index-entries-repository.js +151 -240
  339. package/dist/storage/repositories/index-entry-mapper.js +15 -11
  340. package/dist/storage/repositories/index-fts-repository.js +5 -2
  341. package/dist/storage/repositories/index-llm-cache-repository.js +0 -1
  342. package/dist/storage/repositories/index-meta-repository.js +2 -3
  343. package/dist/storage/repositories/index-schema.js +10 -25
  344. package/dist/storage/repositories/index-utility-repository.js +15 -28
  345. package/dist/storage/repositories/index-vec-repository.js +6 -1
  346. package/dist/storage/repositories/outcome-repository.js +119 -0
  347. package/dist/storage/repositories/proposals-repository.js +296 -59
  348. package/dist/storage/repositories/registry-cache.js +19 -0
  349. package/dist/storage/repositories/salience-repository.js +172 -0
  350. package/dist/storage/repositories/task-history-repository.js +15 -13
  351. package/dist/storage/repositories/workflow-runs-repository.js +52 -40
  352. package/dist/tasks/backends/cron.js +105 -15
  353. package/dist/tasks/backends/index.js +1 -1
  354. package/dist/tasks/backends/launchd.js +85 -38
  355. package/dist/tasks/backends/schtasks.js +135 -15
  356. package/dist/tasks/embedded.js +56 -40
  357. package/dist/tasks/parser.js +7 -157
  358. package/dist/tasks/resolve-akm-bin.js +137 -59
  359. package/dist/tasks/runner.js +79 -42
  360. package/dist/tasks/scheduler-invocation.js +220 -10
  361. package/dist/tasks/schema.js +24 -1
  362. package/dist/tasks/task-id.js +1 -3
  363. package/dist/tasks/validator.js +20 -6
  364. package/dist/workflows/authoring/authoring.js +94 -143
  365. package/dist/workflows/authoring/scope-key.js +1 -1
  366. package/dist/workflows/exec/frozen-judge.js +28 -2
  367. package/dist/workflows/exec/native-executor.js +77 -57
  368. package/dist/workflows/exec/param-secrets.js +9 -9
  369. package/dist/workflows/exec/run-workflow.js +133 -79
  370. package/dist/workflows/exec/step-work.js +219 -346
  371. package/dist/{migrate-storage-node.mjs → workflows/exec/unit-dispatch.js} +1 -5
  372. package/dist/workflows/ir/compile.js +141 -270
  373. package/dist/workflows/ir/freeze.js +40 -30
  374. package/dist/workflows/ir/params.js +135 -11
  375. package/dist/workflows/ir/plan-hash.js +1 -1
  376. package/dist/workflows/ir/schema.js +25 -26
  377. package/dist/workflows/parser.js +872 -307
  378. package/dist/workflows/program/expressions.js +20 -208
  379. package/dist/workflows/program/schema.js +7 -10
  380. package/dist/workflows/renderer.js +95 -68
  381. package/dist/workflows/resource-limits.js +2 -0
  382. package/dist/workflows/runtime/checkin.js +3 -3
  383. package/dist/workflows/runtime/plan-classifier.js +16 -75
  384. package/dist/workflows/runtime/runs.js +186 -127
  385. package/dist/workflows/runtime/unit-checkin.js +1 -1
  386. package/dist/workflows/runtime/unit-phases.js +2 -2
  387. package/dist/workflows/runtime/workflow-asset-loader.js +232 -83
  388. package/dist/workflows/schema.js +1 -11
  389. package/dist/workflows/validate-summary.js +30 -36
  390. package/dist/workflows/validator.js +21 -62
  391. package/docs/README.md +68 -0
  392. package/docs/migration/README.md +8 -0
  393. package/docs/migration/release-notes/0.7.0.md +11 -11
  394. package/docs/migration/release-notes/0.9.0.md +208 -27
  395. package/docs/migration/v0.7-to-v0.8.md +46 -47
  396. package/docs/migration/v0.8-to-v0.9.md +564 -208
  397. package/docs/migration/v0.9.0-troubleshooting.md +561 -0
  398. package/docs/reference/README.md +12 -0
  399. package/docs/reference/cli.md +2253 -0
  400. package/docs/reference/configuration.md +358 -0
  401. package/docs/reference/data-and-telemetry.md +105 -42
  402. package/docs/reference/workflows.md +647 -0
  403. package/package.json +22 -11
  404. package/schemas/akm-asset-envelope.json +93 -0
  405. package/schemas/akm-config.json +81 -128
  406. package/schemas/akm-workflow.json +74 -73
  407. package/dist/assets/tasks/core/backup.yml +0 -5
  408. package/dist/assets/tasks/graph-refresh-weekly.yml +0 -10
  409. package/dist/cli/config-migrate.js +0 -1806
  410. package/dist/cli/config-validate.js +0 -41
  411. package/dist/commands/backup-cli.js +0 -56
  412. package/dist/commands/bundle/bundle-cli.js +0 -68
  413. package/dist/commands/bundle/bundle.js +0 -219
  414. package/dist/commands/graph/graph-cli.js +0 -124
  415. package/dist/commands/graph/graph.js +0 -489
  416. package/dist/commands/improve/extract-watch.js +0 -140
  417. package/dist/commands/mv-cli.js +0 -1221
  418. package/dist/commands/sources/history.js +0 -201
  419. package/dist/commands/tasks/default-tasks.js +0 -186
  420. package/dist/core/migration-backup.js +0 -1234
  421. package/dist/indexer/usage/unmigrated-vaults-guard.js +0 -95
  422. package/dist/llm/memory-infer-impl.js +0 -138
  423. package/dist/migrate/legacy/config-source-migration.js +0 -223
  424. package/dist/migrate/legacy/content-migration.js +0 -305
  425. package/dist/migrate/legacy/legacy-layout.js +0 -779
  426. package/dist/migrate/legacy/legacy-paths.js +0 -25
  427. package/dist/migrate/legacy/legacy-stash-json.js +0 -72
  428. package/dist/migrate/legacy/proposal-fs-import.js +0 -168
  429. package/dist/migrate/legacy/task-target-ref-migration.js +0 -272
  430. package/dist/migrate/legacy/three-db-cutover.js +0 -841
  431. package/dist/migrate/legacy/workflow-migrations-bodies.js +0 -52
  432. package/dist/migrate/legacy/workflow-migrations-frozen.js +0 -21
  433. package/dist/migrate/legacy-ref-grammar.js +0 -214
  434. package/dist/output/shapes/distill.js +0 -14
  435. package/dist/output/shapes/history.js +0 -11
  436. package/dist/output/text/distill.js +0 -6
  437. package/dist/output/text/enable-disable.js +0 -8
  438. package/dist/output/text/history.js +0 -6
  439. package/dist/registry/build-index.js +0 -382
  440. package/dist/schemas/akm-config.json +0 -4704
  441. package/dist/schemas/akm-task.json +0 -87
  442. package/dist/schemas/akm-workflow.json +0 -372
  443. package/dist/scripts/migrate-storage.js +0 -3816
  444. package/dist/workflows/authoring/workflow-program-template.yaml +0 -31
  445. package/dist/workflows/cli.js +0 -53
  446. package/dist/workflows/exec/brief.js +0 -481
  447. package/dist/workflows/exec/report.js +0 -1460
  448. package/dist/workflows/exec/watch.js +0 -116
  449. package/dist/workflows/program/parser.js +0 -813
  450. package/dist/workflows/program/project.js +0 -104
@@ -4,27 +4,20 @@
4
4
  /**
5
5
  * The `akm-workflow` adapter — akm 0.9.0 format-family work item (#46).
6
6
  *
7
- * A native akm workflow bundle (spec §6/§7). Both workflow forms derive
8
- * `type: workflow`:
9
- * - MARKDOWN (`.md`, ≈ an OKF concept) — the classic linear workflow
10
- * (`# Workflow:` / `## Step:` / `Step ID:` / `### Instructions`), detected
11
- * by the shared `looksLikeWorkflow` probe so the matcher and parser cannot
12
- * drift;
13
- * - YAML PROGRAM (`.yaml`/`.yml`, an AKM extension) — `version` + `steps`,
14
- * detected by `looksLikeWorkflowProgram`.
15
- * conceptId strips the recognized workflow extension (`.md`/`.yaml`/`.yml`). A
16
- * plain `.md` that is NOT workflow-shaped (a README, an OKF reserved listing/log
17
- * file) is abstained on — the content probe subsumes the D-R6 reserved-file
18
- * exclusion, so no reserved-basename literal is needed here.
7
+ * A native akm workflow bundle (spec §6/§7). One format now (workflow-format-
8
+ * unification): `.md` files, orchestration graph in frontmatter, per-step
9
+ * prose in the body. Recognition is frontmatter `type: workflow`, or — since
10
+ * this adapter's whole domain IS workflows — simply residing under this
11
+ * bundle's root; no content sniffing. conceptId strips the `.md` extension. A
12
+ * plain `.md` that opts out via an explicit non-workflow `type:` frontmatter
13
+ * key is abstained on.
19
14
  *
20
15
  * ── validate (spec §6 workflow row) ──
21
16
  *
22
17
  * Reuses the akm adapter's per-type workflow checks (shared base checks +
23
- * `placeholder-stub` + `invalid-workflow-structure`, markdown only — the YAML
24
- * program's correctness is `parseWorkflowProgram`'s own result, not a
25
- * markdown-lint path). Delegates to the SAME `perTypeValidateChecks` /
26
- * `runBaseValidateChecks` the `akm` adapter uses, so workflow validation has
27
- * one home.
18
+ * `placeholder-stub` + `invalid-workflow-structure`). Delegates to the SAME
19
+ * `perTypeValidateChecks` / `runBaseValidateChecks` the `akm` adapter uses, so
20
+ * workflow validation has one home.
28
21
  *
29
22
  * Conformance oracle (authored, DO NOT modify): fixture
30
23
  * `tests/fixtures/bundles/akm-workflow/` + goldens
@@ -32,60 +25,52 @@
32
25
  */
33
26
  import fs from "node:fs";
34
27
  import path from "node:path";
35
- import { parse as parseYaml } from "yaml";
36
- import { looksLikeWorkflow } from "../../../workflows/parser.js";
37
- import { looksLikeWorkflowProgram } from "../../../workflows/program/parser.js";
38
28
  import { parseFrontmatter } from "../../asset/frontmatter.js";
39
29
  import { perTypeValidateChecks } from "./akm-lint.js";
40
30
  import { hashContent, nonEmptyString, readTags, runBaseValidateChecks } from "./shared.js";
41
31
  /** A native workflow bundle is single-component; its one component is `main`. */
42
32
  const COMPONENT_ID = "main";
43
- /** Recognized workflow extensions (matcher parity with recognition-util WORKFLOW_EXTENSIONS). */
44
- const WORKFLOW_EXTS = new Set([".md", ".yaml", ".yml"]);
33
+ /** One recognized workflow extension now (workflow-format-unification): `.md`. */
34
+ const WORKFLOW_EXTS = new Set([".md"]);
45
35
  /** Upper bound on the bounded `content` FTS field (mirrors okf-adapter). */
46
36
  const MAX_CONTENT_CHARS = 100_000;
47
37
  function toPosix(p) {
48
38
  return p.replace(/\\/g, "/");
49
39
  }
50
- /** Strip a recognized workflow extension from a component-root-relative path → conceptId. */
40
+ /** Strip the recognized workflow extension from a component-root-relative path → conceptId. */
51
41
  function conceptIdOf(relPath) {
52
- return toPosix(relPath).replace(/\.(md|ya?ml)$/i, "");
42
+ return toPosix(relPath).replace(/\.md$/i, "");
53
43
  }
54
- /** Which workflow form (if any) `file` is — the recognition gate. */
55
- function workflowForm(ext, raw) {
56
- if (ext === ".md")
57
- return looksLikeWorkflow(parseFrontmatter(raw).content) ? "markdown" : null;
58
- if (ext === ".yaml" || ext === ".yml")
59
- return looksLikeWorkflowProgram(raw) ? "yaml-program" : null;
60
- return null;
44
+ /** `README.md` (case-insensitive) is documentation, never the typed asset — mirrors the akm matcher stack's D-R6 reserved-file exclusion (`TYPED_DIR_DOC_FILES`). */
45
+ function isReservedDocFile(fileName) {
46
+ return fileName.toLowerCase() === "readme.md";
61
47
  }
62
- /** The workflow's projected `description` — frontmatter for markdown, the program `description:` for YAML. */
63
- function workflowDescription(form, raw) {
64
- if (form === "markdown")
65
- return nonEmptyString(parseFrontmatter(raw).data.description);
66
- try {
67
- const doc = parseYaml(raw);
68
- if (doc && typeof doc === "object" && !Array.isArray(doc)) {
69
- return nonEmptyString(doc.description);
70
- }
71
- }
72
- catch {
73
- // malformed YAML — no description
74
- }
75
- return undefined;
48
+ /**
49
+ * Recognition gate: any `.md` file in this bundle IS a workflow (this
50
+ * adapter's entire domain is workflows — spec §2.5, "residence under
51
+ * workflows/"), UNLESS its frontmatter declares a DIFFERENT non-empty
52
+ * `type:` (an explicit opt-out, e.g. a README-shaped doc that wants to be
53
+ * something else). No content sniffing.
54
+ */
55
+ function isWorkflowFile(raw) {
56
+ const data = parseFrontmatter(raw).data;
57
+ const type = data.type;
58
+ return type === undefined || type === "workflow";
76
59
  }
77
60
  function recognize(c, file) {
78
61
  if (!WORKFLOW_EXTS.has(file.ext))
79
62
  return null;
63
+ if (isReservedDocFile(file.fileName))
64
+ return null;
80
65
  const raw = file.content();
81
- const form = workflowForm(file.ext, raw);
82
- if (form === null)
66
+ if (!isWorkflowFile(raw))
83
67
  return null;
84
68
  const conceptId = conceptIdOf(file.relPath);
85
69
  const name = conceptId.split("/").pop() ?? conceptId;
86
- const description = workflowDescription(form, raw);
87
- const body = form === "markdown" ? parseFrontmatter(raw).content : raw;
88
- const tags = readTags(parseFrontmatter(raw).data.tags);
70
+ const parsed = parseFrontmatter(raw);
71
+ const description = nonEmptyString(parsed.data.description);
72
+ const body = parsed.content;
73
+ const tags = readTags(parsed.data.tags);
89
74
  const doc = {
90
75
  ref: `${c.id}//${conceptId}`,
91
76
  bundle: c.id,
@@ -113,18 +98,11 @@ async function validate(c, changes, ctx) {
113
98
  if (typeof raw !== "string")
114
99
  continue;
115
100
  const ext = path.extname(change.path).toLowerCase();
116
- if (!WORKFLOW_EXTS.has(ext) || workflowForm(ext, raw) === null)
101
+ if (!WORKFLOW_EXTS.has(ext) || isReservedDocFile(path.basename(change.path)) || !isWorkflowFile(raw))
117
102
  continue;
118
103
  const relPath = toPosix(change.path);
119
- // Markdown workflows parse via frontmatter; YAML programs are pure YAML (no frontmatter).
120
- let parsed;
121
- if (ext === ".md") {
122
- const p = parseFrontmatter(raw);
123
- parsed = { data: p.data, content: p.content, frontmatter: p.frontmatter };
124
- }
125
- else {
126
- parsed = { data: {}, content: raw, frontmatter: null };
127
- }
104
+ const p = parseFrontmatter(raw);
105
+ const parsed = { data: p.data, content: p.content, frontmatter: p.frontmatter };
128
106
  diagnostics.push(...(await runBaseValidateChecks(relPath, parsed, c.root, ctx)));
129
107
  diagnostics.push(...(await perTypeValidateChecks({
130
108
  type: "workflow",
@@ -139,13 +117,22 @@ async function validate(c, changes, ctx) {
139
117
  }
140
118
  return diagnostics;
141
119
  }
142
- /** True when a top-level file in `root` is workflow-shaped (used by looksLikeRoot). */
120
+ /**
121
+ * True when a top-level file in `root` is workflow-shaped (used by
122
+ * looksLikeRoot). Unlike `isWorkflowFile` (which admits an absent `type:` —
123
+ * the lenient default ONCE a source is already known to be this bundle),
124
+ * this requires an EXPLICIT `type: workflow` — the install-time probe is
125
+ * choosing WHICH adapter owns an unconfigured root among several candidates
126
+ * (spec §1.2), and an incidental `.md` with no frontmatter type at all
127
+ * (common in an OKF or llm-wiki bundle) must not misclassify that root as
128
+ * akm-workflow's.
129
+ */
143
130
  function hasTopLevelWorkflowFile(root, entries) {
144
131
  for (const entry of entries) {
145
132
  if (!entry.isFile())
146
133
  continue;
147
134
  const ext = path.extname(entry.name).toLowerCase();
148
- if (!WORKFLOW_EXTS.has(ext))
135
+ if (!WORKFLOW_EXTS.has(ext) || isReservedDocFile(entry.name))
149
136
  continue;
150
137
  let raw;
151
138
  try {
@@ -154,7 +141,7 @@ function hasTopLevelWorkflowFile(root, entries) {
154
141
  catch {
155
142
  continue;
156
143
  }
157
- if (workflowForm(ext, raw) !== null)
144
+ if (parseFrontmatter(raw).data.type === "workflow")
158
145
  return true;
159
146
  }
160
147
  return false;
@@ -162,13 +149,13 @@ function hasTopLevelWorkflowFile(root, entries) {
162
149
  export const akmWorkflowAdapter = {
163
150
  id: "akm-workflow",
164
151
  version: "0.9.0",
165
- extensions: [".md", ".yaml", ".yml"],
152
+ extensions: [".md"],
166
153
  recognize,
167
154
  validate,
168
- /** Default markdown placement; an explicit `.yaml`/`.yml`/`.md` conceptId short-circuits to that extension. */
155
+ /** Markdown placement; an explicit `.md` conceptId short-circuits to that extension. */
169
156
  placeNew(c, conceptId) {
170
157
  const posix = toPosix(conceptId);
171
- if (/\.(md|ya?ml)$/i.test(posix))
158
+ if (/\.md$/i.test(posix))
172
159
  return path.join(c.root, posix);
173
160
  return path.join(c.root, `${posix}.md`);
174
161
  },
@@ -177,11 +164,8 @@ export const akmWorkflowAdapter = {
177
164
  return ["."];
178
165
  },
179
166
  /**
180
- * Install-time probe (§1.2): a root holding a workflow file at top level — a
181
- * `.yaml`/`.yml` program (`version` + `steps`) or a workflow-shaped `.md`. The
182
- * content probe means an okf reserved listing, a wiki `schema.md`, or a README
183
- * never trips it, so the probe stays disjoint from the other adapters' roots
184
- * without any structural exclusion.
167
+ * Install-time probe (§1.2): a root holding a workflow-shaped `.md` file at
168
+ * top level (frontmatter `type: workflow` or no `type:` at all).
185
169
  */
186
170
  looksLikeRoot(root) {
187
171
  let entries;
@@ -18,7 +18,7 @@
18
18
  *
19
19
  * ── validate (spec §6 env/secret validation column) ──
20
20
  *
21
- * The dangerous-key scan (`dangerous-vault-key`), reusing the akm adapter's
21
+ * The dangerous-key scan (`dangerous-env-key`), reusing the akm adapter's
22
22
  * `dangerousEnvKeyDiagnostics` — which preserves the code-grounded narrowness:
23
23
  * it runs ONLY on `*.env`-suffixed files, so `secrets/<bare-name>` is never
24
24
  * scanned (its whole content is an opaque secret value). Reads KEY NAMES only.
@@ -71,6 +71,8 @@ function recognize(c, file) {
71
71
  name,
72
72
  content: body.length > MAX_CONTENT_CHARS ? body.slice(0, MAX_CONTENT_CHARS) : body,
73
73
  };
74
+ if (type !== "script")
75
+ doc.ownsPresentation = true;
74
76
  const description = nonEmptyString(parsed.data.description);
75
77
  if (description !== undefined)
76
78
  doc.description = description;
@@ -33,10 +33,10 @@ export { websiteSnapshotAdapter } from "./website-snapshot-adapter.js";
33
33
  *
34
34
  * Probe-precedence rationale (grounds "cannot shadow the existing three"):
35
35
  *
36
- * - The three loosest probes — `llm-wiki` (schema.md + pages/), `okf` (root
37
- * index doc), `akm` (a placement stash subdir / `.stash`) — stay LAST and in
38
- * their established order (`llm-wiki` BEFORE `okf`: a wiki root also carries a
39
- * root index doc, so the more-specific wiki probe must win the overlap).
36
+ * - The three loosest probes — `llm-wiki`, `akm`, and `okf` — stay LAST.
37
+ * Native AKM evidence wins before OKF because AKM Markdown is an OKF
38
+ * superset; the AKM probe is correspondingly strict enough not to claim an
39
+ * OKF bundle merely because it contains one familiar directory name.
40
40
  * - `akm.looksLikeRoot` fires on ANY root carrying a stash-subdir-named
41
41
  * directory — which includes a `.claude`/`.opencode` tool dir (`commands/`,
42
42
  * `agents/`, `skills/`) and a dotenv bundle (`env/`, `secrets/`). So
@@ -62,10 +62,10 @@ export const BUILTIN_ADAPTERS = Object.freeze([
62
62
  // Native akm sub-formats (disjoint content-shape probes).
63
63
  akmWorkflowAdapter,
64
64
  akmTaskAdapter,
65
- // The three established loose probes, in their established order.
65
+ // Loose probes: specific native layouts before the portable OKF baseline.
66
66
  llmWikiAdapter,
67
- okfAdapter,
68
67
  akmAdapter,
68
+ okfAdapter,
69
69
  // Explicit-config fallback (never auto-selected) — last.
70
70
  genericFilesAdapter,
71
71
  ]);
@@ -5,8 +5,8 @@
5
5
  * The first-class `llm-wiki` adapter — akm 0.9.0 chunk-4 (DEV-7 restore).
6
6
  *
7
7
  * The `wiki` ASSET-TYPE dies in chunk 4 (plan §11 Chunk 4 / §7.4), but the LLM
8
- * Wiki structure stays first-class as its OWN adapter. This relocates the native
9
- * wiki semantics from `src/wiki/wiki.ts` + `src/wiki/wiki-templates.ts` into a
8
+ * Wiki structure stays first-class as its OWN adapter. This relocates the
9
+ * native wiki semantics from the retired `src/wiki/` module into a
10
10
  * `BundleAdapter` implementing `docs/architecture/specs/akm-0.9.0-bundle-adapter-spec.md` §7
11
11
  * (llm-wiki row), §6 (wiki-page row), §0.2 (the `wiki` asset-type is retired;
12
12
  * the adapter is first-class), §1.2 (probe = schema.md + pages/), §9 (links).
@@ -42,7 +42,8 @@
42
42
  *
43
43
  * ── validate (spec §6/§7, §9) — native wiki checks ONLY ──
44
44
  *
45
- * The native structural checks ported from `wiki.ts#lintWiki`: `broken-xref`
45
+ * The native structural checks ported from the retired `src/wiki/` module's
46
+ * `lintWiki`: `broken-xref`
46
47
  * (an xref target that does not resolve to an existing page), `uncited-raw`
47
48
  * (a `raw/` source not cited by any page's `sources:`), `missing-description`
48
49
  * (a page with no description), and `broken-source` (a page `sources:` entry
@@ -58,11 +59,15 @@
58
59
  *
59
60
  * ── Cycle-safety ──
60
61
  *
61
- * Imported ONLY by the test-only `adapters/index.ts` barrel (nothing in `src/`
62
- * imports that), so this leaf can never gain an inbound edge from a cycle
63
- * participant. It value-imports only pure leaves (`frontmatter`, `shared`) plus
64
- * Node builtins + `yaml` (already a runtime dep). Verified: `bun
65
- * scripts/lint-import-cycles.ts` stays within baseline (13) with this file present.
62
+ * Imported by the `adapters/index.ts` barrel, which is NOT test-only: `core/
63
+ * adapter/registry.ts:33` imports it in production for the frozen
64
+ * `BUILTIN_ADAPTERS` list (`installations.ts#detectAdapterId`,
65
+ * `provider-utils.ts#detectStashRoot`). This still can never gain an inbound
66
+ * edge from a cycle participant, since neither the barrel nor the registry
67
+ * import anything that imports back into this file. It value-imports only
68
+ * pure leaves (`frontmatter`, `shared`) plus Node builtins + `yaml` (already a
69
+ * runtime dep). Verified: `bun scripts/lint-import-cycles.ts` reports 0 cycle
70
+ * participants with this file present.
66
71
  */
67
72
  import fs from "node:fs";
68
73
  import path from "node:path";
@@ -272,6 +277,7 @@ function recognize(c, file) {
272
277
  path: file.absPath,
273
278
  hash: hashContent(raw),
274
279
  adapterId: "llm-wiki",
280
+ ownsPresentation: true,
275
281
  type: isRaw ? WIKI_SOURCE_TYPE : (fm.pageKind ?? DEFAULT_PAGE_KIND),
276
282
  name: lastSegment,
277
283
  content: body.length > MAX_CONTENT_CHARS ? body.slice(0, MAX_CONTENT_CHARS) : body,
@@ -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)