akm-cli 0.7.4 → 0.8.0-rc.10

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 (300) hide show
  1. package/CHANGELOG.md +224 -1
  2. package/README.md +22 -6
  3. package/SECURITY.md +93 -0
  4. package/dist/cli/config-migrate.js +144 -0
  5. package/dist/cli/config-validate.js +39 -0
  6. package/dist/cli/confirm.js +73 -0
  7. package/dist/cli/parse-args.js +133 -0
  8. package/dist/cli/shared.js +129 -0
  9. package/dist/cli.js +2631 -1440
  10. package/dist/commands/add-cli.js +279 -0
  11. package/dist/commands/agent-dispatch.js +110 -0
  12. package/dist/commands/agent-support.js +68 -0
  13. package/dist/commands/completions.js +3 -0
  14. package/dist/commands/config-cli.js +130 -534
  15. package/dist/commands/consolidate.js +2122 -0
  16. package/dist/commands/curate.js +45 -3
  17. package/dist/commands/db-cli.js +23 -0
  18. package/dist/commands/distill-promotion-policy.js +660 -0
  19. package/dist/commands/distill.js +1081 -73
  20. package/dist/commands/env.js +213 -0
  21. package/dist/commands/eval-cases.js +43 -0
  22. package/dist/commands/events.js +15 -24
  23. package/dist/commands/extract-cli.js +127 -0
  24. package/dist/commands/extract-prompt.js +204 -0
  25. package/dist/commands/extract.js +477 -0
  26. package/dist/commands/feedback-cli.js +331 -0
  27. package/dist/commands/graph.js +477 -0
  28. package/dist/commands/health.js +1302 -0
  29. package/dist/commands/help/help-accept.md +12 -0
  30. package/dist/commands/help/help-improve.md +69 -0
  31. package/dist/commands/help/help-proposals.md +18 -0
  32. package/dist/commands/help/help-propose.md +17 -0
  33. package/dist/commands/help/help-reject.md +11 -0
  34. package/dist/commands/history.js +54 -46
  35. package/dist/commands/improve-auto-accept.js +97 -0
  36. package/dist/commands/improve-cli.js +217 -0
  37. package/dist/commands/improve-profiles.js +166 -0
  38. package/dist/commands/improve-result-file.js +167 -0
  39. package/dist/commands/improve.js +2373 -0
  40. package/dist/commands/info.js +5 -2
  41. package/dist/commands/init.js +50 -2
  42. package/dist/commands/installed-stashes.js +102 -139
  43. package/dist/commands/knowledge.js +136 -0
  44. package/dist/commands/lint/agent-linter.js +49 -0
  45. package/dist/commands/lint/base-linter.js +479 -0
  46. package/dist/commands/lint/command-linter.js +49 -0
  47. package/dist/commands/lint/default-linter.js +16 -0
  48. package/dist/commands/lint/env-key-rules.js +154 -0
  49. package/dist/commands/lint/index.js +196 -0
  50. package/dist/commands/lint/knowledge-linter.js +16 -0
  51. package/dist/commands/lint/markdown-insertion.js +343 -0
  52. package/dist/commands/lint/memory-linter.js +61 -0
  53. package/dist/commands/lint/registry.js +36 -0
  54. package/dist/commands/lint/skill-linter.js +45 -0
  55. package/dist/commands/lint/task-linter.js +50 -0
  56. package/dist/commands/lint/types.js +4 -0
  57. package/dist/commands/lint/workflow-linter.js +56 -0
  58. package/dist/commands/lint.js +4 -0
  59. package/dist/commands/migration-help.js +3 -0
  60. package/dist/commands/proposal.js +67 -12
  61. package/dist/commands/propose.js +120 -45
  62. package/dist/commands/reflect.js +1104 -60
  63. package/dist/commands/registry-cli.js +150 -0
  64. package/dist/commands/registry-search.js +5 -2
  65. package/dist/commands/remember-cli.js +257 -0
  66. package/dist/commands/remember.js +70 -7
  67. package/dist/commands/schema-repair.js +203 -0
  68. package/dist/commands/search.js +115 -14
  69. package/dist/commands/secret.js +173 -0
  70. package/dist/commands/self-update.js +3 -0
  71. package/dist/commands/show.js +158 -60
  72. package/dist/commands/source-add.js +17 -45
  73. package/dist/commands/source-clone.js +3 -0
  74. package/dist/commands/source-manage.js +14 -19
  75. package/dist/commands/tasks.js +437 -0
  76. package/dist/commands/url-checker.js +42 -0
  77. package/dist/core/action-contributors.js +28 -0
  78. package/dist/core/asset-ref.js +17 -2
  79. package/dist/core/asset-registry.js +12 -17
  80. package/dist/core/asset-serialize.js +88 -0
  81. package/dist/core/asset-spec.js +67 -1
  82. package/dist/core/common.js +182 -0
  83. package/dist/core/concurrent.js +25 -0
  84. package/dist/core/config-io.js +347 -0
  85. package/dist/core/config-migration.js +622 -0
  86. package/dist/core/config-schema.js +534 -0
  87. package/dist/core/config-sources.js +108 -0
  88. package/dist/core/config-types.js +4 -0
  89. package/dist/core/config-walker.js +337 -0
  90. package/dist/core/config.js +364 -968
  91. package/dist/core/errors.js +42 -20
  92. package/dist/core/events.js +105 -135
  93. package/dist/core/file-lock.js +104 -0
  94. package/dist/core/frontmatter.js +75 -8
  95. package/dist/core/lesson-lint.js +3 -0
  96. package/dist/core/markdown.js +20 -0
  97. package/dist/core/memory-belief.js +62 -0
  98. package/dist/core/memory-contradiction-detect.js +274 -0
  99. package/dist/core/memory-improve.js +806 -0
  100. package/dist/core/parse.js +158 -0
  101. package/dist/core/paths.js +280 -14
  102. package/dist/core/proposal-quality-validators.js +380 -0
  103. package/dist/core/proposal-validators.js +69 -0
  104. package/dist/core/proposals.js +512 -42
  105. package/dist/core/state-db.js +1068 -0
  106. package/dist/core/text-truncation.js +107 -0
  107. package/dist/core/time.js +54 -0
  108. package/dist/core/tty.js +59 -0
  109. package/dist/core/warn.js +64 -1
  110. package/dist/core/write-source.js +3 -0
  111. package/dist/indexer/db-backup.js +391 -0
  112. package/dist/indexer/db-search.js +198 -489
  113. package/dist/indexer/db.js +990 -108
  114. package/dist/indexer/ensure-index.js +136 -0
  115. package/dist/indexer/file-context.js +3 -0
  116. package/dist/indexer/graph-boost.js +376 -101
  117. package/dist/indexer/graph-db.js +391 -0
  118. package/dist/indexer/graph-dedup.js +95 -0
  119. package/dist/indexer/graph-extraction.js +550 -114
  120. package/dist/indexer/index-context.js +4 -0
  121. package/dist/indexer/indexer.js +547 -309
  122. package/dist/indexer/llm-cache.js +52 -0
  123. package/dist/indexer/manifest.js +3 -0
  124. package/dist/indexer/matchers.js +167 -160
  125. package/dist/indexer/memory-inference.js +152 -74
  126. package/dist/indexer/metadata-contributors.js +29 -0
  127. package/dist/indexer/metadata.js +275 -196
  128. package/dist/indexer/path-resolver.js +92 -0
  129. package/dist/indexer/project-context.js +192 -0
  130. package/dist/indexer/ranking-contributors.js +331 -0
  131. package/dist/indexer/ranking.js +81 -0
  132. package/dist/indexer/search-fields.js +5 -9
  133. package/dist/indexer/search-hit-enrichers.js +111 -0
  134. package/dist/indexer/search-source.js +44 -10
  135. package/dist/indexer/semantic-status.js +6 -17
  136. package/dist/indexer/staleness-detect.js +447 -0
  137. package/dist/indexer/usage-events.js +12 -9
  138. package/dist/indexer/walker.js +28 -0
  139. package/dist/integrations/agent/builders.js +135 -0
  140. package/dist/integrations/agent/config.js +122 -230
  141. package/dist/integrations/agent/detect.js +3 -0
  142. package/dist/integrations/agent/index.js +7 -13
  143. package/dist/integrations/agent/model-aliases.js +55 -0
  144. package/dist/integrations/agent/profiles.js +70 -5
  145. package/dist/integrations/agent/prompts.js +250 -36
  146. package/dist/integrations/agent/runner.js +151 -0
  147. package/dist/integrations/agent/sdk-runner.js +126 -0
  148. package/dist/integrations/agent/spawn.js +183 -35
  149. package/dist/integrations/github.js +3 -0
  150. package/dist/integrations/lockfile.js +32 -69
  151. package/dist/integrations/session-logs/index.js +69 -0
  152. package/dist/integrations/session-logs/inline-refs.js +35 -0
  153. package/dist/integrations/session-logs/pre-filter.js +152 -0
  154. package/dist/integrations/session-logs/providers/claude-code.js +282 -0
  155. package/dist/integrations/session-logs/providers/opencode.js +258 -0
  156. package/dist/integrations/session-logs/types.js +4 -0
  157. package/dist/llm/call-ai.js +62 -0
  158. package/dist/llm/client.js +79 -88
  159. package/dist/llm/embedder.js +20 -29
  160. package/dist/llm/embedders/cache.js +3 -7
  161. package/dist/llm/embedders/local.js +42 -1
  162. package/dist/llm/embedders/remote.js +20 -8
  163. package/dist/llm/embedders/types.js +3 -7
  164. package/dist/llm/feature-gate.js +95 -48
  165. package/dist/llm/graph-extract.js +676 -72
  166. package/dist/llm/index-passes.js +44 -29
  167. package/dist/llm/memory-infer.js +80 -71
  168. package/dist/llm/metadata-enhance.js +42 -29
  169. package/dist/llm/prompts/extract-session.md +80 -0
  170. package/dist/llm/prompts/graph-extract-user-prompt.md +35 -0
  171. package/dist/output/cli-hints-full.md +292 -0
  172. package/dist/output/cli-hints-short.md +66 -0
  173. package/dist/output/cli-hints.js +7 -311
  174. package/dist/output/context.js +60 -8
  175. package/dist/output/renderers.js +306 -258
  176. package/dist/output/shapes/curate.js +56 -0
  177. package/dist/output/shapes/distill.js +10 -0
  178. package/dist/output/shapes/env-list.js +19 -0
  179. package/dist/output/shapes/events.js +11 -0
  180. package/dist/output/shapes/helpers.js +424 -0
  181. package/dist/output/shapes/history.js +7 -0
  182. package/dist/output/shapes/passthrough.js +102 -0
  183. package/dist/output/shapes/proposal-accept.js +7 -0
  184. package/dist/output/shapes/proposal-diff.js +7 -0
  185. package/dist/output/shapes/proposal-list.js +7 -0
  186. package/dist/output/shapes/proposal-producer.js +11 -0
  187. package/dist/output/shapes/proposal-reject.js +7 -0
  188. package/dist/output/shapes/proposal-show.js +7 -0
  189. package/dist/output/shapes/registry-search.js +6 -0
  190. package/dist/output/shapes/registry.js +30 -0
  191. package/dist/output/shapes/search.js +6 -0
  192. package/dist/output/shapes/secret-list.js +19 -0
  193. package/dist/output/shapes/show.js +6 -0
  194. package/dist/output/shapes/vault-list.js +19 -0
  195. package/dist/output/shapes.js +51 -511
  196. package/dist/output/text/add.js +6 -0
  197. package/dist/output/text/clone.js +6 -0
  198. package/dist/output/text/config.js +6 -0
  199. package/dist/output/text/curate.js +6 -0
  200. package/dist/output/text/distill.js +7 -0
  201. package/dist/output/text/enable-disable.js +7 -0
  202. package/dist/output/text/events.js +10 -0
  203. package/dist/output/text/feedback.js +6 -0
  204. package/dist/output/text/helpers.js +1039 -0
  205. package/dist/output/text/history.js +7 -0
  206. package/dist/output/text/import.js +6 -0
  207. package/dist/output/text/index.js +6 -0
  208. package/dist/output/text/info.js +6 -0
  209. package/dist/output/text/init.js +6 -0
  210. package/dist/output/text/list.js +6 -0
  211. package/dist/output/text/proposal-producer.js +8 -0
  212. package/dist/output/text/proposal.js +11 -0
  213. package/dist/output/text/registry-commands.js +11 -0
  214. package/dist/output/text/registry.js +30 -0
  215. package/dist/output/text/remember.js +6 -0
  216. package/dist/output/text/remove.js +6 -0
  217. package/dist/output/text/save.js +6 -0
  218. package/dist/output/text/search.js +6 -0
  219. package/dist/output/text/show.js +6 -0
  220. package/dist/output/text/update.js +6 -0
  221. package/dist/output/text/upgrade.js +6 -0
  222. package/dist/output/text/vault.js +16 -0
  223. package/dist/output/text/wiki.js +15 -0
  224. package/dist/output/text/workflow.js +14 -0
  225. package/dist/output/text.js +44 -1093
  226. package/dist/registry/build-index.js +3 -0
  227. package/dist/registry/create-provider-registry.js +3 -0
  228. package/dist/registry/factory.js +4 -1
  229. package/dist/registry/origin-resolve.js +3 -0
  230. package/dist/registry/providers/index.js +3 -0
  231. package/dist/registry/providers/skills-sh.js +71 -50
  232. package/dist/registry/providers/static-index.js +53 -48
  233. package/dist/registry/providers/types.js +3 -24
  234. package/dist/registry/resolve.js +11 -16
  235. package/dist/registry/types.js +3 -0
  236. package/dist/scripts/migrate-storage.js +17750 -0
  237. package/dist/scripts/migrations/import-fs-improve-runs-to-db.js +9031 -0
  238. package/dist/scripts/migrations/v16-to-v17.js +141 -0
  239. package/dist/setup/detect.js +3 -0
  240. package/dist/setup/ripgrep-install.js +3 -0
  241. package/dist/setup/ripgrep-resolve.js +3 -0
  242. package/dist/setup/setup.js +775 -37
  243. package/dist/setup/steps.js +3 -15
  244. package/dist/sources/include.js +3 -0
  245. package/dist/sources/provider-factory.js +5 -12
  246. package/dist/sources/provider.js +3 -20
  247. package/dist/sources/providers/filesystem.js +19 -23
  248. package/dist/sources/providers/git.js +179 -20
  249. package/dist/sources/providers/index.js +3 -0
  250. package/dist/sources/providers/install-types.js +3 -13
  251. package/dist/sources/providers/npm.js +3 -4
  252. package/dist/sources/providers/provider-utils.js +3 -0
  253. package/dist/sources/providers/sync-from-ref.js +3 -11
  254. package/dist/sources/providers/tar-utils.js +3 -0
  255. package/dist/sources/providers/website.js +18 -22
  256. package/dist/sources/resolve.js +3 -0
  257. package/dist/sources/types.js +3 -0
  258. package/dist/sources/website-ingest.js +7 -0
  259. package/dist/tasks/backends/cron.js +203 -0
  260. package/dist/tasks/backends/exec-utils.js +28 -0
  261. package/dist/tasks/backends/index.js +24 -0
  262. package/dist/tasks/backends/launchd-template.xml +19 -0
  263. package/dist/tasks/backends/launchd.js +187 -0
  264. package/dist/tasks/backends/schtasks-template.xml +29 -0
  265. package/dist/tasks/backends/schtasks.js +215 -0
  266. package/dist/tasks/parser.js +211 -0
  267. package/dist/tasks/resolveAkmBin.js +87 -0
  268. package/dist/tasks/runner.js +458 -0
  269. package/dist/tasks/schedule.js +227 -0
  270. package/dist/tasks/schema.js +15 -0
  271. package/dist/tasks/validator.js +62 -0
  272. package/dist/version.js +3 -0
  273. package/dist/wiki/index-template.md +12 -0
  274. package/dist/wiki/ingest-workflow-template.md +54 -0
  275. package/dist/wiki/log-template.md +8 -0
  276. package/dist/wiki/schema-template.md +61 -0
  277. package/dist/wiki/wiki-templates.js +15 -0
  278. package/dist/wiki/wiki.js +13 -61
  279. package/dist/workflows/authoring.js +8 -25
  280. package/dist/workflows/cli.js +3 -0
  281. package/dist/workflows/db.js +141 -2
  282. package/dist/workflows/document-cache.js +3 -10
  283. package/dist/workflows/parser.js +3 -0
  284. package/dist/workflows/renderer.js +11 -3
  285. package/dist/workflows/runs.js +91 -89
  286. package/dist/workflows/schema.js +3 -0
  287. package/dist/workflows/scope-key.js +79 -0
  288. package/dist/workflows/validator.js +4 -8
  289. package/dist/workflows/workflow-template.md +24 -0
  290. package/docs/README.md +10 -2
  291. package/docs/data-and-telemetry.md +225 -0
  292. package/docs/migration/release-notes/0.7.0.md +1 -1
  293. package/docs/migration/release-notes/0.7.4.md +1 -1
  294. package/docs/migration/release-notes/0.7.5.md +20 -0
  295. package/docs/migration/release-notes/0.8.0.md +48 -0
  296. package/docs/migration/v0.7-to-v0.8.md +1307 -0
  297. package/package.json +29 -11
  298. package/dist/commands/install-audit.js +0 -381
  299. package/dist/commands/vault.js +0 -333
  300. package/dist/templates/wiki-templates.js +0 -100
@@ -0,0 +1,12 @@
1
+ Usage:
2
+ akm proposal accept <id>
3
+
4
+ Description:
5
+ Accept a proposal and promote it into the stash.
6
+
7
+ (`akm accept` is a deprecated alias for `akm proposal accept`; it warns on
8
+ stderr and is removed in 0.9.0.)
9
+
10
+ Examples:
11
+ akm proposal accept proposal_123
12
+ akm proposal accept proposal_123 --target team-stash
@@ -0,0 +1,69 @@
1
+ Usage:
2
+ akm improve
3
+ akm improve <type>
4
+ akm improve <ref>
5
+
6
+ Description:
7
+ Analyze existing AKM assets and generate improvement proposals.
8
+
9
+ Modes:
10
+ akm improve
11
+ Improve all eligible assets in the current scope.
12
+
13
+ akm improve <type>
14
+ Improve all assets of a given type.
15
+ Example: akm improve memory
16
+
17
+ akm improve <ref>
18
+ Improve one specific asset.
19
+ Example: akm improve workflow:release-checklist
20
+
21
+ What it does:
22
+ - reviews feedback and recent history
23
+ - proposes edits to existing assets
24
+ - distills lessons where useful
25
+ - promotes durable skill lessons into skill reference-doc proposals when justified
26
+ - cleans and consolidates memories
27
+ - writes results to the proposal queue
28
+
29
+ Options:
30
+ --task <text> Add extra guidance for this improvement pass
31
+ --dry-run Show planned actions without generating proposals
32
+ --target <source> Override the write target for accepted proposals
33
+ --auto-accept[=<value>]
34
+ Confidence threshold (0-100) for auto-accepting proposals.
35
+ Default when flag is absent: ON at threshold 90 (all sub-processes).
36
+ --auto-accept same as --auto-accept=90
37
+ --auto-accept=<N> integer 0-100; accept proposals at or above N
38
+ --auto-accept=safe alias for 90 (back-compat, not deprecated)
39
+ --auto-accept=false disable auto-accept for all sub-processes;
40
+ reflect/distill proposals go to the queue and
41
+ consolidation will prompt interactively on HTTP paths
42
+ Note: until proposals carry real confidence scores, any non-`false`
43
+ value behaves like the legacy "safe" mode (whole-batch auto-accept).
44
+ --profile <name> Improve profile to apply. Built-ins: default, quick,
45
+ thorough, memory-focus. User-defined profiles under
46
+ `profiles.improve.<name>` in config are also accepted.
47
+ Profiles bundle process gating, type filters, and
48
+ run-level autoAccept/limit defaults. Falls back to
49
+ `defaults.improve` in config, then to "default".
50
+ Unknown names fall back to default with a warning.
51
+ --consolidate-recovery <mode>
52
+ Recovery mode for stale consolidate journals: abort (default) or clean
53
+ --require-feedback-signal
54
+ Only process refs with recent feedback signal events
55
+ --min-retrieval-count <n>
56
+ Retrieval fallback threshold when no recent feedback exists (default: 5)
57
+ --json-to-stdout Emit the full JSON result on stdout (legacy behaviour).
58
+ (0.8.0+: full result is recorded in the improve_runs table of
59
+ state.db and stdout is empty; use --json-to-stdout for the prior
60
+ behaviour, e.g. `akm improve --json-to-stdout | jq`.)
61
+
62
+ Examples:
63
+ akm improve
64
+ akm improve memory
65
+ akm improve skill
66
+ akm improve skill:code-review
67
+ akm improve workflow:incident-response --task "reduce duplication"
68
+ akm improve --profile quick
69
+ akm improve --profile memory-focus
@@ -0,0 +1,18 @@
1
+ Usage:
2
+ akm proposal list
3
+
4
+ Description:
5
+ List proposal queue entries.
6
+
7
+ (`akm proposals` is a deprecated alias for `akm proposal list`; it warns on
8
+ stderr and is removed in 0.9.0.)
9
+
10
+ Options:
11
+ --status <status> Filter by pending, accepted, or rejected
12
+ --type <type> Filter by asset type
13
+ --ref <ref> Filter by exact asset ref
14
+
15
+ Examples:
16
+ akm proposal list
17
+ akm proposal list --status pending
18
+ akm proposal list --type skill
@@ -0,0 +1,17 @@
1
+ Usage:
2
+ akm propose <type> <name> --task "..."
3
+ akm propose <type> <name> --file <path>
4
+
5
+ Description:
6
+ Create a proposal for a brand-new AKM asset.
7
+
8
+ Input:
9
+ --task <text> Inline task or prompt text
10
+ --file <path> Read task or prompt text from a file
11
+
12
+ Rules:
13
+ Exactly one of --task or --file is required.
14
+
15
+ Examples:
16
+ akm propose skill release-auditor --task "review release artifacts before publish"
17
+ akm propose workflow hotfix-triage --file ./prompts/hotfix-triage.md
@@ -0,0 +1,11 @@
1
+ Usage:
2
+ akm proposal reject <id> --reason "..."
3
+
4
+ Description:
5
+ Reject a proposal and record the reason.
6
+
7
+ (`akm reject` is a deprecated alias for `akm proposal reject`; it warns on
8
+ stderr and is removed in 0.9.0.)
9
+
10
+ Examples:
11
+ akm proposal reject proposal_123 --reason "duplicates existing workflow"
@@ -1,53 +1,15 @@
1
- /**
2
- * `akm history` surfaces internal mutation/usage events for a single asset
3
- * (`--ref`) or stash-wide.
4
- *
5
- * Event sources:
6
- * - `usage_events` SQLite table: search, show, and feedback events recorded
7
- * by the local indexer during normal CLI use.
8
- * - `events.jsonl` append-only stream (opt-in via `--include-proposals`):
9
- * proposal lifecycle events (`promoted`, `rejected`) emitted by
10
- * `akm proposal accept` / `akm proposal reject`. Use this flag to see
11
- * the full proposal review trail alongside usage events.
12
- *
13
- * The two sources are merged and sorted chronologically (oldest first) so
14
- * consumers see a coherent lifecycle trail in a single output.
15
- */
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/.
16
4
  import { parseAssetRef } from "../core/asset-ref";
17
5
  import { UsageError } from "../core/errors";
18
6
  import { readEvents } from "../core/events";
7
+ import { listProposals } from "../core/proposals";
8
+ import { isoToSqlite, parseSinceToIso } from "../core/time";
19
9
  import { closeDatabase, openExistingDatabase } from "../indexer/db";
20
10
  // Proposal lifecycle event types emitted by the proposal substrate (#225).
21
11
  const PROPOSAL_EVENT_TYPES = new Set(["promoted", "rejected"]);
22
12
  // ── Helpers ──────────────────────────────────────────────────────────────────
23
- function normalizeSince(since) {
24
- // Accept "YYYY-MM-DD", "YYYY-MM-DDTHH:MM:SSZ", epoch ms, or anything Date can parse.
25
- const trimmed = since.trim();
26
- if (!trimmed) {
27
- throw new UsageError("--since cannot be empty.", "INVALID_FLAG_VALUE");
28
- }
29
- // Pure-digit input → epoch milliseconds
30
- if (/^\d+$/.test(trimmed)) {
31
- const ms = Number.parseInt(trimmed, 10);
32
- const d = new Date(ms);
33
- if (Number.isNaN(d.getTime())) {
34
- throw new UsageError(`Invalid --since value: ${since}`, "INVALID_FLAG_VALUE");
35
- }
36
- return d
37
- .toISOString()
38
- .replace("T", " ")
39
- .replace(/\.\d+Z$/, "");
40
- }
41
- const parsed = new Date(trimmed);
42
- if (Number.isNaN(parsed.getTime())) {
43
- throw new UsageError(`Invalid --since value: ${since}. Expected ISO timestamp (e.g. 2026-04-01T00:00:00Z) or epoch ms.`, "INVALID_FLAG_VALUE");
44
- }
45
- // Match the "YYYY-MM-DD HH:MM:SS" format SQLite's datetime('now') stores.
46
- return parsed
47
- .toISOString()
48
- .replace("T", " ")
49
- .replace(/\.\d+Z$/, "");
50
- }
51
13
  function parseMetadata(raw) {
52
14
  if (!raw)
53
15
  return null;
@@ -66,6 +28,7 @@ function toEntry(row) {
66
28
  entryId: row.entry_id,
67
29
  query: row.query,
68
30
  signal: row.signal,
31
+ source: row.source,
69
32
  metadata: parseMetadata(row.metadata),
70
33
  createdAt: row.created_at,
71
34
  };
@@ -104,7 +67,7 @@ export async function akmHistory(options = {}) {
104
67
  parseAssetRef(trimmed);
105
68
  normalizedRef = trimmed;
106
69
  }
107
- const sinceNormalized = options.since !== undefined ? normalizeSince(options.since) : undefined;
70
+ const sinceNormalized = options.since !== undefined ? isoToSqlite(parseSinceToIso(options.since)) : undefined;
108
71
  const db = options.db ?? openExistingDatabase();
109
72
  const ownsDb = options.db === undefined;
110
73
  try {
@@ -118,8 +81,12 @@ export async function akmHistory(options = {}) {
118
81
  conditions.push("created_at >= ?");
119
82
  params.push(sinceNormalized);
120
83
  }
84
+ if (options.source !== undefined) {
85
+ conditions.push("source = ?");
86
+ params.push(options.source);
87
+ }
121
88
  const where = conditions.length > 0 ? `WHERE ${conditions.join(" AND ")}` : "";
122
- const sql = `SELECT id, event_type, query, entry_id, entry_ref, signal, metadata, created_at
89
+ const sql = `SELECT id, event_type, query, entry_id, entry_ref, signal, metadata, source, created_at
123
90
  FROM usage_events ${where}
124
91
  ORDER BY id ASC`;
125
92
  const rows = db.prepare(sql).all(...params);
@@ -128,7 +95,7 @@ export async function akmHistory(options = {}) {
128
95
  const sources = ["usage_events"];
129
96
  const proposalEntries = [];
130
97
  if (options.includeProposals === true) {
131
- sources.push("events.jsonl");
98
+ sources.push("state.db");
132
99
  // Convert sinceNormalized ("YYYY-MM-DD HH:MM:SS") to ISO for readEvents
133
100
  // which uses `ts >= since` where `ts` is ISO-8601.
134
101
  const sinceIso = sinceNormalized !== undefined ? `${sinceNormalized.replace(" ", "T")}Z` : undefined;
@@ -154,6 +121,7 @@ export async function akmHistory(options = {}) {
154
121
  entryId: null,
155
122
  query: null,
156
123
  signal: null,
124
+ source: null,
157
125
  metadata: event.metadata ?? null,
158
126
  createdAt,
159
127
  });
@@ -169,6 +137,45 @@ export async function akmHistory(options = {}) {
169
137
  return 1;
170
138
  return a.id - b.id;
171
139
  });
140
+ // ── Accept-rate-per-source (F-4 / #385) ─────────────────────────────────
141
+ let acceptRateBySource;
142
+ if (options.acceptRateBySource) {
143
+ const stashDir = options.stashDir;
144
+ if (stashDir) {
145
+ const bySource = new Map();
146
+ const countProposals = (statuses, includeArchive) => {
147
+ for (const status of statuses) {
148
+ const proposals = listProposals(stashDir, { status, includeArchive });
149
+ for (const p of proposals) {
150
+ const src = p.source || "(unknown)";
151
+ const entry = bySource.get(src) ?? { accepted: 0, rejected: 0, pending: 0 };
152
+ if (status === "accepted")
153
+ entry.accepted++;
154
+ else if (status === "rejected")
155
+ entry.rejected++;
156
+ else
157
+ entry.pending++;
158
+ bySource.set(src, entry);
159
+ }
160
+ }
161
+ };
162
+ countProposals(["pending"], false);
163
+ countProposals(["accepted", "rejected"], true);
164
+ acceptRateBySource = Array.from(bySource.entries())
165
+ .map(([source, counts]) => {
166
+ const decided = counts.accepted + counts.rejected;
167
+ return {
168
+ source,
169
+ total: decided + counts.pending,
170
+ accepted: counts.accepted,
171
+ rejected: counts.rejected,
172
+ pending: counts.pending,
173
+ acceptRate: decided > 0 ? counts.accepted / decided : null,
174
+ };
175
+ })
176
+ .sort((a, b) => b.total - a.total); // Most active source first
177
+ }
178
+ }
172
179
  const response = {
173
180
  schemaVersion: 1,
174
181
  ...(normalizedRef !== undefined ? { ref: normalizedRef } : {}),
@@ -176,6 +183,7 @@ export async function akmHistory(options = {}) {
176
183
  totalCount: entries.length,
177
184
  entries,
178
185
  sources,
186
+ ...(acceptRateBySource !== undefined ? { acceptRateBySource } : {}),
179
187
  };
180
188
  return response;
181
189
  }
@@ -0,0 +1,97 @@
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 { loadConfig } from "../core/config";
5
+ import { appendEvent } from "../core/events";
6
+ import { promoteProposal } from "../core/proposals";
7
+ import { info, warn } from "../core/warn";
8
+ // ---------------------------------------------------------------------------
9
+ // Gate implementation
10
+ // ---------------------------------------------------------------------------
11
+ /**
12
+ * Attempt to auto-accept each candidate proposal whose confidence meets the
13
+ * effective threshold. Safe to call unconditionally — returns all-empty when
14
+ * the gate is disabled or the run is a dry-run.
15
+ *
16
+ * @param candidates Proposals to evaluate, each with an optional confidence.
17
+ * @param cfg Gate configuration (phase label, thresholds, context).
18
+ * @param promoteFn Injectable override for `promoteProposal` (test seam).
19
+ */
20
+ export async function runAutoAcceptGate(candidates, cfg, promoteFn = promoteProposal) {
21
+ const result = { promoted: [], skipped: [], failed: [] };
22
+ // --- Guard: gate is disabled or context is incomplete ---
23
+ if (cfg.dryRun || cfg.globalThreshold === undefined || !cfg.stashDir) {
24
+ result.skipped = candidates.map((c) => c.proposalId);
25
+ return result;
26
+ }
27
+ const effectiveThreshold = Math.max(cfg.globalThreshold, cfg.minimumThreshold ?? 0) / 100;
28
+ const resolvedConfig = typeof cfg.config === "function" ? cfg.config() : cfg.config;
29
+ for (const candidate of candidates) {
30
+ const { proposalId, confidence } = candidate;
31
+ if (confidence === undefined || confidence < effectiveThreshold) {
32
+ result.skipped.push(proposalId);
33
+ continue;
34
+ }
35
+ try {
36
+ const promotion = await promoteFn(cfg.stashDir, resolvedConfig, proposalId, {}, undefined);
37
+ appendEvent({
38
+ eventType: "promoted",
39
+ ref: promotion.ref,
40
+ metadata: {
41
+ proposalId: promotion.proposal.id,
42
+ source: promotion.proposal.source,
43
+ ...(promotion.proposal.sourceRun !== undefined ? { sourceRun: promotion.proposal.sourceRun } : {}),
44
+ assetPath: promotion.assetPath,
45
+ autoAccept: true,
46
+ confidence,
47
+ threshold: effectiveThreshold,
48
+ phase: cfg.phase,
49
+ },
50
+ }, cfg.eventsCtx ?? {});
51
+ info(`[improve] auto-accepted ${promotion.ref} (${cfg.phase}; confidence=${confidence.toFixed(2)} >= threshold=${effectiveThreshold.toFixed(2)})`);
52
+ result.promoted.push(proposalId);
53
+ }
54
+ catch (err) {
55
+ warn(`[improve] ${cfg.phase} auto-accept failed for ${proposalId}: ${err instanceof Error ? err.message : String(err)}`);
56
+ result.failed.push(proposalId);
57
+ }
58
+ }
59
+ return result;
60
+ }
61
+ // ---------------------------------------------------------------------------
62
+ // Confidence resolvers
63
+ // ---------------------------------------------------------------------------
64
+ /**
65
+ * Read the confidence value for an extract proposal.
66
+ * Extract stores confidence at `payload.frontmatter.confidence` (set by
67
+ * extract.ts when the LLM response is parsed), not at the top-level field.
68
+ */
69
+ export function resolveExtractConfidence(proposal) {
70
+ const fm = proposal.payload.frontmatter;
71
+ const fmConf = fm?.confidence;
72
+ if (typeof fmConf === "number")
73
+ return fmConf;
74
+ // Fall back to top-level in case a future extract version normalises the path
75
+ if (typeof proposal.confidence === "number")
76
+ return proposal.confidence;
77
+ return undefined;
78
+ }
79
+ // ---------------------------------------------------------------------------
80
+ // Config builder helpers
81
+ // ---------------------------------------------------------------------------
82
+ /**
83
+ * Build a gate config for a phase, inheriting global settings from the
84
+ * improve options. Callers supply only the phase-specific overrides.
85
+ */
86
+ export function makeGateConfig(phase, shared, overrides = {}) {
87
+ return {
88
+ phase,
89
+ globalThreshold: shared.globalThreshold,
90
+ dryRun: shared.dryRun,
91
+ stashDir: shared.stashDir,
92
+ config: shared.config,
93
+ eventsCtx: shared.eventsCtx,
94
+ ...overrides,
95
+ };
96
+ }
97
+ export { loadConfig };
@@ -0,0 +1,217 @@
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 path from "node:path";
5
+ import { defineCommand } from "citty";
6
+ import { getStringArg, parseAutoAcceptFlag, parseNonNegativeIntFlag, parsePositiveIntFlag } from "../cli/parse-args";
7
+ import { output, runWithJsonErrors } from "../cli/shared";
8
+ import { loadConfig } from "../core/config";
9
+ import { UsageError } from "../core/errors";
10
+ import { getCacheDir } from "../core/paths";
11
+ import { clearLogFile, setLogFile } from "../core/warn";
12
+ import { resolveSourceEntries } from "../indexer/search-source";
13
+ import { getHyphenatedArg, getHyphenatedBoolean, parseFlagValue } from "../output/context";
14
+ import { akmImprove } from "./improve";
15
+ import { buildImproveRunId, recordTerminatedImproveRun, relativeImproveResultPath, writeImproveResultFile, } from "./improve-result-file";
16
+ export const improveCommand = defineCommand({
17
+ meta: {
18
+ name: "improve",
19
+ description: "Analyze existing AKM assets and generate improvement proposals; also consolidates memories when profiles.improve.default.processes.consolidate.enabled is true",
20
+ },
21
+ args: {
22
+ scope: {
23
+ type: "positional",
24
+ description: "Optional asset type or asset ref to improve",
25
+ required: false,
26
+ },
27
+ task: { type: "string", description: "Add extra guidance for this improvement pass" },
28
+ "dry-run": { type: "boolean", description: "Show planned actions without writing", default: false },
29
+ target: { type: "string", description: "Override the write target for accepted proposals" },
30
+ "auto-accept": {
31
+ type: "string",
32
+ description: "Auto-accept proposals at or above this confidence threshold (0-100). Default: disabled. Pass a value 0-100 to enable. 'safe' is an alias for 90. Pass 'false' to be explicit.",
33
+ },
34
+ limit: { type: "string", description: "Maximum number of assets to process (highest utility first)" },
35
+ "timeout-ms": {
36
+ type: "string",
37
+ description: "Wall-clock budget for the entire run in milliseconds (default: 7200000 = 2 hours)",
38
+ },
39
+ "consolidate-recovery": {
40
+ type: "string",
41
+ description: "How to handle stale/incomplete consolidation journals: abort (default) or clean (remove stale journal artifacts)",
42
+ },
43
+ "require-feedback-signal": {
44
+ type: "boolean",
45
+ description: "Only process assets with recent feedback signals (disables retrieval fallback)",
46
+ default: false,
47
+ },
48
+ "min-retrieval-count": {
49
+ type: "string",
50
+ description: "Minimum retrieval count for zero-feedback fallback eligibility (default: 1, set 0 to include all assets regardless of retrieval history)",
51
+ },
52
+ "json-to-stdout": {
53
+ type: "boolean",
54
+ description: "Emit the full JSON result on stdout (legacy behaviour). (0.8.0+: full result is recorded in the improve_runs table of state.db and stdout is empty; use this flag for the prior behaviour, e.g. `akm improve --json-to-stdout | jq`.)",
55
+ default: false,
56
+ },
57
+ profile: {
58
+ type: "string",
59
+ description: "Named improve profile from profiles.improve or built-in profiles (default, quick, thorough, memory-focus). Controls which sub-processes run and which asset types are processed.",
60
+ },
61
+ },
62
+ async run({ args }) {
63
+ await runWithJsonErrors(async () => {
64
+ const formatFlagValue = parseFlagValue(process.argv, "--format");
65
+ if (formatFlagValue !== undefined) {
66
+ throw new UsageError(`akm improve does not accept --format. That flag controls output formatting for other commands (search, show, etc.).\n` +
67
+ `Did you mean: akm improve (no --format flag)?`, "INVALID_FLAG_VALUE");
68
+ }
69
+ const jsonToStdout = getHyphenatedBoolean(args, "json-to-stdout");
70
+ const autoAcceptRaw = getHyphenatedArg(args, "auto-accept");
71
+ const autoAccept = parseAutoAcceptFlag(autoAcceptRaw);
72
+ const targetArg = getStringArg(args, "target");
73
+ const taskArg = getStringArg(args, "task");
74
+ const dryRun = getHyphenatedBoolean(args, "dry-run");
75
+ const limitRaw = parsePositiveIntFlag(args.limit ?? undefined);
76
+ const timeoutMs = parsePositiveIntFlag(getHyphenatedArg(args, "timeout-ms"), "--timeout-ms");
77
+ const consolidateRecoveryRaw = getHyphenatedArg(args, "consolidate-recovery");
78
+ const consolidateRecovery = consolidateRecoveryRaw === undefined
79
+ ? undefined
80
+ : consolidateRecoveryRaw.trim().toLowerCase();
81
+ if (consolidateRecovery !== undefined && consolidateRecovery !== "abort" && consolidateRecovery !== "clean") {
82
+ throw new UsageError(`Invalid --consolidate-recovery value: "${consolidateRecoveryRaw}". Must be one of: abort, clean.`, "INVALID_FLAG_VALUE");
83
+ }
84
+ const minRetrievalCountRaw = getHyphenatedArg(args, "min-retrieval-count");
85
+ const minRetrievalCount = parseNonNegativeIntFlag(minRetrievalCountRaw, "--min-retrieval-count");
86
+ const requireFeedbackSignal = getHyphenatedBoolean(args, "require-feedback-signal");
87
+ const profileArg = getStringArg(args, "profile");
88
+ const improveLogFile = path.join(getCacheDir(), "logs", "improve", `${new Date().toISOString().replace(/[:.]/g, "-")}.log`);
89
+ setLogFile(improveLogFile);
90
+ const startedAtMs = Date.now();
91
+ const startedAtIso = new Date(startedAtMs).toISOString();
92
+ // Mint the run-id up front so signal handlers can persist a partial
93
+ // record if the process is killed mid-run. Pre-2026-05-26 the runId
94
+ // was minted at end-of-run, so SIGTERM'd runs (cron timeout) left no
95
+ // row in improve_runs and effectively disappeared from `akm health`.
96
+ const runId = buildImproveRunId();
97
+ const primaryStashDir = resolveSourceEntries(undefined, loadConfig())[0]?.path;
98
+ const scopeArg = getStringArg(args, "scope");
99
+ const inferredScopeMode = (scopeArg ?? "").includes(":") ? "ref" : scopeArg ? "type" : "all";
100
+ // Signal handler + exception path both flow through this helper so
101
+ // every abnormal termination produces a row with ok:false and a
102
+ // reason in metadata.terminated.
103
+ let runRecorded = false;
104
+ const persistTerminated = (reason, errorMessage) => {
105
+ if (runRecorded)
106
+ return;
107
+ if (!primaryStashDir)
108
+ return;
109
+ runRecorded = true;
110
+ try {
111
+ recordTerminatedImproveRun(primaryStashDir, runId, startedAtIso, reason, {
112
+ scopeMode: inferredScopeMode,
113
+ scopeValue: scopeArg ?? null,
114
+ dryRun: Boolean(dryRun),
115
+ profile: profileArg ?? null,
116
+ ...(errorMessage ? { errorMessage } : {}),
117
+ });
118
+ }
119
+ catch (err) {
120
+ process.stderr.write(`warning: failed to persist terminated improve run ${runId}: ${err instanceof Error ? err.message : String(err)}\n`);
121
+ }
122
+ };
123
+ const sigtermHandler = () => {
124
+ persistTerminated("SIGTERM");
125
+ process.stderr.write(`[improve] received SIGTERM; recorded terminated run ${runId}\n`);
126
+ process.exit(143);
127
+ };
128
+ const sigintHandler = () => {
129
+ persistTerminated("SIGINT");
130
+ process.stderr.write(`[improve] received SIGINT; recorded terminated run ${runId}\n`);
131
+ process.exit(130);
132
+ };
133
+ const sighupHandler = () => {
134
+ persistTerminated("SIGHUP");
135
+ process.exit(129);
136
+ };
137
+ process.once("SIGTERM", sigtermHandler);
138
+ process.once("SIGINT", sigintHandler);
139
+ process.once("SIGHUP", sighupHandler);
140
+ let improveResult;
141
+ try {
142
+ improveResult = await akmImprove({
143
+ scope: scopeArg,
144
+ task: taskArg,
145
+ dryRun,
146
+ target: targetArg,
147
+ autoAccept,
148
+ ...(limitRaw !== undefined ? { limit: limitRaw } : {}),
149
+ ...(timeoutMs !== undefined ? { timeoutMs } : {}),
150
+ ...(minRetrievalCount !== undefined ? { minRetrievalCount } : {}),
151
+ ...(requireFeedbackSignal ? { requireFeedbackSignal } : {}),
152
+ ...(profileArg !== undefined ? { profile: profileArg } : {}),
153
+ consolidateOptions: {
154
+ target: targetArg,
155
+ dryRun,
156
+ autoAccept,
157
+ task: taskArg,
158
+ ...(consolidateRecovery !== undefined ? { recoveryMode: consolidateRecovery } : {}),
159
+ },
160
+ });
161
+ }
162
+ catch (err) {
163
+ // akmImprove threw — record the failure before letting runWithJsonErrors
164
+ // emit the standard JSON error envelope. Without this, exceptions in
165
+ // the main loop (LLM provider crash, OOM, etc.) leave no improve_runs
166
+ // row, matching the SIGTERM gap.
167
+ persistTerminated("exception", err instanceof Error ? err.message : String(err));
168
+ throw err;
169
+ }
170
+ finally {
171
+ process.removeListener("SIGTERM", sigtermHandler);
172
+ process.removeListener("SIGINT", sigintHandler);
173
+ process.removeListener("SIGHUP", sighupHandler);
174
+ clearLogFile();
175
+ }
176
+ const durationMs = Date.now() - startedAtMs;
177
+ if (jsonToStdout) {
178
+ // Legacy / escape-hatch mode: full JSON on stdout, no file write.
179
+ // Kept for scripts/agents that already pipe to jq.
180
+ output("improve", improveResult);
181
+ process.exit(0);
182
+ }
183
+ // Default mode (0.8.0+): persist the full result as a row in the
184
+ // `improve_runs` table of state.db (migration 003) and emit NOTHING
185
+ // on stdout. The verbose JSON would otherwise scroll earlier progress
186
+ // logs out of the terminal buffer. The existing `[improve] ...`
187
+ // progress log lines on stderr remain the canonical console UX —
188
+ // do NOT add any new console output here.
189
+ //
190
+ // Pre-0.8.0 wrote `<stash>/.akm/runs/<run-id>/improve-result.json`;
191
+ // those files are no longer authored. Query recent runs with:
192
+ // sqlite3 "$AKM_DATA_DIR/state.db" \
193
+ // "SELECT id, started_at, ok, dry_run FROM improve_runs \
194
+ // ORDER BY started_at DESC LIMIT 10"
195
+ // runId + primaryStashDir minted up-top so signal handlers can record
196
+ // partial runs; reuse them here for the success path.
197
+ const resultRef = relativeImproveResultPath(runId);
198
+ runRecorded = true; // Suppress any late signal-handler write — the success path owns the row now.
199
+ if (primaryStashDir) {
200
+ try {
201
+ writeImproveResultFile(primaryStashDir, runId, improveResult);
202
+ }
203
+ catch (err) {
204
+ // Stderr warning on the failure path is preferable to crashing
205
+ // the run after all the work has completed.
206
+ process.stderr.write(`warning: failed to record improve run ${resultRef}: ${err instanceof Error ? err.message : String(err)}\n`);
207
+ }
208
+ }
209
+ else {
210
+ process.stderr.write(`warning: no writable stash directory resolved; improve result not persisted to state.db (use --json-to-stdout to capture)\n`);
211
+ }
212
+ // durationMs reserved for future use (no console emission today).
213
+ void durationMs;
214
+ process.exit(0);
215
+ });
216
+ },
217
+ });