akm-cli 0.9.0-rc.9 → 0.9.1-beta.1

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 (477) hide show
  1. package/CHANGELOG.md +1770 -44
  2. package/README.md +38 -47
  3. package/SECURITY.md +14 -1
  4. package/STABILITY.md +499 -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 +74 -89
  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 +148 -88
  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/llm-usage.js +2 -1
  57. package/dist/commands/health/md-report.js +5 -9
  58. package/dist/commands/health/metrics.js +62 -20
  59. package/dist/commands/health/renderers.js +47 -0
  60. package/dist/commands/health/report-view-model.js +4 -5
  61. package/dist/commands/health/stash-exposure.js +1 -1
  62. package/dist/commands/health/surfaces.js +4 -122
  63. package/dist/commands/health/task-runs.js +3 -67
  64. package/dist/commands/health/types-improve.js +7 -0
  65. package/dist/commands/health.js +160 -35
  66. package/dist/commands/improve/anti-collapse.js +2 -2
  67. package/dist/commands/improve/autonomy-gate.js +68 -0
  68. package/dist/commands/improve/collapse-detector.js +41 -40
  69. package/dist/commands/improve/consolidate/eligibility.js +1 -23
  70. package/dist/commands/improve/consolidate/merge.js +4 -0
  71. package/dist/commands/improve/consolidate.js +140 -1000
  72. package/dist/commands/improve/distill/promote-memory.js +12 -12
  73. package/dist/commands/improve/distill/quality-gate.js +11 -6
  74. package/dist/commands/improve/distill.js +58 -69
  75. package/dist/commands/improve/eligibility.js +101 -55
  76. package/dist/commands/improve/extract-cli.js +14 -133
  77. package/dist/commands/improve/improve-cli.js +98 -114
  78. package/dist/commands/improve/improve-result-file.js +1 -28
  79. package/dist/commands/improve/improve-strategies.js +8 -5
  80. package/dist/commands/improve/improve.js +254 -101
  81. package/dist/commands/improve/locks.js +7 -0
  82. package/dist/commands/improve/loop-stages.js +182 -20
  83. package/dist/commands/improve/memory/derived-ref.js +45 -43
  84. package/dist/commands/improve/memory/memory-belief.js +1 -1
  85. package/dist/commands/improve/memory/memory-contradiction-detect.js +4 -12
  86. package/dist/commands/improve/memory/memory-improve.js +14 -5
  87. package/dist/commands/improve/outcome-loop.js +22 -65
  88. package/dist/commands/improve/preparation.js +114 -123
  89. package/dist/commands/improve/proactive-maintenance.js +2 -5
  90. package/dist/commands/improve/reflect.js +56 -160
  91. package/dist/commands/improve/run-context.js +5 -0
  92. package/dist/commands/improve/salience.js +11 -122
  93. package/dist/commands/improve/session-asset.js +4 -0
  94. package/dist/commands/improve/source-identity.js +10 -38
  95. package/dist/commands/lint/base-linter.js +51 -131
  96. package/dist/commands/lint/env-key-rules.js +31 -47
  97. package/dist/commands/lint/index.js +444 -88
  98. package/dist/commands/lint/types.js +22 -1
  99. package/dist/commands/{events.js → log.js} +33 -38
  100. package/dist/commands/migrate-cli.js +92 -12
  101. package/dist/commands/migration-tool.js +46 -0
  102. package/dist/commands/observability-cli.js +70 -209
  103. package/dist/commands/proposal/drain.js +101 -29
  104. package/dist/commands/proposal/proposal-cli.js +76 -48
  105. package/dist/commands/proposal/proposal.js +54 -18
  106. package/dist/commands/proposal/propose-cli.js +88 -0
  107. package/dist/commands/proposal/propose.js +23 -15
  108. package/dist/commands/proposal/repository.js +718 -279
  109. package/dist/commands/proposal/validators/proposal-quality-validators.js +2 -8
  110. package/dist/commands/proposal/validators/proposal-validators.js +55 -7
  111. package/dist/commands/proposal/validators/proposals.js +4 -7
  112. package/dist/commands/read/curate.js +34 -53
  113. package/dist/commands/read/knowledge.js +150 -95
  114. package/dist/commands/read/registry-search.js +2 -2
  115. package/dist/commands/read/remember-cli.js +42 -15
  116. package/dist/commands/read/search-cli.js +180 -78
  117. package/dist/commands/read/search.js +58 -43
  118. package/dist/commands/read/show.js +197 -141
  119. package/dist/commands/registry-cli.js +12 -51
  120. package/dist/commands/remember.js +14 -57
  121. package/dist/commands/sources/add-cli.js +100 -31
  122. package/dist/commands/sources/bundle-cli.js +166 -0
  123. package/dist/commands/sources/bundle-config-ops.js +7 -2
  124. package/dist/commands/sources/info.js +30 -7
  125. package/dist/commands/sources/init.js +12 -12
  126. package/dist/commands/sources/installed-stashes.js +387 -98
  127. package/dist/commands/sources/schema-repair.js +3 -2
  128. package/dist/commands/sources/self-update.js +131 -38
  129. package/dist/commands/sources/source-add.js +72 -17
  130. package/dist/commands/sources/source-clone.js +129 -45
  131. package/dist/commands/sources/source-manage.js +43 -23
  132. package/dist/commands/sources/sources-cli.js +57 -208
  133. package/dist/commands/sources/stash-cli.js +46 -53
  134. package/dist/commands/tasks/tasks-cli.js +91 -97
  135. package/dist/commands/tasks/tasks.js +284 -423
  136. package/dist/commands/workflow-cli.js +181 -450
  137. package/dist/core/abort-deadline.js +28 -0
  138. package/dist/core/adapter/adapters/agent-skills-adapter.js +67 -5
  139. package/dist/core/adapter/adapters/akm-adapter.js +60 -38
  140. package/dist/core/adapter/adapters/akm-lint.js +109 -42
  141. package/dist/core/adapter/adapters/akm-metadata.js +15 -44
  142. package/dist/core/adapter/adapters/akm-task-adapter.js +54 -31
  143. package/dist/core/adapter/adapters/akm-workflow-adapter.js +55 -71
  144. package/dist/core/adapter/adapters/dotenv-adapter.js +1 -1
  145. package/dist/core/adapter/adapters/generic-files-adapter.js +2 -0
  146. package/dist/core/adapter/adapters/index.js +6 -6
  147. package/dist/core/adapter/adapters/llm-wiki-adapter.js +14 -8
  148. package/dist/core/adapter/adapters/okf-adapter.js +187 -19
  149. package/dist/core/adapter/adapters/shared.js +3 -19
  150. package/dist/core/adapter/adapters/tool-dir-shared.js +13 -6
  151. package/dist/core/adapter/adapters/website-snapshot-adapter.js +1 -0
  152. package/dist/core/adapter/detect-adapter.js +17 -0
  153. package/dist/core/adapter/recognize-match.js +6 -4
  154. package/dist/core/adapter/validate-context.js +214 -0
  155. package/dist/core/asset/akm-markdown.js +63 -0
  156. package/dist/core/asset/asset-placement.js +20 -6
  157. package/dist/core/asset/asset-ref.js +11 -9
  158. package/dist/core/asset/frontmatter-lint.js +30 -0
  159. package/dist/core/asset/frontmatter.js +29 -9
  160. package/dist/core/asset/markdown.js +40 -51
  161. package/dist/core/asset/resolve-ref.js +89 -18
  162. package/dist/core/asset/stash-meta.js +1 -1
  163. package/dist/core/bundle-id.js +51 -0
  164. package/dist/core/common.js +218 -44
  165. package/dist/core/concurrent.js +32 -0
  166. package/dist/core/config/config-io.js +12 -1
  167. package/dist/core/config/config-schema.js +35 -8
  168. package/dist/core/config/config-sources.js +55 -11
  169. package/dist/core/config/config-walker.js +25 -9
  170. package/dist/core/config/config.js +9 -48
  171. package/dist/core/config/experimental.js +21 -0
  172. package/dist/core/config/schema/embedding.js +5 -1
  173. package/dist/core/config/schema/experimental.js +30 -0
  174. package/dist/core/config/schema/improve-processes.js +0 -6
  175. package/dist/core/config/schema/improve.js +21 -3
  176. package/dist/core/config/schema/index-config.js +8 -15
  177. package/dist/core/config/schema/output.js +4 -1
  178. package/dist/core/config/schema/setup.js +9 -18
  179. package/dist/core/config/schema/sources-bundles.js +49 -33
  180. package/dist/core/config/schema/workflow.js +14 -3
  181. package/dist/core/env-secret-ref.js +76 -46
  182. package/dist/core/errors.js +43 -12
  183. package/dist/core/events.js +76 -152
  184. package/dist/core/file-change.js +6 -5
  185. package/dist/core/file-lock.js +7 -1
  186. package/dist/core/fs-txn.js +83 -7
  187. package/dist/core/git-message.js +2 -2
  188. package/dist/core/improve-result.js +6 -100
  189. package/dist/core/json-schema.js +327 -9
  190. package/dist/core/lesson-lint.js +1 -17
  191. package/dist/core/logs-db.js +2 -1
  192. package/dist/core/loopback.js +89 -0
  193. package/dist/core/migration-operation.js +33 -2
  194. package/dist/core/mutation-target.js +78 -0
  195. package/dist/core/parse.js +4 -1
  196. package/dist/core/path-access.js +107 -0
  197. package/dist/core/paths.js +17 -20
  198. package/dist/core/recognition-util.js +12 -14
  199. package/dist/core/redaction.js +98 -17
  200. package/dist/core/spawn-env.js +234 -0
  201. package/dist/core/standards/resolve-standards-context.js +2 -14
  202. package/dist/core/standards/resolve-stash-standards.js +2 -2
  203. package/dist/core/standards/resolve-type-conventions.js +2 -2
  204. package/dist/core/state/migrations.js +41 -18
  205. package/dist/core/state-db-scope.js +134 -0
  206. package/dist/core/state-db.js +5 -14
  207. package/dist/core/structured.js +1 -1
  208. package/dist/core/subprocess.js +182 -36
  209. package/dist/core/text-truncation.js +9 -5
  210. package/dist/core/type-presentation.js +3 -3
  211. package/dist/core/warn.js +0 -3
  212. package/dist/core/write-provenance.js +85 -0
  213. package/dist/core/write-source.js +778 -95
  214. package/dist/indexer/bundle-identity-guard.js +3 -2
  215. package/dist/indexer/db/graph-db.js +17 -30
  216. package/dist/indexer/ensure-index.js +11 -3
  217. package/dist/indexer/graph/graph-boost.js +9 -34
  218. package/dist/indexer/graph/graph-extraction.js +8 -5
  219. package/dist/indexer/index-writer-lock.js +53 -17
  220. package/dist/indexer/index-written-assets.js +33 -24
  221. package/dist/indexer/indexer.js +551 -253
  222. package/dist/indexer/installations.js +14 -96
  223. package/dist/indexer/passes/dir-staleness.js +16 -9
  224. package/dist/indexer/passes/memory-inference.js +15 -9
  225. package/dist/indexer/passes/metadata.js +113 -47
  226. package/dist/indexer/scan/doc-to-entry.js +38 -1
  227. package/dist/indexer/scan/drain-dir.js +13 -23
  228. package/dist/indexer/search/db-search.js +107 -69
  229. package/dist/indexer/search/fts-query.js +47 -24
  230. package/dist/indexer/search/ranking-contributors.js +42 -20
  231. package/dist/indexer/search/ranking.js +18 -99
  232. package/dist/indexer/search/search-fields.js +7 -2
  233. package/dist/indexer/search/search-source.js +82 -93
  234. package/dist/indexer/usage/usage-events.js +0 -89
  235. package/dist/indexer/walk/file-context.js +2 -1
  236. package/dist/indexer/walk/matchers.js +30 -43
  237. package/dist/indexer/walk/path-resolver.js +7 -2
  238. package/dist/indexer/walk/walker.js +38 -12
  239. package/dist/integrations/agent/builders.js +0 -6
  240. package/dist/integrations/agent/config.js +2 -2
  241. package/dist/integrations/agent/detect.js +49 -19
  242. package/dist/integrations/agent/engine-fallback.js +76 -0
  243. package/dist/integrations/agent/engine-resolution.js +24 -11
  244. package/dist/integrations/agent/model-aliases.js +1 -1
  245. package/dist/integrations/agent/profiles.js +23 -1
  246. package/dist/integrations/agent/prompts.js +12 -8
  247. package/dist/integrations/agent/runner-dispatch.js +4 -2
  248. package/dist/integrations/agent/runner.js +0 -1
  249. package/dist/integrations/agent/spawn.js +20 -93
  250. package/dist/integrations/github.js +1 -1
  251. package/dist/integrations/harnesses/aider/agent-builder.js +6 -4
  252. package/dist/integrations/harnesses/amazonq/agent-builder.js +7 -4
  253. package/dist/integrations/harnesses/claude/session-log.js +0 -10
  254. package/dist/integrations/harnesses/codex/agent-builder.js +5 -2
  255. package/dist/integrations/harnesses/copilot/agent-builder.js +5 -3
  256. package/dist/integrations/harnesses/gemini/agent-builder.js +5 -3
  257. package/dist/integrations/harnesses/index.js +3 -7
  258. package/dist/integrations/harnesses/opencode/agent-builder.js +21 -2
  259. package/dist/integrations/harnesses/opencode/session-log.js +0 -15
  260. package/dist/integrations/harnesses/opencode-sdk/sdk-runner.js +13 -4
  261. package/dist/integrations/harnesses/openhands/agent-builder.js +9 -6
  262. package/dist/integrations/harnesses/pi/agent-builder.js +6 -4
  263. package/dist/integrations/lockfile.js +132 -8
  264. package/dist/integrations/session-logs/index.js +3 -28
  265. package/dist/llm/client.js +136 -100
  266. package/dist/llm/embedders/remote.js +13 -5
  267. package/dist/llm/feature-gate.js +4 -12
  268. package/dist/llm/graph-extract.js +5 -11
  269. package/dist/llm/memory-infer.js +144 -1
  270. package/dist/llm/metadata-enhance.js +5 -7
  271. package/dist/llm/structured-call.js +1 -1
  272. package/dist/llm/usage-persist.js +26 -5
  273. package/dist/llm/usage-telemetry.js +25 -2
  274. package/dist/output/cli-hints.js +1 -2
  275. package/dist/output/context.js +22 -7
  276. package/dist/output/format-exempt.js +80 -0
  277. package/dist/output/generic-render.js +259 -0
  278. package/dist/output/render-registry.js +57 -0
  279. package/dist/output/renderers.js +14 -36
  280. package/dist/output/shapes/curate.js +10 -1
  281. package/dist/output/shapes/events.js +12 -7
  282. package/dist/output/shapes/helpers.js +56 -83
  283. package/dist/output/shapes/migrate.js +8 -0
  284. package/dist/output/shapes/passthrough.js +7 -41
  285. package/dist/output/shapes/proposal/producer.js +15 -7
  286. package/dist/output/shapes.js +2 -9
  287. package/dist/output/text/{init.js → bundle-create.js} +3 -1
  288. package/dist/output/text/bundle-show.js +7 -0
  289. package/dist/output/text/command-format.js +164 -96
  290. package/dist/output/text/env.js +1 -3
  291. package/dist/output/text/events.js +8 -7
  292. package/dist/output/text/health-format.js +103 -0
  293. package/dist/output/text/health.js +7 -0
  294. package/dist/output/text/helpers.js +10 -8
  295. package/dist/output/text/lint-format.js +56 -0
  296. package/dist/{migrate-storage-node.mjs → output/text/lint.js} +2 -5
  297. package/dist/output/text/migrate.js +88 -0
  298. package/dist/output/text/proposal/producer.js +4 -2
  299. package/dist/output/text/proposal-format.js +44 -72
  300. package/dist/output/text/registry-commands.js +1 -2
  301. package/dist/output/text/show-directives.js +15 -7
  302. package/dist/output/text/status-list.js +32 -0
  303. package/dist/output/text/{save.js → sync.js} +2 -2
  304. package/dist/output/text/workflow-format.js +24 -203
  305. package/dist/output/text/workflow.js +1 -7
  306. package/dist/output/text.js +16 -17
  307. package/dist/registry/factory.js +4 -6
  308. package/dist/registry/origin-resolve.js +16 -27
  309. package/dist/registry/providers/skills-sh.js +3 -3
  310. package/dist/registry/providers/static-index.js +13 -23
  311. package/dist/registry/resolve.js +42 -7
  312. package/dist/registry/semver.js +34 -84
  313. package/dist/runtime.js +2 -23
  314. package/dist/scripts/akm-migrate-node.js +60972 -0
  315. package/dist/scripts/akm-migrate.js +60310 -0
  316. package/dist/setup/detect.js +42 -15
  317. package/dist/setup/registry-stash-loader.js +2 -2
  318. package/dist/setup/setup.js +236 -136
  319. package/dist/setup/steps/connection.js +7 -9
  320. package/dist/setup/steps/platforms.js +9 -9
  321. package/dist/setup/steps/semantic.js +15 -3
  322. package/dist/setup/steps/sources.js +12 -13
  323. package/dist/setup/steps/stashdir.js +2 -3
  324. package/dist/setup/steps/tasks.js +237 -120
  325. package/dist/sources/freshness.js +1 -1
  326. package/dist/sources/provider-factory.js +11 -17
  327. package/dist/sources/providers/filesystem.js +2 -3
  328. package/dist/sources/providers/git-install.js +278 -34
  329. package/dist/sources/providers/git-provider.js +25 -23
  330. package/dist/sources/providers/git-stash.js +414 -106
  331. package/dist/sources/providers/git.js +2 -2
  332. package/dist/sources/providers/npm.js +16 -19
  333. package/dist/sources/providers/provider-utils.js +7 -4
  334. package/dist/sources/providers/sync-from-ref.js +3 -9
  335. package/dist/sources/providers/website.js +6 -1
  336. package/dist/sources/resolve.js +6 -5
  337. package/dist/sources/snapshot-fetchers/bluesky.js +146 -0
  338. package/dist/sources/snapshot-fetchers/content-extract.js +566 -0
  339. package/dist/sources/snapshot-fetchers/fetcher-util.js +41 -0
  340. package/dist/sources/snapshot-fetchers/github.js +100 -0
  341. package/dist/sources/snapshot-fetchers/host-guard.js +291 -0
  342. package/dist/sources/snapshot-fetchers/registry.js +17 -1
  343. package/dist/sources/snapshot-fetchers/robots.js +348 -0
  344. package/dist/sources/snapshot-fetchers/rss.js +282 -0
  345. package/dist/sources/snapshot-fetchers/secret-seam.js +42 -0
  346. package/dist/sources/snapshot-fetchers/website-ingest.js +686 -282
  347. package/dist/sources/snapshot-fetchers/x.js +910 -0
  348. package/dist/storage/database.js +13 -1
  349. package/dist/storage/engines/sqlite-migrations.js +23 -111
  350. package/dist/storage/managed-db.js +21 -2
  351. package/dist/storage/repositories/canaries-repository.js +1 -1
  352. package/dist/storage/repositories/events-repository.js +27 -11
  353. package/dist/storage/repositories/improve-runs-repository.js +6 -12
  354. package/dist/storage/repositories/index-connection.js +44 -8
  355. package/dist/storage/repositories/index-entries-repository.js +157 -241
  356. package/dist/storage/repositories/index-entry-mapper.js +15 -11
  357. package/dist/storage/repositories/index-fts-repository.js +5 -2
  358. package/dist/storage/repositories/index-llm-cache-repository.js +0 -1
  359. package/dist/storage/repositories/index-meta-repository.js +2 -3
  360. package/dist/storage/repositories/index-schema.js +10 -25
  361. package/dist/storage/repositories/index-utility-repository.js +15 -28
  362. package/dist/storage/repositories/index-vec-repository.js +6 -1
  363. package/dist/storage/repositories/outcome-repository.js +119 -0
  364. package/dist/storage/repositories/proposals-repository.js +296 -59
  365. package/dist/storage/repositories/registry-cache.js +19 -0
  366. package/dist/storage/repositories/salience-repository.js +172 -0
  367. package/dist/storage/repositories/task-history-repository.js +15 -13
  368. package/dist/storage/repositories/workflow-runs-repository.js +118 -53
  369. package/dist/tasks/backends/cron.js +105 -15
  370. package/dist/tasks/backends/index.js +1 -1
  371. package/dist/tasks/backends/launchd.js +85 -38
  372. package/dist/tasks/backends/schtasks.js +135 -15
  373. package/dist/tasks/embedded.js +56 -40
  374. package/dist/tasks/log-redaction.js +156 -0
  375. package/dist/tasks/parser.js +87 -160
  376. package/dist/tasks/resolve-akm-bin.js +137 -59
  377. package/dist/tasks/runner.js +209 -44
  378. package/dist/tasks/scheduler-invocation.js +220 -10
  379. package/dist/tasks/schema.js +89 -1
  380. package/dist/tasks/task-id.js +1 -3
  381. package/dist/tasks/validator.js +20 -6
  382. package/dist/workflows/authoring/authoring.js +94 -143
  383. package/dist/workflows/authoring/scope-key.js +1 -1
  384. package/dist/workflows/concurrency-policy.js +95 -1
  385. package/dist/workflows/exec/dispatch-redaction.js +114 -0
  386. package/dist/workflows/exec/exec-unit.js +542 -0
  387. package/dist/workflows/exec/frozen-judge.js +130 -32
  388. package/dist/workflows/exec/native-executor.js +513 -274
  389. package/dist/workflows/exec/param-secrets.js +12 -11
  390. package/dist/workflows/exec/run-workflow.js +496 -237
  391. package/dist/workflows/exec/step-work.js +616 -412
  392. package/dist/workflows/exec/unit-dispatch.js +34 -0
  393. package/dist/workflows/exec/unit-writer.js +53 -13
  394. package/dist/workflows/exec/worktree.js +454 -41
  395. package/dist/workflows/ir/compile.js +166 -271
  396. package/dist/workflows/ir/freeze.js +119 -42
  397. package/dist/workflows/ir/params.js +135 -11
  398. package/dist/workflows/ir/plan-hash.js +1 -1
  399. package/dist/workflows/ir/schema.js +129 -45
  400. package/dist/workflows/parser.js +1070 -307
  401. package/dist/workflows/program/expressions.js +20 -208
  402. package/dist/workflows/program/schema.js +31 -10
  403. package/dist/workflows/renderer.js +123 -68
  404. package/dist/workflows/resource-limits.js +184 -0
  405. package/dist/workflows/runtime/checkin.js +3 -3
  406. package/dist/workflows/runtime/plan-classifier.js +16 -75
  407. package/dist/workflows/runtime/runs.js +331 -132
  408. package/dist/workflows/runtime/unit-checkin.js +1 -1
  409. package/dist/workflows/runtime/unit-phases.js +2 -2
  410. package/dist/workflows/runtime/workflow-asset-loader.js +232 -83
  411. package/dist/workflows/schema.js +1 -11
  412. package/dist/workflows/validate-summary.js +47 -38
  413. package/dist/workflows/validator.js +21 -62
  414. package/docs/README.md +110 -0
  415. package/docs/migration/README.md +8 -0
  416. package/docs/migration/release-notes/0.7.0.md +11 -11
  417. package/docs/migration/release-notes/0.9.0.md +209 -27
  418. package/docs/migration/v0.7-to-v0.8.md +48 -48
  419. package/docs/migration/v0.8-to-v0.9.md +567 -209
  420. package/docs/migration/v0.9.0-troubleshooting.md +561 -0
  421. package/docs/reference/README.md +19 -0
  422. package/docs/reference/bundle-types.md +19 -0
  423. package/docs/reference/cli.md +2342 -0
  424. package/docs/reference/configuration.md +371 -0
  425. package/docs/reference/data-and-telemetry.md +129 -46
  426. package/docs/reference/supported-formats.md +50 -0
  427. package/docs/reference/workflow-schema.md +1014 -0
  428. package/docs/reference/workflows.md +51 -0
  429. package/package.json +31 -13
  430. package/schemas/akm-asset-envelope.json +93 -0
  431. package/schemas/akm-config.json +89 -128
  432. package/schemas/akm-task.json +27 -5
  433. package/schemas/akm-workflow.json +157 -81
  434. package/dist/assets/tasks/core/backup.yml +0 -5
  435. package/dist/assets/tasks/graph-refresh-weekly.yml +0 -10
  436. package/dist/cli/config-migrate.js +0 -1878
  437. package/dist/cli/config-validate.js +0 -41
  438. package/dist/commands/backup-cli.js +0 -56
  439. package/dist/commands/bundle/bundle-cli.js +0 -68
  440. package/dist/commands/bundle/bundle.js +0 -219
  441. package/dist/commands/graph/graph-cli.js +0 -124
  442. package/dist/commands/graph/graph.js +0 -489
  443. package/dist/commands/improve/extract-watch.js +0 -140
  444. package/dist/commands/mv-cli.js +0 -1221
  445. package/dist/commands/sources/history.js +0 -201
  446. package/dist/commands/tasks/default-tasks.js +0 -186
  447. package/dist/core/migration-backup.js +0 -1231
  448. package/dist/indexer/usage/unmigrated-vaults-guard.js +0 -95
  449. package/dist/llm/memory-infer-impl.js +0 -138
  450. package/dist/migrate/legacy/config-source-migration.js +0 -257
  451. package/dist/migrate/legacy/content-migration.js +0 -350
  452. package/dist/migrate/legacy/legacy-layout.js +0 -779
  453. package/dist/migrate/legacy/legacy-paths.js +0 -25
  454. package/dist/migrate/legacy/legacy-stash-json.js +0 -80
  455. package/dist/migrate/legacy/proposal-fs-import.js +0 -168
  456. package/dist/migrate/legacy/task-target-ref-migration.js +0 -278
  457. package/dist/migrate/legacy/three-db-cutover.js +0 -845
  458. package/dist/migrate/legacy/workflow-migrations-bodies.js +0 -52
  459. package/dist/migrate/legacy/workflow-migrations-frozen.js +0 -21
  460. package/dist/migrate/legacy-ref-grammar.js +0 -214
  461. package/dist/output/shapes/distill.js +0 -14
  462. package/dist/output/shapes/history.js +0 -11
  463. package/dist/output/text/distill.js +0 -6
  464. package/dist/output/text/enable-disable.js +0 -8
  465. package/dist/output/text/history.js +0 -6
  466. package/dist/registry/build-index.js +0 -382
  467. package/dist/schemas/akm-config.json +0 -4704
  468. package/dist/schemas/akm-task.json +0 -87
  469. package/dist/schemas/akm-workflow.json +0 -372
  470. package/dist/scripts/migrate-storage.js +0 -3816
  471. package/dist/workflows/authoring/workflow-program-template.yaml +0 -31
  472. package/dist/workflows/cli.js +0 -53
  473. package/dist/workflows/exec/brief.js +0 -481
  474. package/dist/workflows/exec/report.js +0 -1460
  475. package/dist/workflows/exec/watch.js +0 -116
  476. package/dist/workflows/program/parser.js +0 -813
  477. package/dist/workflows/program/project.js +0 -104
@@ -0,0 +1,63 @@
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 { localDateStamp } from "../common.js";
6
+ import { UsageError } from "../errors.js";
7
+ import { serializeFrontmatter } from "./asset-serialize.js";
8
+ import { parseFrontmatterBlock, spliceFrontmatterLine } from "./frontmatter.js";
9
+ /**
10
+ * Ensure an AKM-authored Markdown concept is also a conformant OKF concept.
11
+ *
12
+ * Stamps BOTH `type` and `updated`, because both are required of a conformant
13
+ * document and this is the one chokepoint every `.md` write passes through
14
+ * (`core/write-source.ts`). Without the `updated` stamp, every asset akm
15
+ * created for you — `akm remember`, `akm import`, accepted proposals,
16
+ * authored workflows — was immediately flagged `missing-updated` by akm's own
17
+ * `akm lint`, so the tool disagreed with itself about its own output.
18
+ *
19
+ * An existing `updated` is left alone: this fills a gap, it does not
20
+ * re-stamp on every write (which would churn timestamps and manufacture
21
+ * needless diffs in git-backed bundles).
22
+ *
23
+ * Source preservation: when the type already matches and the ONLY change is
24
+ * adding `updated`, the line is spliced into the original block textually —
25
+ * round-tripping through the YAML serializer would drop user-authored
26
+ * comments and normalize formatting just to contribute one field. Only a
27
+ * document whose `type` must actually be corrected takes the re-serialize
28
+ * path (as it always has).
29
+ */
30
+ export function ensureAkmMarkdownType(content, type, now = new Date()) {
31
+ const block = parseFrontmatterBlock(content);
32
+ if (!block) {
33
+ return `---\n${serializeFrontmatter({ type, updated: localDateStamp(now) })}\n---\n${content}`;
34
+ }
35
+ let parsed;
36
+ try {
37
+ parsed = block.frontmatter.trim() ? parseYaml(block.frontmatter) : {};
38
+ }
39
+ catch {
40
+ throw new UsageError("AKM Markdown has malformed YAML frontmatter.", "INVALID_FLAG_VALUE");
41
+ }
42
+ if (parsed === null)
43
+ parsed = {};
44
+ if (typeof parsed !== "object" || Array.isArray(parsed)) {
45
+ throw new UsageError("AKM Markdown frontmatter must be a YAML mapping.", "INVALID_FLAG_VALUE");
46
+ }
47
+ const data = parsed;
48
+ const needsUpdated = !("updated" in data);
49
+ if (data.type === type) {
50
+ if (!needsUpdated)
51
+ return content;
52
+ const spliced = spliceFrontmatterLine(content, `updated: ${localDateStamp(now)}`);
53
+ if (spliced !== null)
54
+ return spliced;
55
+ // Unreachable in practice (parseFrontmatterBlock succeeded above), but a
56
+ // re-serialized document beats a non-conformant one.
57
+ }
58
+ const { type: _priorType, ...rest } = data;
59
+ const next = { type, ...rest };
60
+ if (needsUpdated)
61
+ next.updated = localDateStamp(now);
62
+ return `---\n${serializeFrontmatter(next)}\n---\n${block.content}`;
63
+ }
@@ -46,7 +46,7 @@ const workflowSpec = {
46
46
  if (lower.endsWith(ext))
47
47
  return path.join(typeRoot, name);
48
48
  }
49
- // Probe in priority order — `.md` first for back-compat — and fall back
49
+ // Probe in canonical extension priority order and fall back
50
50
  // to the markdown path so error messages keep naming the canonical file.
51
51
  for (const ext of WORKFLOW_EXTENSIONS) {
52
52
  const candidate = path.join(typeRoot, `${name}${ext}`);
@@ -60,7 +60,7 @@ const markdownSpec = {
60
60
  isRelevantFile: (fileName) => path.extname(fileName).toLowerCase() === ".md",
61
61
  toCanonicalName: (typeRoot, filePath) => {
62
62
  const rel = toPosix(path.relative(typeRoot, filePath));
63
- // Strip .md extension from canonical names (agent:code-reviewer, not agent:code-reviewer.md)
63
+ // Strip .md extension from canonical names.
64
64
  return rel.endsWith(".md") ? rel.slice(0, -3) : rel;
65
65
  },
66
66
  toAssetPath: (typeRoot, name) => {
@@ -74,7 +74,7 @@ const scriptSpec = {
74
74
  toCanonicalName: (typeRoot, filePath) => toPosix(path.relative(typeRoot, filePath)),
75
75
  toAssetPath: (typeRoot, name) => path.join(typeRoot, name),
76
76
  };
77
- const PLACEMENT_SPECS = {
77
+ const BUILTIN_PLACEMENT_SPECS = {
78
78
  skill: {
79
79
  stashDir: "skills",
80
80
  isRelevantFile: (fileName) => fileName === "SKILL.md",
@@ -89,6 +89,18 @@ const PLACEMENT_SPECS = {
89
89
  command: { stashDir: "commands", ...markdownSpec },
90
90
  agent: { stashDir: "agents", ...markdownSpec },
91
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 },
92
104
  workflow: { stashDir: "workflows", ...workflowSpec },
93
105
  script: { stashDir: "scripts", ...scriptSpec },
94
106
  memory: { stashDir: "memories", ...markdownSpec },
@@ -154,6 +166,9 @@ const PLACEMENT_SPECS = {
154
166
  // project. A plain markdown spec; see docs/architecture/specs/fact-asset-type.md.
155
167
  fact: { stashDir: "facts", ...markdownSpec },
156
168
  };
169
+ const _placementKeysSubsetOfKnownTypes = true;
170
+ void _placementKeysSubsetOfKnownTypes;
171
+ const PLACEMENT_SPECS = { ...BUILTIN_PLACEMENT_SPECS };
157
172
  /** Live placement spec for a type, or `undefined` for an unknown type. */
158
173
  export function placementSpecFor(type) {
159
174
  return PLACEMENT_SPECS[type];
@@ -174,9 +189,8 @@ export function stashDirFor(type) {
174
189
  * Reverse of {@link stashDirFor}: the placement type owning a stash subdir, or
175
190
  * `undefined` when no registered type places into it. The type→subdir map is a
176
191
  * bijection over the built-in types (each type has a distinct subdir), so this
177
- * is the well-defined inverse. Used by the Chunk-5 dual-grammar shim to reverse
178
- * a D-R2 qualified conceptId (`<stash-subdir>/<name>`) back to a legacy
179
- * `type:name` predicate so NULL-`item_ref` rows stay findable by new refs.
192
+ * is the well-defined inverse used to project a path-based conceptId onto the
193
+ * asset-type metadata required by native adapters.
180
194
  */
181
195
  export function typeForStashDir(stashDir) {
182
196
  for (const [type, spec] of Object.entries(PLACEMENT_SPECS)) {
@@ -11,16 +11,19 @@ function validateName(name) {
11
11
  throw new UsageError("Null byte in asset name.", "MISSING_REQUIRED_ARGUMENT");
12
12
  if (/^[A-Za-z]:/.test(name))
13
13
  throw new UsageError("Windows drive path in asset name.", "MISSING_REQUIRED_ARGUMENT");
14
- const normalized = path.posix.normalize(name.replace(/\\/g, "/"));
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);
15
22
  if (path.posix.isAbsolute(normalized))
16
23
  throw new UsageError("Absolute path in asset name.", "MISSING_REQUIRED_ARGUMENT");
17
24
  if (normalized === ".." || normalized.startsWith("../")) {
18
25
  throw new UsageError("Path traversal in asset name.", "MISSING_REQUIRED_ARGUMENT");
19
26
  }
20
- const segments = normalized.split("/");
21
- if (segments.some((seg) => seg === "." || seg === "..")) {
22
- throw new UsageError("Asset name cannot contain relative path segments.", "MISSING_REQUIRED_ARGUMENT");
23
- }
24
27
  }
25
28
  function normalizeName(name) {
26
29
  return path.posix.normalize(name.replace(/\\/g, "/"));
@@ -34,9 +37,8 @@ function normalizeName(name) {
34
37
  const BUNDLE_SLUG_RE = /^[^\s:.#/]+$/;
35
38
  /**
36
39
  * True when `s` is a legal bundle slug (spec §11.1 / D-R5 charset: non-empty,
37
- * no `:`/`.`/`#`/`/` or whitespace). Exported so the dual-grammar input dispatch
38
- * (`resolve-ref.ts`) can classify a `prefix//tail` token by whether its prefix
39
- * is a legal bundle slug — the clean legacy-vs-new-grammar discriminator.
40
+ * no `:`/`.`/`#`/`/` or whitespace). Exported for input boundaries that need
41
+ * to distinguish bundle-qualified refs from source locators.
40
42
  */
41
43
  export function isBundleSlug(s) {
42
44
  return BUNDLE_SLUG_RE.test(s);
@@ -53,7 +55,7 @@ export function isBundleSlug(s) {
53
55
  * boundary punctuation (brackets/parens/quotes/backtick/comma/angle) so a
54
56
  * leading boundary char (e.g. the `[` of a markdown link) is not absorbed into
55
57
  * the slug. The concept segment reuses the same terminator charset as the
56
- * legacy body-ref scan (whitespace/quotes/brackets/comma/nl), and admits `/`,
58
+ * body-ref scan (whitespace/quotes/brackets/comma/nl), and admits `/`,
57
59
  * `.`, and a trailing `#fragment`.
58
60
  */
59
61
  export const BUNDLE_REF_RE = /(?:^|[\s`"'(,[])([^\s:.#/`"'()[\],<>]+\/\/[^\s"'`)\]>,\n]+)/gm;
@@ -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 { isScalar, parseDocument } from "yaml";
5
+ /**
6
+ * Report a colon-bearing description only when YAML parsed it as a plain
7
+ * scalar. Quoted and block scalars may span physical lines and remain valid.
8
+ */
9
+ export function checkUnquotedDescriptionColon(frontmatterText) {
10
+ if (!frontmatterText)
11
+ return null;
12
+ const document = parseDocument(frontmatterText);
13
+ const description = document.get("description", true);
14
+ if (document.errors.length === 0 &&
15
+ isScalar(description) &&
16
+ description.type === "PLAIN" &&
17
+ typeof description.value === "string" &&
18
+ description.value.includes(":")) {
19
+ return `description value contains unquoted colon: ${description.value}`;
20
+ }
21
+ // Preserve the existing finding for malformed plain scalars that YAML cannot
22
+ // construct, while letting valid multiline quoted/block scalars pass above.
23
+ if (document.errors.length > 0) {
24
+ const line = frontmatterText.split(/\r?\n/).find((candidate) => candidate.startsWith("description:"));
25
+ const value = line?.slice("description:".length).trim();
26
+ if (value?.includes(":"))
27
+ return `description value contains unquoted colon: ${value}`;
28
+ }
29
+ return null;
30
+ }
@@ -10,6 +10,7 @@
10
10
  */
11
11
  import fs from "node:fs";
12
12
  import { parse as yamlParse, stringify as yamlStringify } from "yaml";
13
+ import { recordWrittenPath } from "../write-provenance.js";
13
14
  import { assembleAsset, serializeFrontmatter } from "./asset-serialize.js";
14
15
  /**
15
16
  * Parse YAML frontmatter from a Markdown (or similar) string.
@@ -51,15 +52,11 @@ export function parseFrontmatter(raw) {
51
52
  };
52
53
  }
53
54
  /**
54
- * Normalize YAML-parsed values to match expected AKM frontmatter types.
55
+ * Normalize YAML dates to match expected AKM frontmatter types.
55
56
  *
56
- * Two conversions:
57
- * 1. `Date` → YYYY-MM-DD string: the yaml "core" schema parses bare date
57
+ * `Date` → YYYY-MM-DD string: the yaml "core" schema parses bare date
58
58
  * scalars like `2026-06-18` as JS Date instances. AKM frontmatter treats
59
59
  * `updated:` and similar fields as plain strings.
60
- * 2. `null` → `""`: the yaml library parses empty-value keys (`key:` with no
61
- * value) as `null`, but AKM callers historically received `""` from the
62
- * hand-rolled parser. Convert to preserve backward compatibility.
63
60
  */
64
61
  function normalizeYamlValues(value) {
65
62
  if (value instanceof Date) {
@@ -68,11 +65,9 @@ function normalizeYamlValues(value) {
68
65
  const d = String(value.getUTCDate()).padStart(2, "0");
69
66
  return `${y}-${m}-${d}`;
70
67
  }
71
- if (value === null)
72
- return "";
73
68
  if (Array.isArray(value))
74
69
  return value.map(normalizeYamlValues);
75
- if (typeof value === "object") {
70
+ if (value !== null && typeof value === "object") {
76
71
  return Object.fromEntries(Object.entries(value).map(([k, v]) => [k, normalizeYamlValues(v)]));
77
72
  }
78
73
  return value;
@@ -149,6 +144,9 @@ export function mutateFrontmatter(filePath, mutator) {
149
144
  ? `---\n${serializeFrontmatter(nextFrontmatter)}\n---\n${parsed.content}`
150
145
  : assembleAsset(nextFrontmatter, parsed.content);
151
146
  fs.writeFileSync(filePath, next, "utf8");
147
+ // #652: in-place frontmatter stamps (belief state, contradiction markers,
148
+ // salience) are real asset mutations — journal them for the run's sync.
149
+ recordWrittenPath(filePath);
152
150
  return true;
153
151
  }
154
152
  export function parseFrontmatterBlock(raw) {
@@ -182,6 +180,28 @@ function countLines(text) {
182
180
  return 0;
183
181
  return text.split(/\r?\n/).length - 1;
184
182
  }
183
+ /**
184
+ * Insert one `key: value` line just before the closing `---` of an existing
185
+ * frontmatter block, leaving every other byte — YAML comments, quoting, key
186
+ * order, line endings — untouched. Returns null when `raw` has no well-formed
187
+ * block, so callers can fall back to a parse-and-serialize path.
188
+ *
189
+ * This is the source-preserving way to ADD a field to user-authored
190
+ * frontmatter: round-tripping the mapping through the YAML serializer drops
191
+ * comments and normalizes formatting, which is unacceptable for a write that
192
+ * only needs to contribute one line. Shared by `ensureAkmMarkdownType`
193
+ * (stamping `updated:` on write) and lint's `--fix` for `missing-updated`.
194
+ */
195
+ export function spliceFrontmatterLine(raw, line) {
196
+ const lines = raw.split(/\r?\n/);
197
+ if (lines[0]?.trim() !== "---")
198
+ return null;
199
+ const closeIdx = lines.findIndex((l, i) => i > 0 && l.trim() === "---");
200
+ if (closeIdx === -1)
201
+ return null;
202
+ lines.splice(closeIdx, 0, line);
203
+ return lines.join("\n");
204
+ }
185
205
  /**
186
206
  * Parse a YAML scalar value (string, boolean, or number).
187
207
  *
@@ -2,6 +2,34 @@
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 { parseFrontmatter } from "./frontmatter.js";
5
+ /** Stable GitHub-style selector for a Markdown heading. */
6
+ export function markdownHeadingSlug(heading) {
7
+ return heading
8
+ .trim()
9
+ .toLowerCase()
10
+ .replace(/<[^>]*>/g, "")
11
+ .replace(/[^\p{L}\p{N}\s_-]+/gu, "-")
12
+ .replace(/[\s_]+/g, "-")
13
+ .replace(/-+/g, "-")
14
+ .replace(/^-|-$/g, "");
15
+ }
16
+ export function markdownFragmentSlugs(content) {
17
+ return uniqueHeadingSlugs(parseMarkdownToc(content).headings).filter(Boolean);
18
+ }
19
+ function uniqueHeadingSlugs(headings) {
20
+ const used = new Set();
21
+ return headings.map((heading) => {
22
+ const base = markdownHeadingSlug(heading.text);
23
+ if (!base)
24
+ return "";
25
+ let slug = base;
26
+ let suffix = 0;
27
+ while (used.has(slug))
28
+ slug = `${base}-${++suffix}`;
29
+ used.add(slug);
30
+ return slug;
31
+ });
32
+ }
5
33
  // ── Parsing ─────────────────────────────────────────────────────────────────
6
34
  export function parseMarkdownToc(content) {
7
35
  const lines = content.split(/\r?\n/);
@@ -31,61 +59,22 @@ export function parseMarkdownToc(content) {
31
59
  // ── Extraction ──────────────────────────────────────────────────────────────
32
60
  export function extractSection(content, heading) {
33
61
  const lines = content.split(/\r?\n/);
34
- const target = heading.toLowerCase();
35
- let startIdx = -1;
36
- let startLevel = 0;
37
- for (let i = 0; i < lines.length; i++) {
38
- const match = lines[i].match(/^(#{1,6})\s+(.+)$/);
39
- if (!match)
40
- continue;
41
- const text = match[2].replace(/\s+#+\s*$/, "").trim();
42
- if (text.toLowerCase() === target && startIdx === -1) {
43
- startIdx = i;
44
- startLevel = match[1].length;
45
- }
46
- else if (startIdx !== -1 && match[1].length <= startLevel) {
47
- return {
48
- content: lines.slice(startIdx, i).join("\n"),
49
- startLine: startIdx + 1,
50
- endLine: i,
51
- };
52
- }
53
- }
54
- if (startIdx === -1)
62
+ const headings = parseMarkdownToc(content).headings;
63
+ const fragment = heading.trim();
64
+ const slugIndex = uniqueHeadingSlugs(headings).indexOf(fragment);
65
+ const exact = slugIndex < 0 ? headings.find((candidate) => candidate.text.toLowerCase() === fragment.toLowerCase()) : undefined;
66
+ const selected = slugIndex >= 0 ? headings[slugIndex] : exact;
67
+ if (!selected)
55
68
  return null;
69
+ const next = headings.find((candidate) => candidate.line > selected.line && candidate.level <= selected.level);
70
+ const startIdx = selected.line - 1;
71
+ const endIdx = next ? next.line - 1 : lines.length;
56
72
  return {
57
- content: lines.slice(startIdx).join("\n"),
58
- startLine: startIdx + 1,
59
- endLine: lines.length,
73
+ content: lines.slice(startIdx, endIdx).join("\n"),
74
+ startLine: selected.line,
75
+ endLine: endIdx,
60
76
  };
61
77
  }
62
- export function extractLineRange(content, start, end) {
63
- const lines = content.split(/\r?\n/);
64
- if (end < start)
65
- return "";
66
- const s = Math.max(1, Math.min(start, lines.length));
67
- const e = Math.min(end, lines.length);
68
- return lines.slice(s - 1, e).join("\n");
69
- }
70
- export function extractFrontmatterOnly(content) {
71
- const parsed = parseFrontmatter(content);
72
- return parsed.frontmatter;
73
- }
74
- // ── Formatting ──────────────────────────────────────────────────────────────
75
- export function formatToc(toc) {
76
- if (toc.headings.length === 0) {
77
- return `(no headings found — ${toc.totalLines} lines total)`;
78
- }
79
- const lineWidth = String(toc.totalLines).length;
80
- const parts = toc.headings.map((h) => {
81
- const lineNum = `L${String(h.line).padStart(lineWidth)}`;
82
- const indent = " ".repeat(h.level - 1);
83
- const prefix = "#".repeat(h.level);
84
- return `${lineNum} ${indent}${prefix} ${h.text}`;
85
- });
86
- parts.push(`\n${toc.totalLines} lines total`);
87
- return parts.join("\n");
88
- }
89
78
  // ── Fence stripping ──────────────────────────────────────────────────────────
90
79
  /**
91
80
  * Best-effort fence stripping. Strips `<think>` reasoning blocks emitted by
@@ -34,7 +34,7 @@
34
34
  * grammar to bridge any more.
35
35
  */
36
36
  import { NotFoundError, UsageError } from "../errors.js";
37
- import { stashDirFor, typeForStashDir } from "./asset-placement.js";
37
+ import { deriveCanonicalAssetNameFromStashRoot, placementSpecFor, stashDirFor, typeForStashDir, } from "./asset-placement.js";
38
38
  import { isBundleSlug, parseBundleRef } from "./asset-ref.js";
39
39
  /**
40
40
  * Resolve a maybe-short input ref to a fully-qualified {@link ResolvedRef}
@@ -82,10 +82,10 @@ function notFound(conceptId, triedForms, ctx) {
82
82
  return new NotFoundError(`No bundle contains concept "${conceptId}"${scope} (tried ${forms}).`, "ASSET_NOT_FOUND");
83
83
  }
84
84
  /**
85
- * D-R2 conceptId derivation from a legacy `type`/`name` pair
85
+ * D-R2 conceptId derivation from an asset `type`/`name` pair
86
86
  * (`stashDirFor(type)/name`; bare name for a foreign type with no placement
87
87
  * stash-subdir). Kept self-contained so {@link displayRef} — a PERMANENT display
88
- * rule — does not depend on the transient legacy shims.
88
+ * rule remains independent of input parsing.
89
89
  *
90
90
  * Exported (Chunk-8 WI-8.5c) as the ONE conceptId derivation the improve
91
91
  * correlation sites (`eligibility.ts` candidate refs, `salience.ts`
@@ -97,6 +97,19 @@ export function conceptIdFromTypeName(type, name) {
97
97
  const stashDir = stashDirFor(type);
98
98
  return stashDir !== undefined ? `${stashDir}/${name}` : name;
99
99
  }
100
+ /**
101
+ * User-facing conceptId for a file on disk, derived through the placement
102
+ * spec's canonical-name rule — the ONE way a diagnostic should spell a ref it
103
+ * expects the user to paste into `akm show`. (The dangerous-env-key lint used
104
+ * to hand-build `env:<base>` colon refs the parser rejects; both its emission
105
+ * sites now route through here.) For a type with no placement spec — which no
106
+ * built-in caller passes — falls back to the raw name so the output is still
107
+ * informative rather than empty.
108
+ */
109
+ export function conceptIdForStashFile(type, stashRoot, filePath) {
110
+ const name = deriveCanonicalAssetNameFromStashRoot(type, stashRoot, filePath);
111
+ return name === undefined ? filePath : conceptIdFromTypeName(type, name);
112
+ }
100
113
  /**
101
114
  * Build the USER-FACING / envelope ref string for an indexed item, applying the
102
115
  * Chunk-5 flip F4b output-spelling rule (orchestrator decision; ref-grammar
@@ -116,19 +129,23 @@ export function conceptIdFromTypeName(type, name) {
116
129
  * derived slug bundle id, never the retired `origin//type:name` spelling.
117
130
  */
118
131
  export function displayRef(item, defaultBundleId) {
119
- const conceptId = item.conceptId ?? conceptIdFromTypeName(item.type, item.name);
120
- const { bundleId } = item;
121
- // Default/primary bundle → SHORT conceptId (the flip). `"local"`/`"stash"` are
122
- // the primary-stash origin sentinels (never real bundle slugs — they name the
123
- // workspace's own stash, exactly where the pre-0.9.0 output was un-qualified),
124
- // so they display short too.
125
- if (bundleId === undefined || bundleId === defaultBundleId || bundleId === "local" || bundleId === "stash")
132
+ return displayRefForConceptId(item.conceptId ?? conceptIdFromTypeName(item.type, item.name), item.bundleId, defaultBundleId);
133
+ }
134
+ /**
135
+ * The F4b output-spelling flip itself, for a caller that already holds the
136
+ * conceptId (no `type`/`name` derivation needed — e.g. lint findings built
137
+ * from {@link conceptIdForStashFile}). {@link displayRef} delegates here, so
138
+ * the short-default / qualified-secondary rule still has exactly one home.
139
+ */
140
+ export function displayRefForConceptId(conceptId, bundleId, defaultBundleId) {
141
+ // Default/primary bundle → SHORT conceptId (the flip).
142
+ if (bundleId === undefined || bundleId === defaultBundleId)
126
143
  return conceptId;
127
144
  // Non-default bundle → the new fully-qualified `bundle//conceptId` grammar.
128
145
  return `${bundleId}//${conceptId}`;
129
146
  }
130
147
  /**
131
- * Split a qualified conceptId (`<stash-subdir>/<name>`) back to its legacy
148
+ * Split a qualified conceptId (`<stash-subdir>/<name>`) into its asset
132
149
  * `type`/`name`, or `undefined` when the leading segment is not a known stash
133
150
  * subdir (a bare-name conceptId from a foreign type — no legacy predicate
134
151
  * applies). The PERMANENT D-R2 reverse table: the input boundary uses it to map
@@ -144,6 +161,54 @@ export function typeNameFromConceptId(conceptId) {
144
161
  return undefined;
145
162
  return { type, name: conceptId.slice(slash + 1) };
146
163
  }
164
+ /**
165
+ * D11 — the opaque-adapter-conceptId fallback. `typeNameFromConceptId` only
166
+ * ever answers for the PLACEMENT_SPECS stash-resident subset (D-R2); a
167
+ * conceptId whose leading segment is NOT a registered placement stashDir is
168
+ * still perfectly legal DATA per D11 — an OKF item (`tables/customers`), a
169
+ * website page, a wiki pageKind, an adapter `instruction` doc, … — and the
170
+ * ref-consuming commands must accept it rather than treat "not an AKM
171
+ * placement dir" as "malformed ref". This function draws the line: it accepts
172
+ * any well-formed `<segment>/<rest>` conceptId (so the shape is still
173
+ * anchored — a bare no-slash name stays the caller's job to pre-qualify with a
174
+ * default type, the existing env/secret/`akm mv` "bare name" convenience), and
175
+ * REJECTS anything shaped like the retired `type:name` colon grammar (Q-02):
176
+ * a `:` in the leading segment is that grammar smuggled through a conceptId
177
+ * string (e.g. `script:db/migrate/run.sh`, `workflow:release/train`), not a
178
+ * real adapter directory name, so it is refused rather than silently
179
+ * reinterpreted as opaque data.
180
+ *
181
+ * `name` deliberately carries the FULL original conceptId, not just the tail.
182
+ * This is what makes the pair round-trip through the UNCHANGED
183
+ * {@link conceptIdFromTypeName} (`stashDirFor(type)/name`, bare `name` when
184
+ * `type` has no placement stashDir): since an opaque `type` never owns a
185
+ * placement stashDir, `conceptIdFromTypeName(type, name)` falls to its bare-
186
+ * `name` branch and returns `name` verbatim — the original conceptId,
187
+ * unchanged — instead of losing the leading segment. `type` itself carries
188
+ * the leading segment (informational/behavioral use: `.type === "lesson"`
189
+ * checks correctly miss for opaque data) UNLESS that segment happens to
190
+ * collide with a real PLACEMENT_SPECS type key that merely uses a different
191
+ * stashDir spelling (e.g. a foreign top-level dir literally named "skill",
192
+ * singular) — that pathological case would make `stashDirFor` succeed on the
193
+ * "type" and corrupt the round-trip, so it falls back to the full conceptId as
194
+ * `type` too (never a placement key, since a placement key never contains
195
+ * `/`).
196
+ *
197
+ * This intentionally does NOT collapse the KNOWN_TYPES/PLACEMENT_SPECS split:
198
+ * `typeNameFromConceptId` (PLACEMENT_SPECS only) is untouched, and an opaque
199
+ * `type` returned here is never a {@link KnownType} — it is a passthrough
200
+ * label, not a claim that AKM recognizes or owns the concept.
201
+ */
202
+ function opaqueRefParts(conceptId, allowRoot = false) {
203
+ const slash = conceptId.indexOf("/");
204
+ if (slash <= 0)
205
+ return allowRoot && !conceptId.includes(":") ? { type: conceptId, name: conceptId } : undefined;
206
+ const segment = conceptId.slice(0, slash);
207
+ if (segment.includes(":"))
208
+ return undefined; // retired `type:name` grammar, not opaque data (Q-02).
209
+ const type = placementSpecFor(segment) === undefined ? segment : conceptId;
210
+ return { type, name: conceptId };
211
+ }
147
212
  /**
148
213
  * Parse a RAW user / CLI / API ref string in the 0.9.0 `[bundle//]conceptId`
149
214
  * grammar, returning it in today's {@link AssetRef} value-object shape
@@ -154,9 +219,14 @@ export function typeNameFromConceptId(conceptId) {
154
219
  *
155
220
  * Mapping (new grammar → {@link AssetRef}):
156
221
  * - `conceptId` → `type`/`name` via {@link typeNameFromConceptId} (the D-R2
157
- * static stash-subdir table). A conceptId whose leading segment is not a
158
- * known stash subdir has no legacy `type` predicate — the same outcome an
159
- * unknown asset type produces today (a not-found).
222
+ * static stash-subdir table) when the leading segment is a known AKM
223
+ * placement stashDir, else via {@link opaqueRefParts} (D11 — the ref-parser
224
+ * seam accepts opaque adapter conceptIds, e.g. OKF items, website pages,
225
+ * wiki pageKinds, adapter `instruction` docs) when it is merely a well-
226
+ * formed but foreign `<segment>/<rest>` shape. A conceptId that is neither
227
+ * — no slash at all, or a retired colon-grammar shape smuggled through —
228
+ * has no type predicate: the same not-found outcome a genuine typo
229
+ * produces today.
160
230
  * - `bundle` → `origin`. A new-grammar bundle slug is a registryId-shaped
161
231
  * id by construction, so it flows straight into the legacy origin channel
162
232
  * that `resolveSourcesForOrigin` matches on `registryId`. The SHORT form (no
@@ -168,11 +238,11 @@ export function parseRefInput(raw) {
168
238
  if (ref.fragment !== undefined) {
169
239
  throw new UsageError(`Export fragment "#${ref.fragment}" is not accepted here — drop it from "${raw.trim()}".`, "INVALID_FLAG_VALUE");
170
240
  }
171
- const legacy = typeNameFromConceptId(ref.conceptId);
172
- if (legacy === undefined) {
241
+ const parts = typeNameFromConceptId(ref.conceptId) ?? opaqueRefParts(ref.conceptId, ref.bundle !== undefined);
242
+ if (parts === undefined) {
173
243
  throw new NotFoundError(`Unrecognized asset ref "${raw.trim()}": conceptId "${ref.conceptId}" has no known asset-type prefix.`, "ASSET_NOT_FOUND");
174
244
  }
175
- return { type: legacy.type, name: legacy.name, origin: ref.bundle };
245
+ return { type: parts.type, name: parts.name, origin: ref.bundle };
176
246
  }
177
247
  /**
178
248
  * Parse a CLI/API ref that MAY be qualified by a NON-slug origin — a registry
@@ -215,7 +285,8 @@ export function isFullRefInput(raw) {
215
285
  if (!trimmed)
216
286
  return false;
217
287
  try {
218
- return typeNameFromConceptId(parseBundleRef(trimmed).conceptId) !== undefined;
288
+ const parsed = parseBundleRef(trimmed);
289
+ return parsed.bundle !== undefined || typeNameFromConceptId(parsed.conceptId) !== undefined;
219
290
  }
220
291
  catch {
221
292
  return false;
@@ -7,7 +7,7 @@
7
7
  * A stash may carry an optional, human-authored `.meta/` directory at its
8
8
  * root holding orientation docs for the stash as a whole: purpose, key
9
9
  * assets, conventions, maintainer info. Because `.meta/` is a dot-directory,
10
- * the indexer's walker already skips it (see `src/indexer/walker.ts`), so
10
+ * the indexer's walker already skips it (see `src/indexer/walk/walker.ts`), so
11
11
  * these files never pollute the search corpus. They are surfaced on demand
12
12
  * via `akm show [<origin>//]meta[:<name>]`, which direct-reads the file
13
13
  * rather than going through the index.
@@ -0,0 +1,51 @@
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 crypto from "node:crypto";
5
+ import path from "node:path";
6
+ import { isBundleSlug } from "./asset/asset-ref.js";
7
+ /** Deterministic, filesystem-safe bundle slug from a source path. */
8
+ export function slugForPath(sourcePath) {
9
+ const resolved = path.resolve(sourcePath);
10
+ const base = path
11
+ .basename(resolved)
12
+ .toLowerCase()
13
+ .replace(/[^a-z0-9]+/g, "-")
14
+ .replace(/^-+|-+$/g, "");
15
+ if (base.length > 0)
16
+ return base;
17
+ return `bundle-${shortHash(resolved)}`;
18
+ }
19
+ /** Derive one batch-unique bundle id. */
20
+ export function deriveBundleId(registryId, sourcePath, usedIds) {
21
+ const preferred = registryId && registryId.length > 0 && isBundleSlug(registryId) ? registryId : slugForPath(sourcePath);
22
+ const id = ensureUniqueId(preferred, sourcePath, usedIds);
23
+ usedIds.add(id);
24
+ return id;
25
+ }
26
+ /** Derive an ordered batch while reserving every explicit configured bundle id. */
27
+ export function deriveBundleIds(sources) {
28
+ const usedIds = new Set();
29
+ const reservedIds = new Set(sources.flatMap((source) => (source.registryId && isBundleSlug(source.registryId) ? [source.registryId] : [])));
30
+ return sources.map((source) => {
31
+ const id = source.registryId && isBundleSlug(source.registryId)
32
+ ? deriveBundleId(source.registryId, source.path, usedIds)
33
+ : deriveBundleId(undefined, source.path, new Set([...usedIds, ...reservedIds]));
34
+ usedIds.add(id);
35
+ return id;
36
+ });
37
+ }
38
+ function ensureUniqueId(preferred, sourcePath, used) {
39
+ if (!used.has(preferred))
40
+ return preferred;
41
+ const suffixed = `${preferred}-${shortHash(path.resolve(sourcePath))}`;
42
+ if (!used.has(suffixed))
43
+ return suffixed;
44
+ let n = 2;
45
+ while (used.has(`${suffixed}-${n}`))
46
+ n++;
47
+ return `${suffixed}-${n}`;
48
+ }
49
+ function shortHash(input) {
50
+ return crypto.createHash("sha256").update(input).digest("hex").slice(0, 8);
51
+ }