akm-cli 0.8.6 → 0.8.14

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (324) hide show
  1. package/CHANGELOG.md +442 -0
  2. package/dist/assets/help/help-proposals.md +1 -2
  3. package/dist/assets/hints/cli-hints-full.md +34 -19
  4. package/dist/assets/hints/cli-hints-short.md +1 -1
  5. package/dist/assets/profiles/catchup.json +13 -0
  6. package/dist/assets/profiles/consolidate.json +13 -0
  7. package/dist/assets/profiles/frequent.json +13 -0
  8. package/dist/assets/tasks/core/backup.yml +4 -0
  9. package/dist/assets/tasks/core/extract.yml +4 -0
  10. package/dist/assets/tasks/core/improve.yml +4 -0
  11. package/dist/assets/tasks/core/index-refresh.yml +4 -0
  12. package/dist/assets/tasks/core/sync.yml +4 -0
  13. package/dist/assets/tasks/core/update-stashes.yml +4 -0
  14. package/dist/assets/tasks/core/version-check.yml +4 -0
  15. package/dist/assets/templates/html/default.html +78 -0
  16. package/dist/assets/templates/html/health.html +560 -0
  17. package/dist/assets/templates/html/vendor/echarts.min.js +45 -0
  18. package/dist/cli/config-migrate.js +6 -6
  19. package/dist/cli/config-validate.js +4 -4
  20. package/dist/cli/confirm.js +3 -3
  21. package/dist/cli/parse-args.js +1 -1
  22. package/dist/cli/shared.js +72 -19
  23. package/dist/cli-node.mjs +26 -0
  24. package/dist/cli.js +206 -3866
  25. package/dist/commands/{agent-dispatch.js → agent/agent-dispatch.js} +6 -6
  26. package/dist/commands/{agent-support.js → agent/agent-support.js} +2 -2
  27. package/dist/commands/agent/contribute-cli.js +200 -0
  28. package/dist/commands/completions.js +1 -1
  29. package/dist/commands/config-cli.js +230 -3
  30. package/dist/commands/db-cli.js +2 -2
  31. package/dist/commands/env/env-cli.js +529 -0
  32. package/dist/commands/env/env.js +410 -0
  33. package/dist/commands/env/secret-cli.js +259 -0
  34. package/dist/commands/{secret.js → env/secret.js} +6 -47
  35. package/dist/commands/events.js +4 -4
  36. package/dist/commands/feedback-cli.js +18 -34
  37. package/dist/commands/graph/graph-cli.js +132 -0
  38. package/dist/commands/{graph.js → graph/graph.js} +22 -16
  39. package/dist/commands/health/checks.js +279 -0
  40. package/dist/commands/health/html-report.js +448 -0
  41. package/dist/commands/health.js +189 -266
  42. package/dist/commands/{consolidate.js → improve/consolidate.js} +63 -38
  43. package/dist/commands/{distill-promotion-policy.js → improve/distill-promotion-policy.js} +3 -3
  44. package/dist/commands/{distill.js → improve/distill.js} +39 -18
  45. package/dist/commands/{eval-cases.js → improve/eval-cases.js} +1 -1
  46. package/dist/commands/{extract-cli.js → improve/extract-cli.js} +4 -4
  47. package/dist/commands/{extract-prompt.js → improve/extract-prompt.js} +2 -2
  48. package/dist/commands/{extract.js → improve/extract.js} +221 -26
  49. package/dist/commands/{improve-auto-accept.js → improve/improve-auto-accept.js} +30 -4
  50. package/dist/commands/{improve-cli.js → improve/improve-cli.js} +44 -22
  51. package/dist/commands/{improve-profiles.js → improve/improve-profiles.js} +13 -7
  52. package/dist/commands/{improve-result-file.js → improve/improve-result-file.js} +1 -1
  53. package/dist/commands/{improve.js → improve/improve.js} +672 -292
  54. package/dist/{core → commands/improve/memory}/memory-belief.js +2 -2
  55. package/dist/{core → commands/improve/memory}/memory-contradiction-detect.js +5 -5
  56. package/dist/{core → commands/improve/memory}/memory-improve.js +4 -4
  57. package/dist/commands/improve/reflect-noise.js +0 -0
  58. package/dist/commands/{reflect.js → improve/reflect.js} +58 -28
  59. package/dist/commands/improve/session-asset.js +248 -0
  60. package/dist/commands/lint/agent-linter.js +1 -1
  61. package/dist/commands/lint/base-linter.js +55 -37
  62. package/dist/commands/lint/command-linter.js +1 -1
  63. package/dist/commands/lint/default-linter.js +1 -1
  64. package/dist/commands/lint/env-key-rules.js +1 -1
  65. package/dist/commands/lint/index.js +19 -25
  66. package/dist/commands/lint/knowledge-linter.js +1 -1
  67. package/dist/commands/lint/memory-linter.js +1 -1
  68. package/dist/commands/lint/registry.js +8 -8
  69. package/dist/commands/lint/skill-linter.js +1 -1
  70. package/dist/commands/lint/task-linter.js +1 -1
  71. package/dist/commands/lint/workflow-linter.js +1 -1
  72. package/dist/commands/lint.js +1 -1
  73. package/dist/commands/observability-cli.js +244 -0
  74. package/dist/commands/proposal/drain-policies.js +3 -3
  75. package/dist/commands/proposal/drain.js +87 -15
  76. package/dist/commands/proposal/proposal-cli.js +490 -0
  77. package/dist/commands/{proposal.js → proposal/proposal.js} +17 -6
  78. package/dist/commands/{propose.js → proposal/propose.js} +11 -11
  79. package/dist/{core → commands/proposal/validators}/proposal-quality-validators.js +8 -3
  80. package/dist/{core → commands/proposal/validators}/proposal-validators.js +5 -5
  81. package/dist/{core → commands/proposal/validators}/proposals.js +374 -345
  82. package/dist/commands/{curate.js → read/curate.js} +7 -7
  83. package/dist/commands/{knowledge.js → read/knowledge.js} +22 -9
  84. package/dist/commands/{registry-search.js → read/registry-search.js} +5 -5
  85. package/dist/commands/{remember-cli.js → read/remember-cli.js} +15 -7
  86. package/dist/commands/read/search-cli.js +207 -0
  87. package/dist/commands/{search.js → read/search.js} +22 -27
  88. package/dist/commands/{show.js → read/show.js} +31 -45
  89. package/dist/commands/registry-cli.js +8 -8
  90. package/dist/commands/remember.js +14 -10
  91. package/dist/commands/sources/add-cli.js +293 -0
  92. package/dist/commands/{history.js → sources/history.js} +27 -25
  93. package/dist/commands/{info.js → sources/info.js} +6 -6
  94. package/dist/commands/{init.js → sources/init.js} +6 -6
  95. package/dist/commands/{installed-stashes.js → sources/installed-stashes.js} +12 -12
  96. package/dist/commands/{migration-help.js → sources/migration-help.js} +3 -2
  97. package/dist/commands/{schema-repair.js → sources/schema-repair.js} +8 -8
  98. package/dist/commands/{self-update.js → sources/self-update.js} +10 -9
  99. package/dist/commands/{source-add.js → sources/source-add.js} +10 -10
  100. package/dist/commands/{source-clone.js → sources/source-clone.js} +7 -7
  101. package/dist/commands/{source-manage.js → sources/source-manage.js} +4 -4
  102. package/dist/commands/sources/sources-cli.js +305 -0
  103. package/dist/commands/sources/stash-cli.js +219 -0
  104. package/dist/commands/{stash-skeleton.js → sources/stash-skeleton.js} +2 -1
  105. package/dist/commands/tasks/default-tasks.js +173 -0
  106. package/dist/commands/tasks/tasks-cli.js +210 -0
  107. package/dist/commands/{tasks.js → tasks/tasks.js} +14 -14
  108. package/dist/commands/wiki-cli.js +307 -0
  109. package/dist/commands/workflow-cli.js +329 -0
  110. package/dist/core/action-contributors.js +1 -1
  111. package/dist/core/assert.js +40 -0
  112. package/dist/core/asset/asset-create.js +54 -0
  113. package/dist/core/{asset-ref.js → asset/asset-ref.js} +21 -4
  114. package/dist/core/{asset-registry.js → asset/asset-registry.js} +3 -3
  115. package/dist/core/{asset-spec.js → asset/asset-spec.js} +17 -31
  116. package/dist/core/{markdown.js → asset/markdown.js} +1 -1
  117. package/dist/core/{stash-meta.js → asset/stash-meta.js} +1 -1
  118. package/dist/core/best-effort.js +64 -0
  119. package/dist/core/common.js +32 -18
  120. package/dist/core/{config-io.js → config/config-io.js} +29 -19
  121. package/dist/core/{config-migration.js → config/config-migration.js} +11 -9
  122. package/dist/core/{config-schema.js → config/config-schema.js} +50 -7
  123. package/dist/core/config/config-types.js +16 -0
  124. package/dist/core/{config-walker.js → config/config-walker.js} +2 -2
  125. package/dist/core/{config.js → config/config.js} +10 -8
  126. package/dist/core/env-secret-ref.js +90 -0
  127. package/dist/core/errors.js +13 -3
  128. package/dist/core/events.js +27 -4
  129. package/dist/core/file-lock.js +1 -1
  130. package/dist/core/improve-types.js +48 -0
  131. package/dist/core/lesson-lint.js +2 -2
  132. package/dist/core/logs-db.js +304 -0
  133. package/dist/core/paths.js +2 -2
  134. package/dist/core/ripgrep/install.js +2 -2
  135. package/dist/core/ripgrep/resolve.js +2 -2
  136. package/dist/core/state-db.js +195 -60
  137. package/dist/core/text-truncation.js +148 -0
  138. package/dist/core/time.js +1 -1
  139. package/dist/core/write-source.js +98 -85
  140. package/dist/indexer/{db-backup.js → db/db-backup.js} +9 -24
  141. package/dist/indexer/{db.js → db/db.js} +128 -118
  142. package/dist/indexer/{graph-db.js → db/graph-db.js} +9 -4
  143. package/dist/indexer/{llm-cache.js → db/llm-cache.js} +15 -12
  144. package/dist/indexer/ensure-index.js +4 -4
  145. package/dist/indexer/{graph-boost.js → graph/graph-boost.js} +1 -1
  146. package/dist/indexer/{graph-extraction.js → graph/graph-extraction.js} +55 -13
  147. package/dist/indexer/indexer.js +37 -30
  148. package/dist/indexer/init.js +54 -0
  149. package/dist/indexer/manifest.js +10 -10
  150. package/dist/indexer/{memory-inference.js → passes/memory-inference.js} +141 -33
  151. package/dist/indexer/{metadata-contributors.js → passes/metadata-contributors.js} +10 -8
  152. package/dist/indexer/{metadata.js → passes/metadata.js} +15 -19
  153. package/dist/indexer/{staleness-detect.js → passes/staleness-detect.js} +53 -12
  154. package/dist/indexer/{db-search.js → search/db-search.js} +28 -16
  155. package/dist/indexer/{ranking-contributors.js → search/ranking-contributors.js} +1 -1
  156. package/dist/indexer/{ranking.js → search/ranking.js} +2 -2
  157. package/dist/indexer/{search-hit-enrichers.js → search/search-hit-enrichers.js} +3 -3
  158. package/dist/indexer/{search-source.js → search/search-source.js} +8 -8
  159. package/dist/indexer/{semantic-status.js → search/semantic-status.js} +3 -3
  160. package/dist/indexer/usage/unmigrated-vaults-guard.js +94 -0
  161. package/dist/indexer/{usage-events.js → usage/usage-events.js} +32 -0
  162. package/dist/indexer/{file-context.js → walk/file-context.js} +10 -15
  163. package/dist/indexer/{matchers.js → walk/matchers.js} +13 -9
  164. package/dist/indexer/{path-resolver.js → walk/path-resolver.js} +6 -6
  165. package/dist/indexer/{project-context.js → walk/project-context.js} +1 -1
  166. package/dist/indexer/{walker.js → walk/walker.js} +4 -3
  167. package/dist/integrations/agent/builder-shared.js +39 -0
  168. package/dist/integrations/agent/builders.js +14 -81
  169. package/dist/integrations/agent/config.js +6 -4
  170. package/dist/integrations/agent/detect.js +1 -1
  171. package/dist/integrations/agent/index.js +23 -8
  172. package/dist/integrations/agent/prompts.js +2 -3
  173. package/dist/integrations/agent/runner.js +22 -3
  174. package/dist/integrations/agent/spawn.js +9 -10
  175. package/dist/integrations/harnesses/claude/agent-builder.js +48 -0
  176. package/dist/integrations/harnesses/claude/config-import.js +70 -0
  177. package/dist/integrations/harnesses/claude/index.js +64 -0
  178. package/dist/integrations/{session-logs/providers/claude-code.js → harnesses/claude/session-log.js} +32 -5
  179. package/dist/integrations/harnesses/index.js +144 -0
  180. package/dist/integrations/harnesses/opencode/agent-builder.js +43 -0
  181. package/dist/integrations/harnesses/opencode/config-import.js +82 -0
  182. package/dist/integrations/harnesses/opencode/index.js +59 -0
  183. package/dist/integrations/{session-logs/providers/opencode.js → harnesses/opencode/session-log.js} +1 -1
  184. package/dist/integrations/harnesses/opencode-sdk/index.js +49 -0
  185. package/dist/integrations/harnesses/opencode-sdk/sdk-runner.js +234 -0
  186. package/dist/integrations/harnesses/types.js +43 -0
  187. package/dist/integrations/lockfile.js +7 -16
  188. package/dist/integrations/session-logs/index.js +82 -9
  189. package/dist/llm/call-ai.js +4 -4
  190. package/dist/llm/client.js +146 -6
  191. package/dist/llm/embedder.js +6 -6
  192. package/dist/llm/embedders/local.js +9 -22
  193. package/dist/llm/embedders/remote.js +2 -2
  194. package/dist/llm/embedders/types.js +1 -1
  195. package/dist/llm/graph-extract.js +31 -12
  196. package/dist/llm/index-passes.js +1 -1
  197. package/dist/llm/memory-infer.js +12 -5
  198. package/dist/llm/metadata-enhance.js +2 -2
  199. package/dist/llm/usage-persist.js +77 -0
  200. package/dist/llm/usage-telemetry.js +103 -0
  201. package/dist/output/context.js +9 -46
  202. package/dist/output/html-render.js +73 -0
  203. package/dist/output/renderers.js +88 -58
  204. package/dist/output/shapes/curate.js +7 -3
  205. package/dist/output/shapes/distill.js +7 -3
  206. package/dist/output/shapes/env-list.js +18 -16
  207. package/dist/output/shapes/events.js +5 -4
  208. package/dist/output/shapes/helpers.js +19 -5
  209. package/dist/output/shapes/history.js +7 -3
  210. package/dist/output/shapes/passthrough.js +8 -11
  211. package/dist/output/shapes/{proposal-accept.js → proposal/accept.js} +7 -3
  212. package/dist/output/shapes/{proposal-diff.js → proposal/diff.js} +7 -3
  213. package/dist/output/shapes/{proposal-list.js → proposal/list.js} +7 -3
  214. package/dist/output/shapes/{proposal-producer.js → proposal/producer.js} +5 -4
  215. package/dist/output/shapes/{proposal-reject.js → proposal/reject.js} +7 -3
  216. package/dist/output/shapes/{proposal-show.js → proposal/show.js} +7 -3
  217. package/dist/output/shapes/registry-search.js +7 -3
  218. package/dist/output/shapes/registry.js +12 -0
  219. package/dist/output/shapes/search.js +7 -3
  220. package/dist/output/shapes/secret-list.js +18 -16
  221. package/dist/output/shapes/show.js +7 -3
  222. package/dist/output/shapes.js +55 -30
  223. package/dist/output/text/add.js +2 -3
  224. package/dist/output/text/clone.js +2 -3
  225. package/dist/output/text/config.js +2 -3
  226. package/dist/output/text/curate.js +4 -3
  227. package/dist/output/text/distill.js +2 -3
  228. package/dist/output/text/enable-disable.js +5 -4
  229. package/dist/output/text/env.js +13 -0
  230. package/dist/output/text/events.js +5 -4
  231. package/dist/output/text/feedback.js +4 -3
  232. package/dist/output/text/helpers.js +123 -40
  233. package/dist/output/text/history.js +2 -3
  234. package/dist/output/text/import.js +2 -3
  235. package/dist/output/text/index.js +2 -3
  236. package/dist/output/text/info.js +2 -3
  237. package/dist/output/text/init.js +2 -3
  238. package/dist/output/text/list.js +2 -3
  239. package/dist/output/text/proposal/producer.js +9 -0
  240. package/dist/output/text/proposal/proposal.js +13 -0
  241. package/dist/output/text/registry-commands.js +8 -7
  242. package/dist/output/text/registry.js +12 -0
  243. package/dist/output/text/remember.js +4 -3
  244. package/dist/output/text/remove.js +2 -3
  245. package/dist/output/text/save.js +2 -3
  246. package/dist/output/text/search.js +4 -3
  247. package/dist/output/text/show.js +4 -3
  248. package/dist/output/text/update.js +2 -3
  249. package/dist/output/text/upgrade.js +2 -3
  250. package/dist/output/text/wiki.js +12 -11
  251. package/dist/output/text/workflow.js +12 -10
  252. package/dist/output/text.js +66 -32
  253. package/dist/registry/build-index.js +11 -10
  254. package/dist/registry/factory.js +1 -1
  255. package/dist/registry/origin-resolve.js +1 -1
  256. package/dist/registry/providers/index.js +2 -2
  257. package/dist/registry/providers/skills-sh.js +91 -72
  258. package/dist/registry/providers/static-index.js +75 -52
  259. package/dist/registry/resolve.js +3 -3
  260. package/dist/runtime.js +242 -0
  261. package/dist/scripts/migrate-storage.js +1654 -683
  262. package/dist/scripts/migrations/import-fs-improve-runs-to-db.js +254 -168
  263. package/dist/setup/detect.js +311 -9
  264. package/dist/setup/harness-config-import.js +6 -120
  265. package/dist/setup/setup.js +454 -43
  266. package/dist/sources/include.js +1 -1
  267. package/dist/sources/provider-factory.js +2 -2
  268. package/dist/sources/providers/filesystem.js +3 -3
  269. package/dist/sources/providers/git.js +9 -9
  270. package/dist/sources/providers/index.js +4 -4
  271. package/dist/sources/providers/npm.js +6 -6
  272. package/dist/sources/providers/provider-utils.js +13 -20
  273. package/dist/sources/providers/sync-from-ref.js +5 -5
  274. package/dist/sources/providers/tar-utils.js +2 -2
  275. package/dist/sources/providers/website.js +2 -2
  276. package/dist/sources/resolve.js +5 -5
  277. package/dist/sources/website-ingest.js +5 -5
  278. package/dist/storage/database.js +102 -0
  279. package/dist/storage/engines/sqlite-migrations.js +42 -0
  280. package/dist/storage/locations.js +25 -0
  281. package/dist/storage/repositories/index-db.js +43 -0
  282. package/dist/storage/repositories/workflow-runs-repository.js +141 -0
  283. package/dist/tasks/backends/cron.js +4 -4
  284. package/dist/tasks/backends/exec-utils.js +32 -0
  285. package/dist/tasks/backends/index.js +3 -3
  286. package/dist/tasks/backends/launchd.js +7 -14
  287. package/dist/tasks/backends/schtasks.js +7 -16
  288. package/dist/tasks/embedded.js +71 -0
  289. package/dist/tasks/parser.js +2 -2
  290. package/dist/tasks/resolveAkmBin.js +1 -1
  291. package/dist/tasks/runner.js +127 -31
  292. package/dist/tasks/schedule.js +1 -1
  293. package/dist/tasks/validator.js +7 -7
  294. package/dist/text-import-hook.mjs +51 -0
  295. package/dist/version.js +2 -1
  296. package/dist/wiki/wiki.js +7 -7
  297. package/dist/workflows/{authoring.js → authoring/authoring.js} +6 -6
  298. package/dist/workflows/{scope-key.js → authoring/scope-key.js} +1 -1
  299. package/dist/workflows/cli.js +1 -1
  300. package/dist/workflows/db.js +54 -32
  301. package/dist/workflows/parser.js +4 -4
  302. package/dist/workflows/renderer.js +5 -5
  303. package/dist/workflows/runtime/agent-identity.js +56 -0
  304. package/dist/workflows/runtime/checkin.js +57 -0
  305. package/dist/workflows/{runs.js → runtime/runs.js} +197 -101
  306. package/dist/workflows/validate-summary.js +82 -0
  307. package/docs/README.md +1 -1
  308. package/docs/data-and-telemetry.md +6 -6
  309. package/package.json +17 -8
  310. package/dist/commands/add-cli.js +0 -279
  311. package/dist/commands/env.js +0 -213
  312. package/dist/integrations/agent/sdk-runner.js +0 -126
  313. package/dist/output/shapes/vault-list.js +0 -19
  314. package/dist/output/text/proposal-producer.js +0 -8
  315. package/dist/output/text/proposal.js +0 -12
  316. package/dist/output/text/vault.js +0 -16
  317. /package/dist/core/{asset-serialize.js → asset/asset-serialize.js} +0 -0
  318. /package/dist/core/{frontmatter.js → asset/frontmatter.js} +0 -0
  319. /package/dist/core/{config-sources.js → config/config-sources.js} +0 -0
  320. /package/dist/indexer/{graph-dedup.js → graph/graph-dedup.js} +0 -0
  321. /package/dist/{core/config-types.js → indexer/passes/pass-context.js} +0 -0
  322. /package/dist/indexer/{search-fields.js → search/search-fields.js} +0 -0
  323. /package/dist/indexer/{index-context.js → walk/index-context.js} +0 -0
  324. /package/dist/workflows/{document-cache.js → runtime/document-cache.js} +0 -0
@@ -384,7 +384,73 @@ var require_main = __commonJS((exports, module) => {
384
384
  module.exports = DotenvModule;
385
385
  });
386
386
 
387
- // src/commands/env.ts
387
+ // src/core/errors.ts
388
+ function isTestIsolationError(err) {
389
+ return err instanceof ConfigError && err.code === "TEST_ISOLATION_MISSING";
390
+ }
391
+ function rethrowIfTestIsolationError(err) {
392
+ if (isTestIsolationError(err)) {
393
+ throw err;
394
+ }
395
+ }
396
+ var CONFIG_HINTS, USAGE_HINTS, AkmError, ConfigError, UsageError;
397
+ var init_errors = __esm(() => {
398
+ CONFIG_HINTS = {
399
+ STASH_DIR_NOT_FOUND: "Run `akm setup` to create and configure your stash, or set stashDir in your config.",
400
+ STASH_DIR_NOT_A_DIRECTORY: "The configured stashDir exists but isn't a directory. Update stashDir to point at a folder.",
401
+ STASH_DIR_UNREADABLE: "Check the path exists and your user has read permission, or update stashDir.",
402
+ EMBEDDING_NOT_CONFIGURED: 'Run `akm config set embedding \'{"endpoint":"...","model":"..."}\'` to enable embeddings.',
403
+ LLM_NOT_CONFIGURED: 'Run `akm setup` or `akm config set profiles.llm.default \'{"endpoint":"...","model":"..."}\' to configure an LLM profile.',
404
+ TEST_ISOLATION_MISSING: "Under bun test, when AKM_STASH_DIR is set you MUST also set XDG_DATA_HOME (or AKM_DATA_DIR) and XDG_STATE_HOME (or AKM_STATE_DIR) to temp directories so the test does not touch the developer's real ~/.local/share/akm or ~/.local/state/akm.",
405
+ SETUP_TMP_STASH_REFUSED: "Use a persistent directory, or set AKM_FORCE_SETUP_TMP_STASH=1 to opt in to a sandboxed setup (setup also pre-sets AKM_STASH_DIR so config and cache writes auto-isolate into $stashDir/.akm/ \u2014 host config is preserved).",
406
+ UNSAFE_STASH_DIR: "Choose a path inside your home directory (e.g. ~/akm) or another empty workspace. The stash directory cannot be the filesystem root, your home directory itself, or a sensitive system path like /etc, /var, ~/.config, or ~/.ssh."
407
+ };
408
+ USAGE_HINTS = {
409
+ INVALID_FLAG_VALUE: "Run `akm <command> --help` to see accepted values.",
410
+ INVALID_SOURCE_VALUE: "Pick one of: stash, registry, both.",
411
+ INVALID_FORMAT_VALUE: "Pick one of: json, jsonl, text, yaml.",
412
+ INVALID_DETAIL_VALUE: "Pick one of: brief, normal, full. For agent/summary projections use --shape.",
413
+ INVALID_SHAPE_VALUE: "Pick one of: human, agent, summary (summary is only valid on `akm show`).",
414
+ INVALID_JSON_CONFIG_VALUE: `Quote JSON values in your shell, for example: akm config set embedding '{"endpoint":"http://localhost:11434/v1/embeddings","model":"nomic-embed-text"}'.`,
415
+ MISSING_OR_AMBIGUOUS_TARGET: "Use `akm update --all` or pass a target like `akm update npm:@scope/pkg` (not both).",
416
+ TARGET_NOT_UPDATABLE: "Run `akm list` to view your sources, then retry with one of those values.",
417
+ MISSING_REQUIRED_ARGUMENT: "Refs use the form type:name, e.g. `akm show skill:deploy` or `akm show knowledge:guide.md`."
418
+ };
419
+ AkmError = class AkmError extends Error {
420
+ };
421
+ ConfigError = class ConfigError extends AkmError {
422
+ kind = "config";
423
+ code;
424
+ _hint;
425
+ constructor(msg, code = "INVALID_CONFIG_FILE", hint) {
426
+ super(msg);
427
+ this.name = "ConfigError";
428
+ this.code = code;
429
+ this._hint = hint;
430
+ Object.setPrototypeOf(this, new.target.prototype);
431
+ }
432
+ hint() {
433
+ return this._hint ?? CONFIG_HINTS[this.code];
434
+ }
435
+ };
436
+ UsageError = class UsageError extends AkmError {
437
+ kind = "usage";
438
+ code;
439
+ _hint;
440
+ constructor(msg, code = "INVALID_FLAG_VALUE", hint) {
441
+ super(msg);
442
+ this.name = "UsageError";
443
+ this.code = code;
444
+ this._hint = hint;
445
+ Object.setPrototypeOf(this, new.target.prototype);
446
+ }
447
+ hint() {
448
+ return this._hint ?? USAGE_HINTS[this.code];
449
+ }
450
+ };
451
+ });
452
+
453
+ // src/commands/env/env.ts
388
454
  import fs from "fs";
389
455
  function scanKeys(text) {
390
456
  const keys = [];
@@ -420,11 +486,12 @@ function listKeys(envPath) {
420
486
  var import_dotenv, ASSIGN_RE;
421
487
  var init_env = __esm(() => {
422
488
  init_common();
489
+ init_errors();
423
490
  import_dotenv = __toESM(require_main(), 1);
424
491
  ASSIGN_RE = /^\s*(?:export\s+)?([A-Za-z_][A-Za-z0-9_]*)\s*=/;
425
492
  });
426
493
 
427
- // src/core/frontmatter.ts
494
+ // src/core/asset/frontmatter.ts
428
495
  function parseFrontmatter(raw) {
429
496
  const parsedBlock = parseFrontmatterBlock(raw);
430
497
  if (!parsedBlock) {
@@ -572,7 +639,7 @@ function parseYamlScalar(value) {
572
639
  return value;
573
640
  }
574
641
 
575
- // src/core/markdown.ts
642
+ // src/core/asset/markdown.ts
576
643
  function parseMarkdownToc(content) {
577
644
  const lines = content.split(/\r?\n/);
578
645
  const headings = [];
@@ -592,15 +659,16 @@ function parseMarkdownToc(content) {
592
659
  }
593
660
  var init_markdown = () => {};
594
661
 
595
- // src/indexer/file-context.ts
596
- var renderers;
597
- var init_file_context = __esm(() => {
598
- init_common();
599
- renderers = new Map;
600
- });
601
-
602
662
  // src/core/warn.ts
603
663
  import fs2 from "fs";
664
+ function isVerbose() {
665
+ const env = process.env.AKM_VERBOSE?.trim().toLowerCase();
666
+ if (env === "1" || env === "true" || env === "yes" || env === "on")
667
+ return true;
668
+ if (env === "0" || env === "false" || env === "no" || env === "off")
669
+ return false;
670
+ return verbose;
671
+ }
604
672
  function appendToLogFile(level, args) {
605
673
  if (!logFilePath)
606
674
  return;
@@ -628,10 +696,22 @@ function error(...args) {
628
696
  console.error(...args);
629
697
  }
630
698
  }
631
- var quiet = false, logFilePath;
699
+ function warnVerbose(...args) {
700
+ if (isVerbose()) {
701
+ warn(...args);
702
+ }
703
+ }
704
+ var quiet = false, verbose = false, logFilePath;
632
705
  var init_warn = () => {};
633
706
 
634
- // src/indexer/metadata-contributors.ts
707
+ // src/indexer/walk/file-context.ts
708
+ var renderers;
709
+ var init_file_context = __esm(() => {
710
+ init_common();
711
+ renderers = new Map;
712
+ });
713
+
714
+ // src/indexer/passes/metadata-contributors.ts
635
715
  function registerMetadataContributor(contributor) {
636
716
  contributors.push(contributor);
637
717
  }
@@ -640,7 +720,7 @@ var init_metadata_contributors = __esm(() => {
640
720
  contributors = [];
641
721
  });
642
722
 
643
- // src/indexer/metadata.ts
723
+ // src/indexer/passes/metadata.ts
644
724
  import fs3 from "fs";
645
725
  function extractDescriptionFromComments(filePath) {
646
726
  let content;
@@ -692,69 +772,7 @@ var init_metadata = __esm(() => {
692
772
  WIKI_INFRA_FILES = new Set(["schema.md", "index.md", "log.md"]);
693
773
  });
694
774
 
695
- // src/core/errors.ts
696
- function isTestIsolationError(err) {
697
- return err instanceof ConfigError && err.code === "TEST_ISOLATION_MISSING";
698
- }
699
- function rethrowIfTestIsolationError(err) {
700
- if (isTestIsolationError(err)) {
701
- throw err;
702
- }
703
- }
704
- var CONFIG_HINTS, USAGE_HINTS, ConfigError, UsageError;
705
- var init_errors = __esm(() => {
706
- CONFIG_HINTS = {
707
- STASH_DIR_NOT_FOUND: "Run `akm setup` to create and configure your stash, or set stashDir in your config.",
708
- STASH_DIR_NOT_A_DIRECTORY: "The configured stashDir exists but isn't a directory. Update stashDir to point at a folder.",
709
- STASH_DIR_UNREADABLE: "Check the path exists and your user has read permission, or update stashDir.",
710
- EMBEDDING_NOT_CONFIGURED: 'Run `akm config set embedding \'{"endpoint":"...","model":"..."}\'` to enable embeddings.',
711
- LLM_NOT_CONFIGURED: 'Run `akm setup` or `akm config set profiles.llm.default \'{"endpoint":"...","model":"..."}\' to configure an LLM profile.',
712
- TEST_ISOLATION_MISSING: "Under bun test, when AKM_STASH_DIR is set you MUST also set XDG_DATA_HOME (or AKM_DATA_DIR) and XDG_STATE_HOME (or AKM_STATE_DIR) to temp directories so the test does not touch the developer's real ~/.local/share/akm or ~/.local/state/akm.",
713
- SETUP_TMP_STASH_REFUSED: "Use a persistent directory, or set AKM_FORCE_SETUP_TMP_STASH=1 to opt in to a sandboxed setup (setup also pre-sets AKM_STASH_DIR so config and cache writes auto-isolate into $stashDir/.akm/ \u2014 host config is preserved).",
714
- UNSAFE_STASH_DIR: "Choose a path inside your home directory (e.g. ~/akm) or another empty workspace. The stash directory cannot be the filesystem root, your home directory itself, or a sensitive system path like /etc, /var, ~/.config, or ~/.ssh."
715
- };
716
- USAGE_HINTS = {
717
- INVALID_FLAG_VALUE: "Run `akm <command> --help` to see accepted values.",
718
- INVALID_SOURCE_VALUE: "Pick one of: stash, registry, both.",
719
- INVALID_FORMAT_VALUE: "Pick one of: json, jsonl, text, yaml.",
720
- INVALID_DETAIL_VALUE: "Pick one of: brief, normal, full. For agent/summary projections use --shape.",
721
- INVALID_SHAPE_VALUE: "Pick one of: human, agent, summary (summary is only valid on `akm show`).",
722
- INVALID_JSON_CONFIG_VALUE: `Quote JSON values in your shell, for example: akm config set embedding '{"endpoint":"http://localhost:11434/v1/embeddings","model":"nomic-embed-text"}'.`,
723
- MISSING_OR_AMBIGUOUS_TARGET: "Use `akm update --all` or pass a target like `akm update npm:@scope/pkg` (not both).",
724
- TARGET_NOT_UPDATABLE: "Run `akm list` to view your sources, then retry with one of those values.",
725
- MISSING_REQUIRED_ARGUMENT: "Refs use the form type:name, e.g. `akm show skill:deploy` or `akm show knowledge:guide.md`."
726
- };
727
- ConfigError = class ConfigError extends Error {
728
- code;
729
- _hint;
730
- constructor(msg, code = "INVALID_CONFIG_FILE", hint) {
731
- super(msg);
732
- this.name = "ConfigError";
733
- this.code = code;
734
- this._hint = hint;
735
- Object.setPrototypeOf(this, new.target.prototype);
736
- }
737
- hint() {
738
- return this._hint ?? CONFIG_HINTS[this.code];
739
- }
740
- };
741
- UsageError = class UsageError extends Error {
742
- code;
743
- _hint;
744
- constructor(msg, code = "INVALID_FLAG_VALUE", hint) {
745
- super(msg);
746
- this.name = "UsageError";
747
- this.code = code;
748
- this._hint = hint;
749
- Object.setPrototypeOf(this, new.target.prototype);
750
- }
751
- hint() {
752
- return this._hint ?? USAGE_HINTS[this.code];
753
- }
754
- };
755
- });
756
-
757
- // src/core/asset-ref.ts
775
+ // src/core/asset/asset-ref.ts
758
776
  import path from "path";
759
777
  function parseAssetRef(ref) {
760
778
  const trimmed = ref.trim();
@@ -775,6 +793,9 @@ function parseAssetRef(ref) {
775
793
  }
776
794
  const rawType = body.slice(0, colon);
777
795
  const rawName = body.slice(colon + 1);
796
+ if (rawType === "vault") {
797
+ throw new UsageError("The `vault` asset type was removed in 0.9.0 \u2014 use `env:` (whole .env config) or `secret:` (a single value).", "MISSING_REQUIRED_ARGUMENT");
798
+ }
778
799
  const resolvedType = TYPE_ALIASES[rawType] ?? rawType;
779
800
  if (!isAssetType(resolvedType)) {
780
801
  throw new UsageError(`Invalid asset type: "${rawType}".`, "MISSING_REQUIRED_ARGUMENT");
@@ -813,15 +834,6 @@ var init_asset_ref = __esm(() => {
813
834
  };
814
835
  });
815
836
 
816
- // src/workflows/document-cache.ts
817
- function cacheWorkflowDocument(entry, doc) {
818
- cache.set(entry, doc);
819
- }
820
- var cache;
821
- var init_document_cache = __esm(() => {
822
- cache = new WeakMap;
823
- });
824
-
825
837
  // node_modules/yaml/dist/nodes/identity.js
826
838
  var require_identity = __commonJS((exports) => {
827
839
  var ALIAS = Symbol.for("yaml.alias");
@@ -8192,6 +8204,15 @@ var init_parser = __esm(() => {
8192
8204
  BULLET_LINE = /^[-*]\s+(.+)$/;
8193
8205
  });
8194
8206
 
8207
+ // src/workflows/runtime/document-cache.ts
8208
+ function cacheWorkflowDocument(entry, doc) {
8209
+ cache.set(entry, doc);
8210
+ }
8211
+ var cache;
8212
+ var init_document_cache = __esm(() => {
8213
+ cache = new WeakMap;
8214
+ });
8215
+
8195
8216
  // src/workflows/renderer.ts
8196
8217
  function shellQuote(value) {
8197
8218
  return `'${value.replace(/'/g, `'\\''`)}'`;
@@ -8212,8 +8233,8 @@ var init_renderer = __esm(() => {
8212
8233
  init_asset_ref();
8213
8234
  init_errors();
8214
8235
  init_metadata_contributors();
8215
- init_document_cache();
8216
8236
  init_parser();
8237
+ init_document_cache();
8217
8238
  registerMetadataContributor({
8218
8239
  name: "workflow-document-metadata",
8219
8240
  appliesTo: ({ rendererName }) => rendererName === "workflow-md",
@@ -8242,6 +8263,24 @@ var init_renderer = __esm(() => {
8242
8263
  });
8243
8264
 
8244
8265
  // src/output/renderers.ts
8266
+ function applySessionMetadata(entry, ctx) {
8267
+ try {
8268
+ const fm = applyFrontmatterDescriptionAndTags(entry, ctx);
8269
+ entry.tags = Array.from(new Set([...entry.tags ?? [], "session"]));
8270
+ const hints = new Set(entry.searchHints ?? []);
8271
+ const harness = asNonEmptyString(fm.harness);
8272
+ if (harness)
8273
+ hints.add(`harness:${harness}`);
8274
+ const project = asNonEmptyString(fm.project);
8275
+ if (project)
8276
+ hints.add(`project:${project}`);
8277
+ const logPath = asNonEmptyString(fm.log_path);
8278
+ if (logPath)
8279
+ hints.add(`log_path:${logPath}`);
8280
+ if (hints.size > 0)
8281
+ entry.searchHints = Array.from(hints).filter(Boolean);
8282
+ } catch {}
8283
+ }
8245
8284
  function applyTocMetadata(entry, ctx) {
8246
8285
  try {
8247
8286
  const toc = parseMarkdownToc(ctx.content());
@@ -8313,18 +8352,6 @@ function applyScriptMetadata(entry, ctx) {
8313
8352
  entry.confidence = 0.7;
8314
8353
  }
8315
8354
  }
8316
- function applyVaultMetadata(entry, ctx) {
8317
- const { keys, comments } = listKeys(ctx.absPath);
8318
- if (comments.length > 0 && !entry.description) {
8319
- entry.description = comments.join(" ").slice(0, 500);
8320
- entry.source = "comments";
8321
- entry.confidence = 0.7;
8322
- }
8323
- if (keys.length > 0) {
8324
- entry.searchHints = keys;
8325
- }
8326
- entry.tags = Array.from(new Set([...entry.tags ?? [], "vault", "secrets"]));
8327
- }
8328
8355
  function applyEnvMetadata(entry, ctx) {
8329
8356
  const { keys, comments } = listKeys(ctx.absPath);
8330
8357
  if (comments.length > 0 && !entry.description) {
@@ -8360,11 +8387,11 @@ function applyTaskMetadata(entry, ctx) {
8360
8387
  }
8361
8388
  var init_renderers = __esm(() => {
8362
8389
  init_env();
8363
- init_common();
8364
8390
  init_markdown();
8365
- init_file_context();
8391
+ init_common();
8366
8392
  init_metadata();
8367
8393
  init_metadata_contributors();
8394
+ init_file_context();
8368
8395
  init_renderer();
8369
8396
  registerMetadataContributor({
8370
8397
  name: "toc-metadata",
@@ -8386,11 +8413,6 @@ var init_renderers = __esm(() => {
8386
8413
  appliesTo: ({ rendererName }) => rendererName === "script-source",
8387
8414
  contribute: (entry, ctx) => applyScriptMetadata(entry, ctx.renderContext)
8388
8415
  });
8389
- registerMetadataContributor({
8390
- name: "vault-secret-metadata",
8391
- appliesTo: ({ rendererName }) => rendererName === "vault-env",
8392
- contribute: (entry, ctx) => applyVaultMetadata(entry, ctx.renderContext)
8393
- });
8394
8416
  registerMetadataContributor({
8395
8417
  name: "env-file-metadata",
8396
8418
  appliesTo: ({ rendererName }) => rendererName === "env-file",
@@ -8406,20 +8428,28 @@ var init_renderers = __esm(() => {
8406
8428
  appliesTo: ({ rendererName }) => rendererName === "task-yaml",
8407
8429
  contribute: (entry, ctx) => applyTaskMetadata(entry, ctx.renderContext)
8408
8430
  });
8431
+ registerMetadataContributor({
8432
+ name: "session-md-metadata",
8433
+ appliesTo: ({ rendererName }) => rendererName === "session-md",
8434
+ contribute: (entry, ctx) => applySessionMetadata(entry, ctx.renderContext)
8435
+ });
8409
8436
  });
8410
8437
 
8411
- // src/core/asset-registry.ts
8438
+ // src/core/asset/asset-registry.ts
8412
8439
  var init_asset_registry = __esm(() => {
8413
8440
  init_renderers();
8414
8441
  });
8415
8442
 
8416
- // src/core/asset-spec.ts
8443
+ // src/core/asset/asset-spec.ts
8417
8444
  import path2 from "path";
8445
+ function getAssetTypes() {
8446
+ return Object.keys(ASSET_SPECS_INTERNAL);
8447
+ }
8418
8448
  var buildTaskAction = (ref) => `akm tasks show ${ref.replace(/^task:/, "")} -> inspect; akm tasks run <id> -> run now; akm tasks remove <id> -> unschedule`, markdownSpec, SCRIPT_EXTENSIONS, scriptSpec, ASSET_SPECS_INTERNAL, TYPE_DIRS;
8419
8449
  var init_asset_spec = __esm(() => {
8420
8450
  init_renderers();
8421
- init_asset_registry();
8422
8451
  init_common();
8452
+ init_asset_registry();
8423
8453
  markdownSpec = {
8424
8454
  isRelevantFile: (fileName) => path2.extname(fileName).toLowerCase() === ".md",
8425
8455
  toCanonicalName: (typeRoot, filePath) => {
@@ -8498,27 +8528,6 @@ var init_asset_spec = __esm(() => {
8498
8528
  rendererName: "env-file",
8499
8529
  actionBuilder: (ref) => `akm show ${ref} -> inspect key names; akm env run ${ref} -- <command> -> run with the whole .env injected (values never reach stdout); akm env export ${ref} --out <file> -> write a sourceable script to a file`
8500
8530
  },
8501
- vault: {
8502
- stashDir: "vaults",
8503
- isRelevantFile: (fileName) => fileName === ".env" || fileName.endsWith(".env"),
8504
- toCanonicalName: (typeRoot, filePath) => {
8505
- const rel = toPosix(path2.relative(typeRoot, filePath));
8506
- const fileName = path2.basename(rel);
8507
- if (fileName === ".env") {
8508
- const dir = path2.dirname(rel);
8509
- return dir === "." || dir === "" ? "default" : `${dir}/default`;
8510
- }
8511
- const stripped = rel.endsWith(".env") ? rel.slice(0, -4) : rel;
8512
- return stripped;
8513
- },
8514
- toAssetPath: (typeRoot, name) => {
8515
- if (name === "default")
8516
- return path2.join(typeRoot, ".env");
8517
- return path2.join(typeRoot, name.endsWith(".env") ? name : `${name}.env`);
8518
- },
8519
- rendererName: "vault-env",
8520
- actionBuilder: (ref) => `DEPRECATED (use env): akm show ${ref} -> inspect key names; akm env run ${ref} -- <command> -> run with injected env`
8521
- },
8522
8531
  secret: {
8523
8532
  stashDir: "secrets",
8524
8533
  isRelevantFile: (fileName) => !fileName.endsWith(".lock") && !fileName.endsWith(".sensitive"),
@@ -8552,6 +8561,12 @@ var init_asset_spec = __esm(() => {
8552
8561
  },
8553
8562
  rendererName: "task-yaml",
8554
8563
  actionBuilder: buildTaskAction
8564
+ },
8565
+ session: {
8566
+ stashDir: "sessions",
8567
+ ...markdownSpec,
8568
+ rendererName: "session-md",
8569
+ actionBuilder: (ref) => `akm show ${ref} -> read the session summary; follow the \`access\` frontmatter to open the raw log at \`log_path\``
8555
8570
  }
8556
8571
  };
8557
8572
  TYPE_DIRS = Object.fromEntries(Object.entries(ASSET_SPECS_INTERNAL).map(([type, spec]) => [type, spec.stashDir]));
@@ -8568,7 +8583,11 @@ function writeFileAtomic(target, content, mode) {
8568
8583
  const tmp = `${target}.tmp.${process.pid}.${crypto.randomBytes(8).toString("hex")}`;
8569
8584
  const fd = fs4.openSync(tmp, "w", mode ?? 384);
8570
8585
  try {
8571
- fs4.writeSync(fd, content);
8586
+ if (typeof content === "string") {
8587
+ fs4.writeSync(fd, content);
8588
+ } else {
8589
+ fs4.writeSync(fd, content);
8590
+ }
8572
8591
  try {
8573
8592
  fs4.fdatasyncSync(fd);
8574
8593
  } catch {}
@@ -8607,20 +8626,7 @@ var init_common = __esm(() => {
8607
8626
  init_asset_spec();
8608
8627
  init_errors();
8609
8628
  init_paths();
8610
- ASSET_TYPES = [
8611
- "skill",
8612
- "command",
8613
- "agent",
8614
- "knowledge",
8615
- "workflow",
8616
- "script",
8617
- "memory",
8618
- "env",
8619
- "vault",
8620
- "secret",
8621
- "wiki",
8622
- "lesson"
8623
- ];
8629
+ ASSET_TYPES = Object.freeze([...getAssetTypes()]);
8624
8630
  ASSET_TYPE_SET = new Set(ASSET_TYPES);
8625
8631
  IS_WINDOWS = process.platform === "win32";
8626
8632
  DEFAULT_RESPONSE_BYTE_CAP = 10 * 1024 * 1024;
@@ -8760,67 +8766,71 @@ var init_paths = __esm(() => {
8760
8766
  init_errors();
8761
8767
  });
8762
8768
 
8763
- // src/core/state-db.ts
8764
- var exports_state_db = {};
8765
- __export(exports_state_db, {
8766
- upsertTaskHistory: () => upsertTaskHistory,
8767
- upsertProposal: () => upsertProposal,
8768
- upsertExtractedSession: () => upsertExtractedSession,
8769
- shouldSkipAlreadyExtractedSession: () => shouldSkipAlreadyExtractedSession,
8770
- runMigrations: () => runMigrations,
8771
- recordImproveRun: () => recordImproveRun,
8772
- readStateEvents: () => readStateEvents,
8773
- queryTaskHistory: () => queryTaskHistory,
8774
- purgeOldImproveRuns: () => purgeOldImproveRuns,
8775
- purgeOldEvents: () => purgeOldEvents,
8776
- proposalToRowValues: () => proposalToRowValues,
8777
- proposalRowToProposal: () => proposalRowToProposal,
8778
- openStateDatabase: () => openStateDatabase,
8779
- listStateProposals: () => listStateProposals,
8780
- insertEvent: () => insertEvent,
8781
- importEventsJsonl: () => importEventsJsonl,
8782
- getTaskHistoryRuns: () => getTaskHistoryRuns,
8783
- getTaskHistory: () => getTaskHistory,
8784
- getStateProposal: () => getStateProposal,
8785
- getStateDbPath: () => getStateDbPath,
8786
- getExtractedSessionsMap: () => getExtractedSessionsMap,
8787
- getExtractedSession: () => getExtractedSession,
8788
- eventRowToEnvelope: () => eventRowToEnvelope,
8789
- computeImproveRunMetrics: () => computeImproveRunMetrics,
8790
- REGISTRY_INDEX_CACHE_DDL: () => REGISTRY_INDEX_CACHE_DDL
8791
- });
8792
- import { Database } from "bun:sqlite";
8793
- import fs5 from "fs";
8794
- import path5 from "path";
8795
- function getStateDbPath() {
8796
- return path5.join(getDataDir(), "state.db");
8797
- }
8798
- function openStateDatabase(dbPath) {
8799
- const resolvedPath = dbPath ?? getStateDbPath();
8800
- const dir = path5.dirname(resolvedPath);
8801
- if (!fs5.existsSync(dir)) {
8802
- fs5.mkdirSync(dir, { recursive: true });
8769
+ // src/storage/database.ts
8770
+ import { createRequire } from "module";
8771
+ function openDatabase(path5, opts) {
8772
+ if (isBun) {
8773
+ return openBunDatabase(path5, opts);
8803
8774
  }
8804
- const db = new Database(resolvedPath);
8805
- db.exec("PRAGMA journal_mode = WAL");
8806
- db.exec("PRAGMA foreign_keys = ON");
8807
- db.exec("PRAGMA busy_timeout = 5000");
8808
- runMigrations(db);
8775
+ return openNodeDatabase(path5, opts);
8776
+ }
8777
+ function openBunDatabase(path5, opts) {
8778
+ const { Database: BunDatabase } = loadBunSqlite();
8779
+ const db = opts ? new BunDatabase(path5, bunOptions(opts)) : new BunDatabase(path5);
8809
8780
  return db;
8810
8781
  }
8811
- function ensureMigrationsTable(db) {
8812
- db.exec(`
8782
+ function bunOptions(opts) {
8783
+ const out = {};
8784
+ if (opts.readonly !== undefined)
8785
+ out.readonly = opts.readonly;
8786
+ if (opts.create !== undefined)
8787
+ out.create = opts.create;
8788
+ return out;
8789
+ }
8790
+ function loadBunSqlite() {
8791
+ if (!bunSqliteModule) {
8792
+ bunSqliteModule = nodeRequire("bun:sqlite");
8793
+ }
8794
+ return bunSqliteModule;
8795
+ }
8796
+ function loadBetterSqlite3() {
8797
+ if (!betterSqlite3Ctor) {
8798
+ const mod = nodeRequire("better-sqlite3");
8799
+ betterSqlite3Ctor = mod.default ?? mod;
8800
+ }
8801
+ return betterSqlite3Ctor;
8802
+ }
8803
+ function openNodeDatabase(path5, opts) {
8804
+ const BetterSqlite3 = loadBetterSqlite3();
8805
+ const options = {};
8806
+ if (opts?.readonly !== undefined)
8807
+ options.readonly = opts.readonly;
8808
+ if (opts?.create === false)
8809
+ options.fileMustExist = true;
8810
+ const db = opts ? new BetterSqlite3(path5, options) : new BetterSqlite3(path5);
8811
+ return db;
8812
+ }
8813
+ var isBun, nodeRequire, bunSqliteModule, betterSqlite3Ctor;
8814
+ var init_database = __esm(() => {
8815
+ isBun = !!process.versions?.bun;
8816
+ nodeRequire = createRequire(import.meta.url);
8817
+ });
8818
+
8819
+ // src/storage/engines/sqlite-migrations.ts
8820
+ function ensureMigrationsTable(db) {
8821
+ db.exec(`
8813
8822
  CREATE TABLE IF NOT EXISTS schema_migrations (
8814
8823
  id TEXT PRIMARY KEY,
8815
8824
  applied_at TEXT NOT NULL DEFAULT (datetime('now'))
8816
8825
  );
8817
8826
  `);
8818
8827
  }
8819
- function runMigrations(db) {
8828
+ function runMigrations(db, migrations, opts) {
8820
8829
  ensureMigrationsTable(db);
8830
+ opts?.bootstrap?.(db);
8821
8831
  const appliedRows = db.prepare("SELECT id FROM schema_migrations").all();
8822
8832
  const applied = new Set(appliedRows.map((r) => r.id));
8823
- for (const migration of MIGRATIONS) {
8833
+ for (const migration of migrations) {
8824
8834
  if (applied.has(migration.id))
8825
8835
  continue;
8826
8836
  db.transaction(() => {
@@ -8829,6 +8839,103 @@ function runMigrations(db) {
8829
8839
  })();
8830
8840
  }
8831
8841
  }
8842
+
8843
+ // src/core/assert.ts
8844
+ function assertNever(x, context) {
8845
+ let serialized;
8846
+ try {
8847
+ serialized = JSON.stringify(x);
8848
+ } catch {
8849
+ serialized = String(x);
8850
+ }
8851
+ if (serialized === undefined) {
8852
+ serialized = String(x);
8853
+ }
8854
+ const where = context ? ` (${context})` : "";
8855
+ throw new Error(`Unexpected value reached assertNever${where}: ${serialized}`);
8856
+ }
8857
+
8858
+ // src/core/improve-types.ts
8859
+ function classifyImproveAction(mode) {
8860
+ switch (mode) {
8861
+ case "reflect":
8862
+ case "distill":
8863
+ case "memory-inference":
8864
+ case "graph-extraction":
8865
+ return "accepted";
8866
+ case "reflect-cooldown":
8867
+ case "reflect-skipped":
8868
+ case "distill-skipped":
8869
+ case "reflect-guard-rejected":
8870
+ return "rejected";
8871
+ case "reflect-failed":
8872
+ case "error":
8873
+ return "error";
8874
+ case "memory-prune":
8875
+ return "noop";
8876
+ default:
8877
+ return assertNever(mode);
8878
+ }
8879
+ }
8880
+ var init_improve_types = () => {};
8881
+
8882
+ // src/core/state-db.ts
8883
+ var exports_state_db = {};
8884
+ __export(exports_state_db, {
8885
+ upsertTaskHistory: () => upsertTaskHistory,
8886
+ upsertProposal: () => upsertProposal,
8887
+ upsertExtractedSession: () => upsertExtractedSession,
8888
+ shouldSkipAlreadyExtractedSession: () => shouldSkipAlreadyExtractedSession,
8889
+ runMigrations: () => runMigrations2,
8890
+ recordImproveRun: () => recordImproveRun,
8891
+ recordFsProposalsImport: () => recordFsProposalsImport,
8892
+ readStateEvents: () => readStateEvents,
8893
+ queryTaskHistory: () => queryTaskHistory,
8894
+ queryImproveRuns: () => queryImproveRuns,
8895
+ queryCompletedTaskIntervals: () => queryCompletedTaskIntervals,
8896
+ purgeOldImproveRuns: () => purgeOldImproveRuns,
8897
+ purgeOldEvents: () => purgeOldEvents,
8898
+ proposalToRowValues: () => proposalToRowValues,
8899
+ proposalRowToProposal: () => proposalRowToProposal,
8900
+ openStateDatabase: () => openStateDatabase,
8901
+ listStateProposals: () => listStateProposals,
8902
+ listStateProposalIdsByPrefix: () => listStateProposalIdsByPrefix,
8903
+ listExistingTableNames: () => listExistingTableNames,
8904
+ insertProposalIfAbsent: () => insertProposalIfAbsent,
8905
+ insertEvent: () => insertEvent,
8906
+ importEventsJsonl: () => importEventsJsonl,
8907
+ hasImportedFsProposals: () => hasImportedFsProposals,
8908
+ getTaskHistoryRuns: () => getTaskHistoryRuns,
8909
+ getTaskHistory: () => getTaskHistory,
8910
+ getStateProposal: () => getStateProposal,
8911
+ getStateDbPath: () => getStateDbPath,
8912
+ getExtractedSessionsMap: () => getExtractedSessionsMap,
8913
+ getExtractedSession: () => getExtractedSession,
8914
+ eventRowToEnvelope: () => eventRowToEnvelope,
8915
+ computeImproveRunMetrics: () => computeImproveRunMetrics,
8916
+ REGISTRY_INDEX_CACHE_DDL: () => REGISTRY_INDEX_CACHE_DDL
8917
+ });
8918
+ import fs5 from "fs";
8919
+ import path5 from "path";
8920
+ function getStateDbPath() {
8921
+ return path5.join(getDataDir(), "state.db");
8922
+ }
8923
+ function openStateDatabase(dbPath) {
8924
+ const resolvedPath = dbPath ?? getStateDbPath();
8925
+ const dir = path5.dirname(resolvedPath);
8926
+ if (!fs5.existsSync(dir)) {
8927
+ fs5.mkdirSync(dir, { recursive: true });
8928
+ }
8929
+ const db = openDatabase(resolvedPath);
8930
+ db.exec("PRAGMA journal_mode = WAL");
8931
+ db.exec("PRAGMA foreign_keys = ON");
8932
+ db.exec("PRAGMA busy_timeout = 30000");
8933
+ runMigrations2(db);
8934
+ return db;
8935
+ }
8936
+ function runMigrations2(db) {
8937
+ runMigrations(db, MIGRATIONS);
8938
+ }
8832
8939
  function eventRowToEnvelope(row) {
8833
8940
  let metadata;
8834
8941
  try {
@@ -8869,7 +8976,10 @@ function proposalRowToProposal(row) {
8869
8976
  content: row.content,
8870
8977
  ...frontmatter !== undefined ? { frontmatter } : {}
8871
8978
  },
8872
- ...meta.review !== undefined ? { review: meta.review } : {}
8979
+ ...meta.review !== undefined ? { review: meta.review } : {},
8980
+ ...typeof meta.confidence === "number" ? { confidence: meta.confidence } : {},
8981
+ ...meta.gateDecision !== undefined ? { gateDecision: meta.gateDecision } : {},
8982
+ ...typeof meta.backupContent === "string" ? { backupContent: meta.backupContent } : {}
8873
8983
  };
8874
8984
  }
8875
8985
  function proposalToRowValues(proposal, stashDir) {
@@ -8878,6 +8988,12 @@ function proposalToRowValues(proposal, stashDir) {
8878
8988
  metaObj.sourceRun = proposal.sourceRun;
8879
8989
  if (proposal.review !== undefined)
8880
8990
  metaObj.review = proposal.review;
8991
+ if (proposal.confidence !== undefined)
8992
+ metaObj.confidence = proposal.confidence;
8993
+ if (proposal.gateDecision !== undefined)
8994
+ metaObj.gateDecision = proposal.gateDecision;
8995
+ if (proposal.backupContent !== undefined)
8996
+ metaObj.backupContent = proposal.backupContent;
8881
8997
  return {
8882
8998
  id: proposal.id,
8883
8999
  stash_dir: stashDir,
@@ -8971,15 +9087,39 @@ function listStateProposals(db, options = {}) {
8971
9087
  const where = conditions.length > 0 ? `WHERE ${conditions.join(" AND ")}` : "";
8972
9088
  const rows = db.prepare(`SELECT id, stash_dir, ref, status, source, created_at, updated_at,
8973
9089
  content, frontmatter_json, metadata_json
8974
- FROM proposals ${where} ORDER BY created_at ASC`).all(...params);
9090
+ FROM proposals ${where} ORDER BY created_at ASC, rowid ASC`).all(...params);
8975
9091
  return rows.map(proposalRowToProposal);
8976
9092
  }
8977
- function getStateProposal(db, id) {
8978
- const row = db.prepare(`SELECT id, stash_dir, ref, status, source, created_at, updated_at,
9093
+ function getStateProposal(db, id, stashDir) {
9094
+ const sql = `SELECT id, stash_dir, ref, status, source, created_at, updated_at,
8979
9095
  content, frontmatter_json, metadata_json
8980
- FROM proposals WHERE id = ?`).get(id);
9096
+ FROM proposals WHERE id = ?${stashDir ? " AND stash_dir = ?" : ""}`;
9097
+ const row = stashDir ? db.prepare(sql).get(id, stashDir) : db.prepare(sql).get(id);
8981
9098
  return row ? proposalRowToProposal(row) : undefined;
8982
9099
  }
9100
+ function listStateProposalIdsByPrefix(db, stashDir, idPrefix) {
9101
+ const escaped = idPrefix.replace(/[\\%_]/g, (ch) => `\\${ch}`);
9102
+ const rows = db.prepare(`SELECT id FROM proposals
9103
+ WHERE stash_dir = ? AND status = 'pending' AND id LIKE ? ESCAPE '\\'
9104
+ ORDER BY id ASC`).all(stashDir, `${escaped}%`);
9105
+ return rows.map((r) => r.id);
9106
+ }
9107
+ function hasImportedFsProposals(db, stashDir) {
9108
+ return Boolean(db.prepare("SELECT 1 FROM proposal_fs_imports WHERE stash_dir = ?").get(stashDir));
9109
+ }
9110
+ function recordFsProposalsImport(db, stashDir, importedCount) {
9111
+ db.prepare("INSERT OR REPLACE INTO proposal_fs_imports (stash_dir, imported_at, imported_count) VALUES (?, ?, ?)").run(stashDir, new Date().toISOString(), importedCount);
9112
+ }
9113
+ function insertProposalIfAbsent(db, proposal, stashDir) {
9114
+ const v = proposalToRowValues(proposal, stashDir);
9115
+ const result = db.prepare(`
9116
+ INSERT OR IGNORE INTO proposals
9117
+ (id, stash_dir, ref, status, source, created_at, updated_at, content, frontmatter_json, metadata_json)
9118
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
9119
+ `).run(v.id, v.stash_dir, v.ref, v.status, v.source, v.created_at, v.updated_at, v.content, v.frontmatter_json, v.metadata_json);
9120
+ const changes = result.changes ?? 0;
9121
+ return Number(changes) > 0;
9122
+ }
8983
9123
  function upsertTaskHistory(db, row) {
8984
9124
  db.prepare(`
8985
9125
  INSERT OR IGNORE INTO task_history
@@ -9026,6 +9166,16 @@ function queryTaskHistory(db, options = {}) {
9026
9166
  target_kind, target_ref, metadata_json
9027
9167
  FROM task_history ${where} ORDER BY started_at DESC`).all(...params);
9028
9168
  }
9169
+ function queryCompletedTaskIntervals(db, since, until) {
9170
+ const sql = until ? "SELECT started_at, completed_at FROM task_history WHERE task_id = 'akm-improve' AND started_at >= ? AND started_at < ? AND completed_at IS NOT NULL ORDER BY started_at" : "SELECT started_at, completed_at FROM task_history WHERE task_id = 'akm-improve' AND started_at >= ? AND completed_at IS NOT NULL ORDER BY started_at";
9171
+ return until ? db.prepare(sql).all(since, until) : db.prepare(sql).all(since);
9172
+ }
9173
+ function listExistingTableNames(db, names) {
9174
+ if (names.length === 0)
9175
+ return [];
9176
+ const placeholders = names.map(() => "?").join(", ");
9177
+ return db.prepare(`SELECT name FROM sqlite_master WHERE type = 'table' AND name IN (${placeholders}) ORDER BY name`).all(...names);
9178
+ }
9029
9179
  async function importEventsJsonl(db, jsonlPath) {
9030
9180
  const { readFileSync, existsSync } = await import("fs");
9031
9181
  if (!existsSync(jsonlPath)) {
@@ -9082,23 +9232,17 @@ function computeImproveRunMetrics(result) {
9082
9232
  let autoAcceptedCount = 0;
9083
9233
  let errorCount = 0;
9084
9234
  for (const action of actions) {
9085
- switch (action.mode) {
9086
- case "reflect":
9087
- case "distill":
9088
- case "memory-inference":
9089
- case "graph-extraction":
9235
+ switch (classifyImproveAction(action.mode)) {
9236
+ case "accepted":
9090
9237
  acceptedCount++;
9091
9238
  break;
9092
- case "reflect-cooldown":
9093
- case "reflect-skipped":
9094
- case "distill-skipped":
9239
+ case "rejected":
9095
9240
  rejectedCount++;
9096
9241
  break;
9097
- case "reflect-failed":
9098
9242
  case "error":
9099
9243
  errorCount++;
9100
9244
  break;
9101
- case "memory-prune":
9245
+ case "noop":
9102
9246
  break;
9103
9247
  }
9104
9248
  const r = action.result;
@@ -9117,6 +9261,10 @@ function recordImproveRun(db, input) {
9117
9261
  VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
9118
9262
  `).run(input.id, input.startedAt, input.completedAt, input.stashDir, input.dryRun ? 1 : 0, input.profile, input.scopeMode, input.scopeValue, input.guidance, input.ok ? 1 : 0, JSON.stringify(input.result), JSON.stringify(metricsObj), JSON.stringify(input.metadata ?? {}));
9119
9263
  }
9264
+ function queryImproveRuns(db, since, until) {
9265
+ const sql = until ? "SELECT id, started_at, completed_at, ok, scope_mode, scope_value, result_json FROM improve_runs WHERE started_at >= ? AND started_at < ? AND dry_run = 0 ORDER BY started_at DESC" : "SELECT id, started_at, completed_at, ok, scope_mode, scope_value, result_json FROM improve_runs WHERE started_at >= ? AND dry_run = 0 ORDER BY started_at DESC";
9266
+ return until ? db.prepare(sql).all(since, until) : db.prepare(sql).all(since);
9267
+ }
9120
9268
  function purgeOldImproveRuns(db, retentionDays = 90) {
9121
9269
  if (!Number.isFinite(retentionDays) || retentionDays <= 0)
9122
9270
  return 0;
@@ -9177,6 +9325,8 @@ var MIGRATIONS, REGISTRY_INDEX_CACHE_DDL = `
9177
9325
  ON registry_index_cache(fetched_at);
9178
9326
  `;
9179
9327
  var init_state_db = __esm(() => {
9328
+ init_database();
9329
+ init_improve_types();
9180
9330
  init_paths();
9181
9331
  init_warn();
9182
9332
  MIGRATIONS = [
@@ -9250,7 +9400,9 @@ var init_state_db = __esm(() => {
9250
9400
  --
9251
9401
  -- Extensible (metadata_json) columns:
9252
9402
  -- metadata_json TEXT \u2014 JSON object for future proposal fields.
9253
- -- Current fields stored here: sourceRun, review.
9403
+ -- Current fields stored here: sourceRun,
9404
+ -- review, confidence, gateDecision (#577),
9405
+ -- backupContent.
9254
9406
  --
9255
9407
  -- ADD COLUMN extension points (future migrations):
9256
9408
  -- ALTER TABLE proposals ADD COLUMN source_run TEXT DEFAULT NULL;
@@ -9437,10 +9589,35 @@ var init_state_db = __esm(() => {
9437
9589
  CREATE INDEX IF NOT EXISTS idx_extract_sessions_processed
9438
9590
  ON extract_sessions_seen(processed_at);
9439
9591
  `
9592
+ },
9593
+ {
9594
+ id: "005-proposal-fs-imports",
9595
+ up: `
9596
+ CREATE TABLE IF NOT EXISTS proposal_fs_imports (
9597
+ stash_dir TEXT PRIMARY KEY,
9598
+ imported_at TEXT NOT NULL,
9599
+ imported_count INTEGER NOT NULL DEFAULT 0
9600
+ );
9601
+ `
9440
9602
  }
9441
9603
  ];
9442
9604
  });
9443
9605
 
9606
+ // src/core/best-effort.ts
9607
+ function bestEffort(fn, context) {
9608
+ try {
9609
+ return fn();
9610
+ } catch (err) {
9611
+ if (isVerbose()) {
9612
+ warnVerbose(`[akm:best-effort] ${context ? `${context}: ` : ""}swallowed error`, err);
9613
+ }
9614
+ return;
9615
+ }
9616
+ }
9617
+ var init_best_effort = __esm(() => {
9618
+ init_warn();
9619
+ });
9620
+
9444
9621
  // src/llm/embedders/types.ts
9445
9622
  function cosineSimilarity(a, b) {
9446
9623
  if (a.length !== b.length) {
@@ -9465,7 +9642,98 @@ var init_types = __esm(() => {
9465
9642
  init_warn();
9466
9643
  });
9467
9644
 
9468
- // src/indexer/db-backup.ts
9645
+ // src/runtime.ts
9646
+ import { createHash } from "crypto";
9647
+ import { createRequire as createRequire2 } from "module";
9648
+ function sha256Hex(data) {
9649
+ return createHash("sha256").update(data).digest("hex");
9650
+ }
9651
+ function sleepSync(ms) {
9652
+ if (isBun2) {
9653
+ bunGlobal().sleepSync(ms);
9654
+ return;
9655
+ }
9656
+ Atomics.wait(new Int32Array(new SharedArrayBuffer(4)), 0, 0, ms);
9657
+ }
9658
+ function bunGlobal() {
9659
+ return globalThis.Bun;
9660
+ }
9661
+ var isBun2, nodeRequire2, mainPath;
9662
+ var init_runtime = __esm(() => {
9663
+ isBun2 = !!process.versions?.bun;
9664
+ nodeRequire2 = createRequire2(import.meta.url);
9665
+ mainPath = isBun2 ? bunGlobal().main : process.argv[1];
9666
+ });
9667
+
9668
+ // src/indexer/search/search-fields.ts
9669
+ function buildSearchFields(entry) {
9670
+ const name = entry.name.replace(/[-_]/g, " ").toLowerCase();
9671
+ const description = (entry.description ?? "").toLowerCase();
9672
+ const tagParts = [];
9673
+ if (entry.tags)
9674
+ tagParts.push(entry.tags.join(" "));
9675
+ if (entry.aliases)
9676
+ tagParts.push(entry.aliases.join(" "));
9677
+ const tags = tagParts.join(" ").toLowerCase();
9678
+ const hintParts = [];
9679
+ if (entry.searchHints)
9680
+ hintParts.push(entry.searchHints.join(" "));
9681
+ if (entry.examples)
9682
+ hintParts.push(entry.examples.join(" "));
9683
+ if (entry.usage)
9684
+ hintParts.push(entry.usage.join(" "));
9685
+ if (entry.intent) {
9686
+ if (entry.intent.when)
9687
+ hintParts.push(entry.intent.when);
9688
+ if (entry.intent.input)
9689
+ hintParts.push(entry.intent.input);
9690
+ if (entry.intent.output)
9691
+ hintParts.push(entry.intent.output);
9692
+ }
9693
+ if (entry.xrefs)
9694
+ hintParts.push(entry.xrefs.join(" "));
9695
+ if (entry.pageKind)
9696
+ hintParts.push(entry.pageKind);
9697
+ if (entry.whenToUse)
9698
+ hintParts.push(entry.whenToUse);
9699
+ const hints = hintParts.join(" ").toLowerCase();
9700
+ const contentParts = [];
9701
+ if (entry.toc) {
9702
+ contentParts.push(entry.toc.map((h) => h.text).join(" "));
9703
+ }
9704
+ if (entry.parameters) {
9705
+ for (const param of entry.parameters) {
9706
+ contentParts.push(param.name);
9707
+ if (param.description)
9708
+ contentParts.push(param.description);
9709
+ }
9710
+ }
9711
+ const content = contentParts.join(" ").toLowerCase();
9712
+ return { name, description, tags, hints, content };
9713
+ }
9714
+
9715
+ // src/indexer/usage/usage-events.ts
9716
+ function ensureUsageEventsSchema(db) {
9717
+ db.exec(`
9718
+ CREATE TABLE IF NOT EXISTS usage_events (
9719
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
9720
+ event_type TEXT NOT NULL,
9721
+ query TEXT,
9722
+ entry_id INTEGER,
9723
+ entry_ref TEXT,
9724
+ signal TEXT,
9725
+ metadata TEXT,
9726
+ source TEXT NOT NULL DEFAULT 'user',
9727
+ created_at TEXT NOT NULL DEFAULT (datetime('now'))
9728
+ );
9729
+ CREATE INDEX IF NOT EXISTS idx_usage_events_entry ON usage_events(entry_id);
9730
+ CREATE INDEX IF NOT EXISTS idx_usage_events_type ON usage_events(event_type);
9731
+ CREATE INDEX IF NOT EXISTS idx_usage_events_ref ON usage_events(entry_ref);
9732
+ CREATE INDEX IF NOT EXISTS idx_usage_events_source ON usage_events(source);
9733
+ `);
9734
+ }
9735
+
9736
+ // src/indexer/db/db-backup.ts
9469
9737
  import fs6 from "fs";
9470
9738
  import path6 from "path";
9471
9739
  function resolveRetention(env = process.env) {
@@ -9507,9 +9775,9 @@ function measureDataDirSize(dirPath) {
9507
9775
  if (entry.isDirectory()) {
9508
9776
  stack.push(full);
9509
9777
  } else if (entry.isFile()) {
9510
- try {
9778
+ bestEffort(() => {
9511
9779
  total += fs6.statSync(full).size;
9512
- } catch {}
9780
+ }, "file vanished between readdir and stat");
9513
9781
  }
9514
9782
  }
9515
9783
  }
@@ -9545,7 +9813,7 @@ function listBackups(dataDir) {
9545
9813
  let sizeBytes;
9546
9814
  let reason = DEFAULT_BACKUP_REASON;
9547
9815
  if (fs6.existsSync(metaPath)) {
9548
- try {
9816
+ bestEffort(() => {
9549
9817
  const raw = fs6.readFileSync(metaPath, "utf8");
9550
9818
  const parsed = JSON.parse(raw);
9551
9819
  if (typeof parsed.createdAt === "string")
@@ -9558,7 +9826,7 @@ function listBackups(dataDir) {
9558
9826
  sizeBytes = parsed.sizeBytes;
9559
9827
  if (typeof parsed.reason === "string" && parsed.reason.length > 0)
9560
9828
  reason = parsed.reason;
9561
- } catch {}
9829
+ }, "malformed backup metadata \u2014 fall back to filesystem-derived values");
9562
9830
  }
9563
9831
  if (!createdAt) {
9564
9832
  try {
@@ -9639,9 +9907,7 @@ function backupDataDir(opts) {
9639
9907
  copyDataDirExcludingBackups(dataDir, finalDest);
9640
9908
  } catch (err) {
9641
9909
  warn("[akm] data dir backup failed \u2014 %s; upgrade will proceed without a snapshot", err instanceof Error ? err.message : String(err));
9642
- try {
9643
- fs6.rmSync(finalDest, { recursive: true, force: true });
9644
- } catch {}
9910
+ bestEffort(() => fs6.rmSync(finalDest, { recursive: true, force: true }), "cleanup partial backup copy");
9645
9911
  return null;
9646
9912
  }
9647
9913
  const createdAt = now.toISOString();
@@ -9697,9 +9963,7 @@ function copyDataDirExcludingBackups(srcRoot, destRoot) {
9697
9963
  fs6.copyFileSync(srcPath, destPath);
9698
9964
  } else if (entry.isSymbolicLink()) {
9699
9965
  const target = fs6.readlinkSync(srcPath);
9700
- try {
9701
- fs6.symlinkSync(target, destPath);
9702
- } catch {}
9966
+ bestEffort(() => fs6.symlinkSync(target, destPath), "symlink creation can fail on Windows without admin");
9703
9967
  }
9704
9968
  }
9705
9969
  }
@@ -9714,77 +9978,10 @@ function tryHostname() {
9714
9978
  }
9715
9979
  var DEFAULT_BACKUP_REASON = "version-upgrade", EMBEDDING_DIM_CHANGE_REASON = "embedding-dim-change", BACKUPS_DIR_NAME = "backups", BACKUP_METADATA_FILE = "backup.meta.json", DEFAULT_RETAIN = 5, FREE_SPACE_MULTIPLIER = 1.1;
9716
9980
  var init_db_backup = __esm(() => {
9981
+ init_best_effort();
9717
9982
  init_warn();
9718
9983
  });
9719
9984
 
9720
- // src/indexer/search-fields.ts
9721
- function buildSearchFields(entry) {
9722
- const name = entry.name.replace(/[-_]/g, " ").toLowerCase();
9723
- const description = (entry.description ?? "").toLowerCase();
9724
- const tagParts = [];
9725
- if (entry.tags)
9726
- tagParts.push(entry.tags.join(" "));
9727
- if (entry.aliases)
9728
- tagParts.push(entry.aliases.join(" "));
9729
- const tags = tagParts.join(" ").toLowerCase();
9730
- const hintParts = [];
9731
- if (entry.searchHints)
9732
- hintParts.push(entry.searchHints.join(" "));
9733
- if (entry.examples)
9734
- hintParts.push(entry.examples.join(" "));
9735
- if (entry.usage)
9736
- hintParts.push(entry.usage.join(" "));
9737
- if (entry.intent) {
9738
- if (entry.intent.when)
9739
- hintParts.push(entry.intent.when);
9740
- if (entry.intent.input)
9741
- hintParts.push(entry.intent.input);
9742
- if (entry.intent.output)
9743
- hintParts.push(entry.intent.output);
9744
- }
9745
- if (entry.xrefs)
9746
- hintParts.push(entry.xrefs.join(" "));
9747
- if (entry.pageKind)
9748
- hintParts.push(entry.pageKind);
9749
- if (entry.whenToUse)
9750
- hintParts.push(entry.whenToUse);
9751
- const hints = hintParts.join(" ").toLowerCase();
9752
- const contentParts = [];
9753
- if (entry.toc) {
9754
- contentParts.push(entry.toc.map((h) => h.text).join(" "));
9755
- }
9756
- if (entry.parameters) {
9757
- for (const param of entry.parameters) {
9758
- contentParts.push(param.name);
9759
- if (param.description)
9760
- contentParts.push(param.description);
9761
- }
9762
- }
9763
- const content = contentParts.join(" ").toLowerCase();
9764
- return { name, description, tags, hints, content };
9765
- }
9766
-
9767
- // src/indexer/usage-events.ts
9768
- function ensureUsageEventsSchema(db) {
9769
- db.exec(`
9770
- CREATE TABLE IF NOT EXISTS usage_events (
9771
- id INTEGER PRIMARY KEY AUTOINCREMENT,
9772
- event_type TEXT NOT NULL,
9773
- query TEXT,
9774
- entry_id INTEGER,
9775
- entry_ref TEXT,
9776
- signal TEXT,
9777
- metadata TEXT,
9778
- source TEXT NOT NULL DEFAULT 'user',
9779
- created_at TEXT NOT NULL DEFAULT (datetime('now'))
9780
- );
9781
- CREATE INDEX IF NOT EXISTS idx_usage_events_entry ON usage_events(entry_id);
9782
- CREATE INDEX IF NOT EXISTS idx_usage_events_type ON usage_events(event_type);
9783
- CREATE INDEX IF NOT EXISTS idx_usage_events_ref ON usage_events(entry_ref);
9784
- CREATE INDEX IF NOT EXISTS idx_usage_events_source ON usage_events(source);
9785
- `);
9786
- }
9787
-
9788
9985
  // src/core/file-lock.ts
9789
9986
  import fs7 from "fs";
9790
9987
  function tryAcquireLockSync(lockPath, payload) {
@@ -9850,7 +10047,7 @@ var init_file_lock = __esm(() => {
9850
10047
  init_common();
9851
10048
  });
9852
10049
 
9853
- // src/core/config-io.ts
10050
+ // src/core/config/config-io.ts
9854
10051
  import fs8 from "fs";
9855
10052
  import path7 from "path";
9856
10053
  function parseConfigText(text, sourcePath) {
@@ -9891,9 +10088,12 @@ function backupExistingConfig(configPath) {
9891
10088
  const backupDir = path7.join(getCacheDir(), "config-backups");
9892
10089
  fs8.mkdirSync(backupDir, { recursive: true });
9893
10090
  const timestamp = new Date().toISOString().replace(/[.:]/g, "-");
9894
- fs8.copyFileSync(configPath, path7.join(backupDir, `config-${timestamp}.json`));
9895
- fs8.copyFileSync(configPath, path7.join(backupDir, "config.latest.json"));
10091
+ const timestamped = path7.join(backupDir, `config-${timestamp}.json`);
10092
+ const latest = path7.join(backupDir, "config.latest.json");
10093
+ fs8.copyFileSync(configPath, timestamped);
10094
+ fs8.copyFileSync(configPath, latest);
9896
10095
  pruneOldBackups2(backupDir);
10096
+ return { timestamped, latest };
9897
10097
  }
9898
10098
  function pruneOldBackups2(backupDir) {
9899
10099
  let entries;
@@ -9919,6 +10119,9 @@ function pruneOldBackups2(backupDir) {
9919
10119
  function getConfigLockPath() {
9920
10120
  return path7.join(getConfigDir(), "config.json.lck");
9921
10121
  }
10122
+ function sleepSyncMs(ms) {
10123
+ sleepSync(ms);
10124
+ }
9922
10125
  function acquireConfigLock() {
9923
10126
  const lockPath = getConfigLockPath();
9924
10127
  try {
@@ -9937,8 +10140,7 @@ function acquireConfigLock() {
9937
10140
  continue;
9938
10141
  }
9939
10142
  if (attempt < CONFIG_LOCK_MAX_RETRIES - 1) {
9940
- const deadline = Date.now() + CONFIG_LOCK_RETRY_DELAY_MS;
9941
- while (Date.now() < deadline) {}
10143
+ sleepSyncMs(CONFIG_LOCK_RETRY_DELAY_MS);
9942
10144
  }
9943
10145
  }
9944
10146
  return () => {};
@@ -9991,17 +10193,802 @@ function stripJsonComments(text) {
9991
10193
  result += text[i];
9992
10194
  i++;
9993
10195
  }
9994
- return result;
10196
+ return result;
10197
+ }
10198
+ var MAX_CONFIG_BACKUPS = 5, CONFIG_LOCK_MAX_RETRIES = 10, CONFIG_LOCK_RETRY_DELAY_MS = 50;
10199
+ var init_config_io = __esm(() => {
10200
+ init_runtime();
10201
+ init_common();
10202
+ init_errors();
10203
+ init_file_lock();
10204
+ init_paths();
10205
+ });
10206
+
10207
+ // src/integrations/harnesses/types.ts
10208
+ class BaseHarness {
10209
+ runtimeId;
10210
+ setupDetectionDir;
10211
+ v1ProfilePrefixes = [];
10212
+ matchesV1ProfileName(name) {
10213
+ if (!this.capabilities.v1Migration)
10214
+ return false;
10215
+ const lower = name.trim().toLowerCase();
10216
+ if (!lower)
10217
+ return false;
10218
+ if (lower === this.id.toLowerCase())
10219
+ return true;
10220
+ for (const alias of this.aliases) {
10221
+ if (lower === alias.toLowerCase())
10222
+ return true;
10223
+ }
10224
+ for (const prefix of this.v1ProfilePrefixes) {
10225
+ if (lower.startsWith(prefix.toLowerCase()))
10226
+ return true;
10227
+ }
10228
+ return false;
10229
+ }
10230
+ }
10231
+
10232
+ // src/integrations/agent/builder-shared.ts
10233
+ var init_builder_shared = __esm(() => {
10234
+ init_errors();
10235
+ });
10236
+
10237
+ // src/integrations/agent/model-aliases.ts
10238
+ var init_model_aliases = () => {};
10239
+
10240
+ // src/integrations/harnesses/claude/agent-builder.ts
10241
+ var init_agent_builder = __esm(() => {
10242
+ init_builder_shared();
10243
+ init_model_aliases();
10244
+ });
10245
+
10246
+ // src/integrations/harnesses/claude/config-import.ts
10247
+ var init_config_import = () => {};
10248
+
10249
+ // src/integrations/session-logs/inline-refs.ts
10250
+ function extractInlineRefMentions(text, ts) {
10251
+ if (!text || text.length < 10)
10252
+ return [];
10253
+ const out = [];
10254
+ REMEMBER_RE.lastIndex = 0;
10255
+ for (const m of text.matchAll(REMEMBER_RE)) {
10256
+ const body = m[1] ?? m[2] ?? "";
10257
+ if (!body.trim())
10258
+ continue;
10259
+ out.push({
10260
+ kind: "remember",
10261
+ text: body,
10262
+ ...ts !== undefined ? { ts } : {}
10263
+ });
10264
+ }
10265
+ FEEDBACK_RE.lastIndex = 0;
10266
+ for (const m of text.matchAll(FEEDBACK_RE)) {
10267
+ const ref = m[1] ?? "";
10268
+ const note = m[2] ?? m[3] ?? "";
10269
+ if (!ref)
10270
+ continue;
10271
+ out.push({
10272
+ kind: "feedback",
10273
+ ref,
10274
+ text: note,
10275
+ ...ts !== undefined ? { ts } : {}
10276
+ });
10277
+ }
10278
+ return out;
10279
+ }
10280
+ var REMEMBER_RE, FEEDBACK_RE;
10281
+ var init_inline_refs = __esm(() => {
10282
+ REMEMBER_RE = /\bakm\s+remember\s+(?:"((?:[^"\\]|\\.)*)"|'((?:[^'\\]|\\.)*)')/g;
10283
+ FEEDBACK_RE = /\bakm\s+feedback\s+(\S+)(?:\s+--[a-z-]+)*\s+(?:--note|-n)\s+(?:"((?:[^"\\]|\\.)*)"|'((?:[^'\\]|\\.)*)')/g;
10284
+ });
10285
+
10286
+ // src/integrations/harnesses/claude/session-log.ts
10287
+ import fs9 from "fs";
10288
+ import os from "os";
10289
+ import path8 from "path";
10290
+ function claudeProjectsDir() {
10291
+ return process.env.AKM_CLAUDE_PROJECTS_DIR ?? path8.join(os.homedir(), ".claude", "projects");
10292
+ }
10293
+ function parseClaudeEvent(entry, sessionId, filePath, fallbackTsMs) {
10294
+ if (!entry || typeof entry !== "object")
10295
+ return;
10296
+ const e = entry;
10297
+ const tsRaw = e.timestamp;
10298
+ const ts = typeof tsRaw === "number" ? tsRaw : typeof tsRaw === "string" ? Date.parse(tsRaw) || fallbackTsMs : fallbackTsMs;
10299
+ const message = e.message ?? undefined;
10300
+ const role = typeof message?.role === "string" ? message.role : e.type ?? "unknown";
10301
+ const content = message?.content;
10302
+ let text = "";
10303
+ if (typeof content === "string") {
10304
+ text = content;
10305
+ } else if (Array.isArray(content)) {
10306
+ const parts = [];
10307
+ for (const block of content) {
10308
+ if (!block || typeof block !== "object")
10309
+ continue;
10310
+ const b = block;
10311
+ if (b.type === "text" && typeof b.text === "string")
10312
+ parts.push(b.text);
10313
+ else if (b.type === "thinking" && typeof b.thinking === "string")
10314
+ parts.push(b.thinking);
10315
+ else if (b.type === "tool_use") {
10316
+ const toolName = typeof b.name === "string" ? b.name : "tool";
10317
+ const inputObj = b.input;
10318
+ let inputText = "";
10319
+ if (inputObj && typeof inputObj === "object") {
10320
+ const cmd = inputObj.command;
10321
+ inputText = typeof cmd === "string" ? cmd : JSON.stringify(inputObj);
10322
+ } else if (typeof inputObj === "string") {
10323
+ inputText = inputObj;
10324
+ }
10325
+ parts.push(`[tool:${toolName}] ${inputText}`);
10326
+ } else if (b.type === "tool_result") {
10327
+ const out = typeof b.content === "string" ? b.content : JSON.stringify(b.content ?? "");
10328
+ parts.push(`[tool_result] ${out}`);
10329
+ }
10330
+ }
10331
+ text = parts.join(`
10332
+ `);
10333
+ }
10334
+ if (!text || text.length < 1)
10335
+ return;
10336
+ return {
10337
+ harness: "claude-code",
10338
+ text,
10339
+ ts,
10340
+ sessionId,
10341
+ role,
10342
+ filePath
10343
+ };
10344
+ }
10345
+
10346
+ class ClaudeCodeProvider {
10347
+ name = "claude-code";
10348
+ isAvailable() {
10349
+ return fs9.existsSync(claudeProjectsDir());
10350
+ }
10351
+ *readEvents(input) {
10352
+ try {
10353
+ for (const jsonlPath of this.#walkJsonl(claudeProjectsDir())) {
10354
+ const stat = fs9.statSync(jsonlPath);
10355
+ if (stat.mtimeMs < input.sinceMs)
10356
+ continue;
10357
+ const lines = fs9.readFileSync(jsonlPath, "utf8").split(`
10358
+ `).filter(Boolean);
10359
+ for (const line of lines) {
10360
+ try {
10361
+ const entry = JSON.parse(line);
10362
+ const text = entry?.message?.content ?? entry?.content ?? "";
10363
+ if (typeof text !== "string" || text.length < 10)
10364
+ continue;
10365
+ yield {
10366
+ harness: this.name,
10367
+ text,
10368
+ ts: typeof entry?.timestamp === "number" ? entry.timestamp : stat.mtimeMs,
10369
+ sessionId: typeof entry?.session_id === "string" ? entry.session_id : undefined,
10370
+ role: typeof entry?.role === "string" ? entry.role : "unknown",
10371
+ filePath: jsonlPath
10372
+ };
10373
+ } catch {}
10374
+ }
10375
+ }
10376
+ } catch {
10377
+ return;
10378
+ }
10379
+ }
10380
+ listSessions(input = {}) {
10381
+ const root = input.location ?? claudeProjectsDir();
10382
+ const sinceMs = input.sinceMs ?? 0;
10383
+ const summaries = [];
10384
+ try {
10385
+ for (const jsonlPath of this.#walkJsonl(root)) {
10386
+ let stat;
10387
+ try {
10388
+ stat = fs9.statSync(jsonlPath);
10389
+ } catch {
10390
+ continue;
10391
+ }
10392
+ if (stat.mtimeMs < sinceMs)
10393
+ continue;
10394
+ const sessionId = path8.basename(jsonlPath, ".jsonl");
10395
+ const projectHint = path8.basename(path8.dirname(jsonlPath));
10396
+ const peek = this.#peekJsonl(jsonlPath);
10397
+ summaries.push({
10398
+ harness: this.name,
10399
+ sessionId,
10400
+ filePath: jsonlPath,
10401
+ startedAt: peek.firstTsMs ?? stat.ctimeMs,
10402
+ endedAt: peek.lastTsMs ?? stat.mtimeMs,
10403
+ projectHint,
10404
+ ...peek.title ? { title: peek.title } : {}
10405
+ });
10406
+ }
10407
+ } catch {}
10408
+ return summaries.sort((a, b) => (b.endedAt ?? 0) - (a.endedAt ?? 0));
10409
+ }
10410
+ readSession(ref) {
10411
+ const stat = fs9.statSync(ref.filePath);
10412
+ const lines = fs9.readFileSync(ref.filePath, "utf8").split(`
10413
+ `).filter(Boolean);
10414
+ const events = [];
10415
+ const inlineRefs = [];
10416
+ let title;
10417
+ let firstTsMs;
10418
+ let lastTsMs;
10419
+ const projectHint = path8.basename(path8.dirname(ref.filePath));
10420
+ for (const line of lines) {
10421
+ let entry;
10422
+ try {
10423
+ entry = JSON.parse(line);
10424
+ } catch {
10425
+ continue;
10426
+ }
10427
+ if (!entry)
10428
+ continue;
10429
+ if (entry.type === "custom-title" && typeof entry.customTitle === "string") {
10430
+ title = entry.customTitle;
10431
+ continue;
10432
+ }
10433
+ const parsed = parseClaudeEvent(entry, ref.sessionId, ref.filePath, stat.mtimeMs);
10434
+ if (!parsed)
10435
+ continue;
10436
+ events.push(parsed);
10437
+ if (firstTsMs === undefined || (parsed.ts ?? 0) < firstTsMs)
10438
+ firstTsMs = parsed.ts;
10439
+ if (lastTsMs === undefined || (parsed.ts ?? 0) > lastTsMs)
10440
+ lastTsMs = parsed.ts;
10441
+ inlineRefs.push(...extractInlineRefMentions(parsed.text, parsed.ts));
10442
+ }
10443
+ return {
10444
+ ref: {
10445
+ harness: this.name,
10446
+ sessionId: ref.sessionId,
10447
+ filePath: ref.filePath,
10448
+ startedAt: firstTsMs ?? stat.ctimeMs,
10449
+ endedAt: lastTsMs ?? stat.mtimeMs,
10450
+ projectHint,
10451
+ ...title ? { title } : {}
10452
+ },
10453
+ events,
10454
+ inlineRefs
10455
+ };
10456
+ }
10457
+ #peekJsonl(filePath) {
10458
+ const result = {};
10459
+ try {
10460
+ const fd = fs9.openSync(filePath, "r");
10461
+ try {
10462
+ const stat = fs9.fstatSync(fd);
10463
+ const headSize = Math.min(stat.size, 4096);
10464
+ const head = Buffer.alloc(headSize);
10465
+ fs9.readSync(fd, head, 0, headSize, 0);
10466
+ const headLines = head.toString("utf8").split(`
10467
+ `).filter(Boolean);
10468
+ for (const line of headLines) {
10469
+ try {
10470
+ const e = JSON.parse(line);
10471
+ if (e.type === "custom-title" && typeof e.customTitle === "string") {
10472
+ result.title = e.customTitle;
10473
+ }
10474
+ if (typeof e.timestamp === "string") {
10475
+ const t = Date.parse(e.timestamp);
10476
+ if (!Number.isNaN(t)) {
10477
+ if (result.firstTsMs === undefined)
10478
+ result.firstTsMs = t;
10479
+ result.lastTsMs = t;
10480
+ }
10481
+ }
10482
+ } catch {}
10483
+ }
10484
+ if (stat.size > 4096) {
10485
+ const tailSize = Math.min(stat.size, 4096);
10486
+ const tail = Buffer.alloc(tailSize);
10487
+ fs9.readSync(fd, tail, 0, tailSize, stat.size - tailSize);
10488
+ const tailLines = tail.toString("utf8").split(`
10489
+ `).filter(Boolean);
10490
+ for (let i = tailLines.length - 1;i >= 0; i--) {
10491
+ try {
10492
+ const e = JSON.parse(tailLines[i] ?? "");
10493
+ if (typeof e.timestamp === "string") {
10494
+ const t = Date.parse(e.timestamp);
10495
+ if (!Number.isNaN(t)) {
10496
+ result.lastTsMs = t;
10497
+ break;
10498
+ }
10499
+ }
10500
+ } catch {}
10501
+ }
10502
+ }
10503
+ } finally {
10504
+ fs9.closeSync(fd);
10505
+ }
10506
+ } catch {}
10507
+ return result;
10508
+ }
10509
+ *#walkJsonl(dir) {
10510
+ try {
10511
+ for (const entry of fs9.readdirSync(dir, { withFileTypes: true })) {
10512
+ const full = path8.join(dir, entry.name);
10513
+ if (entry.isDirectory())
10514
+ yield* this.#walkJsonl(full);
10515
+ else if (entry.name.endsWith(".jsonl"))
10516
+ yield full;
10517
+ }
10518
+ } catch {}
10519
+ }
10520
+ }
10521
+ var init_session_log = __esm(() => {
10522
+ init_inline_refs();
10523
+ });
10524
+
10525
+ // src/integrations/harnesses/claude/index.ts
10526
+ function caps(c) {
10527
+ return {
10528
+ sessionLogs: false,
10529
+ agentDispatch: false,
10530
+ detection: false,
10531
+ configImport: false,
10532
+ runtimeIdentity: false,
10533
+ v1Migration: false,
10534
+ ...c
10535
+ };
10536
+ }
10537
+ var ClaudeHarness;
10538
+ var init_claude = __esm(() => {
10539
+ init_agent_builder();
10540
+ init_config_import();
10541
+ init_session_log();
10542
+ ClaudeHarness = class ClaudeHarness extends BaseHarness {
10543
+ id = "claude";
10544
+ displayName = "Claude Code";
10545
+ aliases = ["claude-code"];
10546
+ runtimeId = "claude-code";
10547
+ setupDetectionDir = ".claude";
10548
+ capabilities = caps({
10549
+ sessionLogs: true,
10550
+ agentDispatch: true,
10551
+ detection: true,
10552
+ configImport: true,
10553
+ runtimeIdentity: true,
10554
+ v1Migration: true
10555
+ });
10556
+ };
10557
+ });
10558
+
10559
+ // src/integrations/harnesses/opencode/agent-builder.ts
10560
+ var init_agent_builder2 = __esm(() => {
10561
+ init_builder_shared();
10562
+ init_model_aliases();
10563
+ });
10564
+
10565
+ // src/integrations/harnesses/opencode/config-import.ts
10566
+ var init_config_import2 = () => {};
10567
+
10568
+ // src/integrations/harnesses/opencode/session-log.ts
10569
+ import fs10 from "fs";
10570
+ import os2 from "os";
10571
+ import path9 from "path";
10572
+ function getOpenCodeBaseDir() {
10573
+ if (process.platform === "darwin") {
10574
+ return path9.join(os2.homedir(), "Library", "Application Support", "opencode");
10575
+ }
10576
+ return path9.join(os2.homedir(), ".local", "share", "opencode");
10577
+ }
10578
+
10579
+ class OpenCodeProvider {
10580
+ name = "opencode";
10581
+ #baseDir = getOpenCodeBaseDir();
10582
+ isAvailable() {
10583
+ return fs10.existsSync(this.#baseDir);
10584
+ }
10585
+ *readEvents(input) {
10586
+ const candidates = [this.#baseDir, path9.join(this.#baseDir, "log")];
10587
+ for (const dir of candidates) {
10588
+ if (!fs10.existsSync(dir))
10589
+ continue;
10590
+ try {
10591
+ for (const file of fs10.readdirSync(dir)) {
10592
+ const full = path9.join(dir, file);
10593
+ let stat;
10594
+ try {
10595
+ stat = fs10.statSync(full);
10596
+ } catch {
10597
+ continue;
10598
+ }
10599
+ if (!stat.isFile())
10600
+ continue;
10601
+ if (stat.mtimeMs < input.sinceMs)
10602
+ continue;
10603
+ if (!file.endsWith(".json") && !file.endsWith(".jsonl") && !file.endsWith(".log"))
10604
+ continue;
10605
+ const content = fs10.readFileSync(full, "utf8");
10606
+ const lines = content.includes(`
10607
+ `) ? content.split(`
10608
+ `) : [content];
10609
+ for (const line of lines) {
10610
+ try {
10611
+ const entry = JSON.parse(line);
10612
+ const text = entry?.content ?? entry?.message ?? entry?.text ?? "";
10613
+ if (typeof text !== "string" || text.length < 10)
10614
+ continue;
10615
+ yield {
10616
+ harness: this.name,
10617
+ text,
10618
+ ts: typeof entry?.timestamp === "number" ? entry.timestamp : stat.mtimeMs,
10619
+ sessionId: typeof entry?.sessionId === "string" ? entry.sessionId : undefined,
10620
+ role: typeof entry?.role === "string" ? entry.role : "unknown",
10621
+ filePath: full
10622
+ };
10623
+ } catch {}
10624
+ }
10625
+ }
10626
+ } catch {}
10627
+ }
10628
+ }
10629
+ listSessions(input = {}) {
10630
+ const base = input.location ?? this.#baseDir;
10631
+ const sinceMs = input.sinceMs ?? 0;
10632
+ const sessionRoot = path9.join(base, "storage", "session");
10633
+ if (!fs10.existsSync(sessionRoot))
10634
+ return [];
10635
+ const summaries = [];
10636
+ try {
10637
+ for (const projectId of fs10.readdirSync(sessionRoot)) {
10638
+ const projectDir = path9.join(sessionRoot, projectId);
10639
+ let pstat;
10640
+ try {
10641
+ pstat = fs10.statSync(projectDir);
10642
+ } catch {
10643
+ continue;
10644
+ }
10645
+ if (!pstat.isDirectory())
10646
+ continue;
10647
+ for (const file of fs10.readdirSync(projectDir)) {
10648
+ if (!file.endsWith(".json"))
10649
+ continue;
10650
+ const filePath = path9.join(projectDir, file);
10651
+ let stat;
10652
+ try {
10653
+ stat = fs10.statSync(filePath);
10654
+ } catch {
10655
+ continue;
10656
+ }
10657
+ if (stat.mtimeMs < sinceMs)
10658
+ continue;
10659
+ let meta;
10660
+ try {
10661
+ meta = JSON.parse(fs10.readFileSync(filePath, "utf8"));
10662
+ } catch {
10663
+ continue;
10664
+ }
10665
+ const sessionId = typeof meta?.id === "string" ? meta.id : path9.basename(file, ".json");
10666
+ const time = meta?.time ?? undefined;
10667
+ const startedAt = typeof time?.created === "number" ? time.created : stat.ctimeMs;
10668
+ const endedAt = typeof time?.updated === "number" ? time.updated : stat.mtimeMs;
10669
+ const title = typeof meta?.title === "string" ? meta.title : undefined;
10670
+ const projectHint = typeof meta?.directory === "string" ? meta.directory : projectId;
10671
+ summaries.push({
10672
+ harness: this.name,
10673
+ sessionId,
10674
+ filePath,
10675
+ startedAt,
10676
+ endedAt,
10677
+ projectHint,
10678
+ ...title ? { title } : {}
10679
+ });
10680
+ }
10681
+ }
10682
+ } catch {}
10683
+ return summaries.sort((a, b) => (b.endedAt ?? 0) - (a.endedAt ?? 0));
10684
+ }
10685
+ readSession(ref) {
10686
+ let meta = {};
10687
+ try {
10688
+ meta = JSON.parse(fs10.readFileSync(ref.filePath, "utf8"));
10689
+ } catch {}
10690
+ const time = meta.time ?? undefined;
10691
+ const startedAt = typeof time?.created === "number" ? time.created : undefined;
10692
+ const endedAt = typeof time?.updated === "number" ? time.updated : undefined;
10693
+ const title = typeof meta.title === "string" ? meta.title : undefined;
10694
+ const projectHint = typeof meta.directory === "string" ? meta.directory : undefined;
10695
+ const events = [];
10696
+ const inlineRefs = [];
10697
+ const inferredBase = this.#inferBaseFromSessionPath(ref.filePath) ?? this.#baseDir;
10698
+ const msgDir = path9.join(inferredBase, "storage", "message", ref.sessionId);
10699
+ if (fs10.existsSync(msgDir)) {
10700
+ try {
10701
+ const files = fs10.readdirSync(msgDir).filter((f) => f.endsWith(".json"));
10702
+ for (const file of files) {
10703
+ const full = path9.join(msgDir, file);
10704
+ let msg;
10705
+ try {
10706
+ msg = JSON.parse(fs10.readFileSync(full, "utf8"));
10707
+ } catch {
10708
+ continue;
10709
+ }
10710
+ if (!msg)
10711
+ continue;
10712
+ const evt = this.#messageToEvent(msg, ref.sessionId, full);
10713
+ if (evt) {
10714
+ events.push(evt);
10715
+ inlineRefs.push(...extractInlineRefMentions(evt.text, evt.ts));
10716
+ }
10717
+ }
10718
+ } catch {}
10719
+ }
10720
+ events.sort((a, b) => (a.ts ?? 0) - (b.ts ?? 0));
10721
+ return {
10722
+ ref: {
10723
+ harness: this.name,
10724
+ sessionId: ref.sessionId,
10725
+ filePath: ref.filePath,
10726
+ ...startedAt !== undefined ? { startedAt } : {},
10727
+ ...endedAt !== undefined ? { endedAt } : {},
10728
+ ...projectHint ? { projectHint } : {},
10729
+ ...title ? { title } : {}
10730
+ },
10731
+ events,
10732
+ inlineRefs
10733
+ };
10734
+ }
10735
+ #inferBaseFromSessionPath(filePath) {
10736
+ const dir = path9.dirname(filePath);
10737
+ const parts = dir.split(path9.sep);
10738
+ if (parts.length < 3)
10739
+ return;
10740
+ const last = parts[parts.length - 1];
10741
+ const sndLast = parts[parts.length - 2];
10742
+ const thirdLast = parts[parts.length - 3];
10743
+ if (sndLast !== "session" || thirdLast !== "storage" || !last)
10744
+ return;
10745
+ return parts.slice(0, parts.length - 3).join(path9.sep);
10746
+ }
10747
+ #messageToEvent(msg, sessionId, filePath) {
10748
+ const time = msg.time ?? undefined;
10749
+ const ts = typeof time?.created === "number" ? time.created : typeof msg.timestamp === "number" ? msg.timestamp : 0;
10750
+ const role = typeof msg.role === "string" ? msg.role : "unknown";
10751
+ const summary = msg.summary;
10752
+ const parts = [];
10753
+ if (typeof summary?.title === "string")
10754
+ parts.push(summary.title);
10755
+ if (Array.isArray(summary?.parts)) {
10756
+ for (const p of summary.parts) {
10757
+ if (typeof p === "string")
10758
+ parts.push(p);
10759
+ else if (p && typeof p === "object") {
10760
+ const text2 = p.text;
10761
+ if (typeof text2 === "string")
10762
+ parts.push(text2);
10763
+ }
10764
+ }
10765
+ }
10766
+ if (typeof msg.content === "string")
10767
+ parts.push(msg.content);
10768
+ const text = parts.join(`
10769
+ `).trim();
10770
+ if (text.length < 1)
10771
+ return;
10772
+ return {
10773
+ harness: this.name,
10774
+ text,
10775
+ ts: ts || undefined,
10776
+ sessionId,
10777
+ role,
10778
+ filePath
10779
+ };
10780
+ }
10781
+ }
10782
+ var init_session_log2 = __esm(() => {
10783
+ init_inline_refs();
10784
+ });
10785
+
10786
+ // src/integrations/harnesses/opencode/index.ts
10787
+ function caps2(c) {
10788
+ return {
10789
+ sessionLogs: false,
10790
+ agentDispatch: false,
10791
+ detection: false,
10792
+ configImport: false,
10793
+ runtimeIdentity: false,
10794
+ v1Migration: false,
10795
+ ...c
10796
+ };
10797
+ }
10798
+ var OpencodeHarness;
10799
+ var init_opencode = __esm(() => {
10800
+ init_agent_builder2();
10801
+ init_config_import2();
10802
+ init_session_log2();
10803
+ OpencodeHarness = class OpencodeHarness extends BaseHarness {
10804
+ id = "opencode";
10805
+ displayName = "OpenCode";
10806
+ aliases = [];
10807
+ setupDetectionDir = ".config/opencode";
10808
+ v1ProfilePrefixes = ["opencode"];
10809
+ capabilities = caps2({
10810
+ sessionLogs: true,
10811
+ agentDispatch: true,
10812
+ detection: true,
10813
+ configImport: true,
10814
+ runtimeIdentity: true,
10815
+ v1Migration: true
10816
+ });
10817
+ };
10818
+ });
10819
+
10820
+ // src/integrations/agent/profiles.ts
10821
+ var COMMON_PASSTHROUGH, BUILTINS, HEADLESS_BUILTINS, BUILTIN_AGENT_PROFILE_NAMES;
10822
+ var init_profiles = __esm(() => {
10823
+ COMMON_PASSTHROUGH = ["HOME", "PATH", "USER", "LANG", "LC_ALL", "TERM", "TMPDIR"];
10824
+ BUILTINS = {
10825
+ opencode: {
10826
+ name: "opencode",
10827
+ bin: "opencode",
10828
+ args: ["run"],
10829
+ stdio: "interactive",
10830
+ envPassthrough: [...COMMON_PASSTHROUGH, "OPENCODE_API_KEY", "OPENCODE_CONFIG"],
10831
+ parseOutput: "text"
10832
+ },
10833
+ claude: {
10834
+ name: "claude",
10835
+ bin: "claude",
10836
+ args: [],
10837
+ stdio: "interactive",
10838
+ envPassthrough: [...COMMON_PASSTHROUGH, "ANTHROPIC_API_KEY", "CLAUDE_CONFIG"],
10839
+ parseOutput: "text"
10840
+ },
10841
+ codex: {
10842
+ name: "codex",
10843
+ bin: "codex",
10844
+ args: [],
10845
+ stdio: "interactive",
10846
+ envPassthrough: [...COMMON_PASSTHROUGH, "OPENAI_API_KEY", "CODEX_CONFIG"],
10847
+ parseOutput: "text"
10848
+ },
10849
+ gemini: {
10850
+ name: "gemini",
10851
+ bin: "gemini",
10852
+ args: [],
10853
+ stdio: "interactive",
10854
+ envPassthrough: [...COMMON_PASSTHROUGH, "GEMINI_API_KEY", "GOOGLE_API_KEY"],
10855
+ parseOutput: "text"
10856
+ },
10857
+ aider: {
10858
+ name: "aider",
10859
+ bin: "aider",
10860
+ args: ["--no-auto-commits"],
10861
+ stdio: "interactive",
10862
+ envPassthrough: [...COMMON_PASSTHROUGH, "OPENAI_API_KEY", "ANTHROPIC_API_KEY"],
10863
+ parseOutput: "text"
10864
+ }
10865
+ };
10866
+ HEADLESS_BUILTINS = {
10867
+ "opencode-headless": {
10868
+ name: "opencode-headless",
10869
+ bin: "opencode",
10870
+ args: ["run"],
10871
+ stdio: "captured",
10872
+ envPassthrough: [...COMMON_PASSTHROUGH, "OPENCODE_API_KEY", "OPENCODE_CONFIG"],
10873
+ parseOutput: "json"
10874
+ },
10875
+ "claude-headless": {
10876
+ name: "claude-headless",
10877
+ bin: "claude",
10878
+ args: [],
10879
+ stdio: "captured",
10880
+ envPassthrough: [...COMMON_PASSTHROUGH, "ANTHROPIC_API_KEY", "CLAUDE_CONFIG"],
10881
+ parseOutput: "json"
10882
+ },
10883
+ "codex-headless": {
10884
+ name: "codex-headless",
10885
+ bin: "codex",
10886
+ args: [],
10887
+ stdio: "captured",
10888
+ envPassthrough: [...COMMON_PASSTHROUGH, "OPENAI_API_KEY", "CODEX_CONFIG"],
10889
+ parseOutput: "json"
10890
+ },
10891
+ "gemini-headless": {
10892
+ name: "gemini-headless",
10893
+ bin: "gemini",
10894
+ args: [],
10895
+ stdio: "captured",
10896
+ envPassthrough: [...COMMON_PASSTHROUGH, "GEMINI_API_KEY", "GOOGLE_API_KEY"],
10897
+ parseOutput: "json"
10898
+ },
10899
+ "aider-headless": {
10900
+ name: "aider-headless",
10901
+ bin: "aider",
10902
+ args: ["--no-auto-commits"],
10903
+ stdio: "captured",
10904
+ envPassthrough: [...COMMON_PASSTHROUGH, "OPENAI_API_KEY", "ANTHROPIC_API_KEY"],
10905
+ parseOutput: "json"
10906
+ }
10907
+ };
10908
+ BUILTIN_AGENT_PROFILE_NAMES = Object.freeze(Object.keys(BUILTINS).sort());
10909
+ });
10910
+
10911
+ // src/integrations/agent/config.ts
10912
+ var init_config = __esm(() => {
10913
+ init_config2();
10914
+ init_errors();
10915
+ init_warn();
10916
+ init_profiles();
10917
+ });
10918
+
10919
+ // src/integrations/harnesses/opencode-sdk/sdk-runner.ts
10920
+ var init_sdk_runner = __esm(() => {
10921
+ init_config2();
10922
+ init_config();
10923
+ });
10924
+
10925
+ // src/integrations/harnesses/opencode-sdk/index.ts
10926
+ function caps3(c) {
10927
+ return {
10928
+ sessionLogs: false,
10929
+ agentDispatch: false,
10930
+ detection: false,
10931
+ configImport: false,
10932
+ runtimeIdentity: false,
10933
+ v1Migration: false,
10934
+ ...c
10935
+ };
10936
+ }
10937
+ var OpencodeSdkHarness;
10938
+ var init_opencode_sdk = __esm(() => {
10939
+ init_sdk_runner();
10940
+ OpencodeSdkHarness = class OpencodeSdkHarness extends BaseHarness {
10941
+ id = "opencode-sdk";
10942
+ displayName = "OpenCode SDK";
10943
+ aliases = [];
10944
+ v1ProfilePrefixes = ["opencode-sdk"];
10945
+ capabilities = caps3({
10946
+ agentDispatch: true,
10947
+ detection: true,
10948
+ v1Migration: true
10949
+ });
10950
+ };
10951
+ });
10952
+
10953
+ // src/integrations/harnesses/index.ts
10954
+ function v1ProfilePlatform(name) {
10955
+ for (const h of V1_RESOLUTION_ORDER) {
10956
+ if (h.matchesV1ProfileName(name))
10957
+ return h.id;
10958
+ }
10959
+ return;
9995
10960
  }
9996
- var MAX_CONFIG_BACKUPS = 5, CONFIG_LOCK_MAX_RETRIES = 10, CONFIG_LOCK_RETRY_DELAY_MS = 50;
9997
- var init_config_io = __esm(() => {
9998
- init_common();
9999
- init_errors();
10000
- init_file_lock();
10001
- init_paths();
10961
+ var HARNESS_REGISTRY, HARNESS_BY_ID, HARNESS_BY_ANY_ID, VALID_HARNESS_IDS, SESSION_LOG_HARNESSES, AGENT_DISPATCH_HARNESSES, CONFIG_IMPORTER_HARNESSES, DETECTION_HARNESSES, V1_RESOLUTION_ORDER;
10962
+ var init_harnesses = __esm(() => {
10963
+ init_claude();
10964
+ init_opencode();
10965
+ init_opencode_sdk();
10966
+ HARNESS_REGISTRY = Object.freeze([
10967
+ new OpencodeHarness,
10968
+ new ClaudeHarness,
10969
+ new OpencodeSdkHarness
10970
+ ]);
10971
+ HARNESS_BY_ID = new Map(HARNESS_REGISTRY.map((h) => [h.id, h]));
10972
+ HARNESS_BY_ANY_ID = (() => {
10973
+ const m = new Map;
10974
+ for (const h of HARNESS_REGISTRY) {
10975
+ m.set(h.id, h);
10976
+ if (h.runtimeId)
10977
+ m.set(h.runtimeId, h);
10978
+ for (const a of h.aliases)
10979
+ m.set(a, h);
10980
+ }
10981
+ return m;
10982
+ })();
10983
+ VALID_HARNESS_IDS = Object.freeze(HARNESS_REGISTRY.map((h) => h.id));
10984
+ SESSION_LOG_HARNESSES = HARNESS_REGISTRY.filter((h) => h.capabilities.sessionLogs);
10985
+ AGENT_DISPATCH_HARNESSES = HARNESS_REGISTRY.filter((h) => h.capabilities.agentDispatch);
10986
+ CONFIG_IMPORTER_HARNESSES = HARNESS_REGISTRY.filter((h) => h.capabilities.configImport);
10987
+ DETECTION_HARNESSES = HARNESS_REGISTRY.filter((h) => h.capabilities.detection);
10988
+ V1_RESOLUTION_ORDER = [...HARNESS_REGISTRY].sort((a, b) => b.id.length - a.id.length);
10002
10989
  });
10003
10990
 
10004
- // src/core/config-migration.ts
10991
+ // src/core/config/config-migration.ts
10005
10992
  function compareConfigVersion(a, b) {
10006
10993
  const partsA = normalizeVersion(a);
10007
10994
  const partsB = normalizeVersion(b);
@@ -10450,14 +11437,7 @@ function migrateConfigShape(raw) {
10450
11437
  return { changed, result };
10451
11438
  }
10452
11439
  function guessAgentPlatform(name) {
10453
- const lower = name.toLowerCase();
10454
- if (lower === "claude" || lower === "claude-code")
10455
- return "claude";
10456
- if (lower.startsWith("opencode-sdk"))
10457
- return "opencode-sdk";
10458
- if (lower.startsWith("opencode"))
10459
- return "opencode";
10460
- return;
11440
+ return v1ProfilePlatform(name);
10461
11441
  }
10462
11442
  function mapV1ProcessName(name) {
10463
11443
  switch (name) {
@@ -10486,6 +11466,7 @@ function mapV1ProcessName(name) {
10486
11466
  }
10487
11467
  var CURRENT_CONFIG_VERSION = "0.8.0";
10488
11468
  var init_config_migration = __esm(() => {
11469
+ init_harnesses();
10489
11470
  init_warn();
10490
11471
  });
10491
11472
 
@@ -10536,10 +11517,10 @@ var init_util = __esm(() => {
10536
11517
  util2.assertEqual = (_) => {};
10537
11518
  function assertIs(_arg) {}
10538
11519
  util2.assertIs = assertIs;
10539
- function assertNever(_x) {
11520
+ function assertNever2(_x) {
10540
11521
  throw new Error;
10541
11522
  }
10542
- util2.assertNever = assertNever;
11523
+ util2.assertNever = assertNever2;
10543
11524
  util2.arrayToEnum = (items) => {
10544
11525
  const obj = {};
10545
11526
  for (const item of items) {
@@ -10933,8 +11914,8 @@ class ParseStatus {
10933
11914
  }
10934
11915
  }
10935
11916
  var makeIssue = (params) => {
10936
- const { data, path: path8, errorMaps, issueData } = params;
10937
- const fullPath = [...path8, ...issueData.path || []];
11917
+ const { data, path: path10, errorMaps, issueData } = params;
11918
+ const fullPath = [...path10, ...issueData.path || []];
10938
11919
  const fullIssue = {
10939
11920
  ...issueData,
10940
11921
  path: fullPath
@@ -10980,11 +11961,11 @@ var init_errorUtil = __esm(() => {
10980
11961
 
10981
11962
  // node_modules/zod/v3/types.js
10982
11963
  class ParseInputLazyPath {
10983
- constructor(parent, value, path8, key) {
11964
+ constructor(parent, value, path10, key) {
10984
11965
  this._cachedPath = [];
10985
11966
  this.parent = parent;
10986
11967
  this.data = value;
10987
- this._path = path8;
11968
+ this._path = path10;
10988
11969
  this._key = key;
10989
11970
  }
10990
11971
  get path() {
@@ -14455,10 +15436,16 @@ var init_zod = __esm(() => {
14455
15436
  init_external();
14456
15437
  });
14457
15438
 
14458
- // src/core/config-schema.ts
14459
- var positiveInt, nonNegativeNumber, nonEmptyString, httpUrl, LlmCapabilitiesSchema, LlmConnectionConfigSchema, LlmProfileConfigSchema, EmbeddingOllamaOptionsSchema, EmbeddingConnectionConfigSchema, AgentPlatformSchema, AgentProfileConfigSchema, ImproveProcessConfigSchema, ImproveProfileProcessesSchema, ImproveProfileConfigSchema, ProfilesSchema, DefaultsSchema, SourceConfigEntryOptionsSchema, SourceConfigEntrySchema, RegistryConfigEntrySchema, KitSourceSchema, InstalledStashEntrySchema, OutputConfigSchema, SearchGraphBoostSchema, SearchConfigSchema, FeedbackConfigSchema, ImproveUtilityDecaySchema, ImproveConfigSchema, GRAPH_EXTRACTION_INCLUDE_TYPES_ALLOWED, INDEX_PASS_PROVIDER_KEYS, INDEX_PASS_KNOWN_KEYS, IndexPassConfigSchema, MetadataEnhanceSchema, StalenessDetectionSchema, IndexConfigSchema, AkmConfigShape, AkmConfigBaseSchema, AkmConfigSchema;
15439
+ // src/core/config/config-types.ts
15440
+ var init_config_types = __esm(() => {
15441
+ init_harnesses();
15442
+ });
15443
+
15444
+ // src/core/config/config-schema.ts
15445
+ var positiveInt, nonNegativeNumber, nonEmptyString, httpUrl, LlmCapabilitiesSchema, LlmConnectionConfigSchema, LlmProfileConfigSchema, EmbeddingOllamaOptionsSchema, EmbeddingConnectionConfigSchema, AgentPlatformSchema, AgentProfileConfigSchema, ImproveProcessConfigSchema, ImproveProfileProcessesSchema, ImproveProfileConfigSchema, ProfilesSchema, DefaultsSchema, SourceConfigEntryOptionsSchema, SourceConfigEntrySchema, RegistryConfigEntrySchema, KitSourceSchema, InstalledStashEntrySchema, OutputConfigSchema, SearchGraphBoostSchema, SearchConfigSchema, FeedbackConfigSchema, ImproveUtilityDecaySchema, ImproveConfigSchema, GRAPH_EXTRACTION_INCLUDE_TYPES_ALLOWED, INDEX_PASS_PROVIDER_KEYS, INDEX_PASS_KNOWN_KEYS, IndexPassConfigSchema, MetadataEnhanceSchema, StalenessDetectionSchema, IndexConfigSchema, SetupTaskSchedulesSchema, SetupConfigSchema, AkmConfigShape, AkmConfigBaseSchema, AkmConfigSchema;
14460
15446
  var init_config_schema = __esm(() => {
14461
15447
  init_zod();
15448
+ init_config_types();
14462
15449
  positiveInt = exports_external.number().int().positive();
14463
15450
  nonNegativeNumber = exports_external.number().finite().min(0);
14464
15451
  nonEmptyString = exports_external.string().min(1).refine((v) => v.trim().length > 0, { message: "expected a non-empty string" });
@@ -14502,7 +15489,7 @@ var init_config_schema = __esm(() => {
14502
15489
  contextLength: positiveInt.optional(),
14503
15490
  ollamaOptions: EmbeddingOllamaOptionsSchema.optional()
14504
15491
  }).strict();
14505
- AgentPlatformSchema = exports_external.enum(["opencode", "claude", "opencode-sdk"]);
15492
+ AgentPlatformSchema = exports_external.enum(VALID_HARNESS_IDS);
14506
15493
  AgentProfileConfigSchema = exports_external.object({
14507
15494
  platform: AgentPlatformSchema,
14508
15495
  bin: exports_external.string().min(1).optional(),
@@ -14516,12 +15503,16 @@ var init_config_schema = __esm(() => {
14516
15503
  profile: exports_external.string().min(1).optional(),
14517
15504
  timeoutMs: exports_external.union([positiveInt, exports_external.null()]).optional(),
14518
15505
  allowedTypes: exports_external.array(exports_external.string().min(1)).optional(),
15506
+ minPoolSize: exports_external.number().int().min(0).optional(),
14519
15507
  qualityGate: exports_external.object({ enabled: exports_external.boolean().optional() }).strict().optional(),
14520
15508
  contradictionDetection: exports_external.object({ enabled: exports_external.boolean().optional() }).strict().optional(),
14521
15509
  defaultSince: exports_external.string().min(1).optional(),
14522
15510
  maxTotalChars: positiveInt.optional(),
15511
+ minContentChars: exports_external.number().int().min(0).optional(),
14523
15512
  maxChunkSize: exports_external.number().int().min(1).max(50).optional(),
14524
- incrementalSince: exports_external.string().min(1).optional(),
15513
+ minNewSessions: exports_external.number().int().min(0).optional(),
15514
+ indexSessions: exports_external.boolean().optional(),
15515
+ minSessionDuration: exports_external.number().min(0).optional(),
14525
15516
  applyMode: exports_external.enum(["queue", "promote"]).optional(),
14526
15517
  policy: exports_external.string().min(1).optional(),
14527
15518
  maxAcceptsPerRun: positiveInt.optional(),
@@ -14779,6 +15770,13 @@ var init_config_schema = __esm(() => {
14779
15770
  metadataEnhance: MetadataEnhanceSchema.optional(),
14780
15771
  stalenessDetection: StalenessDetectionSchema.optional()
14781
15772
  }).catchall(IndexPassConfigSchema));
15773
+ SetupTaskSchedulesSchema = exports_external.object({
15774
+ improve: exports_external.string().min(1).optional(),
15775
+ index: exports_external.string().min(1).optional()
15776
+ }).strict();
15777
+ SetupConfigSchema = exports_external.object({
15778
+ taskSchedules: SetupTaskSchedulesSchema.optional()
15779
+ }).strict();
14782
15780
  AkmConfigShape = {
14783
15781
  configVersion: exports_external.union([exports_external.string().min(1), exports_external.number()]).optional(),
14784
15782
  profiles: ProfilesSchema.optional(),
@@ -14796,7 +15794,8 @@ var init_config_schema = __esm(() => {
14796
15794
  search: SearchConfigSchema.optional(),
14797
15795
  feedback: FeedbackConfigSchema.optional(),
14798
15796
  archiveRetentionDays: nonNegativeNumber.optional(),
14799
- improve: ImproveConfigSchema.optional()
15797
+ improve: ImproveConfigSchema.optional(),
15798
+ setup: SetupConfigSchema.optional()
14800
15799
  };
14801
15800
  AkmConfigBaseSchema = exports_external.object(AkmConfigShape).strict();
14802
15801
  AkmConfigSchema = AkmConfigBaseSchema.superRefine((config, ctx) => {
@@ -14819,8 +15818,8 @@ var init_config_schema = __esm(() => {
14819
15818
  });
14820
15819
  });
14821
15820
 
14822
- // src/core/config-sources.ts
14823
- import { createHash } from "crypto";
15821
+ // src/core/config/config-sources.ts
15822
+ import { createHash as createHash2 } from "crypto";
14824
15823
  function deriveStashEntryName(entry) {
14825
15824
  if (entry.name)
14826
15825
  return entry.name;
@@ -14829,7 +15828,7 @@ function deriveStashEntryName(entry) {
14829
15828
  path: entry.path ?? null,
14830
15829
  url: entry.url ?? null
14831
15830
  });
14832
- const hash = createHash("sha256").update(seed).digest("hex").slice(0, 8);
15831
+ const hash = createHash2("sha256").update(seed).digest("hex").slice(0, 8);
14833
15832
  return `${entry.type}-${hash}`;
14834
15833
  }
14835
15834
  function parseSourceSpec(entry) {
@@ -14898,7 +15897,7 @@ function toConfiguredSource(persisted, isPrimary) {
14898
15897
  }
14899
15898
  var init_config_sources = () => {};
14900
15899
 
14901
- // src/core/config.ts
15900
+ // src/core/config/config.ts
14902
15901
  var exports_config = {};
14903
15902
  __export(exports_config, {
14904
15903
  updateConfig: () => updateConfig,
@@ -14916,12 +15915,13 @@ __export(exports_config, {
14916
15915
  getIndexPassConfig: () => getIndexPassConfig,
14917
15916
  getEffectiveRegistries: () => getEffectiveRegistries,
14918
15917
  getDefaultLlmConfig: () => getDefaultLlmConfig,
15918
+ VALID_HARNESS_IDS: () => VALID_HARNESS_IDS,
14919
15919
  FEEDBACK_FAILURE_MODES: () => FEEDBACK_FAILURE_MODES,
14920
15920
  DEFAULT_GRAPH_EXTRACTION_BATCH_SIZE: () => DEFAULT_GRAPH_EXTRACTION_BATCH_SIZE,
14921
15921
  DEFAULT_CONFIG: () => DEFAULT_CONFIG
14922
15922
  });
14923
- import fs9 from "fs";
14924
- import path8 from "path";
15923
+ import fs11 from "fs";
15924
+ import path10 from "path";
14925
15925
  function resolveBatchSize(configured, contextLength) {
14926
15926
  const base = configured && configured > 0 ? configured : DEFAULT_GRAPH_EXTRACTION_BATCH_SIZE;
14927
15927
  if (!contextLength || contextLength <= 0)
@@ -14936,7 +15936,7 @@ function loadUserConfig() {
14936
15936
  const configPath = getConfigPath();
14937
15937
  let stat;
14938
15938
  try {
14939
- stat = fs9.statSync(configPath);
15939
+ stat = fs11.statSync(configPath);
14940
15940
  } catch {
14941
15941
  cachedConfig = undefined;
14942
15942
  return applyRuntimeEnvApiKeys({ ...DEFAULT_CONFIG });
@@ -14946,7 +15946,7 @@ function loadUserConfig() {
14946
15946
  }
14947
15947
  let text;
14948
15948
  try {
14949
- text = fs9.readFileSync(configPath, "utf8");
15949
+ text = fs11.readFileSync(configPath, "utf8");
14950
15950
  } catch {
14951
15951
  cachedConfig = undefined;
14952
15952
  return applyRuntimeEnvApiKeys({ ...DEFAULT_CONFIG });
@@ -14955,7 +15955,7 @@ function loadUserConfig() {
14955
15955
  const finalConfig = applyRuntimeEnvApiKeys(parseAndValidate(text, configPath));
14956
15956
  let finalStat = stat;
14957
15957
  try {
14958
- finalStat = fs9.statSync(configPath);
15958
+ finalStat = fs11.statSync(configPath);
14959
15959
  } catch {}
14960
15960
  cachedConfig = {
14961
15961
  config: finalConfig,
@@ -15058,8 +16058,8 @@ function loadConfig() {
15058
16058
  function saveConfig(config) {
15059
16059
  cachedConfig = undefined;
15060
16060
  const configPath = getConfigPath();
15061
- const dir = path8.dirname(configPath);
15062
- fs9.mkdirSync(dir, { recursive: true });
16061
+ const dir = path10.dirname(configPath);
16062
+ fs11.mkdirSync(dir, { recursive: true });
15063
16063
  const sanitized = sanitizeConfigForWrite(config);
15064
16064
  const parseResult = AkmConfigSchema.safeParse(sanitized);
15065
16065
  if (!parseResult.success) {
@@ -15204,14 +16204,14 @@ function applyRuntimeEnvApiKeys(config) {
15204
16204
  return next;
15205
16205
  }
15206
16206
  function warnIfProjectConfigPresent(startDir) {
15207
- let currentDir = path8.resolve(startDir);
16207
+ let currentDir = path10.resolve(startDir);
15208
16208
  while (true) {
15209
- const configPath = path8.join(currentDir, PROJECT_CONFIG_RELATIVE_PATH);
16209
+ const configPath = path10.join(currentDir, PROJECT_CONFIG_RELATIVE_PATH);
15210
16210
  if (isFile(configPath) && !PROJECT_CONFIG_DEPRECATION_WARNED.has(configPath)) {
15211
16211
  PROJECT_CONFIG_DEPRECATION_WARNED.add(configPath);
15212
16212
  warn(`[akm] DEPRECATED: project-level config file found at ${configPath}. ` + "Project-level config files are no longer merged (removed after 0.8.x deprecation). " + "Move any needed settings to ~/.config/akm/config.json; this file is ignored.");
15213
16213
  }
15214
- const parentDir = path8.dirname(currentDir);
16214
+ const parentDir = path10.dirname(currentDir);
15215
16215
  if (parentDir === currentDir)
15216
16216
  break;
15217
16217
  currentDir = parentDir;
@@ -15219,20 +16219,21 @@ function warnIfProjectConfigPresent(startDir) {
15219
16219
  }
15220
16220
  function isFile(filePath) {
15221
16221
  try {
15222
- return fs9.statSync(filePath).isFile();
16222
+ return fs11.statSync(filePath).isFile();
15223
16223
  } catch {
15224
16224
  return false;
15225
16225
  }
15226
16226
  }
15227
16227
  var FEEDBACK_FAILURE_MODES, DEFAULT_GRAPH_EXTRACTION_BATCH_SIZE = 4, GRAPH_EXTRACTION_CHARS_PER_BODY = 1500, DEFAULT_CONFIG, PROJECT_CONFIG_RELATIVE_PATH, cachedConfig, INDEX_RESERVED_KEYS, PROJECT_CONFIG_DEPRECATION_WARNED;
15228
- var init_config = __esm(() => {
16228
+ var init_config2 = __esm(() => {
16229
+ init_errors();
15229
16230
  init_config_io();
15230
16231
  init_config_migration();
15231
16232
  init_config_schema();
15232
- init_errors();
15233
16233
  init_config_io();
15234
16234
  init_paths();
15235
16235
  init_warn();
16236
+ init_config_types();
15236
16237
  init_config_sources();
15237
16238
  FEEDBACK_FAILURE_MODES = [
15238
16239
  "incorrect",
@@ -15252,12 +16253,12 @@ var init_config = __esm(() => {
15252
16253
  detail: "brief"
15253
16254
  }
15254
16255
  };
15255
- PROJECT_CONFIG_RELATIVE_PATH = path8.join(".akm", "config.json");
16256
+ PROJECT_CONFIG_RELATIVE_PATH = path10.join(".akm", "config.json");
15256
16257
  INDEX_RESERVED_KEYS = new Set(["metadataEnhance", "stalenessDetection"]);
15257
16258
  PROJECT_CONFIG_DEPRECATION_WARNED = new Set;
15258
16259
  });
15259
16260
 
15260
- // src/indexer/db.ts
16261
+ // src/indexer/db/db.ts
15261
16262
  var exports_db = {};
15262
16263
  __export(exports_db, {
15263
16264
  warnIfVecMissing: () => warnIfVecMissing,
@@ -15275,7 +16276,7 @@ __export(exports_db, {
15275
16276
  relinkUsageEvents: () => relinkUsageEvents,
15276
16277
  rebuildFts: () => rebuildFts,
15277
16278
  openExistingDatabase: () => openExistingDatabase,
15278
- openDatabase: () => openDatabase,
16279
+ openDatabase: () => openDatabase2,
15279
16280
  isVecAvailable: () => isVecAvailable,
15280
16281
  getZeroResultSearches: () => getZeroResultSearches,
15281
16282
  getUtilityScoresByIds: () => getUtilityScoresByIds,
@@ -15288,11 +16289,15 @@ __export(exports_db, {
15288
16289
  getLlmCacheEntry: () => getLlmCacheEntry,
15289
16290
  getLlmCacheEntriesByRefs: () => getLlmCacheEntriesByRefs,
15290
16291
  getIndexDirState: () => getIndexDirState,
16292
+ getEntryRefRowsForStashRoot: () => getEntryRefRowsForStashRoot,
16293
+ getEntryIdByFilePath: () => getEntryIdByFilePath,
16294
+ getEntryFilePathById: () => getEntryFilePathById,
15291
16295
  getEntryCount: () => getEntryCount,
15292
16296
  getEntryByRef: () => getEntryByRef,
15293
16297
  getEntryById: () => getEntryById,
15294
16298
  getEntriesByDir: () => getEntriesByDir,
15295
16299
  getEmbeddingCount: () => getEmbeddingCount,
16300
+ getEmbeddableEntryCount: () => getEmbeddableEntryCount,
15296
16301
  getDerivedForParent: () => getDerivedForParent,
15297
16302
  getAllEntriesForEmbedding: () => getAllEntriesForEmbedding,
15298
16303
  getAllEntries: () => getAllEntries,
@@ -15304,6 +16309,7 @@ __export(exports_db, {
15304
16309
  deleteEntriesByDir: () => deleteEntriesByDir,
15305
16310
  computeSourceHash: () => computeSourceHash,
15306
16311
  computeBodyHash: () => computeBodyHash,
16312
+ collectTagSetFromEntries: () => collectTagSetFromEntries,
15307
16313
  closeDatabase: () => closeDatabase,
15308
16314
  clearStaleCacheEntries: () => clearStaleCacheEntries,
15309
16315
  bumpUtilityScoresBatch: () => bumpUtilityScoresBatch,
@@ -15314,19 +16320,18 @@ __export(exports_db, {
15314
16320
  EMBEDDING_DIM: () => EMBEDDING_DIM,
15315
16321
  DB_VERSION: () => DB_VERSION
15316
16322
  });
15317
- import { Database as Database2 } from "bun:sqlite";
15318
- import fs10 from "fs";
15319
- import { createRequire } from "module";
15320
- import path9 from "path";
15321
- function openDatabase(dbPath, options) {
16323
+ import fs12 from "fs";
16324
+ import { createRequire as createRequire3 } from "module";
16325
+ import path11 from "path";
16326
+ function openDatabase2(dbPath, options) {
15322
16327
  const resolvedPath = dbPath ?? getDbPath();
15323
- const dir = path9.dirname(resolvedPath);
15324
- if (!fs10.existsSync(dir)) {
15325
- fs10.mkdirSync(dir, { recursive: true });
16328
+ const dir = path11.dirname(resolvedPath);
16329
+ if (!fs12.existsSync(dir)) {
16330
+ fs12.mkdirSync(dir, { recursive: true });
15326
16331
  }
15327
- const db = new Database2(resolvedPath);
16332
+ const db = openDatabase(resolvedPath);
15328
16333
  db.exec("PRAGMA journal_mode = WAL");
15329
- db.exec("PRAGMA busy_timeout = 5000");
16334
+ db.exec("PRAGMA busy_timeout = 30000");
15330
16335
  db.exec("PRAGMA foreign_keys = ON");
15331
16336
  loadVecExtension(db);
15332
16337
  const resolvedDim = options?.embeddingDim ?? resolveConfiguredEmbeddingDim();
@@ -15336,7 +16341,7 @@ function openDatabase(dbPath, options) {
15336
16341
  }
15337
16342
  function resolveConfiguredEmbeddingDim() {
15338
16343
  try {
15339
- const { loadConfig: loadConfig2 } = (init_config(), __toCommonJS(exports_config));
16344
+ const { loadConfig: loadConfig2 } = (init_config2(), __toCommonJS(exports_config));
15340
16345
  const dim = loadConfig2().embedding?.dimension;
15341
16346
  if (typeof dim === "number" && Number.isInteger(dim) && dim > 0 && dim <= 4096) {
15342
16347
  return dim;
@@ -15348,9 +16353,9 @@ function resolveConfiguredEmbeddingDim() {
15348
16353
  }
15349
16354
  function openExistingDatabase(dbPath) {
15350
16355
  const resolvedPath = dbPath ?? getDbPath();
15351
- const db = new Database2(resolvedPath);
16356
+ const db = openDatabase(resolvedPath);
15352
16357
  db.exec("PRAGMA journal_mode = WAL");
15353
- db.exec("PRAGMA busy_timeout = 5000");
16358
+ db.exec("PRAGMA busy_timeout = 30000");
15354
16359
  db.exec("PRAGMA foreign_keys = ON");
15355
16360
  loadVecExtension(db);
15356
16361
  return db;
@@ -15360,7 +16365,7 @@ function closeDatabase(db) {
15360
16365
  }
15361
16366
  function loadVecExtension(db) {
15362
16367
  try {
15363
- const esmRequire = createRequire(import.meta.url);
16368
+ const esmRequire = createRequire3(import.meta.url);
15364
16369
  const sqliteVec = esmRequire("sqlite-vec");
15365
16370
  sqliteVec.load(db);
15366
16371
  vecStatus.set(db, true);
@@ -15376,7 +16381,7 @@ function warnIfVecMissing(db, { once } = { once: false }) {
15376
16381
  return;
15377
16382
  if (once && vecInitWarnedDbs.has(db))
15378
16383
  return;
15379
- try {
16384
+ bestEffort(() => {
15380
16385
  const row = db.prepare("SELECT COUNT(*) AS cnt FROM embeddings").get();
15381
16386
  const count = row?.cnt ?? 0;
15382
16387
  if (count >= VEC_FALLBACK_THRESHOLD) {
@@ -15385,7 +16390,7 @@ function warnIfVecMissing(db, { once } = { once: false }) {
15385
16390
  if (once)
15386
16391
  vecInitWarnedDbs.add(db);
15387
16392
  }
15388
- } catch {}
16393
+ }, "embeddings table may not exist yet during init");
15389
16394
  }
15390
16395
  function ensureSchema(db, embeddingDim, options) {
15391
16396
  db.exec(`
@@ -15591,12 +16596,8 @@ function ensureSchema(db, embeddingDim, options) {
15591
16596
  const storedDim = getMeta(db, "embeddingDim");
15592
16597
  if (storedDim && storedDim !== String(embeddingDim)) {
15593
16598
  backupBeforeEmbeddingDimChange(options?.dataDir, storedDim, String(embeddingDim));
15594
- try {
15595
- db.exec("DROP TABLE IF EXISTS entries_vec");
15596
- } catch {}
15597
- try {
15598
- db.exec("DELETE FROM embeddings");
15599
- } catch {}
16599
+ bestEffort(() => db.exec("DROP TABLE IF EXISTS entries_vec"), "drop entries_vec on dim change");
16600
+ bestEffort(() => db.exec("DELETE FROM embeddings"), "delete stale embeddings on dim change");
15600
16601
  setMeta(db, "hasEmbeddings", "0");
15601
16602
  }
15602
16603
  }
@@ -15620,9 +16621,7 @@ function ensureSchema(db, embeddingDim, options) {
15620
16621
  const storedDim = getMeta(db, "embeddingDim");
15621
16622
  if (storedDim && storedDim !== String(embeddingDim)) {
15622
16623
  backupBeforeEmbeddingDimChange(options?.dataDir, storedDim, String(embeddingDim));
15623
- try {
15624
- db.exec("DELETE FROM embeddings");
15625
- } catch {}
16624
+ bestEffort(() => db.exec("DELETE FROM embeddings"), "delete embeddings on explicit dim change");
15626
16625
  setMeta(db, "hasEmbeddings", "0");
15627
16626
  }
15628
16627
  setMeta(db, "embeddingDim", String(embeddingDim));
@@ -15637,9 +16636,9 @@ function handleVersionUpgrade(db) {
15637
16636
  if (storedVersion === undefined || storedVersion === "" || storedVersion === String(DB_VERSION))
15638
16637
  return [];
15639
16638
  let usageBackup = [];
15640
- try {
16639
+ bestEffort(() => {
15641
16640
  usageBackup = db.prepare("SELECT * FROM usage_events").all();
15642
- } catch {}
16641
+ }, "usage_events table may not exist in older versions");
15643
16642
  db.exec("DROP TABLE IF EXISTS utility_scores");
15644
16643
  db.exec("DROP TABLE IF EXISTS utility_scores_scoped");
15645
16644
  db.exec("DROP INDEX IF EXISTS idx_utility_scores_scoped_entry_id");
@@ -15756,7 +16755,7 @@ function deleteIndexDirState(db, dirPath) {
15756
16755
  db.prepare("DELETE FROM index_dir_state WHERE dir_path = ?").run(dirPath);
15757
16756
  }
15758
16757
  function deleteIndexDirStatesByStashDir(db, stashDir) {
15759
- db.prepare("DELETE FROM index_dir_state WHERE dir_path = ? OR dir_path LIKE ?").run(stashDir, `${stashDir}${path9.sep}%`);
16758
+ db.prepare("DELETE FROM index_dir_state WHERE dir_path = ? OR dir_path LIKE ?").run(stashDir, `${stashDir}${path11.sep}%`);
15760
16759
  }
15761
16760
  function upsertEntry(db, entryKey, dirPath, filePath, stashDir, entry, searchText) {
15762
16761
  const stmts = getUpsertStmts(db);
@@ -15791,14 +16790,14 @@ function getUpsertStmts(db) {
15791
16790
  return stmts;
15792
16791
  }
15793
16792
  function ensureDerivedFromColumn(db) {
15794
- try {
16793
+ bestEffort(() => {
15795
16794
  const cols = db.prepare("PRAGMA table_info(entries)").all();
15796
16795
  const hasColumn = cols.some((c) => c.name === "derived_from");
15797
16796
  if (!hasColumn) {
15798
16797
  db.exec("ALTER TABLE entries ADD COLUMN derived_from TEXT");
15799
16798
  }
15800
16799
  db.exec("CREATE INDEX IF NOT EXISTS idx_entries_derived_from ON entries(derived_from)");
15801
- } catch {}
16800
+ }, "entries table may not exist on a brand-new DB before CREATE \u2014 caller is responsible");
15802
16801
  }
15803
16802
  function getDerivedForParent(db, parentRef) {
15804
16803
  if (!parentRef)
@@ -15838,7 +16837,7 @@ function getPositiveFeedbackCountsByIds(db, ids) {
15838
16837
  for (let i = 0;i < ids.length; i += SQLITE_CHUNK_SIZE) {
15839
16838
  const chunk = ids.slice(i, i + SQLITE_CHUNK_SIZE);
15840
16839
  const placeholders = chunk.map(() => "?").join(",");
15841
- try {
16840
+ bestEffort(() => {
15842
16841
  const rows = db.prepare(`SELECT entry_id, COUNT(*) AS cnt
15843
16842
  FROM usage_events
15844
16843
  WHERE event_type = 'feedback'
@@ -15850,7 +16849,7 @@ function getPositiveFeedbackCountsByIds(db, ids) {
15850
16849
  result.set(row.entry_id, row.cnt);
15851
16850
  }
15852
16851
  }
15853
- } catch {}
16852
+ }, "usage_events table may be missing on legacy DBs \u2014 treat as zero counts");
15854
16853
  }
15855
16854
  return result;
15856
16855
  }
@@ -15875,33 +16874,19 @@ function deleteRelatedRows(db, ids) {
15875
16874
  for (let i = 0;i < numericIds.length; i += SQLITE_CHUNK_SIZE) {
15876
16875
  const chunk = numericIds.slice(i, i + SQLITE_CHUNK_SIZE);
15877
16876
  const placeholders = chunk.map(() => "?").join(",");
15878
- try {
15879
- db.prepare(`DELETE FROM entries_fts WHERE entry_id IN (${placeholders})`).run(...chunk);
15880
- } catch {}
15881
- try {
15882
- db.prepare(`DELETE FROM entries_fts_dirty WHERE entry_id IN (${placeholders})`).run(...chunk);
15883
- } catch {}
16877
+ bestEffort(() => db.prepare(`DELETE FROM entries_fts WHERE entry_id IN (${placeholders})`).run(...chunk), "fts table may not exist on a brand-new db");
16878
+ bestEffort(() => db.prepare(`DELETE FROM entries_fts_dirty WHERE entry_id IN (${placeholders})`).run(...chunk), "fts dirty table is created lazily by upsertEntry");
15884
16879
  }
15885
16880
  for (let i = 0;i < numericIds.length; i += SQLITE_CHUNK_SIZE) {
15886
16881
  const chunk = numericIds.slice(i, i + SQLITE_CHUNK_SIZE);
15887
16882
  const placeholders = chunk.map(() => "?").join(",");
15888
- try {
15889
- db.prepare(`DELETE FROM embeddings WHERE id IN (${placeholders})`).run(...chunk);
15890
- } catch {}
16883
+ bestEffort(() => db.prepare(`DELETE FROM embeddings WHERE id IN (${placeholders})`).run(...chunk), "delete embeddings for entries");
15891
16884
  if (vecAvail) {
15892
- try {
15893
- db.prepare(`DELETE FROM entries_vec WHERE id IN (${placeholders})`).run(...chunk);
15894
- } catch {}
16885
+ bestEffort(() => db.prepare(`DELETE FROM entries_vec WHERE id IN (${placeholders})`).run(...chunk), "delete entries_vec for entries");
15895
16886
  }
15896
- try {
15897
- db.prepare(`DELETE FROM utility_scores WHERE entry_id IN (${placeholders})`).run(...chunk);
15898
- } catch {}
15899
- try {
15900
- db.prepare(`DELETE FROM utility_scores_scoped WHERE entry_id IN (${placeholders})`).run(...chunk);
15901
- } catch {}
15902
- try {
15903
- db.prepare(`DELETE FROM usage_events WHERE entry_id IN (${placeholders})`).run(...chunk);
15904
- } catch {}
16887
+ bestEffort(() => db.prepare(`DELETE FROM utility_scores WHERE entry_id IN (${placeholders})`).run(...chunk), "delete utility_scores for entries");
16888
+ bestEffort(() => db.prepare(`DELETE FROM utility_scores_scoped WHERE entry_id IN (${placeholders})`).run(...chunk), "delete utility_scores_scoped for entries");
16889
+ bestEffort(() => db.prepare(`DELETE FROM usage_events WHERE entry_id IN (${placeholders})`).run(...chunk), "delete usage_events for entries");
15905
16890
  }
15906
16891
  }
15907
16892
  function deleteEntriesByIds(db, ids) {
@@ -15964,12 +16949,12 @@ function upsertEmbedding(db, entryId, embedding) {
15964
16949
  const buf = float32Buffer(embedding);
15965
16950
  db.prepare("INSERT OR REPLACE INTO embeddings (id, embedding) VALUES (?, ?)").run(entryId, buf);
15966
16951
  if (isVecAvailable(db)) {
15967
- try {
16952
+ bestEffort(() => {
15968
16953
  db.transaction(() => {
15969
16954
  db.prepare("DELETE FROM entries_vec WHERE id = ?").run(entryId);
15970
16955
  db.prepare("INSERT INTO entries_vec (id, embedding) VALUES (?, ?)").run(entryId, buf);
15971
16956
  })();
15972
- } catch {}
16957
+ }, "vec table unavailable or constraint failure");
15973
16958
  }
15974
16959
  return true;
15975
16960
  }
@@ -16173,6 +17158,10 @@ function getEntryCount(db) {
16173
17158
  const row = db.prepare("SELECT COUNT(*) AS cnt FROM entries").get();
16174
17159
  return row.cnt;
16175
17160
  }
17161
+ function getEmbeddableEntryCount(db) {
17162
+ const row = db.prepare("SELECT COUNT(*) AS cnt FROM entries").get();
17163
+ return row.cnt;
17164
+ }
16176
17165
  function getEmbeddingCount(db) {
16177
17166
  const row = db.prepare("SELECT COUNT(*) AS cnt FROM embeddings").get();
16178
17167
  return row.cnt;
@@ -16194,6 +17183,17 @@ function getEntriesByDir(db, dirPath) {
16194
17183
  const rows = db.prepare("SELECT id, entry_key, dir_path, file_path, stash_dir, entry_json, search_text FROM entries WHERE dir_path = ?").all(dirPath);
16195
17184
  return parseEntryRows(rows, "getEntriesByDir");
16196
17185
  }
17186
+ function getEntryIdByFilePath(db, filePath) {
17187
+ const row = db.prepare("SELECT id FROM entries WHERE file_path = ? LIMIT 1").get(filePath);
17188
+ return row?.id;
17189
+ }
17190
+ function getEntryFilePathById(db, id) {
17191
+ const row = db.prepare("SELECT file_path FROM entries WHERE id = ?").get(id);
17192
+ return row?.file_path;
17193
+ }
17194
+ function getEntryRefRowsForStashRoot(db, stashRoot) {
17195
+ return db.prepare("SELECT file_path, entry_json FROM entries WHERE stash_dir = ? OR file_path LIKE ?").all(stashRoot, `${stashRoot}%`);
17196
+ }
16197
17197
  function getUtilityScore(db, entryId) {
16198
17198
  const row = db.prepare("SELECT entry_id, utility, show_count, search_count, select_rate, last_used_at, updated_at FROM utility_scores WHERE entry_id = ?").get(entryId);
16199
17199
  if (!row)
@@ -16303,18 +17303,16 @@ function upsertLlmCacheEntry(db, assetRef, bodyHash, resultJson, cacheVariant =
16303
17303
  updated_at = excluded.updated_at`).run(assetRef, cacheVariant, bodyHash, resultJson, Date.now());
16304
17304
  }
16305
17305
  function clearStaleCacheEntries(db) {
16306
- try {
17306
+ bestEffort(() => {
16307
17307
  db.exec(`
16308
17308
  DELETE FROM llm_enrichment_cache
16309
17309
  WHERE asset_ref NOT IN (SELECT file_path FROM entries)
16310
17310
  AND asset_ref NOT IN (SELECT entry_key FROM entries)
16311
17311
  `);
16312
- } catch {}
17312
+ }, "llm_enrichment_cache may not exist in very old DBs opened without ensureSchema");
16313
17313
  }
16314
17314
  function computeBodyHash(body) {
16315
- const hasher = new Bun.CryptoHasher("sha256");
16316
- hasher.update(body);
16317
- return hasher.digest("hex");
17315
+ return sha256Hex(body);
16318
17316
  }
16319
17317
  function getRetrievalCounts(db, refs) {
16320
17318
  if (refs.length === 0)
@@ -16369,7 +17367,6 @@ function getAllEntriesForEmbedding(db) {
16369
17367
  return db.prepare(`
16370
17368
  SELECT e.id, e.search_text AS searchText, e.entry_key AS entryKey, e.file_path AS filePath FROM entries e
16371
17369
  WHERE NOT EXISTS (SELECT 1 FROM embeddings b WHERE b.id = e.id)
16372
- AND e.entry_type != 'vault'
16373
17370
  `).all();
16374
17371
  }
16375
17372
  function upsertWorkflowDocument(db, entryId, doc, content) {
@@ -16433,7 +17430,7 @@ function applyFeedbackToUtilityScore(db, entryId, positiveCount, negativeCount)
16433
17430
  return { previousUtility, nextUtility, crossedReviewThreshold };
16434
17431
  }
16435
17432
  function relinkUsageEvents(db) {
16436
- try {
17433
+ bestEffort(() => {
16437
17434
  db.exec(`
16438
17435
  UPDATE usage_events
16439
17436
  SET entry_id = NULL
@@ -16448,7 +17445,7 @@ function relinkUsageEvents(db) {
16448
17445
  )
16449
17446
  WHERE entry_id IS NULL AND entry_ref IS NOT NULL
16450
17447
  `);
16451
- } catch {}
17448
+ }, "usage_events table may not exist yet during entry_id re-resolution");
16452
17449
  }
16453
17450
  function upsertRegistryIndexCache(db, registryUrl, indexJson, opts) {
16454
17451
  db.prepare(`
@@ -16471,20 +17468,44 @@ function getRegistryIndexCache(db, registryUrl, maxAgeMs = 3600000) {
16471
17468
  return;
16472
17469
  return { indexJson: row.index_json, etag: row.etag, lastModified: row.last_modified };
16473
17470
  }
17471
+ function collectTagSetFromEntries(db, entryType) {
17472
+ const tags = new Set;
17473
+ const stmt = entryType ? db.prepare("SELECT entry_json FROM entries WHERE entry_type = ?") : db.prepare("SELECT entry_json FROM entries");
17474
+ const rows = entryType ? stmt.all(entryType) : stmt.all();
17475
+ for (const row of rows) {
17476
+ let parsed;
17477
+ try {
17478
+ parsed = JSON.parse(row.entry_json);
17479
+ } catch {
17480
+ continue;
17481
+ }
17482
+ if (!Array.isArray(parsed.tags))
17483
+ continue;
17484
+ for (const tag of parsed.tags) {
17485
+ if (typeof tag === "string" && tag.trim().length > 0) {
17486
+ tags.add(tag.trim().toLowerCase());
17487
+ }
17488
+ }
17489
+ }
17490
+ return tags;
17491
+ }
16474
17492
  var DB_VERSION = 17, EMBEDDING_DIM = 384, GRAPH_SCHEMA_VERSION = 3, vecStatus, VEC_DOCS_URL = "https://github.com/itlackey/akm/blob/main/docs/configuration.md#sqlite-vec-extension", VEC_FALLBACK_THRESHOLD = 1e4, vecInitWarnedDbs, SQLITE_CHUNK_SIZE = 500, upsertStmtsByDb, FEEDBACK_LR = 0.1, FEEDBACK_REWARD_POSITIVE = 1, FEEDBACK_REWARD_NEGATIVE = 0, MAX_NEG_DELTA_PER_CALL = 0.15, UTILITY_REVIEW_THRESHOLD = 0.5, HIGH_UTILITY_THRESHOLD = 0.5;
16475
17493
  var init_db = __esm(() => {
16476
17494
  init_asset_ref();
17495
+ init_best_effort();
16477
17496
  init_paths();
16478
17497
  init_state_db();
16479
17498
  init_warn();
16480
17499
  init_types();
17500
+ init_runtime();
17501
+ init_database();
16481
17502
  init_db_backup();
16482
17503
  vecStatus = new WeakMap;
16483
17504
  vecInitWarnedDbs = new WeakSet;
16484
17505
  upsertStmtsByDb = new WeakMap;
16485
17506
  });
16486
17507
 
16487
- // src/indexer/graph-db.ts
17508
+ // src/indexer/db/graph-db.ts
16488
17509
  var exports_graph_db = {};
16489
17510
  __export(exports_graph_db, {
16490
17511
  resolveEntryIdForPath: () => resolveEntryIdForPath,
@@ -16496,12 +17517,12 @@ __export(exports_graph_db, {
16496
17517
  loadGraphEntitiesByEntry: () => loadGraphEntitiesByEntry,
16497
17518
  deleteStoredGraph: () => deleteStoredGraph
16498
17519
  });
16499
- import fs11 from "fs";
17520
+ import fs13 from "fs";
16500
17521
  function withReadableGraphDb(db, fn) {
16501
17522
  if (db)
16502
17523
  return fn(db);
16503
17524
  const dbPath = getDbPath();
16504
- if (!fs11.existsSync(dbPath))
17525
+ if (!fs13.existsSync(dbPath))
16505
17526
  throw new Error("GRAPH_DB_MISSING");
16506
17527
  const opened = openExistingDatabase(dbPath);
16507
17528
  try {
@@ -16721,7 +17742,8 @@ function loadStoredGraphMeta(stashPath, db) {
16721
17742
  cacheHits: row.cache_hits,
16722
17743
  cacheMisses: row.cache_misses,
16723
17744
  truncationCount: row.truncation_count,
16724
- failureCount: row.failure_count
17745
+ failureCount: row.failure_count,
17746
+ retryAttempts: 0
16725
17747
  }
16726
17748
  };
16727
17749
  } catch {
@@ -16821,10 +17843,17 @@ var init_graph_db = __esm(() => {
16821
17843
 
16822
17844
  // scripts/migrate-storage.ts
16823
17845
  init_paths();
16824
- import fs12 from "fs";
16825
- import os from "os";
16826
- import path10 from "path";
17846
+ import fs14 from "fs";
17847
+ import os3 from "os";
17848
+ import path12 from "path";
16827
17849
  import readline from "readline";
17850
+
17851
+ // src/indexer/usage/unmigrated-vaults-guard.ts
17852
+ init_warn();
17853
+ var MIGRATED_MARKER = ".migrated";
17854
+ var warnedStashDirs = new Set;
17855
+
17856
+ // scripts/migrate-storage.ts
16828
17857
  var args = process.argv.slice(2);
16829
17858
  var DRY_RUN = args.includes("--dry-run");
16830
17859
  var YES = args.includes("--yes");
@@ -16841,12 +17870,12 @@ function parseFromArg() {
16841
17870
  return val;
16842
17871
  }
16843
17872
  var FROM_VERSION = parseFromArg();
16844
- var dataDir = process.env.AKM_DATA_DIR?.trim() ?? (process.env.XDG_DATA_HOME?.trim() ? path10.join(process.env.XDG_DATA_HOME.trim(), "akm") : path10.join(os.homedir(), ".local", "share", "akm"));
16845
- var stateDir = process.env.AKM_STATE_DIR?.trim() ?? (process.env.XDG_STATE_HOME?.trim() ? path10.join(process.env.XDG_STATE_HOME.trim(), "akm") : path10.join(os.homedir(), ".local", "state", "akm"));
17873
+ var dataDir = process.env.AKM_DATA_DIR?.trim() ?? (process.env.XDG_DATA_HOME?.trim() ? path12.join(process.env.XDG_DATA_HOME.trim(), "akm") : path12.join(os3.homedir(), ".local", "share", "akm"));
17874
+ var stateDir = process.env.AKM_STATE_DIR?.trim() ?? (process.env.XDG_STATE_HOME?.trim() ? path12.join(process.env.XDG_STATE_HOME.trim(), "akm") : path12.join(os3.homedir(), ".local", "state", "akm"));
16846
17875
  var cacheDir = getCacheDir();
16847
17876
  var configDir = getConfigDir();
16848
- var stateDbPath = path10.join(dataDir, "state.db");
16849
- var indexDbPath = path10.join(dataDir, "index.db");
17877
+ var stateDbPath = path12.join(dataDir, "state.db");
17878
+ var indexDbPath = path12.join(dataDir, "index.db");
16850
17879
  var PATHS = {
16851
17880
  cacheDir,
16852
17881
  configDir,
@@ -16857,92 +17886,97 @@ var PATHS = {
16857
17886
  };
16858
17887
  var versionReports = [];
16859
17888
  function ensureDir(dir) {
16860
- if (!fs12.existsSync(dir)) {
16861
- fs12.mkdirSync(dir, { recursive: true });
17889
+ if (!fs14.existsSync(dir)) {
17890
+ fs14.mkdirSync(dir, { recursive: true });
16862
17891
  }
16863
17892
  }
16864
17893
  function copyAndVerify(src, dest) {
16865
- fs12.copyFileSync(src, dest);
16866
- const srcStat = fs12.statSync(src);
16867
- const destStat = fs12.statSync(dest);
17894
+ fs14.copyFileSync(src, dest);
17895
+ const srcStat = fs14.statSync(src);
17896
+ const destStat = fs14.statSync(dest);
16868
17897
  return destStat.size === srcStat.size;
16869
17898
  }
16870
- function copyDirRecursive(src, dest) {
17899
+ function copyTree(src, dest, opts) {
16871
17900
  let copied = 0;
17901
+ let skipped = 0;
16872
17902
  let failed = 0;
16873
17903
  ensureDir(dest);
16874
- for (const entry of fs12.readdirSync(src, { withFileTypes: true })) {
16875
- const srcEntry = path10.join(src, entry.name);
16876
- const destEntry = path10.join(dest, entry.name);
17904
+ for (const entry of fs14.readdirSync(src, { withFileTypes: true })) {
17905
+ const srcEntry = path12.join(src, entry.name);
17906
+ const destEntry = path12.join(dest, entry.name);
16877
17907
  if (entry.isDirectory()) {
16878
- const sub = copyDirRecursive(srcEntry, destEntry);
17908
+ const sub = copyTree(srcEntry, destEntry, opts);
16879
17909
  copied += sub.copied;
17910
+ skipped += sub.skipped;
16880
17911
  failed += sub.failed;
16881
- } else {
16882
- try {
16883
- if (copyAndVerify(srcEntry, destEntry)) {
16884
- copied++;
16885
- } else {
16886
- failed++;
16887
- }
16888
- } catch {
17912
+ continue;
17913
+ }
17914
+ if (!opts.clobber && fs14.existsSync(destEntry)) {
17915
+ skipped++;
17916
+ continue;
17917
+ }
17918
+ try {
17919
+ if (copyAndVerify(srcEntry, destEntry)) {
17920
+ copied++;
17921
+ } else {
16889
17922
  failed++;
16890
17923
  }
17924
+ } catch {
17925
+ failed++;
16891
17926
  }
16892
17927
  }
16893
- return { copied, failed };
17928
+ return { copied, skipped, failed };
16894
17929
  }
16895
17930
  function countFilesRecursive(dir) {
16896
17931
  let count = 0;
16897
- for (const entry of fs12.readdirSync(dir, { withFileTypes: true })) {
17932
+ for (const entry of fs14.readdirSync(dir, { withFileTypes: true })) {
16898
17933
  if (entry.isDirectory()) {
16899
- count += countFilesRecursive(path10.join(dir, entry.name));
17934
+ count += countFilesRecursive(path12.join(dir, entry.name));
16900
17935
  } else {
16901
17936
  count++;
16902
17937
  }
16903
17938
  }
16904
17939
  return count;
16905
17940
  }
17941
+ async function runSteps(steps, ctx) {
17942
+ for (const step of steps) {
17943
+ ctx.recordStep(await step.run(ctx));
17944
+ }
17945
+ }
16906
17946
  function migrateDb(ctx, filename) {
16907
- const src = path10.join(ctx.paths.cacheDir, filename);
16908
- const dest = path10.join(ctx.paths.dataDir, filename);
16909
- if (!fs12.existsSync(src)) {
16910
- ctx.recordStep({ name: filename, status: "skipped", detail: "source not found" });
16911
- return;
17947
+ const src = path12.join(ctx.paths.cacheDir, filename);
17948
+ const dest = path12.join(ctx.paths.dataDir, filename);
17949
+ if (!fs14.existsSync(src)) {
17950
+ return { name: filename, status: "skipped", detail: "source not found" };
16912
17951
  }
16913
- if (fs12.existsSync(dest)) {
16914
- ctx.recordStep({ name: filename, status: "skipped", detail: "destination already exists" });
16915
- return;
17952
+ if (fs14.existsSync(dest)) {
17953
+ return { name: filename, status: "skipped", detail: "destination already exists" };
16916
17954
  }
16917
17955
  if (ctx.dryRun) {
16918
- ctx.recordStep({ name: filename, status: "success", detail: `[dry-run] would copy ${src} \u2192 ${dest}` });
16919
- return;
17956
+ return { name: filename, status: "success", detail: `[dry-run] would copy ${src} \u2192 ${dest}` };
16920
17957
  }
16921
17958
  ensureDir(ctx.paths.dataDir);
16922
17959
  const ok = copyAndVerify(src, dest);
16923
17960
  if (ok) {
16924
- ctx.recordStep({
17961
+ return {
16925
17962
  name: filename,
16926
17963
  status: "success",
16927
17964
  detail: `copied to ${dest} \u2014 source left at ${src} (delete manually when ready)`
16928
- });
16929
- } else {
16930
- ctx.recordStep({ name: filename, status: "failed", detail: `size mismatch after copy: ${src} \u2192 ${dest}` });
17965
+ };
16931
17966
  }
17967
+ return { name: filename, status: "failed", detail: `size mismatch after copy: ${src} \u2192 ${dest}` };
16932
17968
  }
16933
17969
  async function migrateEventsJsonl(ctx) {
16934
- const src = path10.join(ctx.paths.cacheDir, "events.jsonl");
16935
- if (!fs12.existsSync(src)) {
16936
- ctx.recordStep({ name: "events.jsonl \u2192 state.db", status: "skipped", detail: "source not found" });
16937
- return;
17970
+ const src = path12.join(ctx.paths.cacheDir, "events.jsonl");
17971
+ if (!fs14.existsSync(src)) {
17972
+ return { name: "events.jsonl \u2192 state.db", status: "skipped", detail: "source not found" };
16938
17973
  }
16939
17974
  if (ctx.dryRun) {
16940
- ctx.recordStep({
17975
+ return {
16941
17976
  name: "events.jsonl \u2192 state.db",
16942
17977
  status: "success",
16943
17978
  detail: `[dry-run] would import ${src} into ${ctx.paths.stateDbPath}`
16944
- });
16945
- return;
17979
+ };
16946
17980
  }
16947
17981
  try {
16948
17982
  ensureDir(ctx.paths.dataDir);
@@ -16964,46 +17998,43 @@ async function migrateEventsJsonl(ctx) {
16964
17998
  throw err;
16965
17999
  }
16966
18000
  const dedupNote = skipped > 0 ? ` (${skipped} duplicate(s) skipped \u2014 re-run idempotency)` : "";
16967
- ctx.recordStep({
18001
+ return {
16968
18002
  name: "events.jsonl \u2192 state.db",
16969
18003
  status: "success",
16970
18004
  detail: `imported ${imported} events (max id: ${maxId})${dedupNote} \u2014 source left at ${src} (delete manually when ready)`
16971
- });
18005
+ };
16972
18006
  } finally {
16973
18007
  db.close();
16974
18008
  }
16975
18009
  } catch (err) {
16976
18010
  const msg = err instanceof Error ? err.message : String(err);
16977
- ctx.recordStep({ name: "events.jsonl \u2192 state.db", status: "failed", detail: msg });
18011
+ return { name: "events.jsonl \u2192 state.db", status: "failed", detail: msg };
16978
18012
  }
16979
18013
  }
16980
18014
  function migrateTaskHistory(ctx) {
16981
- const src = path10.join(ctx.paths.cacheDir, "tasks", "history");
16982
- const dest = path10.join(ctx.paths.stateDir, "tasks", "history");
16983
- if (!fs12.existsSync(src)) {
16984
- ctx.recordStep({ name: "tasks/history/", status: "skipped", detail: "source directory not found" });
16985
- return;
18015
+ const src = path12.join(ctx.paths.cacheDir, "tasks", "history");
18016
+ const dest = path12.join(ctx.paths.stateDir, "tasks", "history");
18017
+ if (!fs14.existsSync(src)) {
18018
+ return { name: "tasks/history/", status: "skipped", detail: "source directory not found" };
16986
18019
  }
16987
18020
  try {
16988
- const files = fs12.readdirSync(src).filter((f) => f.endsWith(".jsonl"));
18021
+ const files = fs14.readdirSync(src).filter((f) => f.endsWith(".jsonl"));
16989
18022
  if (files.length === 0) {
16990
- ctx.recordStep({ name: "tasks/history/", status: "skipped", detail: "no *.jsonl files found in source directory" });
16991
- return;
18023
+ return { name: "tasks/history/", status: "skipped", detail: "no *.jsonl files found in source directory" };
16992
18024
  }
16993
18025
  if (ctx.dryRun) {
16994
- ctx.recordStep({
18026
+ return {
16995
18027
  name: "tasks/history/",
16996
18028
  status: "success",
16997
18029
  detail: `[dry-run] would copy ${files.length} *.jsonl file(s) from ${src} \u2192 ${dest}`
16998
- });
16999
- return;
18030
+ };
17000
18031
  }
17001
18032
  ensureDir(dest);
17002
18033
  let copied = 0;
17003
18034
  let failed = 0;
17004
18035
  for (const file of files) {
17005
- const srcFile = path10.join(src, file);
17006
- const destFile = path10.join(dest, file);
18036
+ const srcFile = path12.join(src, file);
18037
+ const destFile = path12.join(dest, file);
17007
18038
  try {
17008
18039
  if (copyAndVerify(srcFile, destFile)) {
17009
18040
  copied++;
@@ -17015,119 +18046,107 @@ function migrateTaskHistory(ctx) {
17015
18046
  }
17016
18047
  }
17017
18048
  if (failed === 0) {
17018
- ctx.recordStep({
18049
+ return {
17019
18050
  name: "tasks/history/",
17020
18051
  status: "success",
17021
18052
  detail: `copied ${copied} files to ${dest} \u2014 sources left in place (delete manually when ready)`
17022
- });
17023
- } else {
17024
- ctx.recordStep({
17025
- name: "tasks/history/",
17026
- status: "failed",
17027
- detail: `copied ${copied}/${files.length} files; ${failed} failed`
17028
- });
18053
+ };
17029
18054
  }
18055
+ return {
18056
+ name: "tasks/history/",
18057
+ status: "failed",
18058
+ detail: `copied ${copied}/${files.length} files; ${failed} failed`
18059
+ };
17030
18060
  } catch (err) {
17031
18061
  const msg = err instanceof Error ? err.message : String(err);
17032
- ctx.recordStep({ name: "tasks/history/", status: "failed", detail: msg });
18062
+ return { name: "tasks/history/", status: "failed", detail: msg };
17033
18063
  }
17034
18064
  }
17035
18065
  function migrateLockfile(ctx) {
17036
- const src = path10.join(ctx.paths.configDir, "akm.lock");
17037
- const dest = path10.join(ctx.paths.dataDir, "akm.lock");
17038
- if (!fs12.existsSync(src)) {
17039
- ctx.recordStep({ name: "akm.lock", status: "skipped", detail: "source not found" });
17040
- return;
18066
+ const src = path12.join(ctx.paths.configDir, "akm.lock");
18067
+ const dest = path12.join(ctx.paths.dataDir, "akm.lock");
18068
+ if (!fs14.existsSync(src)) {
18069
+ return { name: "akm.lock", status: "skipped", detail: "source not found" };
17041
18070
  }
17042
- if (fs12.existsSync(dest)) {
17043
- ctx.recordStep({ name: "akm.lock", status: "skipped", detail: "destination already exists" });
17044
- return;
18071
+ if (fs14.existsSync(dest)) {
18072
+ return { name: "akm.lock", status: "skipped", detail: "destination already exists" };
17045
18073
  }
17046
18074
  if (ctx.dryRun) {
17047
- ctx.recordStep({ name: "akm.lock", status: "success", detail: `[dry-run] would copy ${src} \u2192 ${dest}` });
17048
- return;
18075
+ return { name: "akm.lock", status: "success", detail: `[dry-run] would copy ${src} \u2192 ${dest}` };
17049
18076
  }
17050
18077
  ensureDir(ctx.paths.dataDir);
17051
18078
  const ok = copyAndVerify(src, dest);
17052
18079
  if (ok) {
17053
- ctx.recordStep({
18080
+ return {
17054
18081
  name: "akm.lock",
17055
18082
  status: "success",
17056
18083
  detail: `copied to ${dest} \u2014 source left at ${src}.
17057
18084
  IMPORTANT: akm now reads ONLY from $DATA/akm.lock. If this step is skipped,
17058
18085
  akm will start with an empty lockfile and 'akm add' will rebuild it from scratch.`
17059
- });
17060
- } else {
17061
- ctx.recordStep({ name: "akm.lock", status: "failed", detail: `size mismatch after copy: ${src} \u2192 ${dest}` });
18086
+ };
17062
18087
  }
18088
+ return { name: "akm.lock", status: "failed", detail: `size mismatch after copy: ${src} \u2192 ${dest}` };
17063
18089
  }
17064
18090
  function migrateConfigBackups(ctx) {
17065
- const src = path10.join(ctx.paths.cacheDir, "config-backups");
17066
- const dest = path10.join(ctx.paths.dataDir, "config-backups");
17067
- if (!fs12.existsSync(src)) {
17068
- ctx.recordStep({ name: "config-backups/", status: "skipped", detail: "source directory not found" });
17069
- return;
18091
+ const src = path12.join(ctx.paths.cacheDir, "config-backups");
18092
+ const dest = path12.join(ctx.paths.dataDir, "config-backups");
18093
+ if (!fs14.existsSync(src)) {
18094
+ return { name: "config-backups/", status: "skipped", detail: "source directory not found" };
17070
18095
  }
17071
- if (fs12.existsSync(dest)) {
17072
- ctx.recordStep({ name: "config-backups/", status: "skipped", detail: "destination already exists" });
17073
- return;
18096
+ if (fs14.existsSync(dest)) {
18097
+ return { name: "config-backups/", status: "skipped", detail: "destination already exists" };
17074
18098
  }
17075
18099
  if (ctx.dryRun) {
17076
18100
  let srcCount = 0;
17077
18101
  try {
17078
18102
  srcCount = countFilesRecursive(src);
17079
18103
  } catch {}
17080
- ctx.recordStep({
18104
+ return {
17081
18105
  name: "config-backups/",
17082
18106
  status: "success",
17083
18107
  detail: `[dry-run] would recursively copy ${srcCount} file(s) from ${src} \u2192 ${dest}`
17084
- });
17085
- return;
18108
+ };
17086
18109
  }
17087
18110
  try {
17088
18111
  const srcCount = countFilesRecursive(src);
17089
- const { copied, failed } = copyDirRecursive(src, dest);
17090
- const destCount = fs12.existsSync(dest) ? countFilesRecursive(dest) : 0;
18112
+ const { copied, failed } = copyTree(src, dest, { clobber: true });
18113
+ const destCount = fs14.existsSync(dest) ? countFilesRecursive(dest) : 0;
17091
18114
  if (failed === 0 && destCount === srcCount) {
17092
- ctx.recordStep({
18115
+ return {
17093
18116
  name: "config-backups/",
17094
18117
  status: "success",
17095
18118
  detail: `copied ${copied} files to ${dest} \u2014 sources left in place (delete manually when ready)`
17096
- });
17097
- } else {
17098
- ctx.recordStep({
17099
- name: "config-backups/",
17100
- status: "failed",
17101
- detail: `file count mismatch: source ${srcCount}, destination ${destCount}; ${failed} copy errors`
17102
- });
18119
+ };
17103
18120
  }
18121
+ return {
18122
+ name: "config-backups/",
18123
+ status: "failed",
18124
+ detail: `file count mismatch: source ${srcCount}, destination ${destCount}; ${failed} copy errors`
18125
+ };
17104
18126
  } catch (err) {
17105
18127
  const msg = err instanceof Error ? err.message : String(err);
17106
- ctx.recordStep({ name: "config-backups/", status: "failed", detail: msg });
18128
+ return { name: "config-backups/", status: "failed", detail: msg };
17107
18129
  }
17108
18130
  }
17109
18131
  async function migrateTaskHistoryToDb(ctx) {
17110
- const src = path10.join(ctx.paths.cacheDir, "tasks", "history");
17111
- if (!fs12.existsSync(src)) {
17112
- ctx.recordStep({ name: "tasks/history/ \u2192 state.db", status: "skipped", detail: "source directory not found" });
17113
- return;
18132
+ const src = path12.join(ctx.paths.cacheDir, "tasks", "history");
18133
+ if (!fs14.existsSync(src)) {
18134
+ return { name: "tasks/history/ \u2192 state.db", status: "skipped", detail: "source directory not found" };
17114
18135
  }
17115
- const files = fs12.readdirSync(src).filter((f) => f.endsWith(".jsonl"));
18136
+ const files = fs14.readdirSync(src).filter((f) => f.endsWith(".jsonl"));
17116
18137
  if (files.length === 0) {
17117
- ctx.recordStep({
18138
+ return {
17118
18139
  name: "tasks/history/ \u2192 state.db",
17119
18140
  status: "skipped",
17120
18141
  detail: "no *.jsonl files found in source directory"
17121
- });
17122
- return;
18142
+ };
17123
18143
  }
17124
18144
  if (ctx.dryRun) {
17125
- ctx.recordStep({
18145
+ return {
17126
18146
  name: "tasks/history/ \u2192 state.db",
17127
18147
  status: "success",
17128
18148
  detail: `[dry-run] would parse ${files.length} *.jsonl file(s) and import rows into ${ctx.paths.stateDbPath}`
17129
- });
17130
- return;
18149
+ };
17131
18150
  }
17132
18151
  try {
17133
18152
  ensureDir(ctx.paths.dataDir);
@@ -17137,8 +18156,8 @@ async function migrateTaskHistoryToDb(ctx) {
17137
18156
  let failed = 0;
17138
18157
  try {
17139
18158
  for (const file of files) {
17140
- const filePath = path10.join(src, file);
17141
- const text = fs12.readFileSync(filePath, "utf8");
18159
+ const filePath = path12.join(src, file);
18160
+ const text = fs14.readFileSync(filePath, "utf8");
17142
18161
  const lines = text.split(`
17143
18162
  `).filter((l) => l.trim().length > 0);
17144
18163
  db.exec("BEGIN");
@@ -17180,33 +18199,30 @@ async function migrateTaskHistoryToDb(ctx) {
17180
18199
  db.close();
17181
18200
  }
17182
18201
  if (failed === 0) {
17183
- ctx.recordStep({
18202
+ return {
17184
18203
  name: "tasks/history/ \u2192 state.db",
17185
18204
  status: "success",
17186
18205
  detail: `imported ${imported} task history row(s) from ${files.length} JSONL file(s) into state.db \u2014 sources left in place (delete manually when ready)`
17187
- });
17188
- } else {
17189
- ctx.recordStep({
17190
- name: "tasks/history/ \u2192 state.db",
17191
- status: "failed",
17192
- detail: `imported ${imported} row(s); ${failed} line(s) could not be parsed`
17193
- });
18206
+ };
17194
18207
  }
18208
+ return {
18209
+ name: "tasks/history/ \u2192 state.db",
18210
+ status: "failed",
18211
+ detail: `imported ${imported} row(s); ${failed} line(s) could not be parsed`
18212
+ };
17195
18213
  } catch (err) {
17196
18214
  const msg = err instanceof Error ? err.message : String(err);
17197
- ctx.recordStep({ name: "tasks/history/ \u2192 state.db", status: "failed", detail: msg });
18215
+ return { name: "tasks/history/ \u2192 state.db", status: "failed", detail: msg };
17198
18216
  }
17199
18217
  }
17200
18218
  function noteRegistryIndexCache(ctx) {
17201
- const src = path10.join(ctx.paths.cacheDir, "registry-index");
17202
- if (!fs12.existsSync(src)) {
17203
- ctx.recordStep({ name: "registry-index/ (note)", status: "skipped", detail: "no old $CACHE/registry-index/ directory found" });
17204
- return;
18219
+ const src = path12.join(ctx.paths.cacheDir, "registry-index");
18220
+ if (!fs14.existsSync(src)) {
18221
+ return { name: "registry-index/ (note)", status: "skipped", detail: "no old $CACHE/registry-index/ directory found" };
17205
18222
  }
17206
- const legacyFiles = fs12.readdirSync(src).filter((f) => f.endsWith(".json") && !f.startsWith("website-"));
18223
+ const legacyFiles = fs14.readdirSync(src).filter((f) => f.endsWith(".json") && !f.startsWith("website-"));
17207
18224
  if (legacyFiles.length === 0) {
17208
- ctx.recordStep({ name: "registry-index/ (note)", status: "skipped", detail: "no old *.json cache files found" });
17209
- return;
18225
+ return { name: "registry-index/ (note)", status: "skipped", detail: "no old *.json cache files found" };
17210
18226
  }
17211
18227
  if (!ctx.dryRun) {
17212
18228
  console.log(`
@@ -17214,57 +18230,57 @@ function noteRegistryIndexCache(ctx) {
17214
18230
  These are ignored in v0.9 \u2014 data is now stored in the registry_index_cache` + `
17215
18231
  table in $DATA/index.db and will be rebuilt on next 'akm registry search'.
17216
18232
  You may safely delete these files after migration:
17217
- ` + legacyFiles.map((f) => ` ${path10.join(src, f)}`).join(`
18233
+ ` + legacyFiles.map((f) => ` ${path12.join(src, f)}`).join(`
17218
18234
  `));
17219
18235
  }
17220
- ctx.recordStep({
18236
+ return {
17221
18237
  name: "registry-index/ (note)",
17222
18238
  status: "success",
17223
18239
  detail: `${legacyFiles.length} old file(s) noted at ${src} \u2014 registry index cache will be rebuilt on next ` + `'akm registry search'. Safe to delete: ${src}/*.json`
17224
- });
18240
+ };
17225
18241
  }
17226
18242
  var v07To08Migration = {
17227
18243
  label: "0.7 \u2192 0.8",
17228
18244
  sourceVersion: "0.7",
17229
18245
  isNeeded: (paths) => {
17230
18246
  const candidates = [
17231
- path10.join(paths.cacheDir, "index.db"),
17232
- path10.join(paths.cacheDir, "workflow.db"),
17233
- path10.join(paths.cacheDir, "events.jsonl"),
17234
- path10.join(paths.cacheDir, "tasks", "history"),
17235
- path10.join(paths.configDir, "akm.lock"),
17236
- path10.join(paths.cacheDir, "config-backups"),
17237
- path10.join(paths.cacheDir, "registry-index")
18247
+ path12.join(paths.cacheDir, "index.db"),
18248
+ path12.join(paths.cacheDir, "workflow.db"),
18249
+ path12.join(paths.cacheDir, "events.jsonl"),
18250
+ path12.join(paths.cacheDir, "tasks", "history"),
18251
+ path12.join(paths.configDir, "akm.lock"),
18252
+ path12.join(paths.cacheDir, "config-backups"),
18253
+ path12.join(paths.cacheDir, "registry-index")
17238
18254
  ];
17239
- return candidates.some((p) => fs12.existsSync(p));
18255
+ return candidates.some((p) => fs14.existsSync(p));
17240
18256
  },
17241
- run: async (ctx) => {
17242
- migrateDb(ctx, "index.db");
17243
- migrateDb(ctx, "workflow.db");
17244
- await migrateEventsJsonl(ctx);
17245
- migrateTaskHistory(ctx);
17246
- migrateLockfile(ctx);
17247
- migrateConfigBackups(ctx);
17248
- await migrateTaskHistoryToDb(ctx);
17249
- noteRegistryIndexCache(ctx);
17250
- }
18257
+ steps: [
18258
+ { id: "index-db", title: "index.db", run: (ctx) => migrateDb(ctx, "index.db") },
18259
+ { id: "workflow-db", title: "workflow.db", run: (ctx) => migrateDb(ctx, "workflow.db") },
18260
+ { id: "events-jsonl", title: "events.jsonl \u2192 state.db", run: migrateEventsJsonl },
18261
+ { id: "tasks-history-files", title: "tasks/history/", run: migrateTaskHistory },
18262
+ { id: "lockfile", title: "akm.lock", run: migrateLockfile },
18263
+ { id: "config-backups", title: "config-backups/", run: migrateConfigBackups },
18264
+ { id: "tasks-history-db", title: "tasks/history/ \u2192 state.db", run: migrateTaskHistoryToDb },
18265
+ { id: "registry-index-note", title: "registry-index/ (note)", run: noteRegistryIndexCache }
18266
+ ]
17251
18267
  };
17252
18268
  function legacyGraphCandidatePaths(ctx) {
17253
18269
  return [
17254
- path10.join(ctx.paths.cacheDir, "graph-snapshot.json"),
17255
- path10.join(ctx.paths.dataDir, "graph-snapshot.json"),
17256
- path10.join(ctx.paths.dataDir, "graph-export.json")
18270
+ path12.join(ctx.paths.cacheDir, "graph-snapshot.json"),
18271
+ path12.join(ctx.paths.dataDir, "graph-snapshot.json"),
18272
+ path12.join(ctx.paths.dataDir, "graph-export.json")
17257
18273
  ];
17258
18274
  }
17259
18275
  function findLegacyGraphFile(ctx) {
17260
18276
  for (const candidate of legacyGraphCandidatePaths(ctx)) {
17261
- if (fs12.existsSync(candidate) && fs12.statSync(candidate).isFile())
18277
+ if (fs14.existsSync(candidate) && fs14.statSync(candidate).isFile())
17262
18278
  return candidate;
17263
18279
  }
17264
- const graphDir = path10.join(ctx.paths.cacheDir, "graph");
17265
- if (fs12.existsSync(graphDir) && fs12.statSync(graphDir).isDirectory()) {
18280
+ const graphDir = path12.join(ctx.paths.cacheDir, "graph");
18281
+ if (fs14.existsSync(graphDir) && fs14.statSync(graphDir).isDirectory()) {
17266
18282
  try {
17267
- const json = fs12.readdirSync(graphDir).filter((f) => f.endsWith(".json")).map((f) => path10.join(graphDir, f));
18283
+ const json = fs14.readdirSync(graphDir).filter((f) => f.endsWith(".json")).map((f) => path12.join(graphDir, f));
17268
18284
  if (json.length > 0)
17269
18285
  return json[0];
17270
18286
  } catch {}
@@ -17298,43 +18314,38 @@ async function migrateGraphFileToDb(ctx) {
17298
18314
  const name = "Graph snapshot import";
17299
18315
  const legacyFile = findLegacyGraphFile(ctx);
17300
18316
  if (!legacyFile) {
17301
- ctx.recordStep({ name, status: "skipped", detail: "no legacy graph file found" });
17302
- return;
18317
+ return { name, status: "skipped", detail: "no legacy graph file found" };
17303
18318
  }
17304
- if (!fs12.existsSync(ctx.paths.indexDbPath)) {
17305
- ctx.recordStep({
18319
+ if (!fs14.existsSync(ctx.paths.indexDbPath)) {
18320
+ return {
17306
18321
  name,
17307
18322
  status: "skipped",
17308
18323
  detail: `index.db not found at ${ctx.paths.indexDbPath}; run akm to initialize, then re-run this migration`
17309
- });
17310
- return;
18324
+ };
17311
18325
  }
17312
18326
  if (ctx.dryRun) {
17313
- ctx.recordStep({
18327
+ return {
17314
18328
  name,
17315
18329
  status: "success",
17316
18330
  detail: `[dry-run] would parse ${legacyFile} and import into ${ctx.paths.indexDbPath} graph tables`
17317
- });
17318
- return;
18331
+ };
17319
18332
  }
17320
18333
  try {
17321
- const raw = fs12.readFileSync(legacyFile, "utf8");
18334
+ const raw = fs14.readFileSync(legacyFile, "utf8");
17322
18335
  let parsed;
17323
18336
  try {
17324
18337
  parsed = JSON.parse(raw);
17325
18338
  } catch (err) {
17326
18339
  const msg = err instanceof Error ? err.message : String(err);
17327
- ctx.recordStep({ name, status: "failed", detail: `${legacyFile} is not valid JSON: ${msg}` });
17328
- return;
18340
+ return { name, status: "failed", detail: `${legacyFile} is not valid JSON: ${msg}` };
17329
18341
  }
17330
18342
  const validation = validateGraphSnapshot(parsed);
17331
18343
  if (!validation.ok) {
17332
- ctx.recordStep({
18344
+ return {
17333
18345
  name,
17334
18346
  status: "failed",
17335
18347
  detail: `${legacyFile} does not match expected GraphSnapshot shape: ${validation.reason}`
17336
- });
17337
- return;
18348
+ };
17338
18349
  }
17339
18350
  const snapshot = validation.data;
17340
18351
  const { openExistingDatabase: openExistingDatabase2, closeDatabase: closeDatabase2 } = await Promise.resolve().then(() => (init_db(), exports_db));
@@ -17382,51 +18393,48 @@ async function migrateGraphFileToDb(ctx) {
17382
18393
  const importedCount = db.prepare("SELECT COUNT(*) AS cnt FROM graph_files WHERE stash_root = ?").get(snapshot.stashRoot).cnt;
17383
18394
  const meta = loadStoredGraphMeta2(snapshot.stashRoot, db);
17384
18395
  if (!meta) {
17385
- ctx.recordStep({
18396
+ return {
17386
18397
  name,
17387
18398
  status: "failed",
17388
18399
  detail: `import did not produce a graph_meta row for stash ${snapshot.stashRoot}`
17389
- });
17390
- return;
18400
+ };
17391
18401
  }
17392
18402
  if (importedCount === 0) {
17393
- ctx.recordStep({
18403
+ return {
17394
18404
  name,
17395
18405
  status: "failed",
17396
18406
  detail: `import produced zero graph_files rows for stash ${snapshot.stashRoot} \u2014 ` + `the entries table has no matching paths. Run "akm index" first, then retry the migration. Source file ${legacyFile} was NOT renamed.`
17397
- });
17398
- return;
18407
+ };
17399
18408
  }
17400
18409
  const renamed = `${legacyFile}.migrated`;
17401
18410
  try {
17402
- fs12.renameSync(legacyFile, renamed);
18411
+ fs14.renameSync(legacyFile, renamed);
17403
18412
  } catch (err) {
17404
18413
  const msg = err instanceof Error ? err.message : String(err);
17405
- ctx.recordStep({
18414
+ return {
17406
18415
  name,
17407
18416
  status: "success",
17408
18417
  detail: `imported graph for ${snapshot.stashRoot} into ${ctx.paths.indexDbPath} but could not rename ${legacyFile}: ${msg}`
17409
- });
17410
- return;
18418
+ };
17411
18419
  }
17412
- ctx.recordStep({
18420
+ return {
17413
18421
  name,
17414
18422
  status: "success",
17415
18423
  detail: `imported graph snapshot from ${legacyFile} into ${ctx.paths.indexDbPath} (stash ${snapshot.stashRoot}; ${importedCount} file(s) imported of ${graph.files.length} in source). Source renamed to ${renamed} \u2014 delete manually when ready.`
17416
- });
18424
+ };
17417
18425
  } finally {
17418
18426
  closeDatabase2(db);
17419
18427
  }
17420
18428
  } catch (err) {
17421
18429
  const msg = err instanceof Error ? err.message : String(err);
17422
- ctx.recordStep({ name, status: "failed", detail: msg });
18430
+ return { name, status: "failed", detail: msg };
17423
18431
  }
17424
18432
  }
17425
18433
  function resolvePrimaryStashDir(configDirPath) {
17426
18434
  try {
17427
- const cfgPath = path10.join(configDirPath, "config.json");
17428
- if (fs12.existsSync(cfgPath)) {
17429
- const cfg = JSON.parse(fs12.readFileSync(cfgPath, "utf8"));
18435
+ const cfgPath = path12.join(configDirPath, "config.json");
18436
+ if (fs14.existsSync(cfgPath)) {
18437
+ const cfg = JSON.parse(fs14.readFileSync(cfgPath, "utf8"));
17430
18438
  if (typeof cfg.stashDir === "string" && cfg.stashDir.length > 0)
17431
18439
  return cfg.stashDir;
17432
18440
  }
@@ -17435,8 +18443,8 @@ function resolvePrimaryStashDir(configDirPath) {
17435
18443
  }
17436
18444
  function countEnvFilesRecursive(dir) {
17437
18445
  let count = 0;
17438
- for (const entry of fs12.readdirSync(dir, { withFileTypes: true })) {
17439
- const full = path10.join(dir, entry.name);
18446
+ for (const entry of fs14.readdirSync(dir, { withFileTypes: true })) {
18447
+ const full = path12.join(dir, entry.name);
17440
18448
  if (entry.isDirectory()) {
17441
18449
  count += countEnvFilesRecursive(full);
17442
18450
  } else if (entry.name === ".env" || entry.name.endsWith(".env")) {
@@ -17445,46 +18453,16 @@ function countEnvFilesRecursive(dir) {
17445
18453
  }
17446
18454
  return count;
17447
18455
  }
17448
- function copyDirNoClobber(src, dest) {
17449
- let copied = 0;
17450
- let skipped = 0;
17451
- let failed = 0;
17452
- ensureDir(dest);
17453
- for (const entry of fs12.readdirSync(src, { withFileTypes: true })) {
17454
- const srcEntry = path10.join(src, entry.name);
17455
- const destEntry = path10.join(dest, entry.name);
17456
- if (entry.isDirectory()) {
17457
- const sub = copyDirNoClobber(srcEntry, destEntry);
17458
- copied += sub.copied;
17459
- skipped += sub.skipped;
17460
- failed += sub.failed;
17461
- continue;
17462
- }
17463
- if (fs12.existsSync(destEntry)) {
17464
- skipped++;
17465
- continue;
17466
- }
17467
- try {
17468
- if (copyAndVerify(srcEntry, destEntry))
17469
- copied++;
17470
- else
17471
- failed++;
17472
- } catch {
17473
- failed++;
17474
- }
17475
- }
17476
- return { copied, skipped, failed };
17477
- }
17478
18456
  function chmodTreeSecure(dir) {
17479
18457
  const isWin = process.platform === "win32";
17480
18458
  try {
17481
- fs12.chmodSync(dir, 448);
18459
+ fs14.chmodSync(dir, 448);
17482
18460
  } catch (err) {
17483
18461
  if (!isWin)
17484
18462
  return { ok: false, detail: `chmod 0700 ${dir} failed: ${err instanceof Error ? err.message : err}` };
17485
18463
  }
17486
- for (const entry of fs12.readdirSync(dir, { withFileTypes: true })) {
17487
- const full = path10.join(dir, entry.name);
18464
+ for (const entry of fs14.readdirSync(dir, { withFileTypes: true })) {
18465
+ const full = path12.join(dir, entry.name);
17488
18466
  if (entry.isDirectory()) {
17489
18467
  const sub = chmodTreeSecure(full);
17490
18468
  if (!sub.ok)
@@ -17492,8 +18470,8 @@ function chmodTreeSecure(dir) {
17492
18470
  continue;
17493
18471
  }
17494
18472
  try {
17495
- fs12.chmodSync(full, 384);
17496
- if (!isWin && (fs12.statSync(full).mode & 511) !== 384) {
18473
+ fs14.chmodSync(full, 384);
18474
+ if (!isWin && (fs14.statSync(full).mode & 511) !== 384) {
17497
18475
  return { ok: false, detail: `mode verification failed for ${full}` };
17498
18476
  }
17499
18477
  } catch (err) {
@@ -17506,59 +18484,52 @@ function chmodTreeSecure(dir) {
17506
18484
  async function migrateVaultsToEnv(ctx) {
17507
18485
  const name = "vaults/ \u2192 env/";
17508
18486
  const stashDir = resolvePrimaryStashDir(ctx.paths.configDir);
17509
- const vaultsDir = path10.join(stashDir, "vaults");
17510
- const envDir = path10.join(stashDir, "env");
17511
- const marker = path10.join(vaultsDir, ".migrated");
17512
- if (!fs12.existsSync(vaultsDir)) {
17513
- ctx.recordStep({ name, status: "skipped", detail: `no vaults/ directory under ${stashDir}` });
17514
- return;
18487
+ const vaultsDir = path12.join(stashDir, "vaults");
18488
+ const envDir = path12.join(stashDir, "env");
18489
+ const marker = path12.join(vaultsDir, MIGRATED_MARKER);
18490
+ if (!fs14.existsSync(vaultsDir)) {
18491
+ return { name, status: "skipped", detail: `no vaults/ directory under ${stashDir}` };
17515
18492
  }
17516
- if (fs12.existsSync(marker)) {
17517
- ctx.recordStep({ name, status: "skipped", detail: "already migrated (.migrated marker present)" });
17518
- return;
18493
+ if (fs14.existsSync(marker)) {
18494
+ return { name, status: "skipped", detail: "already migrated (.migrated marker present)" };
17519
18495
  }
17520
18496
  const vaultEnvCount = countEnvFilesRecursive(vaultsDir);
17521
18497
  if (vaultEnvCount === 0) {
17522
- ctx.recordStep({ name, status: "skipped", detail: "vaults/ has no .env files to migrate" });
17523
- return;
18498
+ return { name, status: "skipped", detail: "vaults/ has no .env files to migrate" };
17524
18499
  }
17525
18500
  if (ctx.dryRun) {
17526
- ctx.recordStep({
18501
+ return {
17527
18502
  name,
17528
18503
  status: "success",
17529
18504
  detail: `[dry-run] would copy ${vaultEnvCount} .env file(s) ${vaultsDir} \u2192 ${envDir}, chmod 0600/0700, and write ${marker}`
17530
- });
17531
- return;
18505
+ };
17532
18506
  }
17533
18507
  try {
17534
- const { copied, skipped, failed } = copyDirNoClobber(vaultsDir, envDir);
18508
+ const { copied, skipped, failed } = copyTree(vaultsDir, envDir, { clobber: false });
17535
18509
  if (failed > 0) {
17536
- ctx.recordStep({ name, status: "failed", detail: `${failed} file(s) failed to copy; env/ left as-is, no marker written` });
17537
- return;
18510
+ return { name, status: "failed", detail: `${failed} file(s) failed to copy; env/ left as-is, no marker written` };
17538
18511
  }
17539
18512
  const chmodRes = chmodTreeSecure(envDir);
17540
18513
  if (!chmodRes.ok) {
17541
- ctx.recordStep({ name, status: "failed", detail: chmodRes.detail ?? "permission tightening failed" });
17542
- return;
18514
+ return { name, status: "failed", detail: chmodRes.detail ?? "permission tightening failed" };
17543
18515
  }
17544
18516
  const envEnvCount = countEnvFilesRecursive(envDir);
17545
18517
  if (envEnvCount < vaultEnvCount) {
17546
- ctx.recordStep({
18518
+ return {
17547
18519
  name,
17548
18520
  status: "failed",
17549
18521
  detail: `post-copy count ${envEnvCount} < source ${vaultEnvCount}; no marker written`
17550
- });
17551
- return;
18522
+ };
17552
18523
  }
17553
- fs12.writeFileSync(marker, `migrated vaults/ -> env/ at ${new Date().toISOString()}
18524
+ fs14.writeFileSync(marker, `migrated vaults/ -> env/ at ${new Date().toISOString()}
17554
18525
  `, { mode: 384 });
17555
- ctx.recordStep({
18526
+ return {
17556
18527
  name,
17557
18528
  status: "success",
17558
18529
  detail: `copied ${copied} file(s) (${skipped} already present in env/, preserved) ${vaultsDir} \u2192 ${envDir}; vaults/ left intact as a frozen copy. Run \`akm index\` to refresh search.`
17559
- });
18530
+ };
17560
18531
  } catch (err) {
17561
- ctx.recordStep({ name, status: "failed", detail: err instanceof Error ? err.message : String(err) });
18532
+ return { name, status: "failed", detail: err instanceof Error ? err.message : String(err) };
17562
18533
  }
17563
18534
  }
17564
18535
  var v08To09Migration = {
@@ -17567,10 +18538,10 @@ var v08To09Migration = {
17567
18538
  isNeeded: (_paths) => {
17568
18539
  return true;
17569
18540
  },
17570
- run: async (ctx) => {
17571
- await migrateGraphFileToDb(ctx);
17572
- await migrateVaultsToEnv(ctx);
17573
- }
18541
+ steps: [
18542
+ { id: "graph-snapshot-import", title: "Graph snapshot import", run: migrateGraphFileToDb },
18543
+ { id: "vaults-to-env", title: "vaults/ \u2192 env/", run: migrateVaultsToEnv }
18544
+ ]
17574
18545
  };
17575
18546
  var MIGRATIONS2 = [v07To08Migration, v08To09Migration];
17576
18547
  async function confirm() {
@@ -17656,12 +18627,12 @@ task_history: task_id PK).
17656
18627
  Migration complete. No errors.`);
17657
18628
  console.log(`
17658
18629
  Old files at the original locations are safe to delete manually after verifying akm works:
17659
- ${path10.join(cacheDir, "index.db")}
17660
- ${path10.join(cacheDir, "workflow.db")}
17661
- ${path10.join(cacheDir, "events.jsonl")}
17662
- ${path10.join(cacheDir, "tasks", "history")}
17663
- ${path10.join(cacheDir, "config-backups")}
17664
- ${path10.join(configDir, "akm.lock")}
18630
+ ${path12.join(cacheDir, "index.db")}
18631
+ ${path12.join(cacheDir, "workflow.db")}
18632
+ ${path12.join(cacheDir, "events.jsonl")}
18633
+ ${path12.join(cacheDir, "tasks", "history")}
18634
+ ${path12.join(cacheDir, "config-backups")}
18635
+ ${path12.join(configDir, "akm.lock")}
17665
18636
 
17666
18637
  Next step \u2014 repopulate graph data (if migrating from 0.7):
17667
18638
  The 0.8.0 graph schema redesign (DB_VERSION 12 \u2192 13) rebuilds the graph
@@ -17673,7 +18644,7 @@ Next step \u2014 repopulate graph data (if migrating from 0.7):
17673
18644
  docs/migration/v0.7-to-v0.8.md#graph-extraction-will-re-run-after-upgrade.
17674
18645
  `);
17675
18646
  }
17676
- async function runMigrations2(opts = { dryRun: DRY_RUN }) {
18647
+ async function runMigrations3(opts = { dryRun: DRY_RUN }) {
17677
18648
  const paths = opts.paths ?? PATHS;
17678
18649
  const reports = [];
17679
18650
  const migrations = filteredMigrations();
@@ -17702,7 +18673,7 @@ async function runMigrations2(opts = { dryRun: DRY_RUN }) {
17702
18673
  continue;
17703
18674
  }
17704
18675
  try {
17705
- await migration.run(ctx);
18676
+ await runSteps(migration.steps, ctx);
17706
18677
  reports.push({ label: migration.label, ran: true, results: stepResults });
17707
18678
  } catch (err) {
17708
18679
  const msg = err instanceof Error ? err.message : String(err);
@@ -17741,7 +18712,7 @@ async function main() {
17741
18712
  console.log(`[dry-run] No changes will be written.
17742
18713
  `);
17743
18714
  }
17744
- const reports = await runMigrations2({ dryRun: DRY_RUN, paths: PATHS });
18715
+ const reports = await runMigrations3({ dryRun: DRY_RUN, paths: PATHS });
17745
18716
  versionReports.push(...reports);
17746
18717
  printGroupedSummary();
17747
18718
  if (DRY_RUN) {
@@ -17754,7 +18725,7 @@ var invokedDirectly = (() => {
17754
18725
  if (!entry)
17755
18726
  return false;
17756
18727
  const here = new URL(import.meta.url).pathname;
17757
- return path10.resolve(entry) === here;
18728
+ return path12.resolve(entry) === here;
17758
18729
  } catch {
17759
18730
  return false;
17760
18731
  }
@@ -17763,6 +18734,6 @@ if (invokedDirectly) {
17763
18734
  await main();
17764
18735
  }
17765
18736
  export {
17766
- runMigrations2 as runMigrations,
18737
+ runMigrations3 as runMigrations,
17767
18738
  MIGRATIONS2 as MIGRATIONS
17768
18739
  };