engrm 0.4.9 → 0.4.11

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.
package/dist/cli.js CHANGED
@@ -607,9 +607,59 @@ function isVecExtensionLoaded(db) {
607
607
  return false;
608
608
  }
609
609
  }
610
+ function tableExists(db, name) {
611
+ const row = db.query(`SELECT name FROM sqlite_master WHERE type IN ('table', 'view') AND name = ?`).get(name);
612
+ return Boolean(row?.name);
613
+ }
614
+ function columnExists(db, table, column) {
615
+ if (!tableExists(db, table))
616
+ return false;
617
+ const rows = db.query(`PRAGMA table_info(${table})`).all();
618
+ return rows.some((row) => row.name === column);
619
+ }
620
+ function ensureLegacyBaseTables(db) {
621
+ db.exec(`
622
+ CREATE TABLE IF NOT EXISTS sync_state (
623
+ key TEXT PRIMARY KEY,
624
+ value TEXT NOT NULL
625
+ );
626
+ `);
627
+ }
628
+ function inferLegacySchemaVersion(db) {
629
+ const hasProjects = tableExists(db, "projects");
630
+ const hasObservations = tableExists(db, "observations");
631
+ const hasSessions = tableExists(db, "sessions");
632
+ if (!(hasProjects && hasObservations && hasSessions))
633
+ return 0;
634
+ ensureLegacyBaseTables(db);
635
+ let version = 1;
636
+ if (columnExists(db, "observations", "superseded_by"))
637
+ version = Math.max(version, 2);
638
+ if (columnExists(db, "observations", "remote_source_id"))
639
+ version = Math.max(version, 3);
640
+ if (tableExists(db, "vec_observations"))
641
+ version = Math.max(version, 4);
642
+ const hasSessionMetrics = columnExists(db, "sessions", "files_touched_count") && columnExists(db, "sessions", "searches_performed") && columnExists(db, "sessions", "tool_calls_count");
643
+ if (hasSessionMetrics || tableExists(db, "security_findings"))
644
+ version = Math.max(version, 5);
645
+ if (columnExists(db, "sessions", "risk_score"))
646
+ version = Math.max(version, 6);
647
+ if (tableExists(db, "packs_installed"))
648
+ version = Math.max(version, 7);
649
+ if (tableExists(db, "user_prompts"))
650
+ version = Math.max(version, 9);
651
+ if (tableExists(db, "tool_events"))
652
+ version = Math.max(version, 10);
653
+ return version;
654
+ }
610
655
  function runMigrations(db) {
611
656
  const currentVersion = db.query("PRAGMA user_version").get();
612
657
  let version = currentVersion.user_version;
658
+ const inferred = inferLegacySchemaVersion(db);
659
+ if (inferred > version) {
660
+ db.exec(`PRAGMA user_version = ${inferred}`);
661
+ version = inferred;
662
+ }
613
663
  for (const migration of MIGRATIONS) {
614
664
  if (migration.version <= version)
615
665
  continue;
@@ -689,6 +739,11 @@ import { createHash as createHash2 } from "node:crypto";
689
739
  var IS_BUN = typeof globalThis.Bun !== "undefined";
690
740
  function openDatabase(dbPath) {
691
741
  if (IS_BUN) {
742
+ if (process.platform === "darwin") {
743
+ try {
744
+ return openNodeDatabase(dbPath);
745
+ } catch {}
746
+ }
692
747
  return openBunDatabase(dbPath);
693
748
  }
694
749
  return openNodeDatabase(dbPath);
@@ -1437,9 +1437,59 @@ function isVecExtensionLoaded(db) {
1437
1437
  return false;
1438
1438
  }
1439
1439
  }
1440
+ function tableExists(db, name) {
1441
+ const row = db.query(`SELECT name FROM sqlite_master WHERE type IN ('table', 'view') AND name = ?`).get(name);
1442
+ return Boolean(row?.name);
1443
+ }
1444
+ function columnExists(db, table, column) {
1445
+ if (!tableExists(db, table))
1446
+ return false;
1447
+ const rows = db.query(`PRAGMA table_info(${table})`).all();
1448
+ return rows.some((row) => row.name === column);
1449
+ }
1450
+ function ensureLegacyBaseTables(db) {
1451
+ db.exec(`
1452
+ CREATE TABLE IF NOT EXISTS sync_state (
1453
+ key TEXT PRIMARY KEY,
1454
+ value TEXT NOT NULL
1455
+ );
1456
+ `);
1457
+ }
1458
+ function inferLegacySchemaVersion(db) {
1459
+ const hasProjects = tableExists(db, "projects");
1460
+ const hasObservations = tableExists(db, "observations");
1461
+ const hasSessions = tableExists(db, "sessions");
1462
+ if (!(hasProjects && hasObservations && hasSessions))
1463
+ return 0;
1464
+ ensureLegacyBaseTables(db);
1465
+ let version = 1;
1466
+ if (columnExists(db, "observations", "superseded_by"))
1467
+ version = Math.max(version, 2);
1468
+ if (columnExists(db, "observations", "remote_source_id"))
1469
+ version = Math.max(version, 3);
1470
+ if (tableExists(db, "vec_observations"))
1471
+ version = Math.max(version, 4);
1472
+ const hasSessionMetrics = columnExists(db, "sessions", "files_touched_count") && columnExists(db, "sessions", "searches_performed") && columnExists(db, "sessions", "tool_calls_count");
1473
+ if (hasSessionMetrics || tableExists(db, "security_findings"))
1474
+ version = Math.max(version, 5);
1475
+ if (columnExists(db, "sessions", "risk_score"))
1476
+ version = Math.max(version, 6);
1477
+ if (tableExists(db, "packs_installed"))
1478
+ version = Math.max(version, 7);
1479
+ if (tableExists(db, "user_prompts"))
1480
+ version = Math.max(version, 9);
1481
+ if (tableExists(db, "tool_events"))
1482
+ version = Math.max(version, 10);
1483
+ return version;
1484
+ }
1440
1485
  function runMigrations(db) {
1441
1486
  const currentVersion = db.query("PRAGMA user_version").get();
1442
1487
  let version = currentVersion.user_version;
1488
+ const inferred = inferLegacySchemaVersion(db);
1489
+ if (inferred > version) {
1490
+ db.exec(`PRAGMA user_version = ${inferred}`);
1491
+ version = inferred;
1492
+ }
1443
1493
  for (const migration of MIGRATIONS) {
1444
1494
  if (migration.version <= version)
1445
1495
  continue;
@@ -1515,6 +1565,11 @@ import { createHash as createHash2 } from "node:crypto";
1515
1565
  var IS_BUN = typeof globalThis.Bun !== "undefined";
1516
1566
  function openDatabase(dbPath) {
1517
1567
  if (IS_BUN) {
1568
+ if (process.platform === "darwin") {
1569
+ try {
1570
+ return openNodeDatabase(dbPath);
1571
+ } catch {}
1572
+ }
1518
1573
  return openBunDatabase(dbPath);
1519
1574
  }
1520
1575
  return openNodeDatabase(dbPath);
@@ -785,9 +785,59 @@ function isVecExtensionLoaded(db) {
785
785
  return false;
786
786
  }
787
787
  }
788
+ function tableExists(db, name) {
789
+ const row = db.query(`SELECT name FROM sqlite_master WHERE type IN ('table', 'view') AND name = ?`).get(name);
790
+ return Boolean(row?.name);
791
+ }
792
+ function columnExists(db, table, column) {
793
+ if (!tableExists(db, table))
794
+ return false;
795
+ const rows = db.query(`PRAGMA table_info(${table})`).all();
796
+ return rows.some((row) => row.name === column);
797
+ }
798
+ function ensureLegacyBaseTables(db) {
799
+ db.exec(`
800
+ CREATE TABLE IF NOT EXISTS sync_state (
801
+ key TEXT PRIMARY KEY,
802
+ value TEXT NOT NULL
803
+ );
804
+ `);
805
+ }
806
+ function inferLegacySchemaVersion(db) {
807
+ const hasProjects = tableExists(db, "projects");
808
+ const hasObservations = tableExists(db, "observations");
809
+ const hasSessions = tableExists(db, "sessions");
810
+ if (!(hasProjects && hasObservations && hasSessions))
811
+ return 0;
812
+ ensureLegacyBaseTables(db);
813
+ let version = 1;
814
+ if (columnExists(db, "observations", "superseded_by"))
815
+ version = Math.max(version, 2);
816
+ if (columnExists(db, "observations", "remote_source_id"))
817
+ version = Math.max(version, 3);
818
+ if (tableExists(db, "vec_observations"))
819
+ version = Math.max(version, 4);
820
+ const hasSessionMetrics = columnExists(db, "sessions", "files_touched_count") && columnExists(db, "sessions", "searches_performed") && columnExists(db, "sessions", "tool_calls_count");
821
+ if (hasSessionMetrics || tableExists(db, "security_findings"))
822
+ version = Math.max(version, 5);
823
+ if (columnExists(db, "sessions", "risk_score"))
824
+ version = Math.max(version, 6);
825
+ if (tableExists(db, "packs_installed"))
826
+ version = Math.max(version, 7);
827
+ if (tableExists(db, "user_prompts"))
828
+ version = Math.max(version, 9);
829
+ if (tableExists(db, "tool_events"))
830
+ version = Math.max(version, 10);
831
+ return version;
832
+ }
788
833
  function runMigrations(db) {
789
834
  const currentVersion = db.query("PRAGMA user_version").get();
790
835
  let version = currentVersion.user_version;
836
+ const inferred = inferLegacySchemaVersion(db);
837
+ if (inferred > version) {
838
+ db.exec(`PRAGMA user_version = ${inferred}`);
839
+ version = inferred;
840
+ }
791
841
  for (const migration of MIGRATIONS) {
792
842
  if (migration.version <= version)
793
843
  continue;
@@ -863,6 +913,11 @@ import { createHash as createHash2 } from "node:crypto";
863
913
  var IS_BUN = typeof globalThis.Bun !== "undefined";
864
914
  function openDatabase(dbPath) {
865
915
  if (IS_BUN) {
916
+ if (process.platform === "darwin") {
917
+ try {
918
+ return openNodeDatabase(dbPath);
919
+ } catch {}
920
+ }
866
921
  return openBunDatabase(dbPath);
867
922
  }
868
923
  return openNodeDatabase(dbPath);
@@ -579,9 +579,59 @@ function isVecExtensionLoaded(db) {
579
579
  return false;
580
580
  }
581
581
  }
582
+ function tableExists(db, name) {
583
+ const row = db.query(`SELECT name FROM sqlite_master WHERE type IN ('table', 'view') AND name = ?`).get(name);
584
+ return Boolean(row?.name);
585
+ }
586
+ function columnExists(db, table, column) {
587
+ if (!tableExists(db, table))
588
+ return false;
589
+ const rows = db.query(`PRAGMA table_info(${table})`).all();
590
+ return rows.some((row) => row.name === column);
591
+ }
592
+ function ensureLegacyBaseTables(db) {
593
+ db.exec(`
594
+ CREATE TABLE IF NOT EXISTS sync_state (
595
+ key TEXT PRIMARY KEY,
596
+ value TEXT NOT NULL
597
+ );
598
+ `);
599
+ }
600
+ function inferLegacySchemaVersion(db) {
601
+ const hasProjects = tableExists(db, "projects");
602
+ const hasObservations = tableExists(db, "observations");
603
+ const hasSessions = tableExists(db, "sessions");
604
+ if (!(hasProjects && hasObservations && hasSessions))
605
+ return 0;
606
+ ensureLegacyBaseTables(db);
607
+ let version = 1;
608
+ if (columnExists(db, "observations", "superseded_by"))
609
+ version = Math.max(version, 2);
610
+ if (columnExists(db, "observations", "remote_source_id"))
611
+ version = Math.max(version, 3);
612
+ if (tableExists(db, "vec_observations"))
613
+ version = Math.max(version, 4);
614
+ const hasSessionMetrics = columnExists(db, "sessions", "files_touched_count") && columnExists(db, "sessions", "searches_performed") && columnExists(db, "sessions", "tool_calls_count");
615
+ if (hasSessionMetrics || tableExists(db, "security_findings"))
616
+ version = Math.max(version, 5);
617
+ if (columnExists(db, "sessions", "risk_score"))
618
+ version = Math.max(version, 6);
619
+ if (tableExists(db, "packs_installed"))
620
+ version = Math.max(version, 7);
621
+ if (tableExists(db, "user_prompts"))
622
+ version = Math.max(version, 9);
623
+ if (tableExists(db, "tool_events"))
624
+ version = Math.max(version, 10);
625
+ return version;
626
+ }
582
627
  function runMigrations(db) {
583
628
  const currentVersion = db.query("PRAGMA user_version").get();
584
629
  let version = currentVersion.user_version;
630
+ const inferred = inferLegacySchemaVersion(db);
631
+ if (inferred > version) {
632
+ db.exec(`PRAGMA user_version = ${inferred}`);
633
+ version = inferred;
634
+ }
585
635
  for (const migration of MIGRATIONS) {
586
636
  if (migration.version <= version)
587
637
  continue;
@@ -657,6 +707,11 @@ import { createHash as createHash2 } from "node:crypto";
657
707
  var IS_BUN = typeof globalThis.Bun !== "undefined";
658
708
  function openDatabase(dbPath) {
659
709
  if (IS_BUN) {
710
+ if (process.platform === "darwin") {
711
+ try {
712
+ return openNodeDatabase(dbPath);
713
+ } catch {}
714
+ }
660
715
  return openBunDatabase(dbPath);
661
716
  }
662
717
  return openNodeDatabase(dbPath);
@@ -655,9 +655,59 @@ function isVecExtensionLoaded(db) {
655
655
  return false;
656
656
  }
657
657
  }
658
+ function tableExists(db, name) {
659
+ const row = db.query(`SELECT name FROM sqlite_master WHERE type IN ('table', 'view') AND name = ?`).get(name);
660
+ return Boolean(row?.name);
661
+ }
662
+ function columnExists(db, table, column) {
663
+ if (!tableExists(db, table))
664
+ return false;
665
+ const rows = db.query(`PRAGMA table_info(${table})`).all();
666
+ return rows.some((row) => row.name === column);
667
+ }
668
+ function ensureLegacyBaseTables(db) {
669
+ db.exec(`
670
+ CREATE TABLE IF NOT EXISTS sync_state (
671
+ key TEXT PRIMARY KEY,
672
+ value TEXT NOT NULL
673
+ );
674
+ `);
675
+ }
676
+ function inferLegacySchemaVersion(db) {
677
+ const hasProjects = tableExists(db, "projects");
678
+ const hasObservations = tableExists(db, "observations");
679
+ const hasSessions = tableExists(db, "sessions");
680
+ if (!(hasProjects && hasObservations && hasSessions))
681
+ return 0;
682
+ ensureLegacyBaseTables(db);
683
+ let version = 1;
684
+ if (columnExists(db, "observations", "superseded_by"))
685
+ version = Math.max(version, 2);
686
+ if (columnExists(db, "observations", "remote_source_id"))
687
+ version = Math.max(version, 3);
688
+ if (tableExists(db, "vec_observations"))
689
+ version = Math.max(version, 4);
690
+ const hasSessionMetrics = columnExists(db, "sessions", "files_touched_count") && columnExists(db, "sessions", "searches_performed") && columnExists(db, "sessions", "tool_calls_count");
691
+ if (hasSessionMetrics || tableExists(db, "security_findings"))
692
+ version = Math.max(version, 5);
693
+ if (columnExists(db, "sessions", "risk_score"))
694
+ version = Math.max(version, 6);
695
+ if (tableExists(db, "packs_installed"))
696
+ version = Math.max(version, 7);
697
+ if (tableExists(db, "user_prompts"))
698
+ version = Math.max(version, 9);
699
+ if (tableExists(db, "tool_events"))
700
+ version = Math.max(version, 10);
701
+ return version;
702
+ }
658
703
  function runMigrations(db) {
659
704
  const currentVersion = db.query("PRAGMA user_version").get();
660
705
  let version = currentVersion.user_version;
706
+ const inferred = inferLegacySchemaVersion(db);
707
+ if (inferred > version) {
708
+ db.exec(`PRAGMA user_version = ${inferred}`);
709
+ version = inferred;
710
+ }
661
711
  for (const migration of MIGRATIONS) {
662
712
  if (migration.version <= version)
663
713
  continue;
@@ -733,6 +783,11 @@ import { createHash as createHash2 } from "node:crypto";
733
783
  var IS_BUN = typeof globalThis.Bun !== "undefined";
734
784
  function openDatabase(dbPath) {
735
785
  if (IS_BUN) {
786
+ if (process.platform === "darwin") {
787
+ try {
788
+ return openNodeDatabase(dbPath);
789
+ } catch {}
790
+ }
736
791
  return openBunDatabase(dbPath);
737
792
  }
738
793
  return openNodeDatabase(dbPath);
@@ -2166,9 +2166,59 @@ function isVecExtensionLoaded(db) {
2166
2166
  return false;
2167
2167
  }
2168
2168
  }
2169
+ function tableExists(db, name) {
2170
+ const row = db.query(`SELECT name FROM sqlite_master WHERE type IN ('table', 'view') AND name = ?`).get(name);
2171
+ return Boolean(row?.name);
2172
+ }
2173
+ function columnExists(db, table, column) {
2174
+ if (!tableExists(db, table))
2175
+ return false;
2176
+ const rows = db.query(`PRAGMA table_info(${table})`).all();
2177
+ return rows.some((row) => row.name === column);
2178
+ }
2179
+ function ensureLegacyBaseTables(db) {
2180
+ db.exec(`
2181
+ CREATE TABLE IF NOT EXISTS sync_state (
2182
+ key TEXT PRIMARY KEY,
2183
+ value TEXT NOT NULL
2184
+ );
2185
+ `);
2186
+ }
2187
+ function inferLegacySchemaVersion(db) {
2188
+ const hasProjects = tableExists(db, "projects");
2189
+ const hasObservations = tableExists(db, "observations");
2190
+ const hasSessions = tableExists(db, "sessions");
2191
+ if (!(hasProjects && hasObservations && hasSessions))
2192
+ return 0;
2193
+ ensureLegacyBaseTables(db);
2194
+ let version = 1;
2195
+ if (columnExists(db, "observations", "superseded_by"))
2196
+ version = Math.max(version, 2);
2197
+ if (columnExists(db, "observations", "remote_source_id"))
2198
+ version = Math.max(version, 3);
2199
+ if (tableExists(db, "vec_observations"))
2200
+ version = Math.max(version, 4);
2201
+ const hasSessionMetrics = columnExists(db, "sessions", "files_touched_count") && columnExists(db, "sessions", "searches_performed") && columnExists(db, "sessions", "tool_calls_count");
2202
+ if (hasSessionMetrics || tableExists(db, "security_findings"))
2203
+ version = Math.max(version, 5);
2204
+ if (columnExists(db, "sessions", "risk_score"))
2205
+ version = Math.max(version, 6);
2206
+ if (tableExists(db, "packs_installed"))
2207
+ version = Math.max(version, 7);
2208
+ if (tableExists(db, "user_prompts"))
2209
+ version = Math.max(version, 9);
2210
+ if (tableExists(db, "tool_events"))
2211
+ version = Math.max(version, 10);
2212
+ return version;
2213
+ }
2169
2214
  function runMigrations(db) {
2170
2215
  const currentVersion = db.query("PRAGMA user_version").get();
2171
2216
  let version = currentVersion.user_version;
2217
+ const inferred = inferLegacySchemaVersion(db);
2218
+ if (inferred > version) {
2219
+ db.exec(`PRAGMA user_version = ${inferred}`);
2220
+ version = inferred;
2221
+ }
2172
2222
  for (const migration of MIGRATIONS) {
2173
2223
  if (migration.version <= version)
2174
2224
  continue;
@@ -2244,6 +2294,11 @@ import { createHash as createHash3 } from "node:crypto";
2244
2294
  var IS_BUN = typeof globalThis.Bun !== "undefined";
2245
2295
  function openDatabase(dbPath) {
2246
2296
  if (IS_BUN) {
2297
+ if (process.platform === "darwin") {
2298
+ try {
2299
+ return openNodeDatabase(dbPath);
2300
+ } catch {}
2301
+ }
2247
2302
  return openBunDatabase(dbPath);
2248
2303
  }
2249
2304
  return openNodeDatabase(dbPath);
@@ -3009,19 +3064,28 @@ function formatVisibleStartupBrief(context) {
3009
3064
  const sessionFallbacks = sessionFallbacksFromContext(context);
3010
3065
  const recentOutcomeLines = buildRecentOutcomeLines(context, latest);
3011
3066
  const projectSignals = buildProjectSignalLine(context);
3067
+ const shownItems = new Set;
3012
3068
  if (promptLines.length > 0) {
3013
3069
  lines.push(`${c2.cyan}Recent Requests:${c2.reset}`);
3014
3070
  for (const item of promptLines) {
3015
3071
  lines.push(` - ${truncateInline(item, 160)}`);
3072
+ rememberShownItem(shownItems, item);
3016
3073
  }
3017
3074
  }
3018
3075
  if (latest) {
3076
+ const sanitizedNextSteps = sanitizeNextSteps(latest.next_steps, {
3077
+ request: currentRequest,
3078
+ investigated: chooseSection(latest.investigated, observationFallbacks.investigated, "Investigated"),
3079
+ learned: latest.learned,
3080
+ completed: chooseSection(latest.completed, observationFallbacks.completed, "Completed"),
3081
+ recentOutcomes: recentOutcomeLines
3082
+ });
3019
3083
  const sections = [
3020
3084
  ["Request", currentRequest, 1],
3021
3085
  ["Investigated", chooseSection(latest.investigated, observationFallbacks.investigated, "Investigated"), 2],
3022
3086
  ["Learned", latest.learned, 2],
3023
3087
  ["Completed", chooseSection(latest.completed, observationFallbacks.completed, "Completed"), 2],
3024
- ["Next Steps", latest.next_steps, 2]
3088
+ ["Next Steps", sanitizedNextSteps, 2]
3025
3089
  ];
3026
3090
  for (const [label, value, maxItems] of sections) {
3027
3091
  const formatted = toSplashLines(value, maxItems ?? 2);
@@ -3029,33 +3093,45 @@ function formatVisibleStartupBrief(context) {
3029
3093
  lines.push(`${c2.cyan}${label}:${c2.reset}`);
3030
3094
  for (const item of formatted) {
3031
3095
  lines.push(` ${item}`);
3096
+ rememberShownItem(shownItems, item);
3032
3097
  }
3033
3098
  }
3034
3099
  }
3035
3100
  } else if (currentRequest && !duplicatesPromptLine(currentRequest, latestPromptLine)) {
3036
3101
  lines.push(`${c2.cyan}Current Request:${c2.reset}`);
3037
3102
  lines.push(` - ${truncateInline(currentRequest, 160)}`);
3103
+ rememberShownItem(shownItems, currentRequest);
3038
3104
  if (toolFallbacks.length > 0) {
3039
- lines.push(`${c2.cyan}Recent Tools:${c2.reset}`);
3040
- for (const item of toolFallbacks) {
3041
- lines.push(` - ${truncateInline(item, 160)}`);
3105
+ const additiveTools = filterAdditiveToolFallbacks(toolFallbacks, shownItems);
3106
+ if (additiveTools.length > 0) {
3107
+ lines.push(`${c2.cyan}Recent Tools:${c2.reset}`);
3108
+ for (const item of additiveTools) {
3109
+ lines.push(` - ${truncateInline(item, 160)}`);
3110
+ rememberShownItem(shownItems, item);
3111
+ }
3042
3112
  }
3043
3113
  }
3044
3114
  }
3045
3115
  if (latest && currentRequest && !hasRequestSection(lines) && !duplicatesPromptLine(currentRequest, latestPromptLine)) {
3046
3116
  lines.push(`${c2.cyan}Current Request:${c2.reset}`);
3047
3117
  lines.push(` - ${truncateInline(currentRequest, 160)}`);
3118
+ rememberShownItem(shownItems, currentRequest);
3048
3119
  }
3049
3120
  if (recentOutcomeLines.length > 0) {
3050
3121
  lines.push(`${c2.cyan}Recent Work:${c2.reset}`);
3051
3122
  for (const item of recentOutcomeLines) {
3052
3123
  lines.push(` - ${truncateInline(item, 160)}`);
3124
+ rememberShownItem(shownItems, item);
3053
3125
  }
3054
3126
  }
3055
3127
  if (toolFallbacks.length > 0 && latest) {
3056
- lines.push(`${c2.cyan}Recent Tools:${c2.reset}`);
3057
- for (const item of toolFallbacks) {
3058
- lines.push(` - ${truncateInline(item, 160)}`);
3128
+ const additiveTools = filterAdditiveToolFallbacks(toolFallbacks, shownItems);
3129
+ if (additiveTools.length > 0) {
3130
+ lines.push(`${c2.cyan}Recent Tools:${c2.reset}`);
3131
+ for (const item of additiveTools) {
3132
+ lines.push(` - ${truncateInline(item, 160)}`);
3133
+ rememberShownItem(shownItems, item);
3134
+ }
3059
3135
  }
3060
3136
  }
3061
3137
  if (sessionFallbacks.length > 0) {
@@ -3080,6 +3156,46 @@ function formatVisibleStartupBrief(context) {
3080
3156
  }
3081
3157
  return lines.slice(0, 14);
3082
3158
  }
3159
+ function rememberShownItem(shown, value) {
3160
+ if (!value)
3161
+ return;
3162
+ for (const item of value.split(`
3163
+ `)) {
3164
+ const normalized = normalizeStartupItem(item);
3165
+ if (normalized)
3166
+ shown.add(normalized);
3167
+ }
3168
+ }
3169
+ function extractNormalizedSplashItems(value) {
3170
+ if (!value)
3171
+ return [];
3172
+ return value.split(`
3173
+ `).map((line) => normalizeStartupItem(line)).filter(Boolean);
3174
+ }
3175
+ function sanitizeNextSteps(nextSteps, context) {
3176
+ if (!nextSteps)
3177
+ return null;
3178
+ const covered = new Set([
3179
+ normalizeStartupItem(context.request ?? ""),
3180
+ ...extractNormalizedSplashItems(context.investigated),
3181
+ ...extractNormalizedSplashItems(context.learned),
3182
+ ...extractNormalizedSplashItems(context.completed),
3183
+ ...context.recentOutcomes.map((item) => normalizeStartupItem(item))
3184
+ ].filter(Boolean));
3185
+ const kept = nextSteps.split(`
3186
+ `).map((line) => line.trim()).filter(Boolean).map((line) => line.replace(/^[-*]\s*/, "")).filter((line) => {
3187
+ const normalized = normalizeStartupItem(line.replace(/^(investigate|follow through):\s*/i, ""));
3188
+ return normalized && !covered.has(normalized);
3189
+ });
3190
+ return kept.length > 0 ? kept.map((line) => `- ${line}`).join(`
3191
+ `) : null;
3192
+ }
3193
+ function filterAdditiveToolFallbacks(toolFallbacks, shownItems) {
3194
+ return toolFallbacks.filter((item) => {
3195
+ const normalized = normalizeStartupItem(item);
3196
+ return normalized && !shownItems.has(normalized);
3197
+ });
3198
+ }
3083
3199
  function buildPromptFallback(context) {
3084
3200
  const latest = (context.recentPrompts ?? []).find((prompt) => isMeaningfulPrompt2(prompt.prompt));
3085
3201
  if (!latest?.prompt)
@@ -43,7 +43,10 @@ function extractRetrospective(observations, sessionId, projectId, userId) {
43
43
  const investigated = extractInvestigated(observations);
44
44
  const learned = extractLearned(observations);
45
45
  const completed = extractCompleted(observations);
46
- const nextSteps = extractNextSteps(observations);
46
+ const nextSteps = extractNextSteps(observations, {
47
+ request,
48
+ completed
49
+ });
47
50
  if (!request && !investigated && !learned && !completed && !nextSteps) {
48
51
  return null;
49
52
  }
@@ -101,13 +104,17 @@ ${facts}` : `- ${title}`;
101
104
  return dedupeBulletLines(lines).join(`
102
105
  `);
103
106
  }
104
- function extractNextSteps(observations) {
107
+ function extractNextSteps(observations, existing) {
105
108
  if (observations.length < 2)
106
109
  return null;
107
110
  const lastQuarterStart = Math.max(0, Math.min(observations.length - 1, observations.length - 3, Math.floor(observations.length * 0.75)));
108
111
  const lastQuarter = observations.slice(lastQuarterStart);
109
- const unresolved = lastQuarter.filter((o) => o.type === "bugfix" && o.narrative && /error|fail|exception/i.test(o.narrative));
110
- const explicitDecisions = lastQuarter.filter((o) => o.type === "decision").sort((a, b) => scoreNarrativeObservation(b) - scoreNarrativeObservation(a)).slice(0, 2).map((o) => `- Follow through: ${o.title}`);
112
+ const alreadyCovered = new Set([
113
+ normalizeObservationKey(existing.request ?? ""),
114
+ ...extractNormalizedSummaryItems(existing.completed)
115
+ ].filter(Boolean));
116
+ const unresolved = lastQuarter.filter((o) => o.type === "bugfix" && o.narrative && /error|fail|exception/i.test(o.narrative) && !alreadyCovered.has(normalizeObservationKey(o.title)));
117
+ const explicitDecisions = lastQuarter.filter((o) => o.type === "decision").sort((a, b) => scoreNarrativeObservation(b) - scoreNarrativeObservation(a)).slice(0, 2).filter((o) => !alreadyCovered.has(normalizeObservationKey(o.title))).map((o) => `- Follow through: ${o.title}`);
111
118
  if (unresolved.length === 0 && explicitDecisions.length === 0)
112
119
  return null;
113
120
  const lines = unresolved.map((o) => `- Investigate: ${o.title}`).slice(0, 3).concat(explicitDecisions);
@@ -148,6 +155,12 @@ function dedupeObservationsByTitle(observations) {
148
155
  }
149
156
  return deduped;
150
157
  }
158
+ function extractNormalizedSummaryItems(value) {
159
+ if (!value)
160
+ return [];
161
+ return value.split(`
162
+ `).map((line) => line.trim()).filter(Boolean).map((line) => line.replace(/^[-*]\s*/, "")).map((line) => line.replace(/^(investigate|follow through):\s*/i, "")).map((line) => normalizeObservationKey(line)).filter(Boolean);
163
+ }
151
164
  function scoreCompletedObservation(obs) {
152
165
  let score = scoreNarrativeObservation(obs);
153
166
  if (obs.type === "feature")
@@ -801,9 +814,59 @@ function isVecExtensionLoaded(db) {
801
814
  return false;
802
815
  }
803
816
  }
817
+ function tableExists(db, name) {
818
+ const row = db.query(`SELECT name FROM sqlite_master WHERE type IN ('table', 'view') AND name = ?`).get(name);
819
+ return Boolean(row?.name);
820
+ }
821
+ function columnExists(db, table, column) {
822
+ if (!tableExists(db, table))
823
+ return false;
824
+ const rows = db.query(`PRAGMA table_info(${table})`).all();
825
+ return rows.some((row) => row.name === column);
826
+ }
827
+ function ensureLegacyBaseTables(db) {
828
+ db.exec(`
829
+ CREATE TABLE IF NOT EXISTS sync_state (
830
+ key TEXT PRIMARY KEY,
831
+ value TEXT NOT NULL
832
+ );
833
+ `);
834
+ }
835
+ function inferLegacySchemaVersion(db) {
836
+ const hasProjects = tableExists(db, "projects");
837
+ const hasObservations = tableExists(db, "observations");
838
+ const hasSessions = tableExists(db, "sessions");
839
+ if (!(hasProjects && hasObservations && hasSessions))
840
+ return 0;
841
+ ensureLegacyBaseTables(db);
842
+ let version = 1;
843
+ if (columnExists(db, "observations", "superseded_by"))
844
+ version = Math.max(version, 2);
845
+ if (columnExists(db, "observations", "remote_source_id"))
846
+ version = Math.max(version, 3);
847
+ if (tableExists(db, "vec_observations"))
848
+ version = Math.max(version, 4);
849
+ const hasSessionMetrics = columnExists(db, "sessions", "files_touched_count") && columnExists(db, "sessions", "searches_performed") && columnExists(db, "sessions", "tool_calls_count");
850
+ if (hasSessionMetrics || tableExists(db, "security_findings"))
851
+ version = Math.max(version, 5);
852
+ if (columnExists(db, "sessions", "risk_score"))
853
+ version = Math.max(version, 6);
854
+ if (tableExists(db, "packs_installed"))
855
+ version = Math.max(version, 7);
856
+ if (tableExists(db, "user_prompts"))
857
+ version = Math.max(version, 9);
858
+ if (tableExists(db, "tool_events"))
859
+ version = Math.max(version, 10);
860
+ return version;
861
+ }
804
862
  function runMigrations(db) {
805
863
  const currentVersion = db.query("PRAGMA user_version").get();
806
864
  let version = currentVersion.user_version;
865
+ const inferred = inferLegacySchemaVersion(db);
866
+ if (inferred > version) {
867
+ db.exec(`PRAGMA user_version = ${inferred}`);
868
+ version = inferred;
869
+ }
807
870
  for (const migration of MIGRATIONS) {
808
871
  if (migration.version <= version)
809
872
  continue;
@@ -879,6 +942,11 @@ import { createHash as createHash2 } from "node:crypto";
879
942
  var IS_BUN = typeof globalThis.Bun !== "undefined";
880
943
  function openDatabase(dbPath) {
881
944
  if (IS_BUN) {
945
+ if (process.platform === "darwin") {
946
+ try {
947
+ return openNodeDatabase(dbPath);
948
+ } catch {}
949
+ }
882
950
  return openBunDatabase(dbPath);
883
951
  }
884
952
  return openNodeDatabase(dbPath);
@@ -1857,8 +1925,13 @@ function buildSummaryVectorDocument(summary, config, project, observations = [],
1857
1925
  next_step_items: extractSectionItems(summary.next_steps),
1858
1926
  prompt_count: captureContext?.prompt_count ?? 0,
1859
1927
  tool_event_count: captureContext?.tool_event_count ?? 0,
1928
+ capture_state: captureContext?.capture_state ?? "summary-only",
1929
+ recent_request_prompts: captureContext?.recent_request_prompts ?? [],
1860
1930
  latest_request: captureContext?.latest_request ?? null,
1861
1931
  recent_tool_names: captureContext?.recent_tool_names ?? [],
1932
+ recent_tool_commands: captureContext?.recent_tool_commands ?? [],
1933
+ hot_files: captureContext?.hot_files ?? [],
1934
+ recent_outcomes: captureContext?.recent_outcomes ?? [],
1862
1935
  decisions_count: valueSignals.decisions_count,
1863
1936
  lessons_count: valueSignals.lessons_count,
1864
1937
  discoveries_count: valueSignals.discoveries_count,
@@ -1902,7 +1975,7 @@ async function pushOutbox(db, client, config, batchSize = 50) {
1902
1975
  const doc2 = buildSummaryVectorDocument(summary, config, {
1903
1976
  canonical_id: project2.canonical_id,
1904
1977
  name: project2.name
1905
- }, summaryObservations, buildSummaryCaptureContext(sessionPrompts, sessionToolEvents));
1978
+ }, summaryObservations, buildSummaryCaptureContext(sessionPrompts, sessionToolEvents, summaryObservations));
1906
1979
  batch.push({ entryId: entry.id, doc: doc2 });
1907
1980
  continue;
1908
1981
  }
@@ -1976,16 +2049,39 @@ function extractSectionItems(section) {
1976
2049
  return section.split(`
1977
2050
  `).map((line) => line.trim()).filter(Boolean).map((line) => line.replace(/^[-*]\s*/, "").trim()).filter(Boolean).slice(0, 4);
1978
2051
  }
1979
- function buildSummaryCaptureContext(prompts, toolEvents) {
2052
+ function buildSummaryCaptureContext(prompts, toolEvents, observations) {
1980
2053
  const latestRequest = prompts.length > 0 ? prompts[prompts.length - 1]?.prompt ?? null : null;
2054
+ const recentRequestPrompts = prompts.slice(-3).map((prompt) => prompt.prompt.trim()).filter(Boolean);
1981
2055
  const recentToolNames = [...new Set(toolEvents.slice(-8).map((tool) => tool.tool_name).filter(Boolean))];
2056
+ const recentToolCommands = [...new Set(toolEvents.slice(-5).map((tool) => (tool.command ?? tool.file_path ?? "").trim()).filter(Boolean))];
2057
+ const hotFiles = [...new Set(observations.flatMap((obs) => [
2058
+ ...parseJsonArray2(obs.files_modified),
2059
+ ...parseJsonArray2(obs.files_read)
2060
+ ]).filter(Boolean))].slice(0, 6);
2061
+ const recentOutcomes = observations.filter((obs) => ["bugfix", "feature", "refactor", "change", "decision"].includes(obs.type)).map((obs) => obs.title.trim()).filter((title) => title.length > 0).slice(0, 6);
2062
+ const captureState = prompts.length > 0 && toolEvents.length > 0 ? "rich" : prompts.length > 0 || toolEvents.length > 0 ? "partial" : "summary-only";
1982
2063
  return {
1983
2064
  prompt_count: prompts.length,
1984
2065
  tool_event_count: toolEvents.length,
2066
+ recent_request_prompts: recentRequestPrompts,
1985
2067
  latest_request: latestRequest,
1986
- recent_tool_names: recentToolNames
2068
+ recent_tool_names: recentToolNames,
2069
+ recent_tool_commands: recentToolCommands,
2070
+ capture_state: captureState,
2071
+ hot_files: hotFiles,
2072
+ recent_outcomes: recentOutcomes
1987
2073
  };
1988
2074
  }
2075
+ function parseJsonArray2(value) {
2076
+ if (!value)
2077
+ return [];
2078
+ try {
2079
+ const parsed = JSON.parse(value);
2080
+ return Array.isArray(parsed) ? parsed.filter((item) => typeof item === "string" && item.trim().length > 0) : [];
2081
+ } catch {
2082
+ return [];
2083
+ }
2084
+ }
1989
2085
 
1990
2086
  // src/embeddings/embedder.ts
1991
2087
  var _available = null;
@@ -723,9 +723,59 @@ function isVecExtensionLoaded(db) {
723
723
  return false;
724
724
  }
725
725
  }
726
+ function tableExists(db, name) {
727
+ const row = db.query(`SELECT name FROM sqlite_master WHERE type IN ('table', 'view') AND name = ?`).get(name);
728
+ return Boolean(row?.name);
729
+ }
730
+ function columnExists(db, table, column) {
731
+ if (!tableExists(db, table))
732
+ return false;
733
+ const rows = db.query(`PRAGMA table_info(${table})`).all();
734
+ return rows.some((row) => row.name === column);
735
+ }
736
+ function ensureLegacyBaseTables(db) {
737
+ db.exec(`
738
+ CREATE TABLE IF NOT EXISTS sync_state (
739
+ key TEXT PRIMARY KEY,
740
+ value TEXT NOT NULL
741
+ );
742
+ `);
743
+ }
744
+ function inferLegacySchemaVersion(db) {
745
+ const hasProjects = tableExists(db, "projects");
746
+ const hasObservations = tableExists(db, "observations");
747
+ const hasSessions = tableExists(db, "sessions");
748
+ if (!(hasProjects && hasObservations && hasSessions))
749
+ return 0;
750
+ ensureLegacyBaseTables(db);
751
+ let version = 1;
752
+ if (columnExists(db, "observations", "superseded_by"))
753
+ version = Math.max(version, 2);
754
+ if (columnExists(db, "observations", "remote_source_id"))
755
+ version = Math.max(version, 3);
756
+ if (tableExists(db, "vec_observations"))
757
+ version = Math.max(version, 4);
758
+ const hasSessionMetrics = columnExists(db, "sessions", "files_touched_count") && columnExists(db, "sessions", "searches_performed") && columnExists(db, "sessions", "tool_calls_count");
759
+ if (hasSessionMetrics || tableExists(db, "security_findings"))
760
+ version = Math.max(version, 5);
761
+ if (columnExists(db, "sessions", "risk_score"))
762
+ version = Math.max(version, 6);
763
+ if (tableExists(db, "packs_installed"))
764
+ version = Math.max(version, 7);
765
+ if (tableExists(db, "user_prompts"))
766
+ version = Math.max(version, 9);
767
+ if (tableExists(db, "tool_events"))
768
+ version = Math.max(version, 10);
769
+ return version;
770
+ }
726
771
  function runMigrations(db) {
727
772
  const currentVersion = db.query("PRAGMA user_version").get();
728
773
  let version = currentVersion.user_version;
774
+ const inferred = inferLegacySchemaVersion(db);
775
+ if (inferred > version) {
776
+ db.exec(`PRAGMA user_version = ${inferred}`);
777
+ version = inferred;
778
+ }
729
779
  for (const migration of MIGRATIONS) {
730
780
  if (migration.version <= version)
731
781
  continue;
@@ -801,6 +851,11 @@ import { createHash as createHash2 } from "node:crypto";
801
851
  var IS_BUN = typeof globalThis.Bun !== "undefined";
802
852
  function openDatabase(dbPath) {
803
853
  if (IS_BUN) {
854
+ if (process.platform === "darwin") {
855
+ try {
856
+ return openNodeDatabase(dbPath);
857
+ } catch {}
858
+ }
804
859
  return openBunDatabase(dbPath);
805
860
  }
806
861
  return openNodeDatabase(dbPath);
package/dist/server.js CHANGED
@@ -14129,9 +14129,59 @@ function isVecExtensionLoaded(db) {
14129
14129
  return false;
14130
14130
  }
14131
14131
  }
14132
+ function tableExists(db, name) {
14133
+ const row = db.query(`SELECT name FROM sqlite_master WHERE type IN ('table', 'view') AND name = ?`).get(name);
14134
+ return Boolean(row?.name);
14135
+ }
14136
+ function columnExists(db, table, column) {
14137
+ if (!tableExists(db, table))
14138
+ return false;
14139
+ const rows = db.query(`PRAGMA table_info(${table})`).all();
14140
+ return rows.some((row) => row.name === column);
14141
+ }
14142
+ function ensureLegacyBaseTables(db) {
14143
+ db.exec(`
14144
+ CREATE TABLE IF NOT EXISTS sync_state (
14145
+ key TEXT PRIMARY KEY,
14146
+ value TEXT NOT NULL
14147
+ );
14148
+ `);
14149
+ }
14150
+ function inferLegacySchemaVersion(db) {
14151
+ const hasProjects = tableExists(db, "projects");
14152
+ const hasObservations = tableExists(db, "observations");
14153
+ const hasSessions = tableExists(db, "sessions");
14154
+ if (!(hasProjects && hasObservations && hasSessions))
14155
+ return 0;
14156
+ ensureLegacyBaseTables(db);
14157
+ let version2 = 1;
14158
+ if (columnExists(db, "observations", "superseded_by"))
14159
+ version2 = Math.max(version2, 2);
14160
+ if (columnExists(db, "observations", "remote_source_id"))
14161
+ version2 = Math.max(version2, 3);
14162
+ if (tableExists(db, "vec_observations"))
14163
+ version2 = Math.max(version2, 4);
14164
+ const hasSessionMetrics = columnExists(db, "sessions", "files_touched_count") && columnExists(db, "sessions", "searches_performed") && columnExists(db, "sessions", "tool_calls_count");
14165
+ if (hasSessionMetrics || tableExists(db, "security_findings"))
14166
+ version2 = Math.max(version2, 5);
14167
+ if (columnExists(db, "sessions", "risk_score"))
14168
+ version2 = Math.max(version2, 6);
14169
+ if (tableExists(db, "packs_installed"))
14170
+ version2 = Math.max(version2, 7);
14171
+ if (tableExists(db, "user_prompts"))
14172
+ version2 = Math.max(version2, 9);
14173
+ if (tableExists(db, "tool_events"))
14174
+ version2 = Math.max(version2, 10);
14175
+ return version2;
14176
+ }
14132
14177
  function runMigrations(db) {
14133
14178
  const currentVersion = db.query("PRAGMA user_version").get();
14134
14179
  let version2 = currentVersion.user_version;
14180
+ const inferred = inferLegacySchemaVersion(db);
14181
+ if (inferred > version2) {
14182
+ db.exec(`PRAGMA user_version = ${inferred}`);
14183
+ version2 = inferred;
14184
+ }
14135
14185
  for (const migration of MIGRATIONS) {
14136
14186
  if (migration.version <= version2)
14137
14187
  continue;
@@ -14211,6 +14261,11 @@ import { createHash as createHash2 } from "node:crypto";
14211
14261
  var IS_BUN = typeof globalThis.Bun !== "undefined";
14212
14262
  function openDatabase(dbPath) {
14213
14263
  if (IS_BUN) {
14264
+ if (process.platform === "darwin") {
14265
+ try {
14266
+ return openNodeDatabase(dbPath);
14267
+ } catch {}
14268
+ }
14214
14269
  return openBunDatabase(dbPath);
14215
14270
  }
14216
14271
  return openNodeDatabase(dbPath);
@@ -17331,18 +17386,44 @@ function getSessionContext(db, input) {
17331
17386
  });
17332
17387
  if (!context)
17333
17388
  return null;
17389
+ const recentRequests = context.recentPrompts?.length ?? 0;
17390
+ const recentTools = context.recentToolEvents?.length ?? 0;
17391
+ const captureState = recentRequests > 0 && recentTools > 0 ? "rich" : recentRequests > 0 || recentTools > 0 ? "partial" : "summary-only";
17392
+ const hotFiles = buildHotFiles(context);
17334
17393
  return {
17335
17394
  project_name: context.project_name,
17336
17395
  canonical_id: context.canonical_id,
17337
17396
  session_count: context.session_count,
17338
17397
  total_active: context.total_active,
17339
- recent_requests: context.recentPrompts?.length ?? 0,
17340
- recent_tools: context.recentToolEvents?.length ?? 0,
17398
+ recent_requests: recentRequests,
17399
+ recent_tools: recentTools,
17341
17400
  recent_sessions: context.recentSessions?.length ?? 0,
17342
- raw_capture_active: (context.recentPrompts?.length ?? 0) > 0 || (context.recentToolEvents?.length ?? 0) > 0,
17401
+ recent_outcomes: context.recentOutcomes ?? [],
17402
+ hot_files: hotFiles,
17403
+ capture_state: captureState,
17404
+ raw_capture_active: recentRequests > 0 || recentTools > 0,
17343
17405
  preview: formatContextForInjection(context)
17344
17406
  };
17345
17407
  }
17408
+ function buildHotFiles(context) {
17409
+ const counts = new Map;
17410
+ for (const obs of context.observations) {
17411
+ for (const path of [...parseJsonArray(obs.files_read), ...parseJsonArray(obs.files_modified)]) {
17412
+ counts.set(path, (counts.get(path) ?? 0) + 1);
17413
+ }
17414
+ }
17415
+ return Array.from(counts.entries()).map(([path, count]) => ({ path, count })).sort((a, b) => b.count - a.count || a.path.localeCompare(b.path)).slice(0, 6);
17416
+ }
17417
+ function parseJsonArray(value) {
17418
+ if (!value)
17419
+ return [];
17420
+ try {
17421
+ const parsed = JSON.parse(value);
17422
+ return Array.isArray(parsed) ? parsed.filter((item) => typeof item === "string" && item.trim().length > 0) : [];
17423
+ } catch {
17424
+ return [];
17425
+ }
17426
+ }
17346
17427
 
17347
17428
  // src/tools/send-message.ts
17348
17429
  async function sendMessage(db, config2, input) {
@@ -17959,8 +18040,13 @@ function buildSummaryVectorDocument(summary, config2, project, observations = []
17959
18040
  next_step_items: extractSectionItems2(summary.next_steps),
17960
18041
  prompt_count: captureContext?.prompt_count ?? 0,
17961
18042
  tool_event_count: captureContext?.tool_event_count ?? 0,
18043
+ capture_state: captureContext?.capture_state ?? "summary-only",
18044
+ recent_request_prompts: captureContext?.recent_request_prompts ?? [],
17962
18045
  latest_request: captureContext?.latest_request ?? null,
17963
18046
  recent_tool_names: captureContext?.recent_tool_names ?? [],
18047
+ recent_tool_commands: captureContext?.recent_tool_commands ?? [],
18048
+ hot_files: captureContext?.hot_files ?? [],
18049
+ recent_outcomes: captureContext?.recent_outcomes ?? [],
17964
18050
  decisions_count: valueSignals.decisions_count,
17965
18051
  lessons_count: valueSignals.lessons_count,
17966
18052
  discoveries_count: valueSignals.discoveries_count,
@@ -18004,7 +18090,7 @@ async function pushOutbox(db, client, config2, batchSize = 50) {
18004
18090
  const doc3 = buildSummaryVectorDocument(summary, config2, {
18005
18091
  canonical_id: project2.canonical_id,
18006
18092
  name: project2.name
18007
- }, summaryObservations, buildSummaryCaptureContext(sessionPrompts, sessionToolEvents));
18093
+ }, summaryObservations, buildSummaryCaptureContext(sessionPrompts, sessionToolEvents, summaryObservations));
18008
18094
  batch.push({ entryId: entry.id, doc: doc3 });
18009
18095
  continue;
18010
18096
  }
@@ -18078,16 +18164,39 @@ function extractSectionItems2(section) {
18078
18164
  return section.split(`
18079
18165
  `).map((line) => line.trim()).filter(Boolean).map((line) => line.replace(/^[-*]\s*/, "").trim()).filter(Boolean).slice(0, 4);
18080
18166
  }
18081
- function buildSummaryCaptureContext(prompts, toolEvents) {
18167
+ function buildSummaryCaptureContext(prompts, toolEvents, observations) {
18082
18168
  const latestRequest = prompts.length > 0 ? prompts[prompts.length - 1]?.prompt ?? null : null;
18169
+ const recentRequestPrompts = prompts.slice(-3).map((prompt) => prompt.prompt.trim()).filter(Boolean);
18083
18170
  const recentToolNames = [...new Set(toolEvents.slice(-8).map((tool) => tool.tool_name).filter(Boolean))];
18171
+ const recentToolCommands = [...new Set(toolEvents.slice(-5).map((tool) => (tool.command ?? tool.file_path ?? "").trim()).filter(Boolean))];
18172
+ const hotFiles = [...new Set(observations.flatMap((obs) => [
18173
+ ...parseJsonArray2(obs.files_modified),
18174
+ ...parseJsonArray2(obs.files_read)
18175
+ ]).filter(Boolean))].slice(0, 6);
18176
+ const recentOutcomes = observations.filter((obs) => ["bugfix", "feature", "refactor", "change", "decision"].includes(obs.type)).map((obs) => obs.title.trim()).filter((title) => title.length > 0).slice(0, 6);
18177
+ const captureState = prompts.length > 0 && toolEvents.length > 0 ? "rich" : prompts.length > 0 || toolEvents.length > 0 ? "partial" : "summary-only";
18084
18178
  return {
18085
18179
  prompt_count: prompts.length,
18086
18180
  tool_event_count: toolEvents.length,
18181
+ recent_request_prompts: recentRequestPrompts,
18087
18182
  latest_request: latestRequest,
18088
- recent_tool_names: recentToolNames
18183
+ recent_tool_names: recentToolNames,
18184
+ recent_tool_commands: recentToolCommands,
18185
+ capture_state: captureState,
18186
+ hot_files: hotFiles,
18187
+ recent_outcomes: recentOutcomes
18089
18188
  };
18090
18189
  }
18190
+ function parseJsonArray2(value) {
18191
+ if (!value)
18192
+ return [];
18193
+ try {
18194
+ const parsed = JSON.parse(value);
18195
+ return Array.isArray(parsed) ? parsed.filter((item) => typeof item === "string" && item.trim().length > 0) : [];
18196
+ } catch {
18197
+ return [];
18198
+ }
18199
+ }
18091
18200
 
18092
18201
  // src/sync/pull.ts
18093
18202
  var PULL_CURSOR_KEY = "pull_cursor";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "engrm",
3
- "version": "0.4.9",
3
+ "version": "0.4.11",
4
4
  "description": "Shared memory across devices, sessions, and coding agents",
5
5
  "mcpName": "io.github.dr12hes/engrm",
6
6
  "type": "module",