akm-cli 0.9.0-beta.6 → 0.9.0-rc.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (326) hide show
  1. package/CHANGELOG.md +663 -0
  2. package/README.md +12 -4
  3. package/dist/akm +38 -0
  4. package/dist/akm-migrate-storage +38 -0
  5. package/dist/assets/help/help-improve.md +9 -6
  6. package/dist/assets/hints/cli-hints-full.md +6 -5
  7. package/dist/assets/profiles/default.json +9 -4
  8. package/dist/assets/profiles/frequent.json +1 -1
  9. package/dist/assets/profiles/memory-focus.json +1 -1
  10. package/dist/assets/profiles/proactive-maintenance.json +25 -0
  11. package/dist/assets/profiles/quick.json +1 -1
  12. package/dist/assets/profiles/recombine-only.json +21 -0
  13. package/dist/assets/profiles/reflect-distill.json +30 -0
  14. package/dist/assets/profiles/synthesize.json +15 -0
  15. package/dist/assets/profiles/thorough.json +1 -1
  16. package/dist/assets/prompts/consolidate-system.md +23 -0
  17. package/dist/assets/prompts/contradiction-judge.md +33 -0
  18. package/dist/assets/prompts/distill-knowledge-system.md +22 -0
  19. package/dist/assets/prompts/distill-lesson-system.md +36 -0
  20. package/dist/assets/prompts/extract-session.md +11 -3
  21. package/dist/assets/prompts/graph-extract-system.md +1 -0
  22. package/dist/assets/prompts/graph-extract-user-prompt.md +1 -1
  23. package/dist/assets/prompts/memory-infer-system.md +1 -0
  24. package/dist/assets/prompts/memory-infer-user.md +5 -0
  25. package/dist/assets/prompts/metadata-enhance-system.md +1 -0
  26. package/dist/assets/prompts/procedural-system.md +44 -0
  27. package/dist/assets/prompts/recombine-system.md +40 -0
  28. package/dist/assets/prompts/staleness-detect-system.md +6 -0
  29. package/dist/assets/prompts/validate-summary-judge.md +1 -0
  30. package/dist/assets/stash-skeleton/facts/conventions/assets/agent.md +38 -0
  31. package/dist/assets/stash-skeleton/facts/conventions/assets/command.md +38 -0
  32. package/dist/assets/stash-skeleton/facts/conventions/assets/fact.md +39 -0
  33. package/dist/assets/stash-skeleton/facts/conventions/assets/knowledge.md +40 -0
  34. package/dist/assets/stash-skeleton/facts/conventions/assets/lesson.md +43 -0
  35. package/dist/assets/stash-skeleton/facts/conventions/assets/memory.md +38 -0
  36. package/dist/assets/stash-skeleton/facts/conventions/assets/script.md +43 -0
  37. package/dist/assets/stash-skeleton/facts/conventions/assets/skill.md +40 -0
  38. package/dist/assets/stash-skeleton/facts/conventions/assets/workflow.md +43 -0
  39. package/dist/assets/templates/html/health.html +281 -111
  40. package/dist/assets/wiki/ingest-workflow-template.md +45 -16
  41. package/dist/assets/wiki/schema-template.md +4 -4
  42. package/dist/cli/clack.js +56 -0
  43. package/dist/cli/config-migrate.js +7 -1
  44. package/dist/cli/confirm.js +1 -1
  45. package/dist/cli/parse-args.js +46 -1
  46. package/dist/cli/shared.js +28 -0
  47. package/dist/cli.js +25 -14
  48. package/dist/commands/agent/agent-dispatch.js +3 -2
  49. package/dist/commands/agent/agent-support.js +0 -7
  50. package/dist/commands/agent/contribute-cli.js +26 -7
  51. package/dist/commands/config-cli.js +26 -13
  52. package/dist/commands/env/child-env.js +47 -0
  53. package/dist/commands/env/env-cli.js +220 -227
  54. package/dist/commands/env/env.js +14 -67
  55. package/dist/commands/env/secret-cli.js +140 -138
  56. package/dist/commands/feedback-cli.js +153 -147
  57. package/dist/commands/graph/graph-cli.js +5 -13
  58. package/dist/commands/graph/graph.js +76 -72
  59. package/dist/commands/health/advisories.js +151 -0
  60. package/dist/commands/health/checks.js +103 -16
  61. package/dist/commands/health/html-report.js +447 -81
  62. package/dist/commands/health/improve-metrics.js +771 -0
  63. package/dist/commands/health/llm-usage.js +65 -0
  64. package/dist/commands/health/md-report.js +103 -0
  65. package/dist/commands/health/metrics.js +278 -0
  66. package/dist/commands/health/stash-exposure.js +46 -0
  67. package/dist/commands/health/surfaces.js +216 -0
  68. package/dist/commands/health/task-runs.js +135 -0
  69. package/dist/commands/health/types.js +26 -0
  70. package/dist/commands/health/windows.js +195 -0
  71. package/dist/commands/health.js +91 -1083
  72. package/dist/commands/improve/anti-collapse.js +170 -0
  73. package/dist/commands/improve/calibration.js +161 -0
  74. package/dist/commands/improve/collapse-detector.js +421 -0
  75. package/dist/commands/improve/consolidate/chunking.js +141 -0
  76. package/dist/commands/improve/consolidate/eligibility.js +64 -0
  77. package/dist/commands/improve/consolidate/merge.js +145 -0
  78. package/dist/commands/improve/consolidate/sanitize.js +231 -0
  79. package/dist/commands/{lint.js → improve/consolidate/types.js} +1 -1
  80. package/dist/commands/improve/consolidate.js +1313 -1278
  81. package/dist/commands/improve/dedup.js +482 -0
  82. package/dist/commands/improve/distill/content-repair.js +202 -0
  83. package/dist/commands/improve/distill/promote-memory.js +229 -0
  84. package/dist/commands/improve/distill/quality-gate.js +236 -0
  85. package/dist/commands/improve/distill-guards.js +127 -0
  86. package/dist/commands/improve/distill-promotion-policy.js +826 -167
  87. package/dist/commands/improve/distill.js +243 -599
  88. package/dist/commands/improve/eligibility.js +434 -0
  89. package/dist/commands/improve/encoding-salience.js +205 -0
  90. package/dist/commands/improve/extract-cli.js +179 -59
  91. package/dist/commands/improve/extract-prompt.js +55 -4
  92. package/dist/commands/improve/extract-watch.js +140 -0
  93. package/dist/commands/improve/extract.js +409 -43
  94. package/dist/commands/improve/feedback-valence.js +54 -0
  95. package/dist/commands/improve/hot-probation.js +45 -0
  96. package/dist/commands/improve/improve-auto-accept.js +160 -7
  97. package/dist/commands/improve/improve-cli.js +115 -73
  98. package/dist/commands/improve/improve-profiles.js +32 -8
  99. package/dist/commands/improve/improve-result-file.js +15 -25
  100. package/dist/commands/improve/improve-session.js +58 -0
  101. package/dist/commands/improve/improve.js +510 -2537
  102. package/dist/commands/improve/locks.js +154 -0
  103. package/dist/commands/improve/loop-stages.js +1100 -0
  104. package/dist/commands/improve/memory/memory-belief.js +14 -15
  105. package/dist/commands/improve/memory/memory-contradiction-detect.js +83 -60
  106. package/dist/commands/improve/memory/memory-improve.js +27 -27
  107. package/dist/commands/improve/outcome-loop.js +270 -0
  108. package/dist/commands/improve/preparation.js +2002 -0
  109. package/dist/commands/improve/proactive-maintenance.js +115 -0
  110. package/dist/commands/improve/procedural.js +398 -0
  111. package/dist/commands/improve/recombine.js +818 -0
  112. package/dist/commands/improve/reflect-noise.js +0 -0
  113. package/dist/commands/improve/reflect.js +212 -45
  114. package/dist/commands/improve/salience.js +455 -0
  115. package/dist/commands/improve/schema-similarity-gate.js +168 -0
  116. package/dist/commands/improve/shared.js +51 -0
  117. package/dist/commands/improve/triage.js +93 -0
  118. package/dist/commands/lint/agent-linter.js +19 -24
  119. package/dist/commands/lint/base-linter.js +173 -60
  120. package/dist/commands/lint/command-linter.js +19 -24
  121. package/dist/commands/lint/env-key-rules.js +38 -1
  122. package/dist/commands/lint/fact-linter.js +39 -0
  123. package/dist/commands/lint/index.js +31 -13
  124. package/dist/commands/lint/memory-linter.js +1 -1
  125. package/dist/commands/lint/registry.js +7 -2
  126. package/dist/commands/lint/task-linter.js +3 -3
  127. package/dist/commands/lint/workflow-linter.js +26 -1
  128. package/dist/commands/observability-cli.js +4 -4
  129. package/dist/commands/proposal/drain-policies.js +13 -4
  130. package/dist/commands/proposal/drain.js +45 -51
  131. package/dist/commands/proposal/legacy-import.js +115 -0
  132. package/dist/commands/proposal/proposal-cli.js +24 -34
  133. package/dist/commands/proposal/proposal.js +7 -1
  134. package/dist/commands/proposal/propose.js +8 -3
  135. package/dist/commands/proposal/repository.js +829 -0
  136. package/dist/commands/proposal/validators/proposal-quality-validators.js +9 -8
  137. package/dist/commands/proposal/validators/proposals.js +93 -882
  138. package/dist/commands/read/curate.js +419 -103
  139. package/dist/commands/read/knowledge.js +10 -3
  140. package/dist/commands/read/remember-cli.js +133 -138
  141. package/dist/commands/read/search-cli.js +15 -8
  142. package/dist/commands/read/search.js +22 -11
  143. package/dist/commands/read/show.js +106 -14
  144. package/dist/commands/registry-cli.js +76 -87
  145. package/dist/commands/remember.js +11 -12
  146. package/dist/commands/sources/add-cli.js +91 -95
  147. package/dist/commands/sources/history.js +1 -1
  148. package/dist/commands/sources/init.js +66 -18
  149. package/dist/commands/sources/installed-stashes.js +11 -3
  150. package/dist/commands/sources/schema-repair.js +44 -46
  151. package/dist/commands/sources/self-update.js +2 -2
  152. package/dist/commands/sources/source-add.js +7 -3
  153. package/dist/commands/sources/sources-cli.js +3 -3
  154. package/dist/commands/sources/stash-cli.js +29 -41
  155. package/dist/commands/sources/stash-skeleton.js +57 -8
  156. package/dist/commands/tasks/default-tasks.js +15 -2
  157. package/dist/commands/tasks/tasks-cli.js +20 -29
  158. package/dist/commands/tasks/tasks.js +39 -11
  159. package/dist/commands/wiki-cli.js +23 -38
  160. package/dist/commands/workflow-cli.js +15 -1
  161. package/dist/core/asset/asset-registry.js +3 -1
  162. package/dist/core/asset/asset-spec.js +21 -4
  163. package/dist/core/asset/frontmatter.js +188 -167
  164. package/dist/core/asset/markdown.js +8 -0
  165. package/dist/core/authoring-rules.js +92 -0
  166. package/dist/core/common.js +4 -23
  167. package/dist/core/concurrent.js +10 -1
  168. package/dist/core/config/config-io.js +10 -1
  169. package/dist/core/config/config-migration.js +18 -40
  170. package/dist/core/config/config-schema.js +389 -58
  171. package/dist/core/config/config-types.js +3 -3
  172. package/dist/core/config/config.js +67 -22
  173. package/dist/core/deep-merge.js +38 -0
  174. package/dist/core/errors.js +1 -0
  175. package/dist/core/eval/rank-metrics.js +113 -0
  176. package/dist/core/events.js +4 -7
  177. package/dist/core/improve-types.js +47 -8
  178. package/dist/core/logs-db.js +14 -75
  179. package/dist/core/parse.js +36 -16
  180. package/dist/core/paths.js +21 -18
  181. package/dist/core/standards/resolve-standards-context.js +87 -0
  182. package/dist/core/standards/resolve-stash-standards.js +99 -0
  183. package/dist/core/standards/resolve-type-conventions.js +66 -0
  184. package/dist/core/state/migrations.js +770 -0
  185. package/dist/core/state-db.js +142 -1091
  186. package/dist/core/structured.js +69 -0
  187. package/dist/core/time.js +53 -0
  188. package/dist/core/warn.js +21 -0
  189. package/dist/core/write-source.js +37 -0
  190. package/dist/indexer/db/db.js +356 -780
  191. package/dist/indexer/db/entry-mapper.js +41 -0
  192. package/dist/indexer/db/graph-db.js +129 -86
  193. package/dist/indexer/db/llm-cache.js +2 -2
  194. package/dist/indexer/db/schema.js +516 -0
  195. package/dist/indexer/ensure-index.js +103 -24
  196. package/dist/indexer/feedback/utility-policy.js +75 -0
  197. package/dist/indexer/graph/graph-boost.js +51 -41
  198. package/dist/indexer/graph/graph-extraction.js +207 -4
  199. package/dist/indexer/index-writer-lock.js +106 -0
  200. package/dist/indexer/index-written-assets.js +105 -0
  201. package/dist/indexer/indexer.js +291 -310
  202. package/dist/indexer/passes/dir-staleness.js +114 -0
  203. package/dist/indexer/passes/memory-inference.js +13 -5
  204. package/dist/indexer/passes/metadata.js +20 -0
  205. package/dist/indexer/read-preflight.js +23 -0
  206. package/dist/indexer/search/db-search.js +89 -13
  207. package/dist/indexer/search/fts-query.js +51 -0
  208. package/dist/indexer/search/ranking-contributors.js +95 -9
  209. package/dist/indexer/search/ranking.js +79 -3
  210. package/dist/indexer/search/search-fields.js +6 -0
  211. package/dist/indexer/search/search-source.js +32 -21
  212. package/dist/indexer/search/semantic-status.js +4 -0
  213. package/dist/indexer/walk/matchers.js +9 -0
  214. package/dist/indexer/walk/walker.js +21 -13
  215. package/dist/integrations/agent/builders.js +39 -13
  216. package/dist/integrations/agent/config.js +20 -59
  217. package/dist/integrations/agent/detect.js +9 -0
  218. package/dist/integrations/agent/index.js +3 -19
  219. package/dist/integrations/agent/model-aliases.js +7 -2
  220. package/dist/integrations/agent/profiles.js +7 -1
  221. package/dist/integrations/agent/prompts.js +75 -9
  222. package/dist/integrations/agent/runner-dispatch.js +59 -0
  223. package/dist/integrations/agent/runner.js +13 -9
  224. package/dist/integrations/agent/spawn.js +69 -67
  225. package/dist/integrations/harnesses/claude/agent-builder.js +1 -1
  226. package/dist/integrations/harnesses/claude/index.js +2 -0
  227. package/dist/integrations/harnesses/claude/session-log.js +11 -1
  228. package/dist/integrations/harnesses/index.js +2 -3
  229. package/dist/integrations/harnesses/opencode/agent-builder.js +1 -1
  230. package/dist/integrations/harnesses/opencode/index.js +2 -0
  231. package/dist/integrations/harnesses/opencode/session-log.js +173 -3
  232. package/dist/integrations/harnesses/opencode-sdk/index.js +2 -2
  233. package/dist/integrations/harnesses/opencode-sdk/sdk-runner.js +98 -17
  234. package/dist/integrations/harnesses/types.js +1 -0
  235. package/dist/integrations/session-logs/index.js +16 -0
  236. package/dist/llm/call-ai.js +2 -2
  237. package/dist/llm/client.js +57 -15
  238. package/dist/llm/embedder.js +67 -4
  239. package/dist/llm/embedders/cache.js +3 -1
  240. package/dist/llm/embedders/deterministic.js +66 -0
  241. package/dist/llm/embedders/local.js +73 -3
  242. package/dist/llm/feature-gate.js +16 -15
  243. package/dist/llm/graph-extract.js +67 -44
  244. package/dist/llm/memory-infer-impl.js +138 -0
  245. package/dist/llm/memory-infer.js +1 -127
  246. package/dist/llm/metadata-enhance.js +44 -31
  247. package/dist/llm/structured-call.js +49 -0
  248. package/dist/migrate-storage-node.mjs +8 -0
  249. package/dist/output/context.js +5 -5
  250. package/dist/output/renderers.js +85 -14
  251. package/dist/output/shapes/curate.js +14 -2
  252. package/dist/output/shapes/helpers.js +0 -3
  253. package/dist/output/shapes/passthrough.js +2 -1
  254. package/dist/output/text/helpers.js +29 -1
  255. package/dist/output/text/workflow.js +1 -0
  256. package/dist/registry/providers/skills-sh.js +21 -147
  257. package/dist/registry/providers/static-index.js +15 -157
  258. package/dist/registry/resolve.js +27 -9
  259. package/dist/runtime.js +25 -1
  260. package/dist/scripts/migrate-storage.js +2718 -2354
  261. package/dist/scripts/migrations/import-fs-improve-runs-to-db.js +891 -597
  262. package/dist/setup/detect.js +9 -0
  263. package/dist/setup/legacy-config.js +106 -0
  264. package/dist/setup/prompt.js +57 -0
  265. package/dist/setup/providers.js +14 -0
  266. package/dist/setup/registry-stash-loader.js +12 -0
  267. package/dist/setup/semantic-assets.js +124 -0
  268. package/dist/setup/setup.js +52 -1614
  269. package/dist/setup/steps/connection.js +734 -0
  270. package/dist/setup/steps/output.js +31 -0
  271. package/dist/setup/steps/platforms.js +124 -0
  272. package/dist/setup/steps/semantic.js +27 -0
  273. package/dist/setup/steps/sources.js +222 -0
  274. package/dist/setup/steps/stashdir.js +42 -0
  275. package/dist/setup/steps/tasks.js +152 -0
  276. package/dist/sources/include.js +6 -2
  277. package/dist/sources/providers/filesystem.js +0 -1
  278. package/dist/sources/providers/git-install.js +210 -0
  279. package/dist/sources/providers/git-provider.js +234 -0
  280. package/dist/sources/providers/git-stash.js +248 -0
  281. package/dist/sources/providers/git.js +10 -661
  282. package/dist/sources/providers/npm.js +2 -6
  283. package/dist/sources/providers/provider-utils.js +13 -7
  284. package/dist/sources/providers/sync-from-ref.js +9 -1
  285. package/dist/sources/providers/tar-utils.js +16 -8
  286. package/dist/sources/providers/website.js +9 -5
  287. package/dist/sources/website-ingest.js +187 -29
  288. package/dist/sources/wiki-fetchers/registry.js +53 -0
  289. package/dist/sources/wiki-fetchers/youtube.js +239 -0
  290. package/dist/storage/database.js +45 -10
  291. package/dist/storage/managed-db.js +82 -0
  292. package/dist/storage/repositories/canaries-repository.js +107 -0
  293. package/dist/storage/repositories/consolidation-repository.js +38 -0
  294. package/dist/storage/repositories/embeddings-repository.js +72 -0
  295. package/dist/storage/repositories/events-repository.js +187 -0
  296. package/dist/storage/repositories/extract-sessions-repository.js +96 -0
  297. package/dist/storage/repositories/improve-runs-repository.js +146 -0
  298. package/dist/storage/repositories/index-db.js +14 -8
  299. package/dist/storage/repositories/proposals-repository.js +220 -0
  300. package/dist/storage/repositories/recombine-repository.js +213 -0
  301. package/dist/storage/repositories/registry-cache.js +93 -0
  302. package/dist/storage/repositories/registry-index-cache-repository.js +46 -0
  303. package/dist/storage/repositories/task-history-repository.js +93 -0
  304. package/dist/storage/sqlite-pragmas.js +146 -0
  305. package/dist/tasks/backends/cron.js +1 -1
  306. package/dist/tasks/backends/index.js +9 -0
  307. package/dist/tasks/backends/launchd.js +1 -1
  308. package/dist/tasks/backends/schtasks.js +1 -1
  309. package/dist/tasks/{resolveAkmBin.js → resolve-akm-bin.js} +2 -2
  310. package/dist/tasks/runner.js +15 -13
  311. package/dist/text-import-hook.mjs +0 -0
  312. package/dist/wiki/wiki.js +52 -11
  313. package/dist/workflows/cli.js +1 -0
  314. package/dist/workflows/db.js +3 -4
  315. package/dist/workflows/runtime/runs.js +43 -118
  316. package/dist/workflows/runtime/workflow-asset-loader.js +125 -0
  317. package/dist/workflows/validate-summary.js +2 -7
  318. package/docs/README.md +69 -18
  319. package/docs/data-and-telemetry.md +5 -4
  320. package/docs/migration/release-notes/0.7.0.md +1 -1
  321. package/docs/migration/release-notes/0.9.0.md +39 -0
  322. package/package.json +10 -10
  323. package/dist/assets/tasks/core/update-stashes.yml +0 -4
  324. package/dist/commands/db-cli.js +0 -23
  325. package/dist/indexer/db/db-backup.js +0 -376
  326. package/dist/indexer/passes/staleness-detect.js +0 -488
@@ -1,5 +1,6 @@
1
1
  #!/usr/bin/env bun
2
2
  // @bun
3
+ import { createRequire } from "node:module";
3
4
  var __create = Object.create;
4
5
  var __getProtoOf = Object.getPrototypeOf;
5
6
  var __defProp = Object.defineProperty;
@@ -32,23 +33,53 @@ var __toESM = (mod, isNodeMode, target) => {
32
33
  };
33
34
  var __commonJS = (cb, mod) => () => (mod || cb((mod = { exports: {} }).exports, mod), mod.exports);
34
35
  var __esm = (fn, res) => () => (fn && (res = fn(fn = 0)), res);
35
- var __require = import.meta.require;
36
+ var __require = /* @__PURE__ */ createRequire(import.meta.url);
37
+
38
+ // src/core/warn.ts
39
+ import fs from "node:fs";
40
+ function appendToLogFile(level, args) {
41
+ if (!logFilePath)
42
+ return;
43
+ const ts = new Date().toISOString();
44
+ const msg = args.map((a) => typeof a === "string" ? a : JSON.stringify(a)).join(" ");
45
+ try {
46
+ fs.appendFileSync(logFilePath, `[${ts}] [${level}] ${msg}
47
+ `);
48
+ } catch (e) {
49
+ process.stderr.write(`[akm:warn] log-file write failed (${logFilePath}): ${e}
50
+ `);
51
+ process.stderr.write(`[${ts}] [${level}] ${msg}
52
+ `);
53
+ }
54
+ }
55
+ function warn(...args) {
56
+ if (sinkOverride) {
57
+ sinkOverride("warn", args);
58
+ return;
59
+ }
60
+ appendToLogFile("WARN", args);
61
+ if (!quiet) {
62
+ console.warn(...args);
63
+ }
64
+ }
65
+ var quiet = false, logFilePath, sinkOverride;
66
+ var init_warn = () => {};
36
67
 
37
68
  // node_modules/dotenv/lib/main.js
38
69
  var require_main = __commonJS((exports, module) => {
39
- var fs = __require("fs");
40
- var path = __require("path");
70
+ var fs3 = __require("fs");
71
+ var path2 = __require("path");
41
72
  var os = __require("os");
42
73
  var crypto = __require("crypto");
43
74
  var TIPS = [
44
- "\u25C8 encrypted .env [www.dotenvx.com]",
45
- "\u25C8 secrets for agents [www.dotenvx.com]",
46
- "\u2301 auth for agents [www.vestauth.com]",
47
- "\u2318 custom filepath { path: '/custom/path/.env' }",
48
- "\u2318 enable debugging { debug: true }",
49
- "\u2318 override existing { override: true }",
50
- "\u2318 suppress logs { quiet: true }",
51
- "\u2318 multiple files { path: ['.env.local', '.env'] }"
75
+ " encrypted .env [www.dotenvx.com]",
76
+ " secrets for agents [www.dotenvx.com]",
77
+ " auth for agents [www.vestauth.com]",
78
+ " custom filepath { path: '/custom/path/.env' }",
79
+ " enable debugging { debug: true }",
80
+ " override existing { override: true }",
81
+ " suppress logs { quiet: true }",
82
+ " multiple files { path: ['.env.local', '.env'] }"
52
83
  ];
53
84
  function _getRandomTip() {
54
85
  return TIPS[Math.floor(Math.random() * TIPS.length)];
@@ -115,13 +146,13 @@ var require_main = __commonJS((exports, module) => {
115
146
  return DotenvModule.parse(decrypted);
116
147
  }
117
148
  function _warn(message) {
118
- console.error(`\u26A0 ${message}`);
149
+ console.error(`⚠ ${message}`);
119
150
  }
120
151
  function _debug(message) {
121
- console.log(`\u2506 ${message}`);
152
+ console.log(`┆ ${message}`);
122
153
  }
123
154
  function _log(message) {
124
- console.log(`\u25C7 ${message}`);
155
+ console.log(`◇ ${message}`);
125
156
  }
126
157
  function _dotenvKey(options) {
127
158
  if (options && options.DOTENV_KEY && options.DOTENV_KEY.length > 0) {
@@ -170,7 +201,7 @@ var require_main = __commonJS((exports, module) => {
170
201
  if (options && options.path && options.path.length > 0) {
171
202
  if (Array.isArray(options.path)) {
172
203
  for (const filepath of options.path) {
173
- if (fs.existsSync(filepath)) {
204
+ if (fs3.existsSync(filepath)) {
174
205
  possibleVaultPath = filepath.endsWith(".vault") ? filepath : `${filepath}.vault`;
175
206
  }
176
207
  }
@@ -178,20 +209,20 @@ var require_main = __commonJS((exports, module) => {
178
209
  possibleVaultPath = options.path.endsWith(".vault") ? options.path : `${options.path}.vault`;
179
210
  }
180
211
  } else {
181
- possibleVaultPath = path.resolve(process.cwd(), ".env.vault");
212
+ possibleVaultPath = path2.resolve(process.cwd(), ".env.vault");
182
213
  }
183
- if (fs.existsSync(possibleVaultPath)) {
214
+ if (fs3.existsSync(possibleVaultPath)) {
184
215
  return possibleVaultPath;
185
216
  }
186
217
  return null;
187
218
  }
188
219
  function _resolveHome(envPath) {
189
- return envPath[0] === "~" ? path.join(os.homedir(), envPath.slice(1)) : envPath;
220
+ return envPath[0] === "~" ? path2.join(os.homedir(), envPath.slice(1)) : envPath;
190
221
  }
191
222
  function _configVault(options) {
192
223
  const debug = parseBoolean(process.env.DOTENV_CONFIG_DEBUG || options && options.debug);
193
- const quiet = parseBoolean(process.env.DOTENV_CONFIG_QUIET || options && options.quiet);
194
- if (debug || !quiet) {
224
+ const quiet2 = parseBoolean(process.env.DOTENV_CONFIG_QUIET || options && options.quiet);
225
+ if (debug || !quiet2) {
195
226
  _log("loading env from encrypted .env.vault");
196
227
  }
197
228
  const parsed = DotenvModule._parseVault(options);
@@ -203,14 +234,14 @@ var require_main = __commonJS((exports, module) => {
203
234
  return { parsed };
204
235
  }
205
236
  function configDotenv(options) {
206
- const dotenvPath = path.resolve(process.cwd(), ".env");
237
+ const dotenvPath = path2.resolve(process.cwd(), ".env");
207
238
  let encoding = "utf8";
208
239
  let processEnv = process.env;
209
240
  if (options && options.processEnv != null) {
210
241
  processEnv = options.processEnv;
211
242
  }
212
243
  let debug = parseBoolean(processEnv.DOTENV_CONFIG_DEBUG || options && options.debug);
213
- let quiet = parseBoolean(processEnv.DOTENV_CONFIG_QUIET || options && options.quiet);
244
+ let quiet2 = parseBoolean(processEnv.DOTENV_CONFIG_QUIET || options && options.quiet);
214
245
  if (options && options.encoding) {
215
246
  encoding = options.encoding;
216
247
  } else {
@@ -231,26 +262,26 @@ var require_main = __commonJS((exports, module) => {
231
262
  }
232
263
  let lastError;
233
264
  const parsedAll = {};
234
- for (const path2 of optionPaths) {
265
+ for (const path3 of optionPaths) {
235
266
  try {
236
- const parsed = DotenvModule.parse(fs.readFileSync(path2, { encoding }));
267
+ const parsed = DotenvModule.parse(fs3.readFileSync(path3, { encoding }));
237
268
  DotenvModule.populate(parsedAll, parsed, options);
238
269
  } catch (e) {
239
270
  if (debug) {
240
- _debug(`failed to load ${path2} ${e.message}`);
271
+ _debug(`failed to load ${path3} ${e.message}`);
241
272
  }
242
273
  lastError = e;
243
274
  }
244
275
  }
245
276
  const populated = DotenvModule.populate(processEnv, parsedAll, options);
246
277
  debug = parseBoolean(processEnv.DOTENV_CONFIG_DEBUG || debug);
247
- quiet = parseBoolean(processEnv.DOTENV_CONFIG_QUIET || quiet);
248
- if (debug || !quiet) {
278
+ quiet2 = parseBoolean(processEnv.DOTENV_CONFIG_QUIET || quiet2);
279
+ if (debug || !quiet2) {
249
280
  const keysCount = Object.keys(populated).length;
250
281
  const shortPaths = [];
251
282
  for (const filePath of optionPaths) {
252
283
  try {
253
- const relative = path.relative(process.cwd(), filePath);
284
+ const relative = path2.relative(process.cwd(), filePath);
254
285
  shortPaths.push(relative);
255
286
  } catch (e) {
256
287
  if (debug) {
@@ -363,8 +394,9 @@ var init_errors = __esm(() => {
363
394
  EMBEDDING_NOT_CONFIGURED: 'Run `akm config set embedding \'{"endpoint":"...","model":"..."}\'` to enable embeddings.',
364
395
  LLM_NOT_CONFIGURED: 'Run `akm setup` or `akm config set profiles.llm.default \'{"endpoint":"...","model":"..."}\' to configure an LLM profile.',
365
396
  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.",
366
- 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).",
367
- 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."
397
+ 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/ host config is preserved).",
398
+ 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.",
399
+ UNKNOWN_IMPROVE_PROFILE: "Pass one of the listed profile names to `--profile`, or define it under `profiles.improve` in your config. Names are case-sensitive."
368
400
  };
369
401
  USAGE_HINTS = {
370
402
  INVALID_FLAG_VALUE: "Run `akm <command> --help` to see accepted values.",
@@ -412,7 +444,7 @@ var init_errors = __esm(() => {
412
444
  });
413
445
 
414
446
  // src/commands/env/env.ts
415
- import fs from "fs";
447
+ import fs3 from "node:fs";
416
448
  function scanKeys(text) {
417
449
  const keys = [];
418
450
  const seen = new Set;
@@ -428,21 +460,11 @@ function scanKeys(text) {
428
460
  }
429
461
  return keys;
430
462
  }
431
- function scanComments(text) {
432
- const comments = [];
433
- for (const line of text.split(/\r?\n/)) {
434
- const trimmed = line.trimStart();
435
- if (trimmed.startsWith("#")) {
436
- comments.push(trimmed.slice(1).trimStart());
437
- }
438
- }
439
- return comments;
440
- }
441
463
  function listKeys(envPath) {
442
- if (!fs.existsSync(envPath))
443
- return { keys: [], comments: [] };
444
- const text = fs.readFileSync(envPath, "utf8");
445
- return { keys: scanKeys(text), comments: scanComments(text) };
464
+ if (!fs3.existsSync(envPath))
465
+ return { keys: [] };
466
+ const text = fs3.readFileSync(envPath, "utf8");
467
+ return { keys: scanKeys(text) };
446
468
  }
447
469
  var import_dotenv, ASSIGN_RE;
448
470
  var init_env = __esm(() => {
@@ -452,251 +474,6 @@ var init_env = __esm(() => {
452
474
  ASSIGN_RE = /^\s*(?:export\s+)?([A-Za-z_][A-Za-z0-9_]*)\s*=/;
453
475
  });
454
476
 
455
- // src/core/asset/frontmatter.ts
456
- function parseFrontmatter(raw) {
457
- const parsedBlock = parseFrontmatterBlock(raw);
458
- if (!parsedBlock) {
459
- return { data: {}, content: raw, frontmatter: null, bodyStartLine: 1 };
460
- }
461
- const data = {};
462
- let currentKey = null;
463
- let mode = "scalar";
464
- let nested = null;
465
- let currentList = null;
466
- let blockLines = null;
467
- let blockChomping = "clip";
468
- const flushPending = () => {
469
- if (mode === "pending" && currentKey !== null) {
470
- data[currentKey] = "";
471
- }
472
- };
473
- const flushBlock = () => {
474
- if (mode !== "block" || currentKey === null || blockLines === null)
475
- return;
476
- const deindented = blockLines.map((l) => l.startsWith(" ") ? l.slice(2) : l);
477
- if (blockChomping === "keep") {
478
- data[currentKey] = deindented.join(`
479
- `);
480
- } else if (blockChomping === "strip") {
481
- data[currentKey] = deindented.join(`
482
- `).replace(/\n+$/, "");
483
- } else {
484
- data[currentKey] = `${deindented.join(`
485
- `).replace(/\n+$/, "")}
486
- `;
487
- }
488
- };
489
- for (const line of parsedBlock.frontmatter.split(/\r?\n/)) {
490
- if (mode === "block") {
491
- if (line.startsWith(" ") || line === "") {
492
- blockLines.push(line);
493
- continue;
494
- }
495
- flushBlock();
496
- mode = "scalar";
497
- blockLines = null;
498
- }
499
- const seqItem = line.match(/^(?: {2})?- (.*)$/);
500
- if (seqItem && currentKey !== null && (mode === "list" || mode === "pending")) {
501
- if (mode === "pending") {
502
- currentList = [];
503
- data[currentKey] = currentList;
504
- mode = "list";
505
- }
506
- currentList.push(parseYamlScalar(seqItem[1].trim()));
507
- continue;
508
- }
509
- if (mode === "scalar" && currentKey !== null && /^ {2}\S/.test(line)) {
510
- data[currentKey] = `${String(data[currentKey])} ${line.trim()}`;
511
- continue;
512
- }
513
- const indented = line.match(/^ {2}(\w[\w-]*):\s*(.+)$/);
514
- if (indented && currentKey !== null && (mode === "object" || mode === "pending")) {
515
- if (mode === "pending") {
516
- nested = {};
517
- data[currentKey] = nested;
518
- mode = "object";
519
- }
520
- nested[indented[1]] = parseYamlScalar(indented[2].trim());
521
- continue;
522
- }
523
- const top = line.match(/^(\w[\w-]*):\s*(.*)$/);
524
- if (!top) {
525
- continue;
526
- }
527
- flushPending();
528
- currentKey = top[1];
529
- const value = top[2].trim();
530
- if (value === "|" || value === "|-" || value === "|+") {
531
- mode = "block";
532
- blockLines = [];
533
- blockChomping = value === "|-" ? "strip" : value === "|+" ? "keep" : "clip";
534
- nested = null;
535
- currentList = null;
536
- } else if (value === "") {
537
- mode = "pending";
538
- nested = null;
539
- currentList = null;
540
- } else if (value.startsWith("[") && value.endsWith("]")) {
541
- mode = "list";
542
- nested = null;
543
- currentList = null;
544
- currentList = parseFlowArray(value);
545
- data[currentKey] = currentList;
546
- } else {
547
- mode = "scalar";
548
- nested = null;
549
- currentList = null;
550
- data[currentKey] = parseYamlScalar(value);
551
- }
552
- }
553
- if (mode === "block") {
554
- flushBlock();
555
- }
556
- flushPending();
557
- return {
558
- data,
559
- content: parsedBlock.content,
560
- frontmatter: parsedBlock.frontmatter,
561
- bodyStartLine: parsedBlock.bodyStartLine
562
- };
563
- }
564
- function parseFlowArray(value) {
565
- const inner = value.slice(1, -1).trim();
566
- if (!inner)
567
- return [];
568
- return inner.split(",").map((item) => parseYamlScalar(item.trim()));
569
- }
570
- function parseFrontmatterBlock(raw) {
571
- const match = raw.match(/^---\r?\n([\s\S]*?)\r?\n---(?:\r\n|\r|\n|$)([\s\S]*)$/);
572
- if (!match)
573
- return null;
574
- const frontmatter = match[1].replace(/\r/g, "");
575
- const content = match[2];
576
- return {
577
- frontmatter,
578
- content,
579
- bodyStartLine: countLines(raw.slice(0, match[0].length - match[2].length)) + 1
580
- };
581
- }
582
- function countLines(text) {
583
- if (text.length === 0)
584
- return 0;
585
- return text.split(/\r?\n/).length - 1;
586
- }
587
- function parseYamlScalar(value) {
588
- if (value === "")
589
- return "";
590
- if (value === "true")
591
- return true;
592
- if (value === "false")
593
- return false;
594
- const asNumber = Number(value);
595
- if (!Number.isNaN(asNumber))
596
- return asNumber;
597
- if (value.startsWith('"') && value.endsWith('"') || value.startsWith("'") && value.endsWith("'")) {
598
- return value.slice(1, -1);
599
- }
600
- return value;
601
- }
602
-
603
- // src/core/asset/markdown.ts
604
- function parseMarkdownToc(content) {
605
- const lines = content.split(/\r?\n/);
606
- const headings = [];
607
- const parsed = parseFrontmatter(content);
608
- const start = parsed.frontmatter ? parsed.bodyStartLine - 1 : 0;
609
- for (let i = start;i < lines.length; i++) {
610
- const match = lines[i].match(/^(#{1,6})\s+(.+)$/);
611
- if (match) {
612
- headings.push({
613
- level: match[1].length,
614
- text: match[2].replace(/\s+#+\s*$/, "").trim(),
615
- line: i + 1
616
- });
617
- }
618
- }
619
- return { headings, totalLines: lines.length };
620
- }
621
- var init_markdown = () => {};
622
-
623
- // src/core/warn.ts
624
- var init_warn = () => {};
625
-
626
- // src/indexer/walk/file-context.ts
627
- var renderers;
628
- var init_file_context = __esm(() => {
629
- init_common();
630
- renderers = new Map;
631
- });
632
-
633
- // src/indexer/passes/metadata-contributors.ts
634
- function registerMetadataContributor(contributor) {
635
- contributors.push(contributor);
636
- }
637
- var contributors;
638
- var init_metadata_contributors = __esm(() => {
639
- contributors = [];
640
- });
641
-
642
- // src/indexer/passes/metadata.ts
643
- import fs2 from "fs";
644
- function extractDescriptionFromComments(filePath) {
645
- let content;
646
- try {
647
- content = fs2.readFileSync(filePath, "utf8");
648
- } catch {
649
- return null;
650
- }
651
- const lines = content.split(/\r?\n/).slice(0, 50);
652
- const blockStart = lines.findIndex((l) => /^\s*\/\*\*/.test(l));
653
- if (blockStart >= 0) {
654
- const desc = [];
655
- for (let i = blockStart;i < lines.length; i++) {
656
- const line = lines[i];
657
- if (i > blockStart && /\*\//.test(line))
658
- break;
659
- const cleaned = line.replace(/^\s*\/?\*\*?\s?/, "").replace(/\*\/\s*$/, "").trim();
660
- if (cleaned)
661
- desc.push(cleaned);
662
- }
663
- if (desc.length > 0)
664
- return desc.join(" ");
665
- }
666
- let start = 0;
667
- if (lines[0]?.startsWith("#!"))
668
- start = 1;
669
- const hashLines = [];
670
- for (let i = start;i < lines.length; i++) {
671
- const line = lines[i].trim();
672
- if (line.startsWith("#") && !line.startsWith("#!")) {
673
- hashLines.push(line.replace(/^#+\s*/, "").trim());
674
- } else if (line === "") {} else {
675
- break;
676
- }
677
- }
678
- if (hashLines.length > 0)
679
- return hashLines.join(" ");
680
- return null;
681
- }
682
- var KNOWN_QUALITY_VALUES, warnedUnknownQualityValues, WIKI_INFRA_FILES;
683
- var init_metadata = __esm(() => {
684
- init_asset_spec();
685
- init_common();
686
- init_warn();
687
- init_file_context();
688
- init_metadata_contributors();
689
- KNOWN_QUALITY_VALUES = new Set(["generated", "curated", "enriched", "proposed"]);
690
- warnedUnknownQualityValues = new Set;
691
- WIKI_INFRA_FILES = new Set(["schema.md", "index.md", "log.md"]);
692
- });
693
-
694
- // src/core/asset/asset-ref.ts
695
- var init_asset_ref = __esm(() => {
696
- init_common();
697
- init_errors();
698
- });
699
-
700
477
  // node_modules/yaml/dist/nodes/identity.js
701
478
  var require_identity = __commonJS((exports) => {
702
479
  var ALIAS = Symbol.for("yaml.alias");
@@ -769,17 +546,17 @@ var require_visit = __commonJS((exports) => {
769
546
  visit.BREAK = BREAK;
770
547
  visit.SKIP = SKIP;
771
548
  visit.REMOVE = REMOVE;
772
- function visit_(key, node, visitor, path) {
773
- const ctrl = callVisitor(key, node, visitor, path);
549
+ function visit_(key, node, visitor, path2) {
550
+ const ctrl = callVisitor(key, node, visitor, path2);
774
551
  if (identity.isNode(ctrl) || identity.isPair(ctrl)) {
775
- replaceNode(key, path, ctrl);
776
- return visit_(key, ctrl, visitor, path);
552
+ replaceNode(key, path2, ctrl);
553
+ return visit_(key, ctrl, visitor, path2);
777
554
  }
778
555
  if (typeof ctrl !== "symbol") {
779
556
  if (identity.isCollection(node)) {
780
- path = Object.freeze(path.concat(node));
557
+ path2 = Object.freeze(path2.concat(node));
781
558
  for (let i = 0;i < node.items.length; ++i) {
782
- const ci = visit_(i, node.items[i], visitor, path);
559
+ const ci = visit_(i, node.items[i], visitor, path2);
783
560
  if (typeof ci === "number")
784
561
  i = ci - 1;
785
562
  else if (ci === BREAK)
@@ -790,13 +567,13 @@ var require_visit = __commonJS((exports) => {
790
567
  }
791
568
  }
792
569
  } else if (identity.isPair(node)) {
793
- path = Object.freeze(path.concat(node));
794
- const ck = visit_("key", node.key, visitor, path);
570
+ path2 = Object.freeze(path2.concat(node));
571
+ const ck = visit_("key", node.key, visitor, path2);
795
572
  if (ck === BREAK)
796
573
  return BREAK;
797
574
  else if (ck === REMOVE)
798
575
  node.key = null;
799
- const cv = visit_("value", node.value, visitor, path);
576
+ const cv = visit_("value", node.value, visitor, path2);
800
577
  if (cv === BREAK)
801
578
  return BREAK;
802
579
  else if (cv === REMOVE)
@@ -817,17 +594,17 @@ var require_visit = __commonJS((exports) => {
817
594
  visitAsync.BREAK = BREAK;
818
595
  visitAsync.SKIP = SKIP;
819
596
  visitAsync.REMOVE = REMOVE;
820
- async function visitAsync_(key, node, visitor, path) {
821
- const ctrl = await callVisitor(key, node, visitor, path);
597
+ async function visitAsync_(key, node, visitor, path2) {
598
+ const ctrl = await callVisitor(key, node, visitor, path2);
822
599
  if (identity.isNode(ctrl) || identity.isPair(ctrl)) {
823
- replaceNode(key, path, ctrl);
824
- return visitAsync_(key, ctrl, visitor, path);
600
+ replaceNode(key, path2, ctrl);
601
+ return visitAsync_(key, ctrl, visitor, path2);
825
602
  }
826
603
  if (typeof ctrl !== "symbol") {
827
604
  if (identity.isCollection(node)) {
828
- path = Object.freeze(path.concat(node));
605
+ path2 = Object.freeze(path2.concat(node));
829
606
  for (let i = 0;i < node.items.length; ++i) {
830
- const ci = await visitAsync_(i, node.items[i], visitor, path);
607
+ const ci = await visitAsync_(i, node.items[i], visitor, path2);
831
608
  if (typeof ci === "number")
832
609
  i = ci - 1;
833
610
  else if (ci === BREAK)
@@ -838,13 +615,13 @@ var require_visit = __commonJS((exports) => {
838
615
  }
839
616
  }
840
617
  } else if (identity.isPair(node)) {
841
- path = Object.freeze(path.concat(node));
842
- const ck = await visitAsync_("key", node.key, visitor, path);
618
+ path2 = Object.freeze(path2.concat(node));
619
+ const ck = await visitAsync_("key", node.key, visitor, path2);
843
620
  if (ck === BREAK)
844
621
  return BREAK;
845
622
  else if (ck === REMOVE)
846
623
  node.key = null;
847
- const cv = await visitAsync_("value", node.value, visitor, path);
624
+ const cv = await visitAsync_("value", node.value, visitor, path2);
848
625
  if (cv === BREAK)
849
626
  return BREAK;
850
627
  else if (cv === REMOVE)
@@ -871,23 +648,23 @@ var require_visit = __commonJS((exports) => {
871
648
  }
872
649
  return visitor;
873
650
  }
874
- function callVisitor(key, node, visitor, path) {
651
+ function callVisitor(key, node, visitor, path2) {
875
652
  if (typeof visitor === "function")
876
- return visitor(key, node, path);
653
+ return visitor(key, node, path2);
877
654
  if (identity.isMap(node))
878
- return visitor.Map?.(key, node, path);
655
+ return visitor.Map?.(key, node, path2);
879
656
  if (identity.isSeq(node))
880
- return visitor.Seq?.(key, node, path);
657
+ return visitor.Seq?.(key, node, path2);
881
658
  if (identity.isPair(node))
882
- return visitor.Pair?.(key, node, path);
659
+ return visitor.Pair?.(key, node, path2);
883
660
  if (identity.isScalar(node))
884
- return visitor.Scalar?.(key, node, path);
661
+ return visitor.Scalar?.(key, node, path2);
885
662
  if (identity.isAlias(node))
886
- return visitor.Alias?.(key, node, path);
663
+ return visitor.Alias?.(key, node, path2);
887
664
  return;
888
665
  }
889
- function replaceNode(key, path, node) {
890
- const parent = path[path.length - 1];
666
+ function replaceNode(key, path2, node) {
667
+ const parent = path2[path2.length - 1];
891
668
  if (identity.isCollection(parent)) {
892
669
  parent.items[key] = node;
893
670
  } else if (identity.isPair(parent)) {
@@ -1446,10 +1223,10 @@ var require_Collection = __commonJS((exports) => {
1446
1223
  var createNode = require_createNode();
1447
1224
  var identity = require_identity();
1448
1225
  var Node = require_Node();
1449
- function collectionFromPath(schema, path, value) {
1226
+ function collectionFromPath(schema, path2, value) {
1450
1227
  let v = value;
1451
- for (let i = path.length - 1;i >= 0; --i) {
1452
- const k = path[i];
1228
+ for (let i = path2.length - 1;i >= 0; --i) {
1229
+ const k = path2[i];
1453
1230
  if (typeof k === "number" && Number.isInteger(k) && k >= 0) {
1454
1231
  const a = [];
1455
1232
  a[k] = v;
@@ -1468,7 +1245,7 @@ var require_Collection = __commonJS((exports) => {
1468
1245
  sourceObjects: new Map
1469
1246
  });
1470
1247
  }
1471
- var isEmptyPath = (path) => path == null || typeof path === "object" && !!path[Symbol.iterator]().next().done;
1248
+ var isEmptyPath = (path2) => path2 == null || typeof path2 === "object" && !!path2[Symbol.iterator]().next().done;
1472
1249
 
1473
1250
  class Collection extends Node.NodeBase {
1474
1251
  constructor(type, schema) {
@@ -1489,11 +1266,11 @@ var require_Collection = __commonJS((exports) => {
1489
1266
  copy.range = this.range.slice();
1490
1267
  return copy;
1491
1268
  }
1492
- addIn(path, value) {
1493
- if (isEmptyPath(path))
1269
+ addIn(path2, value) {
1270
+ if (isEmptyPath(path2))
1494
1271
  this.add(value);
1495
1272
  else {
1496
- const [key, ...rest] = path;
1273
+ const [key, ...rest] = path2;
1497
1274
  const node = this.get(key, true);
1498
1275
  if (identity.isCollection(node))
1499
1276
  node.addIn(rest, value);
@@ -1503,8 +1280,8 @@ var require_Collection = __commonJS((exports) => {
1503
1280
  throw new Error(`Expected YAML collection at ${key}. Remaining path: ${rest}`);
1504
1281
  }
1505
1282
  }
1506
- deleteIn(path) {
1507
- const [key, ...rest] = path;
1283
+ deleteIn(path2) {
1284
+ const [key, ...rest] = path2;
1508
1285
  if (rest.length === 0)
1509
1286
  return this.delete(key);
1510
1287
  const node = this.get(key, true);
@@ -1513,8 +1290,8 @@ var require_Collection = __commonJS((exports) => {
1513
1290
  else
1514
1291
  throw new Error(`Expected YAML collection at ${key}. Remaining path: ${rest}`);
1515
1292
  }
1516
- getIn(path, keepScalar) {
1517
- const [key, ...rest] = path;
1293
+ getIn(path2, keepScalar) {
1294
+ const [key, ...rest] = path2;
1518
1295
  const node = this.get(key, true);
1519
1296
  if (rest.length === 0)
1520
1297
  return !keepScalar && identity.isScalar(node) ? node.value : node;
@@ -1529,15 +1306,15 @@ var require_Collection = __commonJS((exports) => {
1529
1306
  return n == null || allowScalar && identity.isScalar(n) && n.value == null && !n.commentBefore && !n.comment && !n.tag;
1530
1307
  });
1531
1308
  }
1532
- hasIn(path) {
1533
- const [key, ...rest] = path;
1309
+ hasIn(path2) {
1310
+ const [key, ...rest] = path2;
1534
1311
  if (rest.length === 0)
1535
1312
  return this.has(key);
1536
1313
  const node = this.get(key, true);
1537
1314
  return identity.isCollection(node) ? node.hasIn(rest) : false;
1538
1315
  }
1539
- setIn(path, value) {
1540
- const [key, ...rest] = path;
1316
+ setIn(path2, value) {
1317
+ const [key, ...rest] = path2;
1541
1318
  if (rest.length === 0) {
1542
1319
  this.set(key, value);
1543
1320
  } else {
@@ -3930,9 +3707,9 @@ var require_Document = __commonJS((exports) => {
3930
3707
  if (assertCollection(this.contents))
3931
3708
  this.contents.add(value);
3932
3709
  }
3933
- addIn(path, value) {
3710
+ addIn(path2, value) {
3934
3711
  if (assertCollection(this.contents))
3935
- this.contents.addIn(path, value);
3712
+ this.contents.addIn(path2, value);
3936
3713
  }
3937
3714
  createAlias(node, name) {
3938
3715
  if (!node.anchor) {
@@ -3981,30 +3758,30 @@ var require_Document = __commonJS((exports) => {
3981
3758
  delete(key) {
3982
3759
  return assertCollection(this.contents) ? this.contents.delete(key) : false;
3983
3760
  }
3984
- deleteIn(path) {
3985
- if (Collection.isEmptyPath(path)) {
3761
+ deleteIn(path2) {
3762
+ if (Collection.isEmptyPath(path2)) {
3986
3763
  if (this.contents == null)
3987
3764
  return false;
3988
3765
  this.contents = null;
3989
3766
  return true;
3990
3767
  }
3991
- return assertCollection(this.contents) ? this.contents.deleteIn(path) : false;
3768
+ return assertCollection(this.contents) ? this.contents.deleteIn(path2) : false;
3992
3769
  }
3993
3770
  get(key, keepScalar) {
3994
3771
  return identity.isCollection(this.contents) ? this.contents.get(key, keepScalar) : undefined;
3995
3772
  }
3996
- getIn(path, keepScalar) {
3997
- if (Collection.isEmptyPath(path))
3773
+ getIn(path2, keepScalar) {
3774
+ if (Collection.isEmptyPath(path2))
3998
3775
  return !keepScalar && identity.isScalar(this.contents) ? this.contents.value : this.contents;
3999
- return identity.isCollection(this.contents) ? this.contents.getIn(path, keepScalar) : undefined;
3776
+ return identity.isCollection(this.contents) ? this.contents.getIn(path2, keepScalar) : undefined;
4000
3777
  }
4001
3778
  has(key) {
4002
3779
  return identity.isCollection(this.contents) ? this.contents.has(key) : false;
4003
3780
  }
4004
- hasIn(path) {
4005
- if (Collection.isEmptyPath(path))
3781
+ hasIn(path2) {
3782
+ if (Collection.isEmptyPath(path2))
4006
3783
  return this.contents !== undefined;
4007
- return identity.isCollection(this.contents) ? this.contents.hasIn(path) : false;
3784
+ return identity.isCollection(this.contents) ? this.contents.hasIn(path2) : false;
4008
3785
  }
4009
3786
  set(key, value) {
4010
3787
  if (this.contents == null) {
@@ -4013,13 +3790,13 @@ var require_Document = __commonJS((exports) => {
4013
3790
  this.contents.set(key, value);
4014
3791
  }
4015
3792
  }
4016
- setIn(path, value) {
4017
- if (Collection.isEmptyPath(path)) {
3793
+ setIn(path2, value) {
3794
+ if (Collection.isEmptyPath(path2)) {
4018
3795
  this.contents = value;
4019
3796
  } else if (this.contents == null) {
4020
- this.contents = Collection.collectionFromPath(this.schema, Array.from(path), value);
3797
+ this.contents = Collection.collectionFromPath(this.schema, Array.from(path2), value);
4021
3798
  } else if (assertCollection(this.contents)) {
4022
- this.contents.setIn(path, value);
3799
+ this.contents.setIn(path2, value);
4023
3800
  }
4024
3801
  }
4025
3802
  setSchema(version, options = {}) {
@@ -4128,15 +3905,15 @@ var require_errors = __commonJS((exports) => {
4128
3905
  let lineStr = src.substring(lc.lineStarts[line - 1], lc.lineStarts[line]).replace(/[\n\r]+$/, "");
4129
3906
  if (ci >= 60 && lineStr.length > 80) {
4130
3907
  const trimStart = Math.min(ci - 39, lineStr.length - 79);
4131
- lineStr = "\u2026" + lineStr.substring(trimStart);
3908
+ lineStr = "" + lineStr.substring(trimStart);
4132
3909
  ci -= trimStart - 1;
4133
3910
  }
4134
3911
  if (lineStr.length > 80)
4135
- lineStr = lineStr.substring(0, 79) + "\u2026";
3912
+ lineStr = lineStr.substring(0, 79) + "";
4136
3913
  if (line > 1 && /^ *$/.test(lineStr.substring(0, ci))) {
4137
3914
  let prev = src.substring(lc.lineStarts[line - 2], lc.lineStarts[line - 1]);
4138
3915
  if (prev.length > 80)
4139
- prev = prev.substring(0, 79) + `\u2026
3916
+ prev = prev.substring(0, 79) + `…
4140
3917
  `;
4141
3918
  lineStr = prev + lineStr;
4142
3919
  }
@@ -5191,8 +4968,8 @@ var require_resolve_flow_scalar = __commonJS((exports) => {
5191
4968
  r: "\r",
5192
4969
  t: "\t",
5193
4970
  v: "\v",
5194
- N: "\x85",
5195
- _: "\xA0",
4971
+ N: "…",
4972
+ _: " ",
5196
4973
  L: "\u2028",
5197
4974
  P: "\u2029",
5198
4975
  " ": " ",
@@ -5914,9 +5691,9 @@ var require_cst_visit = __commonJS((exports) => {
5914
5691
  visit.BREAK = BREAK;
5915
5692
  visit.SKIP = SKIP;
5916
5693
  visit.REMOVE = REMOVE;
5917
- visit.itemAtPath = (cst, path) => {
5694
+ visit.itemAtPath = (cst, path2) => {
5918
5695
  let item = cst;
5919
- for (const [field, index] of path) {
5696
+ for (const [field, index] of path2) {
5920
5697
  const tok = item?.[field];
5921
5698
  if (tok && "items" in tok) {
5922
5699
  item = tok.items[index];
@@ -5925,23 +5702,23 @@ var require_cst_visit = __commonJS((exports) => {
5925
5702
  }
5926
5703
  return item;
5927
5704
  };
5928
- visit.parentCollection = (cst, path) => {
5929
- const parent = visit.itemAtPath(cst, path.slice(0, -1));
5930
- const field = path[path.length - 1][0];
5705
+ visit.parentCollection = (cst, path2) => {
5706
+ const parent = visit.itemAtPath(cst, path2.slice(0, -1));
5707
+ const field = path2[path2.length - 1][0];
5931
5708
  const coll = parent?.[field];
5932
5709
  if (coll && "items" in coll)
5933
5710
  return coll;
5934
5711
  throw new Error("Parent collection not found");
5935
5712
  };
5936
- function _visit(path, item, visitor) {
5937
- let ctrl = visitor(item, path);
5713
+ function _visit(path2, item, visitor) {
5714
+ let ctrl = visitor(item, path2);
5938
5715
  if (typeof ctrl === "symbol")
5939
5716
  return ctrl;
5940
5717
  for (const field of ["key", "value"]) {
5941
5718
  const token = item[field];
5942
5719
  if (token && "items" in token) {
5943
5720
  for (let i = 0;i < token.items.length; ++i) {
5944
- const ci = _visit(Object.freeze(path.concat([[field, i]])), token.items[i], visitor);
5721
+ const ci = _visit(Object.freeze(path2.concat([[field, i]])), token.items[i], visitor);
5945
5722
  if (typeof ci === "number")
5946
5723
  i = ci - 1;
5947
5724
  else if (ci === BREAK)
@@ -5952,10 +5729,10 @@ var require_cst_visit = __commonJS((exports) => {
5952
5729
  }
5953
5730
  }
5954
5731
  if (typeof ctrl === "function" && field === "key")
5955
- ctrl = ctrl(item, path);
5732
+ ctrl = ctrl(item, path2);
5956
5733
  }
5957
5734
  }
5958
- return typeof ctrl === "function" ? ctrl(item, path) : ctrl;
5735
+ return typeof ctrl === "function" ? ctrl(item, path2) : ctrl;
5959
5736
  }
5960
5737
  exports.visit = visit;
5961
5738
  });
@@ -7224,14 +7001,14 @@ var require_parser = __commonJS((exports) => {
7224
7001
  case "scalar":
7225
7002
  case "single-quoted-scalar":
7226
7003
  case "double-quoted-scalar": {
7227
- const fs3 = this.flowScalar(this.type);
7004
+ const fs4 = this.flowScalar(this.type);
7228
7005
  if (atNextItem || it.value) {
7229
- map.items.push({ start, key: fs3, sep: [] });
7006
+ map.items.push({ start, key: fs4, sep: [] });
7230
7007
  this.onKeyLine = true;
7231
7008
  } else if (it.sep) {
7232
- this.stack.push(fs3);
7009
+ this.stack.push(fs4);
7233
7010
  } else {
7234
- Object.assign(it, { key: fs3, sep: [] });
7011
+ Object.assign(it, { key: fs4, sep: [] });
7235
7012
  this.onKeyLine = true;
7236
7013
  }
7237
7014
  return;
@@ -7359,13 +7136,13 @@ var require_parser = __commonJS((exports) => {
7359
7136
  case "scalar":
7360
7137
  case "single-quoted-scalar":
7361
7138
  case "double-quoted-scalar": {
7362
- const fs3 = this.flowScalar(this.type);
7139
+ const fs4 = this.flowScalar(this.type);
7363
7140
  if (!it || it.value)
7364
- fc.items.push({ start: [], key: fs3, sep: [] });
7141
+ fc.items.push({ start: [], key: fs4, sep: [] });
7365
7142
  else if (it.sep)
7366
- this.stack.push(fs3);
7143
+ this.stack.push(fs4);
7367
7144
  else
7368
- Object.assign(it, { key: fs3, sep: [] });
7145
+ Object.assign(it, { key: fs4, sep: [] });
7369
7146
  return;
7370
7147
  }
7371
7148
  case "flow-map-end":
@@ -7667,67 +7444,266 @@ var init_dist = __esm(() => {
7667
7444
  $visitAsync = visit.visitAsync;
7668
7445
  });
7669
7446
 
7670
- // src/workflows/schema.ts
7671
- var WORKFLOW_SCHEMA_VERSION = 1;
7447
+ // src/core/asset/asset-serialize.ts
7448
+ var init_asset_serialize = __esm(() => {
7449
+ init_dist();
7450
+ });
7672
7451
 
7673
- // src/workflows/validator.ts
7674
- function runSemanticChecks(draft, frontmatterData, frontmatterEndLine, errors2) {
7675
- checkFrontmatterKeys(frontmatterData, frontmatterEndLine, errors2);
7676
- checkStepIdFormat(draft, errors2);
7677
- checkDuplicateStepIds(draft, errors2);
7678
- }
7679
- function checkFrontmatterKeys(data, fmEndLine, errors2) {
7680
- for (const key of Object.keys(data)) {
7681
- if (ALLOWED_FRONTMATTER_KEYS.has(key))
7682
- continue;
7683
- errors2.push({
7684
- line: fmEndLine,
7685
- message: `Workflow frontmatter "${key}" is not supported. Use only: description, tags, params, when_to_use.`
7686
- });
7452
+ // src/core/asset/frontmatter.ts
7453
+ function parseFrontmatter(raw) {
7454
+ const parsedBlock = parseFrontmatterBlock(raw);
7455
+ if (!parsedBlock) {
7456
+ return { data: {}, content: raw, frontmatter: null, bodyStartLine: 1 };
7457
+ }
7458
+ let data = {};
7459
+ if (parsedBlock.frontmatter.trim()) {
7460
+ try {
7461
+ const parsed = $parse(parsedBlock.frontmatter);
7462
+ if (parsed !== null && typeof parsed === "object" && !Array.isArray(parsed)) {
7463
+ data = normalizeYamlValues(parsed);
7464
+ }
7465
+ } catch {
7466
+ data = parseFrontmatterLenient(parsedBlock.frontmatter);
7467
+ }
7687
7468
  }
7469
+ return {
7470
+ data,
7471
+ content: parsedBlock.content,
7472
+ frontmatter: parsedBlock.frontmatter,
7473
+ bodyStartLine: parsedBlock.bodyStartLine
7474
+ };
7688
7475
  }
7689
- function checkStepIdFormat(draft, errors2) {
7690
- for (const step of draft.steps) {
7691
- if (STEP_ID_REGEX.test(step.id))
7692
- continue;
7693
- errors2.push({
7694
- line: step.source.start,
7695
- message: `Step ID "${step.id}" is invalid. Use letters, numbers, ".", "_" or "-" (e.g. "deploy-job").`
7696
- });
7476
+ function normalizeYamlValues(value) {
7477
+ if (value instanceof Date) {
7478
+ const y = value.getUTCFullYear();
7479
+ const m = String(value.getUTCMonth() + 1).padStart(2, "0");
7480
+ const d = String(value.getUTCDate()).padStart(2, "0");
7481
+ return `${y}-${m}-${d}`;
7482
+ }
7483
+ if (value === null)
7484
+ return "";
7485
+ if (Array.isArray(value))
7486
+ return value.map(normalizeYamlValues);
7487
+ if (typeof value === "object") {
7488
+ return Object.fromEntries(Object.entries(value).map(([k, v]) => [k, normalizeYamlValues(v)]));
7697
7489
  }
7490
+ return value;
7698
7491
  }
7699
- function checkDuplicateStepIds(draft, errors2) {
7700
- const firstSeenLine = new Map;
7701
- for (const step of draft.steps) {
7702
- const previous = firstSeenLine.get(step.id);
7703
- if (previous !== undefined) {
7704
- errors2.push({
7705
- line: step.source.start,
7706
- message: `Step ID "${step.id}" is already used on line ${previous}. Step IDs must be unique within a workflow.`
7707
- });
7492
+ function parseFrontmatterLenient(frontmatter) {
7493
+ const data = {};
7494
+ for (const line of frontmatter.split(/\r?\n/)) {
7495
+ const m = line.match(/^([\w][\w-]*):\s*(.*)$/);
7496
+ if (!m)
7708
7497
  continue;
7498
+ const key = m[1];
7499
+ const rawValue = (m[2] ?? "").trim();
7500
+ try {
7501
+ const singleEntry = $parse(`k: ${rawValue}`);
7502
+ if (singleEntry !== null && typeof singleEntry === "object" && !Array.isArray(singleEntry)) {
7503
+ const v = singleEntry.k;
7504
+ data[key] = v === null || v === undefined ? "" : v;
7505
+ } else {
7506
+ data[key] = rawValue;
7507
+ }
7508
+ } catch {
7509
+ data[key] = rawValue;
7709
7510
  }
7710
- firstSeenLine.set(step.id, step.source.start);
7711
7511
  }
7512
+ return data;
7712
7513
  }
7713
- var STEP_ID_REGEX, ALLOWED_FRONTMATTER_KEYS;
7714
- var init_validator = __esm(() => {
7715
- STEP_ID_REGEX = /^[A-Za-z0-9][A-Za-z0-9._-]*$/;
7716
- ALLOWED_FRONTMATTER_KEYS = new Set(["description", "tags", "params", "name", "updated", "when_to_use"]);
7514
+ function parseFrontmatterBlock(raw) {
7515
+ const match = raw.match(/^---\r?\n([\s\S]*?)\r?\n---(?:\r\n|\r|\n|$)([\s\S]*)$/);
7516
+ if (match) {
7517
+ const frontmatter = match[1].replace(/\r/g, "");
7518
+ const content = match[2];
7519
+ return {
7520
+ frontmatter,
7521
+ content,
7522
+ bodyStartLine: countLines(raw.slice(0, match[0].length - match[2].length)) + 1
7523
+ };
7524
+ }
7525
+ const emptyMatch = raw.match(/^---\r?\n---(?:\r\n|\r|\n)([\s\S]*)$/);
7526
+ if (emptyMatch) {
7527
+ return { frontmatter: "", content: emptyMatch[1], bodyStartLine: 3 };
7528
+ }
7529
+ return null;
7530
+ }
7531
+ function countLines(text) {
7532
+ if (text.length === 0)
7533
+ return 0;
7534
+ return text.split(/\r?\n/).length - 1;
7535
+ }
7536
+ var init_frontmatter = __esm(() => {
7537
+ init_dist();
7538
+ init_asset_serialize();
7717
7539
  });
7718
7540
 
7719
- // src/workflows/parser.ts
7720
- function parseWorkflow(markdown, source) {
7721
- const errors2 = [];
7722
- const path = source.path;
7723
- const lines = markdown.split(/\r?\n/);
7724
- const totalLines = lines.length;
7725
- const fmBlock = parseFrontmatterBlock(markdown);
7726
- const frontmatterEndLine = fmBlock ? Math.max(1, fmBlock.bodyStartLine - 1) : 1;
7541
+ // src/core/asset/markdown.ts
7542
+ function parseMarkdownToc(content) {
7543
+ const lines = content.split(/\r?\n/);
7544
+ const headings = [];
7545
+ const parsed = parseFrontmatter(content);
7546
+ const start = parsed.frontmatter ? parsed.bodyStartLine - 1 : 0;
7547
+ let inFence = false;
7548
+ for (let i = start;i < lines.length; i++) {
7549
+ if (/^\s*(`{3,}|~{3,})/.test(lines[i])) {
7550
+ inFence = !inFence;
7551
+ continue;
7552
+ }
7553
+ if (inFence)
7554
+ continue;
7555
+ const match = lines[i].match(/^(#{1,6})\s+(.+)$/);
7556
+ if (match) {
7557
+ headings.push({
7558
+ level: match[1].length,
7559
+ text: match[2].replace(/\s+#+\s*$/, "").trim(),
7560
+ line: i + 1
7561
+ });
7562
+ }
7563
+ }
7564
+ return { headings, totalLines: lines.length };
7565
+ }
7566
+ var init_markdown = __esm(() => {
7567
+ init_frontmatter();
7568
+ });
7569
+
7570
+ // src/indexer/walk/file-context.ts
7571
+ var renderers;
7572
+ var init_file_context = __esm(() => {
7573
+ init_frontmatter();
7574
+ init_common();
7575
+ renderers = new Map;
7576
+ });
7577
+
7578
+ // src/indexer/passes/metadata-contributors.ts
7579
+ function registerMetadataContributor(contributor) {
7580
+ contributors.push(contributor);
7581
+ }
7582
+ var contributors;
7583
+ var init_metadata_contributors = __esm(() => {
7584
+ contributors = [];
7585
+ });
7586
+
7587
+ // src/indexer/passes/metadata.ts
7588
+ import fs4 from "node:fs";
7589
+ function extractDescriptionFromComments(filePath) {
7590
+ let content;
7591
+ try {
7592
+ content = fs4.readFileSync(filePath, "utf8");
7593
+ } catch {
7594
+ return null;
7595
+ }
7596
+ const lines = content.split(/\r?\n/).slice(0, 50);
7597
+ const blockStart = lines.findIndex((l) => /^\s*\/\*\*/.test(l));
7598
+ if (blockStart >= 0) {
7599
+ const desc = [];
7600
+ for (let i = blockStart;i < lines.length; i++) {
7601
+ const line = lines[i];
7602
+ if (i > blockStart && /\*\//.test(line))
7603
+ break;
7604
+ const cleaned = line.replace(/^\s*\/?\*\*?\s?/, "").replace(/\*\/\s*$/, "").trim();
7605
+ if (cleaned)
7606
+ desc.push(cleaned);
7607
+ }
7608
+ if (desc.length > 0)
7609
+ return desc.join(" ");
7610
+ }
7611
+ let start = 0;
7612
+ if (lines[0]?.startsWith("#!"))
7613
+ start = 1;
7614
+ const hashLines = [];
7615
+ for (let i = start;i < lines.length; i++) {
7616
+ const line = lines[i].trim();
7617
+ if (line.startsWith("#") && !line.startsWith("#!")) {
7618
+ hashLines.push(line.replace(/^#+\s*/, "").trim());
7619
+ } else if (line === "") {} else {
7620
+ break;
7621
+ }
7622
+ }
7623
+ if (hashLines.length > 0)
7624
+ return hashLines.join(" ");
7625
+ return null;
7626
+ }
7627
+ var KNOWN_QUALITY_VALUES, warnedUnknownQualityValues, WIKI_INFRA_FILES;
7628
+ var init_metadata = __esm(() => {
7629
+ init_asset_spec();
7630
+ init_frontmatter();
7631
+ init_common();
7632
+ init_warn();
7633
+ init_file_context();
7634
+ init_metadata_contributors();
7635
+ KNOWN_QUALITY_VALUES = new Set(["generated", "curated", "enriched", "proposed"]);
7636
+ warnedUnknownQualityValues = new Set;
7637
+ WIKI_INFRA_FILES = new Set(["schema.md", "index.md", "log.md"]);
7638
+ });
7639
+
7640
+ // src/core/asset/asset-ref.ts
7641
+ var init_asset_ref = __esm(() => {
7642
+ init_common();
7643
+ init_errors();
7644
+ });
7645
+
7646
+ // src/workflows/schema.ts
7647
+ var WORKFLOW_SCHEMA_VERSION = 1;
7648
+
7649
+ // src/workflows/validator.ts
7650
+ function runSemanticChecks(draft, frontmatterData, frontmatterEndLine, errors2) {
7651
+ checkFrontmatterKeys(frontmatterData, frontmatterEndLine, errors2);
7652
+ checkStepIdFormat(draft, errors2);
7653
+ checkDuplicateStepIds(draft, errors2);
7654
+ }
7655
+ function checkFrontmatterKeys(data, fmEndLine, errors2) {
7656
+ for (const key of Object.keys(data)) {
7657
+ if (ALLOWED_FRONTMATTER_KEYS.has(key))
7658
+ continue;
7659
+ errors2.push({
7660
+ line: fmEndLine,
7661
+ message: `Workflow frontmatter "${key}" is not supported. Use only: description, tags, params, when_to_use.`
7662
+ });
7663
+ }
7664
+ }
7665
+ function checkStepIdFormat(draft, errors2) {
7666
+ for (const step of draft.steps) {
7667
+ if (STEP_ID_REGEX.test(step.id))
7668
+ continue;
7669
+ errors2.push({
7670
+ line: step.source.start,
7671
+ message: `Step ID "${step.id}" is invalid. Use letters, numbers, ".", "_" or "-" (e.g. "deploy-job").`
7672
+ });
7673
+ }
7674
+ }
7675
+ function checkDuplicateStepIds(draft, errors2) {
7676
+ const firstSeenLine = new Map;
7677
+ for (const step of draft.steps) {
7678
+ const previous = firstSeenLine.get(step.id);
7679
+ if (previous !== undefined) {
7680
+ errors2.push({
7681
+ line: step.source.start,
7682
+ message: `Step ID "${step.id}" is already used on line ${previous}. Step IDs must be unique within a workflow.`
7683
+ });
7684
+ continue;
7685
+ }
7686
+ firstSeenLine.set(step.id, step.source.start);
7687
+ }
7688
+ }
7689
+ var STEP_ID_REGEX, ALLOWED_FRONTMATTER_KEYS;
7690
+ var init_validator = __esm(() => {
7691
+ STEP_ID_REGEX = /^[A-Za-z0-9][A-Za-z0-9._-]*$/;
7692
+ ALLOWED_FRONTMATTER_KEYS = new Set(["description", "tags", "params", "name", "updated", "when_to_use"]);
7693
+ });
7694
+
7695
+ // src/workflows/parser.ts
7696
+ function parseWorkflow(markdown, source) {
7697
+ const errors2 = [];
7698
+ const path2 = source.path;
7699
+ const lines = markdown.split(/\r?\n/);
7700
+ const totalLines = lines.length;
7701
+ const fmBlock = parseFrontmatterBlock(markdown);
7702
+ const frontmatterEndLine = fmBlock ? Math.max(1, fmBlock.bodyStartLine - 1) : 1;
7727
7703
  const fmData = readFrontmatter(fmBlock?.frontmatter, errors2);
7728
7704
  const description = readDescription(fmData);
7729
7705
  const tags = readTags(fmData, errors2, frontmatterEndLine);
7730
- const parameters = readParameters(fmData, errors2, frontmatterEndLine, path);
7706
+ const parameters = readParameters(fmData, errors2, frontmatterEndLine, path2);
7731
7707
  const toc = parseMarkdownToc(markdown);
7732
7708
  const { title, titleLine } = extractTitle(toc.headings, errors2);
7733
7709
  for (const h of toc.headings) {
@@ -7744,7 +7720,7 @@ function parseWorkflow(markdown, source) {
7744
7720
  });
7745
7721
  }
7746
7722
  }
7747
- const steps = extractSteps(toc.headings, lines, totalLines, path, errors2);
7723
+ const steps = extractSteps(toc.headings, lines, totalLines, path2, errors2);
7748
7724
  if (steps.length === 0 && titleLine > 0) {
7749
7725
  errors2.push({
7750
7726
  line: titleLine,
@@ -7758,7 +7734,7 @@ function parseWorkflow(markdown, source) {
7758
7734
  ...tags ? { tags } : {},
7759
7735
  ...parameters ? { parameters } : {},
7760
7736
  steps,
7761
- source: { path, lineCount: totalLines }
7737
+ source: { path: path2, lineCount: totalLines }
7762
7738
  };
7763
7739
  runSemanticChecks(draft, fmData, frontmatterEndLine, errors2);
7764
7740
  if (errors2.length > 0) {
@@ -7793,7 +7769,7 @@ function extractTitle(headings, errors2) {
7793
7769
  }
7794
7770
  return { title, titleLine: first.line };
7795
7771
  }
7796
- function extractSteps(headings, lines, totalLines, path, errors2) {
7772
+ function extractSteps(headings, lines, totalLines, path2, errors2) {
7797
7773
  const steps = [];
7798
7774
  let sequenceIndex = 0;
7799
7775
  for (let i = 0;i < headings.length; i++) {
@@ -7809,11 +7785,11 @@ function extractSteps(headings, lines, totalLines, path, errors2) {
7809
7785
  continue;
7810
7786
  }
7811
7787
  const stepEnd = Math.min(findNextSiblingOrParentLine(headings, i, 2) - 1, totalLines);
7812
- const stepSource = { path, start: h.line, end: stepEnd };
7788
+ const stepSource = { path: path2, start: h.line, end: stepEnd };
7813
7789
  const subsections = collectSubsections(headings, i, stepEnd);
7814
7790
  const stepIdSearchEnd = subsections.length > 0 ? subsections[0].headingLine - 1 : stepEnd;
7815
7791
  const stepId = scanStepId(lines, h.line + 1, stepIdSearchEnd, stepTitle, errors2);
7816
- const { instructions, completionCriteria } = collectStepBody(subsections, lines, path, stepTitle, errors2);
7792
+ const { instructions, completionCriteria } = collectStepBody(subsections, lines, path2, stepTitle, errors2);
7817
7793
  if (!stepId)
7818
7794
  continue;
7819
7795
  if (!instructions) {
@@ -7853,7 +7829,7 @@ function collectSubsections(headings, stepIndex, stepEnd) {
7853
7829
  }
7854
7830
  return subs;
7855
7831
  }
7856
- function collectStepBody(subsections, lines, path, stepTitle, errors2) {
7832
+ function collectStepBody(subsections, lines, path2, stepTitle, errors2) {
7857
7833
  let instructions;
7858
7834
  let completionCriteria;
7859
7835
  for (const sub of subsections) {
@@ -7875,7 +7851,7 @@ function collectStepBody(subsections, lines, path, stepTitle, errors2) {
7875
7851
  }
7876
7852
  instructions = {
7877
7853
  text,
7878
- source: { path, start: sub.bodyStart, end: sub.bodyEnd }
7854
+ source: { path: path2, start: sub.bodyStart, end: sub.bodyEnd }
7879
7855
  };
7880
7856
  continue;
7881
7857
  }
@@ -7887,7 +7863,7 @@ function collectStepBody(subsections, lines, path, stepTitle, errors2) {
7887
7863
  });
7888
7864
  continue;
7889
7865
  }
7890
- const items = collectBullets(lines, sub.bodyStart, sub.bodyEnd, path);
7866
+ const items = collectBullets(lines, sub.bodyStart, sub.bodyEnd, path2);
7891
7867
  if (items.length === 0) {
7892
7868
  errors2.push({
7893
7869
  line: sub.headingLine,
@@ -7936,7 +7912,7 @@ function scanStepId(lines, startLineInclusive, endLineInclusive, stepTitle, erro
7936
7912
  }
7937
7913
  return foundId;
7938
7914
  }
7939
- function collectBullets(lines, startLineInclusive, endLineInclusive, path) {
7915
+ function collectBullets(lines, startLineInclusive, endLineInclusive, path2) {
7940
7916
  const items = [];
7941
7917
  for (let lineNum = startLineInclusive;lineNum <= endLineInclusive; lineNum++) {
7942
7918
  const trimmed = (lines[lineNum - 1] ?? "").trim();
@@ -7947,7 +7923,7 @@ function collectBullets(lines, startLineInclusive, endLineInclusive, path) {
7947
7923
  continue;
7948
7924
  items.push({
7949
7925
  text: match[1].trim(),
7950
- source: { path, start: lineNum, end: lineNum }
7926
+ source: { path: path2, start: lineNum, end: lineNum }
7951
7927
  });
7952
7928
  }
7953
7929
  return items;
@@ -8016,7 +7992,7 @@ function readTags(data, errors2, fmEndLine) {
8016
7992
  }
8017
7993
  return v.map((tag) => tag.trim());
8018
7994
  }
8019
- function readParameters(data, errors2, fmEndLine, path) {
7995
+ function readParameters(data, errors2, fmEndLine, path2) {
8020
7996
  const v = data.params;
8021
7997
  if (v === undefined || v === null)
8022
7998
  return;
@@ -8050,7 +8026,7 @@ function readParameters(data, errors2, fmEndLine, path) {
8050
8026
  out.push({
8051
8027
  name: trimmedName,
8052
8028
  description: desc.trim(),
8053
- source: { path, start: 1, end: fmEndLine }
8029
+ source: { path: path2, start: 1, end: fmEndLine }
8054
8030
  });
8055
8031
  }
8056
8032
  return out.length > 0 ? out : undefined;
@@ -8061,6 +8037,7 @@ function sortErrors(errors2) {
8061
8037
  var WORKFLOW_TITLE_PREFIX = "Workflow:", STEP_PREFIX = "Step:", STEP_ID_LINE, BULLET_LINE, SUBSECTION_INSTRUCTIONS = "Instructions", SUBSECTION_COMPLETION_CRITERIA = "Completion Criteria";
8062
8038
  var init_parser = __esm(() => {
8063
8039
  init_dist();
8040
+ init_frontmatter();
8064
8041
  init_markdown();
8065
8042
  init_validator();
8066
8043
  STEP_ID_LINE = /^Step ID:\s+(.+?)\s*$/;
@@ -8087,7 +8064,7 @@ function loadDocument(ctx) {
8087
8064
  const result = parseWorkflow(ctx.content(), { path: ctx.relPath });
8088
8065
  if (result.ok)
8089
8066
  return result.document;
8090
- const summary = result.errors.map((e) => `${ctx.relPath}:${e.line} \u2014 ${e.message}`).join(`
8067
+ const summary = result.errors.map((e) => `${ctx.relPath}:${e.line} ${e.message}`).join(`
8091
8068
  `);
8092
8069
  throw new UsageError(`Workflow has errors:
8093
8070
  ${summary}`);
@@ -8151,6 +8128,25 @@ function applyTocMetadata(entry, ctx) {
8151
8128
  entry.toc = toc.headings;
8152
8129
  } catch {}
8153
8130
  }
8131
+ function applyFactMetadata(entry, ctx) {
8132
+ try {
8133
+ const fm = applyFrontmatterDescriptionAndTags(entry, ctx);
8134
+ const tags = new Set([...entry.tags ?? [], "fact"]);
8135
+ const hints = new Set(entry.searchHints ?? []);
8136
+ const category = asNonEmptyString(fm.category);
8137
+ if (category) {
8138
+ tags.add(category);
8139
+ hints.add(`category:${category}`);
8140
+ }
8141
+ if (fm.pinned === true) {
8142
+ tags.add("pinned");
8143
+ hints.add("pinned");
8144
+ }
8145
+ entry.tags = Array.from(tags).filter(Boolean);
8146
+ if (hints.size > 0)
8147
+ entry.searchHints = Array.from(hints).filter(Boolean);
8148
+ } catch {}
8149
+ }
8154
8150
  function applyFrontmatterDescriptionAndTags(entry, ctx) {
8155
8151
  const parsed = parseFrontmatter(ctx.content());
8156
8152
  const fm = parsed.data;
@@ -8216,12 +8212,7 @@ function applyScriptMetadata(entry, ctx) {
8216
8212
  }
8217
8213
  }
8218
8214
  function applyEnvMetadata(entry, ctx) {
8219
- const { keys, comments } = listKeys(ctx.absPath);
8220
- if (comments.length > 0 && !entry.description) {
8221
- entry.description = comments.join(" ").slice(0, 500);
8222
- entry.source = "comments";
8223
- entry.confidence = 0.7;
8224
- }
8215
+ const { keys } = listKeys(ctx.absPath);
8225
8216
  if (keys.length > 0) {
8226
8217
  entry.searchHints = keys;
8227
8218
  }
@@ -8250,6 +8241,7 @@ function applyTaskMetadata(entry, ctx) {
8250
8241
  }
8251
8242
  var init_renderers = __esm(() => {
8252
8243
  init_env();
8244
+ init_frontmatter();
8253
8245
  init_markdown();
8254
8246
  init_common();
8255
8247
  init_metadata();
@@ -8296,6 +8288,11 @@ var init_renderers = __esm(() => {
8296
8288
  appliesTo: ({ rendererName }) => rendererName === "session-md",
8297
8289
  contribute: (entry, ctx) => applySessionMetadata(entry, ctx.renderContext)
8298
8290
  });
8291
+ registerMetadataContributor({
8292
+ name: "fact-md-metadata",
8293
+ appliesTo: ({ rendererName }) => rendererName === "fact-md",
8294
+ contribute: (entry, ctx) => applyFactMetadata(entry, ctx.renderContext)
8295
+ });
8299
8296
  });
8300
8297
 
8301
8298
  // src/core/asset/asset-registry.ts
@@ -8304,24 +8301,26 @@ var init_asset_registry = __esm(() => {
8304
8301
  });
8305
8302
 
8306
8303
  // src/core/asset/asset-spec.ts
8307
- import path from "path";
8304
+ import path2 from "node:path";
8305
+ function toPosix2(input) {
8306
+ return input.replace(/\\/g, "/");
8307
+ }
8308
8308
  function getAssetTypes() {
8309
8309
  return Object.keys(ASSET_SPECS_INTERNAL);
8310
8310
  }
8311
8311
  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;
8312
8312
  var init_asset_spec = __esm(() => {
8313
8313
  init_renderers();
8314
- init_common();
8315
8314
  init_asset_registry();
8316
8315
  markdownSpec = {
8317
- isRelevantFile: (fileName) => path.extname(fileName).toLowerCase() === ".md",
8316
+ isRelevantFile: (fileName) => path2.extname(fileName).toLowerCase() === ".md",
8318
8317
  toCanonicalName: (typeRoot, filePath) => {
8319
- const rel = toPosix(path.relative(typeRoot, filePath));
8318
+ const rel = toPosix2(path2.relative(typeRoot, filePath));
8320
8319
  return rel.endsWith(".md") ? rel.slice(0, -3) : rel;
8321
8320
  },
8322
8321
  toAssetPath: (typeRoot, name) => {
8323
8322
  const withExt = name.endsWith(".md") ? name : `${name}.md`;
8324
- return path.join(typeRoot, withExt);
8323
+ return path2.join(typeRoot, withExt);
8325
8324
  }
8326
8325
  };
8327
8326
  SCRIPT_EXTENSIONS = new Set([
@@ -8343,21 +8342,21 @@ var init_asset_spec = __esm(() => {
8343
8342
  ".kts"
8344
8343
  ]);
8345
8344
  scriptSpec = {
8346
- isRelevantFile: (fileName) => SCRIPT_EXTENSIONS.has(path.extname(fileName).toLowerCase()),
8347
- toCanonicalName: (typeRoot, filePath) => toPosix(path.relative(typeRoot, filePath)),
8348
- toAssetPath: (typeRoot, name) => path.join(typeRoot, name)
8345
+ isRelevantFile: (fileName) => SCRIPT_EXTENSIONS.has(path2.extname(fileName).toLowerCase()),
8346
+ toCanonicalName: (typeRoot, filePath) => toPosix2(path2.relative(typeRoot, filePath)),
8347
+ toAssetPath: (typeRoot, name) => path2.join(typeRoot, name)
8349
8348
  };
8350
8349
  ASSET_SPECS_INTERNAL = {
8351
8350
  skill: {
8352
8351
  stashDir: "skills",
8353
8352
  isRelevantFile: (fileName) => fileName === "SKILL.md",
8354
8353
  toCanonicalName: (typeRoot, filePath) => {
8355
- const relDir = toPosix(path.dirname(path.relative(typeRoot, filePath)));
8354
+ const relDir = toPosix2(path2.dirname(path2.relative(typeRoot, filePath)));
8356
8355
  if (!relDir || relDir === ".")
8357
8356
  return;
8358
8357
  return relDir;
8359
8358
  },
8360
- toAssetPath: (typeRoot, name) => path.join(typeRoot, name, "SKILL.md")
8359
+ toAssetPath: (typeRoot, name) => path2.join(typeRoot, name, "SKILL.md")
8361
8360
  },
8362
8361
  command: { stashDir: "commands", ...markdownSpec },
8363
8362
  agent: { stashDir: "agents", ...markdownSpec },
@@ -8374,10 +8373,10 @@ var init_asset_spec = __esm(() => {
8374
8373
  stashDir: "env",
8375
8374
  isRelevantFile: (fileName) => fileName === ".env" || fileName.endsWith(".env"),
8376
8375
  toCanonicalName: (typeRoot, filePath) => {
8377
- const rel = toPosix(path.relative(typeRoot, filePath));
8378
- const fileName = path.basename(rel);
8376
+ const rel = toPosix2(path2.relative(typeRoot, filePath));
8377
+ const fileName = path2.basename(rel);
8379
8378
  if (fileName === ".env") {
8380
- const dir = path.dirname(rel);
8379
+ const dir = path2.dirname(rel);
8381
8380
  return dir === "." || dir === "" ? "default" : `${dir}/default`;
8382
8381
  }
8383
8382
  const stripped = rel.endsWith(".env") ? rel.slice(0, -4) : rel;
@@ -8385,17 +8384,17 @@ var init_asset_spec = __esm(() => {
8385
8384
  },
8386
8385
  toAssetPath: (typeRoot, name) => {
8387
8386
  if (name === "default")
8388
- return path.join(typeRoot, ".env");
8389
- return path.join(typeRoot, name.endsWith(".env") ? name : `${name}.env`);
8387
+ return path2.join(typeRoot, ".env");
8388
+ return path2.join(typeRoot, name.endsWith(".env") ? name : `${name}.env`);
8390
8389
  },
8391
8390
  rendererName: "env-file",
8392
- 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`
8391
+ actionBuilder: (ref) => `akm show ${ref} -> inspect key names; akm env run ${ref} -- <command> -> run with the whole .env injected (prefer --clean to minimize inherited parent env; child stdout is not redacted); akm env export ${ref} --out <file> -> write a sourceable script to a file`
8393
8392
  },
8394
8393
  secret: {
8395
8394
  stashDir: "secrets",
8396
8395
  isRelevantFile: (fileName) => !fileName.endsWith(".lock") && !fileName.endsWith(".sensitive"),
8397
- toCanonicalName: (typeRoot, filePath) => toPosix(path.relative(typeRoot, filePath)),
8398
- toAssetPath: (typeRoot, name) => path.join(typeRoot, name),
8396
+ toCanonicalName: (typeRoot, filePath) => toPosix2(path2.relative(typeRoot, filePath)),
8397
+ toAssetPath: (typeRoot, name) => path2.join(typeRoot, name),
8399
8398
  rendererName: "secret-file",
8400
8399
  actionBuilder: (ref) => `akm show ${ref} -> name only (value never shown); akm secret path ${ref} -> file path; akm secret run ${ref} <VAR> -- <command> -> run with value injected into $VAR`
8401
8400
  },
@@ -8413,14 +8412,14 @@ var init_asset_spec = __esm(() => {
8413
8412
  },
8414
8413
  task: {
8415
8414
  stashDir: "tasks",
8416
- isRelevantFile: (fileName) => path.extname(fileName).toLowerCase() === ".yml",
8415
+ isRelevantFile: (fileName) => path2.extname(fileName).toLowerCase() === ".yml",
8417
8416
  toCanonicalName: (typeRoot, filePath) => {
8418
- const rel = toPosix(path.relative(typeRoot, filePath));
8417
+ const rel = toPosix2(path2.relative(typeRoot, filePath));
8419
8418
  return rel.endsWith(".yml") ? rel.slice(0, -4) : rel;
8420
8419
  },
8421
8420
  toAssetPath: (typeRoot, name) => {
8422
8421
  const withExt = name.endsWith(".yml") ? name : `${name}.yml`;
8423
- return path.join(typeRoot, withExt);
8422
+ return path2.join(typeRoot, withExt);
8424
8423
  },
8425
8424
  rendererName: "task-yaml",
8426
8425
  actionBuilder: buildTaskAction
@@ -8430,15 +8429,18 @@ var init_asset_spec = __esm(() => {
8430
8429
  ...markdownSpec,
8431
8430
  rendererName: "session-md",
8432
8431
  actionBuilder: (ref) => `akm show ${ref} -> read the session summary; follow the \`access\` frontmatter to open the raw log at \`log_path\``
8432
+ },
8433
+ fact: {
8434
+ stashDir: "facts",
8435
+ ...markdownSpec,
8436
+ rendererName: "fact-md",
8437
+ actionBuilder: (ref) => `akm show ${ref} -> read the stash fact and apply it as durable context`
8433
8438
  }
8434
8439
  };
8435
8440
  TYPE_DIRS = Object.fromEntries(Object.entries(ASSET_SPECS_INTERNAL).map(([type, spec]) => [type, spec.stashDir]));
8436
8441
  });
8437
8442
 
8438
8443
  // src/core/common.ts
8439
- function toPosix(input) {
8440
- return input.replace(/\\/g, "/");
8441
- }
8442
8444
  function asNonEmptyString(value) {
8443
8445
  if (typeof value !== "string")
8444
8446
  return;
@@ -8457,7 +8459,7 @@ var init_common = __esm(() => {
8457
8459
  });
8458
8460
 
8459
8461
  // src/core/paths.ts
8460
- import path2 from "path";
8462
+ import path3 from "node:path";
8461
8463
  function isUnderBunTest(env) {
8462
8464
  return env.BUN_TEST === "1" || env.NODE_ENV === "test";
8463
8465
  }
@@ -8474,23 +8476,23 @@ function getDataDir(env = process.env, platform = process.platform) {
8474
8476
  if (platform === "win32") {
8475
8477
  const localAppData = env.LOCALAPPDATA?.trim();
8476
8478
  if (localAppData)
8477
- return path2.join(localAppData, "akm", "data");
8479
+ return path3.join(localAppData, "akm", "data");
8478
8480
  const userProfile = env.USERPROFILE?.trim();
8479
8481
  if (userProfile)
8480
- return path2.join(userProfile, "AppData", "Local", "akm", "data");
8482
+ return path3.join(userProfile, "AppData", "Local", "akm", "data");
8481
8483
  const appData = env.APPDATA?.trim();
8482
8484
  if (!appData) {
8483
8485
  throw new ConfigError("Unable to determine data directory. Set LOCALAPPDATA, USERPROFILE, or APPDATA.", "CONFIG_DIR_UNRESOLVABLE");
8484
8486
  }
8485
- return path2.join(appData, "..", "Local", "akm", "data");
8487
+ return path3.join(appData, "..", "Local", "akm", "data");
8486
8488
  }
8487
8489
  const xdgDataHome = env.XDG_DATA_HOME?.trim();
8488
8490
  if (xdgDataHome)
8489
- return path2.join(xdgDataHome, "akm");
8491
+ return path3.join(xdgDataHome, "akm");
8490
8492
  const home = env.HOME?.trim();
8491
8493
  if (!home)
8492
- return path2.join("/tmp", "akm-data");
8493
- return path2.join(home, ".local", "share", "akm");
8494
+ return path3.join("/tmp", "akm-data");
8495
+ return path3.join(home, ".local", "share", "akm");
8494
8496
  }
8495
8497
  var init_paths = __esm(() => {
8496
8498
  init_common();
@@ -8498,22 +8500,40 @@ var init_paths = __esm(() => {
8498
8500
  });
8499
8501
 
8500
8502
  // scripts/migrations/import-fs-improve-runs-to-db.ts
8501
- import fs4 from "fs";
8502
- import path4 from "path";
8503
+ import fs5 from "fs";
8504
+ import path5 from "path";
8503
8505
 
8504
8506
  // src/core/state-db.ts
8505
- import fs3 from "fs";
8506
- import path3 from "path";
8507
+ import path4 from "node:path";
8508
+
8509
+ // src/storage/managed-db.ts
8510
+ import fs2 from "node:fs";
8511
+ import path from "node:path";
8507
8512
 
8508
8513
  // src/storage/database.ts
8509
- import { createRequire } from "module";
8514
+ import { createRequire as createRequire2 } from "node:module";
8510
8515
  var isBun = !!process.versions?.bun;
8511
- var nodeRequire = createRequire(import.meta.url);
8512
- function openDatabase(path, opts) {
8513
- if (isBun) {
8514
- return openBunDatabase(path, opts);
8516
+ var nodeRequire = createRequire2(import.meta.url);
8517
+ var bunSqliteProvider = {
8518
+ name: "bun:sqlite",
8519
+ supported: () => isBun,
8520
+ open: openBunDatabase
8521
+ };
8522
+ var nodeSqliteProvider = {
8523
+ name: "better-sqlite3",
8524
+ supported: () => !isBun,
8525
+ open: openNodeDatabase
8526
+ };
8527
+ var PROVIDERS = [bunSqliteProvider, nodeSqliteProvider];
8528
+ function selectProvider() {
8529
+ const provider = PROVIDERS.find((p) => p.supported());
8530
+ if (!provider) {
8531
+ throw new Error(`No storage provider supports the current runtime (${isBun ? "Bun" : "Node"}).`);
8515
8532
  }
8516
- return openNodeDatabase(path, opts);
8533
+ return provider;
8534
+ }
8535
+ function openDatabase(path, opts) {
8536
+ return selectProvider().open(path, opts);
8517
8537
  }
8518
8538
  function openBunDatabase(path, opts) {
8519
8539
  const { Database: BunDatabase } = loadBunSqlite();
@@ -8538,7 +8558,16 @@ function loadBunSqlite() {
8538
8558
  var betterSqlite3Ctor;
8539
8559
  function loadBetterSqlite3() {
8540
8560
  if (!betterSqlite3Ctor) {
8541
- const mod = nodeRequire("better-sqlite3");
8561
+ let mod;
8562
+ try {
8563
+ mod = nodeRequire("better-sqlite3");
8564
+ } catch (err) {
8565
+ throw new Error(`akm could not load 'better-sqlite3', the SQLite driver it needs on Node.js.
8566
+ ` + ` • Reinstall akm with a working C/C++ build toolchain so its optional
8567
+ ` + " 'better-sqlite3' native binding rebuilds (a global `npm i -g better-sqlite3`\n" + ` will NOT be resolved — Node loads it from akm's own node_modules).
8568
+ ` + ` • Or run akm under Bun, which has a built-in SQLite driver and needs no native build.
8569
+ ` + ` Underlying load error: ${err instanceof Error ? err.message : String(err)}`);
8570
+ }
8542
8571
  betterSqlite3Ctor = mod.default ?? mod;
8543
8572
  }
8544
8573
  return betterSqlite3Ctor;
@@ -8554,6 +8583,109 @@ function openNodeDatabase(path, opts) {
8554
8583
  return db;
8555
8584
  }
8556
8585
 
8586
+ // src/storage/sqlite-pragmas.ts
8587
+ init_warn();
8588
+
8589
+ // src/runtime.ts
8590
+ import { createWriteStream, statfsSync } from "node:fs";
8591
+ import { createRequire as createRequire3 } from "node:module";
8592
+ var isBun2 = !!process.versions?.bun;
8593
+ var nodeRequire2 = createRequire3(import.meta.url);
8594
+ function statfsType(path) {
8595
+ try {
8596
+ return statfsSync(path).type;
8597
+ } catch {
8598
+ return;
8599
+ }
8600
+ }
8601
+ var mainPath = isBun2 ? bunGlobal().main : process.argv[1];
8602
+ function bunGlobal() {
8603
+ return globalThis.Bun;
8604
+ }
8605
+
8606
+ // src/storage/sqlite-pragmas.ts
8607
+ var VALID_MODES = new Set(["WAL", "DELETE", "TRUNCATE"]);
8608
+ var warnedInvalid = false;
8609
+ var warnedNetworkFallback = false;
8610
+ function resolveJournalMode(raw) {
8611
+ if (raw === undefined)
8612
+ return "WAL";
8613
+ const normalized = raw.trim().toUpperCase();
8614
+ if (normalized === "")
8615
+ return "WAL";
8616
+ if (VALID_MODES.has(normalized)) {
8617
+ return normalized;
8618
+ }
8619
+ warnInvalidJournalModeOnce(raw);
8620
+ return "WAL";
8621
+ }
8622
+ function resolveConfiguredJournalMode(env = process.env) {
8623
+ return resolveJournalMode(env.AKM_SQLITE_JOURNAL_MODE);
8624
+ }
8625
+ function warnInvalidJournalModeOnce(raw) {
8626
+ if (warnedInvalid)
8627
+ return;
8628
+ warnedInvalid = true;
8629
+ warn(`[akm] invalid AKM_SQLITE_JOURNAL_MODE=${JSON.stringify(raw)} — using WAL (valid: WAL, DELETE, TRUNCATE)`);
8630
+ }
8631
+ var FS_MAGIC_NFS = 26985;
8632
+ var FS_MAGIC_SMB = 20859;
8633
+ var FS_MAGIC_CIFS = 4283649346;
8634
+ var FS_MAGIC_SMB2 = 4266872130;
8635
+ var FS_MAGIC_FUSE = 1702057286;
8636
+ var NETWORK_FS_MAGICS = new Set([
8637
+ FS_MAGIC_NFS,
8638
+ FS_MAGIC_SMB,
8639
+ FS_MAGIC_CIFS,
8640
+ FS_MAGIC_SMB2,
8641
+ FS_MAGIC_FUSE
8642
+ ]);
8643
+ function isNetworkFilesystem(fsType) {
8644
+ if (fsType === undefined)
8645
+ return false;
8646
+ return NETWORK_FS_MAGICS.has(fsType);
8647
+ }
8648
+ function applyStandardPragmas(db, opts = {}) {
8649
+ let mode = resolveConfiguredJournalMode(opts.env);
8650
+ if (mode === "WAL" && opts.dataDir) {
8651
+ const probe = opts.fsTypeProbe ?? statfsType;
8652
+ if (isNetworkFilesystem(probe(opts.dataDir))) {
8653
+ mode = "DELETE";
8654
+ warnNetworkFallbackOnce(opts.dataDir);
8655
+ }
8656
+ }
8657
+ db.exec("PRAGMA busy_timeout = 30000");
8658
+ db.exec(`PRAGMA journal_mode = ${mode}`);
8659
+ if (opts.foreignKeys !== false) {
8660
+ db.exec("PRAGMA foreign_keys = ON");
8661
+ }
8662
+ if (mode !== "WAL") {
8663
+ db.exec("PRAGMA synchronous = FULL");
8664
+ }
8665
+ return mode;
8666
+ }
8667
+ function warnNetworkFallbackOnce(dataDir) {
8668
+ if (warnedNetworkFallback)
8669
+ return;
8670
+ warnedNetworkFallback = true;
8671
+ warn(`[akm] network filesystem detected at ${dataDir} — WAL unsupported, using DELETE journal mode`);
8672
+ }
8673
+
8674
+ // src/storage/managed-db.ts
8675
+ function openManagedDatabase(spec) {
8676
+ const dir = path.dirname(spec.path);
8677
+ if (!fs2.existsSync(dir)) {
8678
+ fs2.mkdirSync(dir, { recursive: true });
8679
+ }
8680
+ const db = openDatabase(spec.path);
8681
+ applyStandardPragmas(db, spec.pragmas ?? { dataDir: dir });
8682
+ spec.init?.(db);
8683
+ return db;
8684
+ }
8685
+
8686
+ // src/core/state-db.ts
8687
+ init_paths();
8688
+
8557
8689
  // src/storage/engines/sqlite-migrations.ts
8558
8690
  function ensureMigrationsTable(db) {
8559
8691
  db.exec(`
@@ -8578,87 +8710,31 @@ function runMigrations(db, migrations, opts) {
8578
8710
  }
8579
8711
  }
8580
8712
 
8581
- // src/core/assert.ts
8582
- function assertNever(x, context) {
8583
- let serialized;
8584
- try {
8585
- serialized = JSON.stringify(x);
8586
- } catch {
8587
- serialized = String(x);
8588
- }
8589
- if (serialized === undefined) {
8590
- serialized = String(x);
8591
- }
8592
- const where = context ? ` (${context})` : "";
8593
- throw new Error(`Unexpected value reached assertNever${where}: ${serialized}`);
8594
- }
8595
-
8596
- // src/core/improve-types.ts
8597
- function classifyImproveAction(mode) {
8598
- switch (mode) {
8599
- case "reflect":
8600
- case "distill":
8601
- case "memory-inference":
8602
- case "graph-extraction":
8603
- return "accepted";
8604
- case "reflect-cooldown":
8605
- case "reflect-skipped":
8606
- case "distill-skipped":
8607
- case "reflect-guard-rejected":
8608
- return "rejected";
8609
- case "reflect-failed":
8610
- case "error":
8611
- return "error";
8612
- case "memory-prune":
8613
- return "noop";
8614
- default:
8615
- return assertNever(mode);
8616
- }
8617
- }
8618
-
8619
- // src/core/state-db.ts
8620
- init_paths();
8621
- init_warn();
8622
- function getStateDbPath() {
8623
- return path3.join(getDataDir(), "state.db");
8624
- }
8625
- function openStateDatabase(dbPath) {
8626
- const resolvedPath = dbPath ?? getStateDbPath();
8627
- const dir = path3.dirname(resolvedPath);
8628
- if (!fs3.existsSync(dir)) {
8629
- fs3.mkdirSync(dir, { recursive: true });
8630
- }
8631
- const db = openDatabase(resolvedPath);
8632
- db.exec("PRAGMA journal_mode = WAL");
8633
- db.exec("PRAGMA foreign_keys = ON");
8634
- db.exec("PRAGMA busy_timeout = 30000");
8635
- runMigrations2(db);
8636
- return db;
8637
- }
8713
+ // src/core/state/migrations.ts
8638
8714
  var MIGRATIONS = [
8639
8715
  {
8640
8716
  id: "001-initial-schema",
8641
8717
  up: `
8642
- -- \u2500\u2500 events \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500
8718
+ -- ── events ──────────────────────────────────────────────────────────────
8643
8719
  --
8644
8720
  -- Replaces events.jsonl. Indexed (query) columns:
8645
- -- id INTEGER PK \u2014 monotonic rowid; replaces byte-offset cursor.
8721
+ -- id INTEGER PK monotonic rowid; replaces byte-offset cursor.
8646
8722
  -- Callers store this as "sinceId" for resume.
8647
- -- event_type TEXT \u2014 indexed; replaces the type filter in readEvents().
8648
- -- ts TEXT \u2014 ISO-8601 UTC ms; indexed for range queries.
8649
- -- ref TEXT \u2014 nullable asset ref; indexed for ref-scoped queries.
8723
+ -- event_type TEXT indexed; replaces the type filter in readEvents().
8724
+ -- ts TEXT ISO-8601 UTC ms; indexed for range queries.
8725
+ -- ref TEXT nullable asset ref; indexed for ref-scoped queries.
8650
8726
  --
8651
8727
  -- Extensible (metadata_json) columns:
8652
- -- metadata_json TEXT \u2014 JSON object storing all non-indexed payload
8728
+ -- metadata_json TEXT JSON object storing all non-indexed payload
8653
8729
  -- fields (tags, any future structured fields).
8654
8730
  -- Maps directly to EventEnvelope.metadata.
8655
8731
  --
8656
- -- schema_version mirrors EventEnvelope.schemaVersion \u2014 always 1 for v1
8732
+ -- schema_version mirrors EventEnvelope.schemaVersion always 1 for v1
8657
8733
  -- rows. Stored as a column (not in the JSON blob) so future schema
8658
8734
  -- changes can be detected and migrated row-by-row if ever needed.
8659
8735
  --
8660
8736
  -- TTL: rows where ts < NOW() - 90 days can be deleted by a maintenance job.
8661
- -- No automatic deletion occurs here \u2014 callers call purgeOldEvents().
8737
+ -- No automatic deletion occurs here callers call purgeOldEvents().
8662
8738
  --
8663
8739
  -- ADD COLUMN extension points (future migrations):
8664
8740
  -- ALTER TABLE events ADD COLUMN stash_dir TEXT DEFAULT NULL;
@@ -8674,41 +8750,41 @@ var MIGRATIONS = [
8674
8750
  );
8675
8751
 
8676
8752
  -- Query patterns supported by these indexes:
8677
- -- SELECT \u2026 WHERE event_type = ? \u2192 idx_events_type
8678
- -- SELECT \u2026 WHERE ref = ? \u2192 idx_events_ref
8679
- -- SELECT \u2026 WHERE ts >= ? AND ts <= ? \u2192 idx_events_ts
8680
- -- SELECT \u2026 WHERE event_type = ? AND ref = ? \u2192 idx_events_type (prefix scan) + filter
8681
- -- SELECT \u2026 WHERE id > ? \u2192 PK (rowid) \u2014 no extra index needed
8753
+ -- SELECT WHERE event_type = ? idx_events_type
8754
+ -- SELECT WHERE ref = ? idx_events_ref
8755
+ -- SELECT WHERE ts >= ? AND ts <= ? idx_events_ts
8756
+ -- SELECT WHERE event_type = ? AND ref = ? idx_events_type (prefix scan) + filter
8757
+ -- SELECT WHERE id > ? PK (rowid) no extra index needed
8682
8758
  CREATE INDEX IF NOT EXISTS idx_events_type ON events(event_type);
8683
8759
  CREATE INDEX IF NOT EXISTS idx_events_ref ON events(ref);
8684
8760
  CREATE INDEX IF NOT EXISTS idx_events_ts ON events(ts);
8685
8761
 
8686
- -- \u2500\u2500 proposals \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500
8762
+ -- ── proposals ────────────────────────────────────────────────────────────
8687
8763
  --
8688
8764
  -- Replaces per-uuid JSON directories under <stashDir>/.akm/proposals/.
8689
8765
  --
8690
8766
  -- Indexed (query) columns:
8691
- -- id TEXT PK \u2014 UUID (crypto.randomUUID()); stable directory name.
8692
- -- stash_dir TEXT \u2014 absolute stash root; multi-stash installs need
8767
+ -- id TEXT PK UUID (crypto.randomUUID()); stable directory name.
8768
+ -- stash_dir TEXT absolute stash root; multi-stash installs need
8693
8769
  -- this to partition proposal lists per stash.
8694
- -- ref TEXT \u2014 target asset ref (e.g. "lesson:alpha");
8770
+ -- ref TEXT target asset ref (e.g. "lesson:alpha");
8695
8771
  -- indexed for ref-scoped queue views.
8696
- -- status TEXT \u2014 "pending" | "accepted" | "rejected"; indexed
8772
+ -- status TEXT "pending" | "accepted" | "rejected"; indexed
8697
8773
  -- so pending-queue queries are fast.
8698
- -- source TEXT \u2014 human-readable origin tag (e.g. "reflect").
8699
- -- created_at TEXT \u2014 ISO-8601; used for ORDER BY created_at ASC.
8700
- -- updated_at TEXT \u2014 ISO-8601; updated on accept/reject.
8774
+ -- source TEXT human-readable origin tag (e.g. "reflect").
8775
+ -- created_at TEXT ISO-8601; used for ORDER BY created_at ASC.
8776
+ -- updated_at TEXT ISO-8601; updated on accept/reject.
8701
8777
  --
8702
8778
  -- Large payload columns (NOT indexed):
8703
- -- content TEXT \u2014 full markdown text; the proposal payload body.
8704
- -- frontmatter_json TEXT \u2014 JSON of parsed frontmatter (may be NULL when
8779
+ -- content TEXT full markdown text; the proposal payload body.
8780
+ -- frontmatter_json TEXT JSON of parsed frontmatter (may be NULL when
8705
8781
  -- the content has no frontmatter block).
8706
8782
  --
8707
8783
  -- Extensible (metadata_json) columns:
8708
- -- metadata_json TEXT \u2014 JSON object for future proposal fields.
8784
+ -- metadata_json TEXT JSON object for future proposal fields.
8709
8785
  -- Current fields stored here: sourceRun,
8710
8786
  -- review, confidence, gateDecision (#577),
8711
- -- backupContent.
8787
+ -- backupContent, eligibilitySource.
8712
8788
  --
8713
8789
  -- ADD COLUMN extension points (future migrations):
8714
8790
  -- ALTER TABLE proposals ADD COLUMN source_run TEXT DEFAULT NULL;
@@ -8731,38 +8807,38 @@ var MIGRATIONS = [
8731
8807
  );
8732
8808
 
8733
8809
  -- Query patterns:
8734
- -- SELECT \u2026 WHERE stash_dir = ? AND status = ? \u2192 idx_proposals_stash_status
8735
- -- SELECT \u2026 WHERE ref = ? AND status = ? \u2192 idx_proposals_ref_status
8736
- -- SELECT \u2026 WHERE id = ? \u2192 PK
8810
+ -- SELECT WHERE stash_dir = ? AND status = ? idx_proposals_stash_status
8811
+ -- SELECT WHERE ref = ? AND status = ? idx_proposals_ref_status
8812
+ -- SELECT WHERE id = ? PK
8737
8813
  CREATE INDEX IF NOT EXISTS idx_proposals_stash_status
8738
8814
  ON proposals(stash_dir, status);
8739
8815
  CREATE INDEX IF NOT EXISTS idx_proposals_ref_status
8740
8816
  ON proposals(ref, status);
8741
8817
 
8742
- -- \u2500\u2500 task_history \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500
8818
+ -- ── task_history ─────────────────────────────────────────────────────────
8743
8819
  --
8744
8820
  -- Replaces per-task JSONL files under <cacheDir>/tasks/history/.
8745
8821
  --
8746
8822
  -- Indexed (query) columns:
8747
- -- task_id TEXT PK \u2014 stable task identifier string.
8748
- -- status TEXT \u2014 terminal status (e.g. "completed", "failed",
8823
+ -- task_id TEXT PK stable task identifier string.
8824
+ -- status TEXT terminal status (e.g. "completed", "failed",
8749
8825
  -- "cancelled"); indexed for status-scoped queries.
8750
- -- started_at TEXT \u2014 ISO-8601; indexed for time-range queries.
8751
- -- target_kind TEXT \u2014 kind of the target entity (e.g. "issue",
8826
+ -- started_at TEXT ISO-8601; indexed for time-range queries.
8827
+ -- target_kind TEXT kind of the target entity (e.g. "issue",
8752
8828
  -- "workflow", "agent"); indexed for kind-scoped queries.
8753
- -- target_ref TEXT \u2014 stable ref of the target entity; indexed for
8829
+ -- target_ref TEXT stable ref of the target entity; indexed for
8754
8830
  -- per-target history lookups.
8755
8831
  --
8756
8832
  -- Non-indexed time columns:
8757
- -- completed_at TEXT \u2014 ISO-8601 or NULL if still running.
8758
- -- failed_at TEXT \u2014 ISO-8601 or NULL.
8833
+ -- completed_at TEXT ISO-8601 or NULL if still running.
8834
+ -- failed_at TEXT ISO-8601 or NULL.
8759
8835
  --
8760
8836
  -- Non-indexed diagnostic columns:
8761
- -- log_path TEXT \u2014 absolute path to the task log file, if any.
8837
+ -- log_path TEXT absolute path to the task log file, if any.
8762
8838
  --
8763
8839
  -- Extensible (metadata_json) columns:
8764
- -- metadata_json TEXT \u2014 JSON object for future task fields (exit_code,
8765
- -- runner, priority, parent_task_id, \u2026).
8840
+ -- metadata_json TEXT JSON object for future task fields (exit_code,
8841
+ -- runner, priority, parent_task_id, ).
8766
8842
  --
8767
8843
  -- ADD COLUMN extension points (future migrations):
8768
8844
  -- ALTER TABLE task_history ADD COLUMN exit_code INTEGER DEFAULT NULL;
@@ -8783,10 +8859,10 @@ var MIGRATIONS = [
8783
8859
  );
8784
8860
 
8785
8861
  -- Query patterns:
8786
- -- SELECT \u2026 WHERE task_id = ? \u2192 PK
8787
- -- SELECT \u2026 WHERE started_at >= ? AND started_at <= ? \u2192 idx_task_history_started
8788
- -- SELECT \u2026 WHERE target_kind = ? AND target_ref = ? \u2192 idx_task_history_target
8789
- -- SELECT \u2026 WHERE status = ? \u2192 idx_task_history_status
8862
+ -- SELECT WHERE task_id = ? PK
8863
+ -- SELECT WHERE started_at >= ? AND started_at <= ? idx_task_history_started
8864
+ -- SELECT WHERE target_kind = ? AND target_ref = ? idx_task_history_target
8865
+ -- SELECT WHERE status = ? idx_task_history_status
8790
8866
  CREATE INDEX IF NOT EXISTS idx_task_history_started
8791
8867
  ON task_history(started_at);
8792
8868
  CREATE INDEX IF NOT EXISTS idx_task_history_target
@@ -8855,12 +8931,12 @@ var MIGRATIONS = [
8855
8931
  );
8856
8932
 
8857
8933
  -- Query patterns supported:
8858
- -- SELECT \u2026 WHERE started_at >= ? AND started_at <= ?
8859
- -- \u2192 idx_improve_runs_started
8860
- -- SELECT \u2026 WHERE dry_run = 0
8861
- -- \u2192 idx_improve_runs_dry_run (productivity audits filter trap)
8862
- -- SELECT \u2026 WHERE stash_dir = ? AND scope_mode = ?
8863
- -- \u2192 idx_improve_runs_stash_scope
8934
+ -- SELECT WHERE started_at >= ? AND started_at <= ?
8935
+ -- idx_improve_runs_started
8936
+ -- SELECT WHERE dry_run = 0
8937
+ -- idx_improve_runs_dry_run (productivity audits filter trap)
8938
+ -- SELECT WHERE stash_dir = ? AND scope_mode = ?
8939
+ -- idx_improve_runs_stash_scope
8864
8940
  CREATE INDEX IF NOT EXISTS idx_improve_runs_started
8865
8941
  ON improve_runs(started_at);
8866
8942
  CREATE INDEX IF NOT EXISTS idx_improve_runs_dry_run
@@ -8887,9 +8963,9 @@ var MIGRATIONS = [
8887
8963
  );
8888
8964
 
8889
8965
  -- Query patterns:
8890
- -- SELECT \u2026 WHERE harness = ? \u2192 idx_extract_sessions_harness
8891
- -- SELECT \u2026 WHERE processed_at >= ? \u2192 idx_extract_sessions_processed
8892
- -- SELECT \u2026 WHERE harness = ? AND session_id = ? \u2192 PK
8966
+ -- SELECT WHERE harness = ? idx_extract_sessions_harness
8967
+ -- SELECT WHERE processed_at >= ? idx_extract_sessions_processed
8968
+ -- SELECT WHERE harness = ? AND session_id = ? PK
8893
8969
  CREATE INDEX IF NOT EXISTS idx_extract_sessions_harness
8894
8970
  ON extract_sessions_seen(harness);
8895
8971
  CREATE INDEX IF NOT EXISTS idx_extract_sessions_processed
@@ -8905,17 +8981,222 @@ var MIGRATIONS = [
8905
8981
  imported_count INTEGER NOT NULL DEFAULT 0
8906
8982
  );
8907
8983
  `
8984
+ },
8985
+ {
8986
+ id: "006-proposals-pending-ref-source",
8987
+ up: `
8988
+ CREATE INDEX IF NOT EXISTS idx_proposals_stash_status_ref_source
8989
+ ON proposals(stash_dir, status, ref, source);
8990
+ `
8991
+ },
8992
+ {
8993
+ id: "007-consolidation-judged",
8994
+ up: `
8995
+ CREATE TABLE IF NOT EXISTS consolidation_judged (
8996
+ entry_key TEXT PRIMARY KEY,
8997
+ content_hash TEXT NOT NULL,
8998
+ judged_at TEXT NOT NULL,
8999
+ outcome TEXT NOT NULL
9000
+ );
9001
+ `
9002
+ },
9003
+ {
9004
+ id: "008-body-embeddings",
9005
+ up: `
9006
+ CREATE TABLE IF NOT EXISTS body_embeddings (
9007
+ content_hash TEXT PRIMARY KEY,
9008
+ embedding BLOB NOT NULL,
9009
+ model_id TEXT NOT NULL,
9010
+ created_at INTEGER NOT NULL
9011
+ );
9012
+ `
9013
+ },
9014
+ {
9015
+ id: "009-asset-salience",
9016
+ up: `
9017
+ CREATE TABLE IF NOT EXISTS asset_salience (
9018
+ asset_ref TEXT PRIMARY KEY,
9019
+ encoding_salience REAL NOT NULL DEFAULT 0.5,
9020
+ outcome_salience REAL NOT NULL DEFAULT 0.0,
9021
+ retrieval_salience REAL NOT NULL DEFAULT 0.0,
9022
+ rank_score REAL NOT NULL DEFAULT 0.0,
9023
+ consecutive_no_ops INTEGER NOT NULL DEFAULT 0,
9024
+ updated_at INTEGER NOT NULL DEFAULT 0
9025
+ );
9026
+
9027
+ -- Hot path: sort / filter by rank_score for selector queries.
9028
+ CREATE INDEX IF NOT EXISTS idx_asset_salience_rank
9029
+ ON asset_salience(rank_score DESC);
9030
+ `
9031
+ },
9032
+ {
9033
+ id: "010-asset-outcome",
9034
+ up: `
9035
+ CREATE TABLE IF NOT EXISTS asset_outcome (
9036
+ asset_ref TEXT PRIMARY KEY,
9037
+ last_retrieved_at INTEGER NOT NULL DEFAULT 0,
9038
+ retrieval_count INTEGER NOT NULL DEFAULT 0,
9039
+ expected_retrieval_rate REAL NOT NULL DEFAULT 0.0,
9040
+ negative_feedback_count INTEGER NOT NULL DEFAULT 0,
9041
+ accepted_change_count INTEGER NOT NULL DEFAULT 0,
9042
+ review_pressure INTEGER NOT NULL DEFAULT 0,
9043
+ outcome_score REAL NOT NULL DEFAULT 0.0,
9044
+ updated_at INTEGER NOT NULL DEFAULT 0
9045
+ );
9046
+
9047
+ -- Hot path: sort assets by review_pressure DESC for #613 admission.
9048
+ CREATE INDEX IF NOT EXISTS idx_asset_outcome_review_pressure
9049
+ ON asset_outcome(review_pressure DESC);
9050
+
9051
+ -- Secondary: sort by outcome_score DESC for outcomeSalience reads.
9052
+ CREATE INDEX IF NOT EXISTS idx_asset_outcome_score
9053
+ ON asset_outcome(outcome_score DESC);
9054
+ `
9055
+ },
9056
+ {
9057
+ id: "011-asset-salience-homeostatic-demoted-at",
9058
+ up: `
9059
+ ALTER TABLE asset_salience ADD COLUMN homeostatic_demoted_at INTEGER DEFAULT NULL;
9060
+ `
9061
+ },
9062
+ {
9063
+ id: "012-improve-gate-thresholds",
9064
+ up: `
9065
+ CREATE TABLE IF NOT EXISTS improve_gate_thresholds (
9066
+ phase TEXT NOT NULL PRIMARY KEY,
9067
+ threshold INTEGER NOT NULL,
9068
+ updated_at INTEGER NOT NULL
9069
+ );
9070
+ `
9071
+ },
9072
+ {
9073
+ id: "013-extract-sessions-content-hash",
9074
+ up: `
9075
+ ALTER TABLE extract_sessions_seen ADD COLUMN content_hash TEXT DEFAULT NULL;
9076
+ `
9077
+ },
9078
+ {
9079
+ id: "014-recombine-hypotheses",
9080
+ up: `
9081
+ CREATE TABLE IF NOT EXISTS recombine_hypotheses (
9082
+ hypothesis_ref TEXT PRIMARY KEY,
9083
+ signature TEXT NOT NULL,
9084
+ member_key TEXT NOT NULL,
9085
+ consecutive_count INTEGER NOT NULL DEFAULT 0,
9086
+ first_seen_at TEXT NOT NULL,
9087
+ last_seen_at TEXT NOT NULL,
9088
+ last_run TEXT,
9089
+ promoted_at TEXT,
9090
+ metadata_json TEXT NOT NULL DEFAULT '{}'
9091
+ );
9092
+ CREATE INDEX IF NOT EXISTS idx_recombine_hypotheses_last_seen
9093
+ ON recombine_hypotheses(last_seen_at);
9094
+ `
9095
+ },
9096
+ {
9097
+ id: "015-asset-salience-encoding-source",
9098
+ up: `
9099
+ ALTER TABLE asset_salience ADD COLUMN encoding_source TEXT DEFAULT NULL;
9100
+ `
9101
+ },
9102
+ {
9103
+ id: "016-collapse-churn-detector",
9104
+ up: `
9105
+ CREATE TABLE IF NOT EXISTS canary_queries (
9106
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
9107
+ canary_set_id TEXT NOT NULL,
9108
+ anchor_ref TEXT NOT NULL,
9109
+ query TEXT NOT NULL,
9110
+ source TEXT NOT NULL DEFAULT 'auto',
9111
+ active INTEGER NOT NULL DEFAULT 1,
9112
+ created_at TEXT NOT NULL
9113
+ );
9114
+ CREATE INDEX IF NOT EXISTS idx_canary_queries_active
9115
+ ON canary_queries(active, canary_set_id);
9116
+
9117
+ CREATE TABLE IF NOT EXISTS improve_cycle_metrics (
9118
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
9119
+ run_id TEXT NOT NULL,
9120
+ ts TEXT NOT NULL,
9121
+ pass TEXT NOT NULL,
9122
+ canary_set_id TEXT NOT NULL,
9123
+ mean_recall REAL NOT NULL,
9124
+ mean_ndcg REAL NOT NULL,
9125
+ mean_mrr REAL NOT NULL,
9126
+ canary_ranks_json TEXT NOT NULL,
9127
+ store_total INTEGER NOT NULL,
9128
+ store_by_type_json TEXT NOT NULL,
9129
+ distinct_content_ratio REAL NOT NULL,
9130
+ mean_bigram_diversity REAL NOT NULL,
9131
+ over_generation_count INTEGER NOT NULL,
9132
+ accepted_actions INTEGER NOT NULL,
9133
+ merge_floor_violations INTEGER NOT NULL DEFAULT 0,
9134
+ alerts_json TEXT NOT NULL DEFAULT '[]'
9135
+ );
9136
+ CREATE INDEX IF NOT EXISTS idx_improve_cycle_metrics_ts
9137
+ ON improve_cycle_metrics(ts);
9138
+ `
8908
9139
  }
8909
9140
  ];
8910
9141
  function runMigrations2(db) {
8911
9142
  runMigrations(db, MIGRATIONS);
8912
9143
  }
9144
+
9145
+ // src/core/state-db.ts
9146
+ function getStateDbPath() {
9147
+ return path4.join(getDataDir(), "state.db");
9148
+ }
9149
+ function openStateDatabase(dbPath) {
9150
+ return openManagedDatabase({ path: dbPath ?? getStateDbPath(), init: runMigrations2 });
9151
+ }
9152
+
9153
+ // src/core/assert.ts
9154
+ function assertNever(x, context) {
9155
+ let serialized;
9156
+ try {
9157
+ serialized = JSON.stringify(x);
9158
+ } catch {
9159
+ serialized = String(x);
9160
+ }
9161
+ if (serialized === undefined) {
9162
+ serialized = String(x);
9163
+ }
9164
+ const where = context ? ` (${context})` : "";
9165
+ throw new Error(`Unexpected value reached assertNever${where}: ${serialized}`);
9166
+ }
9167
+
9168
+ // src/core/improve-types.ts
9169
+ function classifyImproveAction(mode) {
9170
+ switch (mode) {
9171
+ case "reflect":
9172
+ case "distill":
9173
+ case "memory-inference":
9174
+ case "graph-extraction":
9175
+ return "accepted";
9176
+ case "reflect-cooldown":
9177
+ case "reflect-skipped":
9178
+ case "distill-skipped":
9179
+ return "skipped";
9180
+ case "reflect-guard-rejected":
9181
+ return "rejected";
9182
+ case "reflect-failed":
9183
+ case "error":
9184
+ return "error";
9185
+ case "memory-prune":
9186
+ return "noop";
9187
+ default:
9188
+ return assertNever(mode);
9189
+ }
9190
+ }
9191
+
9192
+ // src/storage/repositories/improve-runs-repository.ts
8913
9193
  function computeImproveRunMetrics(result) {
8914
9194
  const plannedCount = Array.isArray(result.plannedRefs) ? result.plannedRefs.length : 0;
8915
9195
  const actions = Array.isArray(result.actions) ? result.actions : [];
8916
9196
  const actionsCount = actions.length;
8917
9197
  let acceptedCount = 0;
8918
9198
  let rejectedCount = 0;
9199
+ let skippedCount = 0;
8919
9200
  let autoAcceptedCount = 0;
8920
9201
  let errorCount = 0;
8921
9202
  for (const action of actions) {
@@ -8926,6 +9207,9 @@ function computeImproveRunMetrics(result) {
8926
9207
  case "rejected":
8927
9208
  rejectedCount++;
8928
9209
  break;
9210
+ case "skipped":
9211
+ skippedCount++;
9212
+ break;
8929
9213
  case "error":
8930
9214
  errorCount++;
8931
9215
  break;
@@ -8937,7 +9221,17 @@ function computeImproveRunMetrics(result) {
8937
9221
  autoAcceptedCount++;
8938
9222
  }
8939
9223
  autoAcceptedCount += result.gateAutoAcceptedCount ?? 0;
8940
- return { plannedCount, actionsCount, acceptedCount, rejectedCount, autoAcceptedCount, errorCount };
9224
+ const distillSkippedTotal = result.distillSkipped?.total ?? 0;
9225
+ skippedCount += distillSkippedTotal;
9226
+ return {
9227
+ plannedCount,
9228
+ actionsCount: actionsCount + distillSkippedTotal,
9229
+ acceptedCount,
9230
+ rejectedCount,
9231
+ skippedCount,
9232
+ autoAcceptedCount,
9233
+ errorCount
9234
+ };
8941
9235
  }
8942
9236
  function recordImproveRun(db, input) {
8943
9237
  const metricsObj = input.metrics ?? computeImproveRunMetrics(input.result);
@@ -8953,7 +9247,7 @@ function recordImproveRun(db, input) {
8953
9247
  function parseArgs(argv) {
8954
9248
  const stashFromEnv = process.env.AKM_STASH_DIR;
8955
9249
  const out = {
8956
- stashDir: stashFromEnv ?? path4.join(process.env.HOME ?? "", "akm"),
9250
+ stashDir: stashFromEnv ?? path5.join(process.env.HOME ?? "", "akm"),
8957
9251
  dryRun: false,
8958
9252
  archive: true
8959
9253
  };
@@ -9012,12 +9306,12 @@ function inferScope(envelope) {
9012
9306
  }
9013
9307
  async function main() {
9014
9308
  const args = parseArgs(process.argv.slice(2));
9015
- const runsRoot = path4.join(args.stashDir, ".akm", "runs");
9016
- if (!fs4.existsSync(runsRoot)) {
9309
+ const runsRoot = path5.join(args.stashDir, ".akm", "runs");
9310
+ if (!fs5.existsSync(runsRoot)) {
9017
9311
  console.log(`[import] no legacy runs directory at ${runsRoot}; nothing to do`);
9018
9312
  return;
9019
9313
  }
9020
- const entries = fs4.readdirSync(runsRoot, { withFileTypes: true }).filter((e) => e.isDirectory()).map((e) => e.name).sort();
9314
+ const entries = fs5.readdirSync(runsRoot, { withFileTypes: true }).filter((e) => e.isDirectory()).map((e) => e.name).sort();
9021
9315
  if (entries.length === 0) {
9022
9316
  console.log(`[import] ${runsRoot} is empty; nothing to do`);
9023
9317
  return;
@@ -9031,8 +9325,8 @@ async function main() {
9031
9325
  let skippedParseFailed = 0;
9032
9326
  let skippedNoTimestamp = 0;
9033
9327
  for (const id of entries) {
9034
- const resultPath = path4.join(runsRoot, id, "improve-result.json");
9035
- if (!fs4.existsSync(resultPath)) {
9328
+ const resultPath = path5.join(runsRoot, id, "improve-result.json");
9329
+ if (!fs5.existsSync(resultPath)) {
9036
9330
  skippedNoResult++;
9037
9331
  continue;
9038
9332
  }
@@ -9042,7 +9336,7 @@ async function main() {
9042
9336
  }
9043
9337
  let envelope;
9044
9338
  try {
9045
- envelope = JSON.parse(fs4.readFileSync(resultPath, "utf8"));
9339
+ envelope = JSON.parse(fs5.readFileSync(resultPath, "utf8"));
9046
9340
  } catch (err) {
9047
9341
  console.warn(`[import] skipping ${id}: parse failed (${err instanceof Error ? err.message : String(err)})`);
9048
9342
  skippedParseFailed++;
@@ -9107,8 +9401,8 @@ async function main() {
9107
9401
  return;
9108
9402
  }
9109
9403
  const ts = new Date().toISOString().replace(/[:.]/g, "-");
9110
- const archiveTarget = path4.join(args.stashDir, ".akm", `runs.archived-${ts}`);
9111
- fs4.renameSync(runsRoot, archiveTarget);
9404
+ const archiveTarget = path5.join(args.stashDir, ".akm", `runs.archived-${ts}`);
9405
+ fs5.renameSync(runsRoot, archiveTarget);
9112
9406
  console.log(`[import] archived ${runsRoot} \u2192 ${archiveTarget}`);
9113
9407
  }
9114
9408
  main().catch((err) => {