akm-cli 0.9.0-rc.0 → 0.9.0-rc.13

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 (598) hide show
  1. package/CHANGELOG.md +1283 -22
  2. package/README.md +62 -37
  3. package/SECURITY.md +46 -31
  4. package/dist/akm +162 -38
  5. package/dist/akm-migrate +44 -0
  6. package/dist/assets/backends/schtasks-template.xml +2 -1
  7. package/dist/assets/hints/cli-hints-full.md +268 -118
  8. package/dist/assets/hints/cli-hints-short.md +87 -24
  9. package/dist/assets/{profiles → improve-strategies}/catchup.json +3 -1
  10. package/dist/assets/{profiles → improve-strategies}/consolidate.json +3 -1
  11. package/dist/assets/{profiles → improve-strategies}/default.json +6 -7
  12. package/dist/assets/improve-strategies/frequent.json +15 -0
  13. package/dist/assets/{profiles → improve-strategies}/graph-refresh.json +4 -2
  14. package/dist/assets/{profiles → improve-strategies}/memory-focus.json +4 -1
  15. package/dist/assets/{profiles → improve-strategies}/proactive-maintenance.json +5 -5
  16. package/dist/assets/{profiles → improve-strategies}/quick.json +4 -2
  17. package/dist/assets/improve-strategies/reflect-distill.json +30 -0
  18. package/dist/assets/{profiles → improve-strategies}/thorough.json +1 -1
  19. package/dist/assets/prompts/consolidate-system.md +5 -5
  20. package/dist/assets/prompts/extract-session.md +2 -6
  21. package/dist/assets/prompts/memory-infer-user.md +2 -3
  22. package/dist/assets/prompts/reflect-llm-framed-contract.md +11 -0
  23. package/dist/assets/prompts/reflect-llm-schema-contract.md +3 -0
  24. package/dist/assets/prompts/reflect-output-repair.md +3 -0
  25. package/dist/assets/prompts/workflow-unit-preamble.md +26 -0
  26. package/dist/assets/stash-skeleton/README.md +38 -10
  27. package/dist/assets/stash-skeleton/facts/conventions/assets/agent.md +8 -0
  28. package/dist/assets/stash-skeleton/facts/conventions/assets/command.md +8 -0
  29. package/dist/assets/stash-skeleton/facts/conventions/assets/fact.md +14 -1
  30. package/dist/assets/stash-skeleton/facts/conventions/assets/knowledge.md +13 -1
  31. package/dist/assets/stash-skeleton/facts/conventions/assets/lesson.md +9 -1
  32. package/dist/assets/stash-skeleton/facts/conventions/assets/memory.md +11 -0
  33. package/dist/assets/stash-skeleton/facts/conventions/assets/script.md +9 -0
  34. package/dist/assets/stash-skeleton/facts/conventions/assets/skill.md +9 -0
  35. package/dist/assets/stash-skeleton/facts/conventions/assets/workflow.md +8 -0
  36. package/dist/assets/stash-skeleton/facts/conventions/backlinks.md +100 -0
  37. package/dist/assets/stash-skeleton/facts/conventions/domains.md +64 -0
  38. package/dist/assets/stash-skeleton/facts/conventions/organization.md +136 -0
  39. package/dist/assets/tasks/core/extract.yml +3 -2
  40. package/dist/assets/tasks/core/improve.yml +2 -1
  41. package/dist/assets/tasks/core/index-refresh.yml +1 -0
  42. package/dist/assets/tasks/core/sync.yml +1 -0
  43. package/dist/assets/tasks/core/version-check.yml +2 -1
  44. package/dist/assets/tasks/improve/akm-graph-refresh-weekly.yml +5 -0
  45. package/dist/assets/tasks/improve/akm-improve-catchup.yml +8 -0
  46. package/dist/assets/tasks/improve/akm-improve-consolidate.yml +5 -0
  47. package/dist/assets/tasks/improve/akm-improve-frequent.yml +5 -0
  48. package/dist/assets/tasks/improve/akm-improve-nightly.yml +5 -0
  49. package/dist/assets/templates/html/health.html +5 -4
  50. package/dist/assets/workflows/workflow-template.md +31 -15
  51. package/dist/cli/invocation.js +279 -0
  52. package/dist/cli/parse-args.js +5 -90
  53. package/dist/cli/retired-commands.js +78 -0
  54. package/dist/cli/shared.js +158 -48
  55. package/dist/cli-node.mjs +2 -1
  56. package/dist/cli.js +747 -293
  57. package/dist/commands/agent/agent-dispatch.js +19 -18
  58. package/dist/commands/agent/agent-support.js +0 -24
  59. package/dist/commands/agent/contribute-cli.js +43 -97
  60. package/dist/commands/completions.js +80 -23
  61. package/dist/commands/config-cli.js +44 -281
  62. package/dist/commands/env/env-binding.js +99 -0
  63. package/dist/commands/env/env-cli.js +84 -224
  64. package/dist/commands/env/env.js +12 -163
  65. package/dist/commands/env/marker-path.js +6 -0
  66. package/dist/commands/env/secret-cli.js +45 -61
  67. package/dist/commands/env/secret.js +32 -62
  68. package/dist/commands/feedback-cli.js +179 -85
  69. package/dist/commands/health/accept-rate.js +58 -0
  70. package/dist/commands/health/advisories.js +7 -8
  71. package/dist/commands/health/checks.js +279 -94
  72. package/dist/commands/health/html-report.js +197 -578
  73. package/dist/commands/health/improve-metrics.js +277 -246
  74. package/dist/commands/health/llm-usage.js +19 -19
  75. package/dist/commands/health/md-report.js +16 -7
  76. package/dist/commands/health/metrics.js +67 -32
  77. package/dist/commands/health/renderers.js +47 -0
  78. package/dist/commands/health/report-view-model.js +508 -0
  79. package/dist/commands/health/stash-exposure.js +1 -1
  80. package/dist/commands/health/surfaces.js +16 -56
  81. package/dist/commands/health/task-runs.js +3 -67
  82. package/dist/{migrate-storage-node.mjs → commands/health/types-checks.js} +1 -5
  83. package/dist/commands/health/types-improve.js +29 -0
  84. package/dist/{output/text/save.js → commands/health/types-metrics.js} +1 -2
  85. package/dist/commands/health/types-result.js +7 -0
  86. package/dist/commands/health/types-runs.js +4 -0
  87. package/dist/commands/health/types-session-log.js +4 -0
  88. package/dist/commands/health/types-windows.js +4 -0
  89. package/dist/commands/health/types.js +26 -21
  90. package/dist/commands/health/windows.js +2 -3
  91. package/dist/commands/health.js +296 -167
  92. package/dist/commands/improve/anti-collapse.js +5 -5
  93. package/dist/commands/improve/autonomy-gate.js +68 -0
  94. package/dist/commands/improve/collapse-detector.js +65 -52
  95. package/dist/commands/improve/consolidate/chunking.js +9 -7
  96. package/dist/commands/improve/consolidate/eligibility.js +1 -23
  97. package/dist/commands/improve/consolidate/merge.js +4 -0
  98. package/dist/commands/improve/consolidate.js +454 -1354
  99. package/dist/commands/improve/content-hash.js +39 -0
  100. package/dist/commands/improve/distill/content-repair.js +4 -10
  101. package/dist/commands/improve/distill/promote-memory.js +89 -64
  102. package/dist/commands/improve/distill/quality-gate.js +118 -42
  103. package/dist/commands/improve/distill-guards.js +1 -1
  104. package/dist/commands/improve/distill-promotion-policy.js +33 -888
  105. package/dist/commands/improve/distill.js +607 -363
  106. package/dist/commands/improve/eligibility.js +165 -79
  107. package/dist/commands/improve/extract-cli.js +35 -126
  108. package/dist/commands/improve/extract-prompt.js +6 -35
  109. package/dist/commands/improve/extract.js +640 -391
  110. package/dist/commands/improve/feedback-valence.js +2 -12
  111. package/dist/commands/improve/improve-cli.js +134 -135
  112. package/dist/commands/improve/improve-result-file.js +30 -50
  113. package/dist/commands/improve/improve-run-types.js +4 -0
  114. package/dist/commands/improve/improve-strategies.js +135 -0
  115. package/dist/commands/improve/improve.js +904 -701
  116. package/dist/commands/improve/locks.js +64 -111
  117. package/dist/commands/improve/loop-stages.js +1110 -923
  118. package/dist/commands/improve/memory/derived-ref.js +124 -0
  119. package/dist/commands/improve/memory/memory-belief.js +79 -7
  120. package/dist/commands/improve/memory/memory-contradiction-detect.js +49 -52
  121. package/dist/commands/improve/memory/memory-improve.js +25 -37
  122. package/dist/commands/improve/outcome-loop.js +25 -88
  123. package/dist/commands/improve/preparation.js +1034 -813
  124. package/dist/commands/improve/proactive-maintenance.js +34 -9
  125. package/dist/commands/improve/proposal-envelope.js +31 -0
  126. package/dist/commands/improve/reflect.js +983 -794
  127. package/dist/commands/improve/run-context.js +119 -0
  128. package/dist/commands/improve/salience.js +24 -127
  129. package/dist/commands/improve/session-asset.js +7 -3
  130. package/dist/commands/improve/shared.js +14 -34
  131. package/dist/commands/improve/source-identity.js +28 -0
  132. package/dist/commands/improve/triage.js +20 -17
  133. package/dist/commands/lint/base-linter.js +340 -313
  134. package/dist/commands/lint/env-key-rules.js +31 -47
  135. package/dist/commands/lint/index.js +185 -30
  136. package/dist/commands/{events.js → log.js} +28 -38
  137. package/dist/commands/migrate-cli.js +54 -0
  138. package/dist/commands/migration-tool.js +55 -0
  139. package/dist/commands/observability-cli.js +70 -208
  140. package/dist/commands/proposal/diff-format.js +50 -0
  141. package/dist/commands/proposal/drain-policies.js +0 -6
  142. package/dist/commands/proposal/drain.js +91 -40
  143. package/dist/commands/proposal/proposal-cli.js +134 -132
  144. package/dist/commands/proposal/proposal-types.js +56 -0
  145. package/dist/commands/proposal/proposal.js +83 -65
  146. package/dist/commands/proposal/propose-cli.js +88 -0
  147. package/dist/commands/proposal/propose.js +105 -88
  148. package/dist/commands/proposal/repository.js +1303 -278
  149. package/dist/commands/proposal/validators/proposal-quality-validators.js +16 -6
  150. package/dist/commands/proposal/validators/proposal-validators.js +61 -12
  151. package/dist/commands/proposal/validators/proposals.js +6 -8
  152. package/dist/commands/read/curate.js +78 -73
  153. package/dist/commands/read/knowledge.js +510 -13
  154. package/dist/commands/read/registry-search.js +2 -2
  155. package/dist/commands/read/remember-cli.js +84 -15
  156. package/dist/commands/read/search-cli.js +203 -96
  157. package/dist/commands/read/search.js +126 -94
  158. package/dist/commands/read/show.js +226 -250
  159. package/dist/commands/registry-cli.js +34 -60
  160. package/dist/commands/remember.js +18 -57
  161. package/dist/commands/sources/add-cli.js +104 -49
  162. package/dist/commands/sources/bundle-cli.js +166 -0
  163. package/dist/commands/sources/bundle-config-ops.js +63 -0
  164. package/dist/commands/sources/info.js +27 -15
  165. package/dist/commands/sources/init.js +30 -40
  166. package/dist/commands/sources/installed-stashes.js +469 -172
  167. package/dist/commands/sources/migration-help.js +7 -4
  168. package/dist/commands/sources/schema-repair.js +10 -9
  169. package/dist/commands/sources/self-update.js +182 -121
  170. package/dist/commands/sources/source-add.js +169 -178
  171. package/dist/commands/sources/source-clone.js +144 -41
  172. package/dist/commands/sources/source-manage.js +94 -59
  173. package/dist/commands/sources/sources-cli.js +64 -205
  174. package/dist/commands/sources/stash-cli.js +91 -54
  175. package/dist/commands/sources/stash-skeleton.js +1 -1
  176. package/dist/commands/tasks/tasks-cli.js +106 -104
  177. package/dist/commands/tasks/tasks.js +445 -262
  178. package/dist/commands/workflow-cli.js +232 -121
  179. package/dist/core/action-contributors.js +1 -1
  180. package/dist/core/activation-policy.js +49 -0
  181. package/dist/core/adapter/adapters/agent-skills-adapter.js +181 -0
  182. package/dist/core/adapter/adapters/akm-adapter.js +528 -0
  183. package/dist/core/adapter/adapters/akm-lint.js +392 -0
  184. package/dist/core/adapter/adapters/akm-metadata.js +387 -0
  185. package/dist/core/adapter/adapters/akm-task-adapter.js +149 -0
  186. package/dist/core/adapter/adapters/akm-workflow-adapter.js +180 -0
  187. package/dist/core/adapter/adapters/claude-adapter.js +61 -0
  188. package/dist/core/adapter/adapters/dotenv-adapter.js +187 -0
  189. package/dist/core/adapter/adapters/generic-files-adapter.js +119 -0
  190. package/dist/core/adapter/adapters/index.js +80 -0
  191. package/dist/core/adapter/adapters/llm-wiki-adapter.js +419 -0
  192. package/dist/core/adapter/adapters/okf-adapter.js +391 -0
  193. package/dist/core/adapter/adapters/opencode-adapter.js +68 -0
  194. package/dist/core/adapter/adapters/shared.js +286 -0
  195. package/dist/core/adapter/adapters/tool-dir-shared.js +217 -0
  196. package/dist/core/adapter/adapters/website-snapshot-adapter.js +155 -0
  197. package/dist/core/adapter/bundle-adapter.js +4 -0
  198. package/dist/core/adapter/detect-adapter.js +17 -0
  199. package/dist/core/adapter/recognize-match.js +44 -0
  200. package/dist/core/adapter/registry.js +56 -0
  201. package/dist/core/adapter/types.js +4 -0
  202. package/dist/core/asset/akm-markdown.js +30 -0
  203. package/dist/core/asset/asset-placement.js +243 -0
  204. package/dist/core/asset/asset-ref.js +110 -79
  205. package/dist/core/asset/asset-serialize.js +20 -0
  206. package/dist/core/asset/frontmatter.js +28 -12
  207. package/dist/core/asset/markdown.js +40 -51
  208. package/dist/core/asset/resolve-ref.js +274 -0
  209. package/dist/core/asset/stash-meta.js +2 -2
  210. package/dist/core/bundle-id.js +51 -0
  211. package/dist/core/common.js +281 -86
  212. package/dist/core/config/config-io.js +42 -128
  213. package/dist/core/config/config-schema.js +233 -834
  214. package/dist/core/config/config-sources.js +162 -39
  215. package/dist/core/config/config-types.js +16 -11
  216. package/dist/core/config/config-version.js +29 -0
  217. package/dist/core/config/config-walker.js +126 -37
  218. package/dist/core/config/config.js +154 -331
  219. package/dist/core/config/deep-merge.js +41 -0
  220. package/dist/core/config/engine-semantics.js +28 -0
  221. package/dist/core/config/experimental.js +21 -0
  222. package/dist/core/config/schema/embedding.js +38 -0
  223. package/dist/core/config/schema/engines.js +116 -0
  224. package/dist/core/config/schema/experimental.js +47 -0
  225. package/dist/core/config/schema/feedback.js +31 -0
  226. package/dist/core/config/schema/improve-processes.js +389 -0
  227. package/dist/core/config/schema/improve.js +94 -0
  228. package/dist/core/config/schema/index-config.js +176 -0
  229. package/dist/core/config/schema/output.js +18 -0
  230. package/dist/core/config/schema/primitives.js +94 -0
  231. package/dist/core/config/schema/search.js +30 -0
  232. package/dist/core/config/schema/setup.js +18 -0
  233. package/dist/core/config/schema/sources-bundles.js +169 -0
  234. package/dist/core/config/schema/workflow.js +29 -0
  235. package/dist/core/env-secret-ref.js +155 -20
  236. package/dist/core/errors.js +17 -15
  237. package/dist/core/events-types.js +4 -0
  238. package/dist/core/events.js +46 -128
  239. package/dist/core/extra-params.js +62 -0
  240. package/dist/core/file-change.js +17 -0
  241. package/dist/core/file-lock.js +202 -57
  242. package/dist/core/fs-txn.js +392 -0
  243. package/dist/core/git-message.js +59 -0
  244. package/dist/core/improve-result.js +167 -0
  245. package/dist/core/json-schema.js +142 -0
  246. package/dist/core/lesson-lint.js +1 -17
  247. package/dist/core/logs-db.js +1 -1
  248. package/dist/core/maintenance-barrier.js +135 -0
  249. package/dist/core/migration-operation.js +44 -0
  250. package/dist/core/mutation-target.js +78 -0
  251. package/dist/core/paths.js +22 -25
  252. package/dist/core/platform.js +10 -0
  253. package/dist/core/recognition-util.js +128 -0
  254. package/dist/core/redaction.js +392 -0
  255. package/dist/core/standards/resolve-standards-context.js +36 -65
  256. package/dist/core/standards/resolve-stash-standards.js +2 -2
  257. package/dist/core/standards/resolve-type-conventions.js +5 -5
  258. package/dist/core/state/migrations.js +242 -11
  259. package/dist/core/state-db.js +98 -10
  260. package/dist/core/structured.js +1 -1
  261. package/dist/core/subprocess.js +303 -0
  262. package/dist/core/text-truncation.js +9 -5
  263. package/dist/core/time.js +20 -0
  264. package/dist/core/type-presentation.js +130 -0
  265. package/dist/core/warn.js +0 -3
  266. package/dist/core/write-source.js +834 -118
  267. package/dist/indexer/bundle-identity-guard.js +92 -0
  268. package/dist/indexer/db/graph-db.js +1 -25
  269. package/dist/indexer/db/llm-cache.js +1 -1
  270. package/dist/indexer/ensure-index.js +30 -9
  271. package/dist/indexer/graph/graph-boost.js +9 -30
  272. package/dist/indexer/graph/graph-extraction.js +41 -27
  273. package/dist/indexer/graph/graph-types.js +4 -0
  274. package/dist/indexer/index-writer-lock.js +93 -49
  275. package/dist/indexer/index-written-assets.js +100 -53
  276. package/dist/indexer/indexer.js +746 -329
  277. package/dist/indexer/init.js +18 -25
  278. package/dist/indexer/installations.js +142 -0
  279. package/dist/indexer/passes/dir-staleness.js +18 -10
  280. package/dist/indexer/passes/memory-inference.js +25 -15
  281. package/dist/indexer/passes/metadata.js +412 -243
  282. package/dist/indexer/scan/doc-to-entry.js +160 -0
  283. package/dist/indexer/scan/drain-dir.js +134 -0
  284. package/dist/indexer/search/db-search.js +292 -108
  285. package/dist/indexer/search/fts-query.js +64 -0
  286. package/dist/indexer/search/ranking-contributors.js +145 -25
  287. package/dist/indexer/search/ranking-types.js +4 -0
  288. package/dist/indexer/search/ranking.js +28 -71
  289. package/dist/indexer/search/search-attribution.js +67 -0
  290. package/dist/indexer/search/search-fields.js +18 -3
  291. package/dist/indexer/search/search-hit-enrichers.js +30 -40
  292. package/dist/indexer/search/search-source.js +157 -111
  293. package/dist/indexer/search/semantic-status.js +4 -1
  294. package/dist/indexer/usage/usage-events.js +10 -30
  295. package/dist/indexer/walk/file-context.js +3 -45
  296. package/dist/indexer/walk/matchers.js +42 -34
  297. package/dist/indexer/walk/path-resolver.js +11 -5
  298. package/dist/indexer/walk/walker.js +42 -14
  299. package/dist/integrations/agent/builder-shared.js +7 -0
  300. package/dist/integrations/agent/builders.js +5 -56
  301. package/dist/integrations/agent/config.js +3 -143
  302. package/dist/integrations/agent/detect.js +17 -2
  303. package/dist/integrations/agent/engine-resolution.js +231 -0
  304. package/dist/integrations/agent/index.js +1 -2
  305. package/dist/integrations/agent/model-aliases.js +16 -2
  306. package/dist/integrations/agent/profiles.js +36 -62
  307. package/dist/integrations/agent/prompts.js +46 -18
  308. package/dist/integrations/agent/runner-dispatch.js +93 -4
  309. package/dist/integrations/agent/runner.js +76 -208
  310. package/dist/integrations/agent/spawn.js +88 -196
  311. package/dist/integrations/harnesses/aider/agent-builder.js +114 -0
  312. package/dist/integrations/harnesses/aider/index.js +48 -0
  313. package/dist/integrations/harnesses/aider/result-extractor.js +53 -0
  314. package/dist/integrations/harnesses/amazonq/agent-builder.js +147 -0
  315. package/dist/integrations/harnesses/amazonq/index.js +45 -0
  316. package/dist/integrations/harnesses/amazonq/result-extractor.js +48 -0
  317. package/dist/integrations/harnesses/claude/agent-builder.js +46 -8
  318. package/dist/integrations/harnesses/claude/config-import.js +1 -3
  319. package/dist/integrations/harnesses/claude/index.js +24 -35
  320. package/dist/integrations/harnesses/claude/result-extractor.js +52 -0
  321. package/dist/integrations/harnesses/claude/session-log.js +27 -75
  322. package/dist/integrations/harnesses/codex/agent-builder.js +138 -0
  323. package/dist/integrations/harnesses/codex/index.js +52 -0
  324. package/dist/integrations/harnesses/codex/result-extractor.js +73 -0
  325. package/dist/integrations/harnesses/copilot/agent-builder.js +122 -0
  326. package/dist/integrations/harnesses/copilot/index.js +48 -0
  327. package/dist/integrations/harnesses/copilot/result-extractor.js +151 -0
  328. package/dist/integrations/harnesses/gemini/agent-builder.js +120 -0
  329. package/dist/integrations/harnesses/gemini/index.js +48 -0
  330. package/dist/integrations/harnesses/gemini/result-extractor.js +121 -0
  331. package/dist/integrations/harnesses/ids.js +24 -0
  332. package/dist/integrations/harnesses/index.js +54 -34
  333. package/dist/integrations/harnesses/opencode/agent-builder.js +23 -5
  334. package/dist/integrations/harnesses/opencode/config-import.js +1 -3
  335. package/dist/integrations/harnesses/opencode/index.js +14 -32
  336. package/dist/integrations/harnesses/opencode/session-log.js +67 -125
  337. package/dist/integrations/harnesses/opencode-sdk/harness.js +51 -0
  338. package/dist/integrations/harnesses/opencode-sdk/sdk-runner.js +681 -108
  339. package/dist/integrations/harnesses/openhands/agent-builder.js +128 -0
  340. package/dist/integrations/harnesses/openhands/index.js +48 -0
  341. package/dist/integrations/harnesses/openhands/result-extractor.js +103 -0
  342. package/dist/integrations/harnesses/pi/agent-builder.js +97 -0
  343. package/dist/integrations/harnesses/pi/index.js +45 -0
  344. package/dist/integrations/harnesses/pi/result-extractor.js +135 -0
  345. package/dist/integrations/harnesses/shared.js +17 -0
  346. package/dist/integrations/harnesses/types.js +43 -32
  347. package/dist/integrations/lockfile.js +211 -24
  348. package/dist/integrations/session-logs/index.js +36 -39
  349. package/dist/integrations/session-logs/provider-base.js +113 -0
  350. package/dist/llm/client.js +182 -110
  351. package/dist/llm/embedders/deterministic.js +2 -2
  352. package/dist/llm/embedders/remote.js +21 -9
  353. package/dist/llm/feature-gate.js +17 -57
  354. package/dist/llm/graph-extract.js +12 -13
  355. package/dist/llm/index-passes.js +8 -42
  356. package/dist/llm/memory-infer.js +144 -1
  357. package/dist/llm/metadata-enhance.js +45 -30
  358. package/dist/llm/structured-call.js +16 -8
  359. package/dist/llm/usage-persist.js +30 -5
  360. package/dist/llm/usage-telemetry.js +59 -6
  361. package/dist/output/cli-hints.js +1 -2
  362. package/dist/output/command-registry.js +27 -0
  363. package/dist/output/context.js +22 -7
  364. package/dist/output/format-exempt.js +80 -0
  365. package/dist/output/generic-render.js +251 -0
  366. package/dist/output/html-render.js +11 -16
  367. package/dist/output/render-registry.js +57 -0
  368. package/dist/output/renderers.js +14 -279
  369. package/dist/output/shapes/curate.js +10 -1
  370. package/dist/output/shapes/events.js +12 -7
  371. package/dist/output/shapes/helpers.js +58 -84
  372. package/dist/output/shapes/passthrough.js +11 -39
  373. package/dist/output/shapes/proposal/producer.js +15 -7
  374. package/dist/output/shapes/registry.js +12 -6
  375. package/dist/output/shapes.js +0 -9
  376. package/dist/output/text/{init.js → bundle-create.js} +3 -1
  377. package/dist/output/text/bundle-show.js +7 -0
  378. package/dist/output/text/command-format.js +562 -0
  379. package/dist/output/text/env.js +1 -3
  380. package/dist/output/text/events.js +8 -7
  381. package/dist/output/text/helpers.js +15 -1164
  382. package/dist/output/text/proposal/producer.js +4 -2
  383. package/dist/output/text/proposal-format.js +202 -0
  384. package/dist/output/text/registry-commands.js +1 -2
  385. package/dist/output/text/registry.js +12 -6
  386. package/dist/output/text/show-directives.js +117 -0
  387. package/dist/output/text/show-format.js +103 -0
  388. package/dist/output/text/sync.js +5 -0
  389. package/dist/output/text/workflow-format.js +332 -0
  390. package/dist/output/text/workflow.js +3 -2
  391. package/dist/output/text.js +10 -19
  392. package/dist/registry/factory.js +4 -6
  393. package/dist/registry/origin-resolve.js +16 -27
  394. package/dist/registry/providers/skills-sh.js +3 -3
  395. package/dist/registry/providers/static-index.js +15 -25
  396. package/dist/registry/resolve.js +43 -94
  397. package/dist/registry/semver.js +43 -0
  398. package/dist/runtime.js +81 -12
  399. package/dist/scripts/akm-migrate.js +35529 -0
  400. package/dist/setup/detect.js +5 -7
  401. package/dist/setup/detected-engines.js +136 -0
  402. package/dist/setup/engine-config.js +100 -0
  403. package/dist/setup/registry-stash-loader.js +3 -3
  404. package/dist/setup/semantic-assets.js +12 -9
  405. package/dist/setup/setup.js +444 -208
  406. package/dist/setup/steps/connection-shared.js +120 -0
  407. package/dist/setup/steps/connection.js +108 -305
  408. package/dist/setup/steps/platforms.js +13 -12
  409. package/dist/setup/steps/semantic.js +15 -3
  410. package/dist/setup/steps/sources.js +21 -15
  411. package/dist/setup/steps/stashdir.js +6 -4
  412. package/dist/setup/steps/tasks.js +236 -119
  413. package/dist/setup/steps.js +3 -2
  414. package/dist/sources/freshness.js +39 -0
  415. package/dist/sources/provider-factory.js +11 -17
  416. package/dist/sources/providers/filesystem.js +2 -3
  417. package/dist/sources/providers/git-install.js +278 -34
  418. package/dist/sources/providers/git-provider.js +54 -56
  419. package/dist/sources/providers/git-stash.js +420 -91
  420. package/dist/sources/providers/git.js +2 -2
  421. package/dist/sources/providers/npm.js +16 -19
  422. package/dist/sources/providers/provider-utils.js +47 -22
  423. package/dist/sources/providers/sync-from-ref.js +3 -9
  424. package/dist/sources/providers/website.js +2 -2
  425. package/dist/sources/resolve.js +11 -10
  426. package/dist/sources/snapshot-fetchers/types.js +4 -0
  427. package/dist/sources/{website-ingest.js → snapshot-fetchers/website-ingest.js} +110 -41
  428. package/dist/storage/database.js +60 -4
  429. package/dist/storage/engines/sqlite-migrations.js +156 -5
  430. package/dist/storage/locations.js +1 -2
  431. package/dist/storage/repositories/canaries-repository.js +1 -1
  432. package/dist/storage/repositories/events-repository.js +51 -11
  433. package/dist/storage/repositories/improve-runs-repository.js +6 -32
  434. package/dist/storage/repositories/index-connection.js +79 -0
  435. package/dist/storage/repositories/index-db.js +4 -3
  436. package/dist/storage/repositories/index-entries-repository.js +863 -0
  437. package/dist/{indexer/db/entry-mapper.js → storage/repositories/index-entry-mapper.js} +19 -2
  438. package/dist/storage/repositories/index-entry-types.js +4 -0
  439. package/dist/storage/repositories/index-fts-repository.js +167 -0
  440. package/dist/storage/repositories/index-llm-cache-repository.js +108 -0
  441. package/dist/storage/repositories/index-meta-repository.js +49 -0
  442. package/dist/{indexer/db/schema.js → storage/repositories/index-schema.js} +226 -100
  443. package/dist/storage/repositories/index-sql.js +12 -0
  444. package/dist/storage/repositories/index-utility-repository.js +356 -0
  445. package/dist/storage/repositories/index-vec-repository.js +250 -0
  446. package/dist/storage/repositories/outcome-repository.js +119 -0
  447. package/dist/storage/repositories/proposals-repository.js +317 -75
  448. package/dist/storage/repositories/registry-cache.js +1 -1
  449. package/dist/storage/repositories/salience-repository.js +172 -0
  450. package/dist/storage/repositories/task-history-repository.js +110 -3
  451. package/dist/storage/repositories/workflow-runs-repository.js +240 -19
  452. package/dist/tasks/backends/cron.js +169 -46
  453. package/dist/tasks/backends/exec-utils.js +76 -3
  454. package/dist/tasks/backends/index.js +6 -9
  455. package/dist/tasks/backends/launchd.js +292 -55
  456. package/dist/tasks/backends/schtasks.js +557 -70
  457. package/dist/tasks/backends/types.js +4 -0
  458. package/dist/tasks/command-executable.js +93 -0
  459. package/dist/tasks/embedded.js +56 -38
  460. package/dist/tasks/parser.js +156 -64
  461. package/dist/tasks/resolve-akm-bin.js +144 -51
  462. package/dist/tasks/runner.js +377 -209
  463. package/dist/tasks/schedule.js +108 -19
  464. package/dist/tasks/scheduler-invocation.js +296 -0
  465. package/dist/tasks/schema.js +1 -1
  466. package/dist/tasks/task-id.js +35 -0
  467. package/dist/tasks/validator.js +30 -16
  468. package/dist/text-import-hook.mjs +1 -1
  469. package/dist/workflows/authoring/authoring.js +104 -43
  470. package/dist/workflows/authoring/scope-key.js +1 -1
  471. package/dist/workflows/cli.js +0 -16
  472. package/dist/workflows/concurrency-policy.js +15 -0
  473. package/dist/workflows/exec/brief.js +450 -0
  474. package/dist/workflows/exec/frozen-judge.js +47 -0
  475. package/dist/workflows/exec/native-executor.js +1038 -0
  476. package/dist/workflows/exec/param-secrets.js +115 -0
  477. package/dist/workflows/exec/report.js +1460 -0
  478. package/dist/workflows/exec/run-workflow.js +602 -0
  479. package/dist/workflows/exec/scheduler.js +71 -0
  480. package/dist/workflows/exec/step-work.js +1190 -0
  481. package/dist/workflows/exec/unit-writer.js +23 -0
  482. package/dist/workflows/exec/workflow-engine-gate.js +67 -0
  483. package/dist/workflows/exec/worktree.js +171 -0
  484. package/dist/workflows/ir/compile.js +246 -0
  485. package/dist/workflows/ir/freeze.js +233 -0
  486. package/dist/workflows/ir/params.js +54 -0
  487. package/dist/workflows/ir/plan-hash.js +68 -0
  488. package/dist/workflows/ir/schema.js +540 -0
  489. package/dist/workflows/parser.js +878 -304
  490. package/dist/workflows/program/expressions.js +181 -0
  491. package/dist/workflows/program/schema.js +51 -0
  492. package/dist/workflows/renderer.js +100 -45
  493. package/dist/workflows/resource-limits.js +22 -0
  494. package/dist/workflows/runtime/agent-identity.js +59 -14
  495. package/dist/workflows/runtime/checkin.js +1 -1
  496. package/dist/workflows/runtime/plan-classifier.js +131 -0
  497. package/dist/workflows/runtime/runs.js +376 -119
  498. package/dist/workflows/runtime/unit-checkin.js +45 -0
  499. package/dist/workflows/runtime/unit-phases.js +20 -0
  500. package/dist/workflows/runtime/workflow-asset-loader.js +241 -40
  501. package/dist/workflows/schema.js +1 -11
  502. package/dist/workflows/validate-summary.js +2 -3
  503. package/dist/workflows/validator.js +52 -30
  504. package/docs/README.md +42 -78
  505. package/docs/migration/README.md +8 -0
  506. package/docs/migration/release-notes/0.6.0.md +1 -1
  507. package/docs/migration/release-notes/0.7.0.md +9 -8
  508. package/docs/migration/release-notes/0.9.0.md +158 -14
  509. package/docs/migration/v0.7-to-v0.8.md +46 -47
  510. package/docs/migration/v0.8-to-v0.9.md +844 -0
  511. package/docs/reference/README.md +12 -0
  512. package/docs/reference/data-and-telemetry.md +333 -0
  513. package/package.json +21 -17
  514. package/schemas/akm-asset-envelope.json +93 -0
  515. package/schemas/akm-config.json +4636 -0
  516. package/schemas/akm-task.json +87 -0
  517. package/schemas/akm-workflow.json +373 -0
  518. package/dist/akm-migrate-storage +0 -38
  519. package/dist/assets/help/help-accept.md +0 -12
  520. package/dist/assets/help/help-improve.md +0 -84
  521. package/dist/assets/help/help-proposals.md +0 -17
  522. package/dist/assets/help/help-propose.md +0 -17
  523. package/dist/assets/help/help-reject.md +0 -11
  524. package/dist/assets/profiles/frequent.json +0 -13
  525. package/dist/assets/profiles/recombine-only.json +0 -21
  526. package/dist/assets/profiles/reflect-distill.json +0 -30
  527. package/dist/assets/profiles/synthesize.json +0 -15
  528. package/dist/assets/prompts/procedural-system.md +0 -44
  529. package/dist/assets/prompts/recombine-system.md +0 -40
  530. package/dist/assets/prompts/staleness-detect-system.md +0 -6
  531. package/dist/assets/tasks/core/backup.yml +0 -4
  532. package/dist/assets/tasks/graph-refresh-weekly.yml +0 -10
  533. package/dist/assets/templates/html/default.html +0 -78
  534. package/dist/assets/templates/html/vendor/echarts.min.js +0 -45
  535. package/dist/assets/wiki/index-template.md +0 -12
  536. package/dist/assets/wiki/ingest-workflow-template.md +0 -83
  537. package/dist/assets/wiki/log-template.md +0 -8
  538. package/dist/assets/wiki/schema-template.md +0 -61
  539. package/dist/cli/config-migrate.js +0 -150
  540. package/dist/cli/config-validate.js +0 -39
  541. package/dist/commands/graph/graph-cli.js +0 -124
  542. package/dist/commands/graph/graph.js +0 -487
  543. package/dist/commands/improve/calibration.js +0 -161
  544. package/dist/commands/improve/dedup.js +0 -482
  545. package/dist/commands/improve/extract-watch.js +0 -140
  546. package/dist/commands/improve/hot-probation.js +0 -45
  547. package/dist/commands/improve/improve-auto-accept.js +0 -276
  548. package/dist/commands/improve/improve-profiles.js +0 -168
  549. package/dist/commands/improve/procedural.js +0 -398
  550. package/dist/commands/improve/recombine.js +0 -818
  551. package/dist/commands/improve/schema-similarity-gate.js +0 -168
  552. package/dist/commands/lint/agent-linter.js +0 -44
  553. package/dist/commands/lint/command-linter.js +0 -44
  554. package/dist/commands/lint/default-linter.js +0 -16
  555. package/dist/commands/lint/fact-linter.js +0 -39
  556. package/dist/commands/lint/knowledge-linter.js +0 -16
  557. package/dist/commands/lint/memory-linter.js +0 -61
  558. package/dist/commands/lint/registry.js +0 -41
  559. package/dist/commands/lint/skill-linter.js +0 -45
  560. package/dist/commands/lint/task-linter.js +0 -50
  561. package/dist/commands/lint/workflow-linter.js +0 -81
  562. package/dist/commands/proposal/legacy-import.js +0 -115
  563. package/dist/commands/sources/history.js +0 -196
  564. package/dist/commands/tasks/default-tasks.js +0 -186
  565. package/dist/commands/wiki-cli.js +0 -292
  566. package/dist/core/asset/asset-registry.js +0 -76
  567. package/dist/core/asset/asset-spec.js +0 -259
  568. package/dist/core/config/config-migration.js +0 -602
  569. package/dist/core/deep-merge.js +0 -38
  570. package/dist/core/eval/rank-metrics.js +0 -113
  571. package/dist/core/ripgrep/install.js +0 -163
  572. package/dist/core/ripgrep/resolve.js +0 -81
  573. package/dist/indexer/db/db.js +0 -1413
  574. package/dist/indexer/manifest.js +0 -170
  575. package/dist/indexer/passes/metadata-contributors.js +0 -31
  576. package/dist/indexer/usage/unmigrated-vaults-guard.js +0 -94
  577. package/dist/integrations/harnesses/opencode-sdk/index.js +0 -49
  578. package/dist/llm/call-ai.js +0 -62
  579. package/dist/llm/memory-infer-impl.js +0 -138
  580. package/dist/output/shapes/distill.js +0 -14
  581. package/dist/output/shapes/history.js +0 -11
  582. package/dist/output/text/distill.js +0 -6
  583. package/dist/output/text/enable-disable.js +0 -8
  584. package/dist/output/text/history.js +0 -6
  585. package/dist/output/text/wiki.js +0 -16
  586. package/dist/registry/build-index.js +0 -386
  587. package/dist/scripts/migrate-storage.js +0 -19108
  588. package/dist/scripts/migrations/import-fs-improve-runs-to-db.js +0 -9411
  589. package/dist/scripts/migrations/v16-to-v17.js +0 -141
  590. package/dist/setup/legacy-config.js +0 -106
  591. package/dist/storage/repositories/consolidation-repository.js +0 -38
  592. package/dist/storage/repositories/recombine-repository.js +0 -213
  593. package/dist/wiki/wiki-templates.js +0 -15
  594. package/dist/wiki/wiki.js +0 -1012
  595. package/dist/workflows/db.js +0 -215
  596. package/docs/data-and-telemetry.md +0 -226
  597. /package/dist/sources/{wiki-fetchers → snapshot-fetchers}/registry.js +0 -0
  598. /package/dist/sources/{wiki-fetchers → snapshot-fetchers}/youtube.js +0 -0
@@ -2,32 +2,32 @@
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
- * Vault security lint rules — flags known-dangerous environment variable names.
5
+ * Environment security lint rules — flags known-dangerous variable names.
6
6
  *
7
- * These env var names, when present as vault keys, indicate the vault can be
7
+ * These env var names, when present in an environment file, indicate the file can be
8
8
  * used to hijack process execution via loader injection, path override, or
9
9
  * shell/runtime startup hooks. The lint pass emits a warning-level finding;
10
- * it does NOT block vault load or `akm vault setKey`.
10
+ * it does NOT block local environment edits.
11
11
  *
12
12
  * Enforcement scope:
13
- * - `akm lint` reports findings as `dangerous-vault-key` (non-blocking warn).
14
- * - `akm add` BLOCKS install unless `--allow-insecure` is set (or, on TTY,
13
+ * - `akm lint` reports findings as `dangerous-env-key` (non-blocking warn).
14
+ * - `akm bundle add` BLOCKS install unless `--allow-insecure` is set (or, on TTY,
15
15
  * the user explicitly confirms at the prompt).
16
- * - `akm vault setKey` does NOT consult this list — by design, the operator
17
- * owns their own vault and may legitimately store any key locally. The
18
- * gate exists only for third-party stash installation.
16
+ * - Local env writes do NOT consult this list — by design, the operator may
17
+ * legitimately store any key locally. The gate exists only for third-party
18
+ * stash installation.
19
19
  *
20
20
  * False-positive tradeoff:
21
21
  * A handful of keys (EDITOR, VISUAL, PAGER) are included because they are
22
22
  * invoked by many interactive tools and are a documented RCE vector when
23
- * sourced from untrusted vaults. They will also flag on benign vaults
23
+ * sourced from untrusted environments. They will also flag on benign files
24
24
  * where the operator legitimately wants to set their editor — accept the
25
25
  * FP and bypass with `--allow-insecure` after review.
26
26
  */
27
27
  import fs from "node:fs";
28
28
  import { listKeys } from "../env/env.js";
29
29
  // ── Dangerous key set ─────────────────────────────────────────────────────────
30
- export const DANGEROUS_VAULT_KEYS = new Set([
30
+ export const DANGEROUS_ENV_KEYS = new Set([
31
31
  // Dynamic linker hijacking (Linux glibc ld.so)
32
32
  "LD_PRELOAD", // forces shared library injection
33
33
  "LD_LIBRARY_PATH", // overrides library search path
@@ -89,7 +89,7 @@ export const DANGEROUS_VAULT_KEYS = new Set([
89
89
  * prefixed with `BASH_FUNC_`. Listing every concrete name is impossible; we
90
90
  * test against this pattern set in addition to the literal `Set`.
91
91
  */
92
- export const DANGEROUS_VAULT_KEY_PATTERNS = [
92
+ export const DANGEROUS_ENV_KEY_PATTERNS = [
93
93
  {
94
94
  // CVE-2014-6271 (Shellshock) — bash imports exported functions named
95
95
  // `BASH_FUNC_<name>%%` and parses their bodies, enabling RCE.
@@ -103,13 +103,13 @@ export const DANGEROUS_VAULT_KEY_PATTERNS = [
103
103
  ];
104
104
  /**
105
105
  * Returns `true` if the given key name is dangerous — either by literal match
106
- * against `DANGEROUS_VAULT_KEYS` or by matching any entry in
107
- * `DANGEROUS_VAULT_KEY_PATTERNS`.
106
+ * against `DANGEROUS_ENV_KEYS` or by matching any entry in
107
+ * `DANGEROUS_ENV_KEY_PATTERNS`.
108
108
  */
109
- export function isDangerousVaultKey(key) {
110
- if (DANGEROUS_VAULT_KEYS.has(key))
109
+ export function isDangerousEnvKey(key) {
110
+ if (DANGEROUS_ENV_KEYS.has(key))
111
111
  return true;
112
- for (const { pattern } of DANGEROUS_VAULT_KEY_PATTERNS) {
112
+ for (const { pattern } of DANGEROUS_ENV_KEY_PATTERNS) {
113
113
  if (pattern.test(key))
114
114
  return true;
115
115
  }
@@ -117,27 +117,26 @@ export function isDangerousVaultKey(key) {
117
117
  }
118
118
  // ── Checker ───────────────────────────────────────────────────────────────────
119
119
  /**
120
- * Inspect a vault `.env` file and return a lint finding for every key whose
121
- * name appears in `DANGEROUS_VAULT_KEYS` or matches a pattern in
122
- * `DANGEROUS_VAULT_KEY_PATTERNS`.
120
+ * Inspect an `.env` file and return a lint finding for every key whose name
121
+ * appears in `DANGEROUS_ENV_KEYS` or matches `DANGEROUS_ENV_KEY_PATTERNS`.
123
122
  *
124
- * @param vaultPath Absolute path to the `.env` file.
123
+ * @param envPath Absolute path to the `.env` file.
125
124
  * @param relPath Stash-relative path used as the `file` field in findings
126
- * (e.g. `"vaults/prod.env"`).
127
- * @param vaultRef Human-readable vault ref (e.g. `"vault:prod"`) shown in
125
+ * (e.g. `"env/prod.env"`).
126
+ * @param envRef Human-readable env ref (e.g. `"env/prod"`) shown in
128
127
  * the finding message.
129
128
  */
130
129
  /** Suppression comment token checked case-insensitively on the preceding non-empty line. */
131
- const SUPPRESSION_COMMENT = "# akm-lint-ok: dangerous-vault-key";
130
+ const SUPPRESSION_COMMENT = "# akm-lint-ok: dangerous-env-key";
132
131
  /**
133
- * Returns the set of keys suppressed by an inline `# akm-lint-ok: dangerous-vault-key`
132
+ * Returns the set of keys suppressed by an inline `# akm-lint-ok: dangerous-env-key`
134
133
  * comment on the line immediately preceding the key assignment in the `.env` file.
135
134
  */
136
- function collectSuppressedKeys(vaultPath) {
135
+ function collectSuppressedKeys(envPath) {
137
136
  const suppressed = new Set();
138
137
  let raw;
139
138
  try {
140
- raw = fs.readFileSync(vaultPath, "utf8");
139
+ raw = fs.readFileSync(envPath, "utf8");
141
140
  }
142
141
  catch {
143
142
  return suppressed;
@@ -156,36 +155,21 @@ function collectSuppressedKeys(vaultPath) {
156
155
  }
157
156
  return suppressed;
158
157
  }
159
- export function checkVaultForDangerousKeys(vaultPath, relPath, vaultRef) {
160
- const { keys } = listKeys(vaultPath);
161
- const suppressed = collectSuppressedKeys(vaultPath);
158
+ export function checkEnvForDangerousKeys(envPath, relPath, envRef) {
159
+ const { keys } = listKeys(envPath);
160
+ const suppressed = collectSuppressedKeys(envPath);
162
161
  const issues = [];
163
162
  for (const key of keys) {
164
- if (!isDangerousVaultKey(key))
163
+ if (!isDangerousEnvKey(key))
165
164
  continue;
166
165
  if (suppressed.has(key))
167
166
  continue;
168
167
  issues.push({
169
168
  file: relPath,
170
- issue: "dangerous-vault-key",
171
- detail: `Env key \`${key}\` can be used to hijack process execution when injected via \`akm env run\`. Ref: ${vaultRef}. Review this file before running \`akm env run\` commands against untrusted stashes. (suppress with: ${SUPPRESSION_COMMENT} on previous line)`,
169
+ issue: "dangerous-env-key",
170
+ detail: `Env key \`${key}\` can be used to hijack process execution when injected via \`akm env run\`. Ref: ${envRef}. Review this file before running \`akm env run\` commands against untrusted stashes. (suppress with: ${SUPPRESSION_COMMENT} on previous line)`,
172
171
  fixed: false,
173
172
  });
174
173
  }
175
174
  return issues;
176
175
  }
177
- // ── Env-neutral aliases ───────────────────────────────────────────────────────
178
- //
179
- // These primitives guard *environment variable injection*, which is shared by
180
- // the `env` asset type, whole-file `secret` injection, and the `akm add`
181
- // supply-chain gate. The original `*Vault*` names are retained above for
182
- // backward compatibility (and stable lint output) through the 0.8.x
183
- // deprecation window; new call sites should prefer the env-neutral names.
184
- /** Env-neutral alias of {@link DANGEROUS_VAULT_KEYS}. */
185
- export const DANGEROUS_ENV_KEYS = DANGEROUS_VAULT_KEYS;
186
- /** Env-neutral alias of {@link DANGEROUS_VAULT_KEY_PATTERNS}. */
187
- export const DANGEROUS_ENV_KEY_PATTERNS = DANGEROUS_VAULT_KEY_PATTERNS;
188
- /** Env-neutral alias of {@link isDangerousVaultKey}. */
189
- export const isDangerousEnvKey = isDangerousVaultKey;
190
- /** Env-neutral alias of {@link checkVaultForDangerousKeys}. */
191
- export const checkEnvForDangerousKeys = checkVaultForDangerousKeys;
@@ -4,12 +4,14 @@
4
4
  import fs from "node:fs";
5
5
  import path from "node:path";
6
6
  import { parse as parseYaml } from "yaml";
7
- import { parseFrontmatter } from "../../core/asset/frontmatter.js";
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 { parseFrontmatter, parseFrontmatterBlock } from "../../core/asset/frontmatter.js";
8
10
  import { resolveStashDir } from "../../core/common.js";
9
- import { loadConfig } from "../../core/config/config.js";
11
+ import { loadConfig, primaryBundlePath } from "../../core/config/config.js";
10
12
  import { resolveSourceEntries } from "../../indexer/search/search-source.js";
13
+ import { runBaseChecks } from "./base-linter.js";
11
14
  import { checkEnvForDangerousKeys } from "./env-key-rules.js";
12
- import { getLinterForType } from "./registry.js";
13
15
  // ── Constants ─────────────────────────────────────────────────────────────────
14
16
  const STASH_SUBDIRS = [
15
17
  "agents",
@@ -38,21 +40,57 @@ function collectYamlFiles(dir) {
38
40
  }
39
41
  return results;
40
42
  }
41
- function collectMarkdownFiles(dir) {
43
+ function collectMarkdownFiles(dir, caseInsensitive = false) {
42
44
  if (!fs.existsSync(dir))
43
45
  return [];
44
46
  const results = [];
45
47
  for (const entry of fs.readdirSync(dir, { withFileTypes: true })) {
48
+ if (entry.name === ".git" || entry.isSymbolicLink())
49
+ continue;
46
50
  const full = path.join(dir, entry.name);
47
51
  if (entry.isDirectory()) {
48
- results.push(...collectMarkdownFiles(full));
52
+ results.push(...collectMarkdownFiles(full, caseInsensitive));
49
53
  }
50
- else if (entry.isFile() && entry.name.endsWith(".md")) {
54
+ else if (entry.isFile() && (caseInsensitive ? entry.name.toLowerCase() : entry.name).endsWith(".md")) {
51
55
  results.push(full);
52
56
  }
53
57
  }
54
58
  return results;
55
59
  }
60
+ function lintOkfBundle(stashRoot) {
61
+ const flagged = [];
62
+ for (const filePath of collectMarkdownFiles(stashRoot, true)) {
63
+ const basename = path.basename(filePath).toLowerCase();
64
+ if (basename === "index.md" || basename === "log.md")
65
+ continue;
66
+ const relPath = path.relative(stashRoot, filePath);
67
+ let validMappingWithType = false;
68
+ try {
69
+ const block = parseFrontmatterBlock(fs.readFileSync(filePath, "utf8"));
70
+ if (block) {
71
+ const data = parseYaml(block.frontmatter);
72
+ validMappingWithType =
73
+ typeof data === "object" &&
74
+ data !== null &&
75
+ !Array.isArray(data) &&
76
+ typeof data.type === "string" &&
77
+ data.type.trim().length > 0;
78
+ }
79
+ }
80
+ catch {
81
+ validMappingWithType = false;
82
+ }
83
+ if (!validMappingWithType) {
84
+ flagged.push({
85
+ file: relPath,
86
+ issue: "missing-type",
87
+ detail: "OKF concepts require parseable mapping frontmatter with a non-empty type.",
88
+ fixed: false,
89
+ });
90
+ }
91
+ }
92
+ return { ok: true, fixed: [], flagged, summary: { fixed: 0, flagged: flagged.length } };
93
+ }
56
94
  function collectEnvFiles(dir) {
57
95
  const results = [];
58
96
  try {
@@ -73,45 +111,162 @@ function collectEnvFiles(dir) {
73
111
  function isFileDeletion(issue) {
74
112
  return issue.fixed === true && (issue.issue === "orphaned-stub" || issue.issue === "placeholder-stub");
75
113
  }
114
+ // ── Per-file lint dispatch (was registry.ts + the 9 per-type linter classes) ──
115
+ //
116
+ // akm 0.9.0 chunk-3 (plan §12): the 9 `BaseLinter` subclasses + `LINTER_MAP`/
117
+ // `getLinterForType` are gone. The format-generic checks are the shared
118
+ // `runBaseChecks` (`./base-linter`); the per-`type` RULES are the `akm`
119
+ // adapter's `validate` surface (`core/adapter/adapters/akm-lint.ts`), imported
120
+ // here so both the read-only adapter and this fix-capable CLI sweep share ONE
121
+ // definition of each finding. The adapter never writes; the delete-fix for the
122
+ // two stub types is applied HERE (a core/CLI concern), reproducing the old
123
+ // MemoryLinter/WorkflowLinter `--fix` behavior byte-for-byte.
124
+ /**
125
+ * Reproduce `SkillLinter.lintDirectory`: a skill subdirectory with no
126
+ * `SKILL.md` is flagged `missing-skill-md` (never auto-fixable). Exported for
127
+ * the lint-parity golden (`tests/integration/goldens-lint-output.test.ts`).
128
+ */
129
+ export function lintSkillDirectory(subdirPath, stashRoot) {
130
+ if (fs.existsSync(path.join(subdirPath, "SKILL.md")))
131
+ return [];
132
+ const relDir = path.relative(stashRoot, subdirPath);
133
+ return [{ file: relDir, issue: "missing-skill-md", detail: `no SKILL.md in ${relDir}/`, fixed: false }];
134
+ }
135
+ /** MemoryLinter's `orphaned-stub` check WITH its `--fix` delete (memory-linter.ts:19-65). */
136
+ function appendMemoryStubIssue(ctx, issues) {
137
+ if (!memoryOrphanStubApplies(ctx.data, ctx.body))
138
+ return;
139
+ const derivedPath = `${ctx.filePath.replace(/\.md$/i, "")}.derived.md`;
140
+ if (fs.existsSync(derivedPath))
141
+ return;
142
+ if (ctx.fix) {
143
+ try {
144
+ fs.unlinkSync(ctx.filePath);
145
+ issues.push({ file: ctx.relPath, issue: "orphaned-stub", detail: "deleted orphaned stub", fixed: true });
146
+ }
147
+ catch (e) {
148
+ issues.push({
149
+ file: ctx.relPath,
150
+ issue: "orphaned-stub",
151
+ detail: `could not delete: ${e instanceof Error ? e.message : String(e)}`,
152
+ fixed: "failed",
153
+ });
154
+ }
155
+ return;
156
+ }
157
+ issues.push({ file: ctx.relPath, issue: "orphaned-stub", detail: ORPHANED_STUB_DETAIL, fixed: false });
158
+ }
159
+ /** WorkflowLinter's `placeholder-stub` (WITH `--fix` delete) + `invalid-workflow-structure` (workflow-linter.ts:22-79). */
160
+ function appendWorkflowIssues(ctx, issues) {
161
+ const placeholder = matchWorkflowPlaceholder(ctx.body);
162
+ if (placeholder) {
163
+ if (ctx.fix) {
164
+ try {
165
+ fs.unlinkSync(ctx.filePath);
166
+ issues.push({
167
+ file: ctx.relPath,
168
+ issue: "placeholder-stub",
169
+ detail: `deleted: found "${placeholder}"`,
170
+ fixed: true,
171
+ });
172
+ }
173
+ catch (e) {
174
+ issues.push({
175
+ file: ctx.relPath,
176
+ issue: "placeholder-stub",
177
+ detail: `could not delete: ${e instanceof Error ? e.message : String(e)}`,
178
+ fixed: "failed",
179
+ });
180
+ }
181
+ return; // WorkflowLinter returns before the structure check once a stub is fixed.
182
+ }
183
+ issues.push({
184
+ file: ctx.relPath,
185
+ issue: "placeholder-stub",
186
+ detail: `placeholder text: "${placeholder}"`,
187
+ fixed: false,
188
+ });
189
+ }
190
+ // NB: the CLI passes the ABSOLUTE filePath to parseWorkflow (matching the old
191
+ // WorkflowLinter), whereas the adapter passes the change relPath.
192
+ issues.push(...workflowStructureDiagnostics(ctx.relPath, ctx.raw, ctx.filePath));
193
+ }
194
+ /**
195
+ * Lint ONE asset file: the shared base checks, then the winning stash subdir's
196
+ * per-`type` extra rules. Replaces `getLinterForType(subdir).lint(ctx)`.
197
+ * `--fix` mutations (frontmatter rewrites inside `runBaseChecks`; stub deletes
198
+ * here) are applied when `ctx.fix` is set.
199
+ */
200
+ export function lintAssetFile(ctx, subdir) {
201
+ const issues = runBaseChecks(ctx);
202
+ switch (subdir) {
203
+ case "agents":
204
+ issues.push(...nameOrTypeDiagnostics(ctx.relPath, ctx.data, ctx.frontmatter, ["agent"]));
205
+ break;
206
+ case "commands":
207
+ issues.push(...nameOrTypeDiagnostics(ctx.relPath, ctx.data, ctx.frontmatter, ["command"]));
208
+ break;
209
+ case "facts":
210
+ issues.push(...factDiagnostics(ctx.relPath, ctx.data));
211
+ break;
212
+ case "tasks":
213
+ issues.push(...taskDiagnostics(ctx.relPath, ctx.data));
214
+ break;
215
+ case "memories":
216
+ appendMemoryStubIssue(ctx, issues);
217
+ break;
218
+ case "workflows":
219
+ appendWorkflowIssues(ctx, issues);
220
+ break;
221
+ // knowledge / lessons / skills: base checks only (skill directory-level
222
+ // `missing-skill-md` runs separately, per-subdir, in the sweep loop).
223
+ }
224
+ return issues;
225
+ }
76
226
  // ── Main ──────────────────────────────────────────────────────────────────────
77
227
  export function akmLint(options = {}) {
78
- const stashRoot = options.dir ?? options.config?.stashDir ?? resolveStashDir();
79
228
  // Collect secondary stash roots from configured filesystem sources so that
80
229
  // cross-stash refs (e.g. referencing assets in dimm-city/agent-stash) are
81
230
  // not falsely flagged as missing-ref.
82
231
  const cfg = options.config ?? loadConfig();
83
- const extraStashRoots = resolveSourceEntries(stashRoot, cfg)
84
- .map((s) => s.path)
85
- .filter((p) => p !== stashRoot && fs.existsSync(p));
86
- const fix = options.fix ?? false;
232
+ // 0.9.0 (spec §10.1): the primary stash is the defaultBundle's path.
233
+ const stashRoot = options.dir ?? primaryBundlePath(cfg) ?? resolveStashDir();
234
+ const sources = resolveSourceEntries(stashRoot, cfg);
235
+ const configuredAdapter = sources.find((source) => path.resolve(source.path) === path.resolve(stashRoot))?.adapterId;
236
+ const adapterId = configuredAdapter ?? detectAdapterId(stashRoot);
237
+ if (adapterId === "okf")
238
+ return lintOkfBundle(stashRoot);
239
+ const extraStashRoots = sources.map((s) => s.path).filter((p) => p !== stashRoot && fs.existsSync(p));
240
+ const fix = adapterId === "akm" && options.fix === true;
87
241
  const fixed = [];
88
242
  const flagged = [];
89
243
  const dirsToScan = options.typeFilter ? STASH_SUBDIRS.filter((d) => d === options.typeFilter) : STASH_SUBDIRS;
90
244
  for (const subdir of dirsToScan) {
91
245
  const dirPath = path.join(stashRoot, subdir);
92
- // Tasks are .yml files; everything else is .md
93
- const files = subdir === "tasks" ? collectYamlFiles(dirPath) : collectMarkdownFiles(dirPath);
94
- const linter = getLinterForType(subdir);
95
- // If the linter supports directory-level checks, run them for each direct
96
- // subdirectory once before the per-file loop.
97
- if (typeof linter.lintDirectory === "function" && fs.existsSync(dirPath)) {
246
+ // Tasks are .yml files; everything else (including workflows, one
247
+ // markdown format now) is .md
248
+ const files = subdir === "tasks" ? collectYamlFiles(dirPath) : collectMarkdownFiles(dirPath, true);
249
+ const assetFiles = subdir === "workflows" ? files.filter((file) => path.basename(file).toLowerCase() !== "readme.md") : files;
250
+ // Directory-level check: skills require a SKILL.md entry point (was
251
+ // SkillLinter.lintDirectory). Run once per direct subdirectory before the
252
+ // per-file loop.
253
+ if (subdir === "skills" && fs.existsSync(dirPath)) {
98
254
  for (const entry of fs.readdirSync(dirPath, { withFileTypes: true })) {
99
- if (entry.isDirectory()) {
100
- const subdirIssues = linter.lintDirectory(path.join(dirPath, entry.name), stashRoot);
101
- for (const issue of subdirIssues) {
102
- // Tristate-safe: only `true` counts as fixed; `false` and "failed"
103
- // are both flagged.
104
- if (issue.fixed === true) {
105
- fixed.push(issue);
106
- }
107
- else {
108
- flagged.push(issue);
109
- }
255
+ if (!entry.isDirectory())
256
+ continue;
257
+ for (const issue of lintSkillDirectory(path.join(dirPath, entry.name), stashRoot)) {
258
+ // Tristate-safe: only `true` counts as fixed; `false` and "failed"
259
+ // are both flagged.
260
+ if (issue.fixed === true) {
261
+ fixed.push(issue);
262
+ }
263
+ else {
264
+ flagged.push(issue);
110
265
  }
111
266
  }
112
267
  }
113
268
  }
114
- for (const filePath of files) {
269
+ for (const filePath of assetFiles) {
115
270
  // Skip registry-cached read-only files — --fix must not mutate them.
116
271
  if (filePath.includes("/.cache/") || filePath.includes("/registry/"))
117
272
  continue;
@@ -142,7 +297,7 @@ export function akmLint(options = {}) {
142
297
  else {
143
298
  ({ data, content: body, frontmatter } = parseFrontmatter(raw));
144
299
  }
145
- const issues = linter.lint({ filePath, relPath, raw, data, body, frontmatter, fix, stashRoot, extraStashRoots });
300
+ const issues = lintAssetFile({ filePath, relPath, raw, data, body, frontmatter, fix, stashRoot, extraStashRoots }, subdir);
146
301
  let fileDeleted = false;
147
302
  for (const issue of issues) {
148
303
  if (isFileDeletion(issue)) {
@@ -2,21 +2,26 @@
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 { parseAssetRef } from "../core/asset/asset-ref.js";
18
+ import { makeBundleRef, parseBundleRef } from "../core/asset/asset-ref.js";
14
19
  import { UsageError } from "../core/errors.js";
15
- import { readEvents, tailEvents } from "../core/events.js";
20
+ import { readEvents } from "../core/events.js";
16
21
  import { parseSinceToIso } from "../core/time.js";
17
22
  /**
18
- * Parse `--since` accepting either a byte-offset cursor (`@offset:<int>`) for
19
- * cross-process resumption, or a timestamp / epoch-ms (the existing form).
23
+ * Parse `--since` accepting either an opaque row cursor (`@offset:<int>`) for
24
+ * cross-process resumption, or a timestamp / epoch-ms.
20
25
  * Returns one of `{ sinceOffset }` or `{ since }`.
21
26
  */
22
27
  function parseSinceFlag(since) {
@@ -30,7 +35,7 @@ function parseSinceFlag(since) {
30
35
  const raw = trimmed.slice("@offset:".length);
31
36
  const value = Number.parseInt(raw, 10);
32
37
  if (Number.isNaN(value) || value < 0) {
33
- throw new UsageError(`Invalid --since byte offset: "${since}". Expected @offset:<non-negative integer>.`, "INVALID_FLAG_VALUE");
38
+ throw new UsageError(`Invalid --since offset: "${since}". Expected @offset:<non-negative integer>.`, "INVALID_FLAG_VALUE");
34
39
  }
35
40
  return { sinceOffset: value };
36
41
  }
@@ -43,19 +48,31 @@ function validateRef(ref) {
43
48
  if (!trimmed) {
44
49
  throw new UsageError("--ref cannot be empty.", "INVALID_FLAG_VALUE");
45
50
  }
46
- parseAssetRef(trimmed);
51
+ const parsed = parseBundleRef(trimmed);
52
+ return makeBundleRef(parsed.bundle, parsed.conceptId);
53
+ }
54
+ function validateRunId(run) {
55
+ if (run === undefined)
56
+ return undefined;
57
+ const trimmed = run.trim();
58
+ if (!trimmed) {
59
+ throw new UsageError("--run cannot be empty.", "INVALID_FLAG_VALUE");
60
+ }
47
61
  return trimmed;
48
62
  }
49
63
  export function akmEventsList(options = {}) {
50
64
  const ref = validateRef(options.ref);
65
+ const run = validateRunId(options.run);
51
66
  const parsed = parseSinceFlag(options.since);
52
67
  const result = readEvents({
53
68
  since: parsed.since,
54
69
  sinceOffset: parsed.sinceOffset,
55
70
  type: options.type,
56
71
  ref,
72
+ runId: run,
57
73
  excludeTags: options.excludeTags,
58
74
  includeTags: options.includeTags,
75
+ limit: options.limit,
59
76
  }, options.ctx);
60
77
  return {
61
78
  schemaVersion: 1,
@@ -64,36 +81,9 @@ export function akmEventsList(options = {}) {
64
81
  ...(options.type !== undefined ? { type: options.type } : {}),
65
82
  ...(parsed.since !== undefined ? { since: parsed.since } : {}),
66
83
  ...(parsed.sinceOffset !== undefined ? { sinceOffset: parsed.sinceOffset } : {}),
84
+ ...(options.limit !== undefined ? { limit: options.limit } : {}),
85
+ ...(run !== undefined ? { run } : {}),
67
86
  nextOffset: result.nextOffset,
68
87
  events: result.events,
69
88
  };
70
89
  }
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
- }
@@ -0,0 +1,54 @@
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 { defineGroupCommand, defineJsonCommand } from "../cli/shared.js";
5
+ import { runMigrationTool } from "./migration-tool.js";
6
+ const configArg = {
7
+ type: "string",
8
+ description: "Complete operator-prepared current config; optional when the active config is current",
9
+ };
10
+ export const migrateCommand = defineGroupCommand({
11
+ // Hidden from `--help` and the completions walker (S11): this is the
12
+ // self-update contract's internal command, not a surface end users
13
+ // discover by browsing help. `akm migrate status`/`apply` still execute
14
+ // normally — `hidden` only affects listing.
15
+ meta: { name: "migrate", hidden: true, description: "Inspect or apply config and durable database migrations" },
16
+ subCommands: {
17
+ status: defineJsonCommand({
18
+ meta: { name: "status", description: "Read-only cross-artifact migration eligibility check" },
19
+ args: { config: configArg },
20
+ run({ args }) {
21
+ return runMigrationTool(["status", ...(args.config ? ["--config", args.config] : [])]);
22
+ },
23
+ }),
24
+ apply: defineJsonCommand({
25
+ meta: { name: "apply", description: "Create a verified backup and atomically apply pending migrations" },
26
+ args: {
27
+ config: configArg,
28
+ // R-062: canonical spelling is kebab-case, matching every other
29
+ // multi-word flag in the CLI. `--dryRun` (the pre-rename spelling)
30
+ // is kept as an explicit, documented alias — citty registers BOTH
31
+ // the camelCase and kebab-case spelling of any declared flag name
32
+ // automatically, so this is a rename, not a breaking change: both
33
+ // spellings already worked, and both keep working.
34
+ "dry-run": {
35
+ type: "boolean",
36
+ alias: "dryRun",
37
+ default: false,
38
+ description: "Run the same eligibility checks without mutation. Alias: --dryRun.",
39
+ },
40
+ },
41
+ run({ args }) {
42
+ return runMigrationTool([
43
+ "apply",
44
+ ...(args.config ? ["--config", args.config] : []),
45
+ ...(args.dryRun ? ["--dry-run"] : []),
46
+ ]);
47
+ },
48
+ }),
49
+ },
50
+ // No `defaultRun`: bare `akm migrate` is a usage error (exit 2). This group
51
+ // already threw its own hand-rolled UsageError; it now shares the canonical
52
+ // one from `defineGroupCommand` so the message and hint match every other
53
+ // group — owner ruling 12.
54
+ });
@@ -0,0 +1,55 @@
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("../../scripts/akm-migrate.ts", import.meta.url)),
11
+ fileURLToPath(new URL("../scripts/akm-migrate.js", import.meta.url)),
12
+ ];
13
+ return candidates.find((candidate) => fs.existsSync(candidate));
14
+ }
15
+ export async function runMigrationTool(args) {
16
+ const entry = migrationEntryPoint();
17
+ if (!entry && process.env.AKM_MIGRATE_ENTRY === "1") {
18
+ // Re-exec loop guard: we ARE the marked child, yet no migrator entry
19
+ // resolved and the standalone wrapper did not intercept the marker — this
20
+ // binary was compiled without `scripts/akm-standalone.ts`.
21
+ 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.");
22
+ }
23
+ // Compiled standalone: no scripts/ tree exists on disk (`import.meta.url`
24
+ // resolves inside the binary's virtual filesystem), so the documented
25
+ // `./akm-<ver> migrate status/apply` path used to dead-end with
26
+ // FILE_NOT_FOUND. Release binaries are compiled from
27
+ // `scripts/akm-standalone.ts`, which embeds the migrator and dispatches to
28
+ // it when `AKM_MIGRATE_ENTRY=1` — so re-exec ourselves with the marker.
29
+ // (src must not import scripts/: the dist build's tsc has `rootDir: src`.)
30
+ const runtime = process.versions.bun ? process.execPath : "bun";
31
+ const result = spawnSync(runtime, entry ? [entry, ...args] : [...args], {
32
+ encoding: "utf8",
33
+ env: entry ? process.env : { ...process.env, AKM_MIGRATE_ENTRY: "1" },
34
+ maxBuffer: 16 * 1024 * 1024,
35
+ });
36
+ if (result.error) {
37
+ throw new NotFoundError(`Cannot start the standalone akm-migrate tool: ${result.error.message}`, "FILE_NOT_FOUND", "Install Bun, then run akm-migrate directly.");
38
+ }
39
+ if (result.stdout)
40
+ process.stdout.write(result.stdout);
41
+ if (result.stderr)
42
+ process.stderr.write(result.stderr);
43
+ // R-067: was `process.exit(result.status ?? 1)` on the failure branch,
44
+ // which terminates the process synchronously and skips the `finally {
45
+ // await disposeDispatchResources(); }` cleanup in src/cli.ts's
46
+ // `runCommand`. The success path (status === 0) was already fine — it
47
+ // just returns and the process exits 0 naturally. Setting
48
+ // `process.exitCode` and returning still propagates the child's exact
49
+ // non-zero status once the event loop drains, but lets cleanup run first —
50
+ // same pattern `emitJsonError` (src/cli/shared.ts) already established.
51
+ if (result.status !== 0) {
52
+ process.exitCode = result.status ?? 1;
53
+ return;
54
+ }
55
+ }