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
@@ -0,0 +1,17 @@
1
+ // This Source Code Form is subject to the terms of the Mozilla Public
2
+ // License, v. 2.0. If a copy of the MPL was not distributed with this
3
+ // file, You can obtain one at https://mozilla.org/MPL/2.0/.
4
+ import { getAdapters } from "./registry.js";
5
+ /** Select the first built-in adapter whose ordered root probe claims `root`. */
6
+ export function detectAdapterId(root, fallback = "akm") {
7
+ for (const adapter of getAdapters()) {
8
+ try {
9
+ if (adapter.looksLikeRoot?.(root) === true)
10
+ return adapter.id;
11
+ }
12
+ catch {
13
+ // An unreadable or racing probe does not claim the bundle.
14
+ }
15
+ }
16
+ return fallback;
17
+ }
@@ -0,0 +1,44 @@
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 { directoryMatcher, extensionMatcher, parentDirHintMatcher, smartMdMatcher } from "../../indexer/walk/matchers.js";
5
+ /**
6
+ * The four builtin matchers, in registration order. The array index IS the
7
+ * registration index `runMatchers` uses for tie-breaking. (The `wiki` matcher
8
+ * was removed in chunk 4 — the wiki asset-type is retired; LLM Wiki content is
9
+ * served by the first-class `llm-wiki` adapter, not the akm adapter. The YAML
10
+ * workflow-program matcher was removed by workflow-format-unification — one
11
+ * workflow format now, recognized by frontmatter `type: workflow` or
12
+ * residence under `workflows/`, both already covered by the remaining four.)
13
+ */
14
+ const AKM_MATCHERS = [
15
+ extensionMatcher,
16
+ directoryMatcher,
17
+ parentDirHintMatcher,
18
+ smartMdMatcher,
19
+ ];
20
+ /**
21
+ * Synchronous reproduction of `file-context.ts#runMatchers`'s arbitration
22
+ * (`:242-265`), minus its `ensureBuiltinsRegistered()` dynamic import. Runs
23
+ * every builtin matcher in registration order, collects the non-null
24
+ * `MatchResult`s, and returns the highest-specificity one (ties broken by the
25
+ * later-registered matcher — higher index — winning). Returns null when no
26
+ * matcher claims the file.
27
+ */
28
+ export function recognizeMatch(file) {
29
+ const hits = [];
30
+ for (let i = 0; i < AKM_MATCHERS.length; i++) {
31
+ const result = AKM_MATCHERS[i](file);
32
+ if (result !== null)
33
+ hits.push({ result, index: i });
34
+ }
35
+ if (hits.length === 0)
36
+ return null;
37
+ hits.sort((a, b) => {
38
+ const specDiff = b.result.specificity - a.result.specificity;
39
+ if (specDiff !== 0)
40
+ return specDiff;
41
+ return b.index - a.index;
42
+ });
43
+ return hits[0].result;
44
+ }
@@ -0,0 +1,56 @@
1
+ // This Source Code Form is subject to the terms of the Mozilla Public
2
+ // License, v. 2.0. If a copy of the MPL was not distributed with this
3
+ // file, You can obtain one at https://mozilla.org/MPL/2.0/.
4
+ /**
5
+ * The format-family adapter registry — akm 0.9.0 (§4 / normative §12.6).
6
+ *
7
+ * A STATIC, FROZEN registry per normative §12.6: the ordered built-in adapter
8
+ * list ({@link BUILTIN_ADAPTERS}) is defined in the built-in barrel
9
+ * (`./adapters`) and this module exposes read-only lookups over it. There is NO
10
+ * mutable registration step and NO load-order dependency — `getAdapters()` and
11
+ * `adapterForId()` are populated at MODULE LOAD, so every production call site
12
+ * sees the full set without anyone first calling a registration function. This
13
+ * replaces the earlier mutable module-level singleton, whose "someone must
14
+ * register the built-ins first" contract left the registry empty in production
15
+ * (every source then fell back to `akm`).
16
+ *
17
+ * PRODUCTION CONSUMERS (all install/index-time, never query-time): the ordered
18
+ * `looksLikeRoot` probe in `installations.ts#detectAdapterId`, and the
19
+ * bundle-root probe in `provider-utils.ts#detectStashRoot`.
20
+ *
21
+ * KEYED BY `adapter.id` ONLY. Adapters are FORMAT FAMILIES (§0.2): one adapter
22
+ * per component root, and `recognize()` is the single source of truth for what
23
+ * a file IS. The open OKF `type` lives on the emitted `IndexDocument`, never on
24
+ * the adapter — so there is deliberately NO per-`type` → adapter mapping here.
25
+ *
26
+ * QUERY-TIME SAFETY (normative §14.3 / D11 — "adapters/registry never run at
27
+ * query time"): this module is imported only from install/index-time modules;
28
+ * the search path (`indexer/search/**`) does not import it, and importing the
29
+ * frozen list pulls the concrete adapters into no query-time graph.
30
+ */
31
+ import { BUILTIN_ADAPTERS } from "./adapters/index.js";
32
+ export { BUILTIN_ADAPTERS } from "./adapters/index.js";
33
+ /** `adapter.id -> adapter`, frozen at module load from the static built-in list. */
34
+ const BY_ID = new Map(BUILTIN_ADAPTERS.map((a) => [a.id, a]));
35
+ /**
36
+ * Every built-in adapter, in the §1.2 install-time probe order (array order ==
37
+ * probe precedence; see `./adapters` for the ordering rationale). Returns a
38
+ * fresh array each call so callers may sort/filter it without disturbing the
39
+ * frozen registry.
40
+ */
41
+ export function getAdapters() {
42
+ return [...BUILTIN_ADAPTERS];
43
+ }
44
+ /** Look up an adapter by its own `id` (matches `BundleComponent.adapter`); `undefined` for an unknown id (spec §4 — caller skips + warns). */
45
+ export function adapterForId(id) {
46
+ return BY_ID.get(id);
47
+ }
48
+ /**
49
+ * DEPRECATED no-op retained for test back-compat only. The registry is a static
50
+ * frozen map (normative §12.6) populated at module load, so there is nothing to
51
+ * reset — `getAdapters()` / `adapterForId()` are always the full built-in set.
52
+ * New code MUST NOT depend on this.
53
+ */
54
+ export function resetAdapterRegistryForTests() {
55
+ // intentionally empty — the registry is static; see the doc comment above.
56
+ }
@@ -0,0 +1,4 @@
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
+ export {};
@@ -0,0 +1,30 @@
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 { parse as parseYaml } from "yaml";
5
+ import { UsageError } from "../errors.js";
6
+ import { serializeFrontmatter } from "./asset-serialize.js";
7
+ import { parseFrontmatterBlock } from "./frontmatter.js";
8
+ /** Ensure an AKM-authored Markdown concept is also a conformant OKF concept. */
9
+ export function ensureAkmMarkdownType(content, type) {
10
+ const block = parseFrontmatterBlock(content);
11
+ if (!block)
12
+ return `---\ntype: ${type}\n---\n${content}`;
13
+ let parsed;
14
+ try {
15
+ parsed = block.frontmatter.trim() ? parseYaml(block.frontmatter) : {};
16
+ }
17
+ catch {
18
+ throw new UsageError("AKM Markdown has malformed YAML frontmatter.", "INVALID_FLAG_VALUE");
19
+ }
20
+ if (parsed === null)
21
+ parsed = {};
22
+ if (typeof parsed !== "object" || Array.isArray(parsed)) {
23
+ throw new UsageError("AKM Markdown frontmatter must be a YAML mapping.", "INVALID_FLAG_VALUE");
24
+ }
25
+ const data = parsed;
26
+ if (data.type === type)
27
+ return content;
28
+ const { type: _priorType, ...rest } = data;
29
+ return `---\n${serializeFrontmatter({ type, ...rest })}\n---\n${block.content}`;
30
+ }
@@ -0,0 +1,243 @@
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
+ * Pure recognition/placement layer for the built-in akm asset types — akm
6
+ * 0.9.0 chunk-3 (the taxonomy-cutover leaf).
7
+ *
8
+ * This module is the SINGLE SOURCE OF TRUTH for the `AssetSpec` filesystem
9
+ * surface (`stashDir`/`isRelevantFile`/`toCanonicalName`/`toAssetPath`), the
10
+ * per-type stash-subdir map, and the derive/resolve helpers built on them.
11
+ * Chunk-3 deleted the mutable taxonomy globals that used to expose this data
12
+ * as ambient registries; the surface is now reached through the small typed
13
+ * accessors below (`stashDirFor`/`stashDirNames`/`placementTypes`/
14
+ * `assetPathForName`/`placementSpecFor`), which the `akm` bundle adapter
15
+ * (`adapter/adapters/akm-adapter.ts`) consumes for placement.
16
+ *
17
+ * ── Cycle-safety ──
18
+ *
19
+ * This leaf imports only Node builtins and the `core/recognition-util` pure
20
+ * sink, so it is NOT an import-cycle (SCC) participant. Depending on it from
21
+ * the `akm` adapter keeps the adapter a leaf. Renderer NAMES and action
22
+ * builders for the built-in types live in the static `type-presentation.ts`
23
+ * table (`TYPE_PRESENTATION`); they are intentionally absent here — placement
24
+ * is a filesystem concern, presentation is a rendering concern.
25
+ */
26
+ import fs from "node:fs";
27
+ import path from "node:path";
28
+ import { SCRIPT_EXTENSIONS, WORKFLOW_EXTENSIONS } from "../recognition-util.js";
29
+ function toPosix(input) {
30
+ return input.replace(/\\/g, "/");
31
+ }
32
+ const workflowSpec = {
33
+ isRelevantFile: (fileName) => WORKFLOW_EXTENSIONS.includes(path.extname(fileName).toLowerCase()),
34
+ toCanonicalName: (typeRoot, filePath) => {
35
+ const rel = toPosix(path.relative(typeRoot, filePath));
36
+ for (const ext of WORKFLOW_EXTENSIONS) {
37
+ if (rel.toLowerCase().endsWith(ext))
38
+ return rel.slice(0, -ext.length);
39
+ }
40
+ return rel;
41
+ },
42
+ toAssetPath: (typeRoot, name) => {
43
+ // Explicit extension wins (accepts refs like "release/ship.yaml").
44
+ const lower = name.toLowerCase();
45
+ for (const ext of WORKFLOW_EXTENSIONS) {
46
+ if (lower.endsWith(ext))
47
+ return path.join(typeRoot, name);
48
+ }
49
+ // Probe in canonical extension priority order and fall back
50
+ // to the markdown path so error messages keep naming the canonical file.
51
+ for (const ext of WORKFLOW_EXTENSIONS) {
52
+ const candidate = path.join(typeRoot, `${name}${ext}`);
53
+ if (fs.existsSync(candidate))
54
+ return candidate;
55
+ }
56
+ return path.join(typeRoot, `${name}.md`);
57
+ },
58
+ };
59
+ const markdownSpec = {
60
+ isRelevantFile: (fileName) => path.extname(fileName).toLowerCase() === ".md",
61
+ toCanonicalName: (typeRoot, filePath) => {
62
+ const rel = toPosix(path.relative(typeRoot, filePath));
63
+ // Strip .md extension from canonical names.
64
+ return rel.endsWith(".md") ? rel.slice(0, -3) : rel;
65
+ },
66
+ toAssetPath: (typeRoot, name) => {
67
+ // Accept both with and without .md extension
68
+ const withExt = name.endsWith(".md") ? name : `${name}.md`;
69
+ return path.join(typeRoot, withExt);
70
+ },
71
+ };
72
+ const scriptSpec = {
73
+ isRelevantFile: (fileName) => SCRIPT_EXTENSIONS.has(path.extname(fileName).toLowerCase()),
74
+ toCanonicalName: (typeRoot, filePath) => toPosix(path.relative(typeRoot, filePath)),
75
+ toAssetPath: (typeRoot, name) => path.join(typeRoot, name),
76
+ };
77
+ const BUILTIN_PLACEMENT_SPECS = {
78
+ skill: {
79
+ stashDir: "skills",
80
+ isRelevantFile: (fileName) => fileName === "SKILL.md",
81
+ toCanonicalName: (typeRoot, filePath) => {
82
+ const relDir = toPosix(path.dirname(path.relative(typeRoot, filePath)));
83
+ if (!relDir || relDir === ".")
84
+ return undefined;
85
+ return relDir;
86
+ },
87
+ toAssetPath: (typeRoot, name) => path.join(typeRoot, name, "SKILL.md"),
88
+ },
89
+ command: { stashDir: "commands", ...markdownSpec },
90
+ agent: { stashDir: "agents", ...markdownSpec },
91
+ knowledge: { stashDir: "knowledge", ...markdownSpec },
92
+ // R-045 / Q-18 second half (owner ruling 11, EXECUTE NOW) — `instruction` as
93
+ // a real stash-resident type, mirroring `knowledge`'s plain markdown spec.
94
+ // This is distinct from the ADAPTER-OWNED instruction docs emitted by
95
+ // format-family adapters (root CLAUDE.md/AGENTS.md via `tool-dir-shared.ts`):
96
+ // those carry `document.ownsPresentation === true` and are routed straight
97
+ // to their adapter's own projection by `rendererForIndexedEntry`
98
+ // (`src/commands/read/show.ts`), which checks `ownsPresentation` BEFORE
99
+ // ever consulting a type/renderer mapping. A stash-resident `instructions/`
100
+ // asset never sets that marker, so it always renders via the `knowledge-md`
101
+ // renderer (`TYPE_PRESENTATION.instruction`, `src/core/type-presentation.ts`)
102
+ // like any other placement type — no collision with the adapter-owned path.
103
+ instruction: { stashDir: "instructions", ...markdownSpec },
104
+ workflow: { stashDir: "workflows", ...workflowSpec },
105
+ script: { stashDir: "scripts", ...scriptSpec },
106
+ memory: { stashDir: "memories", ...markdownSpec },
107
+ // Environment assets — whole `.env` files sourced/injected wholesale. Replaced
108
+ // the deprecated `vault` type (removed in 0.9.0). Only key NAMES are surfaced
109
+ // as metadata; values and comment text are never read for indexing (comments
110
+ // routinely contain commented-out credentials).
111
+ env: {
112
+ stashDir: "env",
113
+ isRelevantFile: (fileName) => fileName === ".env" || fileName.endsWith(".env"),
114
+ toCanonicalName: (typeRoot, filePath) => {
115
+ const rel = toPosix(path.relative(typeRoot, filePath));
116
+ const fileName = path.basename(rel);
117
+ // Treat ".env" as the "default" env; "<name>.env" → "<name>"
118
+ if (fileName === ".env") {
119
+ const dir = path.dirname(rel);
120
+ return dir === "." || dir === "" ? "default" : `${dir}/default`;
121
+ }
122
+ const stripped = rel.endsWith(".env") ? rel.slice(0, -4) : rel;
123
+ return stripped;
124
+ },
125
+ toAssetPath: (typeRoot, name) => {
126
+ if (name === "default")
127
+ return path.join(typeRoot, ".env");
128
+ return path.join(typeRoot, name.endsWith(".env") ? name : `${name}.env`);
129
+ },
130
+ },
131
+ // Secrets — a single sensitive value used on its own for authentication (a
132
+ // PEM key, API token, TLS cert). Unlike `env` (a group of related .env
133
+ // configuration), the ENTIRE file is the one secret value — there is no safe
134
+ // region to parse, so only the filename is ever surfaced as metadata. A
135
+ // secret is any regular file under `secrets/` except `.lock`/`.sensitive`
136
+ // sidecars; the canonical name preserves the natural filename.
137
+ secret: {
138
+ stashDir: "secrets",
139
+ isRelevantFile: (fileName) => !fileName.endsWith(".lock") && !fileName.endsWith(".sensitive"),
140
+ toCanonicalName: (typeRoot, filePath) => toPosix(path.relative(typeRoot, filePath)),
141
+ toAssetPath: (typeRoot, name) => path.join(typeRoot, name),
142
+ },
143
+ // v1 spec §13 — `lesson` asset type. Required frontmatter fields are
144
+ // `description` and `when_to_use`; lint enforces both.
145
+ lesson: { stashDir: "lessons", ...markdownSpec },
146
+ // Scheduled tasks. A task file pairs a cron-style schedule with a target
147
+ // (workflow ref, prompt, or command). Stored as pure YAML under
148
+ // <stash>/tasks/<id>.yml.
149
+ task: {
150
+ stashDir: "tasks",
151
+ isRelevantFile: (fileName) => path.extname(fileName).toLowerCase() === ".yml",
152
+ toCanonicalName: (typeRoot, filePath) => {
153
+ const rel = toPosix(path.relative(typeRoot, filePath));
154
+ return rel.endsWith(".yml") ? rel.slice(0, -4) : rel;
155
+ },
156
+ toAssetPath: (typeRoot, name) => {
157
+ const withExt = name.endsWith(".yml") ? name : `${name}.yml`;
158
+ return path.join(typeRoot, withExt);
159
+ },
160
+ },
161
+ // #561 — agent sessions indexed as a first-class searchable asset type.
162
+ // Generated markdown written by the `extract` pass to
163
+ // `sessions/<harness>/<session-id>.md`.
164
+ session: { stashDir: "sessions", ...markdownSpec },
165
+ // Durable stash-level semantic knowledge — facts about the user, team, or
166
+ // project. A plain markdown spec; see docs/architecture/specs/fact-asset-type.md.
167
+ fact: { stashDir: "facts", ...markdownSpec },
168
+ };
169
+ const _placementKeysSubsetOfKnownTypes = true;
170
+ void _placementKeysSubsetOfKnownTypes;
171
+ const PLACEMENT_SPECS = { ...BUILTIN_PLACEMENT_SPECS };
172
+ /** Live placement spec for a type, or `undefined` for an unknown type. */
173
+ export function placementSpecFor(type) {
174
+ return PLACEMENT_SPECS[type];
175
+ }
176
+ /** Every registered placement spec (for whole-registry sweeps). */
177
+ export function placementSpecList() {
178
+ return Object.values(PLACEMENT_SPECS);
179
+ }
180
+ /** Every registered placement type key (the set of types that have a stash subdir). */
181
+ export function placementTypes() {
182
+ return Object.keys(PLACEMENT_SPECS);
183
+ }
184
+ /** The stash subdir a type places into, or `undefined` for an unknown type. */
185
+ export function stashDirFor(type) {
186
+ return PLACEMENT_SPECS[type]?.stashDir;
187
+ }
188
+ /**
189
+ * Reverse of {@link stashDirFor}: the placement type owning a stash subdir, or
190
+ * `undefined` when no registered type places into it. The type→subdir map is a
191
+ * bijection over the built-in types (each type has a distinct subdir), so this
192
+ * is the well-defined inverse used to project a path-based conceptId onto the
193
+ * asset-type metadata required by native adapters.
194
+ */
195
+ export function typeForStashDir(stashDir) {
196
+ for (const [type, spec] of Object.entries(PLACEMENT_SPECS)) {
197
+ if (spec.stashDir === stashDir)
198
+ return type;
199
+ }
200
+ return undefined;
201
+ }
202
+ /** All stash subdir names across the registered types. */
203
+ export function stashDirNames() {
204
+ return Object.values(PLACEMENT_SPECS).map((spec) => spec.stashDir);
205
+ }
206
+ /**
207
+ * Mutate the placement registry to add/replace a type's spec. Used by the
208
+ * custom-asset-type registration surface (extension types); built-ins are
209
+ * baked into {@link PLACEMENT_SPECS} above.
210
+ */
211
+ export function registerAssetSpec(type, spec) {
212
+ PLACEMENT_SPECS[type] = spec;
213
+ }
214
+ /** Remove a previously-registered type's spec. */
215
+ export function deregisterAssetSpec(type) {
216
+ delete PLACEMENT_SPECS[type];
217
+ }
218
+ export function isRelevantAssetFile(assetType, fileName) {
219
+ return PLACEMENT_SPECS[assetType]?.isRelevantFile(fileName) ?? false;
220
+ }
221
+ export function deriveCanonicalAssetName(assetType, typeRoot, filePath) {
222
+ return PLACEMENT_SPECS[assetType]?.toCanonicalName(typeRoot, filePath);
223
+ }
224
+ export function deriveCanonicalAssetNameFromStashRoot(assetType, stashRoot, filePath) {
225
+ const relPath = toPosix(path.relative(stashRoot, filePath));
226
+ const segments = relPath.split("/").filter(Boolean);
227
+ const firstSegment = segments[0];
228
+ // When the first segment matches the canonical type dir (e.g. "agents"),
229
+ // use it as the type root so canonical names are relative to it.
230
+ // Otherwise fall back to stashRoot — this preserves the full relative path
231
+ // as the canonical name, which is correct for installed stashes that live
232
+ // under custom directories (e.g. "tools/agents/svelte-file-editor").
233
+ const typeRoot = firstSegment !== undefined && firstSegment === stashDirFor(assetType)
234
+ ? path.join(stashRoot, firstSegment)
235
+ : stashRoot;
236
+ return deriveCanonicalAssetName(assetType, typeRoot, filePath);
237
+ }
238
+ export function assetPathForName(assetType, typeRoot, name) {
239
+ const spec = PLACEMENT_SPECS[assetType];
240
+ if (!spec)
241
+ throw new Error(`Unknown asset type: "${assetType}"`);
242
+ return spec.toAssetPath(typeRoot, name);
243
+ }
@@ -2,104 +2,135 @@
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
  import path from "node:path";
5
- import { isAssetType } from "../common.js";
6
5
  import { UsageError } from "../errors.js";
7
- /** Accepted spelling aliases mapping to a canonical asset type. */
8
- const TYPE_ALIASES = {
9
- environment: "env",
10
- };
11
- // ── Construction ────────────────────────────────────────────────────────────
6
+ // ── Validation ──────────────────────────────────────────────────────────────
7
+ function validateName(name) {
8
+ if (!name)
9
+ throw new UsageError("Empty asset name.", "MISSING_REQUIRED_ARGUMENT");
10
+ if (name.includes("\0"))
11
+ throw new UsageError("Null byte in asset name.", "MISSING_REQUIRED_ARGUMENT");
12
+ if (/^[A-Za-z]:/.test(name))
13
+ throw new UsageError("Windows drive path in asset name.", "MISSING_REQUIRED_ARGUMENT");
14
+ const slashName = name.replace(/\\/g, "/");
15
+ if (slashName === ".." || slashName.startsWith("../")) {
16
+ throw new UsageError("Path traversal in asset name.", "MISSING_REQUIRED_ARGUMENT");
17
+ }
18
+ if (slashName.split("/").some((seg) => seg === "." || seg === "..")) {
19
+ throw new UsageError("Asset name cannot contain relative path segments.", "MISSING_REQUIRED_ARGUMENT");
20
+ }
21
+ const normalized = path.posix.normalize(slashName);
22
+ if (path.posix.isAbsolute(normalized))
23
+ throw new UsageError("Absolute path in asset name.", "MISSING_REQUIRED_ARGUMENT");
24
+ if (normalized === ".." || normalized.startsWith("../")) {
25
+ throw new UsageError("Path traversal in asset name.", "MISSING_REQUIRED_ARGUMENT");
26
+ }
27
+ }
28
+ function normalizeName(name) {
29
+ return path.posix.normalize(name.replace(/\\/g, "/"));
30
+ }
12
31
  /**
13
- * Build a ref string from components.
32
+ * Bundle slug charset (spec §3.4): non-empty, and excluding `:`/`.`/`#`/`/`
33
+ * plus whitespace. Excluding `:`/`.` is what keeps `bundle//conceptId` lexically
34
+ * distinguishable from a URL in prose; excluding `/` is what makes the first
35
+ * `//` an unambiguous bundle boundary; excluding `#` reserves it for fragments.
36
+ */
37
+ const BUNDLE_SLUG_RE = /^[^\s:.#/]+$/;
38
+ /**
39
+ * True when `s` is a legal bundle slug (spec §11.1 / D-R5 charset: non-empty,
40
+ * no `:`/`.`/`#`/`/` or whitespace). Exported for input boundaries that need
41
+ * to distinguish bundle-qualified refs from source locators.
42
+ */
43
+ export function isBundleSlug(s) {
44
+ return BUNDLE_SLUG_RE.test(s);
45
+ }
46
+ /**
47
+ * Body-ref recognition (prose): the FULLY-QUALIFIED anchored form ONLY
48
+ * (`<bundle>//<concept-id>[#<fragment>]`, spec §11.1 — the short form is never
49
+ * recognized in prose). `g`/`m` for scanning; group 1 = the whole ref token.
14
50
  *
15
- * Examples:
16
- * makeAssetRef("script", "deploy.sh")
17
- * → "script:deploy.sh"
18
- * makeAssetRef("script", "deploy.sh", "npm:@scope/pkg")
19
- * → "npm:@scope/pkg//script:deploy.sh"
20
- * makeAssetRef("skill", "code-review", "local")
21
- * → "local//skill:code-review"
22
- * makeAssetRef("script", "db/migrate/run.sh", "owner/repo")
23
- * → "owner/repo//script:db/migrate/run.sh"
51
+ * The leading `//` must be preceded by a bundle slug, so a URL (`https://…`,
52
+ * `//cdn.example.com/…`) never matches: a scheme carries a `:` before `//`
53
+ * (excluded from the slug), and a scheme-relative `//host` has no slug before
54
+ * its `//` at all. The bundle-slug charset here additionally excludes the prose
55
+ * boundary punctuation (brackets/parens/quotes/backtick/comma/angle) so a
56
+ * leading boundary char (e.g. the `[` of a markdown link) is not absorbed into
57
+ * the slug. The concept segment reuses the same terminator charset as the
58
+ * body-ref scan (whitespace/quotes/brackets/comma/nl), and admits `/`,
59
+ * `.`, and a trailing `#fragment`.
24
60
  */
25
- export function makeAssetRef(type, name, origin) {
26
- validateName(name);
27
- const normalized = normalizeName(name);
28
- const asset = `${type}:${normalized}`;
29
- if (!origin)
30
- return asset;
31
- return `${origin}//${asset}`;
61
+ export const BUNDLE_REF_RE = /(?:^|[\s`"'(,[])([^\s:.#/`"'()[\],<>]+\/\/[^\s"'`)\]>,\n]+)/gm;
62
+ /** NFC-normalize + traversal/null-byte/drive-letter guard + path-normalize a concept id. */
63
+ function normalizeConceptId(raw) {
64
+ const nfc = raw.normalize("NFC");
65
+ if (nfc.includes("#")) {
66
+ throw new UsageError("`#` is reserved for the export fragment in a concept id.", "MISSING_REQUIRED_ARGUMENT");
67
+ }
68
+ validateName(nfc);
69
+ return normalizeName(nfc);
32
70
  }
33
71
  /**
34
- * Serialize a parsed {@link AssetRef} value-object back to its canonical
35
- * `[origin//]type:name` string form. The single formatter for refs — call
36
- * this instead of hand-building `${type}:${name}` template strings so the
37
- * serialization rules (origin prefix, name normalization) live in one place
38
- * and stay in lockstep with {@link parseAssetRef}.
72
+ * Build a fully-qualified (or short) bundle ref string from its components.
39
73
  *
40
- * `refToString(parseAssetRef(s))` round-trips for any `s` that
41
- * `parseAssetRef` accepts.
74
+ * Examples:
75
+ * makeBundleRef(undefined, "knowledge/http-caching") → "knowledge/http-caching"
76
+ * makeBundleRef("core", "knowledge/http-caching") → "core//knowledge/http-caching"
77
+ * makeBundleRef("core", "skills/review", "usage") → "core//skills/review#usage"
42
78
  */
43
- export function refToString(ref) {
44
- return makeAssetRef(ref.type, ref.name, ref.origin);
79
+ export function makeBundleRef(bundle, conceptId, fragment) {
80
+ const normalized = normalizeConceptId(conceptId);
81
+ let out = normalized;
82
+ if (bundle) {
83
+ if (!BUNDLE_SLUG_RE.test(bundle)) {
84
+ throw new UsageError(`Invalid bundle slug "${bundle}". A bundle slug may not contain ':', '.', '#', '/', or whitespace.`, "MISSING_REQUIRED_ARGUMENT");
85
+ }
86
+ out = `${bundle}//${normalized}`;
87
+ }
88
+ if (fragment)
89
+ out = `${out}#${fragment}`;
90
+ return out;
45
91
  }
46
- // ── Parsing ─────────────────────────────────────────────────────────────────
47
92
  /**
48
- * Parse a ref string in the format `[origin//]type:name`.
93
+ * Serialize a parsed {@link BundleRef} back to its canonical
94
+ * `[bundle//]conceptId[#fragment]` string form — the single formatter for the
95
+ * 0.9.0 grammar (call this instead of hand-building `${bundle}//${conceptId}`).
96
+ * `bundleRefToString(parseBundleRef(s))` round-trips for any `s` that
97
+ * `parseBundleRef` accepts.
49
98
  */
50
- export function parseAssetRef(ref) {
99
+ export function bundleRefToString(ref) {
100
+ return makeBundleRef(ref.bundle, ref.conceptId, ref.fragment);
101
+ }
102
+ /**
103
+ * Parse a ref string in the 0.9.0 format `[<bundle>//]<concept-id>[#<fragment>]`.
104
+ * The bundle prefix and the export fragment are both optional; the short form
105
+ * (no `bundle//`) leaves `bundle` undefined for the caller to resolve against
106
+ * the containing bundle (§11.1).
107
+ */
108
+ export function parseBundleRef(ref) {
51
109
  const trimmed = ref.trim();
52
110
  if (!trimmed)
53
111
  throw new UsageError("Empty ref.", "MISSING_REQUIRED_ARGUMENT");
54
- let origin;
112
+ let bundle;
55
113
  let body = trimmed;
56
114
  const boundary = trimmed.indexOf("//");
57
115
  if (boundary >= 0) {
58
- origin = trimmed.slice(0, boundary);
116
+ bundle = trimmed.slice(0, boundary);
59
117
  body = trimmed.slice(boundary + 2);
60
- if (!origin)
61
- throw new UsageError("Empty origin in ref.", "MISSING_REQUIRED_ARGUMENT");
62
- }
63
- const colon = body.indexOf(":");
64
- if (colon <= 0) {
65
- throw new UsageError(`Invalid ref "${trimmed}". Expected [origin//]type:name, e.g. skill:deploy or knowledge:guide.md`, "MISSING_REQUIRED_ARGUMENT");
118
+ if (!bundle)
119
+ throw new UsageError("Empty bundle in ref.", "MISSING_REQUIRED_ARGUMENT");
120
+ if (!BUNDLE_SLUG_RE.test(bundle)) {
121
+ throw new UsageError(`Invalid bundle slug "${bundle}" in ref "${trimmed}". A bundle slug may not contain ':', '.', '#', '/', or whitespace.`, "MISSING_REQUIRED_ARGUMENT");
122
+ }
66
123
  }
67
- const rawType = body.slice(0, colon);
68
- const rawName = body.slice(colon + 1);
69
- // The `vault` asset type was removed in 0.9.0. Point callers at its
70
- // replacements rather than failing with a generic unknown-type error.
71
- if (rawType === "vault") {
72
- throw new UsageError("The `vault` asset type was removed in 0.9.0 — use `env:` (whole .env config) or `secret:` (a single value).", "MISSING_REQUIRED_ARGUMENT");
73
- }
74
- // Type aliases: `environment:` is an accepted spelling of the canonical
75
- // `env:` type.
76
- const resolvedType = TYPE_ALIASES[rawType] ?? rawType;
77
- if (!isAssetType(resolvedType)) {
78
- throw new UsageError(`Invalid asset type: "${rawType}".`, "MISSING_REQUIRED_ARGUMENT");
79
- }
80
- validateName(rawName);
81
- const name = normalizeName(rawName);
82
- return { type: resolvedType, name, origin: origin || undefined };
83
- }
84
- // ── Validation ──────────────────────────────────────────────────────────────
85
- function validateName(name) {
86
- if (!name)
87
- throw new UsageError("Empty asset name.", "MISSING_REQUIRED_ARGUMENT");
88
- if (name.includes("\0"))
89
- throw new UsageError("Null byte in asset name.", "MISSING_REQUIRED_ARGUMENT");
90
- if (/^[A-Za-z]:/.test(name))
91
- throw new UsageError("Windows drive path in asset name.", "MISSING_REQUIRED_ARGUMENT");
92
- const normalized = path.posix.normalize(name.replace(/\\/g, "/"));
93
- if (path.posix.isAbsolute(normalized))
94
- throw new UsageError("Absolute path in asset name.", "MISSING_REQUIRED_ARGUMENT");
95
- if (normalized === ".." || normalized.startsWith("../")) {
96
- throw new UsageError("Path traversal in asset name.", "MISSING_REQUIRED_ARGUMENT");
124
+ // Export fragment: everything after the first `#` in the concept body.
125
+ let fragment;
126
+ const hash = body.indexOf("#");
127
+ if (hash >= 0) {
128
+ fragment = body.slice(hash + 1) || undefined;
129
+ body = body.slice(0, hash);
97
130
  }
98
- const segments = normalized.split("/");
99
- if (segments.some((seg) => seg === "." || seg === "..")) {
100
- throw new UsageError("Asset name cannot contain relative path segments.", "MISSING_REQUIRED_ARGUMENT");
131
+ if (!body) {
132
+ throw new UsageError(`Invalid ref "${trimmed}". Expected [bundle//]conceptId, e.g. knowledge/guide or core//skills/review`, "MISSING_REQUIRED_ARGUMENT");
101
133
  }
102
- }
103
- function normalizeName(name) {
104
- return path.posix.normalize(name.replace(/\\/g, "/"));
134
+ const conceptId = normalizeConceptId(body);
135
+ return { bundle: bundle || undefined, conceptId, fragment };
105
136
  }
@@ -45,6 +45,26 @@ import { stringify as yamlStringify } from "yaml";
45
45
  export function serializeFrontmatter(frontmatter) {
46
46
  return yamlStringify(frontmatter).trimEnd();
47
47
  }
48
+ /**
49
+ * Serialize a frontmatter object with every value `JSON.stringify`-quoted,
50
+ * without `---` fences and without a trailing newline.
51
+ *
52
+ * Use this instead of {@link serializeFrontmatter} when the input is a
53
+ * pre-validated LLM payload where `yaml.stringify` may emit shapes
54
+ * (`|`-block scalars, anchors, unquoted multiline) that the project's
55
+ * hand-rolled `parseFrontmatter` subset parser cannot read back. Every scalar
56
+ * becomes a quoted JSON literal; arrays become `[<json>, <json>]` (space after
57
+ * the comma). Field order is preserved from the input's insertion order.
58
+ *
59
+ * This is the single home for the "guaranteed-quoted scalars" serializer the
60
+ * module doc above anticipates; before it, `distill` (array-aware) and
61
+ * `distill/content-repair` (scalar-only) reimplemented it divergently.
62
+ */
63
+ export function serializeFrontmatterQuoted(frontmatter) {
64
+ return Object.entries(frontmatter)
65
+ .map(([k, v]) => Array.isArray(v) ? `${k}: [${v.map((s) => JSON.stringify(s)).join(", ")}]` : `${k}: ${JSON.stringify(v)}`)
66
+ .join("\n");
67
+ }
48
68
  /**
49
69
  * Assemble a complete asset file string from a frontmatter object and a body.
50
70
  *