akm-cli 0.9.0-rc.9 → 0.9.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (450) hide show
  1. package/CHANGELOG.md +1063 -44
  2. package/README.md +51 -25
  3. package/SECURITY.md +14 -1
  4. package/STABILITY.md +497 -0
  5. package/dist/akm +148 -35
  6. package/dist/{akm-migrate-storage → akm-migrate} +6 -9
  7. package/dist/assets/hints/cli-hints-full.md +223 -95
  8. package/dist/assets/hints/cli-hints-short.md +85 -22
  9. package/dist/assets/improve-strategies/default.json +1 -1
  10. package/dist/assets/improve-strategies/reflect-distill.json +1 -1
  11. package/dist/assets/prompts/memory-infer-user.md +2 -3
  12. package/dist/assets/stash-skeleton/README.md +6 -5
  13. package/dist/assets/stash-skeleton/facts/conventions/assets/agent.md +2 -0
  14. package/dist/assets/stash-skeleton/facts/conventions/assets/command.md +2 -0
  15. package/dist/assets/stash-skeleton/facts/conventions/assets/fact.md +2 -0
  16. package/dist/assets/stash-skeleton/facts/conventions/assets/knowledge.md +2 -0
  17. package/dist/assets/stash-skeleton/facts/conventions/assets/lesson.md +2 -0
  18. package/dist/assets/stash-skeleton/facts/conventions/assets/memory.md +2 -0
  19. package/dist/assets/stash-skeleton/facts/conventions/assets/script.md +2 -0
  20. package/dist/assets/stash-skeleton/facts/conventions/assets/skill.md +2 -0
  21. package/dist/assets/stash-skeleton/facts/conventions/assets/workflow.md +2 -0
  22. package/dist/assets/stash-skeleton/facts/conventions/backlinks.md +2 -0
  23. package/dist/assets/stash-skeleton/facts/conventions/domains.md +2 -0
  24. package/dist/assets/stash-skeleton/facts/conventions/organization.md +20 -9
  25. package/dist/assets/tasks/core/extract.yml +1 -1
  26. package/dist/assets/tasks/core/version-check.yml +1 -1
  27. package/dist/assets/tasks/improve/akm-graph-refresh-weekly.yml +5 -0
  28. package/dist/assets/tasks/improve/akm-improve-catchup.yml +8 -0
  29. package/dist/assets/tasks/improve/akm-improve-consolidate.yml +5 -0
  30. package/dist/assets/tasks/improve/akm-improve-frequent.yml +5 -0
  31. package/dist/assets/tasks/improve/akm-improve-nightly.yml +5 -0
  32. package/dist/assets/templates/html/health.html +1 -3
  33. package/dist/assets/workflows/workflow-template.md +32 -15
  34. package/dist/cli/invocation.js +40 -15
  35. package/dist/cli/parse-args.js +0 -22
  36. package/dist/cli/retired-commands.js +121 -0
  37. package/dist/cli/shared.js +154 -22
  38. package/dist/cli/unknown-flags.js +236 -0
  39. package/dist/cli-node.mjs +2 -1
  40. package/dist/cli.js +696 -258
  41. package/dist/commands/agent/agent-dispatch.js +14 -3
  42. package/dist/commands/agent/contribute-cli.js +73 -88
  43. package/dist/commands/completions.js +79 -22
  44. package/dist/commands/config-cli.js +17 -150
  45. package/dist/commands/env/env-cli.js +59 -143
  46. package/dist/commands/env/env.js +12 -163
  47. package/dist/commands/env/marker-path.js +6 -0
  48. package/dist/commands/env/secret-cli.js +36 -66
  49. package/dist/commands/env/secret.js +24 -57
  50. package/dist/commands/feedback-cli.js +141 -87
  51. package/dist/commands/health/accept-rate.js +58 -0
  52. package/dist/commands/health/advisories.js +3 -4
  53. package/dist/commands/health/checks.js +85 -23
  54. package/dist/commands/health/html-report.js +7 -10
  55. package/dist/commands/health/improve-metrics.js +25 -83
  56. package/dist/commands/health/md-report.js +5 -9
  57. package/dist/commands/health/metrics.js +62 -20
  58. package/dist/commands/health/renderers.js +47 -0
  59. package/dist/commands/health/report-view-model.js +4 -5
  60. package/dist/commands/health/stash-exposure.js +1 -1
  61. package/dist/commands/health/surfaces.js +3 -48
  62. package/dist/commands/health/task-runs.js +3 -67
  63. package/dist/commands/health/types-improve.js +7 -0
  64. package/dist/commands/health.js +99 -28
  65. package/dist/commands/improve/anti-collapse.js +2 -2
  66. package/dist/commands/improve/autonomy-gate.js +68 -0
  67. package/dist/commands/improve/collapse-detector.js +41 -40
  68. package/dist/commands/improve/consolidate/eligibility.js +1 -23
  69. package/dist/commands/improve/consolidate/merge.js +4 -0
  70. package/dist/commands/improve/consolidate.js +140 -1000
  71. package/dist/commands/improve/distill/promote-memory.js +12 -12
  72. package/dist/commands/improve/distill/quality-gate.js +6 -6
  73. package/dist/commands/improve/distill.js +58 -69
  74. package/dist/commands/improve/eligibility.js +95 -55
  75. package/dist/commands/improve/extract-cli.js +14 -133
  76. package/dist/commands/improve/improve-cli.js +98 -114
  77. package/dist/commands/improve/improve-result-file.js +1 -28
  78. package/dist/commands/improve/improve-strategies.js +8 -5
  79. package/dist/commands/improve/improve.js +128 -91
  80. package/dist/commands/improve/loop-stages.js +182 -20
  81. package/dist/commands/improve/memory/derived-ref.js +45 -43
  82. package/dist/commands/improve/memory/memory-belief.js +1 -1
  83. package/dist/commands/improve/memory/memory-contradiction-detect.js +4 -12
  84. package/dist/commands/improve/memory/memory-improve.js +6 -5
  85. package/dist/commands/improve/outcome-loop.js +22 -65
  86. package/dist/commands/improve/preparation.js +114 -123
  87. package/dist/commands/improve/proactive-maintenance.js +2 -5
  88. package/dist/commands/improve/reflect.js +56 -160
  89. package/dist/commands/improve/salience.js +11 -122
  90. package/dist/commands/improve/source-identity.js +10 -38
  91. package/dist/commands/lint/base-linter.js +20 -124
  92. package/dist/commands/lint/env-key-rules.js +31 -47
  93. package/dist/commands/lint/index.js +249 -43
  94. package/dist/commands/{events.js → log.js} +33 -38
  95. package/dist/commands/migrate-cli.js +92 -12
  96. package/dist/commands/migration-tool.js +46 -0
  97. package/dist/commands/observability-cli.js +70 -209
  98. package/dist/commands/proposal/drain.js +101 -29
  99. package/dist/commands/proposal/proposal-cli.js +76 -48
  100. package/dist/commands/proposal/proposal.js +54 -18
  101. package/dist/commands/proposal/propose-cli.js +88 -0
  102. package/dist/commands/proposal/propose.js +23 -15
  103. package/dist/commands/proposal/repository.js +701 -278
  104. package/dist/commands/proposal/validators/proposal-quality-validators.js +2 -8
  105. package/dist/commands/proposal/validators/proposal-validators.js +55 -7
  106. package/dist/commands/proposal/validators/proposals.js +4 -7
  107. package/dist/commands/read/curate.js +34 -53
  108. package/dist/commands/read/knowledge.js +150 -95
  109. package/dist/commands/read/registry-search.js +2 -2
  110. package/dist/commands/read/remember-cli.js +42 -15
  111. package/dist/commands/read/search-cli.js +180 -78
  112. package/dist/commands/read/search.js +58 -43
  113. package/dist/commands/read/show.js +197 -141
  114. package/dist/commands/registry-cli.js +12 -51
  115. package/dist/commands/remember.js +14 -57
  116. package/dist/commands/sources/add-cli.js +100 -31
  117. package/dist/commands/sources/bundle-cli.js +166 -0
  118. package/dist/commands/sources/bundle-config-ops.js +7 -2
  119. package/dist/commands/sources/info.js +18 -5
  120. package/dist/commands/sources/init.js +12 -12
  121. package/dist/commands/sources/installed-stashes.js +382 -98
  122. package/dist/commands/sources/schema-repair.js +3 -2
  123. package/dist/commands/sources/self-update.js +131 -38
  124. package/dist/commands/sources/source-add.js +72 -17
  125. package/dist/commands/sources/source-clone.js +129 -45
  126. package/dist/commands/sources/source-manage.js +43 -23
  127. package/dist/commands/sources/sources-cli.js +57 -208
  128. package/dist/commands/sources/stash-cli.js +46 -53
  129. package/dist/commands/tasks/tasks-cli.js +91 -97
  130. package/dist/commands/tasks/tasks.js +276 -421
  131. package/dist/commands/workflow-cli.js +175 -450
  132. package/dist/core/adapter/adapters/akm-adapter.js +47 -28
  133. package/dist/core/adapter/adapters/akm-lint.js +42 -27
  134. package/dist/core/adapter/adapters/akm-metadata.js +15 -44
  135. package/dist/core/adapter/adapters/akm-task-adapter.js +15 -13
  136. package/dist/core/adapter/adapters/akm-workflow-adapter.js +55 -71
  137. package/dist/core/adapter/adapters/dotenv-adapter.js +1 -1
  138. package/dist/core/adapter/adapters/generic-files-adapter.js +2 -0
  139. package/dist/core/adapter/adapters/index.js +6 -6
  140. package/dist/core/adapter/adapters/llm-wiki-adapter.js +14 -8
  141. package/dist/core/adapter/adapters/okf-adapter.js +187 -19
  142. package/dist/core/adapter/adapters/shared.js +3 -19
  143. package/dist/core/adapter/adapters/tool-dir-shared.js +8 -3
  144. package/dist/core/adapter/adapters/website-snapshot-adapter.js +1 -0
  145. package/dist/core/adapter/detect-adapter.js +17 -0
  146. package/dist/core/adapter/recognize-match.js +6 -4
  147. package/dist/core/adapter/validate-context.js +214 -0
  148. package/dist/core/asset/akm-markdown.js +63 -0
  149. package/dist/core/asset/asset-placement.js +20 -6
  150. package/dist/core/asset/asset-ref.js +11 -9
  151. package/dist/core/asset/frontmatter-lint.js +30 -0
  152. package/dist/core/asset/frontmatter.js +25 -9
  153. package/dist/core/asset/markdown.js +40 -51
  154. package/dist/core/asset/resolve-ref.js +89 -18
  155. package/dist/core/asset/stash-meta.js +1 -1
  156. package/dist/core/bundle-id.js +51 -0
  157. package/dist/core/common.js +152 -38
  158. package/dist/core/config/config-io.js +12 -1
  159. package/dist/core/config/config-schema.js +35 -8
  160. package/dist/core/config/config-sources.js +55 -11
  161. package/dist/core/config/config-walker.js +25 -9
  162. package/dist/core/config/config.js +9 -48
  163. package/dist/core/config/experimental.js +21 -0
  164. package/dist/core/config/schema/embedding.js +5 -1
  165. package/dist/core/config/schema/experimental.js +30 -0
  166. package/dist/core/config/schema/improve-processes.js +0 -6
  167. package/dist/core/config/schema/improve.js +21 -3
  168. package/dist/core/config/schema/index-config.js +8 -15
  169. package/dist/core/config/schema/output.js +4 -1
  170. package/dist/core/config/schema/setup.js +9 -18
  171. package/dist/core/config/schema/sources-bundles.js +49 -33
  172. package/dist/core/config/schema/workflow.js +3 -3
  173. package/dist/core/env-secret-ref.js +76 -46
  174. package/dist/core/errors.js +18 -12
  175. package/dist/core/events.js +46 -128
  176. package/dist/core/file-change.js +6 -5
  177. package/dist/core/fs-txn.js +83 -7
  178. package/dist/core/git-message.js +2 -2
  179. package/dist/core/improve-result.js +1 -100
  180. package/dist/core/lesson-lint.js +1 -17
  181. package/dist/core/logs-db.js +2 -1
  182. package/dist/core/migration-operation.js +16 -0
  183. package/dist/core/mutation-target.js +78 -0
  184. package/dist/core/parse.js +4 -1
  185. package/dist/core/paths.js +17 -20
  186. package/dist/core/recognition-util.js +12 -14
  187. package/dist/core/redaction.js +34 -0
  188. package/dist/core/standards/resolve-standards-context.js +2 -14
  189. package/dist/core/standards/resolve-stash-standards.js +2 -2
  190. package/dist/core/standards/resolve-type-conventions.js +2 -2
  191. package/dist/core/state/migrations.js +41 -18
  192. package/dist/core/state-db.js +5 -14
  193. package/dist/core/structured.js +1 -1
  194. package/dist/core/subprocess.js +6 -4
  195. package/dist/core/text-truncation.js +9 -5
  196. package/dist/core/type-presentation.js +3 -3
  197. package/dist/core/warn.js +0 -3
  198. package/dist/core/write-source.js +771 -95
  199. package/dist/indexer/bundle-identity-guard.js +3 -2
  200. package/dist/indexer/db/graph-db.js +0 -24
  201. package/dist/indexer/ensure-index.js +1 -0
  202. package/dist/indexer/graph/graph-boost.js +9 -34
  203. package/dist/indexer/graph/graph-extraction.js +8 -5
  204. package/dist/indexer/index-writer-lock.js +53 -17
  205. package/dist/indexer/index-written-assets.js +16 -22
  206. package/dist/indexer/indexer.js +497 -239
  207. package/dist/indexer/installations.js +14 -96
  208. package/dist/indexer/passes/dir-staleness.js +16 -9
  209. package/dist/indexer/passes/memory-inference.js +11 -9
  210. package/dist/indexer/passes/metadata.js +113 -47
  211. package/dist/indexer/scan/doc-to-entry.js +38 -1
  212. package/dist/indexer/scan/drain-dir.js +13 -23
  213. package/dist/indexer/search/db-search.js +99 -54
  214. package/dist/indexer/search/fts-query.js +47 -24
  215. package/dist/indexer/search/ranking-contributors.js +42 -20
  216. package/dist/indexer/search/ranking.js +18 -99
  217. package/dist/indexer/search/search-fields.js +7 -2
  218. package/dist/indexer/search/search-source.js +82 -93
  219. package/dist/indexer/usage/usage-events.js +0 -89
  220. package/dist/indexer/walk/file-context.js +2 -1
  221. package/dist/indexer/walk/matchers.js +30 -43
  222. package/dist/indexer/walk/path-resolver.js +7 -2
  223. package/dist/indexer/walk/walker.js +38 -12
  224. package/dist/integrations/agent/builders.js +0 -6
  225. package/dist/integrations/agent/config.js +2 -2
  226. package/dist/integrations/agent/detect.js +49 -19
  227. package/dist/integrations/agent/engine-fallback.js +76 -0
  228. package/dist/integrations/agent/profiles.js +14 -0
  229. package/dist/integrations/agent/prompts.js +12 -8
  230. package/dist/integrations/agent/runner-dispatch.js +4 -2
  231. package/dist/integrations/agent/runner.js +0 -1
  232. package/dist/integrations/agent/spawn.js +5 -6
  233. package/dist/integrations/github.js +1 -1
  234. package/dist/integrations/harnesses/aider/agent-builder.js +6 -4
  235. package/dist/integrations/harnesses/amazonq/agent-builder.js +7 -4
  236. package/dist/integrations/harnesses/claude/session-log.js +0 -10
  237. package/dist/integrations/harnesses/codex/agent-builder.js +5 -2
  238. package/dist/integrations/harnesses/copilot/agent-builder.js +5 -3
  239. package/dist/integrations/harnesses/gemini/agent-builder.js +5 -3
  240. package/dist/integrations/harnesses/index.js +3 -7
  241. package/dist/integrations/harnesses/opencode/agent-builder.js +21 -2
  242. package/dist/integrations/harnesses/opencode/session-log.js +0 -15
  243. package/dist/integrations/harnesses/opencode-sdk/sdk-runner.js +13 -4
  244. package/dist/integrations/harnesses/openhands/agent-builder.js +9 -6
  245. package/dist/integrations/harnesses/pi/agent-builder.js +6 -4
  246. package/dist/integrations/lockfile.js +88 -7
  247. package/dist/integrations/session-logs/index.js +3 -28
  248. package/dist/llm/client.js +136 -100
  249. package/dist/llm/embedders/remote.js +13 -5
  250. package/dist/llm/feature-gate.js +4 -12
  251. package/dist/llm/graph-extract.js +5 -11
  252. package/dist/llm/memory-infer.js +144 -1
  253. package/dist/llm/metadata-enhance.js +5 -7
  254. package/dist/llm/structured-call.js +1 -1
  255. package/dist/llm/usage-persist.js +26 -5
  256. package/dist/llm/usage-telemetry.js +25 -2
  257. package/dist/output/cli-hints.js +1 -2
  258. package/dist/output/context.js +22 -7
  259. package/dist/output/format-exempt.js +80 -0
  260. package/dist/output/generic-render.js +259 -0
  261. package/dist/output/render-registry.js +57 -0
  262. package/dist/output/renderers.js +14 -36
  263. package/dist/output/shapes/curate.js +10 -1
  264. package/dist/output/shapes/events.js +12 -7
  265. package/dist/output/shapes/helpers.js +56 -83
  266. package/dist/output/shapes/migrate.js +8 -0
  267. package/dist/output/shapes/passthrough.js +7 -41
  268. package/dist/output/shapes/proposal/producer.js +15 -7
  269. package/dist/output/shapes.js +2 -9
  270. package/dist/output/text/{init.js → bundle-create.js} +3 -1
  271. package/dist/output/text/bundle-show.js +7 -0
  272. package/dist/output/text/command-format.js +164 -96
  273. package/dist/output/text/env.js +1 -3
  274. package/dist/output/text/events.js +8 -7
  275. package/dist/output/text/health-format.js +103 -0
  276. package/dist/output/text/health.js +7 -0
  277. package/dist/output/text/helpers.js +10 -8
  278. package/dist/output/text/lint-format.js +43 -0
  279. package/dist/output/text/{save.js → lint.js} +2 -2
  280. package/dist/output/text/migrate.js +88 -0
  281. package/dist/output/text/proposal/producer.js +4 -2
  282. package/dist/output/text/proposal-format.js +44 -72
  283. package/dist/output/text/registry-commands.js +1 -2
  284. package/dist/output/text/show-directives.js +15 -7
  285. package/dist/output/text/status-list.js +32 -0
  286. package/dist/output/text/sync.js +5 -0
  287. package/dist/output/text/workflow-format.js +24 -203
  288. package/dist/output/text/workflow.js +1 -7
  289. package/dist/output/text.js +16 -17
  290. package/dist/registry/factory.js +4 -6
  291. package/dist/registry/origin-resolve.js +16 -27
  292. package/dist/registry/providers/skills-sh.js +3 -3
  293. package/dist/registry/providers/static-index.js +13 -23
  294. package/dist/registry/resolve.js +42 -7
  295. package/dist/registry/semver.js +34 -84
  296. package/dist/runtime.js +2 -23
  297. package/dist/scripts/akm-migrate-node.js +60290 -0
  298. package/dist/scripts/akm-migrate.js +59628 -0
  299. package/dist/setup/detect.js +42 -15
  300. package/dist/setup/registry-stash-loader.js +2 -2
  301. package/dist/setup/setup.js +236 -136
  302. package/dist/setup/steps/connection.js +7 -9
  303. package/dist/setup/steps/platforms.js +9 -9
  304. package/dist/setup/steps/semantic.js +15 -3
  305. package/dist/setup/steps/sources.js +12 -13
  306. package/dist/setup/steps/stashdir.js +2 -3
  307. package/dist/setup/steps/tasks.js +237 -120
  308. package/dist/sources/freshness.js +1 -1
  309. package/dist/sources/provider-factory.js +11 -17
  310. package/dist/sources/providers/filesystem.js +2 -3
  311. package/dist/sources/providers/git-install.js +278 -34
  312. package/dist/sources/providers/git-provider.js +25 -23
  313. package/dist/sources/providers/git-stash.js +395 -106
  314. package/dist/sources/providers/git.js +2 -2
  315. package/dist/sources/providers/npm.js +16 -19
  316. package/dist/sources/providers/provider-utils.js +7 -4
  317. package/dist/sources/providers/sync-from-ref.js +3 -9
  318. package/dist/sources/providers/website.js +6 -1
  319. package/dist/sources/resolve.js +6 -5
  320. package/dist/sources/snapshot-fetchers/bluesky.js +146 -0
  321. package/dist/sources/snapshot-fetchers/content-extract.js +566 -0
  322. package/dist/sources/snapshot-fetchers/fetcher-util.js +41 -0
  323. package/dist/sources/snapshot-fetchers/github.js +100 -0
  324. package/dist/sources/snapshot-fetchers/host-guard.js +291 -0
  325. package/dist/sources/snapshot-fetchers/registry.js +17 -1
  326. package/dist/sources/snapshot-fetchers/robots.js +348 -0
  327. package/dist/sources/snapshot-fetchers/rss.js +282 -0
  328. package/dist/sources/snapshot-fetchers/secret-seam.js +42 -0
  329. package/dist/sources/snapshot-fetchers/website-ingest.js +566 -268
  330. package/dist/sources/snapshot-fetchers/x.js +910 -0
  331. package/dist/storage/database.js +7 -0
  332. package/dist/storage/engines/sqlite-migrations.js +23 -111
  333. package/dist/storage/managed-db.js +2 -2
  334. package/dist/storage/repositories/canaries-repository.js +1 -1
  335. package/dist/storage/repositories/events-repository.js +27 -11
  336. package/dist/storage/repositories/improve-runs-repository.js +6 -12
  337. package/dist/storage/repositories/index-connection.js +17 -6
  338. package/dist/storage/repositories/index-entries-repository.js +151 -240
  339. package/dist/storage/repositories/index-entry-mapper.js +15 -11
  340. package/dist/storage/repositories/index-fts-repository.js +5 -2
  341. package/dist/storage/repositories/index-llm-cache-repository.js +0 -1
  342. package/dist/storage/repositories/index-meta-repository.js +2 -3
  343. package/dist/storage/repositories/index-schema.js +10 -25
  344. package/dist/storage/repositories/index-utility-repository.js +15 -28
  345. package/dist/storage/repositories/index-vec-repository.js +6 -1
  346. package/dist/storage/repositories/outcome-repository.js +119 -0
  347. package/dist/storage/repositories/proposals-repository.js +296 -59
  348. package/dist/storage/repositories/registry-cache.js +19 -0
  349. package/dist/storage/repositories/salience-repository.js +172 -0
  350. package/dist/storage/repositories/task-history-repository.js +15 -13
  351. package/dist/storage/repositories/workflow-runs-repository.js +52 -40
  352. package/dist/tasks/backends/cron.js +105 -15
  353. package/dist/tasks/backends/index.js +1 -1
  354. package/dist/tasks/backends/launchd.js +85 -38
  355. package/dist/tasks/backends/schtasks.js +135 -15
  356. package/dist/tasks/embedded.js +56 -40
  357. package/dist/tasks/parser.js +7 -157
  358. package/dist/tasks/resolve-akm-bin.js +137 -59
  359. package/dist/tasks/runner.js +79 -42
  360. package/dist/tasks/scheduler-invocation.js +220 -10
  361. package/dist/tasks/schema.js +24 -1
  362. package/dist/tasks/task-id.js +1 -3
  363. package/dist/tasks/validator.js +20 -6
  364. package/dist/workflows/authoring/authoring.js +94 -143
  365. package/dist/workflows/authoring/scope-key.js +1 -1
  366. package/dist/workflows/exec/frozen-judge.js +28 -2
  367. package/dist/workflows/exec/native-executor.js +77 -57
  368. package/dist/workflows/exec/param-secrets.js +9 -9
  369. package/dist/workflows/exec/run-workflow.js +133 -79
  370. package/dist/workflows/exec/step-work.js +219 -346
  371. package/dist/{migrate-storage-node.mjs → workflows/exec/unit-dispatch.js} +1 -5
  372. package/dist/workflows/ir/compile.js +141 -270
  373. package/dist/workflows/ir/freeze.js +40 -30
  374. package/dist/workflows/ir/params.js +135 -11
  375. package/dist/workflows/ir/plan-hash.js +1 -1
  376. package/dist/workflows/ir/schema.js +25 -26
  377. package/dist/workflows/parser.js +872 -307
  378. package/dist/workflows/program/expressions.js +20 -208
  379. package/dist/workflows/program/schema.js +7 -10
  380. package/dist/workflows/renderer.js +95 -68
  381. package/dist/workflows/resource-limits.js +2 -0
  382. package/dist/workflows/runtime/checkin.js +3 -3
  383. package/dist/workflows/runtime/plan-classifier.js +16 -75
  384. package/dist/workflows/runtime/runs.js +186 -127
  385. package/dist/workflows/runtime/unit-checkin.js +1 -1
  386. package/dist/workflows/runtime/unit-phases.js +2 -2
  387. package/dist/workflows/runtime/workflow-asset-loader.js +232 -83
  388. package/dist/workflows/schema.js +1 -11
  389. package/dist/workflows/validate-summary.js +30 -36
  390. package/dist/workflows/validator.js +21 -62
  391. package/docs/README.md +68 -0
  392. package/docs/migration/README.md +8 -0
  393. package/docs/migration/release-notes/0.7.0.md +11 -11
  394. package/docs/migration/release-notes/0.9.0.md +208 -27
  395. package/docs/migration/v0.7-to-v0.8.md +46 -47
  396. package/docs/migration/v0.8-to-v0.9.md +564 -208
  397. package/docs/migration/v0.9.0-troubleshooting.md +561 -0
  398. package/docs/reference/README.md +12 -0
  399. package/docs/reference/cli.md +2253 -0
  400. package/docs/reference/configuration.md +358 -0
  401. package/docs/reference/data-and-telemetry.md +105 -42
  402. package/docs/reference/workflows.md +647 -0
  403. package/package.json +22 -11
  404. package/schemas/akm-asset-envelope.json +93 -0
  405. package/schemas/akm-config.json +81 -128
  406. package/schemas/akm-workflow.json +74 -73
  407. package/dist/assets/tasks/core/backup.yml +0 -5
  408. package/dist/assets/tasks/graph-refresh-weekly.yml +0 -10
  409. package/dist/cli/config-migrate.js +0 -1878
  410. package/dist/cli/config-validate.js +0 -41
  411. package/dist/commands/backup-cli.js +0 -56
  412. package/dist/commands/bundle/bundle-cli.js +0 -68
  413. package/dist/commands/bundle/bundle.js +0 -219
  414. package/dist/commands/graph/graph-cli.js +0 -124
  415. package/dist/commands/graph/graph.js +0 -489
  416. package/dist/commands/improve/extract-watch.js +0 -140
  417. package/dist/commands/mv-cli.js +0 -1221
  418. package/dist/commands/sources/history.js +0 -201
  419. package/dist/commands/tasks/default-tasks.js +0 -186
  420. package/dist/core/migration-backup.js +0 -1231
  421. package/dist/indexer/usage/unmigrated-vaults-guard.js +0 -95
  422. package/dist/llm/memory-infer-impl.js +0 -138
  423. package/dist/migrate/legacy/config-source-migration.js +0 -257
  424. package/dist/migrate/legacy/content-migration.js +0 -350
  425. package/dist/migrate/legacy/legacy-layout.js +0 -779
  426. package/dist/migrate/legacy/legacy-paths.js +0 -25
  427. package/dist/migrate/legacy/legacy-stash-json.js +0 -80
  428. package/dist/migrate/legacy/proposal-fs-import.js +0 -168
  429. package/dist/migrate/legacy/task-target-ref-migration.js +0 -278
  430. package/dist/migrate/legacy/three-db-cutover.js +0 -845
  431. package/dist/migrate/legacy/workflow-migrations-bodies.js +0 -52
  432. package/dist/migrate/legacy/workflow-migrations-frozen.js +0 -21
  433. package/dist/migrate/legacy-ref-grammar.js +0 -214
  434. package/dist/output/shapes/distill.js +0 -14
  435. package/dist/output/shapes/history.js +0 -11
  436. package/dist/output/text/distill.js +0 -6
  437. package/dist/output/text/enable-disable.js +0 -8
  438. package/dist/output/text/history.js +0 -6
  439. package/dist/registry/build-index.js +0 -382
  440. package/dist/schemas/akm-config.json +0 -4704
  441. package/dist/schemas/akm-task.json +0 -87
  442. package/dist/schemas/akm-workflow.json +0 -372
  443. package/dist/scripts/migrate-storage.js +0 -3816
  444. package/dist/workflows/authoring/workflow-program-template.yaml +0 -31
  445. package/dist/workflows/cli.js +0 -53
  446. package/dist/workflows/exec/brief.js +0 -481
  447. package/dist/workflows/exec/report.js +0 -1460
  448. package/dist/workflows/exec/watch.js +0 -116
  449. package/dist/workflows/program/parser.js +0 -813
  450. package/dist/workflows/program/project.js +0 -104
@@ -8,11 +8,11 @@
8
8
  * heuristic derivation, LLM enrichment) is testable in isolation and the
9
9
  * CLI entry point stays focused on argument parsing + output routing.
10
10
  */
11
- import { getParsedInvocation } from "../cli/invocation.js";
12
11
  import { serializeFrontmatter } from "../core/asset/asset-serialize.js";
13
12
  import { toErrorMessage, tryReadStdinText } from "../core/common.js";
14
13
  import { loadConfig } from "../core/config/config.js";
15
14
  import { UsageError } from "../core/errors.js";
15
+ import { parseEmbeddedJsonResponse } from "../core/parse.js";
16
16
  import { DURATION_UNITS, parseDuration as parseDurationSpec } from "../core/time.js";
17
17
  import { warn } from "../core/warn.js";
18
18
  import { SCOPE_KEYS } from "../indexer/passes/metadata.js";
@@ -150,7 +150,7 @@ export async function runLlmEnrich(body) {
150
150
  warn("Warning: --enrich requires an LLM to be configured. Run `akm setup` to configure one.");
151
151
  return { tags: [] };
152
152
  }
153
- const { chatCompletion, parseEmbeddedJsonResponse: parseJsonResponse } = await import("../llm/client.js");
153
+ const { chatCompletion } = await import("../llm/client.js");
154
154
  // #576: attribute this entry point's LLM call to the `remember` stage. The
155
155
  // wrapper is ambient — if a usage sink is active it tags the record; if not,
156
156
  // it is a no-op.
@@ -185,7 +185,7 @@ Return ONLY the JSON object, no prose, no markdown fences.`;
185
185
  }
186
186
  }
187
187
  })();
188
- const parsed = parseJsonResponse(result);
188
+ const parsed = parseEmbeddedJsonResponse(result);
189
189
  if (!parsed) {
190
190
  warn("Warning: --enrich received invalid JSON from the LLM. Writing memory without enrichment.");
191
191
  return { tags: [] };
@@ -204,57 +204,14 @@ Return ONLY the JSON object, no prose, no markdown fences.`;
204
204
  return { tags: [] };
205
205
  }
206
206
  }
207
- // ── Content-arg disambiguation ───────────────────────────────────────────────
208
- /**
209
- * Guard against citty consuming a global flag value as the `content` positional.
210
- *
211
- * When the user runs `akm remember --format json` without a content argument,
212
- * citty may assign `"json"` to the `content` positional because of how it
213
- * handles flag order. This helper detects that case and returns `undefined`
214
- * so `readMemoryContent` falls through to stdin.
215
- */
216
- export function resolveRememberContentArg(content) {
217
- if (content === undefined)
218
- return undefined;
219
- const invocation = getParsedInvocation();
220
- const parsedFormat = invocation.getFlagValue("--format");
221
- if (parsedFormat !== undefined &&
222
- content === parsedFormat &&
223
- wasRememberFlagValueConsumedAsContent(content, parsedFormat, "--format")) {
224
- return undefined;
225
- }
226
- const parsedDetail = invocation.getFlagValue("--detail");
227
- if (parsedDetail !== undefined &&
228
- content === parsedDetail &&
229
- wasRememberFlagValueConsumedAsContent(content, parsedDetail, "--detail")) {
230
- return undefined;
231
- }
232
- return content;
233
- }
234
- function wasRememberFlagValueConsumedAsContent(content, flagValue, flagName) {
235
- const argv = getParsedInvocation().userArgs;
236
- const rememberIndex = argv.indexOf("remember");
237
- const tokens = rememberIndex >= 0 ? argv.slice(rememberIndex + 1) : argv;
238
- let flagIndex = -1;
239
- let flagConsumesNextToken = false;
240
- for (let i = 0; i < tokens.length; i += 1) {
241
- const token = tokens[i];
242
- if (token === flagName) {
243
- flagIndex = i;
244
- flagConsumesNextToken = true;
245
- break;
246
- }
247
- if (token === `${flagName}=${flagValue}`) {
248
- flagIndex = i;
249
- break;
250
- }
251
- }
252
- if (flagIndex === -1)
253
- return false;
254
- if (tokens.slice(0, flagIndex).includes(content))
255
- return false;
256
- const firstTokenAfterFlag = flagIndex + (flagConsumesNextToken ? 2 : 1);
257
- if (tokens.slice(firstTokenAfterFlag).includes(content))
258
- return false;
259
- return true;
260
- }
207
+ // R-061(c): `resolveRememberContentArg` / `wasRememberFlagValueConsumedAsContent`
208
+ // used to live here — a heuristic guarding against citty consuming a global
209
+ // flag's value (`--format`/`--detail`) as the `content` positional. That guard
210
+ // predated `rememberCommand` declaring `GLOBAL_OUTPUT_ARGS` (via
211
+ // `defineJsonCommand`): once the leaf command itself declares `format`/
212
+ // `detail` args, citty's parser consumes their space-separated values as the
213
+ // flag's own value and never assigns them to `content` in the first place, so
214
+ // the heuristic no longer has anything to guard against. Verified: `akm
215
+ // remember "yaml" --format yaml` and `akm remember "brief" --detail brief`
216
+ // both write the literal content unchanged. Deleted rather than kept as
217
+ // defense-in-depth per the cleanup program's dead-code policy.
@@ -4,6 +4,7 @@
4
4
  import fs from "node:fs";
5
5
  import path from "node:path";
6
6
  import * as p from "../../cli/clack.js";
7
+ import { getStringArg, parsePositiveIntFlag } from "../../cli/parse-args.js";
7
8
  import { defineJsonCommand, output } from "../../cli/shared.js";
8
9
  import { decideDangerousKeyInstall } from "../../core/activation-policy.js";
9
10
  import { UsageError } from "../../core/errors.js";
@@ -14,12 +15,11 @@ import { akmAdd } from "./source-add.js";
14
15
  import { addStash } from "./source-manage.js";
15
16
  // ── Shared website-options helper ──────────
16
17
  export function buildWebsiteOptions(args) {
17
- const websiteOptions = {};
18
- if (typeof args["max-pages"] === "string" && args["max-pages"].length > 0)
19
- websiteOptions.maxPages = args["max-pages"];
20
- if (typeof args["max-depth"] === "string" && args["max-depth"].length > 0)
21
- websiteOptions.maxDepth = args["max-depth"];
22
- return websiteOptions;
18
+ // getStringArg maps absent/blank to undefined; parsePositiveIntFlag maps
19
+ // undefined to undefined so an unsupplied flag simply omits the key.
20
+ const maxPages = parsePositiveIntFlag(getStringArg(args, "max-pages"), "--max-pages");
21
+ const maxDepth = parsePositiveIntFlag(getStringArg(args, "max-depth"), "--max-depth");
22
+ return { ...(maxPages !== undefined ? { maxPages } : {}), ...(maxDepth !== undefined ? { maxDepth } : {}) };
23
23
  }
24
24
  // ── HTTP safety check ─────────────────────────────────────────────────────────
25
25
  export function shouldWarnOnPlainHttp(ref) {
@@ -38,24 +38,84 @@ export function shouldWarnOnPlainHttp(ref) {
38
38
  return true;
39
39
  }
40
40
  }
41
- /** Scan every env file in the freshly-installed stash for dangerous env keys. */
41
+ /**
42
+ * Recursively enumerate every real env FILE under `rootDir` (a stash's
43
+ * `env/` directory). "Real env file" is the SAME test used everywhere else
44
+ * in akm decides what `akm env run` / `akm env list` / the indexer will
45
+ * actually load as environment variables — `fileName === ".env" ||
46
+ * fileName.endsWith(".env")` — see `src/indexer/walk/matchers.ts` and
47
+ * `listEnvsRecursive` in `src/commands/env/env-cli.ts`. Reusing that exact
48
+ * rule here (rather than inventing a second, possibly-divergent one) is what
49
+ * closes the nested-directory bypass: `env/nested/inner.env` IS loaded by
50
+ * `akm env run` today via that same recursive walk, so it must be scanned
51
+ * too (previously only a flat, non-recursive `env/*.env` listing was
52
+ * scanned).
53
+ *
54
+ * Deliberately excludes files that do NOT end in `.env` (e.g.
55
+ * `env/notes.txt`). Such a file is never sourced as environment variables by
56
+ * any akm codepath, so a dangerous key sitting in its contents cannot hijack
57
+ * process execution via `akm env run` — there is no live attack through this
58
+ * gate for it. Residual gap: this scanner does not protect against an
59
+ * operator later renaming/copying that file to a `*.env` name, or feeding it
60
+ * into `akm env run` through some other mechanism outside akm; closing that
61
+ * would mean content-sniffing every byte of every file in the stash (README,
62
+ * lockfiles, binaries, …) for a vector that requires a subsequent human
63
+ * action to become live, which is a materially different and much more
64
+ * expensive check than "is this a file `akm` will actually source".
65
+ */
66
+ function collectEnvFilePathsRecursive(rootDir) {
67
+ const results = [];
68
+ const walk = (dir) => {
69
+ let entries;
70
+ try {
71
+ entries = fs.readdirSync(dir, { withFileTypes: true });
72
+ }
73
+ catch {
74
+ return;
75
+ }
76
+ for (const entry of entries) {
77
+ const full = path.join(dir, entry.name);
78
+ if (entry.isDirectory()) {
79
+ walk(full);
80
+ continue;
81
+ }
82
+ if (!entry.isFile())
83
+ continue;
84
+ if (entry.name !== ".env" && !entry.name.endsWith(".env"))
85
+ continue;
86
+ results.push(full);
87
+ }
88
+ };
89
+ walk(rootDir);
90
+ return results;
91
+ }
92
+ /** Scan every real env file (recursively, see {@link collectEnvFilePathsRecursive}) in the freshly-installed stash for dangerous env keys. */
42
93
  function collectDangerousKeyFindings(installedStashRoot, checkEnvForDangerousKeys) {
43
94
  const allFindings = [];
44
95
  const subdir = "env";
45
96
  const prefix = "env";
46
97
  const dir = path.join(installedStashRoot, subdir);
47
- const envFiles = fs.existsSync(dir) ? fs.readdirSync(dir).filter((f) => f.endsWith(".env")) : [];
48
- for (const envFile of envFiles) {
49
- const envPath = path.join(dir, envFile);
50
- const baseName = path.basename(envFile, ".env");
51
- const vaultRef = baseName === "" ? `${prefix}:default` : `${prefix}:${baseName}`;
52
- const relPath = path.join(subdir, envFile);
53
- const findings = checkEnvForDangerousKeys(envPath, relPath, vaultRef);
98
+ if (!fs.existsSync(dir))
99
+ return allFindings;
100
+ for (const envPath of collectEnvFilePathsRecursive(dir)) {
101
+ const relToEnvDir = path.relative(dir, envPath);
102
+ // Only the LAST path segment carries the ".env" suffix / dotfile-default
103
+ // naming rule; intermediate directory segments pass through unchanged.
104
+ // e.g. "nested/inner.env" -> "nested/inner", "nested/.env" ->
105
+ // "nested/default" (not "nested/", which is what naively stripping the
106
+ // suffix from the whole relative path would produce).
107
+ const segments = relToEnvDir.split(path.sep);
108
+ const lastSegment = segments[segments.length - 1] ?? "";
109
+ const lastSegmentBase = lastSegment.endsWith(".env") ? lastSegment.slice(0, -".env".length) : lastSegment;
110
+ const refSegments = [...segments.slice(0, -1), lastSegmentBase === "" ? "default" : lastSegmentBase];
111
+ const envRef = `${prefix}/${refSegments.join("/")}`;
112
+ const relPath = path.join(subdir, relToEnvDir);
113
+ const findings = checkEnvForDangerousKeys(envPath, relPath, envRef);
54
114
  for (const finding of findings) {
55
115
  // Extract the key name from the detail string for the summary line.
56
116
  const keyMatch = finding.detail.match(/Env key `([^`]+)`/);
57
117
  const keyName = keyMatch ? keyMatch[1] : finding.file;
58
- allFindings.push({ vaultRef, keyName, relPath });
118
+ allFindings.push({ envRef, keyName, relPath });
59
119
  }
60
120
  }
61
121
  return allFindings;
@@ -91,7 +151,7 @@ export async function auditInstalledStashForDangerousKeys(opts) {
91
151
  if (stance === "warn-allow") {
92
152
  // Operator has explicitly accepted the risk — warn and continue.
93
153
  for (const f of allFindings) {
94
- warn(`[dangerous-vault-key] ${f.relPath}: key \`${f.keyName}\` in ${f.vaultRef} can hijack process execution via \`akm env run\`. Proceeding because --allow-insecure was set.`);
154
+ warn(`[dangerous-env-key] ${f.relPath}: key \`${f.keyName}\` in ${f.envRef} can hijack process execution via \`akm env run\`. Proceeding because --allow-insecure was set.`);
95
155
  }
96
156
  return { blocked: false };
97
157
  }
@@ -105,7 +165,7 @@ export async function auditInstalledStashForDangerousKeys(opts) {
105
165
  }
106
166
  catch (_rollbackErr) {
107
167
  return (`Rollback failed — stash may still be installed at ${installedStashRoot}. ` +
108
- `Remove it manually with: akm remove ${rollbackTarget}`);
168
+ `Remove it manually with: akm bundle remove ${rollbackTarget}`);
109
169
  }
110
170
  }
111
171
  if (isTTY) {
@@ -113,14 +173,14 @@ export async function auditInstalledStashForDangerousKeys(opts) {
113
173
  // Guard on stdin (not stdout) because p.confirm() reads from stdin;
114
174
  // stdout may be a TTY while stdin is piped, which would cause a hang.
115
175
  const stashLabel = ref;
116
- const groupedByVault = new Map();
176
+ const groupedByEnv = new Map();
117
177
  for (const f of allFindings) {
118
- const existing = groupedByVault.get(f.vaultRef) ?? [];
178
+ const existing = groupedByEnv.get(f.envRef) ?? [];
119
179
  existing.push(f.keyName);
120
- groupedByVault.set(f.vaultRef, existing);
180
+ groupedByEnv.set(f.envRef, existing);
121
181
  }
122
- for (const [vaultRef, keys] of groupedByVault) {
123
- warn(`[warn] Env "${vaultRef}" in stash "${stashLabel}" contains potentially dangerous keys:`);
182
+ for (const [envRef, keys] of groupedByEnv) {
183
+ warn(`[warn] Env "${envRef}" in stash "${stashLabel}" contains potentially dangerous keys:`);
124
184
  for (const key of keys) {
125
185
  warn(` - ${key}: can hijack process execution via \`akm env run\``);
126
186
  }
@@ -134,7 +194,7 @@ export async function auditInstalledStashForDangerousKeys(opts) {
134
194
  console.error(JSON.stringify({
135
195
  ok: false,
136
196
  error: "Install aborted: stash contains dangerous env keys. Remove the keys or re-run with --allow-insecure to bypass.",
137
- code: "DANGEROUS_VAULT_KEY",
197
+ code: "DANGEROUS_ENV_KEY",
138
198
  ...(rollbackWarning ? { rollbackWarning } : {}),
139
199
  }, null, 2));
140
200
  return { blocked: true, exitCode: 1 };
@@ -144,11 +204,11 @@ export async function auditInstalledStashForDangerousKeys(opts) {
144
204
  }
145
205
  // Non-interactive path without bypass flag: fail hard.
146
206
  const rollbackWarning = await rollback();
147
- const keyList = allFindings.map((f) => ` - ${f.keyName} (${f.vaultRef})`).join("\n");
207
+ const keyList = allFindings.map((f) => ` - ${f.keyName} (${f.envRef})`).join("\n");
148
208
  console.error(JSON.stringify({
149
209
  ok: false,
150
210
  error: `Install blocked: stash "${ref}" contains dangerous env keys that can hijack process execution via \`akm env run\`:\n${keyList}\nRe-run with --allow-insecure to bypass this check after reviewing the env file.`,
151
- code: "DANGEROUS_VAULT_KEY",
211
+ code: "DANGEROUS_ENV_KEY",
152
212
  ...(rollbackWarning ? { rollbackWarning } : {}),
153
213
  }, null, 2));
154
214
  return { blocked: true, exitCode: 1 };
@@ -170,14 +230,14 @@ export const addCommand = defineJsonCommand({
170
230
  name: { type: "string", description: "Human-friendly name for the source" },
171
231
  writable: {
172
232
  type: "boolean",
173
- description: "Mark a git stash as writable so changes can be pushed back",
233
+ description: "Mark a git bundle as writable so changes can be pushed back",
174
234
  default: false,
175
235
  },
176
236
  "max-pages": { type: "string", description: "Maximum pages to crawl for website sources (default: 50)" },
177
237
  "max-depth": { type: "string", description: "Maximum crawl depth for website sources (default: 3)" },
178
238
  "allow-insecure": {
179
239
  type: "boolean",
180
- description: "Allow a plain HTTP source URL and skip confirmation for dangerous env keys (e.g. LD_PRELOAD, PATH). Use only after explicitly reviewing the stash.",
240
+ description: "Allow a plain HTTP source URL and skip confirmation for dangerous env keys (e.g. LD_PRELOAD, PATH). Use only after explicitly reviewing the bundle.",
181
241
  default: false,
182
242
  },
183
243
  },
@@ -185,7 +245,9 @@ export const addCommand = defineJsonCommand({
185
245
  const ref = args.ref.trim();
186
246
  const allowInsecure = args["allow-insecure"];
187
247
  const allowDangerousKeys = allowInsecure;
188
- // URL with --provider → stash source (remote or git provider)
248
+ // --provider → declarative bundle source (URL for git/website; bare
249
+ // package spec for npm — R-013). Config-only write; content is not
250
+ // synced until a later `akm update`.
189
251
  if (args.provider) {
190
252
  if (shouldWarnOnPlainHttp(ref)) {
191
253
  if (!allowInsecure) {
@@ -257,8 +319,14 @@ export const addCommand = defineJsonCommand({
257
319
  // Use the canonical installed id (most reliably resolved by akmRemove) rather
258
320
  // than the raw user-supplied ref which may not match after URL normalisation.
259
321
  const rollbackTarget = result.installed?.id ?? result.sourceAdded?.stashRoot ?? ref;
260
- // The audit RETURNS its decision; we decide `process.exit` here, OUTSIDE
261
- // any catch, so the abort cannot be lost to a swallowed exception (C3).
322
+ // The audit RETURNS its decision; we decide the exit outcome here,
323
+ // OUTSIDE any catch, so the abort cannot be lost to a swallowed
324
+ // exception (C3). F4: `process.exitCode = …; return;` instead of
325
+ // `process.exit(...)` — the DANGEROUS_ENV_KEY error envelope has
326
+ // already been written to stderr by the audit itself (or is about to
327
+ // be), so an explicit `return` here (skipping the success `output("add",
328
+ // …)` below) is load-bearing, not merely cosmetic: it is what stops a
329
+ // blocked install from ALSO printing a success envelope.
262
330
  const decision = await auditInstalledStashForDangerousKeys({
263
331
  installedStashRoot,
264
332
  ref,
@@ -267,7 +335,8 @@ export const addCommand = defineJsonCommand({
267
335
  isTTY: process.stdin.isTTY === true,
268
336
  });
269
337
  if (decision.blocked) {
270
- process.exit(decision.exitCode);
338
+ process.exitCode = decision.exitCode;
339
+ return;
271
340
  }
272
341
  }
273
342
  output("add", result);
@@ -0,0 +1,166 @@
1
+ // This Source Code Form is subject to the terms of the Mozilla Public
2
+ // License, v. 2.0. If a copy of the MPL was not distributed with this
3
+ // file, You can obtain one at https://mozilla.org/MPL/2.0/.
4
+ /**
5
+ * `akm bundle` command group — 0.9 CLI overhaul (S7).
6
+ *
7
+ * Consolidates the five former top-level bundle-lifecycle commands under one
8
+ * group, hard break, no alias kept at the top level:
9
+ * - `create` (= old top-level `init`, src/commands/sources/init.ts)
10
+ * - `add` (= old top-level `add`, src/commands/sources/add-cli.ts — the
11
+ * command definition moved here; `add-cli.ts` keeps its exported helpers,
12
+ * including `auditInstalledStashForDangerousKeys`, which a test imports
13
+ * directly)
14
+ * - `list` (= old top-level `list`)
15
+ * - `remove` (= old top-level `remove`)
16
+ * - `update` (= old top-level `update`)
17
+ * - `show <name>` — NEW: a single-bundle detail view of the `list` payload.
18
+ *
19
+ * `add`/`list`/`remove`/`update` are pure relocations (payload and output
20
+ * shape unchanged: `SourceEntry`/`RemoveResponse`/`UpdateResponse` keep their
21
+ * bare `add`/`list`/`remove`/`update` output shape). `create` is a genuine
22
+ * verb rename (was `init`), so its output shape is renamed to `bundle-create`
23
+ * to match (the same convention S8 used for `propose` -> `proposal new`'s
24
+ * `proposal-new` shape). `show` is a new shape, `bundle-show`.
25
+ */
26
+ import { defineGroupCommand, defineJsonCommand, output } from "../../cli/shared.js";
27
+ import { NotFoundError, UsageError } from "../../core/errors.js";
28
+ import { appendEvent } from "../../core/events.js";
29
+ import { addCommand } from "./add-cli.js";
30
+ import { akmInit } from "./init.js";
31
+ import { akmListSources, akmRemove, akmUpdate } from "./installed-stashes.js";
32
+ const VALID_SOURCE_KINDS = new Set(["filesystem", "git", "npm", "website"]);
33
+ function parseKindFilter(raw) {
34
+ if (!raw)
35
+ return undefined;
36
+ const kinds = raw.split(",").map((s) => s.trim());
37
+ for (const k of kinds) {
38
+ if (!VALID_SOURCE_KINDS.has(k)) {
39
+ throw new UsageError(`Invalid --kind value: "${k}". Expected one of: filesystem, git, npm, website`);
40
+ }
41
+ }
42
+ return kinds;
43
+ }
44
+ const createCommand = defineJsonCommand({
45
+ meta: {
46
+ name: "create",
47
+ description: "Initialize akm's working bundle directory and persist it in config",
48
+ },
49
+ args: {
50
+ dir: { type: "string", description: "Custom bundle directory path (default: ~/akm)" },
51
+ "set-default": {
52
+ type: "boolean",
53
+ description: "Make --dir the default bundle. Without this, `akm bundle create --dir X` scaffolds X but leaves your existing default bundle unchanged.",
54
+ default: false,
55
+ },
56
+ },
57
+ async run({ args }) {
58
+ const result = await akmInit({
59
+ dir: args.dir,
60
+ setDefault: args["set-default"],
61
+ });
62
+ output("bundle-create", result);
63
+ },
64
+ });
65
+ const listCommand = defineJsonCommand({
66
+ meta: { name: "list", description: "List configured bundles and their resolved source state" },
67
+ args: {
68
+ kind: {
69
+ type: "string",
70
+ description: "Filter by source provider (filesystem, git, npm, website). Comma-separated.",
71
+ },
72
+ },
73
+ async run({ args }) {
74
+ const kind = parseKindFilter(args.kind);
75
+ const result = await akmListSources({ kind });
76
+ output("list", result);
77
+ },
78
+ });
79
+ const showCommand = defineJsonCommand({
80
+ meta: { name: "show", description: "Show detail for a single configured bundle" },
81
+ args: {
82
+ name: { type: "positional", description: "Bundle name (as reported by `akm bundle list`)", required: true },
83
+ },
84
+ async run({ args }) {
85
+ const { sources } = await akmListSources();
86
+ const entry = sources.find((s) => s.name === args.name);
87
+ if (!entry) {
88
+ throw new NotFoundError(`No configured bundle named "${args.name}"`, "SOURCE_NOT_FOUND");
89
+ }
90
+ output("bundle-show", entry);
91
+ },
92
+ });
93
+ const removeCommand = defineJsonCommand({
94
+ meta: { name: "remove", description: "Remove a bundle by id, ref, path, URL, or name" },
95
+ args: {
96
+ target: { type: "positional", description: "Bundle to remove (id, ref, path, URL, or name)", required: true },
97
+ yes: { type: "boolean", alias: "y", description: "Skip confirmation prompt", default: false },
98
+ },
99
+ async run({ args }) {
100
+ const { confirmDestructive } = await import("../../cli/confirm.js");
101
+ const confirmed = await confirmDestructive(`Remove bundle "${args.target}"? This cannot be undone.`, {
102
+ yes: args.yes === true,
103
+ });
104
+ if (!confirmed) {
105
+ process.stderr.write("Aborted.\n");
106
+ return;
107
+ }
108
+ const result = await akmRemove({ target: args.target });
109
+ appendEvent({
110
+ eventType: "remove",
111
+ metadata: {
112
+ target: args.target,
113
+ ref: typeof result.removed?.ref === "string" ? result.removed.ref : null,
114
+ id: typeof result.removed?.id === "string" ? result.removed.id : null,
115
+ },
116
+ });
117
+ output("remove", result);
118
+ },
119
+ });
120
+ const updateCommand = defineJsonCommand({
121
+ meta: { name: "update", description: "Update one or all managed bundles" },
122
+ args: {
123
+ target: { type: "positional", description: "Bundle to update (id or ref)", required: false },
124
+ all: { type: "boolean", description: "Update all installed entries", default: false },
125
+ force: { type: "boolean", description: "Force fresh download even if version is unchanged", default: false },
126
+ // F1/R-058: gates ONLY the rare branch where the resolved content
127
+ // directory moves and the previous `localRoot` is deleted — a normal
128
+ // refresh (the overwhelming majority of updates) deletes nothing and
129
+ // never consults this flag. Mirrors `remove`'s `-y/--yes`.
130
+ yes: {
131
+ type: "boolean",
132
+ alias: "y",
133
+ description: "Skip the confirmation prompt when an update needs to delete a previous install directory (because the resolved content location moved). No effect on a normal refresh, which deletes nothing.",
134
+ default: false,
135
+ },
136
+ },
137
+ async run({ args }) {
138
+ const result = await akmUpdate({ target: args.target, all: args.all, force: args.force, yes: args.yes });
139
+ appendEvent({
140
+ eventType: "update",
141
+ metadata: {
142
+ target: args.target ?? null,
143
+ all: args.all === true,
144
+ force: args.force === true,
145
+ processed: Array.isArray(result.processed)
146
+ ? result.processed.length
147
+ : 0,
148
+ },
149
+ });
150
+ output("update", result);
151
+ },
152
+ });
153
+ export const bundleCommand = defineGroupCommand({
154
+ meta: {
155
+ name: "bundle",
156
+ description: "Create, add to, inspect, and manage your bundles (working bundle + configured sources)",
157
+ },
158
+ subCommands: {
159
+ create: createCommand,
160
+ add: addCommand,
161
+ list: listCommand,
162
+ show: showCommand,
163
+ remove: removeCommand,
164
+ update: updateCommand,
165
+ },
166
+ });
@@ -6,7 +6,7 @@
6
6
  *
7
7
  * The `stashDir`/`sources[]`/`installed[]` trio is retired: every source is now
8
8
  * a `bundles.<slug>` entry, and the primary working stash is the `defaultBundle`.
9
- * These helpers give the source writers (`akm init`, `akm setup`, `akm add`,
9
+ * These helpers give the source writers (`akm bundle create`, `akm setup`, `akm bundle add`,
10
10
  * `akm sources add/remove`) one place to derive slug-legal bundle keys (via the
11
11
  * shared {@link deriveBundleId} — D-R5), locate an existing bundle by its source
12
12
  * descriptor, and set/repoint the primary bundle — without duplicating the
@@ -28,7 +28,12 @@ export function withPrimaryBundle(config, stashDir) {
28
28
  if (!key || !(key in bundles) || typeof bundles[key]?.path !== "string") {
29
29
  key = deriveBundleId(undefined, stashDir, new Set(Object.keys(bundles)));
30
30
  }
31
- bundles[key] = { ...bundles[key], path: stashDir, writable: true };
31
+ bundles[key] = {
32
+ ...bundles[key],
33
+ path: stashDir,
34
+ writable: true,
35
+ components: { main: { root: ".", adapter: "akm", writable: true } },
36
+ };
32
37
  return { ...config, bundles, defaultBundle: key };
33
38
  }
34
39
  /** Existing bundle key whose filesystem descriptor resolves to `resolvedPath`, or `undefined`. */
@@ -3,11 +3,13 @@
3
3
  // file, You can obtain one at https://mozilla.org/MPL/2.0/.
4
4
  import fs from "node:fs";
5
5
  import { placementTypes } from "../../core/asset/asset-placement.js";
6
+ import { resolveStashDir } from "../../core/common.js";
6
7
  import { getSources, loadConfig } from "../../core/config/config.js";
7
8
  import { getDbPath } from "../../core/paths.js";
9
+ import { error } from "../../core/warn.js";
8
10
  import { getEffectiveSemanticStatus, readSemanticStatus } from "../../indexer/search/semantic-status.js";
9
11
  import { closeDatabase, openExistingDatabase } from "../../storage/repositories/index-connection.js";
10
- import { getEntryCount } from "../../storage/repositories/index-entries-repository.js";
12
+ import { getEntryCount, getEntryCountByType } from "../../storage/repositories/index-entries-repository.js";
11
13
  import { getMeta } from "../../storage/repositories/index-meta-repository.js";
12
14
  import { isVecAvailable } from "../../storage/repositories/index-vec-repository.js";
13
15
  import { pkgVersion } from "../../version.js";
@@ -19,6 +21,11 @@ import { pkgVersion } from "../../version.js";
19
21
  */
20
22
  export function assembleInfo(options) {
21
23
  const config = loadConfig();
24
+ // Primary stash directory + default bundle name — same resolution
25
+ // `akm sources list` uses (R-057), so `akm info` and `akm sources list`
26
+ // agree on which stash is primary.
27
+ const stashDir = resolveStashDir();
28
+ const defaultBundle = config.defaultBundle ?? null;
22
29
  // Asset types (copy into a mutable array — `placementTypes()` returns readonly)
23
30
  const assetTypes = [...placementTypes()];
24
31
  const semanticRuntime = readSemanticStatus();
@@ -45,14 +52,16 @@ export function assembleInfo(options) {
45
52
  ...(s.url ? { url: s.url } : {}),
46
53
  ...(s.enabled !== undefined ? { enabled: s.enabled } : {}),
47
54
  }));
48
- // Index stats — resolve the DB path from config so info reads the same
49
- // database that health and search use, rather than a bare getDbPath() call
50
- // that ignores XDG_DATA_HOME or per-config overrides.
55
+ // Index stats — `options.dbPath` is a test-only override (see the param
56
+ // doc above); real callers fall through to the same `getDbPath()` that
57
+ // health and search use, so info reads the same database they do.
51
58
  const resolvedDbPath = options?.dbPath ?? getDbPath();
52
59
  const indexStats = readIndexStats(resolvedDbPath);
53
60
  return {
54
61
  schemaVersion: 1,
55
62
  version: pkgVersion,
63
+ bundleDir: stashDir,
64
+ defaultBundle,
56
65
  assetTypes,
57
66
  searchModes,
58
67
  semanticSearch: {
@@ -69,6 +78,7 @@ export function assembleInfo(options) {
69
78
  function readIndexStats(resolvedPath) {
70
79
  const EMPTY = {
71
80
  entryCount: 0,
81
+ byType: {},
72
82
  lastBuiltAt: null,
73
83
  hasEmbeddings: false,
74
84
  vecAvailable: false,
@@ -80,6 +90,7 @@ function readIndexStats(resolvedPath) {
80
90
  db = openExistingDatabase(resolvedPath);
81
91
  return {
82
92
  entryCount: getEntryCount(db),
93
+ byType: getEntryCountByType(db),
83
94
  lastBuiltAt: getMeta(db, "builtAt") ?? null,
84
95
  hasEmbeddings: getMeta(db, "hasEmbeddings") === "1",
85
96
  vecAvailable: isVecAvailable(db),
@@ -88,7 +99,9 @@ function readIndexStats(resolvedPath) {
88
99
  catch (err) {
89
100
  // Surface the error so operators can diagnose mismatches between
90
101
  // `akm info` and `akm health` rather than silently returning zeros.
91
- process.stderr.write(`[akm info] failed to read index stats from ${resolvedPath}: ${String(err)}\n`);
102
+ // Routed through core/warn's `error()` (not a raw process.stderr.write)
103
+ // so `--quiet`/`setQuiet()` actually gate this line (R-057).
104
+ error(`[akm info] failed to read index stats from ${resolvedPath}: ${String(err)}`);
92
105
  return EMPTY;
93
106
  }
94
107
  finally {