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
@@ -23,12 +23,18 @@ import { appendEvent } from "../core/events.js";
23
23
  import { clearLlmUsageSink, hasLlmUsageSink, setLlmUsageSink } from "./usage-telemetry.js";
24
24
  /** Event type for persisted per-call LLM usage telemetry. */
25
25
  export const LLM_USAGE_EVENT = "llm_usage";
26
+ /** Event type for the owning sink's terminal-record count marker. */
27
+ export const LLM_USAGE_SUMMARY_EVENT = "llm_usage_summary";
26
28
  /**
27
29
  * Project a usage record into event metadata, dropping `undefined` token
28
30
  * fields so an absent-usage call records only `{stage, model, durationMs}`.
29
31
  */
30
32
  function toEventMetadata(record) {
31
- const metadata = { durationMs: record.durationMs };
33
+ const metadata = {
34
+ durationMs: record.durationMs,
35
+ outcome: record.outcome,
36
+ modelSource: record.modelSource,
37
+ };
32
38
  if (record.stage !== undefined)
33
39
  metadata.stage = record.stage;
34
40
  if (record.engine !== undefined)
@@ -47,6 +53,8 @@ function toEventMetadata(record) {
47
53
  metadata.totalTokens = record.totalTokens;
48
54
  if (record.reasoningTokens !== undefined)
49
55
  metadata.reasoningTokens = record.reasoningTokens;
56
+ if (record.errorCode !== undefined)
57
+ metadata.errorCode = record.errorCode;
50
58
  return metadata;
51
59
  }
52
60
  /**
@@ -56,20 +64,33 @@ function toEventMetadata(record) {
56
64
  * test-isolation harness sees a clean sink between tests).
57
65
  *
58
66
  * `ctx` should carry the same long-lived `state.db` handle the caller already
59
- * opened for its other events; when omitted, `appendEvent` falls back to its
60
- * default open-insert-close path.
67
+ * opened for its other events. A getter is resolved for every append so a
68
+ * caller can replace its context binding without replacing this owning sink.
69
+ * When omitted, `appendEvent` falls back to its default open-insert-close path.
61
70
  */
62
71
  export function installLlmUsagePersistence(ctx) {
72
+ let expectedTerminalRecords = 0;
73
+ let disposed = false;
63
74
  setLlmUsageSink((record) => {
64
- appendEvent({ eventType: LLM_USAGE_EVENT, metadata: toEventMetadata(record) }, ctx);
75
+ expectedTerminalRecords += 1;
76
+ appendEvent({ eventType: LLM_USAGE_EVENT, metadata: toEventMetadata(record) }, typeof ctx === "function" ? ctx() : ctx);
65
77
  });
66
78
  return () => {
79
+ if (disposed)
80
+ return;
81
+ disposed = true;
67
82
  clearLlmUsageSink();
83
+ try {
84
+ appendEvent({ eventType: LLM_USAGE_SUMMARY_EVENT, metadata: { expectedTerminalRecords } }, typeof ctx === "function" ? ctx() : ctx);
85
+ }
86
+ catch {
87
+ // Persistence remains best-effort even during run teardown.
88
+ }
68
89
  };
69
90
  }
70
91
  /**
71
92
  * Like {@link installLlmUsagePersistence}, but a no-op when a sink is already
72
- * installed — used by standalone entry points (`akm consolidate`, `akm drain`)
93
+ * installed — used by standalone entry points (`akm proposal drain`)
73
94
  * that may also run as a sub-step of `akm improve`. When invoked inside an
74
95
  * enclosing run the existing per-run sink keeps ownership; the returned
75
96
  * disposer then does nothing, so the enclosing run's `finally` still clears it.
@@ -5,7 +5,8 @@
5
5
  * Per-call LLM usage telemetry (#576).
6
6
  *
7
7
  * `chatCompletion` captures usage + model + finish_reason + wall-time for
8
- * EVERY OpenAI-compatible call and emits one {@link LlmUsageRecord} through a
8
+ * EVERY OpenAI-compatible HTTP attempt and emits one terminal
9
+ * {@link LlmUsageRecord} through a
9
10
  * module-level sink. The sink indirection keeps `client.ts` free of any
10
11
  * dependency on the events/db layer: the application wires the sink to
11
12
  * persistence at startup / per improve run, and tests can inspect records in
@@ -87,6 +88,20 @@ function asFiniteNonNegative(value) {
87
88
  function asNonEmptyString(value) {
88
89
  return typeof value === "string" && value.length > 0 ? value : undefined;
89
90
  }
91
+ function asLlmUsageErrorCode(value) {
92
+ switch (value) {
93
+ case "rate_limited":
94
+ case "provider_error":
95
+ case "provider_html_error":
96
+ case "network_error":
97
+ case "parse_error":
98
+ case "timeout":
99
+ case "unknown_error":
100
+ return value;
101
+ default:
102
+ return undefined;
103
+ }
104
+ }
90
105
  /** Decode durable event metadata with the same validation used by health aggregation. */
91
106
  export function decodeLlmUsageRecord(value) {
92
107
  if (!value || typeof value !== "object")
@@ -95,7 +110,12 @@ export function decodeLlmUsageRecord(value) {
95
110
  const durationMs = asFiniteNonNegative(raw.durationMs);
96
111
  if (durationMs === undefined)
97
112
  return undefined;
98
- const record = { durationMs };
113
+ // Historical rows predate terminal provenance. Keep health aggregation
114
+ // backward-compatible while conservatively treating their model as configured,
115
+ // never response-observed.
116
+ const outcome = raw.outcome === "success" || raw.outcome === "error" ? raw.outcome : "success";
117
+ const modelSource = raw.modelSource === "response" || raw.modelSource === "configured" ? raw.modelSource : "configured";
118
+ const record = { durationMs, outcome, modelSource };
99
119
  for (const key of ["stage", "engine", "process", "model", "finishReason"]) {
100
120
  const decoded = asNonEmptyString(raw[key]);
101
121
  if (decoded !== undefined)
@@ -106,6 +126,9 @@ export function decodeLlmUsageRecord(value) {
106
126
  if (decoded !== undefined)
107
127
  record[key] = decoded;
108
128
  }
129
+ const errorCode = asLlmUsageErrorCode(raw.errorCode);
130
+ if (errorCode !== undefined)
131
+ record.errorCode = errorCode;
109
132
  return record;
110
133
  }
111
134
  /**
@@ -2,8 +2,7 @@
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
- * Embedded "agent CLI hints" rendered by `akm hints` when no other source
6
- * is available.
5
+ * Embedded agent CLI guide rendered by `akm hints` and `akm help agents`.
7
6
  *
8
7
  * Extracted from `src/cli.ts` so it does not bloat the CLI module and so
9
8
  * docs/CI tooling can re-use the same constants. Two flavors:
@@ -9,7 +9,9 @@
9
9
  * calls read from this in-memory singleton instead of re-scanning argv and
10
10
  * re-loading config on every call.
11
11
  *
12
- * Initialized from `cli.ts` before `runMain`.
12
+ * Initialized from `cli.ts` before `runCli` dispatches the command (`cli.ts`
13
+ * drives citty's `runCommand` directly rather than `runMain` — see the
14
+ * top-level driver comment there).
13
15
  */
14
16
  import { UsageError } from "../core/errors.js";
15
17
  const OUTPUT_FORMATS = ["json", "yaml", "text", "jsonl", "md", "html"];
@@ -39,6 +41,8 @@ function parseShapeMode(value) {
39
41
  export function parseFlagValue(argv, flag) {
40
42
  for (let i = 0; i < argv.length; i++) {
41
43
  const arg = argv[i];
44
+ if (arg === "--")
45
+ break;
42
46
  if (arg === flag)
43
47
  return argv[i + 1];
44
48
  if (arg.startsWith(`${flag}=`))
@@ -47,15 +51,26 @@ export function parseFlagValue(argv, flag) {
47
51
  return undefined;
48
52
  }
49
53
  export function hasBooleanFlag(argv, flag) {
50
- return argv.some((arg) => arg === flag || arg === `${flag}=true`);
54
+ for (const arg of argv) {
55
+ if (arg === "--")
56
+ return false;
57
+ if (arg === flag || arg === `${flag}=true`)
58
+ return true;
59
+ }
60
+ return false;
51
61
  }
52
62
  /**
53
63
  * Read a hyphenated arg out of citty's parsed `args` object.
54
64
  *
55
- * citty does not auto-camelise hyphenated arg keys (see `--max-pages`,
56
- * `--with-sources` for the existing convention), so command handlers end up
57
- * casting `args` to a string-indexed record at every read site. This helper
58
- * encapsulates the cast.
65
+ * Verified live: citty DOES auto-camelise a DECLARED hyphenated arg for
66
+ * `"max-pages": { type: "string" }` in a command's `args`, citty's parsed
67
+ * object answers both `args["max-pages"]` and `args.maxPages` (a `Proxy`
68
+ * falls back through `camelCase`/`kebabCase`, and `--no-<flag>` negation is
69
+ * handled the same way for booleans). What citty's own TS types do NOT give
70
+ * callers is a statically-typed handle on that mapping, and command handlers
71
+ * here type `args` as `unknown` at the boundary — so every read site still
72
+ * needs a cast. This helper encapsulates that cast; it exists for typing
73
+ * convenience, not to work around a citty parsing limitation.
59
74
  */
60
75
  export function getHyphenatedArg(args, key) {
61
76
  if (typeof args !== "object" || args === null)
@@ -81,7 +96,7 @@ export function resolveOutputMode(argv, defaults = {}) {
81
96
  const detail = parseDetailLevel(rawDetail) ?? defaults?.detail ?? "brief";
82
97
  const shape = parseShapeMode(rawShape) ?? "human";
83
98
  const outputPath = parseFlagValue(argv, "--output");
84
- return { format, detail, shape, forAgent: shape === "agent", ...(outputPath ? { outputPath } : {}) };
99
+ return { format, detail, shape, ...(outputPath ? { outputPath } : {}) };
85
100
  }
86
101
  let _mode;
87
102
  /**
@@ -0,0 +1,80 @@
1
+ // This Source Code Form is subject to the terms of the Mozilla Public
2
+ // License, v. 2.0. If a copy of the MPL was not distributed with this
3
+ // file, You can obtain one at https://mozilla.org/MPL/2.0/.
4
+ /**
5
+ * Commands whose output is not an envelope, and therefore not formattable (D7).
6
+ *
7
+ * D7 makes all six `--format` values work on every command that renders through
8
+ * `output()`. A small set does not render an envelope at all: they emit a shell
9
+ * script, drive an interactive prompt, hand stdout to a child process, or print
10
+ * a document that IS the payload. Passing `--format` to one of those cannot do
11
+ * anything useful.
12
+ *
13
+ * The point of declaring the set here is that the exemption stops being
14
+ * implicit. Before D7 you discovered it by getting JSON when you asked for
15
+ * Markdown, or an exit 2 when you asked for HTML. Now the list is one grep away,
16
+ * it is documented in `STABILITY.md`, and asking for a format on an exempt
17
+ * command warns instead of silently doing something else.
18
+ *
19
+ * Names are canonical command paths resolved from the Citty tree.
20
+ */
21
+ /**
22
+ * Top-level command tokens whose entire surface is format-exempt.
23
+ */
24
+ const EXEMPT_COMMANDS = new Set([
25
+ // Emits shell completion script source for eval.
26
+ "completions",
27
+ // `migrate status`/`apply` used to be exempt here too: `runMigrationTool`
28
+ // (src/commands/migration-tool.ts) spawns the standalone
29
+ // `scripts/akm-migrate.ts` tool, which always emitted its own fixed JSON
30
+ // shape and never consulted `--format`. `src/commands/migrate-cli.ts` now
31
+ // parses that child's final result line and renders it through the normal
32
+ // `output()` pipeline (registered shape: `src/output/shapes/migrate.ts`;
33
+ // text renderer: `src/output/text/migrate.ts`), so both subcommands honour
34
+ // `--format` like any other command and are no longer listed here. Any
35
+ // progress-event lines the child prints during a real `apply` still print
36
+ // verbatim ahead of the formatted result — those are operational logging,
37
+ // not part of the result envelope, the same way a progress spinner would be.
38
+ // Document payload group: bare `help` prints the sectioned overview,
39
+ // `help migrate <version>` prints release notes, and `help agents` prints
40
+ // the embedded CLI-reference guide (`src/output/cli-hints.ts`) — none of
41
+ // the three render a result envelope.
42
+ "help",
43
+ // Embedded agent guide document.
44
+ "hints",
45
+ ]);
46
+ /**
47
+ * `<command> <subcommand>` pairs that are exempt while the rest of the group
48
+ * formats normally.
49
+ */
50
+ const EXEMPT_SUBCOMMANDS = new Set([
51
+ // Child-process passthrough (the env/secret groups otherwise format fine).
52
+ "env run",
53
+ "secret run",
54
+ // B3/B4 (W1-F): a bare absolute filesystem path IS the payload — the
55
+ // documented shell-substitution primitive (`$(akm env path <ref>)`,
56
+ // Docker `_FILE` / `--env-file`) — not a field worth wrapping in an
57
+ // envelope. Wrapping it broke every existing substitution silently: the
58
+ // CLI's default format is `json`, so an un-flagged `akm env path <ref>`
59
+ // (exactly how the substitution is always written) started emitting
60
+ // `{"path":"..."}` instead of the raw path. Unlike `config path`, this
61
+ // command has no `--all`-style multi-field variant, so the whole surface
62
+ // can be exempt without wrongly warning on a real envelope case.
63
+ "env path",
64
+ ]);
65
+ /**
66
+ * True when `--format` cannot meaningfully apply to this invocation.
67
+ *
68
+ * `commandPath` contains canonical Citty command names from the top-level
69
+ * command through the deepest resolved subcommand.
70
+ */
71
+ export function isFormatExemptCommand(commandPath) {
72
+ const command = commandPath[0];
73
+ if (command === undefined)
74
+ return false;
75
+ return EXEMPT_COMMANDS.has(command) || EXEMPT_SUBCOMMANDS.has(commandPath.join(" "));
76
+ }
77
+ /** The declared exempt set, for docs generation and tests. */
78
+ export function formatExemptSurfaces() {
79
+ return { commands: [...EXEMPT_COMMANDS], subcommands: [...EXEMPT_SUBCOMMANDS] };
80
+ }
@@ -0,0 +1,259 @@
1
+ // This Source Code Form is subject to the terms of the Mozilla Public
2
+ // License, v. 2.0. If a copy of the MPL was not distributed with this
3
+ // file, You can obtain one at https://mozilla.org/MPL/2.0/.
4
+ /**
5
+ * True when `value` is a non-empty array of plain objects — the shape worth
6
+ * rendering as a table rather than as a nested list. Uniformity is judged on
7
+ * the union of keys, not on strict equality, so one row carrying an extra
8
+ * optional field still tabulates (its column is simply blank elsewhere).
9
+ */
10
+ function isTabular(value) {
11
+ if (!Array.isArray(value) || value.length === 0)
12
+ return false;
13
+ return value.every((row) => row !== null && typeof row === "object" && !Array.isArray(row));
14
+ }
15
+ /** Column order for a table: first appearance across all rows, order-stable. */
16
+ function tableColumns(rows) {
17
+ const columns = [];
18
+ for (const row of rows) {
19
+ for (const key of Object.keys(row)) {
20
+ if (!columns.includes(key))
21
+ columns.push(key);
22
+ }
23
+ }
24
+ return columns;
25
+ }
26
+ /**
27
+ * Render a leaf value for display. Objects and arrays that reach a cell are
28
+ * JSON-encoded rather than expanded — a table cell is not a place to nest.
29
+ */
30
+ function displayScalar(value) {
31
+ if (value === null || value === undefined)
32
+ return "";
33
+ if (typeof value === "string")
34
+ return value;
35
+ if (typeof value === "number" || typeof value === "boolean")
36
+ return String(value);
37
+ return JSON.stringify(value);
38
+ }
39
+ /**
40
+ * Root-level envelope metadata the shape registry stamps on top of a
41
+ * command's actual result: `shape` (the discriminator) and `schemaVersion`
42
+ * (the envelope's own version), both added by the passthrough stamp in
43
+ * `src/output/shapes/passthrough.ts` and equivalent per-command shapers
44
+ * elsewhere. Transport bookkeeping, not a result anyone asked for — a `##
45
+ * shape` / `## schemaVersion` section (`renderGenericMarkdown`), an
46
+ * `<h2>shape</h2>` block (`renderGenericHtml`), or a `shape=…` line
47
+ * (`renderGenericText`) is noise in every command's output, so every generic
48
+ * renderer drops both. ONLY at the root, though: some shapes reuse
49
+ * `schemaVersion` as genuine per-entry content one level down (each event in
50
+ * `log list`'s `events[]` carries its own `schemaVersion`, see
51
+ * `shapeEventEntry` in `src/output/shapes/helpers.ts`), and that must
52
+ * survive untouched — every renderer below applies this filter only to the
53
+ * top-level `Object.entries` loop over the envelope, never recursively.
54
+ */
55
+ const ENVELOPE_META_KEYS = new Set(["shape", "schemaVersion"]);
56
+ // ── Markdown ─────────────────────────────────────────────────────────────────
57
+ /** Escape the one character that can break a Markdown table row. */
58
+ function escapeCell(text) {
59
+ return text.replace(/\|/g, "\\|").replace(/\n/g, " ");
60
+ }
61
+ function markdownTable(rows) {
62
+ const columns = tableColumns(rows);
63
+ if (columns.length === 0)
64
+ return [];
65
+ const lines = [
66
+ `| ${columns.join(" | ")} |`,
67
+ `| ${columns.map(() => "---").join(" | ")} |`,
68
+ ...rows.map((row) => `| ${columns.map((c) => escapeCell(displayScalar(row[c]))).join(" | ")} |`),
69
+ ];
70
+ return lines;
71
+ }
72
+ function markdownValue(value, depth) {
73
+ if (isTabular(value))
74
+ return markdownTable(value);
75
+ if (Array.isArray(value)) {
76
+ if (value.length === 0)
77
+ return ["_(none)_"];
78
+ return value.map((item) => `- ${escapeCell(displayScalar(item))}`);
79
+ }
80
+ if (value !== null && typeof value === "object") {
81
+ const lines = [];
82
+ for (const [key, nested] of Object.entries(value)) {
83
+ // Headings stop at h6; deeper nesting continues as bolded labels.
84
+ const heading = "#".repeat(Math.min(depth, 6));
85
+ lines.push(depth <= 6 ? `${heading} ${key}` : `**${key}**`, "", ...markdownValue(nested, depth + 1), "");
86
+ }
87
+ return lines;
88
+ }
89
+ return [displayScalar(value) === "" ? "_(empty)_" : displayScalar(value)];
90
+ }
91
+ /**
92
+ * Render a shaped envelope as Markdown. `command` titles the document so a
93
+ * rendered file identifies itself once detached from the invocation.
94
+ */
95
+ export function renderGenericMarkdown(command, value) {
96
+ const body = value !== null && typeof value === "object" && !Array.isArray(value)
97
+ ? Object.entries(value)
98
+ .filter(([key]) => !ENVELOPE_META_KEYS.has(key)) // transport metadata, not a result
99
+ .flatMap(([key, nested]) => [`## ${key}`, "", ...markdownValue(nested, 3), ""])
100
+ : markdownValue(value, 2);
101
+ const lines = [`# ${command}`, "", ...body];
102
+ const rendered = lines
103
+ .join("\n")
104
+ .replace(/\n{3,}/g, "\n\n")
105
+ .trimEnd();
106
+ return `${rendered}\n`;
107
+ }
108
+ // ── HTML ─────────────────────────────────────────────────────────────────────
109
+ const HTML_ESCAPES = {
110
+ "&": "&amp;",
111
+ "<": "&lt;",
112
+ ">": "&gt;",
113
+ '"': "&quot;",
114
+ "'": "&#39;",
115
+ };
116
+ /** Escape every value that reaches the document — this is untrusted content. */
117
+ function escapeHtml(text) {
118
+ return text.replace(/[&<>"']/g, (char) => HTML_ESCAPES[char] ?? char);
119
+ }
120
+ function htmlTable(rows) {
121
+ const columns = tableColumns(rows);
122
+ if (columns.length === 0)
123
+ return "";
124
+ const head = `<tr>${columns.map((c) => `<th scope="col">${escapeHtml(c)}</th>`).join("")}</tr>`;
125
+ const body = rows
126
+ .map((row) => `<tr>${columns.map((c) => `<td>${escapeHtml(displayScalar(row[c]))}</td>`).join("")}</tr>`)
127
+ .join("");
128
+ return `<table><thead>${head}</thead><tbody>${body}</tbody></table>`;
129
+ }
130
+ function htmlValue(value, depth) {
131
+ if (isTabular(value))
132
+ return htmlTable(value);
133
+ if (Array.isArray(value)) {
134
+ if (value.length === 0)
135
+ return "<p><em>(none)</em></p>";
136
+ return `<ul>${value.map((item) => `<li>${escapeHtml(displayScalar(item))}</li>`).join("")}</ul>`;
137
+ }
138
+ if (value !== null && typeof value === "object") {
139
+ const level = Math.min(depth, 6);
140
+ return Object.entries(value)
141
+ .map(([key, nested]) => `<h${level}>${escapeHtml(key)}</h${level}>${htmlValue(nested, depth + 1)}`)
142
+ .join("");
143
+ }
144
+ const scalar = displayScalar(value);
145
+ return scalar === "" ? "<p><em>(empty)</em></p>" : `<p>${escapeHtml(scalar)}</p>`;
146
+ }
147
+ /**
148
+ * Render a shaped envelope as a self-contained HTML document.
149
+ *
150
+ * Self-contained matters: the output is routinely redirected to a file and
151
+ * opened directly, so it carries its own minimal styling and references
152
+ * nothing external.
153
+ */
154
+ export function renderGenericHtml(command, value) {
155
+ const body = value !== null && typeof value === "object" && !Array.isArray(value)
156
+ ? Object.entries(value)
157
+ .filter(([key]) => !ENVELOPE_META_KEYS.has(key)) // transport metadata, not a result
158
+ .map(([key, nested]) => `<h2>${escapeHtml(key)}</h2>${htmlValue(nested, 3)}`)
159
+ .join("")
160
+ : htmlValue(value, 2);
161
+ return [
162
+ "<!doctype html>",
163
+ '<html lang="en">',
164
+ "<head>",
165
+ '<meta charset="utf-8">',
166
+ '<meta name="viewport" content="width=device-width, initial-scale=1">',
167
+ `<title>akm ${escapeHtml(command)}</title>`,
168
+ "<style>",
169
+ "body{font:16px/1.5 system-ui,sans-serif;margin:2rem auto;max-width:60rem;padding:0 1rem}",
170
+ "table{border-collapse:collapse;width:100%;margin:0 0 1rem}",
171
+ "th,td{border:1px solid #8884;padding:.35rem .6rem;text-align:left}",
172
+ "th{background:#8881}",
173
+ "h1{margin-bottom:1.5rem}",
174
+ "@media(prefers-color-scheme:dark){body{background:#111;color:#eee}}",
175
+ "</style>",
176
+ "</head>",
177
+ "<body>",
178
+ `<h1>akm ${escapeHtml(command)}</h1>`,
179
+ body === "" ? "<p><em>(no output)</em></p>" : body,
180
+ "</body>",
181
+ "</html>",
182
+ "",
183
+ ].join("\n");
184
+ }
185
+ // ── Text ─────────────────────────────────────────────────────────────────────
186
+ /**
187
+ * Flatten `value` onto `lines` as `dotted.path=value` entries — the exact
188
+ * algorithm `formatConfigPlain` (`src/output/text/command-format.ts`)
189
+ * already established as this CLI's real plain-text house style for `akm
190
+ * config list`. Arrays serialize as one compact-JSON line rather than
191
+ * index-per-line (`items.0.name=…`), matching that same precedent instead of
192
+ * inventing a second convention: the arrays that reach this generic
193
+ * fallback are typically short id/tag lists, where one JSON-array line reads
194
+ * better than N extra `path.0=`, `path.1=`, … lines.
195
+ *
196
+ * Exported so command-specific text formatters (e.g. `formatHealthPlain` in
197
+ * `src/output/text/health-format.ts`) can reuse the SAME scalar-tree
198
+ * convention for the parts of their envelope that are plain nested
199
+ * scalars/objects, while overriding just the parts that need bespoke
200
+ * handling (arrays of status-bearing records, where this function's
201
+ * one-JSON-line-per-array behavior is the exact defect those formatters
202
+ * exist to fix). Reuse beats a second flattener.
203
+ */
204
+ export function flattenForText(value, path, lines) {
205
+ if (value === null || value === undefined) {
206
+ lines.push(`${path}=`);
207
+ }
208
+ else if (Array.isArray(value)) {
209
+ lines.push(`${path}=${JSON.stringify(value)}`);
210
+ }
211
+ else if (typeof value === "object") {
212
+ const entries = Object.entries(value);
213
+ if (entries.length === 0) {
214
+ lines.push(`${path}={}`);
215
+ return;
216
+ }
217
+ for (const [key, nested] of entries) {
218
+ flattenForText(nested, `${path}.${key}`, lines);
219
+ }
220
+ }
221
+ else {
222
+ lines.push(`${path}=${String(value)}`);
223
+ }
224
+ }
225
+ /**
226
+ * Render a shaped envelope as flat `key=value` text — no markup characters.
227
+ *
228
+ * This is a DISTINCT function from `renderGenericMarkdown`, not a reuse of
229
+ * it, and that is a deliberate reversal of this fallback's first cut, which
230
+ * called `renderGenericMarkdown` directly for `text` on the reasoning that
231
+ * it emits no HTML and is therefore "plain-text-safe." That reasoning was
232
+ * wrong: `#` heading markers, `_..._` emphasis, and `| ... |` table syntax
233
+ * ARE markup — a terminal happens to print them as literal characters
234
+ * instead of throwing, but a user piping `--format text` into `grep` or a
235
+ * line-oriented script still sees literal `#`/`_`/`|` noise that plain JSON
236
+ * at least didn't have. That is the exact "one format wearing another
237
+ * format's flag" defect this whole fallback exists to close, with Markdown
238
+ * substituted for JSON instead of JSON itself.
239
+ *
240
+ * `akm config list --format text` and `akm info --format text` already
241
+ * establish this CLI's real plain-text convention — flat `dotted.path=value`
242
+ * lines, no markup — so the generic fallback now matches its registered
243
+ * siblings (what a command looks like once someone writes it a bespoke
244
+ * formatter) instead of matching `md`'s.
245
+ */
246
+ export function renderGenericText(command, value) {
247
+ const lines = [];
248
+ if (value !== null && typeof value === "object" && !Array.isArray(value)) {
249
+ for (const [key, nested] of Object.entries(value)) {
250
+ if (ENVELOPE_META_KEYS.has(key))
251
+ continue; // transport metadata, not a result
252
+ flattenForText(nested, key, lines);
253
+ }
254
+ }
255
+ else {
256
+ flattenForText(value, command, lines);
257
+ }
258
+ return lines.length === 0 ? `${command}: (empty)\n` : `${lines.join("\n")}\n`;
259
+ }
@@ -0,0 +1,57 @@
1
+ // This Source Code Form is subject to the terms of the Mozilla Public
2
+ // License, v. 2.0. If a copy of the MPL was not distributed with this
3
+ // file, You can obtain one at https://mozilla.org/MPL/2.0/.
4
+ /**
5
+ * Per-command `md` / `html` renderer registries (D7).
6
+ *
7
+ * Mirrors `src/output/text/registry.ts`: a command may register a bespoke
8
+ * renderer for a document format, and anything unregistered falls back to the
9
+ * generic rendering of its shaped envelope (`./generic-render`). The fallback
10
+ * is what makes all six `--format` values universal; the registry is what lets
11
+ * a command that has something better to say — `akm health` and its report
12
+ * templates — say it without the output pipeline knowing that command exists.
13
+ *
14
+ * Kept separate from the module that dispatches through it so per-command
15
+ * renderer modules can import `registerMdRenderer` / `registerHtmlRenderer`
16
+ * without a cycle back into the pipeline, exactly as the text registry does.
17
+ *
18
+ * Returning `null` from a handler means "I have nothing special for this
19
+ * payload" and falls through to the generic renderer — `akm health` uses that
20
+ * to keep its bespoke tables for the shapes that have them while still
21
+ * rendering everything else.
22
+ */
23
+ import { createCommandRegistry } from "./command-registry.js";
24
+ const MD_RENDERER_REGISTRY = createCommandRegistry();
25
+ const HTML_RENDERER_REGISTRY = createCommandRegistry();
26
+ /** Register a Markdown renderer for a command name. */
27
+ export function registerMdRenderer(command, handler) {
28
+ MD_RENDERER_REGISTRY.register(command, handler);
29
+ }
30
+ /** Register a batch of Markdown renderers in iteration order. */
31
+ export function registerMdRenderers(entries) {
32
+ MD_RENDERER_REGISTRY.registerAll(entries);
33
+ }
34
+ /** Look up a registered Markdown renderer, or `undefined` when unregistered. */
35
+ export function getMdRendererHandler(command) {
36
+ return MD_RENDERER_REGISTRY.get(command);
37
+ }
38
+ /** Remove a previously-registered Markdown renderer. Test-only utility. */
39
+ export function deregisterMdRenderer(command) {
40
+ MD_RENDERER_REGISTRY.deregister(command);
41
+ }
42
+ /** Register an HTML renderer for a command name. */
43
+ export function registerHtmlRenderer(command, handler) {
44
+ HTML_RENDERER_REGISTRY.register(command, handler);
45
+ }
46
+ /** Register a batch of HTML renderers in iteration order. */
47
+ export function registerHtmlRenderers(entries) {
48
+ HTML_RENDERER_REGISTRY.registerAll(entries);
49
+ }
50
+ /** Look up a registered HTML renderer, or `undefined` when unregistered. */
51
+ export function getHtmlRendererHandler(command) {
52
+ return HTML_RENDERER_REGISTRY.get(command);
53
+ }
54
+ /** Remove a previously-registered HTML renderer. Test-only utility. */
55
+ export function deregisterHtmlRenderer(command) {
56
+ HTML_RENDERER_REGISTRY.deregister(command);
57
+ }