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
@@ -10,13 +10,10 @@
10
10
  * them all the same. For a single sensitive value used on its own for
11
11
  * authentication (a token, key, or cert), use the `secret` type instead.
12
12
  *
13
- * Single keys can be managed with `akm env set <ref> KEY` (value read from stdin
14
- * or `--from-env`/`--from-file`, never argv) and `akm env unset <ref> KEY...`,
15
- * which do a minimal line-level edit that preserves existing comments and key
16
- * order (see `setEnvKey` / `unsetEnvKeys`). You can also just edit the `.env`
17
- * file with your own editor. Values are quoted/escaped only when necessary and
18
- * round-trip through `dotenv`; the shell-load safety guarantee still lives on
19
- * the READ path (see `buildShellExportScript` + `akm env export`).
13
+ * akm does not manage individual keys — edit the `.env` file with your own
14
+ * editor. Values are quoted/escaped only when necessary and round-trip
15
+ * through `dotenv`; the shell-load safety guarantee lives on the READ path
16
+ * (see `buildShellExportScript` + `akm env export`).
20
17
  *
21
18
  * Invariant: nothing from an env file except key NAMES may be written to
22
19
  * stdout, returned through the indexer, the `akm show` renderer, or any
@@ -27,20 +24,18 @@
27
24
  * are:
28
25
  *
29
26
  * - `akm env run <ref> -- <command>` — values injected into the child
30
- * process env (never via a shell), see `injectIntoEnv` / `loadEnv`. This is
31
- * the primary path and the only one safe for AI agents (no values ever
32
- * reach stdout). For an interactive shell, `akm env run <ref> -- $SHELL`.
27
+ * process env (never via a shell), see `loadEnv` (loaded by
28
+ * `env-binding.ts#resolveEnvBinding`, then merged into the child env by
29
+ * `env-cli.ts`). This is the primary path and the only one safe for AI
30
+ * agents (no values ever reach stdout). For an interactive shell, `akm
31
+ * env run <ref> -- $SHELL`.
33
32
  * - `akm env export <ref> --out <file>` — write parse-then-reserialized safe
34
33
  * `export KEY='value'` lines to a file (mode 0600) for `source`-ing. Values
35
34
  * are re-emitted single-quoted so a raw `.env` containing `X=$(cmd)` cannot
36
35
  * execute on load. `export` never prints values to stdout (would leak into
37
36
  * an agent's context); `path` prints only the file path.
38
37
  *
39
- * Value parsing is delegated to the `dotenv` package, and `dotenv` is also the
40
- * serialisation oracle for `env set` (`setEnvKey`): a written value is only
41
- * committed if `dotenv.parse` reads it back exactly, and the whole edit is
42
- * re-parsed to confirm no other key was disturbed. We never hand-roll a
43
- * quoting representation we cannot read back.
38
+ * Value parsing is delegated to the `dotenv` package.
44
39
  *
45
40
  * Secret-token substitution: env VALUES may embed `${secret:NAME}` tokens, which
46
41
  * are replaced at `env run` time with the value of the sibling `secret:NAME`
@@ -54,7 +49,7 @@ import fs from "node:fs";
54
49
  import path from "node:path";
55
50
  import dotenv from "dotenv";
56
51
  import { writeFileAtomic } from "../../core/common.js";
57
- import { UsageError } from "../../core/errors.js";
52
+ import { sensitiveMarkerPath } from "./marker-path.js";
58
53
  /** Matches a KEY=value assignment line, capturing only the key. */
59
54
  const ASSIGN_RE = /^\s*(?:export\s+)?([A-Za-z_][A-Za-z0-9_]*)\s*=/;
60
55
  /** Scan lines and return KEY names in file order, without duplicates. */
@@ -101,21 +96,6 @@ export function loadEnv(envPath) {
101
96
  const buf = fs.readFileSync(envPath);
102
97
  return dotenv.parse(buf);
103
98
  }
104
- /**
105
- * Load an env file and assign its values into `target` (defaults to
106
- * `process.env`). Returns the list of keys that were set so the caller can
107
- * log/observe without touching values.
108
- *
109
- * Existing keys in `target` are overwritten — callers who want to preserve
110
- * pre-existing environment variables should filter before calling.
111
- */
112
- export function injectIntoEnv(envPath, target = process.env) {
113
- const env = loadEnv(envPath);
114
- for (const [key, value] of Object.entries(env)) {
115
- target[key] = value;
116
- }
117
- return Object.keys(env);
118
- }
119
99
  /**
120
100
  * Serialise an env file's values as a POSIX shell script of `export KEY='value'`
121
101
  * lines, with single-quote escaping (`'\''`). Every line is an assignment of a
@@ -214,142 +194,11 @@ export function removeEnv(envPath) {
214
194
  if (!fs.existsSync(envPath))
215
195
  return false;
216
196
  fs.rmSync(envPath);
217
- const marker = `${envPath}.sensitive`;
197
+ const marker = sensitiveMarkerPath(envPath, "env");
218
198
  if (fs.existsSync(marker))
219
199
  fs.rmSync(marker);
220
200
  return true;
221
201
  }
222
- /** A valid env KEY name (same grammar as the assignment scanner). */
223
- export const ENV_KEY_RE = /^[A-Za-z_][A-Za-z0-9_]*$/;
224
- /**
225
- * Build a `KEY=value` assignment line whose value is GUARANTEED to round-trip
226
- * through `dotenv.parse` — dotenv is the serialisation oracle, so we never
227
- * write a representation we cannot read back. Candidate representations are
228
- * tried in order of readability (bare → double-quoted → single-quoted) and the
229
- * first one `dotenv.parse` recovers exactly is used. If a value contains
230
- * characters no inline representation can round-trip (e.g. both quote styles),
231
- * we throw rather than silently corrupt the file.
232
- */
233
- function serializeEnvAssignment(key, value) {
234
- const candidates = [];
235
- // Bare — only for simple values (no whitespace/quotes/#/$/control chars).
236
- if (/^[A-Za-z0-9_@%+=:,./-]*$/.test(value))
237
- candidates.push(`${key}=${value}`);
238
- // Double-quoted — dotenv expands \n \r \t inside double quotes.
239
- const dq = value
240
- .replace(/\\/g, "\\\\")
241
- .replace(/"/g, '\\"')
242
- .replace(/\n/g, "\\n")
243
- .replace(/\r/g, "\\r")
244
- .replace(/\t/g, "\\t");
245
- candidates.push(`${key}="${dq}"`);
246
- // Single-quoted — dotenv takes the content literally (no escapes/expansion).
247
- candidates.push(`${key}='${value}'`);
248
- for (const line of candidates) {
249
- try {
250
- if (dotenv.parse(line)[key] === value)
251
- return line;
252
- }
253
- catch {
254
- // Not parseable as written; try the next representation.
255
- }
256
- }
257
- throw new UsageError(`Value for "${key}" cannot be stored inline in a .env file (it contains characters dotenv cannot round-trip). ` +
258
- "Edit the .env file directly, or choose a different value.");
259
- }
260
- /**
261
- * Assert (using `dotenv.parse` as the oracle) that `after` set `key` to
262
- * `expected` and left every other key from `before` byte-identical. This
263
- * catches a line-level edit accidentally disturbing a quoted/multiline value.
264
- */
265
- function assertEnvEditSafe(before, after, key) {
266
- for (const [k, v] of Object.entries(before)) {
267
- if (k !== key && after[k] !== v) {
268
- throw new UsageError(`Editing "${key}" would disturb "${k}" (the .env file has a value layout dotenv could not safely round-trip). ` +
269
- "Edit the .env file directly.");
270
- }
271
- }
272
- }
273
- /**
274
- * Set (create or update) a single `KEY=value` entry in an env file, preserving
275
- * the file's existing lines, comments, and key order. The first existing
276
- * assignment of `key` is replaced in place; otherwise the entry is appended.
277
- * Creates the file (and parent dirs) if absent. The value is never logged.
278
- *
279
- * The serialised value and the whole resulting file are verified with
280
- * `dotenv.parse` before the write commits.
281
- */
282
- export function setEnvKey(envPath, key, value) {
283
- ensureParentDir(envPath);
284
- const existing = fs.existsSync(envPath) ? fs.readFileSync(envPath, "utf8") : "";
285
- const assignment = serializeEnvAssignment(key, value);
286
- const keyLineRe = new RegExp(`^\\s*(?:export\\s+)?${key}\\s*=`);
287
- const lines = existing.split(/\r?\n/);
288
- let replaced = false;
289
- const out = lines.map((line) => {
290
- if (!replaced && keyLineRe.test(line)) {
291
- replaced = true;
292
- return assignment;
293
- }
294
- return line;
295
- });
296
- if (!replaced) {
297
- while (out.length > 0 && out[out.length - 1] === "")
298
- out.pop();
299
- out.push(assignment);
300
- }
301
- let content = out.join("\n");
302
- if (!content.endsWith("\n"))
303
- content += "\n";
304
- // Verify the edit with dotenv before committing it.
305
- const after = dotenv.parse(content);
306
- if (after[key] !== value) {
307
- throw new UsageError(`Could not set "${key}" reliably (the .env file has a value layout dotenv could not round-trip). ` +
308
- "Edit the .env file directly.");
309
- }
310
- assertEnvEditSafe(dotenv.parse(existing), after, key);
311
- writeFileAtomic(envPath, content, 0o600);
312
- }
313
- /**
314
- * Remove one or more `KEY=value` entries from an env file, preserving all other
315
- * lines and comments. Returns which keys were present (removed) vs. absent.
316
- *
317
- * The result is verified with `dotenv.parse`: the removed keys are gone and
318
- * every surviving key is byte-identical to before.
319
- */
320
- export function unsetEnvKeys(envPath, keys) {
321
- if (!fs.existsSync(envPath))
322
- return { removed: [], missing: keys };
323
- const text = fs.readFileSync(envPath, "utf8");
324
- const before = dotenv.parse(text);
325
- const present = new Set(Object.keys(before));
326
- const toRemove = new Set(keys);
327
- const out = text.split(/\r?\n/).filter((line) => {
328
- const m = line.match(ASSIGN_RE);
329
- return !(m && toRemove.has(m[1]));
330
- });
331
- let content = out.join("\n");
332
- if (content.length > 0 && !content.endsWith("\n"))
333
- content += "\n";
334
- // Verify with dotenv: removed keys gone, survivors unchanged.
335
- const after = dotenv.parse(content);
336
- for (const k of toRemove) {
337
- if (k in after) {
338
- throw new UsageError(`Could not remove "${k}" reliably (the .env file has a value layout dotenv could not round-trip). ` +
339
- "Edit the .env file directly.");
340
- }
341
- }
342
- for (const [k, v] of Object.entries(before)) {
343
- if (!toRemove.has(k) && after[k] !== v) {
344
- throw new UsageError(`Removing those keys would disturb "${k}" (multiline/quoted value). Edit the .env file directly.`);
345
- }
346
- }
347
- writeFileAtomic(envPath, content, 0o600);
348
- return {
349
- removed: keys.filter((k) => present.has(k)),
350
- missing: keys.filter((k) => !present.has(k)),
351
- };
352
- }
353
202
  function ensureParentDir(filePath) {
354
203
  const dir = path.dirname(filePath);
355
204
  if (!fs.existsSync(dir))
@@ -0,0 +1,6 @@
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 function sensitiveMarkerPath(assetPath, type) {
5
+ return type === "env" ? assetPath.replace(/\.env$/, ".sensitive") : `${assetPath}.sensitive`;
6
+ }
@@ -11,23 +11,36 @@
11
11
  *
12
12
  * `akm secret` manages whole-file secrets under each stash's secrets/ directory.
13
13
  * Unlike env files (.env key/value), the ENTIRE file is the secret value. The bytes
14
- * are NEVER written to stdout or structured output. Values reach a command only
15
- * via `akm secret run` (injected into a child env var) or `akm secret path`
16
- * (the Docker /run/secrets + `_FILE` convention).
14
+ * are NEVER written to stdout or structured output. The only value-use path is
15
+ * `akm secret run` (injected into a child env var).
16
+ *
17
+ * `secret path` and `secret remove` were REMOVED in 0.9.0 (R-027 / D-49): an
18
+ * audit found `path` resolved the ref through the read-side, all-sources
19
+ * resolver while `remove` resolved it through the write-target resolver, so
20
+ * `akm secret path <ref>` and `akm secret remove <ref>` could silently name
21
+ * two DIFFERENT files when a ref existed in more than one stash — inspect one,
22
+ * delete the other. The owner's ruling was to drop both subcommands rather
23
+ * than reconcile the resolvers. A ref's file lives at `<bundle>/secrets/<name>`
24
+ * (matching the ref exactly, e.g. `secrets/deploy-key` -> `secrets/deploy-key`
25
+ * under a bundle root — `akm bundle list` prints each configured bundle's root
26
+ * path); locate or delete it directly, or consume its value without touching
27
+ * disk via `akm secret run <ref> <VAR> -- <command>`.
17
28
  */
18
29
  import { spawnSync } from "node:child_process";
19
30
  import fs from "node:fs";
20
31
  import path from "node:path";
32
+ import { getParsedInvocation } from "../../cli/invocation.js";
21
33
  import { getStringArg } from "../../cli/parse-args.js";
22
34
  import { defineGroupCommand, defineJsonCommand, output } from "../../cli/shared.js";
23
- import { deriveCanonicalAssetName } from "../../core/asset/asset-spec.js";
35
+ import { deriveCanonicalAssetName } from "../../core/asset/asset-placement.js";
24
36
  import { loadConfig } from "../../core/config/config.js";
25
- import { makeSecretRef, resolveSecretPath } from "../../core/env-secret-ref.js";
37
+ import { makeSecretRef, resolveSecretPath, resolveSecretWriteTarget, withEnvSecretWrite, } from "../../core/env-secret-ref.js";
26
38
  import { ConfigError, NotFoundError, UsageError } from "../../core/errors.js";
27
39
  import { appendEvent } from "../../core/events.js";
28
40
  import { resolveSourceEntries } from "../../indexer/search/search-source.js";
29
41
  import { readStdin } from "../../runtime.js";
30
42
  import { buildChildEnv } from "./child-env.js";
43
+ import { sensitiveMarkerPath } from "./marker-path.js";
31
44
  function parseKeyListFlag(raw) {
32
45
  if (raw === undefined)
33
46
  return undefined;
@@ -56,7 +69,7 @@ function listSecretsRecursive() {
56
69
  if (entry.name.endsWith(".lock") || entry.name.endsWith(".sensitive"))
57
70
  continue;
58
71
  // A sibling `<name>.sensitive` marker suppresses listing.
59
- if (fs.existsSync(`${full}.sensitive`))
72
+ if (fs.existsSync(sensitiveMarkerPath(full, "secret")))
60
73
  continue;
61
74
  const canonical = deriveCanonicalAssetName("secret", secretsDir, full);
62
75
  if (!canonical)
@@ -71,7 +84,7 @@ function listSecretsRecursive() {
71
84
  const secretListCommand = defineJsonCommand({
72
85
  meta: {
73
86
  name: "list",
74
- description: "List all secrets across all stashes by name (the file contents are never shown)",
87
+ description: "List all secrets across all bundles by name (the file contents are never shown)",
75
88
  },
76
89
  async run() {
77
90
  output("secret-list", { secrets: listSecretsRecursive() });
@@ -85,7 +98,7 @@ const secretSetCommand = defineJsonCommand({
85
98
  args: {
86
99
  ref: {
87
100
  type: "positional",
88
- description: "Secret ref (flat name, e.g. secret:deploy-key or just deploy-key; use --path for a subdirectory)",
101
+ description: "Secret ref (flat name, e.g. secrets/deploy-key or just deploy-key; use --path for a subdirectory)",
89
102
  required: true,
90
103
  },
91
104
  path: {
@@ -94,10 +107,16 @@ const secretSetCommand = defineJsonCommand({
94
107
  },
95
108
  "from-file": { type: "string", description: "Read the value from this file (stored byte-exact)" },
96
109
  "from-env": { type: "string", description: "Read the value from the named environment variable" },
110
+ target: {
111
+ type: "string",
112
+ description: "Override the write destination. Accepts a source name from your config; falls back to defaultWriteTarget then the working bundle.",
113
+ },
97
114
  },
98
115
  async run({ args }) {
99
116
  const { setSecret } = await import("./secret.js");
100
- const { name, absPath, source } = resolveSecretPath(args.ref, { subPath: getStringArg(args, "path") });
117
+ const { name, absPath, target, ref } = resolveSecretWriteTarget(args.ref, args.target, {
118
+ subPath: getStringArg(args, "path"),
119
+ });
101
120
  const fromEnv = args["from-env"];
102
121
  const fromFile = args["from-file"];
103
122
  if (fromEnv !== undefined && fromFile !== undefined) {
@@ -131,24 +150,8 @@ const secretSetCommand = defineJsonCommand({
131
150
  // byte-exact storage of multi-line material (PEM keys, certs).
132
151
  value = Buffer.from(stdinBuf.toString("utf8").replace(/\n$/, ""), "utf8");
133
152
  }
134
- setSecret(absPath, value);
135
- output("secret-set", { ref: makeSecretRef(name, source) });
136
- },
137
- });
138
- const secretPathCommand = defineJsonCommand({
139
- meta: {
140
- name: "path",
141
- description: "Print the absolute secret file path for the Docker `_FILE` convention, e.g. `MY_SECRET_FILE=$(akm secret path secret:deploy-key)`.",
142
- },
143
- args: {
144
- ref: { type: "positional", description: "Secret ref", required: true },
145
- },
146
- async run({ args }) {
147
- const { name, absPath, source } = resolveSecretPath(args.ref);
148
- if (!fs.existsSync(absPath)) {
149
- throw new NotFoundError(`Secret not found: ${makeSecretRef(name, source)}`);
150
- }
151
- process.stdout.write(`${absPath}\n`);
153
+ withEnvSecretWrite(target, { type: "secret", name }, "Update", [absPath], () => setSecret(absPath, value));
154
+ output("secret-set", { ref });
152
155
  },
153
156
  });
154
157
  const secretRunCommand = defineJsonCommand({
@@ -181,11 +184,10 @@ const secretRunCommand = defineJsonCommand({
181
184
  if (isDangerousEnvKey(varName)) {
182
185
  throw new UsageError(`Refusing to inject a secret into "${varName}": it is a known process-hijacking variable (e.g. LD_PRELOAD, PATH).`, "INVALID_FLAG_VALUE");
183
186
  }
184
- const dashIndex = process.argv.indexOf("--");
185
- if (dashIndex < 0 || dashIndex === process.argv.length - 1) {
187
+ const command = getParsedInvocation().passthroughArgs();
188
+ if (command.length === 0) {
186
189
  throw new UsageError("Missing command. Usage: akm secret run <ref> <VAR> -- <command>");
187
190
  }
188
- const command = process.argv.slice(dashIndex + 1);
189
191
  const { name, absPath, source } = resolveSecretPath(args.ref);
190
192
  if (!fs.existsSync(absPath)) {
191
193
  throw new NotFoundError(`Secret not found: ${makeSecretRef(name, source)}`);
@@ -216,46 +218,28 @@ const secretRunCommand = defineJsonCommand({
216
218
  }
217
219
  throw err;
218
220
  }
219
- process.exit(result.status ?? 0);
220
- },
221
- });
222
- const secretRemoveCommand = defineJsonCommand({
223
- meta: { name: "remove", description: "Remove a secret (and its .sensitive marker, if any)" },
224
- args: {
225
- ref: { type: "positional", description: "Secret ref", required: true },
226
- yes: { type: "boolean", alias: "y", description: "Skip confirmation prompt", default: false },
227
- },
228
- async run({ args }) {
229
- const { name, absPath, source } = resolveSecretPath(args.ref);
230
- const { confirmDestructive } = await import("../../cli/confirm.js");
231
- const confirmed = await confirmDestructive(`Remove secret "${args.ref}"? This cannot be undone.`, {
232
- yes: args.yes === true,
233
- });
234
- if (!confirmed) {
235
- process.stderr.write("Aborted.\n");
236
- return;
237
- }
238
- const { removeSecret } = await import("./secret.js");
239
- if (!fs.existsSync(absPath)) {
240
- throw new NotFoundError(`Secret not found: ${makeSecretRef(name, source)}`);
241
- }
242
- const removed = removeSecret(absPath);
243
- output("secret-remove", { ref: makeSecretRef(name, source), removed });
221
+ // R-067: was `process.exit(result.status ?? 0)`, unconditional even on
222
+ // the success path — it skipped the `finally { await
223
+ // disposeDispatchResources(); }` cleanup in src/cli.ts's `runCommand`.
224
+ // Setting `process.exitCode` and returning still exits with the child's
225
+ // exact status once the event loop drains, but lets cleanup run first —
226
+ // same pattern `emitJsonError` (src/cli/shared.ts) already established.
227
+ process.exitCode = result.status ?? 0;
228
+ return;
244
229
  },
245
230
  });
246
231
  export const secretCommand = defineGroupCommand({
247
232
  meta: {
248
233
  name: "secret",
249
- description: "Manage secrets — a single sensitive value used on its own for authentication (an API token, a PEM private key, a TLS cert), one value per file. Names are visible; the file contents are the value and never appear in structured output. For a group of related configuration loaded together, use `akm env`.",
234
+ description: "Manage secrets — one standalone sensitive value per file (an API token, a PEM private key, a TLS cert).\n\n" +
235
+ "Names are visible; the file contents are the value and never appear in structured output. For a group of related configuration loaded together, use `akm env`. A secret's file lives at `<bundle>/secrets/<name>` (`akm bundle list` shows bundle roots) — read or delete it there directly, or consume its value without writing it anywhere via `akm secret run <ref> <VAR> -- <command>`.",
250
236
  },
251
237
  subCommands: {
252
238
  list: secretListCommand,
253
- path: secretPathCommand,
254
239
  run: secretRunCommand,
255
240
  set: secretSetCommand,
256
- remove: secretRemoveCommand,
257
- },
258
- defaultRun() {
259
- output("secret-list", { secrets: listSecretsRecursive() });
260
241
  },
242
+ // No `defaultRun`: bare `akm secret` is a usage error (exit 2), the canonical
243
+ // bare-group behavior — owner ruling 12. Run `akm secret list` for what the
244
+ // bare form used to print.
261
245
  });
@@ -14,20 +14,26 @@
14
14
  *
15
15
  * Invariant: a secret's bytes must never be written to stdout, returned
16
16
  * through the indexer / `akm show` renderer, or any structured output channel.
17
- * The supported value-use paths are:
17
+ * The supported value-use path is:
18
18
  *
19
19
  * - `akm secret run <ref> <VAR> -- <cmd>` — value injected into the child
20
20
  * process env as `VAR=<value>` (see `readValue`).
21
- * - `akm secret path <ref>` — print the file path so a command can read it
22
- * itself (Docker `/run/secrets` + `_FILE` convention).
21
+ *
22
+ * `akm secret path <ref>` (print the file path for the Docker `/run/secrets` +
23
+ * `_FILE` convention) was removed in 0.9.0 alongside `akm secret remove` — an
24
+ * audit found the two resolved a ref through different stash-selection logic
25
+ * and could silently target different files. A ref's file still lives at
26
+ * `<stash>/secrets/<name>`; locate or delete it there directly.
23
27
  *
24
28
  * Values are stored as raw bytes (no quoting, multi-line allowed) so they
25
29
  * round-trip byte-exact, unlike env values which forbid literal newlines.
26
30
  */
31
+ import { createHash } from "node:crypto";
27
32
  import fs from "node:fs";
28
33
  import path from "node:path";
29
- import { writeFileAtomic } from "../../core/common.js";
30
- import { probeLock, releaseLock, tryAcquireLockSync } from "../../core/file-lock.js";
34
+ import { safeRealpath, writeFileAtomic } from "../../core/common.js";
35
+ import { createLockPayload, probeLock, reclaimStaleLock, releaseLock, tryAcquireLockSync, } from "../../core/file-lock.js";
36
+ import { getDataDir } from "../../core/paths.js";
31
37
  import { sleepSync } from "../../runtime.js";
32
38
  // ── Write-lock helper ─────────────────────────────────────────────────────────
33
39
  /**
@@ -37,19 +43,22 @@ import { sleepSync } from "../../runtime.js";
37
43
  * throw rather than silently proceeding.
38
44
  */
39
45
  export function withSecretLock(secretPath, fn) {
40
- const lockPath = `${secretPath}.lock`;
46
+ const lockPath = secretLockPath(secretPath);
47
+ const lockDir = path.dirname(lockPath);
48
+ fs.mkdirSync(lockDir, { recursive: true, mode: 0o700 });
41
49
  const deadline = Date.now() + 5000;
42
- while (!tryAcquireLockSync(lockPath, String(process.pid))) {
50
+ let ownership;
51
+ while (!ownership) {
52
+ ownership = tryAcquireLockSync(lockPath, createLockPayload());
53
+ if (ownership)
54
+ break;
43
55
  const probe = probeLock(lockPath);
44
- if (probe.state === "stale") {
45
- releaseLock(lockPath);
56
+ if (probe.state === "stale" && reclaimStaleLock(lockPath, probe)) {
46
57
  continue;
47
58
  }
48
59
  if (Date.now() > deadline) {
49
- const holderHint = probe.state === "held"
50
- ? ` Lock file ${lockPath} is held by live PID ${probe.holderPid}.`
51
- : ` Lock file ${lockPath} could not be inspected.`;
52
- throw new Error(`Could not acquire secret lock for ${secretPath} after 5s.${holderHint} Retry once any other akm secret operation finishes, or remove the stale lock file.`);
60
+ const holderHint = probe.state === "held" ? ` It is held by live PID ${probe.holderPid}.` : "";
61
+ throw new Error(`Could not acquire the secret write lock after 5s.${holderHint} Retry once any other akm secret operation finishes.`);
53
62
  }
54
63
  sleepSync(10);
55
64
  }
@@ -57,9 +66,15 @@ export function withSecretLock(secretPath, fn) {
57
66
  return fn();
58
67
  }
59
68
  finally {
60
- releaseLock(lockPath);
69
+ releaseLock(ownership);
61
70
  }
62
71
  }
72
+ export function secretLockPath(secretPath, platform = process.platform) {
73
+ const canonical = safeRealpath(secretPath);
74
+ const identity = platform === "win32" ? canonical.toLowerCase() : canonical;
75
+ const key = createHash("sha256").update(identity).digest("hex");
76
+ return path.join(getDataDir(), "secret-locks", `${key}.lock`);
77
+ }
63
78
  // ── Atomic byte write ──────────────────────────────────────────────────────────
64
79
  function ensureParentDir(filePath) {
65
80
  const dir = path.dirname(filePath);
@@ -68,38 +83,8 @@ function ensureParentDir(filePath) {
68
83
  }
69
84
  // ── Public API ──────────────────────────────────────────────────────────────
70
85
  /**
71
- * Walk a `secrets/` directory and return the POSIX-relative names of every
72
- * secret file. Lock files (`*.lock`), sensitive markers (`*.sensitive`), and
73
- * secrets with a sibling `<name>.sensitive` marker are excluded. The file
74
- * bodies are NEVER read.
75
- */
76
- export function listNames(secretsRoot) {
77
- if (!fs.existsSync(secretsRoot))
78
- return [];
79
- const names = [];
80
- const walk = (dir) => {
81
- for (const entry of fs.readdirSync(dir, { withFileTypes: true })) {
82
- const full = path.join(dir, entry.name);
83
- if (entry.isDirectory()) {
84
- walk(full);
85
- continue;
86
- }
87
- if (!entry.isFile())
88
- continue;
89
- if (entry.name.endsWith(".lock") || entry.name.endsWith(".sensitive"))
90
- continue;
91
- // A sibling `<name>.sensitive` marker suppresses listing.
92
- if (fs.existsSync(`${full}.sensitive`))
93
- continue;
94
- names.push(path.relative(secretsRoot, full).split(path.sep).join("/"));
95
- }
96
- };
97
- walk(secretsRoot);
98
- return names.sort();
99
- }
100
- /**
101
- * Read a secret's raw bytes. Internal use only (for `secret run` / `secret
102
- * path`). Callers MUST NOT write the returned value to stdout or any log.
86
+ * Read a secret's raw bytes. Internal use only (for `secret run`). Callers
87
+ * MUST NOT write the returned value to stdout or any log.
103
88
  */
104
89
  export function readValue(secretPath) {
105
90
  return fs.readFileSync(secretPath);
@@ -109,24 +94,9 @@ export function readValue(secretPath) {
109
94
  * mode 0600 under a write-lock. No quoting; multi-line / binary allowed.
110
95
  */
111
96
  export function setSecret(secretPath, value) {
112
- ensureParentDir(secretPath);
113
97
  withSecretLock(secretPath, () => {
98
+ ensureParentDir(secretPath);
114
99
  // Mode 0600: secrets must never be world-readable, even transiently.
115
100
  writeFileAtomic(secretPath, value, 0o600);
116
101
  });
117
102
  }
118
- /**
119
- * Remove a secret file (and its `.sensitive` marker, if present). Returns true
120
- * if the secret existed.
121
- */
122
- export function removeSecret(secretPath) {
123
- return withSecretLock(secretPath, () => {
124
- if (!fs.existsSync(secretPath))
125
- return false;
126
- fs.rmSync(secretPath);
127
- const marker = `${secretPath}.sensitive`;
128
- if (fs.existsSync(marker))
129
- fs.rmSync(marker);
130
- return true;
131
- });
132
- }