engrm 0.2.3 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli.js +78 -0
- package/dist/hooks/elicitation-result.js +57 -0
- package/dist/hooks/post-tool-use.js +57 -0
- package/dist/hooks/pre-compact.js +57 -0
- package/dist/hooks/sentinel.js +57 -0
- package/dist/hooks/session-start.js +65 -0
- package/dist/hooks/stop.js +57 -0
- package/dist/server.js +105 -1
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -460,6 +460,63 @@ var MIGRATIONS = [
|
|
|
460
460
|
observation_count INTEGER DEFAULT 0
|
|
461
461
|
);
|
|
462
462
|
`
|
|
463
|
+
},
|
|
464
|
+
{
|
|
465
|
+
version: 8,
|
|
466
|
+
description: "Add message type to observations CHECK constraint",
|
|
467
|
+
sql: `
|
|
468
|
+
CREATE TABLE IF NOT EXISTS observations_v8 (
|
|
469
|
+
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
470
|
+
session_id TEXT,
|
|
471
|
+
project_id INTEGER NOT NULL REFERENCES projects(id),
|
|
472
|
+
type TEXT NOT NULL CHECK (type IN (
|
|
473
|
+
'bugfix', 'discovery', 'decision', 'pattern',
|
|
474
|
+
'change', 'feature', 'refactor', 'digest', 'standard', 'message'
|
|
475
|
+
)),
|
|
476
|
+
title TEXT NOT NULL,
|
|
477
|
+
narrative TEXT,
|
|
478
|
+
facts TEXT,
|
|
479
|
+
concepts TEXT,
|
|
480
|
+
files_read TEXT,
|
|
481
|
+
files_modified TEXT,
|
|
482
|
+
quality REAL DEFAULT 0.5 CHECK (quality BETWEEN 0.0 AND 1.0),
|
|
483
|
+
lifecycle TEXT DEFAULT 'active' CHECK (lifecycle IN (
|
|
484
|
+
'active', 'aging', 'archived', 'purged', 'pinned'
|
|
485
|
+
)),
|
|
486
|
+
sensitivity TEXT DEFAULT 'shared' CHECK (sensitivity IN (
|
|
487
|
+
'shared', 'personal', 'secret'
|
|
488
|
+
)),
|
|
489
|
+
user_id TEXT NOT NULL,
|
|
490
|
+
device_id TEXT NOT NULL,
|
|
491
|
+
agent TEXT DEFAULT 'claude-code',
|
|
492
|
+
created_at TEXT NOT NULL,
|
|
493
|
+
created_at_epoch INTEGER NOT NULL,
|
|
494
|
+
archived_at_epoch INTEGER,
|
|
495
|
+
compacted_into INTEGER REFERENCES observations(id) ON DELETE SET NULL,
|
|
496
|
+
superseded_by INTEGER REFERENCES observations(id) ON DELETE SET NULL,
|
|
497
|
+
remote_source_id TEXT
|
|
498
|
+
);
|
|
499
|
+
INSERT INTO observations_v8 SELECT * FROM observations;
|
|
500
|
+
DROP TABLE observations;
|
|
501
|
+
ALTER TABLE observations_v8 RENAME TO observations;
|
|
502
|
+
CREATE INDEX idx_observations_project ON observations(project_id);
|
|
503
|
+
CREATE INDEX idx_observations_project_lifecycle ON observations(project_id, lifecycle);
|
|
504
|
+
CREATE INDEX idx_observations_type ON observations(type);
|
|
505
|
+
CREATE INDEX idx_observations_created ON observations(created_at_epoch);
|
|
506
|
+
CREATE INDEX idx_observations_session ON observations(session_id);
|
|
507
|
+
CREATE INDEX idx_observations_lifecycle ON observations(lifecycle);
|
|
508
|
+
CREATE INDEX idx_observations_quality ON observations(quality);
|
|
509
|
+
CREATE INDEX idx_observations_user ON observations(user_id);
|
|
510
|
+
CREATE INDEX idx_observations_superseded ON observations(superseded_by);
|
|
511
|
+
CREATE UNIQUE INDEX idx_observations_remote_source ON observations(remote_source_id) WHERE remote_source_id IS NOT NULL;
|
|
512
|
+
DROP TABLE IF EXISTS observations_fts;
|
|
513
|
+
CREATE VIRTUAL TABLE observations_fts USING fts5(
|
|
514
|
+
title, narrative, facts, concepts,
|
|
515
|
+
content=observations,
|
|
516
|
+
content_rowid=id
|
|
517
|
+
);
|
|
518
|
+
INSERT INTO observations_fts(observations_fts) VALUES('rebuild');
|
|
519
|
+
`
|
|
463
520
|
}
|
|
464
521
|
];
|
|
465
522
|
function isVecExtensionLoaded(db) {
|
|
@@ -2093,6 +2150,9 @@ switch (command) {
|
|
|
2093
2150
|
case "status":
|
|
2094
2151
|
handleStatus();
|
|
2095
2152
|
break;
|
|
2153
|
+
case "update":
|
|
2154
|
+
handleUpdate();
|
|
2155
|
+
break;
|
|
2096
2156
|
case "install-pack":
|
|
2097
2157
|
await handleInstallPack(args.slice(1));
|
|
2098
2158
|
break;
|
|
@@ -2680,6 +2740,23 @@ function handleListPacks() {
|
|
|
2680
2740
|
console.log(`
|
|
2681
2741
|
Install with: engrm install-pack <name>`);
|
|
2682
2742
|
}
|
|
2743
|
+
function handleUpdate() {
|
|
2744
|
+
const { execSync: execSync2 } = __require("child_process");
|
|
2745
|
+
console.log(`Updating Engrm to latest version...
|
|
2746
|
+
`);
|
|
2747
|
+
try {
|
|
2748
|
+
execSync2("npm install -g engrm@latest", { stdio: "inherit" });
|
|
2749
|
+
console.log(`
|
|
2750
|
+
Update complete. Re-registering hooks...`);
|
|
2751
|
+
const result = registerAll();
|
|
2752
|
+
console.log(` MCP server registered \u2192 ${result.mcp.path}`);
|
|
2753
|
+
console.log(` Hooks registered \u2192 ${result.hooks.path}`);
|
|
2754
|
+
console.log(`
|
|
2755
|
+
Restart Claude Code to use the new version.`);
|
|
2756
|
+
} catch (error) {
|
|
2757
|
+
console.error("Update failed. Try manually: npm install -g engrm@latest");
|
|
2758
|
+
}
|
|
2759
|
+
}
|
|
2683
2760
|
function printPostInit() {
|
|
2684
2761
|
console.log(`
|
|
2685
2762
|
Registering with Claude Code...`);
|
|
@@ -2718,6 +2795,7 @@ function printUsage() {
|
|
|
2718
2795
|
console.log(" engrm init --manual Manual setup (enter all values)");
|
|
2719
2796
|
console.log(" engrm init --config <file> Setup from JSON file");
|
|
2720
2797
|
console.log(" engrm status Show status");
|
|
2798
|
+
console.log(" engrm update Update to latest version");
|
|
2721
2799
|
console.log(" engrm packs List available starter packs");
|
|
2722
2800
|
console.log(" engrm install-pack <name> Install a starter pack");
|
|
2723
2801
|
console.log(" engrm sentinel Sentinel code audit commands");
|
|
@@ -426,6 +426,63 @@ var MIGRATIONS = [
|
|
|
426
426
|
observation_count INTEGER DEFAULT 0
|
|
427
427
|
);
|
|
428
428
|
`
|
|
429
|
+
},
|
|
430
|
+
{
|
|
431
|
+
version: 8,
|
|
432
|
+
description: "Add message type to observations CHECK constraint",
|
|
433
|
+
sql: `
|
|
434
|
+
CREATE TABLE IF NOT EXISTS observations_v8 (
|
|
435
|
+
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
436
|
+
session_id TEXT,
|
|
437
|
+
project_id INTEGER NOT NULL REFERENCES projects(id),
|
|
438
|
+
type TEXT NOT NULL CHECK (type IN (
|
|
439
|
+
'bugfix', 'discovery', 'decision', 'pattern',
|
|
440
|
+
'change', 'feature', 'refactor', 'digest', 'standard', 'message'
|
|
441
|
+
)),
|
|
442
|
+
title TEXT NOT NULL,
|
|
443
|
+
narrative TEXT,
|
|
444
|
+
facts TEXT,
|
|
445
|
+
concepts TEXT,
|
|
446
|
+
files_read TEXT,
|
|
447
|
+
files_modified TEXT,
|
|
448
|
+
quality REAL DEFAULT 0.5 CHECK (quality BETWEEN 0.0 AND 1.0),
|
|
449
|
+
lifecycle TEXT DEFAULT 'active' CHECK (lifecycle IN (
|
|
450
|
+
'active', 'aging', 'archived', 'purged', 'pinned'
|
|
451
|
+
)),
|
|
452
|
+
sensitivity TEXT DEFAULT 'shared' CHECK (sensitivity IN (
|
|
453
|
+
'shared', 'personal', 'secret'
|
|
454
|
+
)),
|
|
455
|
+
user_id TEXT NOT NULL,
|
|
456
|
+
device_id TEXT NOT NULL,
|
|
457
|
+
agent TEXT DEFAULT 'claude-code',
|
|
458
|
+
created_at TEXT NOT NULL,
|
|
459
|
+
created_at_epoch INTEGER NOT NULL,
|
|
460
|
+
archived_at_epoch INTEGER,
|
|
461
|
+
compacted_into INTEGER REFERENCES observations(id) ON DELETE SET NULL,
|
|
462
|
+
superseded_by INTEGER REFERENCES observations(id) ON DELETE SET NULL,
|
|
463
|
+
remote_source_id TEXT
|
|
464
|
+
);
|
|
465
|
+
INSERT INTO observations_v8 SELECT * FROM observations;
|
|
466
|
+
DROP TABLE observations;
|
|
467
|
+
ALTER TABLE observations_v8 RENAME TO observations;
|
|
468
|
+
CREATE INDEX idx_observations_project ON observations(project_id);
|
|
469
|
+
CREATE INDEX idx_observations_project_lifecycle ON observations(project_id, lifecycle);
|
|
470
|
+
CREATE INDEX idx_observations_type ON observations(type);
|
|
471
|
+
CREATE INDEX idx_observations_created ON observations(created_at_epoch);
|
|
472
|
+
CREATE INDEX idx_observations_session ON observations(session_id);
|
|
473
|
+
CREATE INDEX idx_observations_lifecycle ON observations(lifecycle);
|
|
474
|
+
CREATE INDEX idx_observations_quality ON observations(quality);
|
|
475
|
+
CREATE INDEX idx_observations_user ON observations(user_id);
|
|
476
|
+
CREATE INDEX idx_observations_superseded ON observations(superseded_by);
|
|
477
|
+
CREATE UNIQUE INDEX idx_observations_remote_source ON observations(remote_source_id) WHERE remote_source_id IS NOT NULL;
|
|
478
|
+
DROP TABLE IF EXISTS observations_fts;
|
|
479
|
+
CREATE VIRTUAL TABLE observations_fts USING fts5(
|
|
480
|
+
title, narrative, facts, concepts,
|
|
481
|
+
content=observations,
|
|
482
|
+
content_rowid=id
|
|
483
|
+
);
|
|
484
|
+
INSERT INTO observations_fts(observations_fts) VALUES('rebuild');
|
|
485
|
+
`
|
|
429
486
|
}
|
|
430
487
|
];
|
|
431
488
|
function isVecExtensionLoaded(db) {
|
|
@@ -426,6 +426,63 @@ var MIGRATIONS = [
|
|
|
426
426
|
observation_count INTEGER DEFAULT 0
|
|
427
427
|
);
|
|
428
428
|
`
|
|
429
|
+
},
|
|
430
|
+
{
|
|
431
|
+
version: 8,
|
|
432
|
+
description: "Add message type to observations CHECK constraint",
|
|
433
|
+
sql: `
|
|
434
|
+
CREATE TABLE IF NOT EXISTS observations_v8 (
|
|
435
|
+
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
436
|
+
session_id TEXT,
|
|
437
|
+
project_id INTEGER NOT NULL REFERENCES projects(id),
|
|
438
|
+
type TEXT NOT NULL CHECK (type IN (
|
|
439
|
+
'bugfix', 'discovery', 'decision', 'pattern',
|
|
440
|
+
'change', 'feature', 'refactor', 'digest', 'standard', 'message'
|
|
441
|
+
)),
|
|
442
|
+
title TEXT NOT NULL,
|
|
443
|
+
narrative TEXT,
|
|
444
|
+
facts TEXT,
|
|
445
|
+
concepts TEXT,
|
|
446
|
+
files_read TEXT,
|
|
447
|
+
files_modified TEXT,
|
|
448
|
+
quality REAL DEFAULT 0.5 CHECK (quality BETWEEN 0.0 AND 1.0),
|
|
449
|
+
lifecycle TEXT DEFAULT 'active' CHECK (lifecycle IN (
|
|
450
|
+
'active', 'aging', 'archived', 'purged', 'pinned'
|
|
451
|
+
)),
|
|
452
|
+
sensitivity TEXT DEFAULT 'shared' CHECK (sensitivity IN (
|
|
453
|
+
'shared', 'personal', 'secret'
|
|
454
|
+
)),
|
|
455
|
+
user_id TEXT NOT NULL,
|
|
456
|
+
device_id TEXT NOT NULL,
|
|
457
|
+
agent TEXT DEFAULT 'claude-code',
|
|
458
|
+
created_at TEXT NOT NULL,
|
|
459
|
+
created_at_epoch INTEGER NOT NULL,
|
|
460
|
+
archived_at_epoch INTEGER,
|
|
461
|
+
compacted_into INTEGER REFERENCES observations(id) ON DELETE SET NULL,
|
|
462
|
+
superseded_by INTEGER REFERENCES observations(id) ON DELETE SET NULL,
|
|
463
|
+
remote_source_id TEXT
|
|
464
|
+
);
|
|
465
|
+
INSERT INTO observations_v8 SELECT * FROM observations;
|
|
466
|
+
DROP TABLE observations;
|
|
467
|
+
ALTER TABLE observations_v8 RENAME TO observations;
|
|
468
|
+
CREATE INDEX idx_observations_project ON observations(project_id);
|
|
469
|
+
CREATE INDEX idx_observations_project_lifecycle ON observations(project_id, lifecycle);
|
|
470
|
+
CREATE INDEX idx_observations_type ON observations(type);
|
|
471
|
+
CREATE INDEX idx_observations_created ON observations(created_at_epoch);
|
|
472
|
+
CREATE INDEX idx_observations_session ON observations(session_id);
|
|
473
|
+
CREATE INDEX idx_observations_lifecycle ON observations(lifecycle);
|
|
474
|
+
CREATE INDEX idx_observations_quality ON observations(quality);
|
|
475
|
+
CREATE INDEX idx_observations_user ON observations(user_id);
|
|
476
|
+
CREATE INDEX idx_observations_superseded ON observations(superseded_by);
|
|
477
|
+
CREATE UNIQUE INDEX idx_observations_remote_source ON observations(remote_source_id) WHERE remote_source_id IS NOT NULL;
|
|
478
|
+
DROP TABLE IF EXISTS observations_fts;
|
|
479
|
+
CREATE VIRTUAL TABLE observations_fts USING fts5(
|
|
480
|
+
title, narrative, facts, concepts,
|
|
481
|
+
content=observations,
|
|
482
|
+
content_rowid=id
|
|
483
|
+
);
|
|
484
|
+
INSERT INTO observations_fts(observations_fts) VALUES('rebuild');
|
|
485
|
+
`
|
|
429
486
|
}
|
|
430
487
|
];
|
|
431
488
|
function isVecExtensionLoaded(db) {
|
|
@@ -426,6 +426,63 @@ var MIGRATIONS = [
|
|
|
426
426
|
observation_count INTEGER DEFAULT 0
|
|
427
427
|
);
|
|
428
428
|
`
|
|
429
|
+
},
|
|
430
|
+
{
|
|
431
|
+
version: 8,
|
|
432
|
+
description: "Add message type to observations CHECK constraint",
|
|
433
|
+
sql: `
|
|
434
|
+
CREATE TABLE IF NOT EXISTS observations_v8 (
|
|
435
|
+
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
436
|
+
session_id TEXT,
|
|
437
|
+
project_id INTEGER NOT NULL REFERENCES projects(id),
|
|
438
|
+
type TEXT NOT NULL CHECK (type IN (
|
|
439
|
+
'bugfix', 'discovery', 'decision', 'pattern',
|
|
440
|
+
'change', 'feature', 'refactor', 'digest', 'standard', 'message'
|
|
441
|
+
)),
|
|
442
|
+
title TEXT NOT NULL,
|
|
443
|
+
narrative TEXT,
|
|
444
|
+
facts TEXT,
|
|
445
|
+
concepts TEXT,
|
|
446
|
+
files_read TEXT,
|
|
447
|
+
files_modified TEXT,
|
|
448
|
+
quality REAL DEFAULT 0.5 CHECK (quality BETWEEN 0.0 AND 1.0),
|
|
449
|
+
lifecycle TEXT DEFAULT 'active' CHECK (lifecycle IN (
|
|
450
|
+
'active', 'aging', 'archived', 'purged', 'pinned'
|
|
451
|
+
)),
|
|
452
|
+
sensitivity TEXT DEFAULT 'shared' CHECK (sensitivity IN (
|
|
453
|
+
'shared', 'personal', 'secret'
|
|
454
|
+
)),
|
|
455
|
+
user_id TEXT NOT NULL,
|
|
456
|
+
device_id TEXT NOT NULL,
|
|
457
|
+
agent TEXT DEFAULT 'claude-code',
|
|
458
|
+
created_at TEXT NOT NULL,
|
|
459
|
+
created_at_epoch INTEGER NOT NULL,
|
|
460
|
+
archived_at_epoch INTEGER,
|
|
461
|
+
compacted_into INTEGER REFERENCES observations(id) ON DELETE SET NULL,
|
|
462
|
+
superseded_by INTEGER REFERENCES observations(id) ON DELETE SET NULL,
|
|
463
|
+
remote_source_id TEXT
|
|
464
|
+
);
|
|
465
|
+
INSERT INTO observations_v8 SELECT * FROM observations;
|
|
466
|
+
DROP TABLE observations;
|
|
467
|
+
ALTER TABLE observations_v8 RENAME TO observations;
|
|
468
|
+
CREATE INDEX idx_observations_project ON observations(project_id);
|
|
469
|
+
CREATE INDEX idx_observations_project_lifecycle ON observations(project_id, lifecycle);
|
|
470
|
+
CREATE INDEX idx_observations_type ON observations(type);
|
|
471
|
+
CREATE INDEX idx_observations_created ON observations(created_at_epoch);
|
|
472
|
+
CREATE INDEX idx_observations_session ON observations(session_id);
|
|
473
|
+
CREATE INDEX idx_observations_lifecycle ON observations(lifecycle);
|
|
474
|
+
CREATE INDEX idx_observations_quality ON observations(quality);
|
|
475
|
+
CREATE INDEX idx_observations_user ON observations(user_id);
|
|
476
|
+
CREATE INDEX idx_observations_superseded ON observations(superseded_by);
|
|
477
|
+
CREATE UNIQUE INDEX idx_observations_remote_source ON observations(remote_source_id) WHERE remote_source_id IS NOT NULL;
|
|
478
|
+
DROP TABLE IF EXISTS observations_fts;
|
|
479
|
+
CREATE VIRTUAL TABLE observations_fts USING fts5(
|
|
480
|
+
title, narrative, facts, concepts,
|
|
481
|
+
content=observations,
|
|
482
|
+
content_rowid=id
|
|
483
|
+
);
|
|
484
|
+
INSERT INTO observations_fts(observations_fts) VALUES('rebuild');
|
|
485
|
+
`
|
|
429
486
|
}
|
|
430
487
|
];
|
|
431
488
|
function isVecExtensionLoaded(db) {
|
package/dist/hooks/sentinel.js
CHANGED
|
@@ -426,6 +426,63 @@ var MIGRATIONS = [
|
|
|
426
426
|
observation_count INTEGER DEFAULT 0
|
|
427
427
|
);
|
|
428
428
|
`
|
|
429
|
+
},
|
|
430
|
+
{
|
|
431
|
+
version: 8,
|
|
432
|
+
description: "Add message type to observations CHECK constraint",
|
|
433
|
+
sql: `
|
|
434
|
+
CREATE TABLE IF NOT EXISTS observations_v8 (
|
|
435
|
+
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
436
|
+
session_id TEXT,
|
|
437
|
+
project_id INTEGER NOT NULL REFERENCES projects(id),
|
|
438
|
+
type TEXT NOT NULL CHECK (type IN (
|
|
439
|
+
'bugfix', 'discovery', 'decision', 'pattern',
|
|
440
|
+
'change', 'feature', 'refactor', 'digest', 'standard', 'message'
|
|
441
|
+
)),
|
|
442
|
+
title TEXT NOT NULL,
|
|
443
|
+
narrative TEXT,
|
|
444
|
+
facts TEXT,
|
|
445
|
+
concepts TEXT,
|
|
446
|
+
files_read TEXT,
|
|
447
|
+
files_modified TEXT,
|
|
448
|
+
quality REAL DEFAULT 0.5 CHECK (quality BETWEEN 0.0 AND 1.0),
|
|
449
|
+
lifecycle TEXT DEFAULT 'active' CHECK (lifecycle IN (
|
|
450
|
+
'active', 'aging', 'archived', 'purged', 'pinned'
|
|
451
|
+
)),
|
|
452
|
+
sensitivity TEXT DEFAULT 'shared' CHECK (sensitivity IN (
|
|
453
|
+
'shared', 'personal', 'secret'
|
|
454
|
+
)),
|
|
455
|
+
user_id TEXT NOT NULL,
|
|
456
|
+
device_id TEXT NOT NULL,
|
|
457
|
+
agent TEXT DEFAULT 'claude-code',
|
|
458
|
+
created_at TEXT NOT NULL,
|
|
459
|
+
created_at_epoch INTEGER NOT NULL,
|
|
460
|
+
archived_at_epoch INTEGER,
|
|
461
|
+
compacted_into INTEGER REFERENCES observations(id) ON DELETE SET NULL,
|
|
462
|
+
superseded_by INTEGER REFERENCES observations(id) ON DELETE SET NULL,
|
|
463
|
+
remote_source_id TEXT
|
|
464
|
+
);
|
|
465
|
+
INSERT INTO observations_v8 SELECT * FROM observations;
|
|
466
|
+
DROP TABLE observations;
|
|
467
|
+
ALTER TABLE observations_v8 RENAME TO observations;
|
|
468
|
+
CREATE INDEX idx_observations_project ON observations(project_id);
|
|
469
|
+
CREATE INDEX idx_observations_project_lifecycle ON observations(project_id, lifecycle);
|
|
470
|
+
CREATE INDEX idx_observations_type ON observations(type);
|
|
471
|
+
CREATE INDEX idx_observations_created ON observations(created_at_epoch);
|
|
472
|
+
CREATE INDEX idx_observations_session ON observations(session_id);
|
|
473
|
+
CREATE INDEX idx_observations_lifecycle ON observations(lifecycle);
|
|
474
|
+
CREATE INDEX idx_observations_quality ON observations(quality);
|
|
475
|
+
CREATE INDEX idx_observations_user ON observations(user_id);
|
|
476
|
+
CREATE INDEX idx_observations_superseded ON observations(superseded_by);
|
|
477
|
+
CREATE UNIQUE INDEX idx_observations_remote_source ON observations(remote_source_id) WHERE remote_source_id IS NOT NULL;
|
|
478
|
+
DROP TABLE IF EXISTS observations_fts;
|
|
479
|
+
CREATE VIRTUAL TABLE observations_fts USING fts5(
|
|
480
|
+
title, narrative, facts, concepts,
|
|
481
|
+
content=observations,
|
|
482
|
+
content_rowid=id
|
|
483
|
+
);
|
|
484
|
+
INSERT INTO observations_fts(observations_fts) VALUES('rebuild');
|
|
485
|
+
`
|
|
429
486
|
}
|
|
430
487
|
];
|
|
431
488
|
function isVecExtensionLoaded(db) {
|
|
@@ -427,6 +427,63 @@ var MIGRATIONS = [
|
|
|
427
427
|
observation_count INTEGER DEFAULT 0
|
|
428
428
|
);
|
|
429
429
|
`
|
|
430
|
+
},
|
|
431
|
+
{
|
|
432
|
+
version: 8,
|
|
433
|
+
description: "Add message type to observations CHECK constraint",
|
|
434
|
+
sql: `
|
|
435
|
+
CREATE TABLE IF NOT EXISTS observations_v8 (
|
|
436
|
+
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
437
|
+
session_id TEXT,
|
|
438
|
+
project_id INTEGER NOT NULL REFERENCES projects(id),
|
|
439
|
+
type TEXT NOT NULL CHECK (type IN (
|
|
440
|
+
'bugfix', 'discovery', 'decision', 'pattern',
|
|
441
|
+
'change', 'feature', 'refactor', 'digest', 'standard', 'message'
|
|
442
|
+
)),
|
|
443
|
+
title TEXT NOT NULL,
|
|
444
|
+
narrative TEXT,
|
|
445
|
+
facts TEXT,
|
|
446
|
+
concepts TEXT,
|
|
447
|
+
files_read TEXT,
|
|
448
|
+
files_modified TEXT,
|
|
449
|
+
quality REAL DEFAULT 0.5 CHECK (quality BETWEEN 0.0 AND 1.0),
|
|
450
|
+
lifecycle TEXT DEFAULT 'active' CHECK (lifecycle IN (
|
|
451
|
+
'active', 'aging', 'archived', 'purged', 'pinned'
|
|
452
|
+
)),
|
|
453
|
+
sensitivity TEXT DEFAULT 'shared' CHECK (sensitivity IN (
|
|
454
|
+
'shared', 'personal', 'secret'
|
|
455
|
+
)),
|
|
456
|
+
user_id TEXT NOT NULL,
|
|
457
|
+
device_id TEXT NOT NULL,
|
|
458
|
+
agent TEXT DEFAULT 'claude-code',
|
|
459
|
+
created_at TEXT NOT NULL,
|
|
460
|
+
created_at_epoch INTEGER NOT NULL,
|
|
461
|
+
archived_at_epoch INTEGER,
|
|
462
|
+
compacted_into INTEGER REFERENCES observations(id) ON DELETE SET NULL,
|
|
463
|
+
superseded_by INTEGER REFERENCES observations(id) ON DELETE SET NULL,
|
|
464
|
+
remote_source_id TEXT
|
|
465
|
+
);
|
|
466
|
+
INSERT INTO observations_v8 SELECT * FROM observations;
|
|
467
|
+
DROP TABLE observations;
|
|
468
|
+
ALTER TABLE observations_v8 RENAME TO observations;
|
|
469
|
+
CREATE INDEX idx_observations_project ON observations(project_id);
|
|
470
|
+
CREATE INDEX idx_observations_project_lifecycle ON observations(project_id, lifecycle);
|
|
471
|
+
CREATE INDEX idx_observations_type ON observations(type);
|
|
472
|
+
CREATE INDEX idx_observations_created ON observations(created_at_epoch);
|
|
473
|
+
CREATE INDEX idx_observations_session ON observations(session_id);
|
|
474
|
+
CREATE INDEX idx_observations_lifecycle ON observations(lifecycle);
|
|
475
|
+
CREATE INDEX idx_observations_quality ON observations(quality);
|
|
476
|
+
CREATE INDEX idx_observations_user ON observations(user_id);
|
|
477
|
+
CREATE INDEX idx_observations_superseded ON observations(superseded_by);
|
|
478
|
+
CREATE UNIQUE INDEX idx_observations_remote_source ON observations(remote_source_id) WHERE remote_source_id IS NOT NULL;
|
|
479
|
+
DROP TABLE IF EXISTS observations_fts;
|
|
480
|
+
CREATE VIRTUAL TABLE observations_fts USING fts5(
|
|
481
|
+
title, narrative, facts, concepts,
|
|
482
|
+
content=observations,
|
|
483
|
+
content_rowid=id
|
|
484
|
+
);
|
|
485
|
+
INSERT INTO observations_fts(observations_fts) VALUES('rebuild');
|
|
486
|
+
`
|
|
430
487
|
}
|
|
431
488
|
];
|
|
432
489
|
function isVecExtensionLoaded(db) {
|
|
@@ -1741,6 +1798,14 @@ async function main() {
|
|
|
1741
1798
|
if (remaining > 0) {
|
|
1742
1799
|
parts.push(`${remaining} more searchable`);
|
|
1743
1800
|
}
|
|
1801
|
+
try {
|
|
1802
|
+
const readKey = `messages_read_${config.device_id}`;
|
|
1803
|
+
const lastReadId = parseInt(db.getSyncState(readKey) ?? "0", 10);
|
|
1804
|
+
const msgCount = db.db.query("SELECT COUNT(*) as c FROM observations WHERE type = 'message' AND id > ? AND lifecycle IN ('active', 'pinned')").get(lastReadId)?.c ?? 0;
|
|
1805
|
+
if (msgCount > 0) {
|
|
1806
|
+
parts.push(`${msgCount} unread message(s)`);
|
|
1807
|
+
}
|
|
1808
|
+
} catch {}
|
|
1744
1809
|
console.error(`Engrm: ${parts.join(" \xB7 ")} \u2014 memory loaded`);
|
|
1745
1810
|
}
|
|
1746
1811
|
try {
|
package/dist/hooks/stop.js
CHANGED
|
@@ -426,6 +426,63 @@ var MIGRATIONS = [
|
|
|
426
426
|
observation_count INTEGER DEFAULT 0
|
|
427
427
|
);
|
|
428
428
|
`
|
|
429
|
+
},
|
|
430
|
+
{
|
|
431
|
+
version: 8,
|
|
432
|
+
description: "Add message type to observations CHECK constraint",
|
|
433
|
+
sql: `
|
|
434
|
+
CREATE TABLE IF NOT EXISTS observations_v8 (
|
|
435
|
+
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
436
|
+
session_id TEXT,
|
|
437
|
+
project_id INTEGER NOT NULL REFERENCES projects(id),
|
|
438
|
+
type TEXT NOT NULL CHECK (type IN (
|
|
439
|
+
'bugfix', 'discovery', 'decision', 'pattern',
|
|
440
|
+
'change', 'feature', 'refactor', 'digest', 'standard', 'message'
|
|
441
|
+
)),
|
|
442
|
+
title TEXT NOT NULL,
|
|
443
|
+
narrative TEXT,
|
|
444
|
+
facts TEXT,
|
|
445
|
+
concepts TEXT,
|
|
446
|
+
files_read TEXT,
|
|
447
|
+
files_modified TEXT,
|
|
448
|
+
quality REAL DEFAULT 0.5 CHECK (quality BETWEEN 0.0 AND 1.0),
|
|
449
|
+
lifecycle TEXT DEFAULT 'active' CHECK (lifecycle IN (
|
|
450
|
+
'active', 'aging', 'archived', 'purged', 'pinned'
|
|
451
|
+
)),
|
|
452
|
+
sensitivity TEXT DEFAULT 'shared' CHECK (sensitivity IN (
|
|
453
|
+
'shared', 'personal', 'secret'
|
|
454
|
+
)),
|
|
455
|
+
user_id TEXT NOT NULL,
|
|
456
|
+
device_id TEXT NOT NULL,
|
|
457
|
+
agent TEXT DEFAULT 'claude-code',
|
|
458
|
+
created_at TEXT NOT NULL,
|
|
459
|
+
created_at_epoch INTEGER NOT NULL,
|
|
460
|
+
archived_at_epoch INTEGER,
|
|
461
|
+
compacted_into INTEGER REFERENCES observations(id) ON DELETE SET NULL,
|
|
462
|
+
superseded_by INTEGER REFERENCES observations(id) ON DELETE SET NULL,
|
|
463
|
+
remote_source_id TEXT
|
|
464
|
+
);
|
|
465
|
+
INSERT INTO observations_v8 SELECT * FROM observations;
|
|
466
|
+
DROP TABLE observations;
|
|
467
|
+
ALTER TABLE observations_v8 RENAME TO observations;
|
|
468
|
+
CREATE INDEX idx_observations_project ON observations(project_id);
|
|
469
|
+
CREATE INDEX idx_observations_project_lifecycle ON observations(project_id, lifecycle);
|
|
470
|
+
CREATE INDEX idx_observations_type ON observations(type);
|
|
471
|
+
CREATE INDEX idx_observations_created ON observations(created_at_epoch);
|
|
472
|
+
CREATE INDEX idx_observations_session ON observations(session_id);
|
|
473
|
+
CREATE INDEX idx_observations_lifecycle ON observations(lifecycle);
|
|
474
|
+
CREATE INDEX idx_observations_quality ON observations(quality);
|
|
475
|
+
CREATE INDEX idx_observations_user ON observations(user_id);
|
|
476
|
+
CREATE INDEX idx_observations_superseded ON observations(superseded_by);
|
|
477
|
+
CREATE UNIQUE INDEX idx_observations_remote_source ON observations(remote_source_id) WHERE remote_source_id IS NOT NULL;
|
|
478
|
+
DROP TABLE IF EXISTS observations_fts;
|
|
479
|
+
CREATE VIRTUAL TABLE observations_fts USING fts5(
|
|
480
|
+
title, narrative, facts, concepts,
|
|
481
|
+
content=observations,
|
|
482
|
+
content_rowid=id
|
|
483
|
+
);
|
|
484
|
+
INSERT INTO observations_fts(observations_fts) VALUES('rebuild');
|
|
485
|
+
`
|
|
429
486
|
}
|
|
430
487
|
];
|
|
431
488
|
function isVecExtensionLoaded(db) {
|
package/dist/server.js
CHANGED
|
@@ -13976,6 +13976,63 @@ var MIGRATIONS = [
|
|
|
13976
13976
|
observation_count INTEGER DEFAULT 0
|
|
13977
13977
|
);
|
|
13978
13978
|
`
|
|
13979
|
+
},
|
|
13980
|
+
{
|
|
13981
|
+
version: 8,
|
|
13982
|
+
description: "Add message type to observations CHECK constraint",
|
|
13983
|
+
sql: `
|
|
13984
|
+
CREATE TABLE IF NOT EXISTS observations_v8 (
|
|
13985
|
+
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
13986
|
+
session_id TEXT,
|
|
13987
|
+
project_id INTEGER NOT NULL REFERENCES projects(id),
|
|
13988
|
+
type TEXT NOT NULL CHECK (type IN (
|
|
13989
|
+
'bugfix', 'discovery', 'decision', 'pattern',
|
|
13990
|
+
'change', 'feature', 'refactor', 'digest', 'standard', 'message'
|
|
13991
|
+
)),
|
|
13992
|
+
title TEXT NOT NULL,
|
|
13993
|
+
narrative TEXT,
|
|
13994
|
+
facts TEXT,
|
|
13995
|
+
concepts TEXT,
|
|
13996
|
+
files_read TEXT,
|
|
13997
|
+
files_modified TEXT,
|
|
13998
|
+
quality REAL DEFAULT 0.5 CHECK (quality BETWEEN 0.0 AND 1.0),
|
|
13999
|
+
lifecycle TEXT DEFAULT 'active' CHECK (lifecycle IN (
|
|
14000
|
+
'active', 'aging', 'archived', 'purged', 'pinned'
|
|
14001
|
+
)),
|
|
14002
|
+
sensitivity TEXT DEFAULT 'shared' CHECK (sensitivity IN (
|
|
14003
|
+
'shared', 'personal', 'secret'
|
|
14004
|
+
)),
|
|
14005
|
+
user_id TEXT NOT NULL,
|
|
14006
|
+
device_id TEXT NOT NULL,
|
|
14007
|
+
agent TEXT DEFAULT 'claude-code',
|
|
14008
|
+
created_at TEXT NOT NULL,
|
|
14009
|
+
created_at_epoch INTEGER NOT NULL,
|
|
14010
|
+
archived_at_epoch INTEGER,
|
|
14011
|
+
compacted_into INTEGER REFERENCES observations(id) ON DELETE SET NULL,
|
|
14012
|
+
superseded_by INTEGER REFERENCES observations(id) ON DELETE SET NULL,
|
|
14013
|
+
remote_source_id TEXT
|
|
14014
|
+
);
|
|
14015
|
+
INSERT INTO observations_v8 SELECT * FROM observations;
|
|
14016
|
+
DROP TABLE observations;
|
|
14017
|
+
ALTER TABLE observations_v8 RENAME TO observations;
|
|
14018
|
+
CREATE INDEX idx_observations_project ON observations(project_id);
|
|
14019
|
+
CREATE INDEX idx_observations_project_lifecycle ON observations(project_id, lifecycle);
|
|
14020
|
+
CREATE INDEX idx_observations_type ON observations(type);
|
|
14021
|
+
CREATE INDEX idx_observations_created ON observations(created_at_epoch);
|
|
14022
|
+
CREATE INDEX idx_observations_session ON observations(session_id);
|
|
14023
|
+
CREATE INDEX idx_observations_lifecycle ON observations(lifecycle);
|
|
14024
|
+
CREATE INDEX idx_observations_quality ON observations(quality);
|
|
14025
|
+
CREATE INDEX idx_observations_user ON observations(user_id);
|
|
14026
|
+
CREATE INDEX idx_observations_superseded ON observations(superseded_by);
|
|
14027
|
+
CREATE UNIQUE INDEX idx_observations_remote_source ON observations(remote_source_id) WHERE remote_source_id IS NOT NULL;
|
|
14028
|
+
DROP TABLE IF EXISTS observations_fts;
|
|
14029
|
+
CREATE VIRTUAL TABLE observations_fts USING fts5(
|
|
14030
|
+
title, narrative, facts, concepts,
|
|
14031
|
+
content=observations,
|
|
14032
|
+
content_rowid=id
|
|
14033
|
+
);
|
|
14034
|
+
INSERT INTO observations_fts(observations_fts) VALUES('rebuild');
|
|
14035
|
+
`
|
|
13979
14036
|
}
|
|
13980
14037
|
];
|
|
13981
14038
|
function isVecExtensionLoaded(db) {
|
|
@@ -16333,7 +16390,8 @@ server.tool("save_observation", "Save an observation to memory", {
|
|
|
16333
16390
|
"change",
|
|
16334
16391
|
"feature",
|
|
16335
16392
|
"refactor",
|
|
16336
|
-
"digest"
|
|
16393
|
+
"digest",
|
|
16394
|
+
"message"
|
|
16337
16395
|
]),
|
|
16338
16396
|
title: exports_external.string().describe("Brief title"),
|
|
16339
16397
|
narrative: exports_external.string().optional().describe("What happened and why"),
|
|
@@ -16525,6 +16583,52 @@ server.tool("pin_observation", "Pin/unpin observation", {
|
|
|
16525
16583
|
]
|
|
16526
16584
|
};
|
|
16527
16585
|
});
|
|
16586
|
+
server.tool("check_messages", "Check for messages sent from other devices or sessions. Messages are cross-device notes left by you or your team.", {
|
|
16587
|
+
mark_read: exports_external.boolean().optional().describe("Mark messages as read after viewing (default: true)")
|
|
16588
|
+
}, async (params) => {
|
|
16589
|
+
const markRead = params.mark_read !== false;
|
|
16590
|
+
const readKey = `messages_read_${config2.device_id}`;
|
|
16591
|
+
const lastReadId = parseInt(db.getSyncState(readKey) ?? "0", 10);
|
|
16592
|
+
const messages = db.db.query(`SELECT id, title, narrative, user_id, device_id, created_at FROM observations
|
|
16593
|
+
WHERE type = 'message' AND id > ? AND lifecycle IN ('active', 'pinned')
|
|
16594
|
+
ORDER BY created_at_epoch DESC LIMIT 20`).all(lastReadId);
|
|
16595
|
+
if (messages.length === 0) {
|
|
16596
|
+
return {
|
|
16597
|
+
content: [{ type: "text", text: "No new messages." }]
|
|
16598
|
+
};
|
|
16599
|
+
}
|
|
16600
|
+
if (markRead && messages.length > 0) {
|
|
16601
|
+
const maxId = Math.max(...messages.map((m) => m.id));
|
|
16602
|
+
db.setSyncState(readKey, String(maxId));
|
|
16603
|
+
}
|
|
16604
|
+
const lines = messages.map((m) => {
|
|
16605
|
+
const from = m.device_id === config2.device_id ? "you (this device)" : m.device_id;
|
|
16606
|
+
const ago = formatTimeAgo(m.created_at);
|
|
16607
|
+
return `[${ago}] from ${from}:
|
|
16608
|
+
${m.title}${m.narrative ? `
|
|
16609
|
+
` + m.narrative : ""}`;
|
|
16610
|
+
});
|
|
16611
|
+
return {
|
|
16612
|
+
content: [{
|
|
16613
|
+
type: "text",
|
|
16614
|
+
text: `${messages.length} message(s):
|
|
16615
|
+
|
|
16616
|
+
${lines.join(`
|
|
16617
|
+
|
|
16618
|
+
`)}`
|
|
16619
|
+
}]
|
|
16620
|
+
};
|
|
16621
|
+
});
|
|
16622
|
+
function formatTimeAgo(isoDate) {
|
|
16623
|
+
const diff = Date.now() - new Date(isoDate).getTime();
|
|
16624
|
+
const mins = Math.floor(diff / 60000);
|
|
16625
|
+
if (mins < 60)
|
|
16626
|
+
return `${mins}m ago`;
|
|
16627
|
+
const hrs = Math.floor(mins / 60);
|
|
16628
|
+
if (hrs < 24)
|
|
16629
|
+
return `${hrs}h ago`;
|
|
16630
|
+
return `${Math.floor(hrs / 24)}d ago`;
|
|
16631
|
+
}
|
|
16528
16632
|
server.tool("install_pack", "Install a help pack (pre-curated observations for a technology stack)", {
|
|
16529
16633
|
pack_name: exports_external.string().describe("Pack name (e.g. 'typescript-patterns', 'react-gotchas')")
|
|
16530
16634
|
}, async (params) => {
|