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
@@ -5,9 +5,16 @@ import fs from "node:fs";
5
5
  import path from "node:path";
6
6
  import { parse as parseYaml } from "yaml";
7
7
  import { factDiagnostics, matchWorkflowPlaceholder, memoryOrphanStubApplies, nameOrTypeDiagnostics, ORPHANED_STUB_DETAIL, taskDiagnostics, workflowStructureDiagnostics, } from "../../core/adapter/adapters/akm-lint.js";
8
+ import { detectAdapterId } from "../../core/adapter/detect-adapter.js";
9
+ import { adapterForId } from "../../core/adapter/registry.js";
10
+ import { createValidateContext } from "../../core/adapter/validate-context.js";
11
+ import { stashDirFor } from "../../core/asset/asset-placement.js";
8
12
  import { parseFrontmatter } from "../../core/asset/frontmatter.js";
13
+ import { conceptIdForStashFile, displayRefForConceptId } from "../../core/asset/resolve-ref.js";
14
+ import { deriveBundleIds } from "../../core/bundle-id.js";
9
15
  import { resolveStashDir } from "../../core/common.js";
10
16
  import { loadConfig, primaryBundlePath } from "../../core/config/config.js";
17
+ import { UsageError } from "../../core/errors.js";
11
18
  import { resolveSourceEntries } from "../../indexer/search/search-source.js";
12
19
  import { runBaseChecks } from "./base-linter.js";
13
20
  import { checkEnvForDangerousKeys } from "./env-key-rules.js";
@@ -39,21 +46,164 @@ function collectYamlFiles(dir) {
39
46
  }
40
47
  return results;
41
48
  }
42
- function collectMarkdownFiles(dir) {
49
+ function collectMarkdownFiles(dir, caseInsensitive = false) {
43
50
  if (!fs.existsSync(dir))
44
51
  return [];
45
52
  const results = [];
46
53
  for (const entry of fs.readdirSync(dir, { withFileTypes: true })) {
54
+ if (entry.name === ".git" || entry.isSymbolicLink())
55
+ continue;
47
56
  const full = path.join(dir, entry.name);
48
57
  if (entry.isDirectory()) {
49
- results.push(...collectMarkdownFiles(full));
58
+ results.push(...collectMarkdownFiles(full, caseInsensitive));
50
59
  }
51
- else if (entry.isFile() && entry.name.endsWith(".md")) {
60
+ else if (entry.isFile() && (caseInsensitive ? entry.name.toLowerCase() : entry.name).endsWith(".md")) {
52
61
  results.push(full);
53
62
  }
54
63
  }
55
64
  return results;
56
65
  }
66
+ // ── Non-akm adapter dispatch (real `adapter.validate()`, not a re-implementation) ──
67
+ //
68
+ // akm 0.9.0 lint/adapter-dispatch wiring: `akm lint` used to special-case
69
+ // exactly one non-akm adapter (`okf`, via a hand-rolled `missing-type`-only
70
+ // `lintOkfBundle` re-implementation this change deletes) and silently route
71
+ // every OTHER non-akm bundle (llm-wiki, dotenv, claude, opencode,
72
+ // agent-skills, website-snapshot, generic-files, akm-task, akm-workflow)
73
+ // through the AKM-shaped STASH_SUBDIRS sweep — the wrong linter for the wrong
74
+ // format, and the reason those adapters' own `validate()` checks (OKF's
75
+ // `missing-ref`; llm-wiki's `uncited-raw`/`missing-description`/`broken-xref`/
76
+ // `broken-source`) were unreachable dead code. Every bundle is now linted by
77
+ // its OWN configured/detected adapter's `validate()` — the single definition
78
+ // of that format's rules, shared with the (now also wired, advisory-only)
79
+ // change-transaction pre-commit gate in `commands/proposal/repository.ts`.
80
+ // This is intentionally the ONLY branch this module adds: the `akm` sweep
81
+ // below is completely untouched (pinned by the goldens/test suite —
82
+ // CRITICAL: akm findings/`--fix` must not move).
83
+ /**
84
+ * Case-insensitive SUFFIX match against an adapter's declared `extensions`
85
+ * hint. Deliberately NOT `path.extname()`: Node's `extname(".env")` is `""`
86
+ * (a leading-dot-only basename has no "extension" by that definition), which
87
+ * would silently skip every bare `env/.env` file — exactly the shape
88
+ * `dotenvAdapter`'s own `classify()` (and the akm adapter's env recognition)
89
+ * match by plain `endsWith`, not `path.extname`. A suffix match is also a
90
+ * strict superset of the `path.extname` behavior for a normal `name.ext`
91
+ * file, so nothing that matched before stops matching.
92
+ */
93
+ function matchesAdapterExtension(fileName, extensions) {
94
+ const lower = fileName.toLowerCase();
95
+ return extensions.some((candidate) => lower.endsWith(candidate.toLowerCase()));
96
+ }
97
+ /** Walk the whole bundle tree, collecting every file whose extension the adapter recognizes (skip `.git`, symlinks, cache/registry copies). */
98
+ function collectAdapterFiles(root, extensions) {
99
+ if (!fs.existsSync(root))
100
+ return [];
101
+ const results = [];
102
+ const walk = (dir) => {
103
+ let entries;
104
+ try {
105
+ entries = fs.readdirSync(dir, { withFileTypes: true });
106
+ }
107
+ catch {
108
+ return;
109
+ }
110
+ for (const entry of entries) {
111
+ if (entry.name === ".git" || entry.isSymbolicLink())
112
+ continue;
113
+ const full = path.join(dir, entry.name);
114
+ if (entry.isDirectory()) {
115
+ walk(full);
116
+ }
117
+ else if (entry.isFile() && matchesAdapterExtension(entry.name, extensions)) {
118
+ // Compare PATH SEGMENTS, not raw substrings: `path.join` yields `\` on
119
+ // Windows so a `"/.cache/"` substring test never matches there, and a
120
+ // substring test would also skip a legitimately-named `registry` file.
121
+ const segments = path.relative(root, full).split(/[\\/]/);
122
+ if (segments.includes(".cache") || segments.includes("registry"))
123
+ continue;
124
+ results.push(full);
125
+ }
126
+ }
127
+ };
128
+ walk(root);
129
+ return results;
130
+ }
131
+ /**
132
+ * Every closed {@link LintIssueType} member a current adapter `validate()` can
133
+ * legitimately emit. Anything outside this set folds onto `"adapter-diagnostic"`
134
+ * (see `types.ts`'s doc comment on that member) rather than being dropped.
135
+ */
136
+ const KNOWN_ADAPTER_ISSUE_TYPES = new Set([
137
+ "unquoted-colon",
138
+ "missing-updated",
139
+ "stale-path",
140
+ "missing-ref",
141
+ "missing-type",
142
+ "missing-name-or-type",
143
+ "missing-skill-md",
144
+ "dangerous-env-key",
145
+ "uncited-raw",
146
+ "missing-description",
147
+ "broken-xref",
148
+ "broken-source",
149
+ ]);
150
+ /** Map one adapter {@link Diagnostic} onto a {@link LintIssue} — see `types.ts`'s `"adapter-diagnostic"` doc comment for the open→closed reconciliation. */
151
+ export function diagnosticToLintIssue(diag) {
152
+ if (KNOWN_ADAPTER_ISSUE_TYPES.has(diag.issue)) {
153
+ return { file: diag.file, issue: diag.issue, detail: diag.detail, fixed: diag.fixed };
154
+ }
155
+ return { file: diag.file, issue: "adapter-diagnostic", detail: `[${diag.issue}] ${diag.detail}`, fixed: diag.fixed };
156
+ }
157
+ /**
158
+ * Lint a bundle through its OWN adapter's `validate()` (spec §12.1): the
159
+ * adapter never writes, so every finding lands in `flagged` — `fixed` is
160
+ * always `false`/`"failed"` for a non-akm bundle regardless of `--fix`
161
+ * (the CLI option is silently a no-op here, exactly as it already was for
162
+ * `okf` before this change).
163
+ */
164
+ async function lintViaAdapter(adapterId, stashRoot, extraStashRoots, sources, cfg, options) {
165
+ const adapter = adapterForId(adapterId);
166
+ // Defensive fallback (shouldn't happen via `detectAdapterId`/a valid config
167
+ // — both only ever name a registered built-in): an unregistered adapter id
168
+ // falls back to the akm-shaped sweep, the same default `akm lint` has
169
+ // always applied to a bundle it can't otherwise place.
170
+ if (!adapter)
171
+ return lintAkmSweep(stashRoot, extraStashRoots, cfg, sources, options);
172
+ const files = collectAdapterFiles(stashRoot, adapter.extensions);
173
+ const changes = files.map((filePath) => ({
174
+ path: path.relative(stashRoot, filePath).replace(/\\/g, "/"),
175
+ op: "update",
176
+ }));
177
+ const ids = deriveBundleIds(sources);
178
+ const sourceIndex = sources.findIndex((s) => path.resolve(s.path) === path.resolve(stashRoot));
179
+ const componentId = sourceIndex >= 0 ? ids[sourceIndex] : stashRoot;
180
+ const ctx = createValidateContext({ root: stashRoot, extraRoots: extraStashRoots });
181
+ const diagnostics = await adapter.validate({ id: componentId, adapter: adapterId, root: stashRoot, writable: true }, changes, ctx);
182
+ const flagged = diagnostics.map(diagnosticToLintIssue);
183
+ // The cross-bundle env dangerous-key sweep (see `runEnvDangerousKeyPass`'s
184
+ // doc comment) ran for every non-akm adapter via the STASH_SUBDIRS
185
+ // fallthrough this dispatch replaces — EXCEPT `okf`, which the old code
186
+ // special-cased out before ever reaching that pass. Preserve both halves of
187
+ // that history exactly: skip only for `okf`. Some adapters (`dotenv`) ALSO
188
+ // find the same `dangerous-env-key` findings natively through their own
189
+ // `validate()` (reusing the same `dangerousEnvKeyDiagnostics` rule) — dedupe
190
+ // by `(file, issue, detail)` so a bundle covered both ways reports each
191
+ // finding once, not twice.
192
+ if (adapterId !== "okf") {
193
+ const seen = new Set(flagged.map(lintIssueDedupeKey));
194
+ for (const issue of runEnvDangerousKeyPass(stashRoot, extraStashRoots, sources, cfg)) {
195
+ const key = lintIssueDedupeKey(issue);
196
+ if (seen.has(key))
197
+ continue;
198
+ seen.add(key);
199
+ flagged.push(issue);
200
+ }
201
+ }
202
+ return { ok: true, fixed: [], flagged, summary: { fixed: 0, flagged: flagged.length } };
203
+ }
204
+ function lintIssueDedupeKey(issue) {
205
+ return `${issue.file} ${issue.issue} ${issue.detail}`;
206
+ }
57
207
  function collectEnvFiles(dir) {
58
208
  const results = [];
59
209
  try {
@@ -70,6 +220,45 @@ function collectEnvFiles(dir) {
70
220
  }
71
221
  return results;
72
222
  }
223
+ /**
224
+ * Scan every `env/`/`secrets/` `.env` file across `[stashRoot, ...extraStashRoots]`
225
+ * for keys that are known to enable process-execution hijacking. This is a
226
+ * cross-bundle SECURITY sweep, not per-adapter validation — it has always run
227
+ * regardless of which format family a given root's OWN files are (verbatim
228
+ * extraction of the pass `lintAkmSweep` still runs inline; kept byte-identical
229
+ * there per the CRITICAL akm-path constraint, and reused here for the
230
+ * non-akm dispatch path so a non-akm PRIMARY bundle keeps the exact
231
+ * cross-bundle coverage it already had — the pass previously reached every
232
+ * non-akm adapter's bundle via the accidental STASH_SUBDIRS fallthrough this
233
+ * change replaces with real dispatch).
234
+ */
235
+ function runEnvDangerousKeyPass(stashRoot, extraStashRoots, sources, cfg) {
236
+ const flagged = [];
237
+ const envRoots = [stashRoot, ...extraStashRoots];
238
+ const bundleIdByRoot = new Map(sources.map((source) => [path.resolve(source.path), source.registryId]));
239
+ for (const root of envRoots) {
240
+ const bundleId = bundleIdByRoot.get(path.resolve(root));
241
+ // `env` assets live under `env/`, whole-file `secret` assets under
242
+ // `secrets/`. `displayRefForConceptId` owns the short-default /
243
+ // qualified-secondary `Ref:` spelling `akm show` emits — the old
244
+ // hand-built `env:<base>` colon grammar is rejected by the 0.9.0 ref
245
+ // parser, which dead-ended a user copying the ref off a security finding.
246
+ for (const assetType of ["env", "secret"]) {
247
+ const dir = path.join(root, stashDirFor(assetType));
248
+ if (!fs.existsSync(dir))
249
+ continue;
250
+ for (const envPath of collectEnvFiles(dir)) {
251
+ const conceptId = conceptIdForStashFile(assetType, root, envPath);
252
+ const ref = displayRefForConceptId(conceptId, bundleId, cfg.defaultBundle);
253
+ const relPath = path.relative(root, envPath);
254
+ for (const issue of checkEnvForDangerousKeys(envPath, relPath, ref)) {
255
+ flagged.push(issue);
256
+ }
257
+ }
258
+ }
259
+ }
260
+ return flagged;
261
+ }
73
262
  /** True when the issue represents a file deletion that was successfully applied. */
74
263
  function isFileDeletion(issue) {
75
264
  return issue.fixed === true && (issue.issue === "orphaned-stub" || issue.issue === "placeholder-stub");
@@ -99,7 +288,7 @@ export function lintSkillDirectory(subdirPath, stashRoot) {
99
288
  function appendMemoryStubIssue(ctx, issues) {
100
289
  if (!memoryOrphanStubApplies(ctx.data, ctx.body))
101
290
  return;
102
- const derivedPath = `${ctx.filePath.replace(/\.md$/, "")}.derived.md`;
291
+ const derivedPath = `${ctx.filePath.replace(/\.md$/i, "")}.derived.md`;
103
292
  if (fs.existsSync(derivedPath))
104
293
  return;
105
294
  if (ctx.fix) {
@@ -187,24 +376,26 @@ export function lintAssetFile(ctx, subdir) {
187
376
  return issues;
188
377
  }
189
378
  // ── Main ──────────────────────────────────────────────────────────────────────
190
- export function akmLint(options = {}) {
191
- // Collect secondary stash roots from configured filesystem sources so that
192
- // cross-stash refs (e.g. referencing assets in dimm-city/agent-stash) are
193
- // not falsely flagged as missing-ref.
194
- const cfg = options.config ?? loadConfig();
195
- // 0.9.0 (spec §10.1): the primary stash is the defaultBundle's path.
196
- const stashRoot = options.dir ?? primaryBundlePath(cfg) ?? resolveStashDir();
197
- const extraStashRoots = resolveSourceEntries(stashRoot, cfg)
198
- .map((s) => s.path)
199
- .filter((p) => p !== stashRoot && fs.existsSync(p));
200
- const fix = options.fix ?? false;
379
+ /**
380
+ * The `akm`-adapter sweep: STASH_SUBDIRS walk + per-file `lintAssetFile` +
381
+ * the env dangerous-key pass. UNTOUCHED by the adapter-dispatch wiring above
382
+ * (CRITICAL CONSTRAINT — the overwhelming majority of real bundles use `akm`,
383
+ * and its findings / `--fix` behavior / exact `LintIssueType` codes are
384
+ * pinned by goldens + a large test suite). Only reached when the resolved
385
+ * adapter id is `"akm"` (or, defensively, an unregistered adapter id —
386
+ * see {@link lintViaAdapter}'s fallback).
387
+ */
388
+ function lintAkmSweep(stashRoot, extraStashRoots, cfg, sources, options) {
389
+ const fix = options.fix === true;
201
390
  const fixed = [];
202
391
  const flagged = [];
203
392
  const dirsToScan = options.typeFilter ? STASH_SUBDIRS.filter((d) => d === options.typeFilter) : STASH_SUBDIRS;
204
393
  for (const subdir of dirsToScan) {
205
394
  const dirPath = path.join(stashRoot, subdir);
206
- // Tasks are .yml files; everything else is .md
207
- const files = subdir === "tasks" ? collectYamlFiles(dirPath) : collectMarkdownFiles(dirPath);
395
+ // Tasks are .yml files; everything else (including workflows, one
396
+ // markdown format now) is .md
397
+ const files = subdir === "tasks" ? collectYamlFiles(dirPath) : collectMarkdownFiles(dirPath, true);
398
+ const assetFiles = subdir === "workflows" ? files.filter((file) => path.basename(file).toLowerCase() !== "readme.md") : files;
208
399
  // Directory-level check: skills require a SKILL.md entry point (was
209
400
  // SkillLinter.lintDirectory). Run once per direct subdirectory before the
210
401
  // per-file loop.
@@ -224,7 +415,7 @@ export function akmLint(options = {}) {
224
415
  }
225
416
  }
226
417
  }
227
- for (const filePath of files) {
418
+ for (const filePath of assetFiles) {
228
419
  // Skip registry-cached read-only files — --fix must not mutate them.
229
420
  if (filePath.includes("/.cache/") || filePath.includes("/registry/"))
230
421
  continue;
@@ -278,31 +469,7 @@ export function akmLint(options = {}) {
278
469
  // Scan every `.env` file under <stashRoot>/env/ across all stash roots for
279
470
  // keys that are known to enable process-execution hijacking. Warn-only —
280
471
  // findings go into `flagged`, never `fixed`.
281
- const envRoots = [stashRoot, ...extraStashRoots];
282
- for (const root of envRoots) {
283
- // The `env` assets live under `env/` (ref prefix `env:`); whole-file
284
- // `secret` assets live under `secrets/` (canonical ref prefix `secret:`,
285
- // singular). Map the scan directory to its canonical ref prefix so the
286
- // finding's `Ref:` field matches what `akm show`/`akm secret` accept.
287
- for (const { scanSubdir, refPrefix } of [
288
- { scanSubdir: "env", refPrefix: "env" },
289
- { scanSubdir: "secrets", refPrefix: "secret" },
290
- ]) {
291
- const dir = path.join(root, scanSubdir);
292
- if (!fs.existsSync(dir))
293
- continue;
294
- for (const envPath of collectEnvFiles(dir)) {
295
- const baseName = path.basename(envPath, ".env");
296
- // A dotfile literally named `.env` has an empty baseName — use the full
297
- // basename so it doesn't collide with `default.env` → refPrefix:default.
298
- const ref = baseName === "" ? `${refPrefix}:.env` : `${refPrefix}:${baseName}`;
299
- const relPath = path.relative(root, envPath);
300
- for (const issue of checkEnvForDangerousKeys(envPath, relPath, ref)) {
301
- flagged.push(issue);
302
- }
303
- }
304
- }
305
- }
472
+ flagged.push(...runEnvDangerousKeyPass(stashRoot, extraStashRoots, sources, cfg));
306
473
  // `ok` reflects whether the lint run completed successfully — NOT whether
307
474
  // it found anything. Findings are surfaced via `summary.flagged`; the CLI
308
475
  // gates its exit code on `--fail-on-flagged`. Conflating "issues exist"
@@ -319,3 +486,42 @@ export function akmLint(options = {}) {
319
486
  summary: { fixed: fixed.length, flagged: flagged.length },
320
487
  };
321
488
  }
489
+ /**
490
+ * Lint the resolved bundle at `options.dir` (default: the primary bundle).
491
+ * Dispatches to the bundle's OWN adapter: the `akm` sweep for `"akm"`
492
+ * (unchanged), or {@link lintViaAdapter} — a real `adapter.validate()` call —
493
+ * for every other configured/detected adapter id (OKF, llm-wiki, dotenv,
494
+ * claude, opencode, agent-skills, website-snapshot, generic-files, akm-task,
495
+ * akm-workflow). `async` because `BundleAdapter.validate()` is async by
496
+ * interface contract (`core/adapter/bundle-adapter.ts`); every existing
497
+ * caller already runs inside an async context (`commands/agent/contribute-cli.ts`,
498
+ * `commands/improve/preparation.ts`) or is a test that can `await` it.
499
+ */
500
+ export async function akmLint(options = {}) {
501
+ // Fail closed on a mistyped invocation (§24.2 "Lint" release gate): a
502
+ // nonexistent --dir used to walk nothing and report a clean
503
+ // `ok:true, flagged:0`, silently passing scripted --fail-on-flagged gates.
504
+ if (options.dir !== undefined && !fs.statSync(options.dir, { throwIfNoEntry: false })?.isDirectory()) {
505
+ throw new UsageError(`lint: --dir "${options.dir}" is not a directory.`, "INVALID_FLAG_VALUE");
506
+ }
507
+ // Collect secondary stash roots from configured filesystem sources so that
508
+ // cross-stash refs (e.g. referencing assets in dimm-city/agent-stash) are
509
+ // not falsely flagged as missing-ref.
510
+ const cfg = options.config ?? loadConfig();
511
+ // 0.9.0 (spec §10.1): the primary stash is the defaultBundle's path.
512
+ const stashRoot = options.dir ?? primaryBundlePath(cfg) ?? resolveStashDir();
513
+ const sources = resolveSourceEntries(stashRoot, cfg);
514
+ const configuredAdapter = sources.find((source) => path.resolve(source.path) === path.resolve(stashRoot))?.adapterId;
515
+ const adapterId = configuredAdapter ?? detectAdapterId(stashRoot);
516
+ const extraStashRoots = sources.map((s) => s.path).filter((p) => p !== stashRoot && fs.existsSync(p));
517
+ if (adapterId !== "akm")
518
+ return lintViaAdapter(adapterId, stashRoot, extraStashRoots, sources, cfg, options);
519
+ // Same fail-closed rule for --type on the akm sweep: an unknown value used
520
+ // to filter the walk to ZERO directories — a false-clean result on the
521
+ // classic singular/plural typo ("workflow" for "workflows"). Non-akm
522
+ // adapters keep their own type vocabularies (see lintViaAdapter).
523
+ if (options.typeFilter && !STASH_SUBDIRS.includes(options.typeFilter)) {
524
+ throw new UsageError(`lint: unknown --type "${options.typeFilter}". Valid types: ${STASH_SUBDIRS.join(", ")}.`, "INVALID_FLAG_VALUE");
525
+ }
526
+ return lintAkmSweep(stashRoot, extraStashRoots, cfg, sources, options);
527
+ }
@@ -2,21 +2,27 @@
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
4
  /**
5
- * `akm events list` and `akm events tail` (#204).
5
+ * Programmatic event listing behind `akm log` (#204).
6
6
  *
7
- * Programmatic surface — the CLI dispatcher in `src/cli.ts` registers two
8
- * verbs that delegate here. Both return JSON envelopes shaped by
7
+ * Programmatic surface — the CLI dispatcher in `src/cli.ts` registers the
8
+ * `log` leaf command that delegates here. Returns a JSON envelope shaped by
9
9
  * `src/output/shapes.ts` so the output flows through the same shape and
10
10
  * text-renderer pipeline as the rest of the CLI (no silent
11
11
  * `JSON.stringify` fallback).
12
+ *
13
+ * 0.9.0 CLI overhaul (S3): `akmEventsTail` (and the `log tail` command it
14
+ * backed) was dropped — a foreground polling daemon in a one-shot CLI. The
15
+ * lower-level `tailEvents` poller it wrapped (src/core/events.ts) had no
16
+ * other caller and was removed with it.
12
17
  */
13
- import { parseRefInput } from "../core/asset/resolve-ref.js";
18
+ import { makeBundleRef, parseBundleRef } from "../core/asset/asset-ref.js";
19
+ import { loadConfig } from "../core/config/config.js";
14
20
  import { UsageError } from "../core/errors.js";
15
- import { readEvents, tailEvents } from "../core/events.js";
21
+ import { readEvents } from "../core/events.js";
16
22
  import { parseSinceToIso } from "../core/time.js";
17
23
  /**
18
- * Parse `--since` accepting either a byte-offset cursor (`@offset:<int>`) for
19
- * cross-process resumption, or a timestamp / epoch-ms (the existing form).
24
+ * Parse `--since` accepting either an opaque row cursor (`@offset:<int>`) for
25
+ * cross-process resumption, or a timestamp / epoch-ms.
20
26
  * Returns one of `{ sinceOffset }` or `{ since }`.
21
27
  */
22
28
  function parseSinceFlag(since) {
@@ -30,7 +36,7 @@ function parseSinceFlag(since) {
30
36
  const raw = trimmed.slice("@offset:".length);
31
37
  const value = Number.parseInt(raw, 10);
32
38
  if (Number.isNaN(value) || value < 0) {
33
- throw new UsageError(`Invalid --since byte offset: "${since}". Expected @offset:<non-negative integer>.`, "INVALID_FLAG_VALUE");
39
+ throw new UsageError(`Invalid --since offset: "${since}". Expected @offset:<non-negative integer>.`, "INVALID_FLAG_VALUE");
34
40
  }
35
41
  return { sinceOffset: value };
36
42
  }
@@ -43,19 +49,35 @@ function validateRef(ref) {
43
49
  if (!trimmed) {
44
50
  throw new UsageError("--ref cannot be empty.", "INVALID_FLAG_VALUE");
45
51
  }
46
- parseRefInput(trimmed);
52
+ const parsed = parseBundleRef(trimmed);
53
+ // Deliberately shallow qualification: a bare conceptId is assumed to mean
54
+ // the default bundle, without the full priority-walk `resolveRef` performs —
55
+ // event refs are recorded fully qualified, so the default is the only
56
+ // spelling a short --ref can usefully mean here.
57
+ return makeBundleRef(parsed.bundle ?? loadConfig().defaultBundle, parsed.conceptId);
58
+ }
59
+ function validateRunId(run) {
60
+ if (run === undefined)
61
+ return undefined;
62
+ const trimmed = run.trim();
63
+ if (!trimmed) {
64
+ throw new UsageError("--run cannot be empty.", "INVALID_FLAG_VALUE");
65
+ }
47
66
  return trimmed;
48
67
  }
49
68
  export function akmEventsList(options = {}) {
50
69
  const ref = validateRef(options.ref);
70
+ const run = validateRunId(options.run);
51
71
  const parsed = parseSinceFlag(options.since);
52
72
  const result = readEvents({
53
73
  since: parsed.since,
54
74
  sinceOffset: parsed.sinceOffset,
55
75
  type: options.type,
56
76
  ref,
77
+ runId: run,
57
78
  excludeTags: options.excludeTags,
58
79
  includeTags: options.includeTags,
80
+ limit: options.limit,
59
81
  }, options.ctx);
60
82
  return {
61
83
  schemaVersion: 1,
@@ -64,36 +86,9 @@ export function akmEventsList(options = {}) {
64
86
  ...(options.type !== undefined ? { type: options.type } : {}),
65
87
  ...(parsed.since !== undefined ? { since: parsed.since } : {}),
66
88
  ...(parsed.sinceOffset !== undefined ? { sinceOffset: parsed.sinceOffset } : {}),
89
+ ...(options.limit !== undefined ? { limit: options.limit } : {}),
90
+ ...(run !== undefined ? { run } : {}),
67
91
  nextOffset: result.nextOffset,
68
92
  events: result.events,
69
93
  };
70
94
  }
71
- export async function akmEventsTail(options = {}) {
72
- const ref = validateRef(options.ref);
73
- const parsed = parseSinceFlag(options.since);
74
- const tailOptions = {
75
- since: parsed.since,
76
- sinceOffset: parsed.sinceOffset,
77
- type: options.type,
78
- ref,
79
- intervalMs: options.intervalMs,
80
- maxDurationMs: options.maxDurationMs,
81
- maxEvents: options.maxEvents,
82
- signal: options.signal,
83
- onEvent: options.onEvent,
84
- excludeTags: options.excludeTags,
85
- includeTags: options.includeTags,
86
- };
87
- const result = await tailEvents(tailOptions, options.ctx);
88
- return {
89
- schemaVersion: 1,
90
- totalCount: result.events.length,
91
- ...(ref !== undefined ? { ref } : {}),
92
- ...(options.type !== undefined ? { type: options.type } : {}),
93
- ...(parsed.since !== undefined ? { since: parsed.since } : {}),
94
- ...(parsed.sinceOffset !== undefined ? { sinceOffset: parsed.sinceOffset } : {}),
95
- nextOffset: result.nextOffset,
96
- events: result.events,
97
- reason: result.reason,
98
- };
99
- }
@@ -1,36 +1,116 @@
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 { defineGroupCommand, defineJsonCommand } from "../cli/shared.js";
5
- import { UsageError } from "../core/errors.js";
4
+ import { defineGroupCommand, defineJsonCommand, output } from "../cli/shared.js";
5
+ import { runMigrationTool } from "./migration-tool.js";
6
6
  const configArg = {
7
7
  type: "string",
8
8
  description: "Complete operator-prepared current config; optional when the active config is current",
9
9
  };
10
+ /**
11
+ * Split the standalone `akm-migrate` tool's captured stdout into its
12
+ * progress-event lines (if any — `apply` prints one JSON line per completed
13
+ * sub-step, e.g. content migration / proposal-ref repair) and its final
14
+ * result line. `status` and `apply --dry-run` never print progress, so
15
+ * `progress` is empty for them; `apply` may print zero or more.
16
+ *
17
+ * Each `console.log` call in the child produces exactly one `\n`-terminated
18
+ * line, so splitting on `\n` and dropping the trailing empty entry from the
19
+ * final newline recovers exactly the lines it printed, in order.
20
+ */
21
+ function splitToolStdout(stdout) {
22
+ const lines = stdout.split("\n");
23
+ if (lines.length > 0 && lines.at(-1) === "")
24
+ lines.pop();
25
+ if (lines.length === 0)
26
+ return { progress: [] };
27
+ return { progress: lines.slice(0, -1), resultLine: lines.at(-1) };
28
+ }
29
+ /**
30
+ * Runs the standalone `akm-migrate` tool and renders its result through the
31
+ * normal `--format` pipeline (D7) instead of a fixed JSON passthrough.
32
+ *
33
+ * The child's progress-event lines (if any) are not part of the result
34
+ * envelope — they print as-is, in order, regardless of `--format`, the same
35
+ * way `apply` always printed them before this change. Only the final result
36
+ * line — always a well-formed `MigrationPlan` JSON object — is parsed and
37
+ * handed to `output()`, so `text`/`md`/`html`/`yaml` render a real
38
+ * (registered or generic) rendering of it instead of silently staying JSON.
39
+ * `--format json` (the default) is therefore the only format whose BYTES can
40
+ * change here (pretty-printed via `output()` instead of the child's compact
41
+ * `JSON.stringify`) — every value stays identical, which is what the
42
+ * migration-lifecycle integration tests assert via `JSON.parse`.
43
+ */
44
+ async function runMigrateSubcommand(command, args) {
45
+ const result = await runMigrationTool(args);
46
+ if (result.stderr)
47
+ process.stderr.write(result.stderr);
48
+ const { progress, resultLine } = splitToolStdout(result.stdout);
49
+ for (const line of progress)
50
+ console.log(line);
51
+ if (resultLine !== undefined) {
52
+ try {
53
+ output(command, JSON.parse(resultLine));
54
+ }
55
+ catch {
56
+ // The child is expected to always print one well-formed JSON result
57
+ // line; if it somehow didn't, don't lose the line, just don't reshape it.
58
+ console.log(resultLine);
59
+ }
60
+ }
61
+ // R-067: `process.exitCode = …; return;` (not `process.exit()`) so the
62
+ // command's normal cleanup (`disposeDispatchResources()` in `runCommand`,
63
+ // src/cli.ts) still runs before the process exits with the child's status.
64
+ if (result.status !== 0) {
65
+ process.exitCode = result.status;
66
+ return;
67
+ }
68
+ }
10
69
  export const migrateCommand = defineGroupCommand({
70
+ // S11 originally hid this from `--help`/completions as an internal,
71
+ // self-update-only surface. That made it undiscoverable even though the
72
+ // 0.9.0 upgrade instructions tell users to run it first (`akm migrate
73
+ // status`, `akm migrate apply`) — the one command those instructions
74
+ // depend on was invisible. Listed in the SYSTEM section of HELP_SECTIONS
75
+ // (src/cli.ts) and in shell completions now; `akm migrate status`/`apply`
76
+ // always executed regardless of `hidden`.
11
77
  meta: { name: "migrate", description: "Inspect or apply config and durable database migrations" },
12
78
  subCommands: {
13
79
  status: defineJsonCommand({
14
80
  meta: { name: "status", description: "Read-only cross-artifact migration eligibility check" },
15
81
  args: { config: configArg },
16
- async run({ args }) {
17
- const { runMigrationStatus } = await import("../cli/config-migrate.js");
18
- await runMigrationStatus({ preparedConfigPath: args.config });
82
+ run({ args }) {
83
+ return runMigrateSubcommand("migrate-status", ["status", ...(args.config ? ["--config", args.config] : [])]);
19
84
  },
20
85
  }),
21
86
  apply: defineJsonCommand({
22
87
  meta: { name: "apply", description: "Create a verified backup and atomically apply pending migrations" },
23
88
  args: {
24
89
  config: configArg,
25
- dryRun: { type: "boolean", default: false, description: "Run the same eligibility checks without mutation" },
90
+ // R-062: canonical spelling is kebab-case, matching every other
91
+ // multi-word flag in the CLI. `--dryRun` (the pre-rename spelling)
92
+ // is kept as an explicit, documented alias — citty registers BOTH
93
+ // the camelCase and kebab-case spelling of any declared flag name
94
+ // automatically, so this is a rename, not a breaking change: both
95
+ // spellings already worked, and both keep working.
96
+ "dry-run": {
97
+ type: "boolean",
98
+ alias: "dryRun",
99
+ default: false,
100
+ description: "Run the same eligibility checks without mutation. Alias: --dryRun.",
101
+ },
26
102
  },
27
- async run({ args }) {
28
- const { runMigrationApply } = await import("../cli/config-migrate.js");
29
- await runMigrationApply({ preparedConfigPath: args.config, dryRun: args.dryRun });
103
+ run({ args }) {
104
+ return runMigrateSubcommand("migrate-apply", [
105
+ "apply",
106
+ ...(args.config ? ["--config", args.config] : []),
107
+ ...(args.dryRun ? ["--dry-run"] : []),
108
+ ]);
30
109
  },
31
110
  }),
32
111
  },
33
- defaultRun() {
34
- throw new UsageError("Choose `migrate status` or `migrate apply`.", "MISSING_REQUIRED_ARGUMENT");
35
- },
112
+ // No `defaultRun`: bare `akm migrate` is a usage error (exit 2). This group
113
+ // already threw its own hand-rolled UsageError; it now shares the canonical
114
+ // one from `defineGroupCommand` so the message and hint match every other
115
+ // group — owner ruling 12.
36
116
  });
@@ -0,0 +1,46 @@
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 { spawnSync } from "node:child_process";
5
+ import fs from "node:fs";
6
+ import { fileURLToPath } from "node:url";
7
+ import { NotFoundError } from "../core/errors.js";
8
+ function migrationEntryPoint() {
9
+ const candidates = [
10
+ fileURLToPath(new URL(process.versions.bun ? "../scripts/akm-migrate.js" : "../scripts/akm-migrate-node.js", import.meta.url)),
11
+ ...(process.versions.bun ? [fileURLToPath(new URL("../../scripts/akm-migrate.ts", import.meta.url))] : []),
12
+ ];
13
+ return candidates.find((candidate) => fs.existsSync(candidate));
14
+ }
15
+ /**
16
+ * Spawns the standalone `akm-migrate` tool and returns its captured exit
17
+ * status plus stdout/stderr — never writes them itself. `migrate-cli.ts`'s
18
+ * `status`/`apply` commands use this to reshape the child's final JSON result
19
+ * line through the normal `--format` pipeline (D7) while any earlier
20
+ * progress-event lines the child printed still go through verbatim.
21
+ */
22
+ export async function runMigrationTool(args) {
23
+ const entry = migrationEntryPoint();
24
+ if (!entry && process.env.AKM_MIGRATE_ENTRY === "1") {
25
+ // Re-exec loop guard: we ARE the marked child, yet no migrator entry
26
+ // resolved and the standalone wrapper did not intercept the marker — this
27
+ // binary was compiled without `scripts/akm-standalone.ts`.
28
+ throw new NotFoundError("This binary was built without the embedded akm-migrate tool.", "FILE_NOT_FOUND", "Rebuild from scripts/akm-standalone.ts, or run akm-migrate from a source/npm install.");
29
+ }
30
+ // Compiled standalone: no scripts/ tree exists on disk (`import.meta.url`
31
+ // resolves inside the binary's virtual filesystem), so the documented
32
+ // `./akm-<ver> migrate status/apply` path used to dead-end with
33
+ // FILE_NOT_FOUND. Release binaries are compiled from
34
+ // `scripts/akm-standalone.ts`, which embeds the migrator and dispatches to
35
+ // it when `AKM_MIGRATE_ENTRY=1` — so re-exec ourselves with the marker.
36
+ // (src must not import scripts/: the dist build's tsc has `rootDir: src`.)
37
+ const result = spawnSync(process.execPath, entry ? [entry, ...args] : [...args], {
38
+ encoding: "utf8",
39
+ env: entry ? process.env : { ...process.env, AKM_MIGRATE_ENTRY: "1" },
40
+ maxBuffer: 16 * 1024 * 1024,
41
+ });
42
+ if (result.error) {
43
+ throw new NotFoundError(`Cannot start the standalone akm-migrate tool: ${result.error.message}`, "FILE_NOT_FOUND", "Reinstall akm-cli, or use a runtime-free standalone release binary.");
44
+ }
45
+ return { status: result.status ?? 1, stdout: result.stdout ?? "", stderr: result.stderr ?? "" };
46
+ }