akm-cli 0.9.0-beta.3 → 0.9.0-beta.31

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 (107) hide show
  1. package/CHANGELOG.md +613 -0
  2. package/dist/assets/prompts/consolidate-system.md +23 -0
  3. package/dist/assets/prompts/contradiction-judge.md +33 -0
  4. package/dist/assets/prompts/distill-knowledge-system.md +22 -0
  5. package/dist/assets/prompts/distill-lesson-system.md +36 -0
  6. package/dist/assets/prompts/extract-session.md +5 -1
  7. package/dist/assets/prompts/graph-extract-system.md +1 -0
  8. package/dist/assets/prompts/memory-infer-system.md +1 -0
  9. package/dist/assets/prompts/memory-infer-user.md +5 -0
  10. package/dist/assets/prompts/metadata-enhance-system.md +1 -0
  11. package/dist/assets/prompts/procedural-system.md +44 -0
  12. package/dist/assets/prompts/recombine-system.md +40 -0
  13. package/dist/assets/prompts/staleness-detect-system.md +6 -0
  14. package/dist/assets/prompts/validate-summary-judge.md +1 -0
  15. package/dist/assets/templates/html/health.html +281 -111
  16. package/dist/cli.js +14 -3
  17. package/dist/commands/agent/contribute-cli.js +16 -3
  18. package/dist/commands/feedback-cli.js +15 -6
  19. package/dist/commands/graph/graph.js +75 -71
  20. package/dist/commands/health/checks.js +48 -0
  21. package/dist/commands/health/html-report.js +422 -80
  22. package/dist/commands/health.js +381 -9
  23. package/dist/commands/improve/calibration.js +161 -0
  24. package/dist/commands/improve/consolidate.js +634 -111
  25. package/dist/commands/improve/dedup.js +482 -0
  26. package/dist/commands/improve/distill.js +145 -69
  27. package/dist/commands/improve/encoding-salience.js +205 -0
  28. package/dist/commands/improve/extract-cli.js +115 -1
  29. package/dist/commands/improve/extract-prompt.js +33 -2
  30. package/dist/commands/improve/extract-watch.js +140 -0
  31. package/dist/commands/improve/extract.js +244 -35
  32. package/dist/commands/improve/feedback-valence.js +54 -0
  33. package/dist/commands/improve/homeostatic.js +467 -0
  34. package/dist/commands/improve/improve-auto-accept.js +113 -6
  35. package/dist/commands/improve/improve-profiles.js +12 -0
  36. package/dist/commands/improve/improve.js +1974 -614
  37. package/dist/commands/improve/memory/memory-contradiction-detect.js +23 -28
  38. package/dist/commands/improve/outcome-loop.js +256 -0
  39. package/dist/commands/improve/proactive-maintenance.js +87 -0
  40. package/dist/commands/improve/procedural.js +409 -0
  41. package/dist/commands/improve/recombine.js +593 -0
  42. package/dist/commands/improve/reflect.js +26 -1
  43. package/dist/commands/improve/related-sessions.js +120 -0
  44. package/dist/commands/improve/salience.js +386 -0
  45. package/dist/commands/improve/triage.js +95 -0
  46. package/dist/commands/lint/agent-linter.js +19 -24
  47. package/dist/commands/lint/base-linter.js +173 -60
  48. package/dist/commands/lint/command-linter.js +19 -24
  49. package/dist/commands/lint/env-key-rules.js +34 -1
  50. package/dist/commands/lint/fact-linter.js +39 -0
  51. package/dist/commands/lint/index.js +31 -13
  52. package/dist/commands/lint/memory-linter.js +1 -1
  53. package/dist/commands/lint/registry.js +7 -2
  54. package/dist/commands/lint/task-linter.js +3 -3
  55. package/dist/commands/lint/workflow-linter.js +26 -1
  56. package/dist/commands/proposal/proposal.js +5 -0
  57. package/dist/commands/proposal/validators/proposals.js +71 -54
  58. package/dist/commands/read/curate.js +344 -80
  59. package/dist/commands/read/search-cli.js +7 -0
  60. package/dist/commands/read/search.js +1 -0
  61. package/dist/commands/read/show.js +67 -2
  62. package/dist/commands/sources/installed-stashes.js +5 -1
  63. package/dist/commands/sources/stash-cli.js +10 -2
  64. package/dist/core/asset/asset-registry.js +2 -0
  65. package/dist/core/asset/asset-spec.js +14 -0
  66. package/dist/core/asset/frontmatter.js +166 -167
  67. package/dist/core/asset/markdown.js +8 -0
  68. package/dist/core/config/config-schema.js +259 -2
  69. package/dist/core/config/config.js +2 -2
  70. package/dist/core/logs-db.js +4 -3
  71. package/dist/core/paths.js +3 -0
  72. package/dist/core/state-db.js +649 -30
  73. package/dist/indexer/db/db.js +364 -38
  74. package/dist/indexer/db/graph-db.js +129 -86
  75. package/dist/indexer/ensure-index.js +152 -17
  76. package/dist/indexer/graph/graph-boost.js +51 -41
  77. package/dist/indexer/graph/graph-extraction.js +203 -3
  78. package/dist/indexer/index-writer-lock.js +99 -0
  79. package/dist/indexer/indexer.js +114 -111
  80. package/dist/indexer/passes/memory-inference.js +10 -3
  81. package/dist/indexer/passes/staleness-detect.js +2 -5
  82. package/dist/indexer/search/db-search.js +15 -4
  83. package/dist/indexer/search/ranking-contributors.js +22 -0
  84. package/dist/indexer/search/ranking.js +4 -0
  85. package/dist/indexer/walk/matchers.js +9 -0
  86. package/dist/integrations/agent/prompts.js +1 -0
  87. package/dist/integrations/harnesses/claude/session-log.js +11 -1
  88. package/dist/integrations/harnesses/opencode/session-log.js +9 -0
  89. package/dist/integrations/session-logs/index.js +16 -0
  90. package/dist/llm/client.js +23 -4
  91. package/dist/llm/embedder.js +27 -3
  92. package/dist/llm/embedders/local.js +66 -2
  93. package/dist/llm/graph-extract.js +2 -1
  94. package/dist/llm/memory-infer.js +4 -8
  95. package/dist/llm/metadata-enhance.js +9 -1
  96. package/dist/output/renderers.js +73 -1
  97. package/dist/output/shapes/curate.js +14 -2
  98. package/dist/output/text/helpers.js +9 -0
  99. package/dist/runtime.js +25 -1
  100. package/dist/scripts/migrate-storage.js +1242 -594
  101. package/dist/scripts/migrations/import-fs-improve-runs-to-db.js +473 -270
  102. package/dist/sources/providers/tar-utils.js +16 -8
  103. package/dist/storage/sqlite-pragmas.js +146 -0
  104. package/dist/workflows/db.js +3 -4
  105. package/dist/workflows/validate-summary.js +2 -7
  106. package/docs/data-and-telemetry.md +1 -0
  107. package/package.json +9 -6
@@ -34,9 +34,35 @@ var __commonJS = (cb, mod) => () => (mod || cb((mod = { exports: {} }).exports,
34
34
  var __esm = (fn, res) => () => (fn && (res = fn(fn = 0)), res);
35
35
  var __require = import.meta.require;
36
36
 
37
+ // src/core/warn.ts
38
+ import fs from "fs";
39
+ function appendToLogFile(level, args) {
40
+ if (!logFilePath)
41
+ return;
42
+ const ts = new Date().toISOString();
43
+ const msg = args.map((a) => typeof a === "string" ? a : JSON.stringify(a)).join(" ");
44
+ try {
45
+ fs.appendFileSync(logFilePath, `[${ts}] [${level}] ${msg}
46
+ `);
47
+ } catch (e) {
48
+ process.stderr.write(`[akm:warn] log-file write failed (${logFilePath}): ${e}
49
+ `);
50
+ process.stderr.write(`[${ts}] [${level}] ${msg}
51
+ `);
52
+ }
53
+ }
54
+ function warn(...args) {
55
+ appendToLogFile("WARN", args);
56
+ if (!quiet) {
57
+ console.warn(...args);
58
+ }
59
+ }
60
+ var quiet = false, logFilePath;
61
+ var init_warn = () => {};
62
+
37
63
  // node_modules/dotenv/lib/main.js
38
64
  var require_main = __commonJS((exports, module) => {
39
- var fs = __require("fs");
65
+ var fs2 = __require("fs");
40
66
  var path = __require("path");
41
67
  var os = __require("os");
42
68
  var crypto = __require("crypto");
@@ -170,7 +196,7 @@ var require_main = __commonJS((exports, module) => {
170
196
  if (options && options.path && options.path.length > 0) {
171
197
  if (Array.isArray(options.path)) {
172
198
  for (const filepath of options.path) {
173
- if (fs.existsSync(filepath)) {
199
+ if (fs2.existsSync(filepath)) {
174
200
  possibleVaultPath = filepath.endsWith(".vault") ? filepath : `${filepath}.vault`;
175
201
  }
176
202
  }
@@ -180,7 +206,7 @@ var require_main = __commonJS((exports, module) => {
180
206
  } else {
181
207
  possibleVaultPath = path.resolve(process.cwd(), ".env.vault");
182
208
  }
183
- if (fs.existsSync(possibleVaultPath)) {
209
+ if (fs2.existsSync(possibleVaultPath)) {
184
210
  return possibleVaultPath;
185
211
  }
186
212
  return null;
@@ -190,8 +216,8 @@ var require_main = __commonJS((exports, module) => {
190
216
  }
191
217
  function _configVault(options) {
192
218
  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) {
219
+ const quiet2 = parseBoolean(process.env.DOTENV_CONFIG_QUIET || options && options.quiet);
220
+ if (debug || !quiet2) {
195
221
  _log("loading env from encrypted .env.vault");
196
222
  }
197
223
  const parsed = DotenvModule._parseVault(options);
@@ -210,7 +236,7 @@ var require_main = __commonJS((exports, module) => {
210
236
  processEnv = options.processEnv;
211
237
  }
212
238
  let debug = parseBoolean(processEnv.DOTENV_CONFIG_DEBUG || options && options.debug);
213
- let quiet = parseBoolean(processEnv.DOTENV_CONFIG_QUIET || options && options.quiet);
239
+ let quiet2 = parseBoolean(processEnv.DOTENV_CONFIG_QUIET || options && options.quiet);
214
240
  if (options && options.encoding) {
215
241
  encoding = options.encoding;
216
242
  } else {
@@ -233,7 +259,7 @@ var require_main = __commonJS((exports, module) => {
233
259
  const parsedAll = {};
234
260
  for (const path2 of optionPaths) {
235
261
  try {
236
- const parsed = DotenvModule.parse(fs.readFileSync(path2, { encoding }));
262
+ const parsed = DotenvModule.parse(fs2.readFileSync(path2, { encoding }));
237
263
  DotenvModule.populate(parsedAll, parsed, options);
238
264
  } catch (e) {
239
265
  if (debug) {
@@ -244,8 +270,8 @@ var require_main = __commonJS((exports, module) => {
244
270
  }
245
271
  const populated = DotenvModule.populate(processEnv, parsedAll, options);
246
272
  debug = parseBoolean(processEnv.DOTENV_CONFIG_DEBUG || debug);
247
- quiet = parseBoolean(processEnv.DOTENV_CONFIG_QUIET || quiet);
248
- if (debug || !quiet) {
273
+ quiet2 = parseBoolean(processEnv.DOTENV_CONFIG_QUIET || quiet2);
274
+ if (debug || !quiet2) {
249
275
  const keysCount = Object.keys(populated).length;
250
276
  const shortPaths = [];
251
277
  for (const filePath of optionPaths) {
@@ -412,7 +438,7 @@ var init_errors = __esm(() => {
412
438
  });
413
439
 
414
440
  // src/commands/env/env.ts
415
- import fs from "fs";
441
+ import fs2 from "fs";
416
442
  function scanKeys(text) {
417
443
  const keys = [];
418
444
  const seen = new Set;
@@ -439,9 +465,9 @@ function scanComments(text) {
439
465
  return comments;
440
466
  }
441
467
  function listKeys(envPath) {
442
- if (!fs.existsSync(envPath))
468
+ if (!fs2.existsSync(envPath))
443
469
  return { keys: [], comments: [] };
444
- const text = fs.readFileSync(envPath, "utf8");
470
+ const text = fs2.readFileSync(envPath, "utf8");
445
471
  return { keys: scanKeys(text), comments: scanComments(text) };
446
472
  }
447
473
  var import_dotenv, ASSIGN_RE;
@@ -452,251 +478,6 @@ var init_env = __esm(() => {
452
478
  ASSIGN_RE = /^\s*(?:export\s+)?([A-Za-z_][A-Za-z0-9_]*)\s*=/;
453
479
  });
454
480
 
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
481
  // node_modules/yaml/dist/nodes/identity.js
701
482
  var require_identity = __commonJS((exports) => {
702
483
  var ALIAS = Symbol.for("yaml.alias");
@@ -7667,6 +7448,199 @@ var init_dist = __esm(() => {
7667
7448
  $visitAsync = visit.visitAsync;
7668
7449
  });
7669
7450
 
7451
+ // src/core/asset/frontmatter.ts
7452
+ function parseFrontmatter(raw) {
7453
+ const parsedBlock = parseFrontmatterBlock(raw);
7454
+ if (!parsedBlock) {
7455
+ return { data: {}, content: raw, frontmatter: null, bodyStartLine: 1 };
7456
+ }
7457
+ let data = {};
7458
+ if (parsedBlock.frontmatter.trim()) {
7459
+ try {
7460
+ const parsed = $parse(parsedBlock.frontmatter);
7461
+ if (parsed !== null && typeof parsed === "object" && !Array.isArray(parsed)) {
7462
+ data = normalizeYamlValues(parsed);
7463
+ }
7464
+ } catch {
7465
+ data = parseFrontmatterLenient(parsedBlock.frontmatter);
7466
+ }
7467
+ }
7468
+ return {
7469
+ data,
7470
+ content: parsedBlock.content,
7471
+ frontmatter: parsedBlock.frontmatter,
7472
+ bodyStartLine: parsedBlock.bodyStartLine
7473
+ };
7474
+ }
7475
+ function normalizeYamlValues(value) {
7476
+ if (value instanceof Date) {
7477
+ const y = value.getUTCFullYear();
7478
+ const m = String(value.getUTCMonth() + 1).padStart(2, "0");
7479
+ const d = String(value.getUTCDate()).padStart(2, "0");
7480
+ return `${y}-${m}-${d}`;
7481
+ }
7482
+ if (value === null)
7483
+ return "";
7484
+ if (Array.isArray(value))
7485
+ return value.map(normalizeYamlValues);
7486
+ if (typeof value === "object") {
7487
+ return Object.fromEntries(Object.entries(value).map(([k, v]) => [k, normalizeYamlValues(v)]));
7488
+ }
7489
+ return value;
7490
+ }
7491
+ function parseFrontmatterLenient(frontmatter) {
7492
+ const data = {};
7493
+ for (const line of frontmatter.split(/\r?\n/)) {
7494
+ const m = line.match(/^([\w][\w-]*):\s*(.*)$/);
7495
+ if (!m)
7496
+ continue;
7497
+ const key = m[1];
7498
+ const rawValue = (m[2] ?? "").trim();
7499
+ try {
7500
+ const singleEntry = $parse(`k: ${rawValue}`);
7501
+ if (singleEntry !== null && typeof singleEntry === "object" && !Array.isArray(singleEntry)) {
7502
+ const v = singleEntry.k;
7503
+ data[key] = v === null || v === undefined ? "" : v;
7504
+ } else {
7505
+ data[key] = rawValue;
7506
+ }
7507
+ } catch {
7508
+ data[key] = rawValue;
7509
+ }
7510
+ }
7511
+ return data;
7512
+ }
7513
+ function parseFrontmatterBlock(raw) {
7514
+ const match = raw.match(/^---\r?\n([\s\S]*?)\r?\n---(?:\r\n|\r|\n|$)([\s\S]*)$/);
7515
+ if (match) {
7516
+ const frontmatter = match[1].replace(/\r/g, "");
7517
+ const content = match[2];
7518
+ return {
7519
+ frontmatter,
7520
+ content,
7521
+ bodyStartLine: countLines(raw.slice(0, match[0].length - match[2].length)) + 1
7522
+ };
7523
+ }
7524
+ const emptyMatch = raw.match(/^---\r?\n---(?:\r\n|\r|\n)([\s\S]*)$/);
7525
+ if (emptyMatch) {
7526
+ return { frontmatter: "", content: emptyMatch[1], bodyStartLine: 3 };
7527
+ }
7528
+ return null;
7529
+ }
7530
+ function countLines(text) {
7531
+ if (text.length === 0)
7532
+ return 0;
7533
+ return text.split(/\r?\n/).length - 1;
7534
+ }
7535
+ var init_frontmatter = __esm(() => {
7536
+ init_dist();
7537
+ });
7538
+
7539
+ // src/core/asset/markdown.ts
7540
+ function parseMarkdownToc(content) {
7541
+ const lines = content.split(/\r?\n/);
7542
+ const headings = [];
7543
+ const parsed = parseFrontmatter(content);
7544
+ const start = parsed.frontmatter ? parsed.bodyStartLine - 1 : 0;
7545
+ let inFence = false;
7546
+ for (let i = start;i < lines.length; i++) {
7547
+ if (/^\s*(`{3,}|~{3,})/.test(lines[i])) {
7548
+ inFence = !inFence;
7549
+ continue;
7550
+ }
7551
+ if (inFence)
7552
+ continue;
7553
+ const match = lines[i].match(/^(#{1,6})\s+(.+)$/);
7554
+ if (match) {
7555
+ headings.push({
7556
+ level: match[1].length,
7557
+ text: match[2].replace(/\s+#+\s*$/, "").trim(),
7558
+ line: i + 1
7559
+ });
7560
+ }
7561
+ }
7562
+ return { headings, totalLines: lines.length };
7563
+ }
7564
+ var init_markdown = __esm(() => {
7565
+ init_frontmatter();
7566
+ });
7567
+
7568
+ // src/indexer/walk/file-context.ts
7569
+ var renderers;
7570
+ var init_file_context = __esm(() => {
7571
+ init_frontmatter();
7572
+ init_common();
7573
+ renderers = new Map;
7574
+ });
7575
+
7576
+ // src/indexer/passes/metadata-contributors.ts
7577
+ function registerMetadataContributor(contributor) {
7578
+ contributors.push(contributor);
7579
+ }
7580
+ var contributors;
7581
+ var init_metadata_contributors = __esm(() => {
7582
+ contributors = [];
7583
+ });
7584
+
7585
+ // src/indexer/passes/metadata.ts
7586
+ import fs3 from "fs";
7587
+ function extractDescriptionFromComments(filePath) {
7588
+ let content;
7589
+ try {
7590
+ content = fs3.readFileSync(filePath, "utf8");
7591
+ } catch {
7592
+ return null;
7593
+ }
7594
+ const lines = content.split(/\r?\n/).slice(0, 50);
7595
+ const blockStart = lines.findIndex((l) => /^\s*\/\*\*/.test(l));
7596
+ if (blockStart >= 0) {
7597
+ const desc = [];
7598
+ for (let i = blockStart;i < lines.length; i++) {
7599
+ const line = lines[i];
7600
+ if (i > blockStart && /\*\//.test(line))
7601
+ break;
7602
+ const cleaned = line.replace(/^\s*\/?\*\*?\s?/, "").replace(/\*\/\s*$/, "").trim();
7603
+ if (cleaned)
7604
+ desc.push(cleaned);
7605
+ }
7606
+ if (desc.length > 0)
7607
+ return desc.join(" ");
7608
+ }
7609
+ let start = 0;
7610
+ if (lines[0]?.startsWith("#!"))
7611
+ start = 1;
7612
+ const hashLines = [];
7613
+ for (let i = start;i < lines.length; i++) {
7614
+ const line = lines[i].trim();
7615
+ if (line.startsWith("#") && !line.startsWith("#!")) {
7616
+ hashLines.push(line.replace(/^#+\s*/, "").trim());
7617
+ } else if (line === "") {} else {
7618
+ break;
7619
+ }
7620
+ }
7621
+ if (hashLines.length > 0)
7622
+ return hashLines.join(" ");
7623
+ return null;
7624
+ }
7625
+ var KNOWN_QUALITY_VALUES, warnedUnknownQualityValues, WIKI_INFRA_FILES;
7626
+ var init_metadata = __esm(() => {
7627
+ init_asset_spec();
7628
+ init_frontmatter();
7629
+ init_common();
7630
+ init_warn();
7631
+ init_file_context();
7632
+ init_metadata_contributors();
7633
+ KNOWN_QUALITY_VALUES = new Set(["generated", "curated", "enriched", "proposed"]);
7634
+ warnedUnknownQualityValues = new Set;
7635
+ WIKI_INFRA_FILES = new Set(["schema.md", "index.md", "log.md"]);
7636
+ });
7637
+
7638
+ // src/core/asset/asset-ref.ts
7639
+ var init_asset_ref = __esm(() => {
7640
+ init_common();
7641
+ init_errors();
7642
+ });
7643
+
7670
7644
  // src/workflows/schema.ts
7671
7645
  var WORKFLOW_SCHEMA_VERSION = 1;
7672
7646
 
@@ -8061,6 +8035,7 @@ function sortErrors(errors2) {
8061
8035
  var WORKFLOW_TITLE_PREFIX = "Workflow:", STEP_PREFIX = "Step:", STEP_ID_LINE, BULLET_LINE, SUBSECTION_INSTRUCTIONS = "Instructions", SUBSECTION_COMPLETION_CRITERIA = "Completion Criteria";
8062
8036
  var init_parser = __esm(() => {
8063
8037
  init_dist();
8038
+ init_frontmatter();
8064
8039
  init_markdown();
8065
8040
  init_validator();
8066
8041
  STEP_ID_LINE = /^Step ID:\s+(.+?)\s*$/;
@@ -8151,6 +8126,25 @@ function applyTocMetadata(entry, ctx) {
8151
8126
  entry.toc = toc.headings;
8152
8127
  } catch {}
8153
8128
  }
8129
+ function applyFactMetadata(entry, ctx) {
8130
+ try {
8131
+ const fm = applyFrontmatterDescriptionAndTags(entry, ctx);
8132
+ const tags = new Set([...entry.tags ?? [], "fact"]);
8133
+ const hints = new Set(entry.searchHints ?? []);
8134
+ const category = asNonEmptyString(fm.category);
8135
+ if (category) {
8136
+ tags.add(category);
8137
+ hints.add(`category:${category}`);
8138
+ }
8139
+ if (fm.pinned === true) {
8140
+ tags.add("pinned");
8141
+ hints.add("pinned");
8142
+ }
8143
+ entry.tags = Array.from(tags).filter(Boolean);
8144
+ if (hints.size > 0)
8145
+ entry.searchHints = Array.from(hints).filter(Boolean);
8146
+ } catch {}
8147
+ }
8154
8148
  function applyFrontmatterDescriptionAndTags(entry, ctx) {
8155
8149
  const parsed = parseFrontmatter(ctx.content());
8156
8150
  const fm = parsed.data;
@@ -8250,6 +8244,7 @@ function applyTaskMetadata(entry, ctx) {
8250
8244
  }
8251
8245
  var init_renderers = __esm(() => {
8252
8246
  init_env();
8247
+ init_frontmatter();
8253
8248
  init_markdown();
8254
8249
  init_common();
8255
8250
  init_metadata();
@@ -8296,6 +8291,11 @@ var init_renderers = __esm(() => {
8296
8291
  appliesTo: ({ rendererName }) => rendererName === "session-md",
8297
8292
  contribute: (entry, ctx) => applySessionMetadata(entry, ctx.renderContext)
8298
8293
  });
8294
+ registerMetadataContributor({
8295
+ name: "fact-md-metadata",
8296
+ appliesTo: ({ rendererName }) => rendererName === "fact-md",
8297
+ contribute: (entry, ctx) => applyFactMetadata(entry, ctx.renderContext)
8298
+ });
8299
8299
  });
8300
8300
 
8301
8301
  // src/core/asset/asset-registry.ts
@@ -8430,6 +8430,12 @@ var init_asset_spec = __esm(() => {
8430
8430
  ...markdownSpec,
8431
8431
  rendererName: "session-md",
8432
8432
  actionBuilder: (ref) => `akm show ${ref} -> read the session summary; follow the \`access\` frontmatter to open the raw log at \`log_path\``
8433
+ },
8434
+ fact: {
8435
+ stashDir: "facts",
8436
+ ...markdownSpec,
8437
+ rendererName: "fact-md",
8438
+ actionBuilder: (ref) => `akm show ${ref} -> read the stash fact and apply it as durable context`
8433
8439
  }
8434
8440
  };
8435
8441
  TYPE_DIRS = Object.fromEntries(Object.entries(ASSET_SPECS_INTERNAL).map(([type, spec]) => [type, spec.stashDir]));
@@ -8498,11 +8504,11 @@ var init_paths = __esm(() => {
8498
8504
  });
8499
8505
 
8500
8506
  // scripts/migrations/import-fs-improve-runs-to-db.ts
8501
- import fs4 from "fs";
8507
+ import fs5 from "fs";
8502
8508
  import path4 from "path";
8503
8509
 
8504
8510
  // src/core/state-db.ts
8505
- import fs3 from "fs";
8511
+ import fs4 from "fs";
8506
8512
  import path3 from "path";
8507
8513
 
8508
8514
  // src/storage/database.ts
@@ -8578,6 +8584,94 @@ function runMigrations(db, migrations, opts) {
8578
8584
  }
8579
8585
  }
8580
8586
 
8587
+ // src/storage/sqlite-pragmas.ts
8588
+ init_warn();
8589
+
8590
+ // src/runtime.ts
8591
+ import { createWriteStream, statfsSync } from "fs";
8592
+ import { createRequire as createRequire2 } from "module";
8593
+ var isBun2 = !!process.versions?.bun;
8594
+ var nodeRequire2 = createRequire2(import.meta.url);
8595
+ function statfsType(path) {
8596
+ try {
8597
+ return statfsSync(path).type;
8598
+ } catch {
8599
+ return;
8600
+ }
8601
+ }
8602
+ var mainPath = isBun2 ? bunGlobal().main : process.argv[1];
8603
+ function bunGlobal() {
8604
+ return globalThis.Bun;
8605
+ }
8606
+
8607
+ // src/storage/sqlite-pragmas.ts
8608
+ var VALID_MODES = new Set(["WAL", "DELETE", "TRUNCATE"]);
8609
+ var warnedInvalid = false;
8610
+ var warnedNetworkFallback = false;
8611
+ function resolveJournalMode(raw) {
8612
+ if (raw === undefined)
8613
+ return "WAL";
8614
+ const normalized = raw.trim().toUpperCase();
8615
+ if (normalized === "")
8616
+ return "WAL";
8617
+ if (VALID_MODES.has(normalized)) {
8618
+ return normalized;
8619
+ }
8620
+ warnInvalidJournalModeOnce(raw);
8621
+ return "WAL";
8622
+ }
8623
+ function resolveConfiguredJournalMode() {
8624
+ return resolveJournalMode(process.env.AKM_SQLITE_JOURNAL_MODE);
8625
+ }
8626
+ function warnInvalidJournalModeOnce(raw) {
8627
+ if (warnedInvalid)
8628
+ return;
8629
+ warnedInvalid = true;
8630
+ warn(`[akm] invalid AKM_SQLITE_JOURNAL_MODE=${JSON.stringify(raw)} \u2014 using WAL (valid: WAL, DELETE, TRUNCATE)`);
8631
+ }
8632
+ var FS_MAGIC_NFS = 26985;
8633
+ var FS_MAGIC_SMB = 20859;
8634
+ var FS_MAGIC_CIFS = 4283649346;
8635
+ var FS_MAGIC_SMB2 = 4266872130;
8636
+ var FS_MAGIC_FUSE = 1702057286;
8637
+ var NETWORK_FS_MAGICS = new Set([
8638
+ FS_MAGIC_NFS,
8639
+ FS_MAGIC_SMB,
8640
+ FS_MAGIC_CIFS,
8641
+ FS_MAGIC_SMB2,
8642
+ FS_MAGIC_FUSE
8643
+ ]);
8644
+ function isNetworkFilesystem(fsType) {
8645
+ if (fsType === undefined)
8646
+ return false;
8647
+ return NETWORK_FS_MAGICS.has(fsType);
8648
+ }
8649
+ function applyStandardPragmas(db, opts = {}) {
8650
+ let mode = resolveConfiguredJournalMode();
8651
+ if (mode === "WAL" && opts.dataDir) {
8652
+ const probe = opts.fsTypeProbe ?? statfsType;
8653
+ if (isNetworkFilesystem(probe(opts.dataDir))) {
8654
+ mode = "DELETE";
8655
+ warnNetworkFallbackOnce(opts.dataDir);
8656
+ }
8657
+ }
8658
+ db.exec("PRAGMA busy_timeout = 30000");
8659
+ db.exec(`PRAGMA journal_mode = ${mode}`);
8660
+ if (opts.foreignKeys !== false) {
8661
+ db.exec("PRAGMA foreign_keys = ON");
8662
+ }
8663
+ if (mode !== "WAL") {
8664
+ db.exec("PRAGMA synchronous = FULL");
8665
+ }
8666
+ return mode;
8667
+ }
8668
+ function warnNetworkFallbackOnce(dataDir) {
8669
+ if (warnedNetworkFallback)
8670
+ return;
8671
+ warnedNetworkFallback = true;
8672
+ warn(`[akm] network filesystem detected at ${dataDir} \u2014 WAL unsupported, using DELETE journal mode`);
8673
+ }
8674
+
8581
8675
  // src/core/assert.ts
8582
8676
  function assertNever(x, context) {
8583
8677
  let serialized;
@@ -8625,13 +8719,11 @@ function getStateDbPath() {
8625
8719
  function openStateDatabase(dbPath) {
8626
8720
  const resolvedPath = dbPath ?? getStateDbPath();
8627
8721
  const dir = path3.dirname(resolvedPath);
8628
- if (!fs3.existsSync(dir)) {
8629
- fs3.mkdirSync(dir, { recursive: true });
8722
+ if (!fs4.existsSync(dir)) {
8723
+ fs4.mkdirSync(dir, { recursive: true });
8630
8724
  }
8631
8725
  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");
8726
+ applyStandardPragmas(db, { dataDir: dir });
8635
8727
  runMigrations2(db);
8636
8728
  return db;
8637
8729
  }
@@ -8708,7 +8800,7 @@ var MIGRATIONS = [
8708
8800
  -- metadata_json TEXT \u2014 JSON object for future proposal fields.
8709
8801
  -- Current fields stored here: sourceRun,
8710
8802
  -- review, confidence, gateDecision (#577),
8711
- -- backupContent.
8803
+ -- backupContent, eligibilitySource.
8712
8804
  --
8713
8805
  -- ADD COLUMN extension points (future migrations):
8714
8806
  -- ALTER TABLE proposals ADD COLUMN source_run TEXT DEFAULT NULL;
@@ -8905,6 +8997,117 @@ var MIGRATIONS = [
8905
8997
  imported_count INTEGER NOT NULL DEFAULT 0
8906
8998
  );
8907
8999
  `
9000
+ },
9001
+ {
9002
+ id: "006-proposals-pending-ref-source",
9003
+ up: `
9004
+ CREATE INDEX IF NOT EXISTS idx_proposals_stash_status_ref_source
9005
+ ON proposals(stash_dir, status, ref, source);
9006
+ `
9007
+ },
9008
+ {
9009
+ id: "007-consolidation-judged",
9010
+ up: `
9011
+ CREATE TABLE IF NOT EXISTS consolidation_judged (
9012
+ entry_key TEXT PRIMARY KEY,
9013
+ content_hash TEXT NOT NULL,
9014
+ judged_at TEXT NOT NULL,
9015
+ outcome TEXT NOT NULL
9016
+ );
9017
+ `
9018
+ },
9019
+ {
9020
+ id: "008-body-embeddings",
9021
+ up: `
9022
+ CREATE TABLE IF NOT EXISTS body_embeddings (
9023
+ content_hash TEXT PRIMARY KEY,
9024
+ embedding BLOB NOT NULL,
9025
+ model_id TEXT NOT NULL,
9026
+ created_at INTEGER NOT NULL
9027
+ );
9028
+ `
9029
+ },
9030
+ {
9031
+ id: "009-asset-salience",
9032
+ up: `
9033
+ CREATE TABLE IF NOT EXISTS asset_salience (
9034
+ asset_ref TEXT PRIMARY KEY,
9035
+ encoding_salience REAL NOT NULL DEFAULT 0.5,
9036
+ outcome_salience REAL NOT NULL DEFAULT 0.0,
9037
+ retrieval_salience REAL NOT NULL DEFAULT 0.0,
9038
+ rank_score REAL NOT NULL DEFAULT 0.0,
9039
+ consecutive_no_ops INTEGER NOT NULL DEFAULT 0,
9040
+ updated_at INTEGER NOT NULL DEFAULT 0
9041
+ );
9042
+
9043
+ -- Hot path: sort / filter by rank_score for selector queries.
9044
+ CREATE INDEX IF NOT EXISTS idx_asset_salience_rank
9045
+ ON asset_salience(rank_score DESC);
9046
+ `
9047
+ },
9048
+ {
9049
+ id: "010-asset-outcome",
9050
+ up: `
9051
+ CREATE TABLE IF NOT EXISTS asset_outcome (
9052
+ asset_ref TEXT PRIMARY KEY,
9053
+ last_retrieved_at INTEGER NOT NULL DEFAULT 0,
9054
+ retrieval_count INTEGER NOT NULL DEFAULT 0,
9055
+ expected_retrieval_rate REAL NOT NULL DEFAULT 0.0,
9056
+ negative_feedback_count INTEGER NOT NULL DEFAULT 0,
9057
+ accepted_change_count INTEGER NOT NULL DEFAULT 0,
9058
+ review_pressure INTEGER NOT NULL DEFAULT 0,
9059
+ outcome_score REAL NOT NULL DEFAULT 0.0,
9060
+ updated_at INTEGER NOT NULL DEFAULT 0
9061
+ );
9062
+
9063
+ -- Hot path: sort assets by review_pressure DESC for #613 admission.
9064
+ CREATE INDEX IF NOT EXISTS idx_asset_outcome_review_pressure
9065
+ ON asset_outcome(review_pressure DESC);
9066
+
9067
+ -- Secondary: sort by outcome_score DESC for outcomeSalience reads.
9068
+ CREATE INDEX IF NOT EXISTS idx_asset_outcome_score
9069
+ ON asset_outcome(outcome_score DESC);
9070
+ `
9071
+ },
9072
+ {
9073
+ id: "011-asset-salience-homeostatic-demoted-at",
9074
+ up: `
9075
+ ALTER TABLE asset_salience ADD COLUMN homeostatic_demoted_at INTEGER DEFAULT NULL;
9076
+ `
9077
+ },
9078
+ {
9079
+ id: "012-improve-gate-thresholds",
9080
+ up: `
9081
+ CREATE TABLE IF NOT EXISTS improve_gate_thresholds (
9082
+ phase TEXT NOT NULL PRIMARY KEY,
9083
+ threshold INTEGER NOT NULL,
9084
+ updated_at INTEGER NOT NULL
9085
+ );
9086
+ `
9087
+ },
9088
+ {
9089
+ id: "013-extract-sessions-content-hash",
9090
+ up: `
9091
+ ALTER TABLE extract_sessions_seen ADD COLUMN content_hash TEXT DEFAULT NULL;
9092
+ `
9093
+ },
9094
+ {
9095
+ id: "014-recombine-hypotheses",
9096
+ up: `
9097
+ CREATE TABLE IF NOT EXISTS recombine_hypotheses (
9098
+ hypothesis_ref TEXT PRIMARY KEY,
9099
+ signature TEXT NOT NULL,
9100
+ member_key TEXT NOT NULL,
9101
+ consecutive_count INTEGER NOT NULL DEFAULT 0,
9102
+ first_seen_at TEXT NOT NULL,
9103
+ last_seen_at TEXT NOT NULL,
9104
+ last_run TEXT,
9105
+ promoted_at TEXT,
9106
+ metadata_json TEXT NOT NULL DEFAULT '{}'
9107
+ );
9108
+ CREATE INDEX IF NOT EXISTS idx_recombine_hypotheses_last_seen
9109
+ ON recombine_hypotheses(last_seen_at);
9110
+ `
8908
9111
  }
8909
9112
  ];
8910
9113
  function runMigrations2(db) {
@@ -9013,11 +9216,11 @@ function inferScope(envelope) {
9013
9216
  async function main() {
9014
9217
  const args = parseArgs(process.argv.slice(2));
9015
9218
  const runsRoot = path4.join(args.stashDir, ".akm", "runs");
9016
- if (!fs4.existsSync(runsRoot)) {
9219
+ if (!fs5.existsSync(runsRoot)) {
9017
9220
  console.log(`[import] no legacy runs directory at ${runsRoot}; nothing to do`);
9018
9221
  return;
9019
9222
  }
9020
- const entries = fs4.readdirSync(runsRoot, { withFileTypes: true }).filter((e) => e.isDirectory()).map((e) => e.name).sort();
9223
+ const entries = fs5.readdirSync(runsRoot, { withFileTypes: true }).filter((e) => e.isDirectory()).map((e) => e.name).sort();
9021
9224
  if (entries.length === 0) {
9022
9225
  console.log(`[import] ${runsRoot} is empty; nothing to do`);
9023
9226
  return;
@@ -9032,7 +9235,7 @@ async function main() {
9032
9235
  let skippedNoTimestamp = 0;
9033
9236
  for (const id of entries) {
9034
9237
  const resultPath = path4.join(runsRoot, id, "improve-result.json");
9035
- if (!fs4.existsSync(resultPath)) {
9238
+ if (!fs5.existsSync(resultPath)) {
9036
9239
  skippedNoResult++;
9037
9240
  continue;
9038
9241
  }
@@ -9042,7 +9245,7 @@ async function main() {
9042
9245
  }
9043
9246
  let envelope;
9044
9247
  try {
9045
- envelope = JSON.parse(fs4.readFileSync(resultPath, "utf8"));
9248
+ envelope = JSON.parse(fs5.readFileSync(resultPath, "utf8"));
9046
9249
  } catch (err) {
9047
9250
  console.warn(`[import] skipping ${id}: parse failed (${err instanceof Error ? err.message : String(err)})`);
9048
9251
  skippedParseFailed++;
@@ -9108,7 +9311,7 @@ async function main() {
9108
9311
  }
9109
9312
  const ts = new Date().toISOString().replace(/[:.]/g, "-");
9110
9313
  const archiveTarget = path4.join(args.stashDir, ".akm", `runs.archived-${ts}`);
9111
- fs4.renameSync(runsRoot, archiveTarget);
9314
+ fs5.renameSync(runsRoot, archiveTarget);
9112
9315
  console.log(`[import] archived ${runsRoot} \u2192 ${archiveTarget}`);
9113
9316
  }
9114
9317
  main().catch((err) => {