engrm 0.3.1 → 0.3.4

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
@@ -548,6 +548,56 @@ function runMigrations(db) {
548
548
  }
549
549
  }
550
550
  }
551
+ function ensureObservationTypes(db) {
552
+ try {
553
+ db.exec("INSERT INTO observations (session_id, project_id, type, title, user_id, device_id, agent, created_at, created_at_epoch) " + "VALUES ('_typecheck', 1, 'message', '_test', '_test', '_test', '_test', '2000-01-01', 0)");
554
+ db.exec("DELETE FROM observations WHERE session_id = '_typecheck'");
555
+ } catch {
556
+ db.exec("BEGIN TRANSACTION");
557
+ try {
558
+ db.exec(`
559
+ CREATE TABLE observations_repair (
560
+ id INTEGER PRIMARY KEY AUTOINCREMENT, session_id TEXT,
561
+ project_id INTEGER NOT NULL REFERENCES projects(id),
562
+ type TEXT NOT NULL CHECK (type IN (
563
+ 'bugfix','discovery','decision','pattern','change','feature',
564
+ 'refactor','digest','standard','message')),
565
+ title TEXT NOT NULL, narrative TEXT, facts TEXT, concepts TEXT,
566
+ files_read TEXT, files_modified TEXT,
567
+ quality REAL DEFAULT 0.5 CHECK (quality BETWEEN 0.0 AND 1.0),
568
+ lifecycle TEXT DEFAULT 'active' CHECK (lifecycle IN ('active','aging','archived','purged','pinned')),
569
+ sensitivity TEXT DEFAULT 'shared' CHECK (sensitivity IN ('shared','personal','secret')),
570
+ user_id TEXT NOT NULL, device_id TEXT NOT NULL, agent TEXT DEFAULT 'claude-code',
571
+ created_at TEXT NOT NULL, created_at_epoch INTEGER NOT NULL,
572
+ archived_at_epoch INTEGER,
573
+ compacted_into INTEGER REFERENCES observations(id) ON DELETE SET NULL,
574
+ superseded_by INTEGER REFERENCES observations(id) ON DELETE SET NULL,
575
+ remote_source_id TEXT
576
+ );
577
+ INSERT INTO observations_repair SELECT * FROM observations;
578
+ DROP TABLE observations;
579
+ ALTER TABLE observations_repair RENAME TO observations;
580
+ CREATE INDEX IF NOT EXISTS idx_observations_project ON observations(project_id);
581
+ CREATE INDEX IF NOT EXISTS idx_observations_type ON observations(type);
582
+ CREATE INDEX IF NOT EXISTS idx_observations_created ON observations(created_at_epoch);
583
+ CREATE INDEX IF NOT EXISTS idx_observations_session ON observations(session_id);
584
+ CREATE INDEX IF NOT EXISTS idx_observations_lifecycle ON observations(lifecycle);
585
+ CREATE INDEX IF NOT EXISTS idx_observations_quality ON observations(quality);
586
+ CREATE INDEX IF NOT EXISTS idx_observations_user ON observations(user_id);
587
+ CREATE INDEX IF NOT EXISTS idx_observations_superseded ON observations(superseded_by);
588
+ CREATE UNIQUE INDEX IF NOT EXISTS idx_observations_remote_source ON observations(remote_source_id) WHERE remote_source_id IS NOT NULL;
589
+ DROP TABLE IF EXISTS observations_fts;
590
+ CREATE VIRTUAL TABLE observations_fts USING fts5(
591
+ title, narrative, facts, concepts, content=observations, content_rowid=id
592
+ );
593
+ INSERT INTO observations_fts(observations_fts) VALUES('rebuild');
594
+ `);
595
+ db.exec("COMMIT");
596
+ } catch (err) {
597
+ db.exec("ROLLBACK");
598
+ }
599
+ }
600
+ }
551
601
  var LATEST_SCHEMA_VERSION = MIGRATIONS.filter((m) => !m.condition).reduce((max, m) => Math.max(max, m.version), 0);
552
602
 
553
603
  // src/storage/sqlite.ts
@@ -614,6 +664,7 @@ class MemDatabase {
614
664
  this.db.exec("PRAGMA foreign_keys = ON");
615
665
  this.vecAvailable = this.loadVecExtension();
616
666
  runMigrations(this.db);
667
+ ensureObservationTypes(this.db);
617
668
  }
618
669
  loadVecExtension() {
619
670
  try {
@@ -1905,7 +1956,8 @@ var VALID_TYPES = [
1905
1956
  "feature",
1906
1957
  "refactor",
1907
1958
  "digest",
1908
- "standard"
1959
+ "standard",
1960
+ "message"
1909
1961
  ];
1910
1962
  async function saveObservation(db, config, input) {
1911
1963
  if (!VALID_TYPES.includes(input.type)) {
@@ -2166,6 +2218,7 @@ switch (command) {
2166
2218
  printUsage();
2167
2219
  break;
2168
2220
  }
2221
+ process.exit(0);
2169
2222
  async function handleInit(flags) {
2170
2223
  const tokenFlag = flags.find((f) => f.startsWith("--token"));
2171
2224
  if (tokenFlag) {
@@ -514,6 +514,56 @@ function runMigrations(db) {
514
514
  }
515
515
  }
516
516
  }
517
+ function ensureObservationTypes(db) {
518
+ try {
519
+ db.exec("INSERT INTO observations (session_id, project_id, type, title, user_id, device_id, agent, created_at, created_at_epoch) " + "VALUES ('_typecheck', 1, 'message', '_test', '_test', '_test', '_test', '2000-01-01', 0)");
520
+ db.exec("DELETE FROM observations WHERE session_id = '_typecheck'");
521
+ } catch {
522
+ db.exec("BEGIN TRANSACTION");
523
+ try {
524
+ db.exec(`
525
+ CREATE TABLE observations_repair (
526
+ id INTEGER PRIMARY KEY AUTOINCREMENT, session_id TEXT,
527
+ project_id INTEGER NOT NULL REFERENCES projects(id),
528
+ type TEXT NOT NULL CHECK (type IN (
529
+ 'bugfix','discovery','decision','pattern','change','feature',
530
+ 'refactor','digest','standard','message')),
531
+ title TEXT NOT NULL, narrative TEXT, facts TEXT, concepts TEXT,
532
+ files_read TEXT, files_modified TEXT,
533
+ quality REAL DEFAULT 0.5 CHECK (quality BETWEEN 0.0 AND 1.0),
534
+ lifecycle TEXT DEFAULT 'active' CHECK (lifecycle IN ('active','aging','archived','purged','pinned')),
535
+ sensitivity TEXT DEFAULT 'shared' CHECK (sensitivity IN ('shared','personal','secret')),
536
+ user_id TEXT NOT NULL, device_id TEXT NOT NULL, agent TEXT DEFAULT 'claude-code',
537
+ created_at TEXT NOT NULL, created_at_epoch INTEGER NOT NULL,
538
+ archived_at_epoch INTEGER,
539
+ compacted_into INTEGER REFERENCES observations(id) ON DELETE SET NULL,
540
+ superseded_by INTEGER REFERENCES observations(id) ON DELETE SET NULL,
541
+ remote_source_id TEXT
542
+ );
543
+ INSERT INTO observations_repair SELECT * FROM observations;
544
+ DROP TABLE observations;
545
+ ALTER TABLE observations_repair RENAME TO observations;
546
+ CREATE INDEX IF NOT EXISTS idx_observations_project ON observations(project_id);
547
+ CREATE INDEX IF NOT EXISTS idx_observations_type ON observations(type);
548
+ CREATE INDEX IF NOT EXISTS idx_observations_created ON observations(created_at_epoch);
549
+ CREATE INDEX IF NOT EXISTS idx_observations_session ON observations(session_id);
550
+ CREATE INDEX IF NOT EXISTS idx_observations_lifecycle ON observations(lifecycle);
551
+ CREATE INDEX IF NOT EXISTS idx_observations_quality ON observations(quality);
552
+ CREATE INDEX IF NOT EXISTS idx_observations_user ON observations(user_id);
553
+ CREATE INDEX IF NOT EXISTS idx_observations_superseded ON observations(superseded_by);
554
+ CREATE UNIQUE INDEX IF NOT EXISTS idx_observations_remote_source ON observations(remote_source_id) WHERE remote_source_id IS NOT NULL;
555
+ DROP TABLE IF EXISTS observations_fts;
556
+ CREATE VIRTUAL TABLE observations_fts USING fts5(
557
+ title, narrative, facts, concepts, content=observations, content_rowid=id
558
+ );
559
+ INSERT INTO observations_fts(observations_fts) VALUES('rebuild');
560
+ `);
561
+ db.exec("COMMIT");
562
+ } catch (err) {
563
+ db.exec("ROLLBACK");
564
+ }
565
+ }
566
+ }
517
567
  var LATEST_SCHEMA_VERSION = MIGRATIONS.filter((m) => !m.condition).reduce((max, m) => Math.max(max, m.version), 0);
518
568
 
519
569
  // src/storage/sqlite.ts
@@ -580,6 +630,7 @@ class MemDatabase {
580
630
  this.db.exec("PRAGMA foreign_keys = ON");
581
631
  this.vecAvailable = this.loadVecExtension();
582
632
  runMigrations(this.db);
633
+ ensureObservationTypes(this.db);
583
634
  }
584
635
  loadVecExtension() {
585
636
  try {
@@ -1537,7 +1588,8 @@ var VALID_TYPES = [
1537
1588
  "feature",
1538
1589
  "refactor",
1539
1590
  "digest",
1540
- "standard"
1591
+ "standard",
1592
+ "message"
1541
1593
  ];
1542
1594
  async function saveObservation(db, config, input) {
1543
1595
  if (!VALID_TYPES.includes(input.type)) {
@@ -514,6 +514,56 @@ function runMigrations(db) {
514
514
  }
515
515
  }
516
516
  }
517
+ function ensureObservationTypes(db) {
518
+ try {
519
+ db.exec("INSERT INTO observations (session_id, project_id, type, title, user_id, device_id, agent, created_at, created_at_epoch) " + "VALUES ('_typecheck', 1, 'message', '_test', '_test', '_test', '_test', '2000-01-01', 0)");
520
+ db.exec("DELETE FROM observations WHERE session_id = '_typecheck'");
521
+ } catch {
522
+ db.exec("BEGIN TRANSACTION");
523
+ try {
524
+ db.exec(`
525
+ CREATE TABLE observations_repair (
526
+ id INTEGER PRIMARY KEY AUTOINCREMENT, session_id TEXT,
527
+ project_id INTEGER NOT NULL REFERENCES projects(id),
528
+ type TEXT NOT NULL CHECK (type IN (
529
+ 'bugfix','discovery','decision','pattern','change','feature',
530
+ 'refactor','digest','standard','message')),
531
+ title TEXT NOT NULL, narrative TEXT, facts TEXT, concepts TEXT,
532
+ files_read TEXT, files_modified TEXT,
533
+ quality REAL DEFAULT 0.5 CHECK (quality BETWEEN 0.0 AND 1.0),
534
+ lifecycle TEXT DEFAULT 'active' CHECK (lifecycle IN ('active','aging','archived','purged','pinned')),
535
+ sensitivity TEXT DEFAULT 'shared' CHECK (sensitivity IN ('shared','personal','secret')),
536
+ user_id TEXT NOT NULL, device_id TEXT NOT NULL, agent TEXT DEFAULT 'claude-code',
537
+ created_at TEXT NOT NULL, created_at_epoch INTEGER NOT NULL,
538
+ archived_at_epoch INTEGER,
539
+ compacted_into INTEGER REFERENCES observations(id) ON DELETE SET NULL,
540
+ superseded_by INTEGER REFERENCES observations(id) ON DELETE SET NULL,
541
+ remote_source_id TEXT
542
+ );
543
+ INSERT INTO observations_repair SELECT * FROM observations;
544
+ DROP TABLE observations;
545
+ ALTER TABLE observations_repair RENAME TO observations;
546
+ CREATE INDEX IF NOT EXISTS idx_observations_project ON observations(project_id);
547
+ CREATE INDEX IF NOT EXISTS idx_observations_type ON observations(type);
548
+ CREATE INDEX IF NOT EXISTS idx_observations_created ON observations(created_at_epoch);
549
+ CREATE INDEX IF NOT EXISTS idx_observations_session ON observations(session_id);
550
+ CREATE INDEX IF NOT EXISTS idx_observations_lifecycle ON observations(lifecycle);
551
+ CREATE INDEX IF NOT EXISTS idx_observations_quality ON observations(quality);
552
+ CREATE INDEX IF NOT EXISTS idx_observations_user ON observations(user_id);
553
+ CREATE INDEX IF NOT EXISTS idx_observations_superseded ON observations(superseded_by);
554
+ CREATE UNIQUE INDEX IF NOT EXISTS idx_observations_remote_source ON observations(remote_source_id) WHERE remote_source_id IS NOT NULL;
555
+ DROP TABLE IF EXISTS observations_fts;
556
+ CREATE VIRTUAL TABLE observations_fts USING fts5(
557
+ title, narrative, facts, concepts, content=observations, content_rowid=id
558
+ );
559
+ INSERT INTO observations_fts(observations_fts) VALUES('rebuild');
560
+ `);
561
+ db.exec("COMMIT");
562
+ } catch (err) {
563
+ db.exec("ROLLBACK");
564
+ }
565
+ }
566
+ }
517
567
  var LATEST_SCHEMA_VERSION = MIGRATIONS.filter((m) => !m.condition).reduce((max, m) => Math.max(max, m.version), 0);
518
568
 
519
569
  // src/storage/sqlite.ts
@@ -580,6 +630,7 @@ class MemDatabase {
580
630
  this.db.exec("PRAGMA foreign_keys = ON");
581
631
  this.vecAvailable = this.loadVecExtension();
582
632
  runMigrations(this.db);
633
+ ensureObservationTypes(this.db);
583
634
  }
584
635
  loadVecExtension() {
585
636
  try {
@@ -1743,7 +1794,8 @@ var VALID_TYPES = [
1743
1794
  "feature",
1744
1795
  "refactor",
1745
1796
  "digest",
1746
- "standard"
1797
+ "standard",
1798
+ "message"
1747
1799
  ];
1748
1800
  async function saveObservation(db, config, input) {
1749
1801
  if (!VALID_TYPES.includes(input.type)) {
@@ -514,6 +514,56 @@ function runMigrations(db) {
514
514
  }
515
515
  }
516
516
  }
517
+ function ensureObservationTypes(db) {
518
+ try {
519
+ db.exec("INSERT INTO observations (session_id, project_id, type, title, user_id, device_id, agent, created_at, created_at_epoch) " + "VALUES ('_typecheck', 1, 'message', '_test', '_test', '_test', '_test', '2000-01-01', 0)");
520
+ db.exec("DELETE FROM observations WHERE session_id = '_typecheck'");
521
+ } catch {
522
+ db.exec("BEGIN TRANSACTION");
523
+ try {
524
+ db.exec(`
525
+ CREATE TABLE observations_repair (
526
+ id INTEGER PRIMARY KEY AUTOINCREMENT, session_id TEXT,
527
+ project_id INTEGER NOT NULL REFERENCES projects(id),
528
+ type TEXT NOT NULL CHECK (type IN (
529
+ 'bugfix','discovery','decision','pattern','change','feature',
530
+ 'refactor','digest','standard','message')),
531
+ title TEXT NOT NULL, narrative TEXT, facts TEXT, concepts TEXT,
532
+ files_read TEXT, files_modified TEXT,
533
+ quality REAL DEFAULT 0.5 CHECK (quality BETWEEN 0.0 AND 1.0),
534
+ lifecycle TEXT DEFAULT 'active' CHECK (lifecycle IN ('active','aging','archived','purged','pinned')),
535
+ sensitivity TEXT DEFAULT 'shared' CHECK (sensitivity IN ('shared','personal','secret')),
536
+ user_id TEXT NOT NULL, device_id TEXT NOT NULL, agent TEXT DEFAULT 'claude-code',
537
+ created_at TEXT NOT NULL, created_at_epoch INTEGER NOT NULL,
538
+ archived_at_epoch INTEGER,
539
+ compacted_into INTEGER REFERENCES observations(id) ON DELETE SET NULL,
540
+ superseded_by INTEGER REFERENCES observations(id) ON DELETE SET NULL,
541
+ remote_source_id TEXT
542
+ );
543
+ INSERT INTO observations_repair SELECT * FROM observations;
544
+ DROP TABLE observations;
545
+ ALTER TABLE observations_repair RENAME TO observations;
546
+ CREATE INDEX IF NOT EXISTS idx_observations_project ON observations(project_id);
547
+ CREATE INDEX IF NOT EXISTS idx_observations_type ON observations(type);
548
+ CREATE INDEX IF NOT EXISTS idx_observations_created ON observations(created_at_epoch);
549
+ CREATE INDEX IF NOT EXISTS idx_observations_session ON observations(session_id);
550
+ CREATE INDEX IF NOT EXISTS idx_observations_lifecycle ON observations(lifecycle);
551
+ CREATE INDEX IF NOT EXISTS idx_observations_quality ON observations(quality);
552
+ CREATE INDEX IF NOT EXISTS idx_observations_user ON observations(user_id);
553
+ CREATE INDEX IF NOT EXISTS idx_observations_superseded ON observations(superseded_by);
554
+ CREATE UNIQUE INDEX IF NOT EXISTS idx_observations_remote_source ON observations(remote_source_id) WHERE remote_source_id IS NOT NULL;
555
+ DROP TABLE IF EXISTS observations_fts;
556
+ CREATE VIRTUAL TABLE observations_fts USING fts5(
557
+ title, narrative, facts, concepts, content=observations, content_rowid=id
558
+ );
559
+ INSERT INTO observations_fts(observations_fts) VALUES('rebuild');
560
+ `);
561
+ db.exec("COMMIT");
562
+ } catch (err) {
563
+ db.exec("ROLLBACK");
564
+ }
565
+ }
566
+ }
517
567
  var LATEST_SCHEMA_VERSION = MIGRATIONS.filter((m) => !m.condition).reduce((max, m) => Math.max(max, m.version), 0);
518
568
 
519
569
  // src/storage/sqlite.ts
@@ -580,6 +630,7 @@ class MemDatabase {
580
630
  this.db.exec("PRAGMA foreign_keys = ON");
581
631
  this.vecAvailable = this.loadVecExtension();
582
632
  runMigrations(this.db);
633
+ ensureObservationTypes(this.db);
583
634
  }
584
635
  loadVecExtension() {
585
636
  try {
@@ -514,6 +514,56 @@ function runMigrations(db) {
514
514
  }
515
515
  }
516
516
  }
517
+ function ensureObservationTypes(db) {
518
+ try {
519
+ db.exec("INSERT INTO observations (session_id, project_id, type, title, user_id, device_id, agent, created_at, created_at_epoch) " + "VALUES ('_typecheck', 1, 'message', '_test', '_test', '_test', '_test', '2000-01-01', 0)");
520
+ db.exec("DELETE FROM observations WHERE session_id = '_typecheck'");
521
+ } catch {
522
+ db.exec("BEGIN TRANSACTION");
523
+ try {
524
+ db.exec(`
525
+ CREATE TABLE observations_repair (
526
+ id INTEGER PRIMARY KEY AUTOINCREMENT, session_id TEXT,
527
+ project_id INTEGER NOT NULL REFERENCES projects(id),
528
+ type TEXT NOT NULL CHECK (type IN (
529
+ 'bugfix','discovery','decision','pattern','change','feature',
530
+ 'refactor','digest','standard','message')),
531
+ title TEXT NOT NULL, narrative TEXT, facts TEXT, concepts TEXT,
532
+ files_read TEXT, files_modified TEXT,
533
+ quality REAL DEFAULT 0.5 CHECK (quality BETWEEN 0.0 AND 1.0),
534
+ lifecycle TEXT DEFAULT 'active' CHECK (lifecycle IN ('active','aging','archived','purged','pinned')),
535
+ sensitivity TEXT DEFAULT 'shared' CHECK (sensitivity IN ('shared','personal','secret')),
536
+ user_id TEXT NOT NULL, device_id TEXT NOT NULL, agent TEXT DEFAULT 'claude-code',
537
+ created_at TEXT NOT NULL, created_at_epoch INTEGER NOT NULL,
538
+ archived_at_epoch INTEGER,
539
+ compacted_into INTEGER REFERENCES observations(id) ON DELETE SET NULL,
540
+ superseded_by INTEGER REFERENCES observations(id) ON DELETE SET NULL,
541
+ remote_source_id TEXT
542
+ );
543
+ INSERT INTO observations_repair SELECT * FROM observations;
544
+ DROP TABLE observations;
545
+ ALTER TABLE observations_repair RENAME TO observations;
546
+ CREATE INDEX IF NOT EXISTS idx_observations_project ON observations(project_id);
547
+ CREATE INDEX IF NOT EXISTS idx_observations_type ON observations(type);
548
+ CREATE INDEX IF NOT EXISTS idx_observations_created ON observations(created_at_epoch);
549
+ CREATE INDEX IF NOT EXISTS idx_observations_session ON observations(session_id);
550
+ CREATE INDEX IF NOT EXISTS idx_observations_lifecycle ON observations(lifecycle);
551
+ CREATE INDEX IF NOT EXISTS idx_observations_quality ON observations(quality);
552
+ CREATE INDEX IF NOT EXISTS idx_observations_user ON observations(user_id);
553
+ CREATE INDEX IF NOT EXISTS idx_observations_superseded ON observations(superseded_by);
554
+ CREATE UNIQUE INDEX IF NOT EXISTS idx_observations_remote_source ON observations(remote_source_id) WHERE remote_source_id IS NOT NULL;
555
+ DROP TABLE IF EXISTS observations_fts;
556
+ CREATE VIRTUAL TABLE observations_fts USING fts5(
557
+ title, narrative, facts, concepts, content=observations, content_rowid=id
558
+ );
559
+ INSERT INTO observations_fts(observations_fts) VALUES('rebuild');
560
+ `);
561
+ db.exec("COMMIT");
562
+ } catch (err) {
563
+ db.exec("ROLLBACK");
564
+ }
565
+ }
566
+ }
517
567
  var LATEST_SCHEMA_VERSION = MIGRATIONS.filter((m) => !m.condition).reduce((max, m) => Math.max(max, m.version), 0);
518
568
 
519
569
  // src/storage/sqlite.ts
@@ -580,6 +630,7 @@ class MemDatabase {
580
630
  this.db.exec("PRAGMA foreign_keys = ON");
581
631
  this.vecAvailable = this.loadVecExtension();
582
632
  runMigrations(this.db);
633
+ ensureObservationTypes(this.db);
583
634
  }
584
635
  loadVecExtension() {
585
636
  try {
@@ -1165,6 +1216,13 @@ async function main() {
1165
1216
  }
1166
1217
  try {
1167
1218
  const filePath = String(event.tool_input["file_path"] ?? "unknown");
1219
+ const defaultSkips = [/migrations?\./, /\.test\./, /\.spec\./, /\.lock$/, /package\.json$/];
1220
+ const customSkips = (config.sentinel.skip_patterns || []).map((p) => new RegExp(p));
1221
+ const allSkips = [...defaultSkips, ...customSkips];
1222
+ if (allSkips.some((re) => re.test(filePath))) {
1223
+ db.close();
1224
+ process.exit(0);
1225
+ }
1168
1226
  const content = event.tool_name === "Write" ? String(event.tool_input["content"] ?? "") : String(event.tool_input["new_string"] ?? "");
1169
1227
  const result = await auditCodeChange(config, db, event.tool_name, filePath, content);
1170
1228
  if (result.verdict === "PASS") {
@@ -515,6 +515,56 @@ function runMigrations(db) {
515
515
  }
516
516
  }
517
517
  }
518
+ function ensureObservationTypes(db) {
519
+ try {
520
+ db.exec("INSERT INTO observations (session_id, project_id, type, title, user_id, device_id, agent, created_at, created_at_epoch) " + "VALUES ('_typecheck', 1, 'message', '_test', '_test', '_test', '_test', '2000-01-01', 0)");
521
+ db.exec("DELETE FROM observations WHERE session_id = '_typecheck'");
522
+ } catch {
523
+ db.exec("BEGIN TRANSACTION");
524
+ try {
525
+ db.exec(`
526
+ CREATE TABLE observations_repair (
527
+ id INTEGER PRIMARY KEY AUTOINCREMENT, session_id TEXT,
528
+ project_id INTEGER NOT NULL REFERENCES projects(id),
529
+ type TEXT NOT NULL CHECK (type IN (
530
+ 'bugfix','discovery','decision','pattern','change','feature',
531
+ 'refactor','digest','standard','message')),
532
+ title TEXT NOT NULL, narrative TEXT, facts TEXT, concepts TEXT,
533
+ files_read TEXT, files_modified TEXT,
534
+ quality REAL DEFAULT 0.5 CHECK (quality BETWEEN 0.0 AND 1.0),
535
+ lifecycle TEXT DEFAULT 'active' CHECK (lifecycle IN ('active','aging','archived','purged','pinned')),
536
+ sensitivity TEXT DEFAULT 'shared' CHECK (sensitivity IN ('shared','personal','secret')),
537
+ user_id TEXT NOT NULL, device_id TEXT NOT NULL, agent TEXT DEFAULT 'claude-code',
538
+ created_at TEXT NOT NULL, created_at_epoch INTEGER NOT NULL,
539
+ archived_at_epoch INTEGER,
540
+ compacted_into INTEGER REFERENCES observations(id) ON DELETE SET NULL,
541
+ superseded_by INTEGER REFERENCES observations(id) ON DELETE SET NULL,
542
+ remote_source_id TEXT
543
+ );
544
+ INSERT INTO observations_repair SELECT * FROM observations;
545
+ DROP TABLE observations;
546
+ ALTER TABLE observations_repair RENAME TO observations;
547
+ CREATE INDEX IF NOT EXISTS idx_observations_project ON observations(project_id);
548
+ CREATE INDEX IF NOT EXISTS idx_observations_type ON observations(type);
549
+ CREATE INDEX IF NOT EXISTS idx_observations_created ON observations(created_at_epoch);
550
+ CREATE INDEX IF NOT EXISTS idx_observations_session ON observations(session_id);
551
+ CREATE INDEX IF NOT EXISTS idx_observations_lifecycle ON observations(lifecycle);
552
+ CREATE INDEX IF NOT EXISTS idx_observations_quality ON observations(quality);
553
+ CREATE INDEX IF NOT EXISTS idx_observations_user ON observations(user_id);
554
+ CREATE INDEX IF NOT EXISTS idx_observations_superseded ON observations(superseded_by);
555
+ CREATE UNIQUE INDEX IF NOT EXISTS idx_observations_remote_source ON observations(remote_source_id) WHERE remote_source_id IS NOT NULL;
556
+ DROP TABLE IF EXISTS observations_fts;
557
+ CREATE VIRTUAL TABLE observations_fts USING fts5(
558
+ title, narrative, facts, concepts, content=observations, content_rowid=id
559
+ );
560
+ INSERT INTO observations_fts(observations_fts) VALUES('rebuild');
561
+ `);
562
+ db.exec("COMMIT");
563
+ } catch (err) {
564
+ db.exec("ROLLBACK");
565
+ }
566
+ }
567
+ }
518
568
  var LATEST_SCHEMA_VERSION = MIGRATIONS.filter((m) => !m.condition).reduce((max, m) => Math.max(max, m.version), 0);
519
569
 
520
570
  // src/storage/sqlite.ts
@@ -581,6 +631,7 @@ class MemDatabase {
581
631
  this.db.exec("PRAGMA foreign_keys = ON");
582
632
  this.vecAvailable = this.loadVecExtension();
583
633
  runMigrations(this.db);
634
+ ensureObservationTypes(this.db);
584
635
  }
585
636
  loadVecExtension() {
586
637
  try {
@@ -514,6 +514,56 @@ function runMigrations(db) {
514
514
  }
515
515
  }
516
516
  }
517
+ function ensureObservationTypes(db) {
518
+ try {
519
+ db.exec("INSERT INTO observations (session_id, project_id, type, title, user_id, device_id, agent, created_at, created_at_epoch) " + "VALUES ('_typecheck', 1, 'message', '_test', '_test', '_test', '_test', '2000-01-01', 0)");
520
+ db.exec("DELETE FROM observations WHERE session_id = '_typecheck'");
521
+ } catch {
522
+ db.exec("BEGIN TRANSACTION");
523
+ try {
524
+ db.exec(`
525
+ CREATE TABLE observations_repair (
526
+ id INTEGER PRIMARY KEY AUTOINCREMENT, session_id TEXT,
527
+ project_id INTEGER NOT NULL REFERENCES projects(id),
528
+ type TEXT NOT NULL CHECK (type IN (
529
+ 'bugfix','discovery','decision','pattern','change','feature',
530
+ 'refactor','digest','standard','message')),
531
+ title TEXT NOT NULL, narrative TEXT, facts TEXT, concepts TEXT,
532
+ files_read TEXT, files_modified TEXT,
533
+ quality REAL DEFAULT 0.5 CHECK (quality BETWEEN 0.0 AND 1.0),
534
+ lifecycle TEXT DEFAULT 'active' CHECK (lifecycle IN ('active','aging','archived','purged','pinned')),
535
+ sensitivity TEXT DEFAULT 'shared' CHECK (sensitivity IN ('shared','personal','secret')),
536
+ user_id TEXT NOT NULL, device_id TEXT NOT NULL, agent TEXT DEFAULT 'claude-code',
537
+ created_at TEXT NOT NULL, created_at_epoch INTEGER NOT NULL,
538
+ archived_at_epoch INTEGER,
539
+ compacted_into INTEGER REFERENCES observations(id) ON DELETE SET NULL,
540
+ superseded_by INTEGER REFERENCES observations(id) ON DELETE SET NULL,
541
+ remote_source_id TEXT
542
+ );
543
+ INSERT INTO observations_repair SELECT * FROM observations;
544
+ DROP TABLE observations;
545
+ ALTER TABLE observations_repair RENAME TO observations;
546
+ CREATE INDEX IF NOT EXISTS idx_observations_project ON observations(project_id);
547
+ CREATE INDEX IF NOT EXISTS idx_observations_type ON observations(type);
548
+ CREATE INDEX IF NOT EXISTS idx_observations_created ON observations(created_at_epoch);
549
+ CREATE INDEX IF NOT EXISTS idx_observations_session ON observations(session_id);
550
+ CREATE INDEX IF NOT EXISTS idx_observations_lifecycle ON observations(lifecycle);
551
+ CREATE INDEX IF NOT EXISTS idx_observations_quality ON observations(quality);
552
+ CREATE INDEX IF NOT EXISTS idx_observations_user ON observations(user_id);
553
+ CREATE INDEX IF NOT EXISTS idx_observations_superseded ON observations(superseded_by);
554
+ CREATE UNIQUE INDEX IF NOT EXISTS idx_observations_remote_source ON observations(remote_source_id) WHERE remote_source_id IS NOT NULL;
555
+ DROP TABLE IF EXISTS observations_fts;
556
+ CREATE VIRTUAL TABLE observations_fts USING fts5(
557
+ title, narrative, facts, concepts, content=observations, content_rowid=id
558
+ );
559
+ INSERT INTO observations_fts(observations_fts) VALUES('rebuild');
560
+ `);
561
+ db.exec("COMMIT");
562
+ } catch (err) {
563
+ db.exec("ROLLBACK");
564
+ }
565
+ }
566
+ }
517
567
  var LATEST_SCHEMA_VERSION = MIGRATIONS.filter((m) => !m.condition).reduce((max, m) => Math.max(max, m.version), 0);
518
568
 
519
569
  // src/storage/sqlite.ts
@@ -580,6 +630,7 @@ class MemDatabase {
580
630
  this.db.exec("PRAGMA foreign_keys = ON");
581
631
  this.vecAvailable = this.loadVecExtension();
582
632
  runMigrations(this.db);
633
+ ensureObservationTypes(this.db);
583
634
  }
584
635
  loadVecExtension() {
585
636
  try {
package/dist/server.js CHANGED
@@ -14064,6 +14064,56 @@ function runMigrations(db) {
14064
14064
  }
14065
14065
  }
14066
14066
  }
14067
+ function ensureObservationTypes(db) {
14068
+ try {
14069
+ db.exec("INSERT INTO observations (session_id, project_id, type, title, user_id, device_id, agent, created_at, created_at_epoch) " + "VALUES ('_typecheck', 1, 'message', '_test', '_test', '_test', '_test', '2000-01-01', 0)");
14070
+ db.exec("DELETE FROM observations WHERE session_id = '_typecheck'");
14071
+ } catch {
14072
+ db.exec("BEGIN TRANSACTION");
14073
+ try {
14074
+ db.exec(`
14075
+ CREATE TABLE observations_repair (
14076
+ id INTEGER PRIMARY KEY AUTOINCREMENT, session_id TEXT,
14077
+ project_id INTEGER NOT NULL REFERENCES projects(id),
14078
+ type TEXT NOT NULL CHECK (type IN (
14079
+ 'bugfix','discovery','decision','pattern','change','feature',
14080
+ 'refactor','digest','standard','message')),
14081
+ title TEXT NOT NULL, narrative TEXT, facts TEXT, concepts TEXT,
14082
+ files_read TEXT, files_modified TEXT,
14083
+ quality REAL DEFAULT 0.5 CHECK (quality BETWEEN 0.0 AND 1.0),
14084
+ lifecycle TEXT DEFAULT 'active' CHECK (lifecycle IN ('active','aging','archived','purged','pinned')),
14085
+ sensitivity TEXT DEFAULT 'shared' CHECK (sensitivity IN ('shared','personal','secret')),
14086
+ user_id TEXT NOT NULL, device_id TEXT NOT NULL, agent TEXT DEFAULT 'claude-code',
14087
+ created_at TEXT NOT NULL, created_at_epoch INTEGER NOT NULL,
14088
+ archived_at_epoch INTEGER,
14089
+ compacted_into INTEGER REFERENCES observations(id) ON DELETE SET NULL,
14090
+ superseded_by INTEGER REFERENCES observations(id) ON DELETE SET NULL,
14091
+ remote_source_id TEXT
14092
+ );
14093
+ INSERT INTO observations_repair SELECT * FROM observations;
14094
+ DROP TABLE observations;
14095
+ ALTER TABLE observations_repair RENAME TO observations;
14096
+ CREATE INDEX IF NOT EXISTS idx_observations_project ON observations(project_id);
14097
+ CREATE INDEX IF NOT EXISTS idx_observations_type ON observations(type);
14098
+ CREATE INDEX IF NOT EXISTS idx_observations_created ON observations(created_at_epoch);
14099
+ CREATE INDEX IF NOT EXISTS idx_observations_session ON observations(session_id);
14100
+ CREATE INDEX IF NOT EXISTS idx_observations_lifecycle ON observations(lifecycle);
14101
+ CREATE INDEX IF NOT EXISTS idx_observations_quality ON observations(quality);
14102
+ CREATE INDEX IF NOT EXISTS idx_observations_user ON observations(user_id);
14103
+ CREATE INDEX IF NOT EXISTS idx_observations_superseded ON observations(superseded_by);
14104
+ CREATE UNIQUE INDEX IF NOT EXISTS idx_observations_remote_source ON observations(remote_source_id) WHERE remote_source_id IS NOT NULL;
14105
+ DROP TABLE IF EXISTS observations_fts;
14106
+ CREATE VIRTUAL TABLE observations_fts USING fts5(
14107
+ title, narrative, facts, concepts, content=observations, content_rowid=id
14108
+ );
14109
+ INSERT INTO observations_fts(observations_fts) VALUES('rebuild');
14110
+ `);
14111
+ db.exec("COMMIT");
14112
+ } catch (err) {
14113
+ db.exec("ROLLBACK");
14114
+ }
14115
+ }
14116
+ }
14067
14117
  var LATEST_SCHEMA_VERSION = MIGRATIONS.filter((m) => !m.condition).reduce((max, m) => Math.max(max, m.version), 0);
14068
14118
 
14069
14119
  // src/storage/sqlite.ts
@@ -14130,6 +14180,7 @@ class MemDatabase {
14130
14180
  this.db.exec("PRAGMA foreign_keys = ON");
14131
14181
  this.vecAvailable = this.loadVecExtension();
14132
14182
  runMigrations(this.db);
14183
+ ensureObservationTypes(this.db);
14133
14184
  }
14134
14185
  loadVecExtension() {
14135
14186
  try {
@@ -15098,7 +15149,8 @@ var VALID_TYPES = [
15098
15149
  "feature",
15099
15150
  "refactor",
15100
15151
  "digest",
15101
- "standard"
15152
+ "standard",
15153
+ "message"
15102
15154
  ];
15103
15155
  async function saveObservation(db, config2, input) {
15104
15156
  if (!VALID_TYPES.includes(input.type)) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "engrm",
3
- "version": "0.3.1",
3
+ "version": "0.3.4",
4
4
  "description": "Cross-device, team-shared memory layer for AI coding agents",
5
5
  "type": "module",
6
6
  "main": "dist/server.js",