akm-cli 0.9.0-beta.9 → 0.9.0-rc.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (381) hide show
  1. package/CHANGELOG.md +715 -0
  2. package/README.md +12 -4
  3. package/dist/akm +38 -0
  4. package/dist/akm-migrate-storage +38 -0
  5. package/dist/assets/help/help-improve.md +9 -6
  6. package/dist/assets/hints/cli-hints-full.md +6 -5
  7. package/dist/assets/profiles/default.json +9 -4
  8. package/dist/assets/profiles/frequent.json +1 -1
  9. package/dist/assets/profiles/memory-focus.json +1 -1
  10. package/dist/assets/profiles/proactive-maintenance.json +25 -0
  11. package/dist/assets/profiles/quick.json +1 -1
  12. package/dist/assets/profiles/recombine-only.json +21 -0
  13. package/dist/assets/profiles/reflect-distill.json +30 -0
  14. package/dist/assets/profiles/synthesize.json +15 -0
  15. package/dist/assets/profiles/thorough.json +1 -1
  16. package/dist/assets/prompts/consolidate-system.md +23 -0
  17. package/dist/assets/prompts/contradiction-judge.md +33 -0
  18. package/dist/assets/prompts/distill-knowledge-system.md +22 -0
  19. package/dist/assets/prompts/distill-lesson-system.md +36 -0
  20. package/dist/assets/prompts/extract-session.md +11 -3
  21. package/dist/assets/prompts/graph-extract-system.md +1 -0
  22. package/dist/assets/prompts/graph-extract-user-prompt.md +1 -1
  23. package/dist/assets/prompts/memory-infer-system.md +1 -0
  24. package/dist/assets/prompts/memory-infer-user.md +5 -0
  25. package/dist/assets/prompts/metadata-enhance-system.md +1 -0
  26. package/dist/assets/prompts/procedural-system.md +44 -0
  27. package/dist/assets/prompts/recombine-system.md +40 -0
  28. package/dist/assets/prompts/staleness-detect-system.md +6 -0
  29. package/dist/assets/prompts/validate-summary-judge.md +1 -0
  30. package/dist/assets/prompts/workflow-unit-preamble.md +26 -0
  31. package/dist/assets/stash-skeleton/facts/conventions/assets/agent.md +38 -0
  32. package/dist/assets/stash-skeleton/facts/conventions/assets/command.md +38 -0
  33. package/dist/assets/stash-skeleton/facts/conventions/assets/fact.md +39 -0
  34. package/dist/assets/stash-skeleton/facts/conventions/assets/knowledge.md +40 -0
  35. package/dist/assets/stash-skeleton/facts/conventions/assets/lesson.md +43 -0
  36. package/dist/assets/stash-skeleton/facts/conventions/assets/memory.md +38 -0
  37. package/dist/assets/stash-skeleton/facts/conventions/assets/script.md +43 -0
  38. package/dist/assets/stash-skeleton/facts/conventions/assets/skill.md +40 -0
  39. package/dist/assets/stash-skeleton/facts/conventions/assets/workflow.md +43 -0
  40. package/dist/assets/templates/html/health.html +281 -111
  41. package/dist/assets/wiki/ingest-workflow-template.md +45 -16
  42. package/dist/assets/wiki/schema-template.md +4 -4
  43. package/dist/cli/clack.js +56 -0
  44. package/dist/cli/config-migrate.js +7 -1
  45. package/dist/cli/confirm.js +1 -1
  46. package/dist/cli/parse-args.js +46 -1
  47. package/dist/cli/shared.js +28 -0
  48. package/dist/cli.js +25 -21
  49. package/dist/commands/agent/agent-dispatch.js +3 -2
  50. package/dist/commands/agent/agent-support.js +0 -7
  51. package/dist/commands/agent/contribute-cli.js +26 -7
  52. package/dist/commands/config-cli.js +26 -13
  53. package/dist/commands/env/child-env.js +47 -0
  54. package/dist/commands/env/env-binding.js +95 -0
  55. package/dist/commands/env/env-cli.js +228 -292
  56. package/dist/commands/env/env.js +14 -67
  57. package/dist/commands/env/secret-cli.js +140 -138
  58. package/dist/commands/feedback-cli.js +156 -155
  59. package/dist/commands/graph/graph-cli.js +5 -13
  60. package/dist/commands/graph/graph.js +3 -3
  61. package/dist/commands/health/advisories.js +151 -0
  62. package/dist/commands/health/checks.js +103 -16
  63. package/dist/commands/health/html-report.js +447 -81
  64. package/dist/commands/health/improve-metrics.js +771 -0
  65. package/dist/commands/health/llm-usage.js +65 -0
  66. package/dist/commands/health/md-report.js +103 -0
  67. package/dist/commands/health/metrics.js +278 -0
  68. package/dist/commands/health/stash-exposure.js +46 -0
  69. package/dist/commands/health/surfaces.js +216 -0
  70. package/dist/commands/health/task-runs.js +135 -0
  71. package/dist/commands/health/types.js +26 -0
  72. package/dist/commands/health/windows.js +195 -0
  73. package/dist/commands/health.js +91 -1091
  74. package/dist/commands/improve/anti-collapse.js +170 -0
  75. package/dist/commands/improve/calibration.js +161 -0
  76. package/dist/commands/improve/collapse-detector.js +421 -0
  77. package/dist/commands/improve/consolidate/chunking.js +141 -0
  78. package/dist/commands/improve/consolidate/eligibility.js +64 -0
  79. package/dist/commands/improve/consolidate/merge.js +145 -0
  80. package/dist/commands/improve/consolidate/sanitize.js +231 -0
  81. package/dist/commands/{lint.js → improve/consolidate/types.js} +1 -1
  82. package/dist/commands/improve/consolidate.js +1295 -1277
  83. package/dist/commands/improve/dedup.js +482 -0
  84. package/dist/commands/improve/distill/content-repair.js +202 -0
  85. package/dist/commands/improve/distill/promote-memory.js +229 -0
  86. package/dist/commands/improve/distill/quality-gate.js +236 -0
  87. package/dist/commands/improve/distill-guards.js +127 -0
  88. package/dist/commands/improve/distill-promotion-policy.js +826 -167
  89. package/dist/commands/improve/distill.js +228 -605
  90. package/dist/commands/improve/eligibility.js +434 -0
  91. package/dist/commands/improve/encoding-salience.js +205 -0
  92. package/dist/commands/improve/extract-cli.js +179 -59
  93. package/dist/commands/improve/extract-prompt.js +54 -3
  94. package/dist/commands/improve/extract-watch.js +140 -0
  95. package/dist/commands/improve/extract.js +409 -43
  96. package/dist/commands/improve/feedback-valence.js +54 -0
  97. package/dist/commands/improve/hot-probation.js +45 -0
  98. package/dist/commands/improve/improve-auto-accept.js +157 -10
  99. package/dist/commands/improve/improve-cli.js +115 -73
  100. package/dist/commands/improve/improve-profiles.js +28 -8
  101. package/dist/commands/improve/improve-result-file.js +15 -25
  102. package/dist/commands/improve/improve-session.js +58 -0
  103. package/dist/commands/improve/improve.js +485 -2764
  104. package/dist/commands/improve/locks.js +154 -0
  105. package/dist/commands/improve/loop-stages.js +1100 -0
  106. package/dist/commands/improve/memory/memory-belief.js +14 -15
  107. package/dist/commands/improve/memory/memory-contradiction-detect.js +83 -60
  108. package/dist/commands/improve/memory/memory-improve.js +27 -27
  109. package/dist/commands/improve/outcome-loop.js +270 -0
  110. package/dist/commands/improve/preparation.js +2002 -0
  111. package/dist/commands/improve/proactive-maintenance.js +37 -35
  112. package/dist/commands/improve/procedural.js +398 -0
  113. package/dist/commands/improve/recombine.js +818 -0
  114. package/dist/commands/improve/reflect-noise.js +0 -0
  115. package/dist/commands/improve/reflect.js +206 -45
  116. package/dist/commands/improve/salience.js +455 -0
  117. package/dist/commands/improve/schema-similarity-gate.js +168 -0
  118. package/dist/commands/improve/shared.js +51 -0
  119. package/dist/commands/improve/triage.js +93 -0
  120. package/dist/commands/lint/agent-linter.js +19 -24
  121. package/dist/commands/lint/base-linter.js +173 -60
  122. package/dist/commands/lint/command-linter.js +19 -24
  123. package/dist/commands/lint/env-key-rules.js +38 -1
  124. package/dist/commands/lint/fact-linter.js +39 -0
  125. package/dist/commands/lint/index.js +31 -13
  126. package/dist/commands/lint/memory-linter.js +1 -1
  127. package/dist/commands/lint/registry.js +7 -2
  128. package/dist/commands/lint/task-linter.js +3 -3
  129. package/dist/commands/lint/workflow-linter.js +26 -1
  130. package/dist/commands/observability-cli.js +4 -4
  131. package/dist/commands/proposal/drain-policies.js +13 -4
  132. package/dist/commands/proposal/drain.js +45 -51
  133. package/dist/commands/proposal/legacy-import.js +115 -0
  134. package/dist/commands/proposal/proposal-cli.js +24 -34
  135. package/dist/commands/proposal/proposal.js +2 -1
  136. package/dist/commands/proposal/propose.js +8 -3
  137. package/dist/commands/proposal/repository.js +829 -0
  138. package/dist/commands/proposal/validators/proposal-quality-validators.js +9 -8
  139. package/dist/commands/proposal/validators/proposals.js +93 -895
  140. package/dist/commands/read/curate.js +410 -111
  141. package/dist/commands/read/knowledge.js +10 -3
  142. package/dist/commands/read/remember-cli.js +133 -138
  143. package/dist/commands/read/search-cli.js +15 -8
  144. package/dist/commands/read/search.js +22 -11
  145. package/dist/commands/read/show.js +106 -14
  146. package/dist/commands/registry-cli.js +76 -87
  147. package/dist/commands/remember.js +11 -12
  148. package/dist/commands/sources/add-cli.js +91 -95
  149. package/dist/commands/sources/history.js +1 -1
  150. package/dist/commands/sources/init.js +66 -18
  151. package/dist/commands/sources/installed-stashes.js +11 -3
  152. package/dist/commands/sources/migration-help.js +7 -4
  153. package/dist/commands/sources/schema-repair.js +44 -46
  154. package/dist/commands/sources/self-update.js +2 -2
  155. package/dist/commands/sources/source-add.js +7 -3
  156. package/dist/commands/sources/sources-cli.js +3 -3
  157. package/dist/commands/sources/stash-cli.js +19 -39
  158. package/dist/commands/sources/stash-skeleton.js +57 -8
  159. package/dist/commands/tasks/default-tasks.js +15 -2
  160. package/dist/commands/tasks/tasks-cli.js +20 -29
  161. package/dist/commands/tasks/tasks.js +39 -11
  162. package/dist/commands/wiki-cli.js +23 -38
  163. package/dist/commands/workflow-cli.js +291 -13
  164. package/dist/core/asset/asset-registry.js +3 -1
  165. package/dist/core/asset/asset-spec.js +79 -5
  166. package/dist/core/asset/frontmatter.js +188 -167
  167. package/dist/core/asset/markdown.js +8 -0
  168. package/dist/core/authoring-rules.js +92 -0
  169. package/dist/core/common.js +4 -23
  170. package/dist/core/concurrent.js +10 -1
  171. package/dist/core/config/config-io.js +10 -1
  172. package/dist/core/config/config-migration.js +18 -40
  173. package/dist/core/config/config-schema.js +403 -62
  174. package/dist/core/config/config-types.js +3 -3
  175. package/dist/core/config/config.js +67 -22
  176. package/dist/core/deep-merge.js +38 -0
  177. package/dist/core/errors.js +1 -0
  178. package/dist/core/eval/rank-metrics.js +113 -0
  179. package/dist/core/events.js +4 -7
  180. package/dist/core/improve-types.js +47 -8
  181. package/dist/core/json-schema.js +142 -0
  182. package/dist/core/logs-db.js +14 -75
  183. package/dist/core/parse.js +36 -16
  184. package/dist/core/paths.js +18 -18
  185. package/dist/core/standards/resolve-standards-context.js +87 -0
  186. package/dist/core/standards/resolve-stash-standards.js +99 -0
  187. package/dist/core/standards/resolve-type-conventions.js +66 -0
  188. package/dist/core/state/migrations.js +770 -0
  189. package/dist/core/state-db.js +132 -1126
  190. package/dist/core/structured.js +69 -0
  191. package/dist/core/time.js +53 -0
  192. package/dist/core/warn.js +21 -0
  193. package/dist/core/write-source.js +37 -0
  194. package/dist/indexer/db/db.js +261 -770
  195. package/dist/indexer/db/entry-mapper.js +41 -0
  196. package/dist/indexer/db/graph-db.js +129 -86
  197. package/dist/indexer/db/llm-cache.js +2 -2
  198. package/dist/indexer/db/schema.js +516 -0
  199. package/dist/indexer/ensure-index.js +36 -92
  200. package/dist/indexer/feedback/utility-policy.js +75 -0
  201. package/dist/indexer/graph/graph-boost.js +51 -41
  202. package/dist/indexer/graph/graph-extraction.js +207 -4
  203. package/dist/indexer/index-writer-lock.js +18 -11
  204. package/dist/indexer/index-written-assets.js +105 -0
  205. package/dist/indexer/indexer.js +182 -204
  206. package/dist/indexer/passes/dir-staleness.js +114 -0
  207. package/dist/indexer/passes/memory-inference.js +13 -5
  208. package/dist/indexer/passes/metadata.js +20 -0
  209. package/dist/indexer/read-preflight.js +23 -0
  210. package/dist/indexer/search/db-search.js +89 -13
  211. package/dist/indexer/search/fts-query.js +51 -0
  212. package/dist/indexer/search/ranking-contributors.js +95 -9
  213. package/dist/indexer/search/ranking.js +79 -3
  214. package/dist/indexer/search/search-fields.js +6 -0
  215. package/dist/indexer/search/search-source.js +32 -21
  216. package/dist/indexer/search/semantic-status.js +4 -0
  217. package/dist/indexer/walk/matchers.js +48 -0
  218. package/dist/indexer/walk/walker.js +21 -13
  219. package/dist/integrations/agent/builders.js +41 -13
  220. package/dist/integrations/agent/config.js +20 -59
  221. package/dist/integrations/agent/detect.js +9 -0
  222. package/dist/integrations/agent/index.js +3 -19
  223. package/dist/integrations/agent/model-aliases.js +16 -2
  224. package/dist/integrations/agent/profiles.js +79 -6
  225. package/dist/integrations/agent/prompts.js +75 -9
  226. package/dist/integrations/agent/runner-dispatch.js +83 -0
  227. package/dist/integrations/agent/runner.js +13 -9
  228. package/dist/integrations/agent/spawn.js +206 -81
  229. package/dist/integrations/harnesses/aider/agent-builder.js +113 -0
  230. package/dist/integrations/harnesses/aider/index.js +58 -0
  231. package/dist/integrations/harnesses/aider/result-extractor.js +53 -0
  232. package/dist/integrations/harnesses/amazonq/agent-builder.js +153 -0
  233. package/dist/integrations/harnesses/amazonq/index.js +59 -0
  234. package/dist/integrations/harnesses/amazonq/result-extractor.js +48 -0
  235. package/dist/integrations/harnesses/claude/agent-builder.js +46 -7
  236. package/dist/integrations/harnesses/claude/index.js +27 -23
  237. package/dist/integrations/harnesses/claude/result-extractor.js +52 -0
  238. package/dist/integrations/harnesses/claude/session-log.js +10 -0
  239. package/dist/integrations/harnesses/codex/agent-builder.js +137 -0
  240. package/dist/integrations/harnesses/codex/index.js +63 -0
  241. package/dist/integrations/harnesses/codex/result-extractor.js +73 -0
  242. package/dist/integrations/harnesses/copilot/agent-builder.js +122 -0
  243. package/dist/integrations/harnesses/copilot/index.js +60 -0
  244. package/dist/integrations/harnesses/copilot/result-extractor.js +151 -0
  245. package/dist/integrations/harnesses/gemini/agent-builder.js +121 -0
  246. package/dist/integrations/harnesses/gemini/index.js +60 -0
  247. package/dist/integrations/harnesses/gemini/result-extractor.js +121 -0
  248. package/dist/integrations/harnesses/index.js +28 -7
  249. package/dist/integrations/harnesses/opencode/agent-builder.js +1 -1
  250. package/dist/integrations/harnesses/opencode/index.js +17 -16
  251. package/dist/integrations/harnesses/opencode/session-log.js +173 -3
  252. package/dist/integrations/harnesses/opencode-sdk/harness.js +65 -0
  253. package/dist/integrations/harnesses/opencode-sdk/index.js +10 -34
  254. package/dist/integrations/harnesses/opencode-sdk/sdk-runner.js +642 -71
  255. package/dist/integrations/harnesses/openhands/agent-builder.js +126 -0
  256. package/dist/integrations/harnesses/openhands/index.js +58 -0
  257. package/dist/integrations/harnesses/openhands/result-extractor.js +103 -0
  258. package/dist/integrations/harnesses/pi/agent-builder.js +104 -0
  259. package/dist/integrations/harnesses/pi/index.js +58 -0
  260. package/dist/integrations/harnesses/pi/result-extractor.js +135 -0
  261. package/dist/integrations/harnesses/types.js +8 -0
  262. package/dist/integrations/session-logs/index.js +40 -11
  263. package/dist/llm/call-ai.js +2 -2
  264. package/dist/llm/client.js +34 -11
  265. package/dist/llm/embedder.js +67 -4
  266. package/dist/llm/embedders/cache.js +3 -1
  267. package/dist/llm/embedders/deterministic.js +66 -0
  268. package/dist/llm/embedders/local.js +73 -3
  269. package/dist/llm/feature-gate.js +16 -15
  270. package/dist/llm/graph-extract.js +67 -44
  271. package/dist/llm/memory-infer-impl.js +138 -0
  272. package/dist/llm/memory-infer.js +1 -127
  273. package/dist/llm/metadata-enhance.js +44 -31
  274. package/dist/llm/structured-call.js +49 -0
  275. package/dist/migrate-storage-node.mjs +8 -0
  276. package/dist/output/context.js +5 -5
  277. package/dist/output/renderers.js +87 -15
  278. package/dist/output/shapes/curate.js +14 -2
  279. package/dist/output/shapes/helpers.js +0 -3
  280. package/dist/output/shapes/passthrough.js +6 -1
  281. package/dist/output/text/helpers.js +241 -2
  282. package/dist/output/text/workflow.js +4 -1
  283. package/dist/registry/providers/skills-sh.js +21 -147
  284. package/dist/registry/providers/static-index.js +15 -157
  285. package/dist/registry/resolve.js +27 -9
  286. package/dist/runtime.js +25 -1
  287. package/dist/schemas/akm-config.json +14225 -0
  288. package/dist/schemas/akm-workflow.json +328 -0
  289. package/dist/scripts/migrate-storage.js +2743 -8390
  290. package/dist/scripts/migrations/import-fs-improve-runs-to-db.js +1652 -607
  291. package/dist/setup/detect.js +9 -0
  292. package/dist/setup/legacy-config.js +106 -0
  293. package/dist/setup/prompt.js +57 -0
  294. package/dist/setup/providers.js +14 -0
  295. package/dist/setup/registry-stash-loader.js +12 -0
  296. package/dist/setup/semantic-assets.js +124 -0
  297. package/dist/setup/setup.js +52 -1614
  298. package/dist/setup/steps/connection.js +734 -0
  299. package/dist/setup/steps/output.js +31 -0
  300. package/dist/setup/steps/platforms.js +124 -0
  301. package/dist/setup/steps/semantic.js +27 -0
  302. package/dist/setup/steps/sources.js +222 -0
  303. package/dist/setup/steps/stashdir.js +42 -0
  304. package/dist/setup/steps/tasks.js +152 -0
  305. package/dist/sources/include.js +6 -2
  306. package/dist/sources/providers/filesystem.js +0 -1
  307. package/dist/sources/providers/git-install.js +210 -0
  308. package/dist/sources/providers/git-provider.js +234 -0
  309. package/dist/sources/providers/git-stash.js +248 -0
  310. package/dist/sources/providers/git.js +10 -661
  311. package/dist/sources/providers/npm.js +2 -6
  312. package/dist/sources/providers/provider-utils.js +13 -7
  313. package/dist/sources/providers/sync-from-ref.js +9 -1
  314. package/dist/sources/providers/website.js +9 -5
  315. package/dist/sources/website-ingest.js +187 -29
  316. package/dist/sources/wiki-fetchers/registry.js +53 -0
  317. package/dist/sources/wiki-fetchers/youtube.js +239 -0
  318. package/dist/storage/database.js +45 -10
  319. package/dist/storage/managed-db.js +82 -0
  320. package/dist/storage/repositories/canaries-repository.js +107 -0
  321. package/dist/storage/repositories/consolidation-repository.js +38 -0
  322. package/dist/storage/repositories/embeddings-repository.js +72 -0
  323. package/dist/storage/repositories/events-repository.js +187 -0
  324. package/dist/storage/repositories/extract-sessions-repository.js +96 -0
  325. package/dist/storage/repositories/improve-runs-repository.js +146 -0
  326. package/dist/storage/repositories/index-db.js +14 -8
  327. package/dist/storage/repositories/proposals-repository.js +220 -0
  328. package/dist/storage/repositories/recombine-repository.js +213 -0
  329. package/dist/storage/repositories/registry-cache.js +93 -0
  330. package/dist/storage/repositories/registry-index-cache-repository.js +46 -0
  331. package/dist/storage/repositories/task-history-repository.js +93 -0
  332. package/dist/storage/repositories/workflow-runs-repository.js +189 -1
  333. package/dist/storage/sqlite-pragmas.js +146 -0
  334. package/dist/tasks/backends/cron.js +1 -1
  335. package/dist/tasks/backends/index.js +9 -0
  336. package/dist/tasks/backends/launchd.js +1 -1
  337. package/dist/tasks/backends/schtasks.js +1 -1
  338. package/dist/tasks/{resolveAkmBin.js → resolve-akm-bin.js} +2 -2
  339. package/dist/tasks/runner.js +15 -13
  340. package/dist/text-import-hook.mjs +1 -1
  341. package/dist/wiki/wiki.js +52 -11
  342. package/dist/workflows/authoring/authoring.js +123 -10
  343. package/dist/workflows/authoring/workflow-program-template.yaml +31 -0
  344. package/dist/workflows/cli.js +5 -0
  345. package/dist/workflows/db.js +138 -4
  346. package/dist/workflows/exec/brief.js +484 -0
  347. package/dist/workflows/exec/native-executor.js +975 -0
  348. package/dist/workflows/exec/param-secrets.js +115 -0
  349. package/dist/workflows/exec/report.js +1295 -0
  350. package/dist/workflows/exec/run-workflow.js +596 -0
  351. package/dist/workflows/exec/scheduler.js +100 -0
  352. package/dist/workflows/exec/step-work.js +1156 -0
  353. package/dist/workflows/exec/unit-writer.js +23 -0
  354. package/dist/workflows/exec/watch.js +116 -0
  355. package/dist/workflows/exec/worktree.js +171 -0
  356. package/dist/workflows/ir/compile.js +388 -0
  357. package/dist/workflows/ir/params.js +54 -0
  358. package/dist/workflows/ir/plan-hash.js +33 -0
  359. package/dist/workflows/ir/schema.js +4 -0
  360. package/dist/workflows/parser.js +3 -1
  361. package/dist/workflows/program/expressions.js +369 -0
  362. package/dist/workflows/program/parser.js +760 -0
  363. package/dist/workflows/program/project.js +105 -0
  364. package/dist/workflows/program/schema.js +54 -0
  365. package/dist/workflows/renderer.js +82 -5
  366. package/dist/workflows/runtime/agent-identity.js +59 -14
  367. package/dist/workflows/runtime/runs.js +248 -153
  368. package/dist/workflows/runtime/unit-checkin.js +45 -0
  369. package/dist/workflows/runtime/workflow-asset-loader.js +188 -0
  370. package/dist/workflows/validate-summary.js +26 -10
  371. package/dist/workflows/validator.js +1 -1
  372. package/docs/README.md +69 -18
  373. package/docs/data-and-telemetry.md +7 -5
  374. package/docs/migration/release-notes/0.7.0.md +1 -1
  375. package/docs/migration/release-notes/0.9.0-beta.60.md +19 -0
  376. package/docs/migration/release-notes/0.9.0.md +39 -0
  377. package/package.json +10 -10
  378. package/dist/assets/tasks/core/update-stashes.yml +0 -4
  379. package/dist/commands/db-cli.js +0 -23
  380. package/dist/indexer/db/db-backup.js +0 -376
  381. package/dist/indexer/passes/staleness-detect.js +0 -488
@@ -14,12 +14,15 @@
14
14
  * Output is the AkmExtractResult JSON envelope (or an aggregated one when
15
15
  * `--auto` runs multiple harnesses).
16
16
  */
17
- import { defineCommand } from "citty";
18
- import { output, runWithJsonErrors } from "../../cli/shared.js";
17
+ import fs from "node:fs";
18
+ import path from "node:path";
19
+ import { getStringArg } from "../../cli/parse-args.js";
20
+ import { defineJsonCommand, EXIT_CODES, output } from "../../cli/shared.js";
19
21
  import { UsageError } from "../../core/errors.js";
20
- import { getAvailableHarnesses } from "../../integrations/session-logs/index.js";
22
+ import { getAvailableHarnesses, getWatchTargets } from "../../integrations/session-logs/index.js";
21
23
  import { akmExtract } from "./extract.js";
22
- export const extractCommand = defineCommand({
24
+ import { akmExtractWatch } from "./extract-watch.js";
25
+ export const extractCommand = defineJsonCommand({
23
26
  meta: {
24
27
  name: "extract",
25
28
  description: "Extract durable insights from native session files (claude-code, opencode) and queue them as proposals. Replaces the legacy session-checkpoint hook.",
@@ -60,68 +63,185 @@ export const extractCommand = defineCommand({
60
63
  type: "string",
61
64
  description: "Per-session LLM timeout in ms (default 60000).",
62
65
  },
66
+ watch: {
67
+ type: "boolean",
68
+ description: "Watch harness session-log directories and run extract on change (debounced). Stays alive until SIGINT/SIGTERM. Falls back to the */30 cron when off.",
69
+ default: false,
70
+ },
71
+ "debounce-ms": {
72
+ type: "string",
73
+ description: "Debounce window in ms for --watch (default 2000).",
74
+ },
63
75
  },
64
76
  async run({ args }) {
65
- await runWithJsonErrors(async () => {
66
- const type = typeof args.type === "string" ? args.type.trim() : "";
67
- const sessionId = typeof args["session-id"] === "string" ? args["session-id"].trim() : "";
68
- const location = typeof args.location === "string" ? args.location.trim() : "";
69
- const since = typeof args.since === "string" ? args.since.trim() : "";
70
- const auto = args.auto === true;
71
- const dryRun = args["dry-run"] === true;
72
- const force = args.force === true;
73
- const timeoutMs = typeof args["timeout-ms"] === "string" && args["timeout-ms"] !== ""
74
- ? Number.parseInt(args["timeout-ms"], 10)
75
- : undefined;
76
- if (timeoutMs !== undefined && (!Number.isFinite(timeoutMs) || timeoutMs <= 0)) {
77
- throw new UsageError(`--timeout-ms must be a positive integer (got "${args["timeout-ms"]}").`, "INVALID_FLAG_VALUE");
78
- }
79
- if (auto && type) {
80
- throw new UsageError("--auto and --type are mutually exclusive. Pick one.", "INVALID_FLAG_VALUE");
81
- }
82
- if (!auto && !type) {
83
- throw new UsageError("--type is required (or pass --auto to try every available harness).", "MISSING_REQUIRED_ARGUMENT");
84
- }
85
- const commonOptions = {
86
- ...(sessionId ? { sessionId } : {}),
87
- ...(location ? { location } : {}),
88
- ...(since ? { since } : {}),
89
- dryRun,
90
- force,
91
- ...(timeoutMs !== undefined ? { timeoutMs } : {}),
92
- };
93
- if (auto) {
94
- const harnesses = getAvailableHarnesses();
95
- if (harnesses.length === 0) {
96
- output("extract", {
97
- schemaVersion: 1,
98
- ok: false,
99
- shape: "extract-auto-result",
100
- warnings: ["no available harnesses found on this machine"],
101
- results: [],
102
- });
103
- return;
104
- }
105
- const results = [];
106
- for (const h of harnesses) {
107
- const result = await akmExtract({ type: h.name, ...commonOptions });
108
- results.push(result);
109
- }
110
- const ok = results.every((r) => r.ok);
111
- const totalProposals = results.reduce((sum, r) => sum + r.proposals.length, 0);
77
+ const type = getStringArg(args, "type") ?? "";
78
+ const sessionId = getStringArg(args, "session-id") ?? "";
79
+ const location = getStringArg(args, "location") ?? "";
80
+ const since = getStringArg(args, "since") ?? "";
81
+ const auto = args.auto === true;
82
+ const dryRun = args["dry-run"] === true;
83
+ const force = args.force === true;
84
+ const timeoutMs = typeof args["timeout-ms"] === "string" && args["timeout-ms"] !== ""
85
+ ? Number.parseInt(args["timeout-ms"], 10)
86
+ : undefined;
87
+ if (timeoutMs !== undefined && (!Number.isFinite(timeoutMs) || timeoutMs <= 0)) {
88
+ throw new UsageError(`--timeout-ms must be a positive integer (got "${args["timeout-ms"]}").`, "INVALID_FLAG_VALUE");
89
+ }
90
+ const watch = args.watch === true;
91
+ const debounceMs = typeof args["debounce-ms"] === "string" && args["debounce-ms"] !== ""
92
+ ? Number.parseInt(args["debounce-ms"], 10)
93
+ : 2000;
94
+ if (watch && (!Number.isFinite(debounceMs) || debounceMs <= 0)) {
95
+ throw new UsageError(`--debounce-ms must be a positive integer (got "${args["debounce-ms"]}").`, "INVALID_FLAG_VALUE");
96
+ }
97
+ if (watch) {
98
+ await runWatchMode({ debounceMs, dryRun, force, ...(since ? { since } : {}) });
99
+ return;
100
+ }
101
+ if (auto && type) {
102
+ throw new UsageError("--auto and --type are mutually exclusive. Pick one.", "INVALID_FLAG_VALUE");
103
+ }
104
+ if (!auto && !type) {
105
+ throw new UsageError("--type is required (or pass --auto to try every available harness).", "MISSING_REQUIRED_ARGUMENT");
106
+ }
107
+ const commonOptions = {
108
+ ...(sessionId ? { sessionId } : {}),
109
+ ...(location ? { location } : {}),
110
+ ...(since ? { since } : {}),
111
+ dryRun,
112
+ force,
113
+ ...(timeoutMs !== undefined ? { timeoutMs } : {}),
114
+ };
115
+ if (auto) {
116
+ const harnesses = getAvailableHarnesses();
117
+ if (harnesses.length === 0) {
112
118
  output("extract", {
113
119
  schemaVersion: 1,
114
- ok,
120
+ ok: false,
115
121
  shape: "extract-auto-result",
116
- dryRun,
117
- harnessesProcessed: results.length,
118
- totalProposals,
119
- results,
122
+ warnings: ["no available harnesses found on this machine"],
123
+ results: [],
120
124
  });
121
125
  return;
122
126
  }
123
- const result = await akmExtract({ type, ...commonOptions });
124
- output("extract", result);
125
- });
127
+ const results = [];
128
+ for (const h of harnesses) {
129
+ const result = await akmExtract({ type: h.name, ...commonOptions });
130
+ results.push(result);
131
+ }
132
+ const ok = results.every((r) => r.ok);
133
+ const totalProposals = results.reduce((sum, r) => sum + r.proposals.length, 0);
134
+ output("extract", {
135
+ schemaVersion: 1,
136
+ ok,
137
+ shape: "extract-auto-result",
138
+ dryRun,
139
+ harnessesProcessed: results.length,
140
+ totalProposals,
141
+ results,
142
+ });
143
+ // Signal failure to callers/cron when every harness failed. output()
144
+ // only renders; without this the */30 cron exits 0 on a total failure
145
+ // and the breakage is invisible to exit-code monitoring. process.exitCode
146
+ // (not process.exit) lets stdout flush and the watcher/timers settle.
147
+ if (!ok)
148
+ process.exitCode = EXIT_CODES.GENERAL;
149
+ return;
150
+ }
151
+ const result = await akmExtract({ type, ...commonOptions });
152
+ output("extract", result);
153
+ if (!result.ok)
154
+ process.exitCode = EXIT_CODES.GENERAL;
126
155
  },
127
156
  });
157
+ /**
158
+ * A thin {@link WatchEventSource} over `fs.watch` for each configured root.
159
+ * This adapter is the ONLY place a real `fs.watch` is created (the core stays
160
+ * injectable + fully unit-tested); it is intentionally not unit-covered.
161
+ *
162
+ * `fs.watch(dir, { recursive: true })` is unreliable for recursive mode on some
163
+ * Node/Bun/Linux combinations — that is acceptable here: missed events degrade
164
+ * to the unchanged scheduled cron fallback, never worse than today.
165
+ */
166
+ function createFsWatchEventSource(roots) {
167
+ return {
168
+ subscribe(listener) {
169
+ const watchers = [];
170
+ for (const root of roots) {
171
+ try {
172
+ const watcher = fs.watch(root, { recursive: true }, (_event, filename) => {
173
+ if (!filename)
174
+ return;
175
+ const filePath = path.isAbsolute(filename.toString())
176
+ ? filename.toString()
177
+ : path.join(root, filename.toString());
178
+ listener({ path: filePath });
179
+ });
180
+ watchers.push(watcher);
181
+ }
182
+ catch {
183
+ // A root that can't be watched is skipped; the cron fallback covers it.
184
+ }
185
+ }
186
+ return () => {
187
+ for (const w of watchers) {
188
+ try {
189
+ w.close();
190
+ }
191
+ catch {
192
+ // best-effort teardown
193
+ }
194
+ }
195
+ };
196
+ },
197
+ };
198
+ }
199
+ /**
200
+ * Run `akm extract --watch`: watch every available harness's session-log
201
+ * roots and run extract (debounced, per-harness) on change. Stays alive until
202
+ * SIGINT/SIGTERM, then stops cleanly. PROCESS-HYGIENE: stop() removes every
203
+ * watcher + pending timer before the process exits.
204
+ */
205
+ async function runWatchMode(opts) {
206
+ const targets = getWatchTargets();
207
+ if (targets.length === 0) {
208
+ output("extract", {
209
+ schemaVersion: 1,
210
+ ok: false,
211
+ shape: "extract-watch-started",
212
+ warnings: ["no watchable harness session-log directories found on this machine"],
213
+ watching: [],
214
+ });
215
+ return;
216
+ }
217
+ const allRoots = targets.flatMap((t) => t.roots);
218
+ const eventSource = createFsWatchEventSource(allRoots);
219
+ const handle = akmExtractWatch({
220
+ roots: targets,
221
+ eventSource,
222
+ debounceMs: opts.debounceMs,
223
+ onTrigger: async (harnessName) => {
224
+ await akmExtract({
225
+ type: harnessName,
226
+ dryRun: opts.dryRun,
227
+ force: opts.force,
228
+ ...(opts.since ? { since: opts.since } : {}),
229
+ });
230
+ },
231
+ });
232
+ output("extract", {
233
+ schemaVersion: 1,
234
+ ok: true,
235
+ shape: "extract-watch-started",
236
+ debounceMs: opts.debounceMs,
237
+ watching: allRoots,
238
+ });
239
+ await new Promise((resolve) => {
240
+ const shutdown = () => {
241
+ handle.stop();
242
+ resolve();
243
+ };
244
+ process.once("SIGINT", shutdown);
245
+ process.once("SIGTERM", shutdown);
246
+ });
247
+ }
@@ -21,12 +21,18 @@ import promptTemplate from "../../assets/prompts/extract-session.md" with { type
21
21
  *
22
22
  * Shape:
23
23
  * {
24
- * "candidates": [{type, name, description, when_to_use?, body, confidence, evidence}, ...],
24
+ * "candidates": [{type, name, description, when_to_use?, body, confidence, evidence,
25
+ * orderedActions?, outcomeData?}, ...],
25
26
  * "rationale_if_empty"?: string
26
27
  * }
27
28
  *
28
29
  * `additionalProperties: false` at each level so any hallucinated keys are
29
30
  * dropped before parsing.
31
+ *
32
+ * `orderedActions` and `outcomeData` are additive fields for #615 procedural
33
+ * compilation (WS-0 data-capture hook). Source transcripts are external logs
34
+ * not guaranteed re-extractable; we capture the ordered-action sequence and
35
+ * outcome now even though the detection/compilation feature is deferred to 0.10+.
30
36
  */
31
37
  export const EXTRACT_JSON_SCHEMA = {
32
38
  type: "object",
@@ -79,6 +85,17 @@ export const EXTRACT_JSON_SCHEMA = {
79
85
  minLength: 5,
80
86
  description: "One-line pointer to the moment in the session that supports this candidate.",
81
87
  },
88
+ orderedActions: {
89
+ type: "array",
90
+ description: "OPTIONAL. Ordered list of discrete actions taken during this episode, as brief imperative phrases (e.g. 'run deploy.sh', 'check VPN status', 'retry with --force'). Capture only when the candidate represents a recurring action sequence that could become a skill or workflow. Omit when the candidate is a standalone fact or observation.",
91
+ items: { type: "string", minLength: 3 },
92
+ maxItems: 20,
93
+ },
94
+ outcomeData: {
95
+ type: "string",
96
+ description: "OPTIONAL. One-sentence description of the outcome when the ordered action sequence completed (e.g. 'deploy succeeded after VPN reconnect', 'build failed with error X'). Required when orderedActions is present; omit otherwise.",
97
+ maxLength: 400,
98
+ },
82
99
  },
83
100
  },
84
101
  },
@@ -106,22 +123,36 @@ function formatAlreadyPreserved(inlineRefs) {
106
123
  })
107
124
  .join("\n");
108
125
  }
126
+ /**
127
+ * Delimiters that fence the untrusted session transcript in the extract prompt.
128
+ * Mirrors the `=== ASSET N ===` convention (`graph-extract.ts`): everything
129
+ * between the markers is DATA to analyze, never instructions to obey. The
130
+ * transcript is external, attacker-influenceable content, so an explicit,
131
+ * greppable boundary defuses prompt-injection that tries to pose as a command.
132
+ */
133
+ export const TRANSCRIPT_FENCE_BEGIN = "=== BEGIN UNTRUSTED SESSION TRANSCRIPT ===";
134
+ export const TRANSCRIPT_FENCE_END = "=== END UNTRUSTED SESSION TRANSCRIPT ===";
109
135
  /**
110
136
  * Format pre-filtered events as a transcript snippet. Each event becomes:
111
137
  * [<role> @ <iso>] <text>
112
138
  * Events are already truncated/cleaned by the pre-filter; this is purely
113
139
  * a render step.
140
+ *
141
+ * Anti-spoof: any occurrence of the fence markers inside the transcript text is
142
+ * neutralised so a crafted session cannot forge the boundary and "escape" the
143
+ * fence to inject trusted-looking instructions.
114
144
  */
115
145
  function formatTranscript(events) {
116
146
  if (events.length === 0)
117
147
  return "(empty — pre-filter removed all events as noise)";
118
- return events
148
+ const body = events
119
149
  .map((e) => {
120
150
  const tsLabel = e.ts ? new Date(e.ts).toISOString() : "unknown-ts";
121
151
  const roleLabel = e.role ?? "unknown";
122
152
  return `[${roleLabel} @ ${tsLabel}] ${e.text}`;
123
153
  })
124
154
  .join("\n\n");
155
+ return body.split(TRANSCRIPT_FENCE_BEGIN).join("=== (fence) ===").split(TRANSCRIPT_FENCE_END).join("=== (fence) ===");
125
156
  }
126
157
  /**
127
158
  * Build the user-prompt body for the extract LLM call by interpolating
@@ -132,6 +163,11 @@ export function buildExtractPrompt(input) {
132
163
  const ref = input.data.ref;
133
164
  const startedAt = ref.startedAt ? new Date(ref.startedAt).toISOString() : "unknown";
134
165
  const endedAt = ref.endedAt ? new Date(ref.endedAt).toISOString() : "unknown";
166
+ // Optional standards block — rendered to the lead-in + body when present,
167
+ // or an empty string (no section) when absent. Gated on non-empty.
168
+ const standards = input.standardsContext?.trim()
169
+ ? `\n## Standards to follow (the rulebook for this target)\n\n${input.standardsContext.trim()}\n`
170
+ : "";
135
171
  return promptTemplate
136
172
  .replace("{{HARNESS}}", ref.harness)
137
173
  .replace("{{TITLE}}", ref.title ?? "(no title)")
@@ -139,7 +175,8 @@ export function buildExtractPrompt(input) {
139
175
  .replace("{{ENDED_AT}}", endedAt)
140
176
  .replace("{{PROJECT_HINT}}", ref.projectHint ?? "(no project hint)")
141
177
  .replace("{{ALREADY_PRESERVED}}", formatAlreadyPreserved(input.inlineRefs))
142
- .replace("{{TRANSCRIPT}}", formatTranscript(input.events));
178
+ .replace("{{STANDARDS}}", standards)
179
+ .replace("{{TRANSCRIPT}}", `${TRANSCRIPT_FENCE_BEGIN}\n${formatTranscript(input.events)}\n${TRANSCRIPT_FENCE_END}`);
143
180
  }
144
181
  /**
145
182
  * Parse the LLM's JSON response into a structured {@link ExtractPayload}.
@@ -207,6 +244,20 @@ export function parseExtractPayload(stdout) {
207
244
  };
208
245
  if (typeof c.when_to_use === "string")
209
246
  candidate.when_to_use = c.when_to_use.trim();
247
+ // #615 WS-0: parse optional ordered-action + outcome fields.
248
+ // Defensive: silently drop malformed entries rather than rejecting the whole candidate.
249
+ if (Array.isArray(c.orderedActions) && c.orderedActions.length > 0) {
250
+ const actions = c.orderedActions
251
+ .filter((a) => typeof a === "string" && a.trim().length >= 3)
252
+ .map((a) => a.trim())
253
+ .slice(0, 20);
254
+ if (actions.length > 0) {
255
+ candidate.orderedActions = actions;
256
+ if (typeof c.outcomeData === "string" && c.outcomeData.trim().length > 0) {
257
+ candidate.outcomeData = c.outcomeData.trim().slice(0, 400);
258
+ }
259
+ }
260
+ }
210
261
  candidates.push(candidate);
211
262
  }
212
263
  const result = { candidates };
@@ -0,0 +1,140 @@
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 { akmExtract } from "./extract.js";
5
+ const DEFAULT_DEBOUNCE_MS = 2000;
6
+ /** Small look-back window for the default trigger's `akmExtract` call. */
7
+ const DEFAULT_TRIGGER_SINCE = "10m";
8
+ /**
9
+ * Does `filePath` look like a real session file under one of the configured
10
+ * roots? True only when the path is under a root AND matches that harness's
11
+ * on-disk session-file shape:
12
+ * - claude-code: `<root>/<project>/<id>.jsonl`
13
+ * - opencode: `<root>/<project>/<id>.json`
14
+ * Anything out-of-root, or in-root with the wrong extension, returns false.
15
+ */
16
+ export function isSessionFile(filePath, roots) {
17
+ return matchHarness(filePath, roots) !== undefined;
18
+ }
19
+ /** Normalize a path for prefix comparison (ensure a trailing separator). */
20
+ function withSep(dir) {
21
+ return dir.endsWith("/") ? dir : `${dir}/`;
22
+ }
23
+ /**
24
+ * Resolve which harness a path belongs to by longest-prefix match against the
25
+ * configured roots, then validate the file shape for that harness. Returns the
26
+ * harness name, or `undefined` when the path is out-of-root or not a session
27
+ * file. Longest-prefix match guards against the (in practice impossible) case
28
+ * of nested roots.
29
+ */
30
+ export function matchHarness(filePath, roots) {
31
+ let best;
32
+ let bestLen = -1;
33
+ for (const target of roots) {
34
+ for (const root of target.roots) {
35
+ const prefix = withSep(root);
36
+ if (filePath.startsWith(prefix) && prefix.length > bestLen) {
37
+ best = { harnessName: target.harnessName };
38
+ bestLen = prefix.length;
39
+ }
40
+ }
41
+ }
42
+ if (!best)
43
+ return undefined;
44
+ // Validate the file shape for the resolved harness.
45
+ if (best.harnessName === "claude-code") {
46
+ return filePath.endsWith(".jsonl") ? best.harnessName : undefined;
47
+ }
48
+ if (best.harnessName === "opencode") {
49
+ return filePath.endsWith(".json") ? best.harnessName : undefined;
50
+ }
51
+ // Unknown harness: accept any file under its root.
52
+ return best.harnessName;
53
+ }
54
+ /**
55
+ * Start the watch-mode core. Returns a handle whose `stop()` tears everything
56
+ * down. The event source is injected, so this never touches the real
57
+ * filesystem itself.
58
+ */
59
+ export function akmExtractWatch(options) {
60
+ const { roots, eventSource, debounceMs = DEFAULT_DEBOUNCE_MS, setTimeoutFn = setTimeout, clearTimeoutFn = clearTimeout, isSessionFile: isSessionFileFn = isSessionFile, } = options;
61
+ const onTrigger = options.onTrigger ??
62
+ (async (harnessName) => {
63
+ await akmExtract({ type: harnessName, since: DEFAULT_TRIGGER_SINCE, force: false });
64
+ });
65
+ let stopped = false;
66
+ // Pending debounce timers, keyed by harness name.
67
+ const pendingTimers = new Map();
68
+ // Harnesses with an in-flight trigger, and whether a re-run is queued.
69
+ const inFlight = new Set();
70
+ const rerunPending = new Set();
71
+ function runTrigger(harnessName) {
72
+ if (stopped)
73
+ return;
74
+ if (inFlight.has(harnessName)) {
75
+ // A trigger is already running for this harness; coalesce into a single
76
+ // re-run after it resolves rather than launching a concurrent extract.
77
+ rerunPending.add(harnessName);
78
+ return;
79
+ }
80
+ inFlight.add(harnessName);
81
+ // Invoke synchronously so a synchronous `onTrigger` observes its effect
82
+ // before control returns (tests advance the clock then assert inline).
83
+ // Wrap the (possibly async) result in a promise to drive the in-flight
84
+ // guard's release without forcing the trigger itself into a microtask.
85
+ let result;
86
+ try {
87
+ result = onTrigger(harnessName);
88
+ }
89
+ catch (err) {
90
+ // One failed extract must never kill the watcher (mirrors the non-fatal
91
+ // handling in collectSessionEvents).
92
+ console.error(`[akm] extract --watch trigger for "${harnessName}" failed:`, err);
93
+ inFlight.delete(harnessName);
94
+ if (rerunPending.delete(harnessName) && !stopped)
95
+ runTrigger(harnessName);
96
+ return;
97
+ }
98
+ Promise.resolve(result)
99
+ .catch((err) => {
100
+ console.error(`[akm] extract --watch trigger for "${harnessName}" failed:`, err);
101
+ })
102
+ .finally(() => {
103
+ inFlight.delete(harnessName);
104
+ if (rerunPending.delete(harnessName) && !stopped) {
105
+ runTrigger(harnessName);
106
+ }
107
+ });
108
+ }
109
+ function onEvent(e) {
110
+ if (stopped)
111
+ return;
112
+ if (!isSessionFileFn(e.path, roots))
113
+ return;
114
+ const harnessName = matchHarness(e.path, roots);
115
+ if (!harnessName)
116
+ return;
117
+ const existing = pendingTimers.get(harnessName);
118
+ if (existing !== undefined)
119
+ clearTimeoutFn(existing);
120
+ const timer = setTimeoutFn(() => {
121
+ pendingTimers.delete(harnessName);
122
+ if (!stopped)
123
+ runTrigger(harnessName);
124
+ }, debounceMs);
125
+ pendingTimers.set(harnessName, timer);
126
+ }
127
+ const unsubscribe = eventSource.subscribe(onEvent);
128
+ return {
129
+ stop() {
130
+ if (stopped)
131
+ return;
132
+ stopped = true;
133
+ unsubscribe();
134
+ for (const timer of pendingTimers.values())
135
+ clearTimeoutFn(timer);
136
+ pendingTimers.clear();
137
+ rerunPending.clear();
138
+ },
139
+ };
140
+ }