akm-cli 0.8.1 → 0.9.0-beta.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 (318) hide show
  1. package/CHANGELOG.md +258 -0
  2. package/dist/assets/help/help-proposals.md +1 -2
  3. package/dist/assets/hints/cli-hints-full.md +34 -19
  4. package/dist/assets/hints/cli-hints-short.md +1 -1
  5. package/dist/assets/profiles/catchup.json +13 -0
  6. package/dist/assets/profiles/consolidate.json +13 -0
  7. package/dist/assets/profiles/frequent.json +13 -0
  8. package/dist/assets/stash-skeleton/README.md +76 -0
  9. package/dist/assets/tasks/core/backup.yml +4 -0
  10. package/dist/assets/tasks/core/extract.yml +4 -0
  11. package/dist/assets/tasks/core/improve.yml +4 -0
  12. package/dist/assets/tasks/core/index-refresh.yml +4 -0
  13. package/dist/assets/tasks/core/sync.yml +4 -0
  14. package/dist/assets/tasks/core/update-stashes.yml +4 -0
  15. package/dist/assets/tasks/core/version-check.yml +4 -0
  16. package/dist/cli/config-migrate.js +6 -6
  17. package/dist/cli/config-validate.js +4 -4
  18. package/dist/cli/confirm.js +3 -3
  19. package/dist/cli/parse-args.js +1 -1
  20. package/dist/cli/shared.js +51 -14
  21. package/dist/cli-node.mjs +26 -0
  22. package/dist/cli.js +171 -3857
  23. package/dist/commands/{agent-dispatch.js → agent/agent-dispatch.js} +6 -6
  24. package/dist/commands/{agent-support.js → agent/agent-support.js} +2 -2
  25. package/dist/commands/agent/contribute-cli.js +200 -0
  26. package/dist/commands/completions.js +1 -1
  27. package/dist/commands/config-cli.js +240 -3
  28. package/dist/commands/config-edit.js +344 -0
  29. package/dist/commands/db-cli.js +2 -2
  30. package/dist/commands/env/env-cli.js +529 -0
  31. package/dist/commands/env/env.js +410 -0
  32. package/dist/commands/env/secret-cli.js +259 -0
  33. package/dist/commands/{secret.js → env/secret.js} +6 -47
  34. package/dist/commands/events.js +4 -4
  35. package/dist/commands/feedback-cli.js +18 -34
  36. package/dist/commands/graph/graph-cli.js +132 -0
  37. package/dist/commands/{graph.js → graph/graph.js} +22 -16
  38. package/dist/commands/health/checks.js +279 -0
  39. package/dist/commands/health.js +101 -249
  40. package/dist/commands/{consolidate.js → improve/consolidate.js} +52 -40
  41. package/dist/commands/{distill-promotion-policy.js → improve/distill-promotion-policy.js} +3 -3
  42. package/dist/commands/{distill.js → improve/distill.js} +39 -18
  43. package/dist/commands/{eval-cases.js → improve/eval-cases.js} +1 -1
  44. package/dist/commands/{extract-cli.js → improve/extract-cli.js} +4 -4
  45. package/dist/commands/{extract-prompt.js → improve/extract-prompt.js} +2 -2
  46. package/dist/commands/{extract.js → improve/extract.js} +185 -26
  47. package/dist/commands/{improve-auto-accept.js → improve/improve-auto-accept.js} +4 -4
  48. package/dist/commands/{improve-cli.js → improve/improve-cli.js} +45 -23
  49. package/dist/commands/{improve-profiles.js → improve/improve-profiles.js} +13 -7
  50. package/dist/commands/{improve-result-file.js → improve/improve-result-file.js} +10 -5
  51. package/dist/commands/{improve.js → improve/improve.js} +536 -248
  52. package/dist/{core → commands/improve/memory}/memory-belief.js +2 -2
  53. package/dist/{core → commands/improve/memory}/memory-contradiction-detect.js +5 -5
  54. package/dist/{core → commands/improve/memory}/memory-improve.js +4 -4
  55. package/dist/commands/{reflect.js → improve/reflect.js} +33 -28
  56. package/dist/commands/improve/session-asset.js +248 -0
  57. package/dist/commands/lint/agent-linter.js +1 -1
  58. package/dist/commands/lint/base-linter.js +55 -37
  59. package/dist/commands/lint/command-linter.js +1 -1
  60. package/dist/commands/lint/default-linter.js +1 -1
  61. package/dist/commands/lint/env-key-rules.js +1 -1
  62. package/dist/commands/lint/index.js +19 -25
  63. package/dist/commands/lint/knowledge-linter.js +1 -1
  64. package/dist/commands/lint/memory-linter.js +1 -1
  65. package/dist/commands/lint/registry.js +8 -8
  66. package/dist/commands/lint/skill-linter.js +1 -1
  67. package/dist/commands/lint/task-linter.js +1 -1
  68. package/dist/commands/lint/workflow-linter.js +1 -1
  69. package/dist/commands/lint.js +1 -1
  70. package/dist/commands/observability-cli.js +244 -0
  71. package/dist/commands/{proposal-drain-policies.js → proposal/drain-policies.js} +3 -3
  72. package/dist/commands/{proposal-drain.js → proposal/drain.js} +15 -10
  73. package/dist/commands/proposal/proposal-cli.js +478 -0
  74. package/dist/commands/{proposal.js → proposal/proposal.js} +5 -5
  75. package/dist/commands/{propose.js → proposal/propose.js} +11 -11
  76. package/dist/{core → commands/proposal/validators}/proposal-quality-validators.js +8 -3
  77. package/dist/{core → commands/proposal/validators}/proposal-validators.js +5 -5
  78. package/dist/{core → commands/proposal/validators}/proposals.js +13 -7
  79. package/dist/commands/{curate.js → read/curate.js} +7 -7
  80. package/dist/commands/{knowledge.js → read/knowledge.js} +22 -9
  81. package/dist/commands/{registry-search.js → read/registry-search.js} +5 -5
  82. package/dist/commands/{remember-cli.js → read/remember-cli.js} +15 -7
  83. package/dist/commands/read/search-cli.js +207 -0
  84. package/dist/commands/{search.js → read/search.js} +22 -27
  85. package/dist/commands/{show.js → read/show.js} +77 -44
  86. package/dist/commands/registry-cli.js +8 -8
  87. package/dist/commands/remember.js +8 -8
  88. package/dist/commands/sources/add-cli.js +293 -0
  89. package/dist/commands/{history.js → sources/history.js} +27 -25
  90. package/dist/commands/{info.js → sources/info.js} +6 -6
  91. package/dist/commands/{init.js → sources/init.js} +10 -5
  92. package/dist/commands/{installed-stashes.js → sources/installed-stashes.js} +12 -12
  93. package/dist/commands/{migration-help.js → sources/migration-help.js} +3 -2
  94. package/dist/commands/{schema-repair.js → sources/schema-repair.js} +8 -8
  95. package/dist/commands/{self-update.js → sources/self-update.js} +10 -9
  96. package/dist/commands/{source-add.js → sources/source-add.js} +10 -10
  97. package/dist/commands/{source-clone.js → sources/source-clone.js} +7 -7
  98. package/dist/commands/{source-manage.js → sources/source-manage.js} +4 -4
  99. package/dist/commands/sources/sources-cli.js +305 -0
  100. package/dist/commands/sources/stash-cli.js +219 -0
  101. package/dist/commands/sources/stash-skeleton.js +79 -0
  102. package/dist/commands/tasks/default-tasks.js +173 -0
  103. package/dist/commands/tasks/tasks-cli.js +210 -0
  104. package/dist/commands/{tasks.js → tasks/tasks.js} +14 -14
  105. package/dist/commands/wiki-cli.js +307 -0
  106. package/dist/commands/workflow-cli.js +329 -0
  107. package/dist/core/action-contributors.js +1 -1
  108. package/dist/core/assert.js +40 -0
  109. package/dist/core/asset/asset-create.js +54 -0
  110. package/dist/core/{asset-ref.js → asset/asset-ref.js} +21 -4
  111. package/dist/core/{asset-registry.js → asset/asset-registry.js} +3 -3
  112. package/dist/core/{asset-spec.js → asset/asset-spec.js} +17 -31
  113. package/dist/core/{markdown.js → asset/markdown.js} +1 -1
  114. package/dist/core/asset/stash-meta.js +110 -0
  115. package/dist/core/best-effort.js +64 -0
  116. package/dist/core/common.js +32 -18
  117. package/dist/core/{config-io.js → config/config-io.js} +29 -19
  118. package/dist/core/{config-migration.js → config/config-migration.js} +11 -9
  119. package/dist/core/{config-schema.js → config/config-schema.js} +45 -1
  120. package/dist/core/config/config-types.js +16 -0
  121. package/dist/core/{config-walker.js → config/config-walker.js} +2 -2
  122. package/dist/core/{config.js → config/config.js} +10 -8
  123. package/dist/core/env-secret-ref.js +90 -0
  124. package/dist/core/errors.js +13 -3
  125. package/dist/core/events.js +27 -4
  126. package/dist/core/file-lock.js +1 -1
  127. package/dist/core/improve-types.js +48 -0
  128. package/dist/core/lesson-lint.js +2 -2
  129. package/dist/core/paths.js +2 -2
  130. package/dist/{setup/ripgrep-install.js → core/ripgrep/install.js} +2 -2
  131. package/dist/{setup/ripgrep-resolve.js → core/ripgrep/resolve.js} +2 -2
  132. package/dist/core/state-db.js +88 -46
  133. package/dist/core/text-truncation.js +148 -0
  134. package/dist/core/time.js +1 -1
  135. package/dist/core/write-source.js +98 -85
  136. package/dist/indexer/{db-backup.js → db/db-backup.js} +9 -24
  137. package/dist/indexer/{db.js → db/db.js} +126 -116
  138. package/dist/indexer/{graph-db.js → db/graph-db.js} +9 -4
  139. package/dist/indexer/{llm-cache.js → db/llm-cache.js} +15 -12
  140. package/dist/indexer/ensure-index.js +4 -4
  141. package/dist/indexer/{graph-boost.js → graph/graph-boost.js} +1 -1
  142. package/dist/indexer/{graph-extraction.js → graph/graph-extraction.js} +55 -13
  143. package/dist/indexer/indexer.js +37 -30
  144. package/dist/indexer/init.js +54 -0
  145. package/dist/indexer/manifest.js +10 -10
  146. package/dist/indexer/{memory-inference.js → passes/memory-inference.js} +92 -23
  147. package/dist/indexer/{metadata-contributors.js → passes/metadata-contributors.js} +10 -8
  148. package/dist/indexer/{metadata.js → passes/metadata.js} +15 -19
  149. package/dist/indexer/{staleness-detect.js → passes/staleness-detect.js} +53 -12
  150. package/dist/indexer/{db-search.js → search/db-search.js} +28 -16
  151. package/dist/indexer/{ranking-contributors.js → search/ranking-contributors.js} +1 -1
  152. package/dist/indexer/{ranking.js → search/ranking.js} +2 -2
  153. package/dist/indexer/{search-hit-enrichers.js → search/search-hit-enrichers.js} +3 -3
  154. package/dist/indexer/{search-source.js → search/search-source.js} +8 -8
  155. package/dist/indexer/{semantic-status.js → search/semantic-status.js} +3 -3
  156. package/dist/indexer/usage/unmigrated-vaults-guard.js +94 -0
  157. package/dist/indexer/{usage-events.js → usage/usage-events.js} +32 -0
  158. package/dist/indexer/{file-context.js → walk/file-context.js} +10 -15
  159. package/dist/indexer/{matchers.js → walk/matchers.js} +13 -9
  160. package/dist/indexer/{path-resolver.js → walk/path-resolver.js} +6 -6
  161. package/dist/indexer/{project-context.js → walk/project-context.js} +1 -1
  162. package/dist/indexer/{walker.js → walk/walker.js} +4 -3
  163. package/dist/integrations/agent/builder-shared.js +39 -0
  164. package/dist/integrations/agent/builders.js +14 -81
  165. package/dist/integrations/agent/config.js +6 -4
  166. package/dist/integrations/agent/detect.js +1 -1
  167. package/dist/integrations/agent/index.js +23 -8
  168. package/dist/integrations/agent/prompts.js +2 -3
  169. package/dist/integrations/agent/runner.js +22 -3
  170. package/dist/integrations/agent/spawn.js +9 -10
  171. package/dist/integrations/harnesses/claude/agent-builder.js +48 -0
  172. package/dist/integrations/harnesses/claude/config-import.js +70 -0
  173. package/dist/integrations/harnesses/claude/index.js +64 -0
  174. package/dist/integrations/{session-logs/providers/claude-code.js → harnesses/claude/session-log.js} +16 -1
  175. package/dist/integrations/harnesses/index.js +144 -0
  176. package/dist/integrations/harnesses/opencode/agent-builder.js +43 -0
  177. package/dist/integrations/harnesses/opencode/config-import.js +82 -0
  178. package/dist/integrations/harnesses/opencode/index.js +59 -0
  179. package/dist/integrations/{session-logs/providers/opencode.js → harnesses/opencode/session-log.js} +1 -1
  180. package/dist/integrations/harnesses/opencode-sdk/index.js +49 -0
  181. package/dist/integrations/harnesses/opencode-sdk/sdk-runner.js +234 -0
  182. package/dist/integrations/harnesses/types.js +43 -0
  183. package/dist/integrations/lockfile.js +7 -16
  184. package/dist/integrations/session-logs/index.js +82 -9
  185. package/dist/llm/call-ai.js +4 -4
  186. package/dist/llm/client.js +131 -6
  187. package/dist/llm/embedder.js +6 -6
  188. package/dist/llm/embedders/local.js +9 -22
  189. package/dist/llm/embedders/remote.js +2 -2
  190. package/dist/llm/embedders/types.js +1 -1
  191. package/dist/llm/graph-extract.js +31 -12
  192. package/dist/llm/index-passes.js +1 -1
  193. package/dist/llm/memory-infer.js +12 -5
  194. package/dist/llm/metadata-enhance.js +2 -2
  195. package/dist/output/context.js +6 -44
  196. package/dist/output/renderers.js +88 -58
  197. package/dist/output/shapes/curate.js +7 -3
  198. package/dist/output/shapes/distill.js +7 -3
  199. package/dist/output/shapes/env-list.js +18 -16
  200. package/dist/output/shapes/events.js +5 -4
  201. package/dist/output/shapes/helpers.js +2 -4
  202. package/dist/output/shapes/history.js +7 -3
  203. package/dist/output/shapes/passthrough.js +8 -11
  204. package/dist/output/shapes/{proposal-accept.js → proposal/accept.js} +7 -3
  205. package/dist/output/shapes/{proposal-diff.js → proposal/diff.js} +7 -3
  206. package/dist/output/shapes/{proposal-list.js → proposal/list.js} +7 -3
  207. package/dist/output/shapes/{proposal-producer.js → proposal/producer.js} +5 -4
  208. package/dist/output/shapes/{proposal-reject.js → proposal/reject.js} +7 -3
  209. package/dist/output/shapes/{proposal-show.js → proposal/show.js} +7 -3
  210. package/dist/output/shapes/registry-search.js +7 -3
  211. package/dist/output/shapes/registry.js +12 -0
  212. package/dist/output/shapes/search.js +7 -3
  213. package/dist/output/shapes/secret-list.js +18 -16
  214. package/dist/output/shapes/show.js +7 -3
  215. package/dist/output/shapes.js +55 -30
  216. package/dist/output/text/add.js +2 -3
  217. package/dist/output/text/clone.js +2 -3
  218. package/dist/output/text/config.js +2 -3
  219. package/dist/output/text/curate.js +4 -3
  220. package/dist/output/text/distill.js +2 -3
  221. package/dist/output/text/enable-disable.js +5 -4
  222. package/dist/output/text/env.js +13 -0
  223. package/dist/output/text/events.js +5 -4
  224. package/dist/output/text/feedback.js +4 -3
  225. package/dist/output/text/helpers.js +54 -39
  226. package/dist/output/text/history.js +2 -3
  227. package/dist/output/text/import.js +2 -3
  228. package/dist/output/text/index.js +2 -3
  229. package/dist/output/text/info.js +2 -3
  230. package/dist/output/text/init.js +2 -3
  231. package/dist/output/text/list.js +2 -3
  232. package/dist/output/text/proposal/producer.js +9 -0
  233. package/dist/output/text/proposal/proposal.js +13 -0
  234. package/dist/output/text/registry-commands.js +8 -7
  235. package/dist/output/text/registry.js +12 -0
  236. package/dist/output/text/remember.js +4 -3
  237. package/dist/output/text/remove.js +2 -3
  238. package/dist/output/text/save.js +2 -3
  239. package/dist/output/text/search.js +4 -3
  240. package/dist/output/text/show.js +4 -3
  241. package/dist/output/text/update.js +2 -3
  242. package/dist/output/text/upgrade.js +2 -3
  243. package/dist/output/text/wiki.js +12 -11
  244. package/dist/output/text/workflow.js +12 -10
  245. package/dist/output/text.js +66 -32
  246. package/dist/registry/build-index.js +11 -10
  247. package/dist/registry/factory.js +1 -1
  248. package/dist/registry/origin-resolve.js +1 -1
  249. package/dist/registry/providers/index.js +2 -2
  250. package/dist/registry/providers/skills-sh.js +91 -72
  251. package/dist/registry/providers/static-index.js +75 -52
  252. package/dist/registry/resolve.js +3 -3
  253. package/dist/runtime.js +242 -0
  254. package/dist/scripts/migrate-storage.js +1594 -673
  255. package/dist/scripts/migrations/import-fs-improve-runs-to-db.js +240 -166
  256. package/dist/setup/detect.js +338 -9
  257. package/dist/setup/harness-config-import.js +56 -0
  258. package/dist/setup/registry-stash-loader.js +99 -0
  259. package/dist/setup/setup.js +664 -96
  260. package/dist/sources/include.js +1 -1
  261. package/dist/sources/provider-factory.js +2 -2
  262. package/dist/sources/providers/filesystem.js +3 -3
  263. package/dist/sources/providers/git.js +9 -9
  264. package/dist/sources/providers/index.js +4 -4
  265. package/dist/sources/providers/npm.js +6 -6
  266. package/dist/sources/providers/provider-utils.js +13 -20
  267. package/dist/sources/providers/sync-from-ref.js +5 -5
  268. package/dist/sources/providers/tar-utils.js +2 -2
  269. package/dist/sources/providers/website.js +2 -2
  270. package/dist/sources/resolve.js +5 -5
  271. package/dist/sources/website-ingest.js +5 -5
  272. package/dist/storage/database.js +102 -0
  273. package/dist/storage/engines/sqlite-migrations.js +42 -0
  274. package/dist/storage/locations.js +25 -0
  275. package/dist/storage/repositories/index-db.js +43 -0
  276. package/dist/storage/repositories/workflow-runs-repository.js +141 -0
  277. package/dist/tasks/backends/cron.js +4 -4
  278. package/dist/tasks/backends/exec-utils.js +32 -0
  279. package/dist/tasks/backends/index.js +3 -3
  280. package/dist/tasks/backends/launchd.js +7 -14
  281. package/dist/tasks/backends/schtasks.js +7 -16
  282. package/dist/tasks/embedded.js +71 -0
  283. package/dist/tasks/parser.js +2 -2
  284. package/dist/tasks/resolveAkmBin.js +1 -1
  285. package/dist/tasks/runner.js +28 -15
  286. package/dist/tasks/schedule.js +1 -1
  287. package/dist/tasks/validator.js +7 -7
  288. package/dist/text-import-hook.mjs +51 -0
  289. package/dist/version.js +2 -1
  290. package/dist/wiki/wiki.js +7 -7
  291. package/dist/workflows/{authoring.js → authoring/authoring.js} +6 -6
  292. package/dist/workflows/{scope-key.js → authoring/scope-key.js} +1 -1
  293. package/dist/workflows/cli.js +1 -1
  294. package/dist/workflows/db.js +50 -32
  295. package/dist/workflows/parser.js +4 -4
  296. package/dist/workflows/renderer.js +5 -5
  297. package/dist/workflows/runtime/agent-identity.js +56 -0
  298. package/dist/workflows/runtime/checkin.js +57 -0
  299. package/dist/workflows/{runs.js → runtime/runs.js} +197 -101
  300. package/dist/workflows/validate-summary.js +82 -0
  301. package/docs/README.md +1 -1
  302. package/docs/data-and-telemetry.md +6 -6
  303. package/package.json +16 -8
  304. package/dist/commands/add-cli.js +0 -279
  305. package/dist/commands/env.js +0 -213
  306. package/dist/integrations/agent/sdk-runner.js +0 -126
  307. package/dist/output/shapes/vault-list.js +0 -19
  308. package/dist/output/text/proposal-producer.js +0 -8
  309. package/dist/output/text/proposal.js +0 -12
  310. package/dist/output/text/vault.js +0 -16
  311. /package/dist/core/{asset-serialize.js → asset/asset-serialize.js} +0 -0
  312. /package/dist/core/{frontmatter.js → asset/frontmatter.js} +0 -0
  313. /package/dist/core/{config-sources.js → config/config-sources.js} +0 -0
  314. /package/dist/indexer/{graph-dedup.js → graph/graph-dedup.js} +0 -0
  315. /package/dist/{core/config-types.js → indexer/passes/pass-context.js} +0 -0
  316. /package/dist/indexer/{search-fields.js → search/search-fields.js} +0 -0
  317. /package/dist/indexer/{index-context.js → walk/index-context.js} +0 -0
  318. /package/dist/workflows/{document-cache.js → runtime/document-cache.js} +0 -0
@@ -18,31 +18,33 @@
18
18
  */
19
19
  import fs from "node:fs";
20
20
  import path from "node:path";
21
- import { parseAssetRef } from "../core/asset-ref";
22
- import { asNonEmptyString } from "../core/common";
23
- import { loadConfig } from "../core/config";
24
- import { NotFoundError, rethrowIfTestIsolationError, UsageError } from "../core/errors";
25
- import { appendEvent, readEvents } from "../core/events";
26
- import { parseFrontmatter } from "../core/frontmatter";
27
- import { closeDatabase, findEntryIdByRef, openExistingDatabase } from "../indexer/db";
28
- import { ensureIndex } from "../indexer/ensure-index";
29
- import { buildFileContext, buildRenderContext, getRenderer, runMatchers } from "../indexer/file-context";
30
- import { listRelatedPathsForFile } from "../indexer/graph-boost";
31
- import { lookup } from "../indexer/indexer";
32
- import { resolveAssetPath } from "../indexer/path-resolver";
33
- import { buildEditHint, findSourceForPath, isEditable, resolveSourceEntries } from "../indexer/search-source";
34
- import { insertUsageEvent } from "../indexer/usage-events";
35
- import { resolveSourcesForOrigin } from "../registry/origin-resolve";
21
+ import { parseAssetRef } from "../../core/asset/asset-ref.js";
22
+ import { parseFrontmatter } from "../../core/asset/frontmatter.js";
23
+ import { META_DIR, parseMetaRef, resolveMetaFilePath } from "../../core/asset/stash-meta.js";
24
+ import { asNonEmptyString } from "../../core/common.js";
25
+ import { loadConfig } from "../../core/config/config.js";
26
+ import { NotFoundError, rethrowIfTestIsolationError, UsageError } from "../../core/errors.js";
27
+ import { appendEvent, readEvents } from "../../core/events.js";
28
+ import { findEntryIdByRef } from "../../indexer/db/db.js";
29
+ import { ensureIndex } from "../../indexer/ensure-index.js";
30
+ import { listRelatedPathsForFile } from "../../indexer/graph/graph-boost.js";
31
+ import { lookup } from "../../indexer/indexer.js";
32
+ import { buildEditHint, findSourceForPath, isEditable, resolveSourceEntries } from "../../indexer/search/search-source.js";
33
+ import { insertUsageEvent } from "../../indexer/usage/usage-events.js";
34
+ import { buildFileContext, buildRenderContext, getRenderer, runMatchers } from "../../indexer/walk/file-context.js";
35
+ import { resolveAssetPath } from "../../indexer/walk/path-resolver.js";
36
+ import { resolveSourcesForOrigin } from "../../registry/origin-resolve.js";
37
+ import { withIndexDb } from "../../storage/repositories/index-db.js";
36
38
  // Eagerly import source providers to trigger self-registration.
37
- import "../sources/providers/index";
38
- import { getActiveWorkflowRun } from "../workflows/runs";
39
- import { getCurrentWorkflowScopeKey } from "../workflows/scope-key";
39
+ import "../../sources/providers/index.js";
40
+ import { getCurrentWorkflowScopeKey } from "../../workflows/authoring/scope-key.js";
41
+ import { getActiveWorkflowRun } from "../../workflows/runtime/runs.js";
40
42
  /**
41
43
  * Show a wiki root (no page path) — returns the same payload as
42
44
  * `akm wiki show <name>`.
43
45
  */
44
46
  async function showWikiRoot(stashDir, wikiName) {
45
- const { showWiki } = await import("../wiki/wiki.js");
47
+ const { showWiki } = await import("../../wiki/wiki.js");
46
48
  const result = showWiki(stashDir, wikiName);
47
49
  return {
48
50
  type: "wiki",
@@ -58,7 +60,7 @@ async function showWikiRoot(stashDir, wikiName) {
58
60
  };
59
61
  }
60
62
  async function showWikiRootForSource(stashDir, source, wikiName) {
61
- const { showWikiAtPath } = await import("../wiki/wiki.js");
63
+ const { showWikiAtPath } = await import("../../wiki/wiki.js");
62
64
  if (source.wikiName === wikiName) {
63
65
  const result = showWikiAtPath(wikiName, source.path);
64
66
  return {
@@ -105,6 +107,16 @@ function resolveRegisteredWikiAssetPath(wikiRoot, wikiName, assetName) {
105
107
  */
106
108
  export async function akmShowUnified(input) {
107
109
  const ref = input.ref.trim();
110
+ // 0a. Stash `.meta/` convention: `[origin//]meta[:name]` direct-reads a
111
+ // human-authored orientation doc from the stash's `.meta/` directory.
112
+ // These files are not indexed (the walker skips dot-dirs), so they are
113
+ // resolved here before the index lookup and the `type:name` parser,
114
+ // which would otherwise reject the non-asset-type `meta`.
115
+ {
116
+ const metaRef = parseMetaRef(ref);
117
+ if (metaRef)
118
+ return showStashMeta(metaRef);
119
+ }
108
120
  // 0. Wiki-root shortcut: `wiki:<name>` with no page path routes to the
109
121
  // wiki summary (same payload as `akm wiki show <name>`). Honour
110
122
  // `parsed.origin` by resolving against the matching stash source(s),
@@ -145,13 +157,49 @@ export async function akmShowUnified(input) {
145
157
  }
146
158
  // Count prior shows of this ref before logging the current one.
147
159
  const priorShowCount = recentShowCount(ref);
148
- logShowEvent(ref, undefined, input.eventSource);
160
+ logShowEvent(ref, input.eventSource);
149
161
  if (priorShowCount >= 2) {
150
162
  // Agent has shown this same asset 3+ times — inject a loop-break hint.
151
163
  result.showLoopWarning = priorShowCount + 1;
152
164
  }
153
165
  return result;
154
166
  }
167
+ /**
168
+ * Resolve a stash `.meta/` doc and return it as a lightweight ShowResponse.
169
+ *
170
+ * With no origin the working stash (and other configured sources, in order)
171
+ * is searched and the first hit wins. With an origin the lookup is narrowed
172
+ * to that stash; an uninstalled origin yields an actionable "not installed"
173
+ * error. The file is read directly from disk — `.meta/` is never indexed.
174
+ */
175
+ async function showStashMeta(metaRef) {
176
+ const allSources = resolveSourceEntries();
177
+ const sources = resolveSourcesForOrigin(metaRef.origin, allSources);
178
+ if (metaRef.origin && sources.length === 0) {
179
+ throw new NotFoundError(`Stash "${metaRef.origin}" is not installed, so its ${META_DIR}/ docs are unavailable. ` +
180
+ `Run: akm add ${metaRef.origin}`);
181
+ }
182
+ const config = loadConfig();
183
+ for (const source of sources) {
184
+ const filePath = resolveMetaFilePath(source.path, metaRef.name);
185
+ if (!filePath)
186
+ continue;
187
+ const content = fs.readFileSync(filePath, "utf8");
188
+ const editable = isEditable(filePath, config);
189
+ appendEvent({ eventType: "show", ref: `meta:${metaRef.name}`, metadata: { type: "meta", name: metaRef.name } });
190
+ return {
191
+ type: "meta",
192
+ name: metaRef.name,
193
+ path: filePath,
194
+ content,
195
+ origin: source.registryId ?? null,
196
+ editable,
197
+ };
198
+ }
199
+ throw new NotFoundError(`No ${META_DIR}/${metaRef.name} doc found${metaRef.origin ? ` in "${metaRef.origin}"` : ""}. ` +
200
+ `Stash maintainers can create ${META_DIR}/${metaRef.name}.md to describe this stash ` +
201
+ `(purpose, key assets, conventions, maintainer).`);
202
+ }
155
203
  function hasAnyScopeKey(scope) {
156
204
  return Boolean(scope.user || scope.agent || scope.run || scope.channel);
157
205
  }
@@ -206,7 +254,7 @@ function recentShowCount(ref) {
206
254
  return 0;
207
255
  }
208
256
  }
209
- function logShowEvent(ref, existingDb, eventSource = "user") {
257
+ function logShowEvent(ref, eventSource = "user") {
210
258
  // Emit a structured event to events.jsonl so workflow-trace consumers
211
259
  // detect akm show invocations without relying on stdout scraping.
212
260
  const parsed = parseAssetRef(ref);
@@ -241,19 +289,14 @@ function logShowEvent(ref, existingDb, eventSource = "user") {
241
289
  /* fire-and-forget — select is best-effort */
242
290
  }
243
291
  try {
244
- const db = existingDb ?? openExistingDatabase();
245
- try {
292
+ withIndexDb((db) => {
246
293
  insertUsageEvent(db, {
247
294
  event_type: "show",
248
295
  entry_ref: ref,
249
296
  entry_id: findEntryIdByRef(db, ref),
250
297
  source: eventSource,
251
298
  });
252
- }
253
- finally {
254
- if (!existingDb)
255
- closeDatabase(db);
256
- }
299
+ });
257
300
  }
258
301
  catch (err) {
259
302
  rethrowIfTestIsolationError(err);
@@ -324,20 +367,16 @@ export async function showLocal(input) {
324
367
  editable,
325
368
  ...(!editable ? { editHint: buildEditHint(assetPath, parsed.type, parsed.name, source?.registryId) } : {}),
326
369
  related: (() => {
327
- let db;
328
370
  try {
329
- db = openExistingDatabase();
330
- const related = listRelatedPathsForFile(sourceStashDir, assetPath, 5, db);
331
- return { total: related.length, hits: related };
371
+ return withIndexDb((db) => {
372
+ const related = listRelatedPathsForFile(sourceStashDir, assetPath, 5, db);
373
+ return { total: related.length, hits: related };
374
+ });
332
375
  }
333
376
  catch (err) {
334
377
  rethrowIfTestIsolationError(err);
335
378
  return { total: 0, hits: [] };
336
379
  }
337
- finally {
338
- if (db)
339
- closeDatabase(db);
340
- }
341
380
  })(),
342
381
  };
343
382
  const activeRun = await getActiveWorkflowRun(getCurrentWorkflowScopeKey());
@@ -435,8 +474,6 @@ export function normalizeShowArgv(argv) {
435
474
  // argv[0]=bun argv[1]=script argv[2]=subcommand argv[3]=ref argv[4..]=rest
436
475
  if (argv[2] !== "show")
437
476
  return argv;
438
- if (argv[3] === "proposal")
439
- return argv;
440
477
  if (argv.includes("--view") || argv.includes("--heading") || argv.includes("--start") || argv.includes("--end")) {
441
478
  throw new UsageError('Legacy show flags are no longer supported. Use positional syntax like `akm show knowledge:guide toc` or `akm show knowledge:guide section "Auth"`.');
442
479
  }
@@ -447,11 +484,7 @@ export function normalizeShowArgv(argv) {
447
484
  const showArgs = [];
448
485
  for (let i = 0; i < rest.length; i++) {
449
486
  const arg = rest[i];
450
- if (arg === "--quiet" ||
451
- arg === "-q" ||
452
- arg === "--verbose" ||
453
- arg === "--for-agent" ||
454
- arg === "--for-agent=true") {
487
+ if (arg === "--quiet" || arg === "-q" || arg === "--verbose") {
455
488
  globalFlags.push(arg);
456
489
  continue;
457
490
  }
@@ -2,14 +2,14 @@
2
2
  // License, v. 2.0. If a copy of the MPL was not distributed with this
3
3
  // file, You can obtain one at https://mozilla.org/MPL/2.0/.
4
4
  import { defineCommand } from "citty";
5
- import { parsePositiveIntFlag } from "../cli/parse-args";
6
- import { output, runWithJsonErrors } from "../cli/shared";
7
- import { DEFAULT_CONFIG, loadUserConfig, saveConfig } from "../core/config";
8
- import { UsageError } from "../core/errors";
9
- import { warn } from "../core/warn";
10
- import { getHyphenatedArg, getHyphenatedBoolean } from "../output/context";
11
- import { buildRegistryIndex, writeRegistryIndex } from "../registry/build-index";
12
- import { searchRegistry } from "./registry-search";
5
+ import { parsePositiveIntFlag } from "../cli/parse-args.js";
6
+ import { output, runWithJsonErrors } from "../cli/shared.js";
7
+ import { DEFAULT_CONFIG, loadUserConfig, saveConfig } from "../core/config/config.js";
8
+ import { UsageError } from "../core/errors.js";
9
+ import { warn } from "../core/warn.js";
10
+ import { getHyphenatedArg, getHyphenatedBoolean } from "../output/context.js";
11
+ import { buildRegistryIndex, writeRegistryIndex } from "../registry/build-index.js";
12
+ import { searchRegistry } from "./read/registry-search.js";
13
13
  export const registryCommand = defineCommand({
14
14
  meta: { name: "registry", description: "Manage stash registries" },
15
15
  subCommands: {
@@ -8,13 +8,13 @@
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 { serializeFrontmatter } from "../core/asset-serialize";
12
- import { toErrorMessage, tryReadStdinText } from "../core/common";
13
- import { getDefaultLlmConfig, loadConfig } from "../core/config";
14
- import { UsageError } from "../core/errors";
15
- import { warn } from "../core/warn";
16
- import { SCOPE_KEYS } from "../indexer/metadata";
17
- import { parseFlagValue } from "../output/context";
11
+ import { serializeFrontmatter } from "../core/asset/asset-serialize.js";
12
+ import { toErrorMessage, tryReadStdinText } from "../core/common.js";
13
+ import { getDefaultLlmConfig, loadConfig } from "../core/config/config.js";
14
+ import { UsageError } from "../core/errors.js";
15
+ import { warn } from "../core/warn.js";
16
+ import { SCOPE_KEYS } from "../indexer/passes/metadata.js";
17
+ import { parseFlagValue } from "../output/context.js";
18
18
  /**
19
19
  * Parse a shorthand duration string to a number of milliseconds.
20
20
  * Supports: `30d` (days), `12h` (hours), `6m` (months, approximated as 30d).
@@ -148,7 +148,7 @@ export async function runLlmEnrich(body) {
148
148
  warn("Warning: --enrich requires an LLM to be configured. Run `akm setup` to configure one.");
149
149
  return { tags: [] };
150
150
  }
151
- const { chatCompletion, parseEmbeddedJsonResponse: parseJsonResponse } = await import("../llm/client");
151
+ const { chatCompletion, parseEmbeddedJsonResponse: parseJsonResponse } = await import("../llm/client.js");
152
152
  const prompt = `You are a memory tagger for a developer knowledge base.
153
153
  Given the memory text below, return ONLY a JSON object with these fields:
154
154
  - "tags": array of 1-5 short lowercase keyword tags
@@ -0,0 +1,293 @@
1
+ // This Source Code Form is subject to the terms of the Mozilla Public
2
+ // License, v. 2.0. If a copy of the MPL was not distributed with this
3
+ // file, You can obtain one at https://mozilla.org/MPL/2.0/.
4
+ import fs from "node:fs";
5
+ import path from "node:path";
6
+ import * as p from "@clack/prompts";
7
+ import { defineCommand } from "citty";
8
+ import { output, runWithJsonErrors } from "../../cli/shared.js";
9
+ import { UsageError } from "../../core/errors.js";
10
+ import { appendEvent } from "../../core/events.js";
11
+ import { warn } from "../../core/warn.js";
12
+ import { getHyphenatedBoolean } from "../../output/context.js";
13
+ import { akmRemove } from "./installed-stashes.js";
14
+ import { akmAdd } from "./source-add.js";
15
+ import { addStash } from "./source-manage.js";
16
+ // ── Shared website-options helper (also used by wikiRegisterCommand) ──────────
17
+ export function buildWebsiteOptions(args) {
18
+ const websiteOptions = {};
19
+ if (typeof args["max-pages"] === "string" && args["max-pages"].length > 0)
20
+ websiteOptions.maxPages = args["max-pages"];
21
+ if (typeof args["max-depth"] === "string" && args["max-depth"].length > 0)
22
+ websiteOptions.maxDepth = args["max-depth"];
23
+ return websiteOptions;
24
+ }
25
+ // ── HTTP safety check ─────────────────────────────────────────────────────────
26
+ export function shouldWarnOnPlainHttp(ref) {
27
+ if (!ref.startsWith("http://"))
28
+ return false;
29
+ try {
30
+ const hostname = new URL(ref).hostname.toLowerCase();
31
+ return (hostname !== "localhost" &&
32
+ hostname !== "127.0.0.1" &&
33
+ hostname !== "0.0.0.0" &&
34
+ hostname !== "::1" &&
35
+ hostname !== "[::1]" &&
36
+ !hostname.endsWith(".localhost"));
37
+ }
38
+ catch {
39
+ return true;
40
+ }
41
+ }
42
+ /** Scan every env file in the freshly-installed stash for dangerous env keys. */
43
+ function collectDangerousKeyFindings(installedStashRoot, checkEnvForDangerousKeys) {
44
+ const allFindings = [];
45
+ const subdir = "env";
46
+ const prefix = "env";
47
+ const dir = path.join(installedStashRoot, subdir);
48
+ const envFiles = fs.existsSync(dir) ? fs.readdirSync(dir).filter((f) => f.endsWith(".env")) : [];
49
+ for (const envFile of envFiles) {
50
+ const envPath = path.join(dir, envFile);
51
+ const baseName = path.basename(envFile, ".env");
52
+ const vaultRef = baseName === "" ? `${prefix}:default` : `${prefix}:${baseName}`;
53
+ const relPath = path.join(subdir, envFile);
54
+ const findings = checkEnvForDangerousKeys(envPath, relPath, vaultRef);
55
+ for (const finding of findings) {
56
+ // Extract the key name from the detail string for the summary line.
57
+ const keyMatch = finding.detail.match(/Env key `([^`]+)`/);
58
+ const keyName = keyMatch ? keyMatch[1] : finding.file;
59
+ allFindings.push({ vaultRef, keyName, relPath });
60
+ }
61
+ }
62
+ return allFindings;
63
+ }
64
+ /**
65
+ * Audit a freshly-installed stash for dangerous env keys and decide whether the
66
+ * install must be blocked. Returns a typed decision instead of calling
67
+ * `process.exit`, so the abort cannot be lost to a swallowed exception. See the
68
+ * block comment above for the security rationale.
69
+ */
70
+ export async function auditInstalledStashForDangerousKeys(opts) {
71
+ const { installedStashRoot, ref, allowDangerousKeys, rollbackTarget, isTTY } = opts;
72
+ // Best-effort scan: if collecting findings itself throws (corrupt env file,
73
+ // fs error) there is nothing concrete to block on, so fail soft. Crucially,
74
+ // this soft path runs BEFORE any findings exist — it can never re-open an
75
+ // already-detected dangerous install.
76
+ let allFindings;
77
+ try {
78
+ const { checkEnvForDangerousKeys } = await import("../lint/env-key-rules.js");
79
+ allFindings = collectDangerousKeyFindings(installedStashRoot, checkEnvForDangerousKeys);
80
+ }
81
+ catch {
82
+ return { blocked: false };
83
+ }
84
+ if (allFindings.length === 0)
85
+ return { blocked: false };
86
+ if (allowDangerousKeys) {
87
+ // Operator has explicitly accepted the risk — warn and continue.
88
+ for (const f of allFindings) {
89
+ 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.`);
90
+ }
91
+ return { blocked: false };
92
+ }
93
+ // Helper: roll the install back before aborting. Rollback is best-effort; a
94
+ // failed rollback never UN-blocks the install — we still abort, just with a
95
+ // warning telling the operator to remove the stash manually.
96
+ async function rollback() {
97
+ try {
98
+ await akmRemove({ target: rollbackTarget });
99
+ return undefined;
100
+ }
101
+ catch (_rollbackErr) {
102
+ return (`Rollback failed — stash may still be installed at ${installedStashRoot}. ` +
103
+ `Remove it manually with: akm remove ${rollbackTarget}`);
104
+ }
105
+ }
106
+ if (isTTY) {
107
+ // Interactive path: show findings and ask the user to confirm.
108
+ // Guard on stdin (not stdout) because p.confirm() reads from stdin;
109
+ // stdout may be a TTY while stdin is piped, which would cause a hang.
110
+ const stashLabel = ref;
111
+ const groupedByVault = new Map();
112
+ for (const f of allFindings) {
113
+ const existing = groupedByVault.get(f.vaultRef) ?? [];
114
+ existing.push(f.keyName);
115
+ groupedByVault.set(f.vaultRef, existing);
116
+ }
117
+ for (const [vaultRef, keys] of groupedByVault) {
118
+ warn(`[warn] Env "${vaultRef}" in stash "${stashLabel}" contains potentially dangerous keys:`);
119
+ for (const key of keys) {
120
+ warn(` - ${key}: can hijack process execution via \`akm env run\``);
121
+ }
122
+ }
123
+ const confirmed = await p.confirm({
124
+ message: "Install anyway?",
125
+ initialValue: false,
126
+ });
127
+ if (p.isCancel(confirmed) || confirmed !== true) {
128
+ const rollbackWarning = await rollback();
129
+ console.error(JSON.stringify({
130
+ ok: false,
131
+ error: "Install aborted: stash contains dangerous env keys. Remove the keys or re-run with --allow-insecure to bypass.",
132
+ code: "DANGEROUS_VAULT_KEY",
133
+ ...(rollbackWarning ? { rollbackWarning } : {}),
134
+ }, null, 2));
135
+ return { blocked: true, exitCode: 1 };
136
+ }
137
+ // Operator confirmed at the prompt — allow the install to proceed.
138
+ return { blocked: false };
139
+ }
140
+ // Non-interactive path without bypass flag: fail hard.
141
+ const rollbackWarning = await rollback();
142
+ const keyList = allFindings.map((f) => ` - ${f.keyName} (${f.vaultRef})`).join("\n");
143
+ console.error(JSON.stringify({
144
+ ok: false,
145
+ 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.`,
146
+ code: "DANGEROUS_VAULT_KEY",
147
+ ...(rollbackWarning ? { rollbackWarning } : {}),
148
+ }, null, 2));
149
+ return { blocked: true, exitCode: 1 };
150
+ }
151
+ // ── Command definition ────────────────────────────────────────────────────────
152
+ export const addCommand = defineCommand({
153
+ meta: {
154
+ name: "add",
155
+ description: "Add a source (local directory, website, npm package, GitHub repo, git URL, or remote provider)",
156
+ },
157
+ args: {
158
+ ref: {
159
+ type: "positional",
160
+ description: "Path, URL, or registry ref (website URL, npm package, owner/repo, git URL, or local directory)",
161
+ required: true,
162
+ },
163
+ provider: { type: "string", description: "Provider type (e.g. website, npm). Required for URL sources." },
164
+ options: { type: "string", description: 'Provider options as JSON (e.g. \'{"apiKey":"key"}\').' },
165
+ name: { type: "string", description: "Human-friendly name for the source" },
166
+ writable: {
167
+ type: "boolean",
168
+ description: "Mark a git stash as writable so changes can be pushed back",
169
+ default: false,
170
+ },
171
+ type: {
172
+ type: "string",
173
+ description: "Override asset type for all files in this stash (currently supports: wiki)",
174
+ },
175
+ "max-pages": { type: "string", description: "Maximum pages to crawl for website sources (default: 50)" },
176
+ "max-depth": { type: "string", description: "Maximum crawl depth for website sources (default: 3)" },
177
+ "allow-insecure": {
178
+ type: "boolean",
179
+ 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.",
180
+ default: false,
181
+ },
182
+ },
183
+ async run({ args }) {
184
+ await runWithJsonErrors(async () => {
185
+ const ref = args.ref.trim();
186
+ const allowInsecure = getHyphenatedBoolean(args, "allow-insecure");
187
+ const allowDangerousKeys = allowInsecure;
188
+ // URL with --provider → stash source (remote or git provider)
189
+ if (args.provider) {
190
+ if (shouldWarnOnPlainHttp(ref)) {
191
+ if (!allowInsecure) {
192
+ throw new UsageError("Source URL uses plain HTTP (not HTTPS). An on-path attacker could substitute a malicious payload. " +
193
+ "Use https:// or pass --allow-insecure if you have explicitly accepted the risk.", "INVALID_FLAG_VALUE", "Re-run with `--allow-insecure` only after confirming the URL is trusted.");
194
+ }
195
+ warn("Warning: source URL uses plain HTTP (not HTTPS). --allow-insecure was set; an on-path attacker could substitute a malicious payload.");
196
+ }
197
+ let parsedOptions;
198
+ if (args.options) {
199
+ try {
200
+ const parsed = JSON.parse(args.options);
201
+ if (typeof parsed !== "object" || parsed === null || Array.isArray(parsed)) {
202
+ throw new UsageError("--options must be a JSON object");
203
+ }
204
+ parsedOptions = parsed;
205
+ }
206
+ catch (err) {
207
+ if (err instanceof UsageError)
208
+ throw err;
209
+ throw new UsageError("--options must be valid JSON");
210
+ }
211
+ }
212
+ const result = addStash({
213
+ target: ref,
214
+ name: args.name,
215
+ providerType: args.provider,
216
+ options: parsedOptions,
217
+ writable: args.writable,
218
+ });
219
+ appendEvent({
220
+ eventType: "add",
221
+ metadata: { target: ref, provider: args.provider, name: args.name ?? null, writable: args.writable === true },
222
+ });
223
+ output("add", result);
224
+ return;
225
+ }
226
+ if (shouldWarnOnPlainHttp(ref)) {
227
+ if (!allowInsecure) {
228
+ throw new UsageError("Source URL uses plain HTTP (not HTTPS). An on-path attacker could substitute a malicious payload. " +
229
+ "Use https:// or pass --allow-insecure if you have explicitly accepted the risk.", "INVALID_FLAG_VALUE", "Re-run with `--allow-insecure` only after confirming the URL is trusted.");
230
+ }
231
+ warn("Warning: source URL uses plain HTTP (not HTTPS). --allow-insecure was set; an on-path attacker could substitute a malicious payload.");
232
+ }
233
+ const websiteOptions = buildWebsiteOptions(args);
234
+ if (args.type === "wiki") {
235
+ const { registerWikiSource } = await import("./source-add.js");
236
+ const result = await registerWikiSource({
237
+ ref,
238
+ name: args.name,
239
+ options: Object.keys(websiteOptions).length > 0 ? websiteOptions : undefined,
240
+ writable: args.writable,
241
+ });
242
+ appendEvent({
243
+ eventType: "add",
244
+ metadata: { target: ref, type: "wiki", name: args.name ?? null, writable: args.writable === true },
245
+ });
246
+ output("add", result);
247
+ return;
248
+ }
249
+ const result = await akmAdd({
250
+ ref,
251
+ name: args.name,
252
+ overrideType: args.type,
253
+ options: Object.keys(websiteOptions).length > 0 ? websiteOptions : undefined,
254
+ writable: args.writable,
255
+ });
256
+ appendEvent({
257
+ eventType: "add",
258
+ metadata: {
259
+ target: ref,
260
+ name: args.name ?? null,
261
+ overrideType: args.type ?? null,
262
+ writable: args.writable === true,
263
+ },
264
+ });
265
+ // ── Post-install env key audit ──────────────────────────────────────────
266
+ // Resolve the stash root from the install result and scan any env files
267
+ // for dangerous env var keys. When findings are present the install is
268
+ // gated: TTY → interactive confirmation prompt; non-TTY without
269
+ // --allow-insecure → hard failure (exit 1). Pass
270
+ // --allow-insecure to skip the prompt non-interactively.
271
+ const installedStashRoot = result.installed?.stashRoot ??
272
+ (result.sourceAdded && "stashRoot" in result.sourceAdded ? result.sourceAdded.stashRoot : undefined);
273
+ if (installedStashRoot) {
274
+ // Use the canonical installed id (most reliably resolved by akmRemove) rather
275
+ // than the raw user-supplied ref which may not match after URL normalisation.
276
+ const rollbackTarget = result.installed?.id ?? result.sourceAdded?.stashRoot ?? ref;
277
+ // The audit RETURNS its decision; we decide `process.exit` here, OUTSIDE
278
+ // any catch, so the abort cannot be lost to a swallowed exception (C3).
279
+ const decision = await auditInstalledStashForDangerousKeys({
280
+ installedStashRoot,
281
+ ref,
282
+ allowDangerousKeys,
283
+ rollbackTarget,
284
+ isTTY: process.stdin.isTTY === true,
285
+ });
286
+ if (decision.blocked) {
287
+ process.exit(decision.exitCode);
288
+ }
289
+ }
290
+ output("add", result);
291
+ });
292
+ },
293
+ });
@@ -1,12 +1,28 @@
1
1
  // This Source Code Form is subject to the terms of the Mozilla Public
2
2
  // License, v. 2.0. If a copy of the MPL was not distributed with this
3
3
  // file, You can obtain one at https://mozilla.org/MPL/2.0/.
4
- import { parseAssetRef } from "../core/asset-ref";
5
- import { UsageError } from "../core/errors";
6
- import { readEvents } from "../core/events";
7
- import { listProposals } from "../core/proposals";
8
- import { isoToSqlite, parseSinceToIso } from "../core/time";
9
- import { closeDatabase, openExistingDatabase } from "../indexer/db";
4
+ /**
5
+ * `akm history` surfaces internal mutation/usage events for a single asset
6
+ * (`--ref`) or stash-wide.
7
+ *
8
+ * Event sources:
9
+ * - `usage_events` SQLite table: search, show, and feedback events recorded
10
+ * by the local indexer during normal CLI use.
11
+ * - `events.jsonl` append-only stream (opt-in via `--include-proposals`):
12
+ * proposal lifecycle events (`promoted`, `rejected`) emitted by
13
+ * `akm proposal accept` / `akm proposal reject`. Use this flag to see
14
+ * the full proposal review trail alongside usage events.
15
+ *
16
+ * The two sources are merged and sorted chronologically (oldest first) so
17
+ * consumers see a coherent lifecycle trail in a single output.
18
+ */
19
+ import { parseAssetRef } from "../../core/asset/asset-ref.js";
20
+ import { UsageError } from "../../core/errors.js";
21
+ import { readEvents } from "../../core/events.js";
22
+ import { isoToSqlite, parseSinceToIso } from "../../core/time.js";
23
+ import { closeDatabase, openExistingDatabase } from "../../indexer/db/db.js";
24
+ import { getUsageEvents } from "../../indexer/usage/usage-events.js";
25
+ import { listProposals } from "../proposal/validators/proposals.js";
10
26
  // Proposal lifecycle event types emitted by the proposal substrate (#225).
11
27
  const PROPOSAL_EVENT_TYPES = new Set(["promoted", "rejected"]);
12
28
  // ── Helpers ──────────────────────────────────────────────────────────────────
@@ -71,25 +87,11 @@ export async function akmHistory(options = {}) {
71
87
  const db = options.db ?? openExistingDatabase();
72
88
  const ownsDb = options.db === undefined;
73
89
  try {
74
- const conditions = [];
75
- const params = [];
76
- if (normalizedRef !== undefined) {
77
- conditions.push("entry_ref = ?");
78
- params.push(normalizedRef);
79
- }
80
- if (sinceNormalized !== undefined) {
81
- conditions.push("created_at >= ?");
82
- params.push(sinceNormalized);
83
- }
84
- if (options.source !== undefined) {
85
- conditions.push("source = ?");
86
- params.push(options.source);
87
- }
88
- const where = conditions.length > 0 ? `WHERE ${conditions.join(" AND ")}` : "";
89
- const sql = `SELECT id, event_type, query, entry_id, entry_ref, signal, metadata, source, created_at
90
- FROM usage_events ${where}
91
- ORDER BY id ASC`;
92
- const rows = db.prepare(sql).all(...params);
90
+ const rows = getUsageEvents(db, {
91
+ entry_ref: normalizedRef,
92
+ since: sinceNormalized,
93
+ source: options.source,
94
+ });
93
95
  const usageEntries = rows.map(toEntry);
94
96
  // ── Proposal lifecycle events (opt-in) ────────────────────────────────
95
97
  const sources = ["usage_events"];
@@ -2,12 +2,12 @@
2
2
  // License, v. 2.0. If a copy of the MPL was not distributed with this
3
3
  // file, You can obtain one at https://mozilla.org/MPL/2.0/.
4
4
  import fs from "node:fs";
5
- import { getAssetTypes } from "../core/asset-spec";
6
- import { getSources, loadConfig } from "../core/config";
7
- import { getDbPath } from "../core/paths";
8
- import { closeDatabase, getEntryCount, getMeta, isVecAvailable, openExistingDatabase } from "../indexer/db";
9
- import { getEffectiveSemanticStatus, readSemanticStatus } from "../indexer/semantic-status";
10
- import { pkgVersion } from "../version";
5
+ import { getAssetTypes } from "../../core/asset/asset-spec.js";
6
+ import { getSources, loadConfig } from "../../core/config/config.js";
7
+ import { getDbPath } from "../../core/paths.js";
8
+ import { closeDatabase, getEntryCount, getMeta, isVecAvailable, openExistingDatabase } from "../../indexer/db/db.js";
9
+ import { getEffectiveSemanticStatus, readSemanticStatus } from "../../indexer/search/semantic-status.js";
10
+ import { pkgVersion } from "../../version.js";
11
11
  /**
12
12
  * Assemble system info describing the current capabilities, configuration,
13
13
  * and index state. Used by `akm info`.