engrm 0.4.18 → 0.4.21
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 +43 -4
- package/dist/hooks/elicitation-result.js +33 -4
- package/dist/hooks/post-tool-use.js +118 -6
- package/dist/hooks/pre-compact.js +66 -7
- package/dist/hooks/sentinel.js +33 -4
- package/dist/hooks/session-start.js +120 -21
- package/dist/hooks/stop.js +199 -87
- package/dist/hooks/user-prompt-submit.js +33 -4
- package/dist/server.js +78 -9
- package/package.json +1 -1
|
@@ -714,6 +714,16 @@ var MIGRATIONS = [
|
|
|
714
714
|
ON tool_events(created_at_epoch DESC, id DESC);
|
|
715
715
|
`
|
|
716
716
|
},
|
|
717
|
+
{
|
|
718
|
+
version: 11,
|
|
719
|
+
description: "Add synced handoff metadata to session summaries",
|
|
720
|
+
sql: `
|
|
721
|
+
ALTER TABLE session_summaries ADD COLUMN capture_state TEXT;
|
|
722
|
+
ALTER TABLE session_summaries ADD COLUMN recent_tool_names TEXT;
|
|
723
|
+
ALTER TABLE session_summaries ADD COLUMN hot_files TEXT;
|
|
724
|
+
ALTER TABLE session_summaries ADD COLUMN recent_outcomes TEXT;
|
|
725
|
+
`
|
|
726
|
+
},
|
|
717
727
|
{
|
|
718
728
|
version: 11,
|
|
719
729
|
description: "Add observation provenance from tool and prompt chronology",
|
|
@@ -1237,6 +1247,10 @@ class MemDatabase {
|
|
|
1237
1247
|
p.name AS project_name,
|
|
1238
1248
|
ss.request AS request,
|
|
1239
1249
|
ss.completed AS completed,
|
|
1250
|
+
ss.capture_state AS capture_state,
|
|
1251
|
+
ss.recent_tool_names AS recent_tool_names,
|
|
1252
|
+
ss.hot_files AS hot_files,
|
|
1253
|
+
ss.recent_outcomes AS recent_outcomes,
|
|
1240
1254
|
(SELECT COUNT(*) FROM user_prompts up WHERE up.session_id = s.session_id) AS prompt_count,
|
|
1241
1255
|
(SELECT COUNT(*) FROM tool_events te WHERE te.session_id = s.session_id) AS tool_event_count
|
|
1242
1256
|
FROM sessions s
|
|
@@ -1251,6 +1265,10 @@ class MemDatabase {
|
|
|
1251
1265
|
p.name AS project_name,
|
|
1252
1266
|
ss.request AS request,
|
|
1253
1267
|
ss.completed AS completed,
|
|
1268
|
+
ss.capture_state AS capture_state,
|
|
1269
|
+
ss.recent_tool_names AS recent_tool_names,
|
|
1270
|
+
ss.hot_files AS hot_files,
|
|
1271
|
+
ss.recent_outcomes AS recent_outcomes,
|
|
1254
1272
|
(SELECT COUNT(*) FROM user_prompts up WHERE up.session_id = s.session_id) AS prompt_count,
|
|
1255
1273
|
(SELECT COUNT(*) FROM tool_events te WHERE te.session_id = s.session_id) AS tool_event_count
|
|
1256
1274
|
FROM sessions s
|
|
@@ -1423,8 +1441,11 @@ class MemDatabase {
|
|
|
1423
1441
|
completed: normalizeSummarySection(summary.completed),
|
|
1424
1442
|
next_steps: normalizeSummarySection(summary.next_steps)
|
|
1425
1443
|
};
|
|
1426
|
-
const result = this.db.query(`INSERT INTO session_summaries (
|
|
1427
|
-
|
|
1444
|
+
const result = this.db.query(`INSERT INTO session_summaries (
|
|
1445
|
+
session_id, project_id, user_id, request, investigated, learned, completed, next_steps,
|
|
1446
|
+
capture_state, recent_tool_names, hot_files, recent_outcomes, created_at_epoch
|
|
1447
|
+
)
|
|
1448
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`).run(summary.session_id, summary.project_id, summary.user_id, normalized.request, normalized.investigated, normalized.learned, normalized.completed, normalized.next_steps, summary.capture_state ?? null, summary.recent_tool_names ?? null, summary.hot_files ?? null, summary.recent_outcomes ?? null, now);
|
|
1428
1449
|
const id = Number(result.lastInsertRowid);
|
|
1429
1450
|
return this.db.query("SELECT * FROM session_summaries WHERE id = ?").get(id);
|
|
1430
1451
|
}
|
|
@@ -1439,7 +1460,11 @@ class MemDatabase {
|
|
|
1439
1460
|
investigated: normalizeSummarySection(summary.investigated ?? existing.investigated),
|
|
1440
1461
|
learned: normalizeSummarySection(summary.learned ?? existing.learned),
|
|
1441
1462
|
completed: normalizeSummarySection(summary.completed ?? existing.completed),
|
|
1442
|
-
next_steps: normalizeSummarySection(summary.next_steps ?? existing.next_steps)
|
|
1463
|
+
next_steps: normalizeSummarySection(summary.next_steps ?? existing.next_steps),
|
|
1464
|
+
capture_state: summary.capture_state ?? existing.capture_state,
|
|
1465
|
+
recent_tool_names: summary.recent_tool_names ?? existing.recent_tool_names,
|
|
1466
|
+
hot_files: summary.hot_files ?? existing.hot_files,
|
|
1467
|
+
recent_outcomes: summary.recent_outcomes ?? existing.recent_outcomes
|
|
1443
1468
|
};
|
|
1444
1469
|
this.db.query(`UPDATE session_summaries
|
|
1445
1470
|
SET project_id = ?,
|
|
@@ -1449,8 +1474,12 @@ class MemDatabase {
|
|
|
1449
1474
|
learned = ?,
|
|
1450
1475
|
completed = ?,
|
|
1451
1476
|
next_steps = ?,
|
|
1477
|
+
capture_state = ?,
|
|
1478
|
+
recent_tool_names = ?,
|
|
1479
|
+
hot_files = ?,
|
|
1480
|
+
recent_outcomes = ?,
|
|
1452
1481
|
created_at_epoch = ?
|
|
1453
|
-
WHERE session_id = ?`).run(summary.project_id ?? existing.project_id, summary.user_id, normalized.request, normalized.investigated, normalized.learned, normalized.completed, normalized.next_steps, now, summary.session_id);
|
|
1482
|
+
WHERE session_id = ?`).run(summary.project_id ?? existing.project_id, summary.user_id, normalized.request, normalized.investigated, normalized.learned, normalized.completed, normalized.next_steps, normalized.capture_state, normalized.recent_tool_names, normalized.hot_files, normalized.recent_outcomes, now, summary.session_id);
|
|
1454
1483
|
return this.getSessionSummary(summary.session_id);
|
|
1455
1484
|
}
|
|
1456
1485
|
getSessionSummary(sessionId) {
|
package/dist/server.js
CHANGED
|
@@ -14120,6 +14120,16 @@ var MIGRATIONS = [
|
|
|
14120
14120
|
ON tool_events(created_at_epoch DESC, id DESC);
|
|
14121
14121
|
`
|
|
14122
14122
|
},
|
|
14123
|
+
{
|
|
14124
|
+
version: 11,
|
|
14125
|
+
description: "Add synced handoff metadata to session summaries",
|
|
14126
|
+
sql: `
|
|
14127
|
+
ALTER TABLE session_summaries ADD COLUMN capture_state TEXT;
|
|
14128
|
+
ALTER TABLE session_summaries ADD COLUMN recent_tool_names TEXT;
|
|
14129
|
+
ALTER TABLE session_summaries ADD COLUMN hot_files TEXT;
|
|
14130
|
+
ALTER TABLE session_summaries ADD COLUMN recent_outcomes TEXT;
|
|
14131
|
+
`
|
|
14132
|
+
},
|
|
14123
14133
|
{
|
|
14124
14134
|
version: 11,
|
|
14125
14135
|
description: "Add observation provenance from tool and prompt chronology",
|
|
@@ -14647,6 +14657,10 @@ class MemDatabase {
|
|
|
14647
14657
|
p.name AS project_name,
|
|
14648
14658
|
ss.request AS request,
|
|
14649
14659
|
ss.completed AS completed,
|
|
14660
|
+
ss.capture_state AS capture_state,
|
|
14661
|
+
ss.recent_tool_names AS recent_tool_names,
|
|
14662
|
+
ss.hot_files AS hot_files,
|
|
14663
|
+
ss.recent_outcomes AS recent_outcomes,
|
|
14650
14664
|
(SELECT COUNT(*) FROM user_prompts up WHERE up.session_id = s.session_id) AS prompt_count,
|
|
14651
14665
|
(SELECT COUNT(*) FROM tool_events te WHERE te.session_id = s.session_id) AS tool_event_count
|
|
14652
14666
|
FROM sessions s
|
|
@@ -14661,6 +14675,10 @@ class MemDatabase {
|
|
|
14661
14675
|
p.name AS project_name,
|
|
14662
14676
|
ss.request AS request,
|
|
14663
14677
|
ss.completed AS completed,
|
|
14678
|
+
ss.capture_state AS capture_state,
|
|
14679
|
+
ss.recent_tool_names AS recent_tool_names,
|
|
14680
|
+
ss.hot_files AS hot_files,
|
|
14681
|
+
ss.recent_outcomes AS recent_outcomes,
|
|
14664
14682
|
(SELECT COUNT(*) FROM user_prompts up WHERE up.session_id = s.session_id) AS prompt_count,
|
|
14665
14683
|
(SELECT COUNT(*) FROM tool_events te WHERE te.session_id = s.session_id) AS tool_event_count
|
|
14666
14684
|
FROM sessions s
|
|
@@ -14833,8 +14851,11 @@ class MemDatabase {
|
|
|
14833
14851
|
completed: normalizeSummarySection(summary.completed),
|
|
14834
14852
|
next_steps: normalizeSummarySection(summary.next_steps)
|
|
14835
14853
|
};
|
|
14836
|
-
const result = this.db.query(`INSERT INTO session_summaries (
|
|
14837
|
-
|
|
14854
|
+
const result = this.db.query(`INSERT INTO session_summaries (
|
|
14855
|
+
session_id, project_id, user_id, request, investigated, learned, completed, next_steps,
|
|
14856
|
+
capture_state, recent_tool_names, hot_files, recent_outcomes, created_at_epoch
|
|
14857
|
+
)
|
|
14858
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`).run(summary.session_id, summary.project_id, summary.user_id, normalized.request, normalized.investigated, normalized.learned, normalized.completed, normalized.next_steps, summary.capture_state ?? null, summary.recent_tool_names ?? null, summary.hot_files ?? null, summary.recent_outcomes ?? null, now);
|
|
14838
14859
|
const id = Number(result.lastInsertRowid);
|
|
14839
14860
|
return this.db.query("SELECT * FROM session_summaries WHERE id = ?").get(id);
|
|
14840
14861
|
}
|
|
@@ -14849,7 +14870,11 @@ class MemDatabase {
|
|
|
14849
14870
|
investigated: normalizeSummarySection(summary.investigated ?? existing.investigated),
|
|
14850
14871
|
learned: normalizeSummarySection(summary.learned ?? existing.learned),
|
|
14851
14872
|
completed: normalizeSummarySection(summary.completed ?? existing.completed),
|
|
14852
|
-
next_steps: normalizeSummarySection(summary.next_steps ?? existing.next_steps)
|
|
14873
|
+
next_steps: normalizeSummarySection(summary.next_steps ?? existing.next_steps),
|
|
14874
|
+
capture_state: summary.capture_state ?? existing.capture_state,
|
|
14875
|
+
recent_tool_names: summary.recent_tool_names ?? existing.recent_tool_names,
|
|
14876
|
+
hot_files: summary.hot_files ?? existing.hot_files,
|
|
14877
|
+
recent_outcomes: summary.recent_outcomes ?? existing.recent_outcomes
|
|
14853
14878
|
};
|
|
14854
14879
|
this.db.query(`UPDATE session_summaries
|
|
14855
14880
|
SET project_id = ?,
|
|
@@ -14859,8 +14884,12 @@ class MemDatabase {
|
|
|
14859
14884
|
learned = ?,
|
|
14860
14885
|
completed = ?,
|
|
14861
14886
|
next_steps = ?,
|
|
14887
|
+
capture_state = ?,
|
|
14888
|
+
recent_tool_names = ?,
|
|
14889
|
+
hot_files = ?,
|
|
14890
|
+
recent_outcomes = ?,
|
|
14862
14891
|
created_at_epoch = ?
|
|
14863
|
-
WHERE session_id = ?`).run(summary.project_id ?? existing.project_id, summary.user_id, normalized.request, normalized.investigated, normalized.learned, normalized.completed, normalized.next_steps, now, summary.session_id);
|
|
14892
|
+
WHERE session_id = ?`).run(summary.project_id ?? existing.project_id, summary.user_id, normalized.request, normalized.investigated, normalized.learned, normalized.completed, normalized.next_steps, normalized.capture_state, normalized.recent_tool_names, normalized.hot_files, normalized.recent_outcomes, now, summary.session_id);
|
|
14864
14893
|
return this.getSessionSummary(summary.session_id);
|
|
14865
14894
|
}
|
|
14866
14895
|
getSessionSummary(sessionId) {
|
|
@@ -16460,6 +16489,16 @@ function findStaleDecisionsGlobal(db, options) {
|
|
|
16460
16489
|
function tokenizeProjectHint(text) {
|
|
16461
16490
|
return Array.from(new Set((text.toLowerCase().match(/[a-z0-9_+-]{4,}/g) ?? []).filter(Boolean)));
|
|
16462
16491
|
}
|
|
16492
|
+
function parseSummaryJsonList(value) {
|
|
16493
|
+
if (!value)
|
|
16494
|
+
return [];
|
|
16495
|
+
try {
|
|
16496
|
+
const parsed = JSON.parse(value);
|
|
16497
|
+
return Array.isArray(parsed) ? parsed.filter((item) => typeof item === "string" && item.trim().length > 0) : [];
|
|
16498
|
+
} catch {
|
|
16499
|
+
return [];
|
|
16500
|
+
}
|
|
16501
|
+
}
|
|
16463
16502
|
function isObservationRelatedToProject(obs, detected) {
|
|
16464
16503
|
const hints = new Set([
|
|
16465
16504
|
...tokenizeProjectHint(detected.name),
|
|
@@ -16591,7 +16630,7 @@ function buildSessionContext(db, cwd, options = {}) {
|
|
|
16591
16630
|
const recentToolEvents2 = db.getRecentToolEvents(isNewProject ? null : projectId, isNewProject ? 8 : 6, opts.userId);
|
|
16592
16631
|
const recentSessions2 = isNewProject ? [] : db.getRecentSessions(projectId, 5, opts.userId);
|
|
16593
16632
|
const projectTypeCounts2 = isNewProject ? undefined : getProjectTypeCounts(db, projectId, opts.userId);
|
|
16594
|
-
const recentOutcomes2 = isNewProject ? undefined : getRecentOutcomes(db, projectId, opts.userId);
|
|
16633
|
+
const recentOutcomes2 = isNewProject ? undefined : getRecentOutcomes(db, projectId, opts.userId, recentSessions2);
|
|
16595
16634
|
return {
|
|
16596
16635
|
project_name: projectName,
|
|
16597
16636
|
canonical_id: canonicalId,
|
|
@@ -16629,7 +16668,7 @@ function buildSessionContext(db, cwd, options = {}) {
|
|
|
16629
16668
|
const recentToolEvents = db.getRecentToolEvents(isNewProject ? null : projectId, isNewProject ? 8 : 6, opts.userId);
|
|
16630
16669
|
const recentSessions = isNewProject ? [] : db.getRecentSessions(projectId, 5, opts.userId);
|
|
16631
16670
|
const projectTypeCounts = isNewProject ? undefined : getProjectTypeCounts(db, projectId, opts.userId);
|
|
16632
|
-
const recentOutcomes = isNewProject ? undefined : getRecentOutcomes(db, projectId, opts.userId);
|
|
16671
|
+
const recentOutcomes = isNewProject ? undefined : getRecentOutcomes(db, projectId, opts.userId, recentSessions);
|
|
16633
16672
|
let securityFindings = [];
|
|
16634
16673
|
if (!isNewProject) {
|
|
16635
16674
|
try {
|
|
@@ -16972,7 +17011,7 @@ function getProjectTypeCounts(db, projectId, userId) {
|
|
|
16972
17011
|
}
|
|
16973
17012
|
return counts;
|
|
16974
17013
|
}
|
|
16975
|
-
function getRecentOutcomes(db, projectId, userId) {
|
|
17014
|
+
function getRecentOutcomes(db, projectId, userId, recentSessions) {
|
|
16976
17015
|
const visibilityClause = userId ? " AND (sensitivity != 'personal' OR user_id = ?)" : "";
|
|
16977
17016
|
const visibilityParams = userId ? [userId] : [];
|
|
16978
17017
|
const summaries = db.db.query(`SELECT * FROM session_summaries
|
|
@@ -16982,6 +17021,15 @@ function getRecentOutcomes(db, projectId, userId) {
|
|
|
16982
17021
|
const picked = [];
|
|
16983
17022
|
const seen = new Set;
|
|
16984
17023
|
for (const summary of summaries) {
|
|
17024
|
+
for (const item of parseSummaryJsonList(summary.recent_outcomes)) {
|
|
17025
|
+
const normalized = item.toLowerCase().replace(/\s+/g, " ").trim();
|
|
17026
|
+
if (!normalized || seen.has(normalized))
|
|
17027
|
+
continue;
|
|
17028
|
+
seen.add(normalized);
|
|
17029
|
+
picked.push(item);
|
|
17030
|
+
if (picked.length >= 5)
|
|
17031
|
+
return picked;
|
|
17032
|
+
}
|
|
16985
17033
|
for (const line of [
|
|
16986
17034
|
...extractMeaningfulLines(summary.completed, 2),
|
|
16987
17035
|
...extractMeaningfulLines(summary.learned, 1)
|
|
@@ -16995,6 +17043,17 @@ function getRecentOutcomes(db, projectId, userId) {
|
|
|
16995
17043
|
return picked;
|
|
16996
17044
|
}
|
|
16997
17045
|
}
|
|
17046
|
+
for (const session of recentSessions ?? []) {
|
|
17047
|
+
for (const item of parseSummaryJsonList(session.recent_outcomes)) {
|
|
17048
|
+
const normalized = item.toLowerCase().replace(/\s+/g, " ").trim();
|
|
17049
|
+
if (!normalized || seen.has(normalized))
|
|
17050
|
+
continue;
|
|
17051
|
+
seen.add(normalized);
|
|
17052
|
+
picked.push(item);
|
|
17053
|
+
if (picked.length >= 5)
|
|
17054
|
+
return picked;
|
|
17055
|
+
}
|
|
17056
|
+
}
|
|
16998
17057
|
const rows = db.db.query(`SELECT * FROM observations
|
|
16999
17058
|
WHERE project_id = ?
|
|
17000
17059
|
AND lifecycle IN ('active', 'aging', 'pinned')
|
|
@@ -19011,10 +19070,20 @@ function mergeRemoteSummary(db, config2, change, projectId) {
|
|
|
19011
19070
|
investigated: typeof change.metadata?.investigated === "string" ? change.metadata.investigated : null,
|
|
19012
19071
|
learned: typeof change.metadata?.learned === "string" ? change.metadata.learned : null,
|
|
19013
19072
|
completed: typeof change.metadata?.completed === "string" ? change.metadata.completed : null,
|
|
19014
|
-
next_steps: typeof change.metadata?.next_steps === "string" ? change.metadata.next_steps : null
|
|
19073
|
+
next_steps: typeof change.metadata?.next_steps === "string" ? change.metadata.next_steps : null,
|
|
19074
|
+
capture_state: typeof change.metadata?.capture_state === "string" ? change.metadata.capture_state : null,
|
|
19075
|
+
recent_tool_names: encodeStringArray(change.metadata?.recent_tool_names),
|
|
19076
|
+
hot_files: encodeStringArray(change.metadata?.hot_files),
|
|
19077
|
+
recent_outcomes: encodeStringArray(change.metadata?.recent_outcomes)
|
|
19015
19078
|
});
|
|
19016
19079
|
return Boolean(summary);
|
|
19017
19080
|
}
|
|
19081
|
+
function encodeStringArray(value) {
|
|
19082
|
+
if (!Array.isArray(value))
|
|
19083
|
+
return null;
|
|
19084
|
+
const normalized = value.filter((item) => typeof item === "string").map((item) => item.trim()).filter(Boolean);
|
|
19085
|
+
return normalized.length > 0 ? JSON.stringify(normalized) : null;
|
|
19086
|
+
}
|
|
19018
19087
|
function normalizeRemoteObservationType(rawType, sourceId) {
|
|
19019
19088
|
const type = typeof rawType === "string" ? rawType.trim().toLowerCase() : "";
|
|
19020
19089
|
if (type === "bugfix" || type === "discovery" || type === "decision" || type === "pattern" || type === "change" || type === "feature" || type === "refactor" || type === "digest" || type === "standard" || type === "message") {
|
|
@@ -19817,7 +19886,7 @@ process.on("SIGTERM", () => {
|
|
|
19817
19886
|
});
|
|
19818
19887
|
var server = new McpServer({
|
|
19819
19888
|
name: "engrm",
|
|
19820
|
-
version: "0.4.
|
|
19889
|
+
version: "0.4.21"
|
|
19821
19890
|
});
|
|
19822
19891
|
server.tool("save_observation", "Save an observation to memory", {
|
|
19823
19892
|
type: exports_external.enum([
|