akm-cli 0.8.7 → 0.8.14

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 (324) hide show
  1. package/CHANGELOG.md +428 -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/tasks/core/backup.yml +4 -0
  9. package/dist/assets/tasks/core/extract.yml +4 -0
  10. package/dist/assets/tasks/core/improve.yml +4 -0
  11. package/dist/assets/tasks/core/index-refresh.yml +4 -0
  12. package/dist/assets/tasks/core/sync.yml +4 -0
  13. package/dist/assets/tasks/core/update-stashes.yml +4 -0
  14. package/dist/assets/tasks/core/version-check.yml +4 -0
  15. package/dist/assets/templates/html/default.html +78 -0
  16. package/dist/assets/templates/html/health.html +560 -0
  17. package/dist/assets/templates/html/vendor/echarts.min.js +45 -0
  18. package/dist/cli/config-migrate.js +6 -6
  19. package/dist/cli/config-validate.js +4 -4
  20. package/dist/cli/confirm.js +3 -3
  21. package/dist/cli/parse-args.js +1 -1
  22. package/dist/cli/shared.js +72 -19
  23. package/dist/cli-node.mjs +26 -0
  24. package/dist/cli.js +206 -3866
  25. package/dist/commands/{agent-dispatch.js → agent/agent-dispatch.js} +6 -6
  26. package/dist/commands/{agent-support.js → agent/agent-support.js} +2 -2
  27. package/dist/commands/agent/contribute-cli.js +200 -0
  28. package/dist/commands/completions.js +1 -1
  29. package/dist/commands/config-cli.js +230 -3
  30. package/dist/commands/db-cli.js +2 -2
  31. package/dist/commands/env/env-cli.js +529 -0
  32. package/dist/commands/env/env.js +410 -0
  33. package/dist/commands/env/secret-cli.js +259 -0
  34. package/dist/commands/{secret.js → env/secret.js} +6 -47
  35. package/dist/commands/events.js +4 -4
  36. package/dist/commands/feedback-cli.js +18 -34
  37. package/dist/commands/graph/graph-cli.js +132 -0
  38. package/dist/commands/{graph.js → graph/graph.js} +22 -16
  39. package/dist/commands/health/checks.js +279 -0
  40. package/dist/commands/health/html-report.js +448 -0
  41. package/dist/commands/health.js +189 -266
  42. package/dist/commands/{consolidate.js → improve/consolidate.js} +48 -36
  43. package/dist/commands/{distill-promotion-policy.js → improve/distill-promotion-policy.js} +3 -3
  44. package/dist/commands/{distill.js → improve/distill.js} +39 -18
  45. package/dist/commands/{eval-cases.js → improve/eval-cases.js} +1 -1
  46. package/dist/commands/{extract-cli.js → improve/extract-cli.js} +4 -4
  47. package/dist/commands/{extract-prompt.js → improve/extract-prompt.js} +2 -2
  48. package/dist/commands/{extract.js → improve/extract.js} +221 -26
  49. package/dist/commands/{improve-auto-accept.js → improve/improve-auto-accept.js} +30 -4
  50. package/dist/commands/{improve-cli.js → improve/improve-cli.js} +44 -22
  51. package/dist/commands/{improve-profiles.js → improve/improve-profiles.js} +13 -7
  52. package/dist/commands/{improve-result-file.js → improve/improve-result-file.js} +1 -1
  53. package/dist/commands/{improve.js → improve/improve.js} +672 -292
  54. package/dist/{core → commands/improve/memory}/memory-belief.js +2 -2
  55. package/dist/{core → commands/improve/memory}/memory-contradiction-detect.js +5 -5
  56. package/dist/{core → commands/improve/memory}/memory-improve.js +4 -4
  57. package/dist/commands/improve/reflect-noise.js +0 -0
  58. package/dist/commands/{reflect.js → improve/reflect.js} +58 -28
  59. package/dist/commands/improve/session-asset.js +248 -0
  60. package/dist/commands/lint/agent-linter.js +1 -1
  61. package/dist/commands/lint/base-linter.js +55 -37
  62. package/dist/commands/lint/command-linter.js +1 -1
  63. package/dist/commands/lint/default-linter.js +1 -1
  64. package/dist/commands/lint/env-key-rules.js +1 -1
  65. package/dist/commands/lint/index.js +19 -25
  66. package/dist/commands/lint/knowledge-linter.js +1 -1
  67. package/dist/commands/lint/memory-linter.js +1 -1
  68. package/dist/commands/lint/registry.js +8 -8
  69. package/dist/commands/lint/skill-linter.js +1 -1
  70. package/dist/commands/lint/task-linter.js +1 -1
  71. package/dist/commands/lint/workflow-linter.js +1 -1
  72. package/dist/commands/lint.js +1 -1
  73. package/dist/commands/observability-cli.js +244 -0
  74. package/dist/commands/proposal/drain-policies.js +3 -3
  75. package/dist/commands/proposal/drain.js +87 -15
  76. package/dist/commands/proposal/proposal-cli.js +490 -0
  77. package/dist/commands/{proposal.js → proposal/proposal.js} +17 -6
  78. package/dist/commands/{propose.js → proposal/propose.js} +11 -11
  79. package/dist/{core → commands/proposal/validators}/proposal-quality-validators.js +8 -3
  80. package/dist/{core → commands/proposal/validators}/proposal-validators.js +5 -5
  81. package/dist/{core → commands/proposal/validators}/proposals.js +374 -345
  82. package/dist/commands/{curate.js → read/curate.js} +7 -7
  83. package/dist/commands/{knowledge.js → read/knowledge.js} +22 -9
  84. package/dist/commands/{registry-search.js → read/registry-search.js} +5 -5
  85. package/dist/commands/{remember-cli.js → read/remember-cli.js} +15 -7
  86. package/dist/commands/read/search-cli.js +207 -0
  87. package/dist/commands/{search.js → read/search.js} +22 -27
  88. package/dist/commands/{show.js → read/show.js} +31 -45
  89. package/dist/commands/registry-cli.js +8 -8
  90. package/dist/commands/remember.js +14 -10
  91. package/dist/commands/sources/add-cli.js +293 -0
  92. package/dist/commands/{history.js → sources/history.js} +27 -25
  93. package/dist/commands/{info.js → sources/info.js} +6 -6
  94. package/dist/commands/{init.js → sources/init.js} +6 -6
  95. package/dist/commands/{installed-stashes.js → sources/installed-stashes.js} +12 -12
  96. package/dist/commands/{migration-help.js → sources/migration-help.js} +3 -2
  97. package/dist/commands/{schema-repair.js → sources/schema-repair.js} +8 -8
  98. package/dist/commands/{self-update.js → sources/self-update.js} +10 -9
  99. package/dist/commands/{source-add.js → sources/source-add.js} +10 -10
  100. package/dist/commands/{source-clone.js → sources/source-clone.js} +7 -7
  101. package/dist/commands/{source-manage.js → sources/source-manage.js} +4 -4
  102. package/dist/commands/sources/sources-cli.js +305 -0
  103. package/dist/commands/sources/stash-cli.js +219 -0
  104. package/dist/commands/{stash-skeleton.js → sources/stash-skeleton.js} +2 -1
  105. package/dist/commands/tasks/default-tasks.js +173 -0
  106. package/dist/commands/tasks/tasks-cli.js +210 -0
  107. package/dist/commands/{tasks.js → tasks/tasks.js} +14 -14
  108. package/dist/commands/wiki-cli.js +307 -0
  109. package/dist/commands/workflow-cli.js +329 -0
  110. package/dist/core/action-contributors.js +1 -1
  111. package/dist/core/assert.js +40 -0
  112. package/dist/core/asset/asset-create.js +54 -0
  113. package/dist/core/{asset-ref.js → asset/asset-ref.js} +21 -4
  114. package/dist/core/{asset-registry.js → asset/asset-registry.js} +3 -3
  115. package/dist/core/{asset-spec.js → asset/asset-spec.js} +17 -31
  116. package/dist/core/{markdown.js → asset/markdown.js} +1 -1
  117. package/dist/core/{stash-meta.js → asset/stash-meta.js} +1 -1
  118. package/dist/core/best-effort.js +64 -0
  119. package/dist/core/common.js +32 -18
  120. package/dist/core/{config-io.js → config/config-io.js} +29 -19
  121. package/dist/core/{config-migration.js → config/config-migration.js} +11 -9
  122. package/dist/core/{config-schema.js → config/config-schema.js} +50 -7
  123. package/dist/core/config/config-types.js +16 -0
  124. package/dist/core/{config-walker.js → config/config-walker.js} +2 -2
  125. package/dist/core/{config.js → config/config.js} +10 -8
  126. package/dist/core/env-secret-ref.js +90 -0
  127. package/dist/core/errors.js +13 -3
  128. package/dist/core/events.js +27 -4
  129. package/dist/core/file-lock.js +1 -1
  130. package/dist/core/improve-types.js +48 -0
  131. package/dist/core/lesson-lint.js +2 -2
  132. package/dist/core/logs-db.js +304 -0
  133. package/dist/core/paths.js +2 -2
  134. package/dist/core/ripgrep/install.js +2 -2
  135. package/dist/core/ripgrep/resolve.js +2 -2
  136. package/dist/core/state-db.js +195 -60
  137. package/dist/core/text-truncation.js +148 -0
  138. package/dist/core/time.js +1 -1
  139. package/dist/core/write-source.js +98 -85
  140. package/dist/indexer/{db-backup.js → db/db-backup.js} +9 -24
  141. package/dist/indexer/{db.js → db/db.js} +128 -118
  142. package/dist/indexer/{graph-db.js → db/graph-db.js} +9 -4
  143. package/dist/indexer/{llm-cache.js → db/llm-cache.js} +15 -12
  144. package/dist/indexer/ensure-index.js +4 -4
  145. package/dist/indexer/{graph-boost.js → graph/graph-boost.js} +1 -1
  146. package/dist/indexer/{graph-extraction.js → graph/graph-extraction.js} +55 -13
  147. package/dist/indexer/indexer.js +37 -30
  148. package/dist/indexer/init.js +54 -0
  149. package/dist/indexer/manifest.js +10 -10
  150. package/dist/indexer/{memory-inference.js → passes/memory-inference.js} +141 -33
  151. package/dist/indexer/{metadata-contributors.js → passes/metadata-contributors.js} +10 -8
  152. package/dist/indexer/{metadata.js → passes/metadata.js} +15 -19
  153. package/dist/indexer/{staleness-detect.js → passes/staleness-detect.js} +53 -12
  154. package/dist/indexer/{db-search.js → search/db-search.js} +28 -16
  155. package/dist/indexer/{ranking-contributors.js → search/ranking-contributors.js} +1 -1
  156. package/dist/indexer/{ranking.js → search/ranking.js} +2 -2
  157. package/dist/indexer/{search-hit-enrichers.js → search/search-hit-enrichers.js} +3 -3
  158. package/dist/indexer/{search-source.js → search/search-source.js} +8 -8
  159. package/dist/indexer/{semantic-status.js → search/semantic-status.js} +3 -3
  160. package/dist/indexer/usage/unmigrated-vaults-guard.js +94 -0
  161. package/dist/indexer/{usage-events.js → usage/usage-events.js} +32 -0
  162. package/dist/indexer/{file-context.js → walk/file-context.js} +10 -15
  163. package/dist/indexer/{matchers.js → walk/matchers.js} +13 -9
  164. package/dist/indexer/{path-resolver.js → walk/path-resolver.js} +6 -6
  165. package/dist/indexer/{project-context.js → walk/project-context.js} +1 -1
  166. package/dist/indexer/{walker.js → walk/walker.js} +4 -3
  167. package/dist/integrations/agent/builder-shared.js +39 -0
  168. package/dist/integrations/agent/builders.js +14 -81
  169. package/dist/integrations/agent/config.js +6 -4
  170. package/dist/integrations/agent/detect.js +1 -1
  171. package/dist/integrations/agent/index.js +23 -8
  172. package/dist/integrations/agent/prompts.js +2 -3
  173. package/dist/integrations/agent/runner.js +22 -3
  174. package/dist/integrations/agent/spawn.js +9 -10
  175. package/dist/integrations/harnesses/claude/agent-builder.js +48 -0
  176. package/dist/integrations/harnesses/claude/config-import.js +70 -0
  177. package/dist/integrations/harnesses/claude/index.js +64 -0
  178. package/dist/integrations/{session-logs/providers/claude-code.js → harnesses/claude/session-log.js} +32 -5
  179. package/dist/integrations/harnesses/index.js +144 -0
  180. package/dist/integrations/harnesses/opencode/agent-builder.js +43 -0
  181. package/dist/integrations/harnesses/opencode/config-import.js +82 -0
  182. package/dist/integrations/harnesses/opencode/index.js +59 -0
  183. package/dist/integrations/{session-logs/providers/opencode.js → harnesses/opencode/session-log.js} +1 -1
  184. package/dist/integrations/harnesses/opencode-sdk/index.js +49 -0
  185. package/dist/integrations/harnesses/opencode-sdk/sdk-runner.js +234 -0
  186. package/dist/integrations/harnesses/types.js +43 -0
  187. package/dist/integrations/lockfile.js +7 -16
  188. package/dist/integrations/session-logs/index.js +82 -9
  189. package/dist/llm/call-ai.js +4 -4
  190. package/dist/llm/client.js +146 -6
  191. package/dist/llm/embedder.js +6 -6
  192. package/dist/llm/embedders/local.js +9 -22
  193. package/dist/llm/embedders/remote.js +2 -2
  194. package/dist/llm/embedders/types.js +1 -1
  195. package/dist/llm/graph-extract.js +31 -12
  196. package/dist/llm/index-passes.js +1 -1
  197. package/dist/llm/memory-infer.js +12 -5
  198. package/dist/llm/metadata-enhance.js +2 -2
  199. package/dist/llm/usage-persist.js +77 -0
  200. package/dist/llm/usage-telemetry.js +103 -0
  201. package/dist/output/context.js +9 -46
  202. package/dist/output/html-render.js +73 -0
  203. package/dist/output/renderers.js +88 -58
  204. package/dist/output/shapes/curate.js +7 -3
  205. package/dist/output/shapes/distill.js +7 -3
  206. package/dist/output/shapes/env-list.js +18 -16
  207. package/dist/output/shapes/events.js +5 -4
  208. package/dist/output/shapes/helpers.js +19 -5
  209. package/dist/output/shapes/history.js +7 -3
  210. package/dist/output/shapes/passthrough.js +8 -11
  211. package/dist/output/shapes/{proposal-accept.js → proposal/accept.js} +7 -3
  212. package/dist/output/shapes/{proposal-diff.js → proposal/diff.js} +7 -3
  213. package/dist/output/shapes/{proposal-list.js → proposal/list.js} +7 -3
  214. package/dist/output/shapes/{proposal-producer.js → proposal/producer.js} +5 -4
  215. package/dist/output/shapes/{proposal-reject.js → proposal/reject.js} +7 -3
  216. package/dist/output/shapes/{proposal-show.js → proposal/show.js} +7 -3
  217. package/dist/output/shapes/registry-search.js +7 -3
  218. package/dist/output/shapes/registry.js +12 -0
  219. package/dist/output/shapes/search.js +7 -3
  220. package/dist/output/shapes/secret-list.js +18 -16
  221. package/dist/output/shapes/show.js +7 -3
  222. package/dist/output/shapes.js +55 -30
  223. package/dist/output/text/add.js +2 -3
  224. package/dist/output/text/clone.js +2 -3
  225. package/dist/output/text/config.js +2 -3
  226. package/dist/output/text/curate.js +4 -3
  227. package/dist/output/text/distill.js +2 -3
  228. package/dist/output/text/enable-disable.js +5 -4
  229. package/dist/output/text/env.js +13 -0
  230. package/dist/output/text/events.js +5 -4
  231. package/dist/output/text/feedback.js +4 -3
  232. package/dist/output/text/helpers.js +123 -40
  233. package/dist/output/text/history.js +2 -3
  234. package/dist/output/text/import.js +2 -3
  235. package/dist/output/text/index.js +2 -3
  236. package/dist/output/text/info.js +2 -3
  237. package/dist/output/text/init.js +2 -3
  238. package/dist/output/text/list.js +2 -3
  239. package/dist/output/text/proposal/producer.js +9 -0
  240. package/dist/output/text/proposal/proposal.js +13 -0
  241. package/dist/output/text/registry-commands.js +8 -7
  242. package/dist/output/text/registry.js +12 -0
  243. package/dist/output/text/remember.js +4 -3
  244. package/dist/output/text/remove.js +2 -3
  245. package/dist/output/text/save.js +2 -3
  246. package/dist/output/text/search.js +4 -3
  247. package/dist/output/text/show.js +4 -3
  248. package/dist/output/text/update.js +2 -3
  249. package/dist/output/text/upgrade.js +2 -3
  250. package/dist/output/text/wiki.js +12 -11
  251. package/dist/output/text/workflow.js +12 -10
  252. package/dist/output/text.js +66 -32
  253. package/dist/registry/build-index.js +11 -10
  254. package/dist/registry/factory.js +1 -1
  255. package/dist/registry/origin-resolve.js +1 -1
  256. package/dist/registry/providers/index.js +2 -2
  257. package/dist/registry/providers/skills-sh.js +91 -72
  258. package/dist/registry/providers/static-index.js +75 -52
  259. package/dist/registry/resolve.js +3 -3
  260. package/dist/runtime.js +242 -0
  261. package/dist/scripts/migrate-storage.js +1654 -683
  262. package/dist/scripts/migrations/import-fs-improve-runs-to-db.js +254 -168
  263. package/dist/setup/detect.js +311 -9
  264. package/dist/setup/harness-config-import.js +6 -120
  265. package/dist/setup/setup.js +454 -43
  266. package/dist/sources/include.js +1 -1
  267. package/dist/sources/provider-factory.js +2 -2
  268. package/dist/sources/providers/filesystem.js +3 -3
  269. package/dist/sources/providers/git.js +9 -9
  270. package/dist/sources/providers/index.js +4 -4
  271. package/dist/sources/providers/npm.js +6 -6
  272. package/dist/sources/providers/provider-utils.js +13 -20
  273. package/dist/sources/providers/sync-from-ref.js +5 -5
  274. package/dist/sources/providers/tar-utils.js +2 -2
  275. package/dist/sources/providers/website.js +2 -2
  276. package/dist/sources/resolve.js +5 -5
  277. package/dist/sources/website-ingest.js +5 -5
  278. package/dist/storage/database.js +102 -0
  279. package/dist/storage/engines/sqlite-migrations.js +42 -0
  280. package/dist/storage/locations.js +25 -0
  281. package/dist/storage/repositories/index-db.js +43 -0
  282. package/dist/storage/repositories/workflow-runs-repository.js +141 -0
  283. package/dist/tasks/backends/cron.js +4 -4
  284. package/dist/tasks/backends/exec-utils.js +32 -0
  285. package/dist/tasks/backends/index.js +3 -3
  286. package/dist/tasks/backends/launchd.js +7 -14
  287. package/dist/tasks/backends/schtasks.js +7 -16
  288. package/dist/tasks/embedded.js +71 -0
  289. package/dist/tasks/parser.js +2 -2
  290. package/dist/tasks/resolveAkmBin.js +1 -1
  291. package/dist/tasks/runner.js +127 -31
  292. package/dist/tasks/schedule.js +1 -1
  293. package/dist/tasks/validator.js +7 -7
  294. package/dist/text-import-hook.mjs +51 -0
  295. package/dist/version.js +2 -1
  296. package/dist/wiki/wiki.js +7 -7
  297. package/dist/workflows/{authoring.js → authoring/authoring.js} +6 -6
  298. package/dist/workflows/{scope-key.js → authoring/scope-key.js} +1 -1
  299. package/dist/workflows/cli.js +1 -1
  300. package/dist/workflows/db.js +54 -32
  301. package/dist/workflows/parser.js +4 -4
  302. package/dist/workflows/renderer.js +5 -5
  303. package/dist/workflows/runtime/agent-identity.js +56 -0
  304. package/dist/workflows/runtime/checkin.js +57 -0
  305. package/dist/workflows/{runs.js → runtime/runs.js} +197 -101
  306. package/dist/workflows/validate-summary.js +82 -0
  307. package/docs/README.md +1 -1
  308. package/docs/data-and-telemetry.md +6 -6
  309. package/package.json +17 -8
  310. package/dist/commands/add-cli.js +0 -279
  311. package/dist/commands/env.js +0 -213
  312. package/dist/integrations/agent/sdk-runner.js +0 -126
  313. package/dist/output/shapes/vault-list.js +0 -19
  314. package/dist/output/text/proposal-producer.js +0 -8
  315. package/dist/output/text/proposal.js +0 -12
  316. package/dist/output/text/vault.js +0 -16
  317. /package/dist/core/{asset-serialize.js → asset/asset-serialize.js} +0 -0
  318. /package/dist/core/{frontmatter.js → asset/frontmatter.js} +0 -0
  319. /package/dist/core/{config-sources.js → config/config-sources.js} +0 -0
  320. /package/dist/indexer/{graph-dedup.js → graph/graph-dedup.js} +0 -0
  321. /package/dist/{core/config-types.js → indexer/passes/pass-context.js} +0 -0
  322. /package/dist/indexer/{search-fields.js → search/search-fields.js} +0 -0
  323. /package/dist/indexer/{index-context.js → walk/index-context.js} +0 -0
  324. /package/dist/workflows/{document-cache.js → runtime/document-cache.js} +0 -0
@@ -24,10 +24,11 @@
24
24
  * Values are stored as raw bytes (no quoting, multi-line allowed) so they
25
25
  * round-trip byte-exact, unlike env values which forbid literal newlines.
26
26
  */
27
- import crypto from "node:crypto";
28
27
  import fs from "node:fs";
29
28
  import path from "node:path";
30
- import { probeLock, releaseLock, tryAcquireLockSync } from "../core/file-lock";
29
+ import { writeFileAtomic } from "../../core/common.js";
30
+ import { probeLock, releaseLock, tryAcquireLockSync } from "../../core/file-lock.js";
31
+ import { sleepSync } from "../../runtime.js";
31
32
  // ── Write-lock helper ─────────────────────────────────────────────────────────
32
33
  /**
33
34
  * Acquire an exclusive lock for the given secret path, run `fn`, then release.
@@ -50,16 +51,7 @@ export function withSecretLock(secretPath, fn) {
50
51
  : ` Lock file ${lockPath} could not be inspected.`;
51
52
  throw new Error(`Could not acquire secret lock for ${secretPath} after 5s.${holderHint} Retry once any other akm secret operation finishes, or remove the stale lock file.`);
52
53
  }
53
- if (typeof globalThis.Bun?.sleepSync ===
54
- "function") {
55
- globalThis.Bun.sleepSync(10);
56
- }
57
- else {
58
- let spin = 0;
59
- while (spin++ < 100_000) {
60
- /* yield */
61
- }
62
- }
54
+ sleepSync(10);
63
55
  }
64
56
  try {
65
57
  return fn();
@@ -69,40 +61,6 @@ export function withSecretLock(secretPath, fn) {
69
61
  }
70
62
  }
71
63
  // ── Atomic byte write ──────────────────────────────────────────────────────────
72
- /**
73
- * Atomically write `data` to `target` at mode 0600. Unlike `writeFileAtomic`
74
- * in core/common (string content), this accepts a Buffer so secret bytes
75
- * round-trip exactly — binary certs and CRLF/LF line endings are preserved.
76
- */
77
- function writeSecretAtomic(target, data) {
78
- const tmp = `${target}.tmp.${process.pid}.${crypto.randomBytes(8).toString("hex")}`;
79
- const fd = fs.openSync(tmp, "w", 0o600);
80
- try {
81
- fs.writeSync(fd, data);
82
- try {
83
- fs.fdatasyncSync(fd);
84
- }
85
- catch {
86
- // Best-effort durability; some pseudo-filesystems lack fdatasync.
87
- }
88
- }
89
- finally {
90
- fs.closeSync(fd);
91
- }
92
- fs.renameSync(tmp, target);
93
- try {
94
- const dirFd = fs.openSync(path.dirname(target), "r");
95
- try {
96
- fs.fsyncSync(dirFd);
97
- }
98
- finally {
99
- fs.closeSync(dirFd);
100
- }
101
- }
102
- catch {
103
- // Directory fsync is unsupported on FAT / some FUSE mounts / Windows.
104
- }
105
- }
106
64
  function ensureParentDir(filePath) {
107
65
  const dir = path.dirname(filePath);
108
66
  if (!fs.existsSync(dir))
@@ -153,7 +111,8 @@ export function readValue(secretPath) {
153
111
  export function setSecret(secretPath, value) {
154
112
  ensureParentDir(secretPath);
155
113
  withSecretLock(secretPath, () => {
156
- writeSecretAtomic(secretPath, value);
114
+ // Mode 0600: secrets must never be world-readable, even transiently.
115
+ writeFileAtomic(secretPath, value, 0o600);
157
116
  });
158
117
  }
159
118
  /**
@@ -10,10 +10,10 @@
10
10
  * text-renderer pipeline as the rest of the CLI (no silent
11
11
  * `JSON.stringify` fallback).
12
12
  */
13
- import { parseAssetRef } from "../core/asset-ref";
14
- import { UsageError } from "../core/errors";
15
- import { readEvents, tailEvents } from "../core/events";
16
- import { parseSinceToIso } from "../core/time";
13
+ import { parseAssetRef } from "../core/asset/asset-ref.js";
14
+ import { UsageError } from "../core/errors.js";
15
+ import { readEvents, tailEvents } from "../core/events.js";
16
+ import { parseSinceToIso } from "../core/time.js";
17
17
  /**
18
18
  * Parse `--since` accepting either a byte-offset cursor (`@offset:<int>`) for
19
19
  * cross-process resumption, or a timestamp / epoch-ms (the existing form).
@@ -3,19 +3,19 @@
3
3
  // file, You can obtain one at https://mozilla.org/MPL/2.0/.
4
4
  import fs from "node:fs";
5
5
  import { defineCommand } from "citty";
6
- import { output, parseAllFlagValues, runWithJsonErrors } from "../cli/shared";
7
- import { parseAssetRef } from "../core/asset-ref";
8
- import { assembleAsset } from "../core/asset-serialize";
9
- import { writeFileAtomic } from "../core/common";
10
- import { FEEDBACK_FAILURE_MODES, loadConfig } from "../core/config";
11
- import { UsageError } from "../core/errors";
12
- import { appendEvent } from "../core/events";
13
- import { parseFrontmatter, parseFrontmatterBlock } from "../core/frontmatter";
14
- import { warn } from "../core/warn";
15
- import { applyFeedbackToUtilityScore, closeDatabase, findEntryIdByRef, openExistingDatabase } from "../indexer/db";
16
- import { ensureIndex } from "../indexer/ensure-index";
17
- import { resolveSourceEntries } from "../indexer/search-source";
18
- import { insertUsageEvent } from "../indexer/usage-events";
6
+ import { output, parseAllFlagValues, runWithJsonErrors } from "../cli/shared.js";
7
+ import { parseAssetRef } from "../core/asset/asset-ref.js";
8
+ import { assembleAsset } from "../core/asset/asset-serialize.js";
9
+ import { parseFrontmatter, parseFrontmatterBlock } from "../core/asset/frontmatter.js";
10
+ import { writeFileAtomic } from "../core/common.js";
11
+ import { FEEDBACK_FAILURE_MODES, loadConfig } from "../core/config/config.js";
12
+ import { UsageError } from "../core/errors.js";
13
+ import { appendEvent } from "../core/events.js";
14
+ import { warn } from "../core/warn.js";
15
+ import { applyFeedbackToUtilityScore, closeDatabase, findEntryIdByRef, getEntryFilePathById, openExistingDatabase, } from "../indexer/db/db.js";
16
+ import { ensureIndex } from "../indexer/ensure-index.js";
17
+ import { resolveSourceEntries } from "../indexer/search/search-source.js";
18
+ import { countFeedbackSignals, insertUsageEvent } from "../indexer/usage/usage-events.js";
19
19
  // ── Tag validation ────────────────────────────────────────────────────────────
20
20
  const TAG_KEY_RE = /^[a-z_][a-z0-9_]*$/;
21
21
  const MAX_FEEDBACK_TAGS = 10;
@@ -64,12 +64,12 @@ function appendLessonStrength(type, name, feedbackRef) {
64
64
  warn(`[feedback] --applied-to: lesson ${ref} is not in the index.`);
65
65
  return null;
66
66
  }
67
- const row = db.prepare("SELECT file_path FROM entries WHERE id = ?").get(entryId);
68
- if (!row?.file_path) {
67
+ const resolvedPath = getEntryFilePathById(db, entryId);
68
+ if (!resolvedPath) {
69
69
  warn(`[feedback] --applied-to: cannot resolve file path for ${ref}.`);
70
70
  return null;
71
71
  }
72
- filePath = row.file_path;
72
+ filePath = resolvedPath;
73
73
  }
74
74
  finally {
75
75
  closeDatabase(db);
@@ -136,7 +136,6 @@ export const feedbackCommand = defineCommand({
136
136
  type: "string",
137
137
  description: "Reason for the feedback (required for negative feedback by default; used by distillation)",
138
138
  },
139
- note: { type: "string", description: "Alias for --reason (backward-compatible, prefer --reason)" },
140
139
  "failure-mode": {
141
140
  type: "string",
142
141
  description: `Structured failure-mode taxonomy for negative feedback (F-3 / #384). ` +
@@ -168,14 +167,7 @@ export const feedbackCommand = defineCommand({
168
167
  throw new UsageError("Specify --positive or --negative.");
169
168
  }
170
169
  const signal = args.positive ? "positive" : "negative";
171
- // `--note` is a deprecated back-compat alias for `--reason` (removed in
172
- // 0.9.0). Warn on stderr when it is used as the sole source (i.e. without
173
- // an explicit `--reason`). Warnings go to stderr only so JSON stdout
174
- // consumers are unaffected.
175
- if (args.note !== undefined && args.reason === undefined) {
176
- warn("warning: '--note' is deprecated for 'akm feedback'; use '--reason'. Removed in 0.9.0.");
177
- }
178
- const reason = args.reason ?? args.note;
170
+ const reason = args.reason;
179
171
  // F-3 / #384: Validate --failure-mode against the curated enum.
180
172
  const failureMode = args["failure-mode"]?.trim() || undefined;
181
173
  if (failureMode) {
@@ -242,15 +234,7 @@ export const feedbackCommand = defineCommand({
242
234
  // usage_events so the delta reflects the entire signal history.
243
235
  // Uses MemRL bounded-step EMA (F-5 / #386, arXiv:2601.03192).
244
236
  try {
245
- const counts = db
246
- .prepare(`SELECT
247
- SUM(CASE WHEN signal = 'positive' THEN 1 ELSE 0 END) AS pos,
248
- SUM(CASE WHEN signal = 'negative' THEN 1 ELSE 0 END) AS neg
249
- FROM usage_events
250
- WHERE event_type = 'feedback' AND entry_id = ?`)
251
- .get(entryId);
252
- const pos = counts?.pos ?? 0;
253
- const neg = counts?.neg ?? 0;
237
+ const { pos, neg } = countFeedbackSignals(db, entryId);
254
238
  utilityResult = applyFeedbackToUtilityScore(db, entryId, pos, neg);
255
239
  }
256
240
  catch {
@@ -0,0 +1,132 @@
1
+ // This Source Code Form is subject to the terms of the Mozilla Public
2
+ // License, v. 2.0. If a copy of the MPL was not distributed with this
3
+ // file, You can obtain one at https://mozilla.org/MPL/2.0/.
4
+ /**
5
+ * `akm graph` command family. Extracted verbatim from src/cli.ts (WS6) so the
6
+ * God Module shrinks; the `main.subCommands.graph` key and every subcommand's
7
+ * args/output shape are byte-identical. Each handler is migrated to
8
+ * `defineJsonCommand`, which wraps the body in `runWithJsonErrors` and emits the
9
+ * same JSON envelope (stdout/stderr/exit-code) as the inline `runWithJsonErrors`
10
+ * form it replaces.
11
+ */
12
+ import { defineCommand } from "citty";
13
+ import { hasSubcommand, parsePositiveIntFlag } from "../../cli/parse-args.js";
14
+ import { defineJsonCommand, output, runWithJsonErrors } from "../../cli/shared.js";
15
+ import { akmGraphEntities, akmGraphEntity, akmGraphExport, akmGraphOrphans, akmGraphRelated, akmGraphRelations, akmGraphSummary, akmGraphUpdate, } from "./graph.js";
16
+ // Single source of truth: the routing set is derived from the subCommands keys
17
+ // (M10) so adding a subcommand can never silently desync from `hasSubcommand`.
18
+ const graphSubCommands = {
19
+ summary: defineJsonCommand({
20
+ meta: { name: "summary", description: "Show entity-graph counts and quality telemetry" },
21
+ args: {
22
+ source: { type: "string", description: "Source name/path (default: primary stash source)" },
23
+ },
24
+ run({ args }) {
25
+ output("graph-summary", akmGraphSummary({ source: args.source }));
26
+ },
27
+ }),
28
+ entities: defineJsonCommand({
29
+ meta: { name: "entities", description: "List entities with per-file occurrence counts" },
30
+ args: {
31
+ source: { type: "string", description: "Source name/path (default: primary stash source)" },
32
+ limit: { type: "string", description: "Maximum entities to return" },
33
+ },
34
+ run({ args }) {
35
+ output("graph-entities", akmGraphEntities({ source: args.source, limit: parsePositiveIntFlag(args.limit ?? undefined) }));
36
+ },
37
+ }),
38
+ relations: defineJsonCommand({
39
+ meta: { name: "relations", description: "List relations with occurrence counts" },
40
+ args: {
41
+ source: { type: "string", description: "Source name/path (default: primary stash source)" },
42
+ limit: { type: "string", description: "Maximum relations to return" },
43
+ },
44
+ run({ args }) {
45
+ output("graph-relations", akmGraphRelations({ source: args.source, limit: parsePositiveIntFlag(args.limit ?? undefined) }));
46
+ },
47
+ }),
48
+ related: defineJsonCommand({
49
+ meta: { name: "related", description: "Show graph-related neighboring assets for a ref" },
50
+ args: {
51
+ ref: { type: "positional", description: "Asset ref", required: true },
52
+ source: { type: "string", description: "Source name/path (default: primary stash source)" },
53
+ limit: { type: "string", description: "Maximum related assets to return" },
54
+ },
55
+ async run({ args }) {
56
+ output("graph-related", await akmGraphRelated({
57
+ ref: args.ref ?? "",
58
+ source: args.source,
59
+ limit: parsePositiveIntFlag(args.limit ?? undefined),
60
+ }));
61
+ },
62
+ }),
63
+ entity: defineJsonCommand({
64
+ meta: { name: "entity", description: "List assets that contain the given entity" },
65
+ args: {
66
+ name: { type: "positional", description: "Entity name", required: true },
67
+ source: { type: "string", description: "Source name/path (default: primary stash source)" },
68
+ limit: { type: "string", description: "Maximum matches to return" },
69
+ },
70
+ run({ args }) {
71
+ output("graph-entity", akmGraphEntity({
72
+ name: args.name ?? "",
73
+ source: args.source,
74
+ limit: parsePositiveIntFlag(args.limit ?? undefined),
75
+ }));
76
+ },
77
+ }),
78
+ orphans: defineJsonCommand({
79
+ meta: { name: "orphans", description: "List assets with no extracted graph entities" },
80
+ args: {
81
+ source: { type: "string", description: "Source name/path (default: primary stash source)" },
82
+ limit: { type: "string", description: "Maximum orphans to return" },
83
+ },
84
+ run({ args }) {
85
+ output("graph-orphans", akmGraphOrphans({ source: args.source, limit: parsePositiveIntFlag(args.limit ?? undefined) }));
86
+ },
87
+ }),
88
+ export: defineJsonCommand({
89
+ meta: { name: "export", description: "Export graph artifact as JSON or JSONL" },
90
+ args: {
91
+ source: { type: "string", description: "Source name/path (default: primary stash source)" },
92
+ out: { type: "string", description: "Output path" },
93
+ format: { type: "string", description: "Export format (json|jsonl)", default: "json" },
94
+ },
95
+ run({ args }) {
96
+ output("graph-export", akmGraphExport({
97
+ source: args.source,
98
+ out: args.out ?? "",
99
+ format: args.format,
100
+ }));
101
+ },
102
+ }),
103
+ update: defineJsonCommand({
104
+ meta: { name: "update", description: "Re-run graph extraction, optionally scoped to specific asset refs" },
105
+ args: {
106
+ refs: {
107
+ type: "positional",
108
+ description: "Zero or more asset refs to scope extraction (omit for a full re-extract)",
109
+ required: false,
110
+ default: "",
111
+ },
112
+ source: { type: "string", description: "Source name/path (default: primary stash source)" },
113
+ },
114
+ async run({ args }) {
115
+ // `refs` is a single positional; collect remaining argv tokens as well.
116
+ const rawRefs = [args.refs, ...(Array.isArray(args._) ? args._ : [])].filter((r) => typeof r === "string" && r.trim().length > 0);
117
+ output("graph-update", await akmGraphUpdate({ refs: rawRefs.length > 0 ? rawRefs : undefined, source: args.source }));
118
+ },
119
+ }),
120
+ };
121
+ const GRAPH_SUBCOMMAND_SET = new Set(Object.keys(graphSubCommands));
122
+ export const graphCommand = defineCommand({
123
+ meta: { name: "graph", description: "Inspect the indexed entity graph stored in SQLite" },
124
+ subCommands: graphSubCommands,
125
+ run({ args }) {
126
+ return runWithJsonErrors(() => {
127
+ if (hasSubcommand(args, GRAPH_SUBCOMMAND_SET))
128
+ return;
129
+ output("graph-summary", akmGraphSummary());
130
+ });
131
+ },
132
+ });
@@ -3,18 +3,18 @@
3
3
  // file, You can obtain one at https://mozilla.org/MPL/2.0/.
4
4
  import fs from "node:fs";
5
5
  import path from "node:path";
6
- import { parseAssetRef } from "../core/asset-ref";
7
- import { loadConfig } from "../core/config";
8
- import { NotFoundError, UsageError } from "../core/errors";
9
- import { getDbPath } from "../core/paths";
10
- import { warn } from "../core/warn";
11
- import { closeDatabase, findEntryIdByRef, getEntryById, openDatabase, openExistingDatabase } from "../indexer/db";
12
- import { listRelatedPathsForFile } from "../indexer/graph-boost";
13
- import { loadStoredGraphSnapshot } from "../indexer/graph-db";
14
- import { runGraphExtractionPass } from "../indexer/graph-extraction";
15
- import { lookup } from "../indexer/indexer";
16
- import { resolveAssetPath } from "../indexer/path-resolver";
17
- import { findSourceForPath, resolveSourceEntries } from "../indexer/search-source";
6
+ import { parseAssetRef } from "../../core/asset/asset-ref.js";
7
+ import { loadConfig } from "../../core/config/config.js";
8
+ import { NotFoundError, UsageError } from "../../core/errors.js";
9
+ import { getDbPath } from "../../core/paths.js";
10
+ import { warn } from "../../core/warn.js";
11
+ import { closeDatabase, findEntryIdByRef, getEntryById, getEntryRefRowsForStashRoot, openDatabase, openExistingDatabase, } from "../../indexer/db/db.js";
12
+ import { loadStoredGraphSnapshot } from "../../indexer/db/graph-db.js";
13
+ import { listRelatedPathsForFile } from "../../indexer/graph/graph-boost.js";
14
+ import { runGraphExtractionPass } from "../../indexer/graph/graph-extraction.js";
15
+ import { lookup } from "../../indexer/indexer.js";
16
+ import { findSourceForPath, resolveSourceEntries } from "../../indexer/search/search-source.js";
17
+ import { resolveAssetPath } from "../../indexer/walk/path-resolver.js";
18
18
  function resolveGraphStashPath(source) {
19
19
  const sources = resolveSourceEntries(undefined, loadConfig());
20
20
  if (sources.length === 0) {
@@ -243,9 +243,7 @@ function normalizeGraphName(value) {
243
243
  return value.trim().toLowerCase();
244
244
  }
245
245
  function buildRefByPath(stashRoot, db) {
246
- const rows = db
247
- .prepare("SELECT file_path, entry_json FROM entries WHERE stash_dir = ? OR file_path LIKE ?")
248
- .all(stashRoot, `${stashRoot}%`);
246
+ const rows = getEntryRefRowsForStashRoot(db, stashRoot);
249
247
  const map = new Map();
250
248
  for (const row of rows) {
251
249
  if (map.has(row.file_path))
@@ -432,7 +430,15 @@ export async function akmGraphUpdate(options) {
432
430
  const file = path.basename(event.currentPath);
433
431
  warn(`[graph] extracting ${event.processed}/${event.total} ${file}`);
434
432
  };
435
- const result = await extractionFn(config, sources, undefined, db, false, onProgress, passOptions);
433
+ const result = await extractionFn({
434
+ config,
435
+ sources,
436
+ signal: undefined,
437
+ db,
438
+ reEnrich: false,
439
+ onProgress,
440
+ options: passOptions,
441
+ });
436
442
  const durationMs = Date.now() - startMs;
437
443
  return {
438
444
  shape: "graph-update",
@@ -0,0 +1,279 @@
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 { spawnSync } from "node:child_process";
5
+ import { loadConfig } from "../../core/config/config.js";
6
+ import { detectAgentCliProfiles, requireAgentProfile } from "../../integrations/agent/index.js";
7
+ const ACTIVE_RUN_WARN_MS = 15 * 60 * 1000;
8
+ /**
9
+ * Probe the configured agent profile. Self-contained (reads config + PATH); the
10
+ * only check that performs IO at dispatch time, preserving the original inline
11
+ * `runAgentProbe()` call site behaviour exactly.
12
+ */
13
+ export function runAgentProbe() {
14
+ const config = loadConfig();
15
+ // v2: check profiles.agent first
16
+ if (config.profiles?.agent) {
17
+ const defaultName = config.defaults?.agent;
18
+ const profileCount = Object.keys(config.profiles.agent).length;
19
+ if (profileCount === 0) {
20
+ return {
21
+ name: "agent-profile",
22
+ kind: "deterministic",
23
+ status: "unknown",
24
+ confidence: "high",
25
+ message: "No agent profiles configured in profiles.agent.",
26
+ };
27
+ }
28
+ const profileName = defaultName ?? Object.keys(config.profiles.agent)[0];
29
+ const profile = config.profiles.agent[profileName];
30
+ return {
31
+ name: "agent-profile",
32
+ kind: "deterministic",
33
+ status: "pass",
34
+ confidence: "high",
35
+ message: `v2 agent profile "${profileName}" configured (platform: ${profile?.platform ?? "unknown"}).`,
36
+ evidence: { profile: profileName, platform: profile?.platform, profileCount },
37
+ };
38
+ }
39
+ if (!config.profiles?.agent && !config.defaults?.agent) {
40
+ return {
41
+ name: "agent-profile",
42
+ kind: "deterministic",
43
+ status: "unknown",
44
+ confidence: "high",
45
+ message: "No agent config present.",
46
+ };
47
+ }
48
+ let profile;
49
+ try {
50
+ profile = requireAgentProfile(config);
51
+ }
52
+ catch (error) {
53
+ return {
54
+ name: "agent-profile",
55
+ kind: "deterministic",
56
+ status: "warn",
57
+ confidence: "high",
58
+ message: error instanceof Error ? error.message : String(error),
59
+ };
60
+ }
61
+ if (profile.sdkMode === true) {
62
+ return {
63
+ name: "agent-profile",
64
+ kind: "deterministic",
65
+ status: profile.model ? "pass" : "warn",
66
+ confidence: "high",
67
+ message: profile.model
68
+ ? `SDK mode profile "${profile.name}" is configured.`
69
+ : `SDK mode profile "${profile.name}" has no explicit model.`,
70
+ evidence: { profile: profile.name, sdkMode: true, model: profile.model ?? null },
71
+ };
72
+ }
73
+ const detections = detectAgentCliProfiles(config);
74
+ const detection = detections.find((entry) => entry.name === profile.name);
75
+ if (!detection?.available) {
76
+ return {
77
+ name: "agent-profile",
78
+ kind: "deterministic",
79
+ status: "fail",
80
+ confidence: "high",
81
+ message: `Default agent profile "${profile.name}" is not available on PATH.`,
82
+ evidence: { profile: profile.name, bin: profile.bin },
83
+ };
84
+ }
85
+ const version = spawnSync(profile.bin, ["--version"], { encoding: "utf8", timeout: 5_000 });
86
+ if ((version.status ?? 1) !== 0) {
87
+ return {
88
+ name: "agent-profile",
89
+ kind: "deterministic",
90
+ status: "warn",
91
+ confidence: "medium",
92
+ message: `Agent binary "${profile.bin}" was found but \`--version\` failed.`,
93
+ evidence: {
94
+ profile: profile.name,
95
+ bin: profile.bin,
96
+ exitCode: version.status ?? null,
97
+ stderr: (version.stderr ?? "").trim(),
98
+ },
99
+ };
100
+ }
101
+ return {
102
+ name: "agent-profile",
103
+ kind: "deterministic",
104
+ status: "pass",
105
+ confidence: "high",
106
+ message: `Agent profile "${profile.name}" is available.`,
107
+ evidence: { profile: profile.name, bin: profile.bin, version: (version.stdout ?? "").trim() },
108
+ };
109
+ }
110
+ /**
111
+ * The ordered health-check registry. ORDER IS LOAD-BEARING: `akmHealth`
112
+ * iterates this array and appends to hardChecks/advisories in sequence, so the
113
+ * declaration order below is exactly the emission order (hard checks first in
114
+ * their array, advisories in theirs). Each `run` is a verbatim copy of the
115
+ * corresponding former inline block.
116
+ */
117
+ export const HEALTH_CHECKS = [
118
+ {
119
+ name: "state-db-schema",
120
+ channel: "hard",
121
+ run: (ctx) => ({
122
+ name: "state-db-schema",
123
+ kind: "deterministic",
124
+ status: ctx.missingTables.length === 0 ? "pass" : "fail",
125
+ confidence: "high",
126
+ message: ctx.missingTables.length === 0
127
+ ? "state.db opened and required tables are present."
128
+ : `state.db is missing required tables: ${ctx.missingTables.join(", ")}`,
129
+ evidence: { path: ctx.stateDbPath, tables: ctx.tableNames },
130
+ }),
131
+ },
132
+ {
133
+ name: "state-db-round-trip",
134
+ channel: "hard",
135
+ run: (ctx) => ({
136
+ name: "state-db-round-trip",
137
+ kind: "deterministic",
138
+ status: ctx.probe.ok ? "pass" : "fail",
139
+ confidence: "high",
140
+ message: ctx.probe.ok
141
+ ? "state.db append/read round-trip succeeded."
142
+ : `state.db round-trip failed: ${ctx.probe.error}`,
143
+ evidence: { path: ctx.stateDbPath, durationMs: ctx.probe.durationMs },
144
+ }),
145
+ },
146
+ {
147
+ name: "task-history-read",
148
+ channel: "hard",
149
+ run: (ctx) => ({
150
+ name: "task-history-read",
151
+ kind: "deterministic",
152
+ status: "pass",
153
+ confidence: "high",
154
+ message: `Read ${ctx.taskRowCount} task-history row(s) since ${ctx.since}.`,
155
+ evidence: { rows: ctx.taskRowCount, since: ctx.since },
156
+ }),
157
+ },
158
+ {
159
+ name: "task-log-backing",
160
+ channel: "hard",
161
+ run: (ctx) => ({
162
+ name: "task-log-backing",
163
+ kind: "deterministic",
164
+ status: ctx.logBackingRate === 1 ? "pass" : "fail",
165
+ confidence: "high",
166
+ message: ctx.logBackingRate === 1
167
+ ? "Every task_history log_path resolved on disk."
168
+ : `${ctx.taskRowsWithLogsCount - ctx.existingLogRowsCount} task log(s) referenced in task_history are missing.`,
169
+ evidence: { totalWithLogs: ctx.taskRowsWithLogsCount, existingLogs: ctx.existingLogRowsCount },
170
+ }),
171
+ },
172
+ {
173
+ name: "active-runs",
174
+ channel: "hard",
175
+ run: (ctx) => ({
176
+ name: "active-runs",
177
+ kind: "deterministic",
178
+ status: ctx.stuckActiveRuns === 0 ? "pass" : "warn",
179
+ confidence: "high",
180
+ message: ctx.stuckActiveRuns === 0
181
+ ? "No active task runs exceeded the stale threshold."
182
+ : `${ctx.stuckActiveRuns} active task run(s) are older than ${Math.round(ACTIVE_RUN_WARN_MS / 60000)} minutes.`,
183
+ evidence: { stuckActiveRuns: ctx.stuckActiveRuns },
184
+ }),
185
+ },
186
+ {
187
+ name: "agent-profile",
188
+ channel: "hard",
189
+ run: () => runAgentProbe(),
190
+ },
191
+ {
192
+ name: "semantic-search-runtime",
193
+ channel: "advisory",
194
+ run: (ctx) => ({
195
+ name: "semantic-search-runtime",
196
+ kind: "deterministic",
197
+ status: !ctx.semanticStatus ||
198
+ ctx.semanticStatus.status === "pending" ||
199
+ ctx.semanticStatus.status === "ready-js" ||
200
+ ctx.semanticStatus.status === "ready-vec"
201
+ ? "pass"
202
+ : "warn",
203
+ confidence: "medium",
204
+ message: ctx.semanticStatus
205
+ ? `Semantic search status: ${ctx.semanticStatus.status}`
206
+ : "No semantic-search runtime status recorded yet.",
207
+ evidence: ctx.semanticStatus ? { ...ctx.semanticStatus } : undefined,
208
+ }),
209
+ },
210
+ {
211
+ // session-log-failures: demoted to informational — the ERROR_PATTERNS regex
212
+ // scans pre-LLM session text and produces false positives on diagnostic
213
+ // conversation. It does not gate the real extraction pipeline (akmExtract).
214
+ // Never triggers warn; kept for backward-compat visibility only.
215
+ name: "session-log-failures",
216
+ channel: "advisory",
217
+ run: (ctx) => ({
218
+ name: "session-log-failures",
219
+ kind: "heuristic",
220
+ status: "pass",
221
+ confidence: "low",
222
+ message: ctx.sessionLogEntries.length === 0
223
+ ? "No repeated external session-log failure patterns were detected."
224
+ : `${ctx.sessionLogEntries.length} raw session-log keyword match(es) detected (pre-LLM, informational only).`,
225
+ evidence: { candidates: ctx.sessionLogEntries.slice(0, 5) },
226
+ }),
227
+ },
228
+ {
229
+ name: "session-extraction",
230
+ channel: "advisory",
231
+ run: (ctx) => {
232
+ const sx = ctx.sessionExtraction;
233
+ const sxWarnReasons = [];
234
+ if (sx.warnings > 0)
235
+ sxWarnReasons.push(`${sx.warnings} harness error(s)`);
236
+ if (sx.ran && sx.sessionsScanned >= 5 && sx.proposalsCreated === 0)
237
+ sxWarnReasons.push("no proposals generated across scanned sessions");
238
+ return {
239
+ name: "session-extraction",
240
+ kind: "heuristic",
241
+ status: sxWarnReasons.length > 0 ? "warn" : "pass",
242
+ confidence: sx.ran ? "medium" : "low",
243
+ message: sx.ran
244
+ ? sxWarnReasons.length > 0
245
+ ? `Session extraction degraded: ${sxWarnReasons.join("; ")}.`
246
+ : `Session extraction healthy: ${sx.sessionsScanned} scanned, ${sx.sessionsExtracted} extracted, ${sx.proposalsCreated} proposal(s) created.`
247
+ : "Session extraction not active (feature disabled or no harness available).",
248
+ evidence: {
249
+ ran: sx.ran,
250
+ sessionsScanned: sx.sessionsScanned,
251
+ sessionsExtracted: sx.sessionsExtracted,
252
+ sessionsSkipped: sx.sessionsSkipped,
253
+ proposalsCreated: sx.proposalsCreated,
254
+ warnings: sx.warnings,
255
+ durationMs: sx.durationMs,
256
+ },
257
+ };
258
+ },
259
+ },
260
+ {
261
+ name: "auto-accept-validation",
262
+ channel: "advisory",
263
+ run: (ctx) => {
264
+ const aa = ctx.autoAccept;
265
+ return {
266
+ name: "auto-accept-validation",
267
+ kind: "heuristic",
268
+ status: aa.validationFailed > 0 ? "warn" : "pass",
269
+ confidence: aa.promoted + aa.validationFailed > 0 ? "high" : "low",
270
+ message: aa.validationFailed > 0
271
+ ? `${aa.validationFailed} proposal(s) passed confidence threshold but failed auto-accept validation (truncated description, invalid frontmatter, etc.) — they remain in the queue for manual review.`
272
+ : aa.promoted > 0
273
+ ? `Auto-accept healthy: ${aa.promoted} proposal(s) promoted, 0 validation failures.`
274
+ : "Auto-accept gate did not run (disabled or no proposals above threshold).",
275
+ evidence: { promoted: aa.promoted, validationFailed: aa.validationFailed },
276
+ };
277
+ },
278
+ },
279
+ ];