akm-cli 0.9.0-rc.9 → 0.9.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (450) hide show
  1. package/CHANGELOG.md +1063 -44
  2. package/README.md +51 -25
  3. package/SECURITY.md +14 -1
  4. package/STABILITY.md +497 -0
  5. package/dist/akm +148 -35
  6. package/dist/{akm-migrate-storage → akm-migrate} +6 -9
  7. package/dist/assets/hints/cli-hints-full.md +223 -95
  8. package/dist/assets/hints/cli-hints-short.md +85 -22
  9. package/dist/assets/improve-strategies/default.json +1 -1
  10. package/dist/assets/improve-strategies/reflect-distill.json +1 -1
  11. package/dist/assets/prompts/memory-infer-user.md +2 -3
  12. package/dist/assets/stash-skeleton/README.md +6 -5
  13. package/dist/assets/stash-skeleton/facts/conventions/assets/agent.md +2 -0
  14. package/dist/assets/stash-skeleton/facts/conventions/assets/command.md +2 -0
  15. package/dist/assets/stash-skeleton/facts/conventions/assets/fact.md +2 -0
  16. package/dist/assets/stash-skeleton/facts/conventions/assets/knowledge.md +2 -0
  17. package/dist/assets/stash-skeleton/facts/conventions/assets/lesson.md +2 -0
  18. package/dist/assets/stash-skeleton/facts/conventions/assets/memory.md +2 -0
  19. package/dist/assets/stash-skeleton/facts/conventions/assets/script.md +2 -0
  20. package/dist/assets/stash-skeleton/facts/conventions/assets/skill.md +2 -0
  21. package/dist/assets/stash-skeleton/facts/conventions/assets/workflow.md +2 -0
  22. package/dist/assets/stash-skeleton/facts/conventions/backlinks.md +2 -0
  23. package/dist/assets/stash-skeleton/facts/conventions/domains.md +2 -0
  24. package/dist/assets/stash-skeleton/facts/conventions/organization.md +20 -9
  25. package/dist/assets/tasks/core/extract.yml +1 -1
  26. package/dist/assets/tasks/core/version-check.yml +1 -1
  27. package/dist/assets/tasks/improve/akm-graph-refresh-weekly.yml +5 -0
  28. package/dist/assets/tasks/improve/akm-improve-catchup.yml +8 -0
  29. package/dist/assets/tasks/improve/akm-improve-consolidate.yml +5 -0
  30. package/dist/assets/tasks/improve/akm-improve-frequent.yml +5 -0
  31. package/dist/assets/tasks/improve/akm-improve-nightly.yml +5 -0
  32. package/dist/assets/templates/html/health.html +1 -3
  33. package/dist/assets/workflows/workflow-template.md +32 -15
  34. package/dist/cli/invocation.js +40 -15
  35. package/dist/cli/parse-args.js +0 -22
  36. package/dist/cli/retired-commands.js +121 -0
  37. package/dist/cli/shared.js +154 -22
  38. package/dist/cli/unknown-flags.js +236 -0
  39. package/dist/cli-node.mjs +2 -1
  40. package/dist/cli.js +696 -258
  41. package/dist/commands/agent/agent-dispatch.js +14 -3
  42. package/dist/commands/agent/contribute-cli.js +73 -88
  43. package/dist/commands/completions.js +79 -22
  44. package/dist/commands/config-cli.js +17 -150
  45. package/dist/commands/env/env-cli.js +59 -143
  46. package/dist/commands/env/env.js +12 -163
  47. package/dist/commands/env/marker-path.js +6 -0
  48. package/dist/commands/env/secret-cli.js +36 -66
  49. package/dist/commands/env/secret.js +24 -57
  50. package/dist/commands/feedback-cli.js +141 -87
  51. package/dist/commands/health/accept-rate.js +58 -0
  52. package/dist/commands/health/advisories.js +3 -4
  53. package/dist/commands/health/checks.js +85 -23
  54. package/dist/commands/health/html-report.js +7 -10
  55. package/dist/commands/health/improve-metrics.js +25 -83
  56. package/dist/commands/health/md-report.js +5 -9
  57. package/dist/commands/health/metrics.js +62 -20
  58. package/dist/commands/health/renderers.js +47 -0
  59. package/dist/commands/health/report-view-model.js +4 -5
  60. package/dist/commands/health/stash-exposure.js +1 -1
  61. package/dist/commands/health/surfaces.js +3 -48
  62. package/dist/commands/health/task-runs.js +3 -67
  63. package/dist/commands/health/types-improve.js +7 -0
  64. package/dist/commands/health.js +99 -28
  65. package/dist/commands/improve/anti-collapse.js +2 -2
  66. package/dist/commands/improve/autonomy-gate.js +68 -0
  67. package/dist/commands/improve/collapse-detector.js +41 -40
  68. package/dist/commands/improve/consolidate/eligibility.js +1 -23
  69. package/dist/commands/improve/consolidate/merge.js +4 -0
  70. package/dist/commands/improve/consolidate.js +140 -1000
  71. package/dist/commands/improve/distill/promote-memory.js +12 -12
  72. package/dist/commands/improve/distill/quality-gate.js +6 -6
  73. package/dist/commands/improve/distill.js +58 -69
  74. package/dist/commands/improve/eligibility.js +95 -55
  75. package/dist/commands/improve/extract-cli.js +14 -133
  76. package/dist/commands/improve/improve-cli.js +98 -114
  77. package/dist/commands/improve/improve-result-file.js +1 -28
  78. package/dist/commands/improve/improve-strategies.js +8 -5
  79. package/dist/commands/improve/improve.js +128 -91
  80. package/dist/commands/improve/loop-stages.js +182 -20
  81. package/dist/commands/improve/memory/derived-ref.js +45 -43
  82. package/dist/commands/improve/memory/memory-belief.js +1 -1
  83. package/dist/commands/improve/memory/memory-contradiction-detect.js +4 -12
  84. package/dist/commands/improve/memory/memory-improve.js +6 -5
  85. package/dist/commands/improve/outcome-loop.js +22 -65
  86. package/dist/commands/improve/preparation.js +114 -123
  87. package/dist/commands/improve/proactive-maintenance.js +2 -5
  88. package/dist/commands/improve/reflect.js +56 -160
  89. package/dist/commands/improve/salience.js +11 -122
  90. package/dist/commands/improve/source-identity.js +10 -38
  91. package/dist/commands/lint/base-linter.js +20 -124
  92. package/dist/commands/lint/env-key-rules.js +31 -47
  93. package/dist/commands/lint/index.js +249 -43
  94. package/dist/commands/{events.js → log.js} +33 -38
  95. package/dist/commands/migrate-cli.js +92 -12
  96. package/dist/commands/migration-tool.js +46 -0
  97. package/dist/commands/observability-cli.js +70 -209
  98. package/dist/commands/proposal/drain.js +101 -29
  99. package/dist/commands/proposal/proposal-cli.js +76 -48
  100. package/dist/commands/proposal/proposal.js +54 -18
  101. package/dist/commands/proposal/propose-cli.js +88 -0
  102. package/dist/commands/proposal/propose.js +23 -15
  103. package/dist/commands/proposal/repository.js +701 -278
  104. package/dist/commands/proposal/validators/proposal-quality-validators.js +2 -8
  105. package/dist/commands/proposal/validators/proposal-validators.js +55 -7
  106. package/dist/commands/proposal/validators/proposals.js +4 -7
  107. package/dist/commands/read/curate.js +34 -53
  108. package/dist/commands/read/knowledge.js +150 -95
  109. package/dist/commands/read/registry-search.js +2 -2
  110. package/dist/commands/read/remember-cli.js +42 -15
  111. package/dist/commands/read/search-cli.js +180 -78
  112. package/dist/commands/read/search.js +58 -43
  113. package/dist/commands/read/show.js +197 -141
  114. package/dist/commands/registry-cli.js +12 -51
  115. package/dist/commands/remember.js +14 -57
  116. package/dist/commands/sources/add-cli.js +100 -31
  117. package/dist/commands/sources/bundle-cli.js +166 -0
  118. package/dist/commands/sources/bundle-config-ops.js +7 -2
  119. package/dist/commands/sources/info.js +18 -5
  120. package/dist/commands/sources/init.js +12 -12
  121. package/dist/commands/sources/installed-stashes.js +382 -98
  122. package/dist/commands/sources/schema-repair.js +3 -2
  123. package/dist/commands/sources/self-update.js +131 -38
  124. package/dist/commands/sources/source-add.js +72 -17
  125. package/dist/commands/sources/source-clone.js +129 -45
  126. package/dist/commands/sources/source-manage.js +43 -23
  127. package/dist/commands/sources/sources-cli.js +57 -208
  128. package/dist/commands/sources/stash-cli.js +46 -53
  129. package/dist/commands/tasks/tasks-cli.js +91 -97
  130. package/dist/commands/tasks/tasks.js +276 -421
  131. package/dist/commands/workflow-cli.js +175 -450
  132. package/dist/core/adapter/adapters/akm-adapter.js +47 -28
  133. package/dist/core/adapter/adapters/akm-lint.js +42 -27
  134. package/dist/core/adapter/adapters/akm-metadata.js +15 -44
  135. package/dist/core/adapter/adapters/akm-task-adapter.js +15 -13
  136. package/dist/core/adapter/adapters/akm-workflow-adapter.js +55 -71
  137. package/dist/core/adapter/adapters/dotenv-adapter.js +1 -1
  138. package/dist/core/adapter/adapters/generic-files-adapter.js +2 -0
  139. package/dist/core/adapter/adapters/index.js +6 -6
  140. package/dist/core/adapter/adapters/llm-wiki-adapter.js +14 -8
  141. package/dist/core/adapter/adapters/okf-adapter.js +187 -19
  142. package/dist/core/adapter/adapters/shared.js +3 -19
  143. package/dist/core/adapter/adapters/tool-dir-shared.js +8 -3
  144. package/dist/core/adapter/adapters/website-snapshot-adapter.js +1 -0
  145. package/dist/core/adapter/detect-adapter.js +17 -0
  146. package/dist/core/adapter/recognize-match.js +6 -4
  147. package/dist/core/adapter/validate-context.js +214 -0
  148. package/dist/core/asset/akm-markdown.js +63 -0
  149. package/dist/core/asset/asset-placement.js +20 -6
  150. package/dist/core/asset/asset-ref.js +11 -9
  151. package/dist/core/asset/frontmatter-lint.js +30 -0
  152. package/dist/core/asset/frontmatter.js +25 -9
  153. package/dist/core/asset/markdown.js +40 -51
  154. package/dist/core/asset/resolve-ref.js +89 -18
  155. package/dist/core/asset/stash-meta.js +1 -1
  156. package/dist/core/bundle-id.js +51 -0
  157. package/dist/core/common.js +152 -38
  158. package/dist/core/config/config-io.js +12 -1
  159. package/dist/core/config/config-schema.js +35 -8
  160. package/dist/core/config/config-sources.js +55 -11
  161. package/dist/core/config/config-walker.js +25 -9
  162. package/dist/core/config/config.js +9 -48
  163. package/dist/core/config/experimental.js +21 -0
  164. package/dist/core/config/schema/embedding.js +5 -1
  165. package/dist/core/config/schema/experimental.js +30 -0
  166. package/dist/core/config/schema/improve-processes.js +0 -6
  167. package/dist/core/config/schema/improve.js +21 -3
  168. package/dist/core/config/schema/index-config.js +8 -15
  169. package/dist/core/config/schema/output.js +4 -1
  170. package/dist/core/config/schema/setup.js +9 -18
  171. package/dist/core/config/schema/sources-bundles.js +49 -33
  172. package/dist/core/config/schema/workflow.js +3 -3
  173. package/dist/core/env-secret-ref.js +76 -46
  174. package/dist/core/errors.js +18 -12
  175. package/dist/core/events.js +46 -128
  176. package/dist/core/file-change.js +6 -5
  177. package/dist/core/fs-txn.js +83 -7
  178. package/dist/core/git-message.js +2 -2
  179. package/dist/core/improve-result.js +1 -100
  180. package/dist/core/lesson-lint.js +1 -17
  181. package/dist/core/logs-db.js +2 -1
  182. package/dist/core/migration-operation.js +16 -0
  183. package/dist/core/mutation-target.js +78 -0
  184. package/dist/core/parse.js +4 -1
  185. package/dist/core/paths.js +17 -20
  186. package/dist/core/recognition-util.js +12 -14
  187. package/dist/core/redaction.js +34 -0
  188. package/dist/core/standards/resolve-standards-context.js +2 -14
  189. package/dist/core/standards/resolve-stash-standards.js +2 -2
  190. package/dist/core/standards/resolve-type-conventions.js +2 -2
  191. package/dist/core/state/migrations.js +41 -18
  192. package/dist/core/state-db.js +5 -14
  193. package/dist/core/structured.js +1 -1
  194. package/dist/core/subprocess.js +6 -4
  195. package/dist/core/text-truncation.js +9 -5
  196. package/dist/core/type-presentation.js +3 -3
  197. package/dist/core/warn.js +0 -3
  198. package/dist/core/write-source.js +771 -95
  199. package/dist/indexer/bundle-identity-guard.js +3 -2
  200. package/dist/indexer/db/graph-db.js +0 -24
  201. package/dist/indexer/ensure-index.js +1 -0
  202. package/dist/indexer/graph/graph-boost.js +9 -34
  203. package/dist/indexer/graph/graph-extraction.js +8 -5
  204. package/dist/indexer/index-writer-lock.js +53 -17
  205. package/dist/indexer/index-written-assets.js +16 -22
  206. package/dist/indexer/indexer.js +497 -239
  207. package/dist/indexer/installations.js +14 -96
  208. package/dist/indexer/passes/dir-staleness.js +16 -9
  209. package/dist/indexer/passes/memory-inference.js +11 -9
  210. package/dist/indexer/passes/metadata.js +113 -47
  211. package/dist/indexer/scan/doc-to-entry.js +38 -1
  212. package/dist/indexer/scan/drain-dir.js +13 -23
  213. package/dist/indexer/search/db-search.js +99 -54
  214. package/dist/indexer/search/fts-query.js +47 -24
  215. package/dist/indexer/search/ranking-contributors.js +42 -20
  216. package/dist/indexer/search/ranking.js +18 -99
  217. package/dist/indexer/search/search-fields.js +7 -2
  218. package/dist/indexer/search/search-source.js +82 -93
  219. package/dist/indexer/usage/usage-events.js +0 -89
  220. package/dist/indexer/walk/file-context.js +2 -1
  221. package/dist/indexer/walk/matchers.js +30 -43
  222. package/dist/indexer/walk/path-resolver.js +7 -2
  223. package/dist/indexer/walk/walker.js +38 -12
  224. package/dist/integrations/agent/builders.js +0 -6
  225. package/dist/integrations/agent/config.js +2 -2
  226. package/dist/integrations/agent/detect.js +49 -19
  227. package/dist/integrations/agent/engine-fallback.js +76 -0
  228. package/dist/integrations/agent/profiles.js +14 -0
  229. package/dist/integrations/agent/prompts.js +12 -8
  230. package/dist/integrations/agent/runner-dispatch.js +4 -2
  231. package/dist/integrations/agent/runner.js +0 -1
  232. package/dist/integrations/agent/spawn.js +5 -6
  233. package/dist/integrations/github.js +1 -1
  234. package/dist/integrations/harnesses/aider/agent-builder.js +6 -4
  235. package/dist/integrations/harnesses/amazonq/agent-builder.js +7 -4
  236. package/dist/integrations/harnesses/claude/session-log.js +0 -10
  237. package/dist/integrations/harnesses/codex/agent-builder.js +5 -2
  238. package/dist/integrations/harnesses/copilot/agent-builder.js +5 -3
  239. package/dist/integrations/harnesses/gemini/agent-builder.js +5 -3
  240. package/dist/integrations/harnesses/index.js +3 -7
  241. package/dist/integrations/harnesses/opencode/agent-builder.js +21 -2
  242. package/dist/integrations/harnesses/opencode/session-log.js +0 -15
  243. package/dist/integrations/harnesses/opencode-sdk/sdk-runner.js +13 -4
  244. package/dist/integrations/harnesses/openhands/agent-builder.js +9 -6
  245. package/dist/integrations/harnesses/pi/agent-builder.js +6 -4
  246. package/dist/integrations/lockfile.js +88 -7
  247. package/dist/integrations/session-logs/index.js +3 -28
  248. package/dist/llm/client.js +136 -100
  249. package/dist/llm/embedders/remote.js +13 -5
  250. package/dist/llm/feature-gate.js +4 -12
  251. package/dist/llm/graph-extract.js +5 -11
  252. package/dist/llm/memory-infer.js +144 -1
  253. package/dist/llm/metadata-enhance.js +5 -7
  254. package/dist/llm/structured-call.js +1 -1
  255. package/dist/llm/usage-persist.js +26 -5
  256. package/dist/llm/usage-telemetry.js +25 -2
  257. package/dist/output/cli-hints.js +1 -2
  258. package/dist/output/context.js +22 -7
  259. package/dist/output/format-exempt.js +80 -0
  260. package/dist/output/generic-render.js +259 -0
  261. package/dist/output/render-registry.js +57 -0
  262. package/dist/output/renderers.js +14 -36
  263. package/dist/output/shapes/curate.js +10 -1
  264. package/dist/output/shapes/events.js +12 -7
  265. package/dist/output/shapes/helpers.js +56 -83
  266. package/dist/output/shapes/migrate.js +8 -0
  267. package/dist/output/shapes/passthrough.js +7 -41
  268. package/dist/output/shapes/proposal/producer.js +15 -7
  269. package/dist/output/shapes.js +2 -9
  270. package/dist/output/text/{init.js → bundle-create.js} +3 -1
  271. package/dist/output/text/bundle-show.js +7 -0
  272. package/dist/output/text/command-format.js +164 -96
  273. package/dist/output/text/env.js +1 -3
  274. package/dist/output/text/events.js +8 -7
  275. package/dist/output/text/health-format.js +103 -0
  276. package/dist/output/text/health.js +7 -0
  277. package/dist/output/text/helpers.js +10 -8
  278. package/dist/output/text/lint-format.js +43 -0
  279. package/dist/output/text/{save.js → lint.js} +2 -2
  280. package/dist/output/text/migrate.js +88 -0
  281. package/dist/output/text/proposal/producer.js +4 -2
  282. package/dist/output/text/proposal-format.js +44 -72
  283. package/dist/output/text/registry-commands.js +1 -2
  284. package/dist/output/text/show-directives.js +15 -7
  285. package/dist/output/text/status-list.js +32 -0
  286. package/dist/output/text/sync.js +5 -0
  287. package/dist/output/text/workflow-format.js +24 -203
  288. package/dist/output/text/workflow.js +1 -7
  289. package/dist/output/text.js +16 -17
  290. package/dist/registry/factory.js +4 -6
  291. package/dist/registry/origin-resolve.js +16 -27
  292. package/dist/registry/providers/skills-sh.js +3 -3
  293. package/dist/registry/providers/static-index.js +13 -23
  294. package/dist/registry/resolve.js +42 -7
  295. package/dist/registry/semver.js +34 -84
  296. package/dist/runtime.js +2 -23
  297. package/dist/scripts/akm-migrate-node.js +60290 -0
  298. package/dist/scripts/akm-migrate.js +59628 -0
  299. package/dist/setup/detect.js +42 -15
  300. package/dist/setup/registry-stash-loader.js +2 -2
  301. package/dist/setup/setup.js +236 -136
  302. package/dist/setup/steps/connection.js +7 -9
  303. package/dist/setup/steps/platforms.js +9 -9
  304. package/dist/setup/steps/semantic.js +15 -3
  305. package/dist/setup/steps/sources.js +12 -13
  306. package/dist/setup/steps/stashdir.js +2 -3
  307. package/dist/setup/steps/tasks.js +237 -120
  308. package/dist/sources/freshness.js +1 -1
  309. package/dist/sources/provider-factory.js +11 -17
  310. package/dist/sources/providers/filesystem.js +2 -3
  311. package/dist/sources/providers/git-install.js +278 -34
  312. package/dist/sources/providers/git-provider.js +25 -23
  313. package/dist/sources/providers/git-stash.js +395 -106
  314. package/dist/sources/providers/git.js +2 -2
  315. package/dist/sources/providers/npm.js +16 -19
  316. package/dist/sources/providers/provider-utils.js +7 -4
  317. package/dist/sources/providers/sync-from-ref.js +3 -9
  318. package/dist/sources/providers/website.js +6 -1
  319. package/dist/sources/resolve.js +6 -5
  320. package/dist/sources/snapshot-fetchers/bluesky.js +146 -0
  321. package/dist/sources/snapshot-fetchers/content-extract.js +566 -0
  322. package/dist/sources/snapshot-fetchers/fetcher-util.js +41 -0
  323. package/dist/sources/snapshot-fetchers/github.js +100 -0
  324. package/dist/sources/snapshot-fetchers/host-guard.js +291 -0
  325. package/dist/sources/snapshot-fetchers/registry.js +17 -1
  326. package/dist/sources/snapshot-fetchers/robots.js +348 -0
  327. package/dist/sources/snapshot-fetchers/rss.js +282 -0
  328. package/dist/sources/snapshot-fetchers/secret-seam.js +42 -0
  329. package/dist/sources/snapshot-fetchers/website-ingest.js +566 -268
  330. package/dist/sources/snapshot-fetchers/x.js +910 -0
  331. package/dist/storage/database.js +7 -0
  332. package/dist/storage/engines/sqlite-migrations.js +23 -111
  333. package/dist/storage/managed-db.js +2 -2
  334. package/dist/storage/repositories/canaries-repository.js +1 -1
  335. package/dist/storage/repositories/events-repository.js +27 -11
  336. package/dist/storage/repositories/improve-runs-repository.js +6 -12
  337. package/dist/storage/repositories/index-connection.js +17 -6
  338. package/dist/storage/repositories/index-entries-repository.js +151 -240
  339. package/dist/storage/repositories/index-entry-mapper.js +15 -11
  340. package/dist/storage/repositories/index-fts-repository.js +5 -2
  341. package/dist/storage/repositories/index-llm-cache-repository.js +0 -1
  342. package/dist/storage/repositories/index-meta-repository.js +2 -3
  343. package/dist/storage/repositories/index-schema.js +10 -25
  344. package/dist/storage/repositories/index-utility-repository.js +15 -28
  345. package/dist/storage/repositories/index-vec-repository.js +6 -1
  346. package/dist/storage/repositories/outcome-repository.js +119 -0
  347. package/dist/storage/repositories/proposals-repository.js +296 -59
  348. package/dist/storage/repositories/registry-cache.js +19 -0
  349. package/dist/storage/repositories/salience-repository.js +172 -0
  350. package/dist/storage/repositories/task-history-repository.js +15 -13
  351. package/dist/storage/repositories/workflow-runs-repository.js +52 -40
  352. package/dist/tasks/backends/cron.js +105 -15
  353. package/dist/tasks/backends/index.js +1 -1
  354. package/dist/tasks/backends/launchd.js +85 -38
  355. package/dist/tasks/backends/schtasks.js +135 -15
  356. package/dist/tasks/embedded.js +56 -40
  357. package/dist/tasks/parser.js +7 -157
  358. package/dist/tasks/resolve-akm-bin.js +137 -59
  359. package/dist/tasks/runner.js +79 -42
  360. package/dist/tasks/scheduler-invocation.js +220 -10
  361. package/dist/tasks/schema.js +24 -1
  362. package/dist/tasks/task-id.js +1 -3
  363. package/dist/tasks/validator.js +20 -6
  364. package/dist/workflows/authoring/authoring.js +94 -143
  365. package/dist/workflows/authoring/scope-key.js +1 -1
  366. package/dist/workflows/exec/frozen-judge.js +28 -2
  367. package/dist/workflows/exec/native-executor.js +77 -57
  368. package/dist/workflows/exec/param-secrets.js +9 -9
  369. package/dist/workflows/exec/run-workflow.js +133 -79
  370. package/dist/workflows/exec/step-work.js +219 -346
  371. package/dist/{migrate-storage-node.mjs → workflows/exec/unit-dispatch.js} +1 -5
  372. package/dist/workflows/ir/compile.js +141 -270
  373. package/dist/workflows/ir/freeze.js +40 -30
  374. package/dist/workflows/ir/params.js +135 -11
  375. package/dist/workflows/ir/plan-hash.js +1 -1
  376. package/dist/workflows/ir/schema.js +25 -26
  377. package/dist/workflows/parser.js +872 -307
  378. package/dist/workflows/program/expressions.js +20 -208
  379. package/dist/workflows/program/schema.js +7 -10
  380. package/dist/workflows/renderer.js +95 -68
  381. package/dist/workflows/resource-limits.js +2 -0
  382. package/dist/workflows/runtime/checkin.js +3 -3
  383. package/dist/workflows/runtime/plan-classifier.js +16 -75
  384. package/dist/workflows/runtime/runs.js +186 -127
  385. package/dist/workflows/runtime/unit-checkin.js +1 -1
  386. package/dist/workflows/runtime/unit-phases.js +2 -2
  387. package/dist/workflows/runtime/workflow-asset-loader.js +232 -83
  388. package/dist/workflows/schema.js +1 -11
  389. package/dist/workflows/validate-summary.js +30 -36
  390. package/dist/workflows/validator.js +21 -62
  391. package/docs/README.md +68 -0
  392. package/docs/migration/README.md +8 -0
  393. package/docs/migration/release-notes/0.7.0.md +11 -11
  394. package/docs/migration/release-notes/0.9.0.md +208 -27
  395. package/docs/migration/v0.7-to-v0.8.md +46 -47
  396. package/docs/migration/v0.8-to-v0.9.md +564 -208
  397. package/docs/migration/v0.9.0-troubleshooting.md +561 -0
  398. package/docs/reference/README.md +12 -0
  399. package/docs/reference/cli.md +2253 -0
  400. package/docs/reference/configuration.md +358 -0
  401. package/docs/reference/data-and-telemetry.md +105 -42
  402. package/docs/reference/workflows.md +647 -0
  403. package/package.json +22 -11
  404. package/schemas/akm-asset-envelope.json +93 -0
  405. package/schemas/akm-config.json +81 -128
  406. package/schemas/akm-workflow.json +74 -73
  407. package/dist/assets/tasks/core/backup.yml +0 -5
  408. package/dist/assets/tasks/graph-refresh-weekly.yml +0 -10
  409. package/dist/cli/config-migrate.js +0 -1878
  410. package/dist/cli/config-validate.js +0 -41
  411. package/dist/commands/backup-cli.js +0 -56
  412. package/dist/commands/bundle/bundle-cli.js +0 -68
  413. package/dist/commands/bundle/bundle.js +0 -219
  414. package/dist/commands/graph/graph-cli.js +0 -124
  415. package/dist/commands/graph/graph.js +0 -489
  416. package/dist/commands/improve/extract-watch.js +0 -140
  417. package/dist/commands/mv-cli.js +0 -1221
  418. package/dist/commands/sources/history.js +0 -201
  419. package/dist/commands/tasks/default-tasks.js +0 -186
  420. package/dist/core/migration-backup.js +0 -1231
  421. package/dist/indexer/usage/unmigrated-vaults-guard.js +0 -95
  422. package/dist/llm/memory-infer-impl.js +0 -138
  423. package/dist/migrate/legacy/config-source-migration.js +0 -257
  424. package/dist/migrate/legacy/content-migration.js +0 -350
  425. package/dist/migrate/legacy/legacy-layout.js +0 -779
  426. package/dist/migrate/legacy/legacy-paths.js +0 -25
  427. package/dist/migrate/legacy/legacy-stash-json.js +0 -80
  428. package/dist/migrate/legacy/proposal-fs-import.js +0 -168
  429. package/dist/migrate/legacy/task-target-ref-migration.js +0 -278
  430. package/dist/migrate/legacy/three-db-cutover.js +0 -845
  431. package/dist/migrate/legacy/workflow-migrations-bodies.js +0 -52
  432. package/dist/migrate/legacy/workflow-migrations-frozen.js +0 -21
  433. package/dist/migrate/legacy-ref-grammar.js +0 -214
  434. package/dist/output/shapes/distill.js +0 -14
  435. package/dist/output/shapes/history.js +0 -11
  436. package/dist/output/text/distill.js +0 -6
  437. package/dist/output/text/enable-disable.js +0 -8
  438. package/dist/output/text/history.js +0 -6
  439. package/dist/registry/build-index.js +0 -382
  440. package/dist/schemas/akm-config.json +0 -4704
  441. package/dist/schemas/akm-task.json +0 -87
  442. package/dist/schemas/akm-workflow.json +0 -372
  443. package/dist/scripts/migrate-storage.js +0 -3816
  444. package/dist/workflows/authoring/workflow-program-template.yaml +0 -31
  445. package/dist/workflows/cli.js +0 -53
  446. package/dist/workflows/exec/brief.js +0 -481
  447. package/dist/workflows/exec/report.js +0 -1460
  448. package/dist/workflows/exec/watch.js +0 -116
  449. package/dist/workflows/program/parser.js +0 -813
  450. package/dist/workflows/program/project.js +0 -104
@@ -1,489 +0,0 @@
1
- // This Source Code Form is subject to the terms of the Mozilla Public
2
- // License, v. 2.0. If a copy of the MPL was not distributed with this
3
- // file, You can obtain one at https://mozilla.org/MPL/2.0/.
4
- import fs from "node:fs";
5
- import path from "node:path";
6
- import { displayRef, parseQualifiedRefInput } from "../../core/asset/resolve-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 { loadStoredGraphSnapshot } from "../../indexer/db/graph-db.js";
12
- import { listRelatedPathsForFile } from "../../indexer/graph/graph-boost.js";
13
- import { runGraphExtractionPass } from "../../indexer/graph/graph-extraction.js";
14
- import { withIndexWriterLease } from "../../indexer/index-writer-lock.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
- import { closeDatabase, openExistingDatabase, openIndexDatabase } from "../../storage/repositories/index-connection.js";
19
- import { findEntryIdByRef, getEntryById, getEntryRefRowsForStashRoot, } from "../../storage/repositories/index-entries-repository.js";
20
- function resolveGraphStashPath(source) {
21
- const sources = resolveSourceEntries(undefined, loadConfig());
22
- if (sources.length === 0) {
23
- throw new NotFoundError("No stash sources are configured.", "STASH_NOT_FOUND");
24
- }
25
- if (!source || source === "primary")
26
- return sources[0].path;
27
- const matched = sources.find((entry) => entry.registryId === source || entry.path === source);
28
- if (!matched) {
29
- throw new NotFoundError(`Source not found: ${source}`, "SOURCE_NOT_FOUND", "Run `akm list` to see source names.");
30
- }
31
- return matched.path;
32
- }
33
- function loadGraph(source) {
34
- const stashPath = resolveGraphStashPath(source);
35
- let db;
36
- try {
37
- db = openExistingDatabase();
38
- const snapshot = loadStoredGraphSnapshot(stashPath, db);
39
- if (!snapshot) {
40
- throw new NotFoundError(`Graph data not found for source ${stashPath}.`, "FILE_NOT_FOUND", "Run the improvement flow that refreshes graph extraction data.");
41
- }
42
- return {
43
- graph: {
44
- schemaVersion: snapshot.schemaVersion,
45
- generatedAt: snapshot.generatedAt,
46
- stashRoot: snapshot.stashPath,
47
- files: snapshot.files,
48
- entities: snapshot.entities,
49
- relations: snapshot.relations,
50
- ...(snapshot.quality ? { quality: snapshot.quality } : {}),
51
- ...(snapshot.telemetry ? { telemetry: snapshot.telemetry } : {}),
52
- },
53
- stashPath,
54
- graphPath: snapshot.graphPath,
55
- };
56
- }
57
- finally {
58
- if (db)
59
- closeDatabase(db);
60
- }
61
- }
62
- function countEntitiesByFile(nodes) {
63
- const counts = new Map();
64
- for (const node of nodes) {
65
- const seen = new Set();
66
- for (const entity of node.entities) {
67
- if (seen.has(entity))
68
- continue;
69
- seen.add(entity);
70
- counts.set(entity, (counts.get(entity) ?? 0) + 1);
71
- }
72
- }
73
- return counts;
74
- }
75
- function aggregateEntityStats(nodes) {
76
- const stats = new Map();
77
- for (const node of nodes) {
78
- const seen = new Set();
79
- for (const entity of node.entities) {
80
- if (seen.has(entity))
81
- continue;
82
- seen.add(entity);
83
- const existing = stats.get(entity);
84
- const nodeConf = typeof node.confidence === "number" && Number.isFinite(node.confidence) ? node.confidence : undefined;
85
- if (existing) {
86
- existing.fileCount += 1;
87
- if (nodeConf !== undefined && (existing.confidence === undefined || nodeConf > existing.confidence)) {
88
- existing.confidence = nodeConf;
89
- }
90
- }
91
- else {
92
- stats.set(entity, { fileCount: 1, ...(nodeConf !== undefined ? { confidence: nodeConf } : {}) });
93
- }
94
- }
95
- }
96
- return stats;
97
- }
98
- export function akmGraphSummary(options) {
99
- const { graph, stashPath, graphPath } = loadGraph(options?.source);
100
- return {
101
- schemaVersion: 1,
102
- shape: "graph-summary",
103
- stashPath,
104
- graphPath,
105
- generatedAt: graph.generatedAt,
106
- fileCount: graph.files.length,
107
- entityCount: Array.isArray(graph.entities) ? graph.entities.length : countEntitiesByFile(graph.files).size,
108
- relationCount: Array.isArray(graph.relations)
109
- ? graph.relations.length
110
- : graph.files.reduce((sum, node) => sum + node.relations.length, 0),
111
- ...(graph.quality ? { quality: graph.quality } : {}),
112
- ...(graph.telemetry ? { telemetry: graph.telemetry } : {}),
113
- };
114
- }
115
- export function akmGraphEntities(options) {
116
- const { graph, stashPath, graphPath } = loadGraph(options?.source);
117
- const limit = options?.limit;
118
- if (limit !== undefined && (!Number.isFinite(limit) || limit <= 0)) {
119
- throw new UsageError("--limit must be a positive integer.", "INVALID_FLAG_VALUE");
120
- }
121
- const stats = aggregateEntityStats(graph.files);
122
- const entities = [...stats.entries()]
123
- .map(([name, info]) => ({
124
- name,
125
- fileCount: info.fileCount,
126
- ...(info.confidence !== undefined ? { confidence: info.confidence } : {}),
127
- }))
128
- .sort((a, b) => b.fileCount - a.fileCount || a.name.localeCompare(b.name));
129
- const sliced = typeof limit === "number" ? entities.slice(0, limit) : entities;
130
- return {
131
- schemaVersion: 1,
132
- shape: "graph-entities",
133
- stashPath,
134
- graphPath,
135
- generatedAt: graph.generatedAt,
136
- total: entities.length,
137
- entities: sliced,
138
- };
139
- }
140
- export function akmGraphRelations(options) {
141
- const { graph, stashPath, graphPath } = loadGraph(options?.source);
142
- const limit = options?.limit;
143
- if (limit !== undefined && (!Number.isFinite(limit) || limit <= 0)) {
144
- throw new UsageError("--limit must be a positive integer.", "INVALID_FLAG_VALUE");
145
- }
146
- const counts = new Map();
147
- for (const node of graph.files) {
148
- for (const rel of node.relations) {
149
- const key = `${rel.from}\u0000${rel.to}\u0000${rel.type ?? ""}`;
150
- const relConf = typeof rel.confidence === "number" && Number.isFinite(rel.confidence) ? rel.confidence : undefined;
151
- const existing = counts.get(key);
152
- if (existing) {
153
- existing.count += 1;
154
- if (relConf !== undefined && (existing.confidence === undefined || relConf > existing.confidence)) {
155
- existing.confidence = relConf;
156
- }
157
- }
158
- else {
159
- counts.set(key, {
160
- from: rel.from,
161
- to: rel.to,
162
- ...(rel.type ? { type: rel.type } : {}),
163
- count: 1,
164
- ...(relConf !== undefined ? { confidence: relConf } : {}),
165
- });
166
- }
167
- }
168
- }
169
- const relations = [...counts.values()].sort((a, b) => b.count - a.count || a.from.localeCompare(b.from) || a.to.localeCompare(b.to));
170
- const sliced = typeof limit === "number" ? relations.slice(0, limit) : relations;
171
- return {
172
- schemaVersion: 1,
173
- shape: "graph-relations",
174
- stashPath,
175
- graphPath,
176
- generatedAt: graph.generatedAt,
177
- total: relations.length,
178
- relations: sliced,
179
- };
180
- }
181
- export function akmGraphExport(options) {
182
- if (!options.out?.trim()) {
183
- throw new UsageError("`akm graph export` requires --out <path>.", "MISSING_REQUIRED_ARGUMENT");
184
- }
185
- const format = options.format ?? "json";
186
- if (format !== "json" && format !== "jsonl") {
187
- throw new UsageError("--format must be one of: json, jsonl.", "INVALID_FLAG_VALUE");
188
- }
189
- const { graph, stashPath, graphPath } = loadGraph(options.source);
190
- const outPath = path.resolve(options.out);
191
- fs.mkdirSync(path.dirname(outPath), { recursive: true });
192
- const payload = format === "json"
193
- ? `${JSON.stringify(graph, null, 2)}\n`
194
- : `${[
195
- ...graph.files.flatMap((file) => file.entities.map((entity) => JSON.stringify({ kind: "entity", entity, file: file.path }))),
196
- ...graph.files.flatMap((file) => file.relations.map((relation) => JSON.stringify({ kind: "relation", ...relation, file: file.path }))),
197
- ].join("\n")}\n`;
198
- fs.writeFileSync(outPath, payload, "utf8");
199
- return {
200
- schemaVersion: 1,
201
- shape: "graph-export",
202
- stashPath,
203
- graphPath,
204
- outPath,
205
- format,
206
- bytes: Buffer.byteLength(payload, "utf8"),
207
- };
208
- }
209
- export async function akmGraphRelated(options) {
210
- const ref = options.ref.trim();
211
- if (!ref) {
212
- throw new UsageError("`akm graph related` requires <ref>.", "MISSING_REQUIRED_ARGUMENT");
213
- }
214
- const limit = options.limit;
215
- if (limit !== undefined && (!Number.isFinite(limit) || limit <= 0)) {
216
- throw new UsageError("--limit must be a positive integer.", "INVALID_FLAG_VALUE");
217
- }
218
- const target = await resolveGraphTarget(ref, options.source);
219
- const { graph, stashPath, graphPath } = loadGraph(target.stashPath);
220
- let db;
221
- const related = (() => {
222
- try {
223
- db = openExistingDatabase();
224
- return listRelatedPathsForFile(stashPath, target.filePath, limit ?? 5, db);
225
- }
226
- finally {
227
- if (db)
228
- closeDatabase(db);
229
- }
230
- })();
231
- return {
232
- schemaVersion: 1,
233
- shape: "graph-related",
234
- stashPath,
235
- graphPath,
236
- generatedAt: graph.generatedAt,
237
- ref: target.ref,
238
- path: target.filePath,
239
- total: related.length,
240
- related,
241
- ...(related.length === 0 ? { tip: "No related graph neighbors were found for this asset." } : {}),
242
- };
243
- }
244
- function normalizeGraphName(value) {
245
- return value.trim().toLowerCase();
246
- }
247
- function buildRefByPath(stashRoot, db) {
248
- const rows = getEntryRefRowsForStashRoot(db, stashRoot);
249
- const map = new Map();
250
- for (const row of rows) {
251
- if (map.has(row.file_path))
252
- continue;
253
- try {
254
- const entry = JSON.parse(row.entry_json);
255
- if (typeof entry.type === "string" && typeof entry.name === "string") {
256
- // F4b output-spelling flip: emit the 0.9.0 conceptId grammar.
257
- map.set(row.file_path, { ref: displayRef({ type: entry.type, name: entry.name }), type: entry.type });
258
- }
259
- }
260
- catch {
261
- // ignore corrupt entry_json
262
- }
263
- }
264
- return map;
265
- }
266
- export function akmGraphEntity(options) {
267
- const name = options.name?.trim();
268
- if (!name) {
269
- throw new UsageError("`akm graph entity` requires <name>.", "MISSING_REQUIRED_ARGUMENT");
270
- }
271
- const limit = options.limit;
272
- if (limit !== undefined && (!Number.isFinite(limit) || limit <= 0)) {
273
- throw new UsageError("--limit must be a positive integer.", "INVALID_FLAG_VALUE");
274
- }
275
- const { graph, stashPath, graphPath } = loadGraph(options.source);
276
- const target = normalizeGraphName(name);
277
- let db;
278
- let refByPath;
279
- try {
280
- db = openExistingDatabase();
281
- refByPath = buildRefByPath(stashPath, db);
282
- }
283
- finally {
284
- if (db)
285
- closeDatabase(db);
286
- }
287
- const matches = [];
288
- for (const node of graph.files) {
289
- const found = node.entities.some((entity) => normalizeGraphName(entity) === target);
290
- if (!found)
291
- continue;
292
- const lookup = refByPath.get(node.path);
293
- const conf = typeof node.confidence === "number" && Number.isFinite(node.confidence) ? node.confidence : undefined;
294
- matches.push({
295
- ...(lookup?.ref ? { ref: lookup.ref } : {}),
296
- path: node.path,
297
- type: node.type,
298
- ...(conf !== undefined ? { confidence: conf } : {}),
299
- });
300
- }
301
- matches.sort((a, b) => {
302
- const ca = a.confidence ?? 0;
303
- const cb = b.confidence ?? 0;
304
- if (cb !== ca)
305
- return cb - ca;
306
- return a.path.localeCompare(b.path);
307
- });
308
- const sliced = typeof limit === "number" ? matches.slice(0, limit) : matches;
309
- return {
310
- schemaVersion: 1,
311
- shape: "graph-entity",
312
- stashPath,
313
- graphPath,
314
- generatedAt: graph.generatedAt,
315
- entity: name,
316
- total: matches.length,
317
- matches: sliced,
318
- };
319
- }
320
- export function akmGraphOrphans(options) {
321
- const limit = options?.limit;
322
- if (limit !== undefined && (!Number.isFinite(limit) || limit <= 0)) {
323
- throw new UsageError("--limit must be a positive integer.", "INVALID_FLAG_VALUE");
324
- }
325
- const { graph, stashPath, graphPath } = loadGraph(options?.source);
326
- let db;
327
- let refByPath;
328
- try {
329
- db = openExistingDatabase();
330
- refByPath = buildRefByPath(stashPath, db);
331
- }
332
- finally {
333
- if (db)
334
- closeDatabase(db);
335
- }
336
- const orphans = [];
337
- for (const node of graph.files) {
338
- if ((node.status ?? (node.entities.length > 0 ? "extracted" : "empty")) === "extracted")
339
- continue;
340
- const lookup = refByPath.get(node.path);
341
- orphans.push({
342
- ...(lookup?.ref ? { ref: lookup.ref } : {}),
343
- path: node.path,
344
- type: node.type,
345
- ...(node.status ? { status: node.status } : {}),
346
- ...(node.reason ? { reason: node.reason } : {}),
347
- });
348
- }
349
- orphans.sort((a, b) => a.type.localeCompare(b.type) || a.path.localeCompare(b.path));
350
- const sliced = typeof limit === "number" ? orphans.slice(0, limit) : orphans;
351
- return {
352
- schemaVersion: 1,
353
- shape: "graph-orphans",
354
- stashPath,
355
- graphPath,
356
- generatedAt: graph.generatedAt,
357
- totalConsidered: graph.files.length,
358
- total: orphans.length,
359
- orphans: sliced,
360
- };
361
- }
362
- /**
363
- * Re-run graph extraction, optionally scoped to specific asset refs.
364
- *
365
- * When `refs` is provided, only those files are re-extracted (incremental).
366
- * When no refs are given, the full eligible set is re-extracted.
367
- */
368
- export async function akmGraphUpdate(options) {
369
- const config = options.config ?? loadConfig();
370
- const sources = resolveSourceEntries(options.stashDir, config);
371
- if (sources.length === 0) {
372
- throw new NotFoundError("No stash sources are configured.", "STASH_NOT_FOUND");
373
- }
374
- if (options.source && options.source !== "primary") {
375
- const matched = sources.find((s) => s.registryId === options.source || s.path === options.source);
376
- if (!matched) {
377
- throw new NotFoundError(`Source not found: ${options.source}`, "SOURCE_NOT_FOUND", "Run `akm list` to see source names.");
378
- }
379
- }
380
- const scoped = Array.isArray(options.refs) && options.refs.length > 0;
381
- return withIndexWriterLease({ purpose: "graph-update" }, async () => {
382
- let candidatePaths;
383
- if (scoped && options.refs) {
384
- // Resolve each ref to an absolute file path while the writer lease is held
385
- // so the scoped graph write sees the same index snapshot it resolved from.
386
- const dbPath = getDbPath();
387
- let db;
388
- const resolvedPaths = new Set();
389
- try {
390
- db = openIndexDatabase(dbPath);
391
- for (const ref of options.refs) {
392
- const trimmed = ref.trim();
393
- if (!trimmed)
394
- continue;
395
- const entryId = findEntryIdByRef(db, trimmed);
396
- if (entryId === undefined) {
397
- warn(`[graph] ref not found in index, skipping: ${trimmed}`);
398
- continue;
399
- }
400
- const row = getEntryById(db, entryId);
401
- if (!row?.filePath) {
402
- warn(`[graph] could not resolve path for ref, skipping: ${trimmed}`);
403
- continue;
404
- }
405
- resolvedPaths.add(row.filePath);
406
- }
407
- }
408
- finally {
409
- if (db)
410
- closeDatabase(db);
411
- }
412
- if (resolvedPaths.size === 0) {
413
- warn("[graph] none of the provided refs resolved to indexed paths — no extraction performed.");
414
- return {
415
- shape: "graph-update",
416
- ok: true,
417
- filesExtracted: 0,
418
- entitiesUpserted: 0,
419
- relationsUpserted: 0,
420
- durationMs: 0,
421
- scoped: true,
422
- };
423
- }
424
- candidatePaths = resolvedPaths;
425
- }
426
- const extractionFn = options.graphExtractionFn ?? runGraphExtractionPass;
427
- const passOptions = candidatePaths ? { candidatePaths } : {};
428
- let db;
429
- const startMs = Date.now();
430
- try {
431
- db = openIndexDatabase(getDbPath());
432
- const onProgress = (event) => {
433
- if (!event.currentPath)
434
- return;
435
- const file = path.basename(event.currentPath);
436
- warn(`[graph] extracting ${event.processed}/${event.total} ${file}`);
437
- };
438
- const result = await extractionFn({
439
- config,
440
- sources,
441
- signal: undefined,
442
- db,
443
- reEnrich: false,
444
- onProgress,
445
- options: passOptions,
446
- });
447
- const durationMs = Date.now() - startMs;
448
- return {
449
- shape: "graph-update",
450
- ok: true,
451
- filesExtracted: result.quality.extractedFiles,
452
- entitiesUpserted: result.quality.entityCount,
453
- relationsUpserted: result.quality.relationCount,
454
- durationMs,
455
- scoped,
456
- };
457
- }
458
- finally {
459
- if (db)
460
- closeDatabase(db);
461
- }
462
- });
463
- }
464
- async function resolveGraphTarget(ref, source) {
465
- const parsedRef = parseQualifiedRefInput(ref);
466
- const filePath = (await resolveAssetPath(parsedRef, {
467
- mode: "index-first",
468
- honorOrigin: true,
469
- })) ?? (await lookup(parsedRef))?.filePath;
470
- if (!filePath) {
471
- throw new NotFoundError(`Asset not found for ref: ${ref}`);
472
- }
473
- const allSources = resolveSourceEntries(undefined, loadConfig());
474
- const matchedSource = findSourceForPath(filePath, allSources);
475
- const inferredStashPath = matchedSource?.path;
476
- const stashPath = source ? resolveGraphStashPath(source) : inferredStashPath;
477
- if (!stashPath) {
478
- throw new NotFoundError(`Could not determine stash source for ref: ${ref}`, "SOURCE_NOT_FOUND");
479
- }
480
- if (!filePath.startsWith(path.resolve(stashPath) + path.sep) && path.resolve(filePath) !== path.resolve(stashPath)) {
481
- throw new UsageError(`Resolved asset ${ref} is not inside source ${source ?? stashPath}.`, "INVALID_SOURCE_VALUE", "Pass --source for the asset's source, or omit it to infer from the resolved asset path.");
482
- }
483
- return {
484
- ref,
485
- parsedRef,
486
- filePath,
487
- stashPath,
488
- };
489
- }
@@ -1,140 +0,0 @@
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
- }