akm-cli 0.9.0-beta.9 → 0.9.0-rc.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 (381) hide show
  1. package/CHANGELOG.md +715 -0
  2. package/README.md +12 -4
  3. package/dist/akm +38 -0
  4. package/dist/akm-migrate-storage +38 -0
  5. package/dist/assets/help/help-improve.md +9 -6
  6. package/dist/assets/hints/cli-hints-full.md +6 -5
  7. package/dist/assets/profiles/default.json +9 -4
  8. package/dist/assets/profiles/frequent.json +1 -1
  9. package/dist/assets/profiles/memory-focus.json +1 -1
  10. package/dist/assets/profiles/proactive-maintenance.json +25 -0
  11. package/dist/assets/profiles/quick.json +1 -1
  12. package/dist/assets/profiles/recombine-only.json +21 -0
  13. package/dist/assets/profiles/reflect-distill.json +30 -0
  14. package/dist/assets/profiles/synthesize.json +15 -0
  15. package/dist/assets/profiles/thorough.json +1 -1
  16. package/dist/assets/prompts/consolidate-system.md +23 -0
  17. package/dist/assets/prompts/contradiction-judge.md +33 -0
  18. package/dist/assets/prompts/distill-knowledge-system.md +22 -0
  19. package/dist/assets/prompts/distill-lesson-system.md +36 -0
  20. package/dist/assets/prompts/extract-session.md +11 -3
  21. package/dist/assets/prompts/graph-extract-system.md +1 -0
  22. package/dist/assets/prompts/graph-extract-user-prompt.md +1 -1
  23. package/dist/assets/prompts/memory-infer-system.md +1 -0
  24. package/dist/assets/prompts/memory-infer-user.md +5 -0
  25. package/dist/assets/prompts/metadata-enhance-system.md +1 -0
  26. package/dist/assets/prompts/procedural-system.md +44 -0
  27. package/dist/assets/prompts/recombine-system.md +40 -0
  28. package/dist/assets/prompts/staleness-detect-system.md +6 -0
  29. package/dist/assets/prompts/validate-summary-judge.md +1 -0
  30. package/dist/assets/prompts/workflow-unit-preamble.md +26 -0
  31. package/dist/assets/stash-skeleton/facts/conventions/assets/agent.md +38 -0
  32. package/dist/assets/stash-skeleton/facts/conventions/assets/command.md +38 -0
  33. package/dist/assets/stash-skeleton/facts/conventions/assets/fact.md +39 -0
  34. package/dist/assets/stash-skeleton/facts/conventions/assets/knowledge.md +40 -0
  35. package/dist/assets/stash-skeleton/facts/conventions/assets/lesson.md +43 -0
  36. package/dist/assets/stash-skeleton/facts/conventions/assets/memory.md +38 -0
  37. package/dist/assets/stash-skeleton/facts/conventions/assets/script.md +43 -0
  38. package/dist/assets/stash-skeleton/facts/conventions/assets/skill.md +40 -0
  39. package/dist/assets/stash-skeleton/facts/conventions/assets/workflow.md +43 -0
  40. package/dist/assets/templates/html/health.html +281 -111
  41. package/dist/assets/wiki/ingest-workflow-template.md +45 -16
  42. package/dist/assets/wiki/schema-template.md +4 -4
  43. package/dist/cli/clack.js +56 -0
  44. package/dist/cli/config-migrate.js +7 -1
  45. package/dist/cli/confirm.js +1 -1
  46. package/dist/cli/parse-args.js +46 -1
  47. package/dist/cli/shared.js +28 -0
  48. package/dist/cli.js +25 -21
  49. package/dist/commands/agent/agent-dispatch.js +3 -2
  50. package/dist/commands/agent/agent-support.js +0 -7
  51. package/dist/commands/agent/contribute-cli.js +26 -7
  52. package/dist/commands/config-cli.js +26 -13
  53. package/dist/commands/env/child-env.js +47 -0
  54. package/dist/commands/env/env-binding.js +95 -0
  55. package/dist/commands/env/env-cli.js +228 -292
  56. package/dist/commands/env/env.js +14 -67
  57. package/dist/commands/env/secret-cli.js +140 -138
  58. package/dist/commands/feedback-cli.js +156 -155
  59. package/dist/commands/graph/graph-cli.js +5 -13
  60. package/dist/commands/graph/graph.js +3 -3
  61. package/dist/commands/health/advisories.js +151 -0
  62. package/dist/commands/health/checks.js +103 -16
  63. package/dist/commands/health/html-report.js +447 -81
  64. package/dist/commands/health/improve-metrics.js +771 -0
  65. package/dist/commands/health/llm-usage.js +65 -0
  66. package/dist/commands/health/md-report.js +103 -0
  67. package/dist/commands/health/metrics.js +278 -0
  68. package/dist/commands/health/stash-exposure.js +46 -0
  69. package/dist/commands/health/surfaces.js +216 -0
  70. package/dist/commands/health/task-runs.js +135 -0
  71. package/dist/commands/health/types.js +26 -0
  72. package/dist/commands/health/windows.js +195 -0
  73. package/dist/commands/health.js +91 -1091
  74. package/dist/commands/improve/anti-collapse.js +170 -0
  75. package/dist/commands/improve/calibration.js +161 -0
  76. package/dist/commands/improve/collapse-detector.js +421 -0
  77. package/dist/commands/improve/consolidate/chunking.js +141 -0
  78. package/dist/commands/improve/consolidate/eligibility.js +64 -0
  79. package/dist/commands/improve/consolidate/merge.js +145 -0
  80. package/dist/commands/improve/consolidate/sanitize.js +231 -0
  81. package/dist/commands/{lint.js → improve/consolidate/types.js} +1 -1
  82. package/dist/commands/improve/consolidate.js +1295 -1277
  83. package/dist/commands/improve/dedup.js +482 -0
  84. package/dist/commands/improve/distill/content-repair.js +202 -0
  85. package/dist/commands/improve/distill/promote-memory.js +229 -0
  86. package/dist/commands/improve/distill/quality-gate.js +236 -0
  87. package/dist/commands/improve/distill-guards.js +127 -0
  88. package/dist/commands/improve/distill-promotion-policy.js +826 -167
  89. package/dist/commands/improve/distill.js +228 -605
  90. package/dist/commands/improve/eligibility.js +434 -0
  91. package/dist/commands/improve/encoding-salience.js +205 -0
  92. package/dist/commands/improve/extract-cli.js +179 -59
  93. package/dist/commands/improve/extract-prompt.js +54 -3
  94. package/dist/commands/improve/extract-watch.js +140 -0
  95. package/dist/commands/improve/extract.js +409 -43
  96. package/dist/commands/improve/feedback-valence.js +54 -0
  97. package/dist/commands/improve/hot-probation.js +45 -0
  98. package/dist/commands/improve/improve-auto-accept.js +157 -10
  99. package/dist/commands/improve/improve-cli.js +115 -73
  100. package/dist/commands/improve/improve-profiles.js +28 -8
  101. package/dist/commands/improve/improve-result-file.js +15 -25
  102. package/dist/commands/improve/improve-session.js +58 -0
  103. package/dist/commands/improve/improve.js +485 -2764
  104. package/dist/commands/improve/locks.js +154 -0
  105. package/dist/commands/improve/loop-stages.js +1100 -0
  106. package/dist/commands/improve/memory/memory-belief.js +14 -15
  107. package/dist/commands/improve/memory/memory-contradiction-detect.js +83 -60
  108. package/dist/commands/improve/memory/memory-improve.js +27 -27
  109. package/dist/commands/improve/outcome-loop.js +270 -0
  110. package/dist/commands/improve/preparation.js +2002 -0
  111. package/dist/commands/improve/proactive-maintenance.js +37 -35
  112. package/dist/commands/improve/procedural.js +398 -0
  113. package/dist/commands/improve/recombine.js +818 -0
  114. package/dist/commands/improve/reflect-noise.js +0 -0
  115. package/dist/commands/improve/reflect.js +206 -45
  116. package/dist/commands/improve/salience.js +455 -0
  117. package/dist/commands/improve/schema-similarity-gate.js +168 -0
  118. package/dist/commands/improve/shared.js +51 -0
  119. package/dist/commands/improve/triage.js +93 -0
  120. package/dist/commands/lint/agent-linter.js +19 -24
  121. package/dist/commands/lint/base-linter.js +173 -60
  122. package/dist/commands/lint/command-linter.js +19 -24
  123. package/dist/commands/lint/env-key-rules.js +38 -1
  124. package/dist/commands/lint/fact-linter.js +39 -0
  125. package/dist/commands/lint/index.js +31 -13
  126. package/dist/commands/lint/memory-linter.js +1 -1
  127. package/dist/commands/lint/registry.js +7 -2
  128. package/dist/commands/lint/task-linter.js +3 -3
  129. package/dist/commands/lint/workflow-linter.js +26 -1
  130. package/dist/commands/observability-cli.js +4 -4
  131. package/dist/commands/proposal/drain-policies.js +13 -4
  132. package/dist/commands/proposal/drain.js +45 -51
  133. package/dist/commands/proposal/legacy-import.js +115 -0
  134. package/dist/commands/proposal/proposal-cli.js +24 -34
  135. package/dist/commands/proposal/proposal.js +2 -1
  136. package/dist/commands/proposal/propose.js +8 -3
  137. package/dist/commands/proposal/repository.js +829 -0
  138. package/dist/commands/proposal/validators/proposal-quality-validators.js +9 -8
  139. package/dist/commands/proposal/validators/proposals.js +93 -895
  140. package/dist/commands/read/curate.js +410 -111
  141. package/dist/commands/read/knowledge.js +10 -3
  142. package/dist/commands/read/remember-cli.js +133 -138
  143. package/dist/commands/read/search-cli.js +15 -8
  144. package/dist/commands/read/search.js +22 -11
  145. package/dist/commands/read/show.js +106 -14
  146. package/dist/commands/registry-cli.js +76 -87
  147. package/dist/commands/remember.js +11 -12
  148. package/dist/commands/sources/add-cli.js +91 -95
  149. package/dist/commands/sources/history.js +1 -1
  150. package/dist/commands/sources/init.js +66 -18
  151. package/dist/commands/sources/installed-stashes.js +11 -3
  152. package/dist/commands/sources/migration-help.js +7 -4
  153. package/dist/commands/sources/schema-repair.js +44 -46
  154. package/dist/commands/sources/self-update.js +2 -2
  155. package/dist/commands/sources/source-add.js +7 -3
  156. package/dist/commands/sources/sources-cli.js +3 -3
  157. package/dist/commands/sources/stash-cli.js +19 -39
  158. package/dist/commands/sources/stash-skeleton.js +57 -8
  159. package/dist/commands/tasks/default-tasks.js +15 -2
  160. package/dist/commands/tasks/tasks-cli.js +20 -29
  161. package/dist/commands/tasks/tasks.js +39 -11
  162. package/dist/commands/wiki-cli.js +23 -38
  163. package/dist/commands/workflow-cli.js +291 -13
  164. package/dist/core/asset/asset-registry.js +3 -1
  165. package/dist/core/asset/asset-spec.js +79 -5
  166. package/dist/core/asset/frontmatter.js +188 -167
  167. package/dist/core/asset/markdown.js +8 -0
  168. package/dist/core/authoring-rules.js +92 -0
  169. package/dist/core/common.js +4 -23
  170. package/dist/core/concurrent.js +10 -1
  171. package/dist/core/config/config-io.js +10 -1
  172. package/dist/core/config/config-migration.js +18 -40
  173. package/dist/core/config/config-schema.js +403 -62
  174. package/dist/core/config/config-types.js +3 -3
  175. package/dist/core/config/config.js +67 -22
  176. package/dist/core/deep-merge.js +38 -0
  177. package/dist/core/errors.js +1 -0
  178. package/dist/core/eval/rank-metrics.js +113 -0
  179. package/dist/core/events.js +4 -7
  180. package/dist/core/improve-types.js +47 -8
  181. package/dist/core/json-schema.js +142 -0
  182. package/dist/core/logs-db.js +14 -75
  183. package/dist/core/parse.js +36 -16
  184. package/dist/core/paths.js +18 -18
  185. package/dist/core/standards/resolve-standards-context.js +87 -0
  186. package/dist/core/standards/resolve-stash-standards.js +99 -0
  187. package/dist/core/standards/resolve-type-conventions.js +66 -0
  188. package/dist/core/state/migrations.js +770 -0
  189. package/dist/core/state-db.js +132 -1126
  190. package/dist/core/structured.js +69 -0
  191. package/dist/core/time.js +53 -0
  192. package/dist/core/warn.js +21 -0
  193. package/dist/core/write-source.js +37 -0
  194. package/dist/indexer/db/db.js +261 -770
  195. package/dist/indexer/db/entry-mapper.js +41 -0
  196. package/dist/indexer/db/graph-db.js +129 -86
  197. package/dist/indexer/db/llm-cache.js +2 -2
  198. package/dist/indexer/db/schema.js +516 -0
  199. package/dist/indexer/ensure-index.js +36 -92
  200. package/dist/indexer/feedback/utility-policy.js +75 -0
  201. package/dist/indexer/graph/graph-boost.js +51 -41
  202. package/dist/indexer/graph/graph-extraction.js +207 -4
  203. package/dist/indexer/index-writer-lock.js +18 -11
  204. package/dist/indexer/index-written-assets.js +105 -0
  205. package/dist/indexer/indexer.js +182 -204
  206. package/dist/indexer/passes/dir-staleness.js +114 -0
  207. package/dist/indexer/passes/memory-inference.js +13 -5
  208. package/dist/indexer/passes/metadata.js +20 -0
  209. package/dist/indexer/read-preflight.js +23 -0
  210. package/dist/indexer/search/db-search.js +89 -13
  211. package/dist/indexer/search/fts-query.js +51 -0
  212. package/dist/indexer/search/ranking-contributors.js +95 -9
  213. package/dist/indexer/search/ranking.js +79 -3
  214. package/dist/indexer/search/search-fields.js +6 -0
  215. package/dist/indexer/search/search-source.js +32 -21
  216. package/dist/indexer/search/semantic-status.js +4 -0
  217. package/dist/indexer/walk/matchers.js +48 -0
  218. package/dist/indexer/walk/walker.js +21 -13
  219. package/dist/integrations/agent/builders.js +41 -13
  220. package/dist/integrations/agent/config.js +20 -59
  221. package/dist/integrations/agent/detect.js +9 -0
  222. package/dist/integrations/agent/index.js +3 -19
  223. package/dist/integrations/agent/model-aliases.js +16 -2
  224. package/dist/integrations/agent/profiles.js +79 -6
  225. package/dist/integrations/agent/prompts.js +75 -9
  226. package/dist/integrations/agent/runner-dispatch.js +83 -0
  227. package/dist/integrations/agent/runner.js +13 -9
  228. package/dist/integrations/agent/spawn.js +206 -81
  229. package/dist/integrations/harnesses/aider/agent-builder.js +113 -0
  230. package/dist/integrations/harnesses/aider/index.js +58 -0
  231. package/dist/integrations/harnesses/aider/result-extractor.js +53 -0
  232. package/dist/integrations/harnesses/amazonq/agent-builder.js +153 -0
  233. package/dist/integrations/harnesses/amazonq/index.js +59 -0
  234. package/dist/integrations/harnesses/amazonq/result-extractor.js +48 -0
  235. package/dist/integrations/harnesses/claude/agent-builder.js +46 -7
  236. package/dist/integrations/harnesses/claude/index.js +27 -23
  237. package/dist/integrations/harnesses/claude/result-extractor.js +52 -0
  238. package/dist/integrations/harnesses/claude/session-log.js +10 -0
  239. package/dist/integrations/harnesses/codex/agent-builder.js +137 -0
  240. package/dist/integrations/harnesses/codex/index.js +63 -0
  241. package/dist/integrations/harnesses/codex/result-extractor.js +73 -0
  242. package/dist/integrations/harnesses/copilot/agent-builder.js +122 -0
  243. package/dist/integrations/harnesses/copilot/index.js +60 -0
  244. package/dist/integrations/harnesses/copilot/result-extractor.js +151 -0
  245. package/dist/integrations/harnesses/gemini/agent-builder.js +121 -0
  246. package/dist/integrations/harnesses/gemini/index.js +60 -0
  247. package/dist/integrations/harnesses/gemini/result-extractor.js +121 -0
  248. package/dist/integrations/harnesses/index.js +28 -7
  249. package/dist/integrations/harnesses/opencode/agent-builder.js +1 -1
  250. package/dist/integrations/harnesses/opencode/index.js +17 -16
  251. package/dist/integrations/harnesses/opencode/session-log.js +173 -3
  252. package/dist/integrations/harnesses/opencode-sdk/harness.js +65 -0
  253. package/dist/integrations/harnesses/opencode-sdk/index.js +10 -34
  254. package/dist/integrations/harnesses/opencode-sdk/sdk-runner.js +642 -71
  255. package/dist/integrations/harnesses/openhands/agent-builder.js +126 -0
  256. package/dist/integrations/harnesses/openhands/index.js +58 -0
  257. package/dist/integrations/harnesses/openhands/result-extractor.js +103 -0
  258. package/dist/integrations/harnesses/pi/agent-builder.js +104 -0
  259. package/dist/integrations/harnesses/pi/index.js +58 -0
  260. package/dist/integrations/harnesses/pi/result-extractor.js +135 -0
  261. package/dist/integrations/harnesses/types.js +8 -0
  262. package/dist/integrations/session-logs/index.js +40 -11
  263. package/dist/llm/call-ai.js +2 -2
  264. package/dist/llm/client.js +34 -11
  265. package/dist/llm/embedder.js +67 -4
  266. package/dist/llm/embedders/cache.js +3 -1
  267. package/dist/llm/embedders/deterministic.js +66 -0
  268. package/dist/llm/embedders/local.js +73 -3
  269. package/dist/llm/feature-gate.js +16 -15
  270. package/dist/llm/graph-extract.js +67 -44
  271. package/dist/llm/memory-infer-impl.js +138 -0
  272. package/dist/llm/memory-infer.js +1 -127
  273. package/dist/llm/metadata-enhance.js +44 -31
  274. package/dist/llm/structured-call.js +49 -0
  275. package/dist/migrate-storage-node.mjs +8 -0
  276. package/dist/output/context.js +5 -5
  277. package/dist/output/renderers.js +87 -15
  278. package/dist/output/shapes/curate.js +14 -2
  279. package/dist/output/shapes/helpers.js +0 -3
  280. package/dist/output/shapes/passthrough.js +6 -1
  281. package/dist/output/text/helpers.js +241 -2
  282. package/dist/output/text/workflow.js +4 -1
  283. package/dist/registry/providers/skills-sh.js +21 -147
  284. package/dist/registry/providers/static-index.js +15 -157
  285. package/dist/registry/resolve.js +27 -9
  286. package/dist/runtime.js +25 -1
  287. package/dist/schemas/akm-config.json +14225 -0
  288. package/dist/schemas/akm-workflow.json +328 -0
  289. package/dist/scripts/migrate-storage.js +2743 -8390
  290. package/dist/scripts/migrations/import-fs-improve-runs-to-db.js +1652 -607
  291. package/dist/setup/detect.js +9 -0
  292. package/dist/setup/legacy-config.js +106 -0
  293. package/dist/setup/prompt.js +57 -0
  294. package/dist/setup/providers.js +14 -0
  295. package/dist/setup/registry-stash-loader.js +12 -0
  296. package/dist/setup/semantic-assets.js +124 -0
  297. package/dist/setup/setup.js +52 -1614
  298. package/dist/setup/steps/connection.js +734 -0
  299. package/dist/setup/steps/output.js +31 -0
  300. package/dist/setup/steps/platforms.js +124 -0
  301. package/dist/setup/steps/semantic.js +27 -0
  302. package/dist/setup/steps/sources.js +222 -0
  303. package/dist/setup/steps/stashdir.js +42 -0
  304. package/dist/setup/steps/tasks.js +152 -0
  305. package/dist/sources/include.js +6 -2
  306. package/dist/sources/providers/filesystem.js +0 -1
  307. package/dist/sources/providers/git-install.js +210 -0
  308. package/dist/sources/providers/git-provider.js +234 -0
  309. package/dist/sources/providers/git-stash.js +248 -0
  310. package/dist/sources/providers/git.js +10 -661
  311. package/dist/sources/providers/npm.js +2 -6
  312. package/dist/sources/providers/provider-utils.js +13 -7
  313. package/dist/sources/providers/sync-from-ref.js +9 -1
  314. package/dist/sources/providers/website.js +9 -5
  315. package/dist/sources/website-ingest.js +187 -29
  316. package/dist/sources/wiki-fetchers/registry.js +53 -0
  317. package/dist/sources/wiki-fetchers/youtube.js +239 -0
  318. package/dist/storage/database.js +45 -10
  319. package/dist/storage/managed-db.js +82 -0
  320. package/dist/storage/repositories/canaries-repository.js +107 -0
  321. package/dist/storage/repositories/consolidation-repository.js +38 -0
  322. package/dist/storage/repositories/embeddings-repository.js +72 -0
  323. package/dist/storage/repositories/events-repository.js +187 -0
  324. package/dist/storage/repositories/extract-sessions-repository.js +96 -0
  325. package/dist/storage/repositories/improve-runs-repository.js +146 -0
  326. package/dist/storage/repositories/index-db.js +14 -8
  327. package/dist/storage/repositories/proposals-repository.js +220 -0
  328. package/dist/storage/repositories/recombine-repository.js +213 -0
  329. package/dist/storage/repositories/registry-cache.js +93 -0
  330. package/dist/storage/repositories/registry-index-cache-repository.js +46 -0
  331. package/dist/storage/repositories/task-history-repository.js +93 -0
  332. package/dist/storage/repositories/workflow-runs-repository.js +189 -1
  333. package/dist/storage/sqlite-pragmas.js +146 -0
  334. package/dist/tasks/backends/cron.js +1 -1
  335. package/dist/tasks/backends/index.js +9 -0
  336. package/dist/tasks/backends/launchd.js +1 -1
  337. package/dist/tasks/backends/schtasks.js +1 -1
  338. package/dist/tasks/{resolveAkmBin.js → resolve-akm-bin.js} +2 -2
  339. package/dist/tasks/runner.js +15 -13
  340. package/dist/text-import-hook.mjs +1 -1
  341. package/dist/wiki/wiki.js +52 -11
  342. package/dist/workflows/authoring/authoring.js +123 -10
  343. package/dist/workflows/authoring/workflow-program-template.yaml +31 -0
  344. package/dist/workflows/cli.js +5 -0
  345. package/dist/workflows/db.js +138 -4
  346. package/dist/workflows/exec/brief.js +484 -0
  347. package/dist/workflows/exec/native-executor.js +975 -0
  348. package/dist/workflows/exec/param-secrets.js +115 -0
  349. package/dist/workflows/exec/report.js +1295 -0
  350. package/dist/workflows/exec/run-workflow.js +596 -0
  351. package/dist/workflows/exec/scheduler.js +100 -0
  352. package/dist/workflows/exec/step-work.js +1156 -0
  353. package/dist/workflows/exec/unit-writer.js +23 -0
  354. package/dist/workflows/exec/watch.js +116 -0
  355. package/dist/workflows/exec/worktree.js +171 -0
  356. package/dist/workflows/ir/compile.js +388 -0
  357. package/dist/workflows/ir/params.js +54 -0
  358. package/dist/workflows/ir/plan-hash.js +33 -0
  359. package/dist/workflows/ir/schema.js +4 -0
  360. package/dist/workflows/parser.js +3 -1
  361. package/dist/workflows/program/expressions.js +369 -0
  362. package/dist/workflows/program/parser.js +760 -0
  363. package/dist/workflows/program/project.js +105 -0
  364. package/dist/workflows/program/schema.js +54 -0
  365. package/dist/workflows/renderer.js +82 -5
  366. package/dist/workflows/runtime/agent-identity.js +59 -14
  367. package/dist/workflows/runtime/runs.js +248 -153
  368. package/dist/workflows/runtime/unit-checkin.js +45 -0
  369. package/dist/workflows/runtime/workflow-asset-loader.js +188 -0
  370. package/dist/workflows/validate-summary.js +26 -10
  371. package/dist/workflows/validator.js +1 -1
  372. package/docs/README.md +69 -18
  373. package/docs/data-and-telemetry.md +7 -5
  374. package/docs/migration/release-notes/0.7.0.md +1 -1
  375. package/docs/migration/release-notes/0.9.0-beta.60.md +19 -0
  376. package/docs/migration/release-notes/0.9.0.md +39 -0
  377. package/package.json +10 -10
  378. package/dist/assets/tasks/core/update-stashes.yml +0 -4
  379. package/dist/commands/db-cli.js +0 -23
  380. package/dist/indexer/db/db-backup.js +0 -376
  381. package/dist/indexer/passes/staleness-detect.js +0 -488
@@ -18,10 +18,13 @@
18
18
  * round-trip through `dotenv`; the shell-load safety guarantee still lives on
19
19
  * the READ path (see `buildShellExportScript` + `akm env export`).
20
20
  *
21
- * Invariant: env values must never be written to stdout, returned through the
22
- * indexer, the `akm show` renderer, or any structured output channel. Key
23
- * NAMES and start-of-line comments ARE surfaced by design (discoverability) —
24
- * only values are secret. The supported value-load paths are:
21
+ * Invariant: nothing from an env file except key NAMES may be written to
22
+ * stdout, returned through the indexer, the `akm show` renderer, or any
23
+ * structured output channel. Key NAMES are surfaced for discoverability;
24
+ * comment text is NOT — real .env files routinely carry commented-out
25
+ * `KEY=value` lines and free-text notes containing live credentials, so
26
+ * comments are treated exactly like values. The supported value-load paths
27
+ * are:
25
28
  *
26
29
  * - `akm env run <ref> -- <command>` — values injected into the child
27
30
  * process env (never via a shell), see `injectIntoEnv` / `loadEnv`. This is
@@ -71,75 +74,19 @@ function scanKeys(text) {
71
74
  return keys;
72
75
  }
73
76
  /**
74
- * Scan lines and return start-of-line `#` comments (with the leading `#` and
75
- * any leading whitespace stripped). Inline/trailing `#` after an assignment is
76
- * never extracted.
77
- */
78
- function scanComments(text) {
79
- const comments = [];
80
- for (const line of text.split(/\r?\n/)) {
81
- const trimmed = line.trimStart();
82
- if (trimmed.startsWith("#")) {
83
- comments.push(trimmed.slice(1).trimStart());
84
- }
85
- }
86
- return comments;
87
- }
88
- /**
89
- * Read and return ONLY non-secret metadata (keys + start-of-line comments).
77
+ * Read and return ONLY non-secret metadata: key names.
90
78
  *
91
79
  * The function reads the whole file into memory (same as any dotenv parser)
92
- * but deliberately does not parse values — the LHS-only regex scanners above
93
- * ensure no value content is retained or returned. The guarantee is that
94
- * values never leave this function.
80
+ * but deliberately does not parse values — the LHS-only regex scanner above
81
+ * ensures no value content is retained or returned. Comment text is never
82
+ * returned either: comments routinely contain commented-out `KEY=value`
83
+ * credentials and free-text secrets, so they never leave this function.
95
84
  */
96
85
  export function listKeys(envPath) {
97
86
  if (!fs.existsSync(envPath))
98
- return { keys: [], comments: [] };
99
- const text = fs.readFileSync(envPath, "utf8");
100
- return { keys: scanKeys(text), comments: scanComments(text) };
101
- }
102
- /**
103
- * Return structured `entries` pairing each key with the nearest preceding
104
- * comment line (if any). This is an easier-to-consume shape than the parallel
105
- * `keys[]` + `comments[]` of `listKeys` (QA #35).
106
- *
107
- * Values are never included — the same privacy guarantee as `listKeys`.
108
- */
109
- export function listEntries(envPath) {
110
- if (!fs.existsSync(envPath))
111
- return [];
87
+ return { keys: [] };
112
88
  const text = fs.readFileSync(envPath, "utf8");
113
- const lines = text.split(/\r?\n/);
114
- const seen = new Set();
115
- const entries = [];
116
- let pendingComment;
117
- for (const line of lines) {
118
- const trimmed = line.trimStart();
119
- if (trimmed.startsWith("#")) {
120
- // Capture the most recent comment before a key
121
- pendingComment = trimmed.slice(1).trimStart() || undefined;
122
- continue;
123
- }
124
- const m = line.match(ASSIGN_RE);
125
- if (m) {
126
- const key = m[1];
127
- if (!seen.has(key)) {
128
- seen.add(key);
129
- const entry = { key };
130
- if (pendingComment)
131
- entry.comment = pendingComment;
132
- entries.push(entry);
133
- }
134
- pendingComment = undefined;
135
- }
136
- else {
137
- // Any non-comment, non-assignment line (including blank lines)
138
- // breaks "nearest preceding comment line" association.
139
- pendingComment = undefined;
140
- }
141
- }
142
- return entries;
89
+ return { keys: scanKeys(text) };
143
90
  }
144
91
  /**
145
92
  * Read all KEY=value pairs from an env file. Intended for programmatic callers
@@ -18,17 +18,25 @@
18
18
  import { spawnSync } from "node:child_process";
19
19
  import fs from "node:fs";
20
20
  import path from "node:path";
21
- import { defineCommand } from "citty";
22
- import { getStringArg, hasSubcommand } from "../../cli/parse-args.js";
23
- import { output, runWithJsonErrors } from "../../cli/shared.js";
21
+ import { getStringArg } from "../../cli/parse-args.js";
22
+ import { defineGroupCommand, defineJsonCommand, output } from "../../cli/shared.js";
24
23
  import { deriveCanonicalAssetName } from "../../core/asset/asset-spec.js";
25
24
  import { loadConfig } from "../../core/config/config.js";
26
25
  import { makeSecretRef, resolveSecretPath } from "../../core/env-secret-ref.js";
27
26
  import { ConfigError, NotFoundError, UsageError } from "../../core/errors.js";
28
27
  import { appendEvent } from "../../core/events.js";
29
28
  import { resolveSourceEntries } from "../../indexer/search/search-source.js";
30
- import { getHyphenatedArg } from "../../output/context.js";
31
29
  import { readStdin } from "../../runtime.js";
30
+ import { buildChildEnv } from "./child-env.js";
31
+ function parseKeyListFlag(raw) {
32
+ if (raw === undefined)
33
+ return undefined;
34
+ const keys = raw
35
+ .split(/[,\s]+/)
36
+ .map((k) => k.trim())
37
+ .filter(Boolean);
38
+ return keys.length > 0 ? keys : undefined;
39
+ }
32
40
  /** Walk `secrets/` across all stashes, returning one entry per secret file. */
33
41
  function listSecretsRecursive() {
34
42
  const result = [];
@@ -60,18 +68,16 @@ function listSecretsRecursive() {
60
68
  }
61
69
  return result;
62
70
  }
63
- const secretListCommand = defineCommand({
71
+ const secretListCommand = defineJsonCommand({
64
72
  meta: {
65
73
  name: "list",
66
74
  description: "List all secrets across all stashes by name (the file contents are never shown)",
67
75
  },
68
- run() {
69
- return runWithJsonErrors(async () => {
70
- output("secret-list", { secrets: listSecretsRecursive() });
71
- });
76
+ async run() {
77
+ output("secret-list", { secrets: listSecretsRecursive() });
72
78
  },
73
79
  });
74
- const secretSetCommand = defineCommand({
80
+ const secretSetCommand = defineJsonCommand({
75
81
  meta: {
76
82
  name: "set",
77
83
  description: "Create or overwrite a secret. The value is read from stdin by default (never via argv). Use --from-file <path> to import an existing file byte-exact, or --from-env <VAR> to read from an environment variable. Multi-line values are allowed.",
@@ -89,49 +95,47 @@ const secretSetCommand = defineCommand({
89
95
  "from-file": { type: "string", description: "Read the value from this file (stored byte-exact)" },
90
96
  "from-env": { type: "string", description: "Read the value from the named environment variable" },
91
97
  },
92
- run({ args }) {
93
- return runWithJsonErrors(async () => {
94
- const { setSecret } = await import("./secret.js");
95
- const { name, absPath, source } = resolveSecretPath(args.ref, { subPath: getStringArg(args, "path") });
96
- const fromEnv = getHyphenatedArg(args, "from-env");
97
- const fromFile = getHyphenatedArg(args, "from-file");
98
- if (fromEnv !== undefined && fromFile !== undefined) {
99
- throw new UsageError("Pass only one of --from-file or --from-env (or use stdin).", "INVALID_FLAG_VALUE");
98
+ async run({ args }) {
99
+ const { setSecret } = await import("./secret.js");
100
+ const { name, absPath, source } = resolveSecretPath(args.ref, { subPath: getStringArg(args, "path") });
101
+ const fromEnv = args["from-env"];
102
+ const fromFile = args["from-file"];
103
+ if (fromEnv !== undefined && fromFile !== undefined) {
104
+ throw new UsageError("Pass only one of --from-file or --from-env (or use stdin).", "INVALID_FLAG_VALUE");
105
+ }
106
+ const MAX_SECRET_BYTES = 5 * 1024 * 1024; // 5 MB
107
+ let value;
108
+ if (fromFile !== undefined) {
109
+ if (!fs.existsSync(fromFile)) {
110
+ throw new NotFoundError(`File not found: ${fromFile}`, "FILE_NOT_FOUND");
100
111
  }
101
- const MAX_SECRET_BYTES = 5 * 1024 * 1024; // 5 MB
102
- let value;
103
- if (fromFile !== undefined) {
104
- if (!fs.existsSync(fromFile)) {
105
- throw new NotFoundError(`File not found: ${fromFile}`, "FILE_NOT_FOUND");
106
- }
107
- value = fs.readFileSync(fromFile);
108
- if (value.byteLength > MAX_SECRET_BYTES) {
109
- throw new UsageError("Secret exceeds the 5 MB limit.");
110
- }
112
+ value = fs.readFileSync(fromFile);
113
+ if (value.byteLength > MAX_SECRET_BYTES) {
114
+ throw new UsageError("Secret exceeds the 5 MB limit.");
111
115
  }
112
- else if (fromEnv !== undefined) {
113
- const envVal = process.env[fromEnv];
114
- if (envVal === undefined) {
115
- throw new UsageError(`Environment variable "${fromEnv}" is not set.`, "INVALID_FLAG_VALUE");
116
- }
117
- value = Buffer.from(envVal, "utf8");
116
+ }
117
+ else if (fromEnv !== undefined) {
118
+ const envVal = process.env[fromEnv];
119
+ if (envVal === undefined) {
120
+ throw new UsageError(`Environment variable "${fromEnv}" is not set.`, "INVALID_FLAG_VALUE");
118
121
  }
119
- else {
120
- if (process.stdin.isTTY) {
121
- process.stderr.write(`Enter value for secret "${name}" (Ctrl-D when done):\n`);
122
- }
123
- const stdinBuf = await readStdin(MAX_SECRET_BYTES, () => new UsageError("Secret exceeds the 5 MB limit."));
124
- // Strip a single trailing newline so `echo "$TOKEN" | akm secret set`
125
- // stores the token without the shell-added newline. Use --from-file for
126
- // byte-exact storage of multi-line material (PEM keys, certs).
127
- value = Buffer.from(stdinBuf.toString("utf8").replace(/\n$/, ""), "utf8");
122
+ value = Buffer.from(envVal, "utf8");
123
+ }
124
+ else {
125
+ if (process.stdin.isTTY) {
126
+ process.stderr.write(`Enter value for secret "${name}" (Ctrl-D when done):\n`);
128
127
  }
129
- setSecret(absPath, value);
130
- output("secret-set", { ref: makeSecretRef(name, source) });
131
- });
128
+ const stdinBuf = await readStdin(MAX_SECRET_BYTES, () => new UsageError("Secret exceeds the 5 MB limit."));
129
+ // Strip a single trailing newline so `echo "$TOKEN" | akm secret set`
130
+ // stores the token without the shell-added newline. Use --from-file for
131
+ // byte-exact storage of multi-line material (PEM keys, certs).
132
+ value = Buffer.from(stdinBuf.toString("utf8").replace(/\n$/, ""), "utf8");
133
+ }
134
+ setSecret(absPath, value);
135
+ output("secret-set", { ref: makeSecretRef(name, source) });
132
136
  },
133
137
  });
134
- const secretPathCommand = defineCommand({
138
+ const secretPathCommand = defineJsonCommand({
135
139
  meta: {
136
140
  name: "path",
137
141
  description: "Print the absolute secret file path for the Docker `_FILE` convention, e.g. `MY_SECRET_FILE=$(akm secret path secret:deploy-key)`.",
@@ -139,121 +143,119 @@ const secretPathCommand = defineCommand({
139
143
  args: {
140
144
  ref: { type: "positional", description: "Secret ref", required: true },
141
145
  },
142
- run({ args }) {
143
- return runWithJsonErrors(async () => {
144
- const { name, absPath, source } = resolveSecretPath(args.ref);
145
- if (!fs.existsSync(absPath)) {
146
- throw new NotFoundError(`Secret not found: ${makeSecretRef(name, source)}`);
147
- }
148
- process.stdout.write(`${absPath}\n`);
149
- });
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`);
150
152
  },
151
153
  });
152
- const secretRunCommand = defineCommand({
154
+ const secretRunCommand = defineJsonCommand({
153
155
  meta: {
154
156
  name: "run",
155
- description: "Run a command with a secret's value injected into an env var: `akm secret run <ref> <VAR> -- <command>`. The value is set as $VAR in the child process only.",
157
+ description: "Run a command with a secret's value injected into an env var: `akm secret run <ref> <VAR> -- <command>`. The value is set as $VAR in the child process only. Pass --clean to start the child with a minimal inherited environment instead of the full parent environment.",
156
158
  },
157
159
  args: {
158
160
  ref: { type: "positional", description: "Secret ref", required: true },
159
161
  var: { type: "positional", description: "Environment variable name to inject the value into", required: true },
162
+ clean: {
163
+ type: "boolean",
164
+ description: "Start the child with a minimal inherited environment (PATH/HOME/locale/terminal basics) instead of the full parent environment.",
165
+ default: false,
166
+ },
167
+ inherit: {
168
+ type: "string",
169
+ description: "When used with --clean, also inherit these parent env vars (comma-separated). Ignored without --clean.",
170
+ },
160
171
  },
161
- run({ args }) {
162
- return runWithJsonErrors(async () => {
163
- // Validate the target env var name FIRST (before the command split) so a
164
- // dangerous/invalid name is rejected regardless of how the command is
165
- // supplied and so the failure does not depend on argv parsing.
166
- const varName = args.var;
167
- if (!/^[A-Za-z_][A-Za-z0-9_]*$/.test(varName)) {
168
- throw new UsageError(`"${varName}" is not a valid environment variable name.`, "INVALID_FLAG_VALUE");
169
- }
170
- const { isDangerousEnvKey } = await import("../lint/env-key-rules.js");
171
- if (isDangerousEnvKey(varName)) {
172
- 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");
173
- }
174
- const dashIndex = process.argv.indexOf("--");
175
- if (dashIndex < 0 || dashIndex === process.argv.length - 1) {
176
- throw new UsageError("Missing command. Usage: akm secret run <ref> <VAR> -- <command>");
177
- }
178
- const command = process.argv.slice(dashIndex + 1);
179
- const { name, absPath, source } = resolveSecretPath(args.ref);
180
- if (!fs.existsSync(absPath)) {
181
- throw new NotFoundError(`Secret not found: ${makeSecretRef(name, source)}`);
172
+ async run({ args }) {
173
+ // Validate the target env var name FIRST (before the command split) so a
174
+ // dangerous/invalid name is rejected regardless of how the command is
175
+ // supplied and so the failure does not depend on argv parsing.
176
+ const varName = args.var;
177
+ if (!/^[A-Za-z_][A-Za-z0-9_]*$/.test(varName)) {
178
+ throw new UsageError(`"${varName}" is not a valid environment variable name.`, "INVALID_FLAG_VALUE");
179
+ }
180
+ const { isDangerousEnvKey } = await import("../lint/env-key-rules.js");
181
+ if (isDangerousEnvKey(varName)) {
182
+ 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
+ }
184
+ const dashIndex = process.argv.indexOf("--");
185
+ if (dashIndex < 0 || dashIndex === process.argv.length - 1) {
186
+ throw new UsageError("Missing command. Usage: akm secret run <ref> <VAR> -- <command>");
187
+ }
188
+ const command = process.argv.slice(dashIndex + 1);
189
+ const { name, absPath, source } = resolveSecretPath(args.ref);
190
+ if (!fs.existsSync(absPath)) {
191
+ throw new NotFoundError(`Secret not found: ${makeSecretRef(name, source)}`);
192
+ }
193
+ const { readValue } = await import("./secret.js");
194
+ const mergedEnv = buildChildEnv(process.env, {
195
+ clean: args.clean === true,
196
+ inherit: parseKeyListFlag(args.inherit) ?? [],
197
+ });
198
+ mergedEnv[varName] = readValue(absPath).toString("utf8");
199
+ // Audit trail: record access by ref + var name only — never the value.
200
+ appendEvent({
201
+ eventType: "secret_access",
202
+ ref: makeSecretRef(name, source),
203
+ metadata: { var: varName },
204
+ });
205
+ const result = spawnSync(command[0], command.slice(1), {
206
+ stdio: "inherit",
207
+ env: mergedEnv,
208
+ });
209
+ if (result.error) {
210
+ const err = result.error;
211
+ if (err.code === "ENOENT") {
212
+ throw new NotFoundError(`Command not found: ${command[0]}`, "FILE_NOT_FOUND", `Install '${command[0]}' or add its directory to PATH before invoking 'akm secret run'.`);
182
213
  }
183
- const { readValue } = await import("./secret.js");
184
- const mergedEnv = { ...process.env };
185
- mergedEnv[varName] = readValue(absPath).toString("utf8");
186
- // Audit trail: record access by ref + var name only — never the value.
187
- appendEvent({
188
- eventType: "secret_access",
189
- ref: makeSecretRef(name, source),
190
- metadata: { var: varName },
191
- });
192
- const result = spawnSync(command[0], command.slice(1), {
193
- stdio: "inherit",
194
- env: mergedEnv,
195
- });
196
- if (result.error) {
197
- const err = result.error;
198
- if (err.code === "ENOENT") {
199
- throw new NotFoundError(`Command not found: ${command[0]}`, "FILE_NOT_FOUND", `Install '${command[0]}' or add its directory to PATH before invoking 'akm secret run'.`);
200
- }
201
- if (err.code === "EACCES") {
202
- throw new ConfigError(`Command not executable: ${command[0]}`, "STASH_DIR_UNREADABLE", `Add execute permission ('chmod +x ${command[0]}') or invoke via an interpreter.`);
203
- }
204
- throw err;
214
+ if (err.code === "EACCES") {
215
+ throw new ConfigError(`Command not executable: ${command[0]}`, "STASH_DIR_UNREADABLE", `Add execute permission ('chmod +x ${command[0]}') or invoke via an interpreter.`);
205
216
  }
206
- process.exit(result.status ?? 0);
207
- });
217
+ throw err;
218
+ }
219
+ process.exit(result.status ?? 0);
208
220
  },
209
221
  });
210
- const secretRemoveCommand = defineCommand({
222
+ const secretRemoveCommand = defineJsonCommand({
211
223
  meta: { name: "remove", description: "Remove a secret (and its .sensitive marker, if any)" },
212
224
  args: {
213
225
  ref: { type: "positional", description: "Secret ref", required: true },
214
226
  yes: { type: "boolean", alias: "y", description: "Skip confirmation prompt", default: false },
215
227
  },
216
- run({ args }) {
217
- return runWithJsonErrors(async () => {
218
- const { name, absPath, source } = resolveSecretPath(args.ref);
219
- const { confirmDestructive } = await import("../../cli/confirm.js");
220
- const confirmed = await confirmDestructive(`Remove secret "${args.ref}"? This cannot be undone.`, {
221
- yes: args.yes === true,
222
- });
223
- if (!confirmed) {
224
- process.stderr.write("Aborted.\n");
225
- return;
226
- }
227
- const { removeSecret } = await import("./secret.js");
228
- if (!fs.existsSync(absPath)) {
229
- throw new NotFoundError(`Secret not found: ${makeSecretRef(name, source)}`);
230
- }
231
- const removed = removeSecret(absPath);
232
- output("secret-remove", { ref: makeSecretRef(name, source), removed });
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
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 });
234
244
  },
235
245
  });
236
- // Single source of truth: the routing set is derived from the subCommands keys
237
- // (M10) so adding a subcommand can never silently desync from `hasSubcommand`.
238
- const secretSubCommands = {
239
- list: secretListCommand,
240
- path: secretPathCommand,
241
- run: secretRunCommand,
242
- set: secretSetCommand,
243
- remove: secretRemoveCommand,
244
- };
245
- const SECRET_SUBCOMMAND_SET = new Set(Object.keys(secretSubCommands));
246
- export const secretCommand = defineCommand({
246
+ export const secretCommand = defineGroupCommand({
247
247
  meta: {
248
248
  name: "secret",
249
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`.",
250
250
  },
251
- subCommands: secretSubCommands,
252
- run({ args }) {
253
- return runWithJsonErrors(async () => {
254
- if (hasSubcommand(args, SECRET_SUBCOMMAND_SET))
255
- return;
256
- output("secret-list", { secrets: listSecretsRecursive() });
257
- });
251
+ subCommands: {
252
+ list: secretListCommand,
253
+ path: secretPathCommand,
254
+ run: secretRunCommand,
255
+ set: secretSetCommand,
256
+ remove: secretRemoveCommand,
257
+ },
258
+ defaultRun() {
259
+ output("secret-list", { secrets: listSecretsRecursive() });
258
260
  },
259
261
  });