engrm 0.4.19 → 0.4.22
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 +64 -4
- package/dist/hooks/elicitation-result.js +58 -4
- package/dist/hooks/post-tool-use.js +195 -6
- package/dist/hooks/pre-compact.js +91 -7
- package/dist/hooks/sentinel.js +58 -4
- package/dist/hooks/session-start.js +139 -11
- package/dist/hooks/stop.js +106 -50
- package/dist/hooks/user-prompt-submit.js +111 -5
- package/dist/server.js +144 -52
- package/package.json +1 -1
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: 12,
|
|
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",
|
|
@@ -14186,6 +14196,9 @@ function inferLegacySchemaVersion(db) {
|
|
|
14186
14196
|
version2 = Math.max(version2, 10);
|
|
14187
14197
|
if (columnExists(db, "observations", "source_tool"))
|
|
14188
14198
|
version2 = Math.max(version2, 11);
|
|
14199
|
+
if (columnExists(db, "session_summaries", "capture_state") && columnExists(db, "session_summaries", "recent_tool_names") && columnExists(db, "session_summaries", "hot_files") && columnExists(db, "session_summaries", "recent_outcomes")) {
|
|
14200
|
+
version2 = Math.max(version2, 12);
|
|
14201
|
+
}
|
|
14189
14202
|
return version2;
|
|
14190
14203
|
}
|
|
14191
14204
|
function runMigrations(db) {
|
|
@@ -14264,6 +14277,23 @@ function ensureObservationTypes(db) {
|
|
|
14264
14277
|
}
|
|
14265
14278
|
}
|
|
14266
14279
|
}
|
|
14280
|
+
function ensureSessionSummaryColumns(db) {
|
|
14281
|
+
const required2 = [
|
|
14282
|
+
"capture_state",
|
|
14283
|
+
"recent_tool_names",
|
|
14284
|
+
"hot_files",
|
|
14285
|
+
"recent_outcomes"
|
|
14286
|
+
];
|
|
14287
|
+
for (const column of required2) {
|
|
14288
|
+
if (columnExists(db, "session_summaries", column))
|
|
14289
|
+
continue;
|
|
14290
|
+
db.exec(`ALTER TABLE session_summaries ADD COLUMN ${column} TEXT`);
|
|
14291
|
+
}
|
|
14292
|
+
const current = getSchemaVersion(db);
|
|
14293
|
+
if (current < 12) {
|
|
14294
|
+
db.exec("PRAGMA user_version = 12");
|
|
14295
|
+
}
|
|
14296
|
+
}
|
|
14267
14297
|
function getSchemaVersion(db) {
|
|
14268
14298
|
const result = db.query("PRAGMA user_version").get();
|
|
14269
14299
|
return result.user_version;
|
|
@@ -14422,6 +14452,7 @@ class MemDatabase {
|
|
|
14422
14452
|
this.vecAvailable = this.loadVecExtension();
|
|
14423
14453
|
runMigrations(this.db);
|
|
14424
14454
|
ensureObservationTypes(this.db);
|
|
14455
|
+
ensureSessionSummaryColumns(this.db);
|
|
14425
14456
|
}
|
|
14426
14457
|
loadVecExtension() {
|
|
14427
14458
|
try {
|
|
@@ -14647,6 +14678,10 @@ class MemDatabase {
|
|
|
14647
14678
|
p.name AS project_name,
|
|
14648
14679
|
ss.request AS request,
|
|
14649
14680
|
ss.completed AS completed,
|
|
14681
|
+
ss.capture_state AS capture_state,
|
|
14682
|
+
ss.recent_tool_names AS recent_tool_names,
|
|
14683
|
+
ss.hot_files AS hot_files,
|
|
14684
|
+
ss.recent_outcomes AS recent_outcomes,
|
|
14650
14685
|
(SELECT COUNT(*) FROM user_prompts up WHERE up.session_id = s.session_id) AS prompt_count,
|
|
14651
14686
|
(SELECT COUNT(*) FROM tool_events te WHERE te.session_id = s.session_id) AS tool_event_count
|
|
14652
14687
|
FROM sessions s
|
|
@@ -14661,6 +14696,10 @@ class MemDatabase {
|
|
|
14661
14696
|
p.name AS project_name,
|
|
14662
14697
|
ss.request AS request,
|
|
14663
14698
|
ss.completed AS completed,
|
|
14699
|
+
ss.capture_state AS capture_state,
|
|
14700
|
+
ss.recent_tool_names AS recent_tool_names,
|
|
14701
|
+
ss.hot_files AS hot_files,
|
|
14702
|
+
ss.recent_outcomes AS recent_outcomes,
|
|
14664
14703
|
(SELECT COUNT(*) FROM user_prompts up WHERE up.session_id = s.session_id) AS prompt_count,
|
|
14665
14704
|
(SELECT COUNT(*) FROM tool_events te WHERE te.session_id = s.session_id) AS tool_event_count
|
|
14666
14705
|
FROM sessions s
|
|
@@ -14833,8 +14872,11 @@ class MemDatabase {
|
|
|
14833
14872
|
completed: normalizeSummarySection(summary.completed),
|
|
14834
14873
|
next_steps: normalizeSummarySection(summary.next_steps)
|
|
14835
14874
|
};
|
|
14836
|
-
const result = this.db.query(`INSERT INTO session_summaries (
|
|
14837
|
-
|
|
14875
|
+
const result = this.db.query(`INSERT INTO session_summaries (
|
|
14876
|
+
session_id, project_id, user_id, request, investigated, learned, completed, next_steps,
|
|
14877
|
+
capture_state, recent_tool_names, hot_files, recent_outcomes, created_at_epoch
|
|
14878
|
+
)
|
|
14879
|
+
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
14880
|
const id = Number(result.lastInsertRowid);
|
|
14839
14881
|
return this.db.query("SELECT * FROM session_summaries WHERE id = ?").get(id);
|
|
14840
14882
|
}
|
|
@@ -14849,7 +14891,11 @@ class MemDatabase {
|
|
|
14849
14891
|
investigated: normalizeSummarySection(summary.investigated ?? existing.investigated),
|
|
14850
14892
|
learned: normalizeSummarySection(summary.learned ?? existing.learned),
|
|
14851
14893
|
completed: normalizeSummarySection(summary.completed ?? existing.completed),
|
|
14852
|
-
next_steps: normalizeSummarySection(summary.next_steps ?? existing.next_steps)
|
|
14894
|
+
next_steps: normalizeSummarySection(summary.next_steps ?? existing.next_steps),
|
|
14895
|
+
capture_state: summary.capture_state ?? existing.capture_state,
|
|
14896
|
+
recent_tool_names: summary.recent_tool_names ?? existing.recent_tool_names,
|
|
14897
|
+
hot_files: summary.hot_files ?? existing.hot_files,
|
|
14898
|
+
recent_outcomes: summary.recent_outcomes ?? existing.recent_outcomes
|
|
14853
14899
|
};
|
|
14854
14900
|
this.db.query(`UPDATE session_summaries
|
|
14855
14901
|
SET project_id = ?,
|
|
@@ -14859,8 +14905,12 @@ class MemDatabase {
|
|
|
14859
14905
|
learned = ?,
|
|
14860
14906
|
completed = ?,
|
|
14861
14907
|
next_steps = ?,
|
|
14908
|
+
capture_state = ?,
|
|
14909
|
+
recent_tool_names = ?,
|
|
14910
|
+
hot_files = ?,
|
|
14911
|
+
recent_outcomes = ?,
|
|
14862
14912
|
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);
|
|
14913
|
+
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
14914
|
return this.getSessionSummary(summary.session_id);
|
|
14865
14915
|
}
|
|
14866
14916
|
getSessionSummary(sessionId) {
|
|
@@ -16460,6 +16510,16 @@ function findStaleDecisionsGlobal(db, options) {
|
|
|
16460
16510
|
function tokenizeProjectHint(text) {
|
|
16461
16511
|
return Array.from(new Set((text.toLowerCase().match(/[a-z0-9_+-]{4,}/g) ?? []).filter(Boolean)));
|
|
16462
16512
|
}
|
|
16513
|
+
function parseSummaryJsonList(value) {
|
|
16514
|
+
if (!value)
|
|
16515
|
+
return [];
|
|
16516
|
+
try {
|
|
16517
|
+
const parsed = JSON.parse(value);
|
|
16518
|
+
return Array.isArray(parsed) ? parsed.filter((item) => typeof item === "string" && item.trim().length > 0) : [];
|
|
16519
|
+
} catch {
|
|
16520
|
+
return [];
|
|
16521
|
+
}
|
|
16522
|
+
}
|
|
16463
16523
|
function isObservationRelatedToProject(obs, detected) {
|
|
16464
16524
|
const hints = new Set([
|
|
16465
16525
|
...tokenizeProjectHint(detected.name),
|
|
@@ -16591,7 +16651,7 @@ function buildSessionContext(db, cwd, options = {}) {
|
|
|
16591
16651
|
const recentToolEvents2 = db.getRecentToolEvents(isNewProject ? null : projectId, isNewProject ? 8 : 6, opts.userId);
|
|
16592
16652
|
const recentSessions2 = isNewProject ? [] : db.getRecentSessions(projectId, 5, opts.userId);
|
|
16593
16653
|
const projectTypeCounts2 = isNewProject ? undefined : getProjectTypeCounts(db, projectId, opts.userId);
|
|
16594
|
-
const recentOutcomes2 = isNewProject ? undefined : getRecentOutcomes(db, projectId, opts.userId);
|
|
16654
|
+
const recentOutcomes2 = isNewProject ? undefined : getRecentOutcomes(db, projectId, opts.userId, recentSessions2);
|
|
16595
16655
|
return {
|
|
16596
16656
|
project_name: projectName,
|
|
16597
16657
|
canonical_id: canonicalId,
|
|
@@ -16629,7 +16689,7 @@ function buildSessionContext(db, cwd, options = {}) {
|
|
|
16629
16689
|
const recentToolEvents = db.getRecentToolEvents(isNewProject ? null : projectId, isNewProject ? 8 : 6, opts.userId);
|
|
16630
16690
|
const recentSessions = isNewProject ? [] : db.getRecentSessions(projectId, 5, opts.userId);
|
|
16631
16691
|
const projectTypeCounts = isNewProject ? undefined : getProjectTypeCounts(db, projectId, opts.userId);
|
|
16632
|
-
const recentOutcomes = isNewProject ? undefined : getRecentOutcomes(db, projectId, opts.userId);
|
|
16692
|
+
const recentOutcomes = isNewProject ? undefined : getRecentOutcomes(db, projectId, opts.userId, recentSessions);
|
|
16633
16693
|
let securityFindings = [];
|
|
16634
16694
|
if (!isNewProject) {
|
|
16635
16695
|
try {
|
|
@@ -16972,7 +17032,7 @@ function getProjectTypeCounts(db, projectId, userId) {
|
|
|
16972
17032
|
}
|
|
16973
17033
|
return counts;
|
|
16974
17034
|
}
|
|
16975
|
-
function getRecentOutcomes(db, projectId, userId) {
|
|
17035
|
+
function getRecentOutcomes(db, projectId, userId, recentSessions) {
|
|
16976
17036
|
const visibilityClause = userId ? " AND (sensitivity != 'personal' OR user_id = ?)" : "";
|
|
16977
17037
|
const visibilityParams = userId ? [userId] : [];
|
|
16978
17038
|
const summaries = db.db.query(`SELECT * FROM session_summaries
|
|
@@ -16982,6 +17042,15 @@ function getRecentOutcomes(db, projectId, userId) {
|
|
|
16982
17042
|
const picked = [];
|
|
16983
17043
|
const seen = new Set;
|
|
16984
17044
|
for (const summary of summaries) {
|
|
17045
|
+
for (const item of parseSummaryJsonList(summary.recent_outcomes)) {
|
|
17046
|
+
const normalized = item.toLowerCase().replace(/\s+/g, " ").trim();
|
|
17047
|
+
if (!normalized || seen.has(normalized))
|
|
17048
|
+
continue;
|
|
17049
|
+
seen.add(normalized);
|
|
17050
|
+
picked.push(item);
|
|
17051
|
+
if (picked.length >= 5)
|
|
17052
|
+
return picked;
|
|
17053
|
+
}
|
|
16985
17054
|
for (const line of [
|
|
16986
17055
|
...extractMeaningfulLines(summary.completed, 2),
|
|
16987
17056
|
...extractMeaningfulLines(summary.learned, 1)
|
|
@@ -16995,6 +17064,17 @@ function getRecentOutcomes(db, projectId, userId) {
|
|
|
16995
17064
|
return picked;
|
|
16996
17065
|
}
|
|
16997
17066
|
}
|
|
17067
|
+
for (const session of recentSessions ?? []) {
|
|
17068
|
+
for (const item of parseSummaryJsonList(session.recent_outcomes)) {
|
|
17069
|
+
const normalized = item.toLowerCase().replace(/\s+/g, " ").trim();
|
|
17070
|
+
if (!normalized || seen.has(normalized))
|
|
17071
|
+
continue;
|
|
17072
|
+
seen.add(normalized);
|
|
17073
|
+
picked.push(item);
|
|
17074
|
+
if (picked.length >= 5)
|
|
17075
|
+
return picked;
|
|
17076
|
+
}
|
|
17077
|
+
}
|
|
16998
17078
|
const rows = db.db.query(`SELECT * FROM observations
|
|
16999
17079
|
WHERE project_id = ?
|
|
17000
17080
|
AND lifecycle IN ('active', 'aging', 'pinned')
|
|
@@ -18653,6 +18733,50 @@ class VectorApiError extends Error {
|
|
|
18653
18733
|
}
|
|
18654
18734
|
}
|
|
18655
18735
|
|
|
18736
|
+
// src/capture/session-handoff.ts
|
|
18737
|
+
function buildSessionHandoffMetadata(prompts, toolEvents, observations) {
|
|
18738
|
+
const latestRequest = prompts.length > 0 ? prompts[prompts.length - 1]?.prompt ?? null : null;
|
|
18739
|
+
const recentRequestPrompts = prompts.slice(-3).map((prompt) => prompt.prompt.trim()).filter(Boolean);
|
|
18740
|
+
const recentToolNames = [...new Set(toolEvents.slice(-8).map((tool) => tool.tool_name).filter(Boolean))];
|
|
18741
|
+
const recentToolCommands = [...new Set(toolEvents.slice(-5).map((tool) => (tool.command ?? tool.file_path ?? "").trim()).filter(Boolean))];
|
|
18742
|
+
const hotFiles = [...new Set(observations.flatMap((obs) => [
|
|
18743
|
+
...parseJsonArray3(obs.files_modified),
|
|
18744
|
+
...parseJsonArray3(obs.files_read)
|
|
18745
|
+
]).filter(Boolean))].slice(0, 6);
|
|
18746
|
+
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);
|
|
18747
|
+
const captureState = prompts.length > 0 && toolEvents.length > 0 ? "rich" : prompts.length > 0 || toolEvents.length > 0 ? "partial" : "summary-only";
|
|
18748
|
+
const observationSourceTools = Array.from(observations.reduce((acc, obs) => {
|
|
18749
|
+
if (!obs.source_tool)
|
|
18750
|
+
return acc;
|
|
18751
|
+
acc.set(obs.source_tool, (acc.get(obs.source_tool) ?? 0) + 1);
|
|
18752
|
+
return acc;
|
|
18753
|
+
}, new Map).entries()).map(([tool, count]) => ({ tool, count })).sort((a, b) => b.count - a.count || a.tool.localeCompare(b.tool)).slice(0, 6);
|
|
18754
|
+
const latestObservationPromptNumber = observations.map((obs) => obs.source_prompt_number).filter((value) => typeof value === "number").sort((a, b) => b - a)[0] ?? null;
|
|
18755
|
+
return {
|
|
18756
|
+
prompt_count: prompts.length,
|
|
18757
|
+
tool_event_count: toolEvents.length,
|
|
18758
|
+
recent_request_prompts: recentRequestPrompts,
|
|
18759
|
+
latest_request: latestRequest,
|
|
18760
|
+
recent_tool_names: recentToolNames,
|
|
18761
|
+
recent_tool_commands: recentToolCommands,
|
|
18762
|
+
capture_state: captureState,
|
|
18763
|
+
hot_files: hotFiles,
|
|
18764
|
+
recent_outcomes: recentOutcomes,
|
|
18765
|
+
observation_source_tools: observationSourceTools,
|
|
18766
|
+
latest_observation_prompt_number: latestObservationPromptNumber
|
|
18767
|
+
};
|
|
18768
|
+
}
|
|
18769
|
+
function parseJsonArray3(value) {
|
|
18770
|
+
if (!value)
|
|
18771
|
+
return [];
|
|
18772
|
+
try {
|
|
18773
|
+
const parsed = JSON.parse(value);
|
|
18774
|
+
return Array.isArray(parsed) ? parsed.filter((item) => typeof item === "string" && item.trim().length > 0) : [];
|
|
18775
|
+
} catch {
|
|
18776
|
+
return [];
|
|
18777
|
+
}
|
|
18778
|
+
}
|
|
18779
|
+
|
|
18656
18780
|
// src/sync/push.ts
|
|
18657
18781
|
function buildVectorDocument(obs, config2, project) {
|
|
18658
18782
|
const parts = [obs.title];
|
|
@@ -18793,7 +18917,7 @@ async function pushOutbox(db, client, config2, batchSize = 50) {
|
|
|
18793
18917
|
const doc3 = buildSummaryVectorDocument(summary, config2, {
|
|
18794
18918
|
canonical_id: project2.canonical_id,
|
|
18795
18919
|
name: project2.name
|
|
18796
|
-
}, summaryObservations,
|
|
18920
|
+
}, summaryObservations, buildSessionHandoffMetadata(sessionPrompts, sessionToolEvents, summaryObservations));
|
|
18797
18921
|
batch.push({ entryId: entry.id, doc: doc3 });
|
|
18798
18922
|
continue;
|
|
18799
18923
|
}
|
|
@@ -18864,48 +18988,6 @@ function countPresentSections2(summary) {
|
|
|
18864
18988
|
function extractSectionItems2(section) {
|
|
18865
18989
|
return extractSummaryItems(section, 4);
|
|
18866
18990
|
}
|
|
18867
|
-
function buildSummaryCaptureContext(prompts, toolEvents, observations) {
|
|
18868
|
-
const latestRequest = prompts.length > 0 ? prompts[prompts.length - 1]?.prompt ?? null : null;
|
|
18869
|
-
const recentRequestPrompts = prompts.slice(-3).map((prompt) => prompt.prompt.trim()).filter(Boolean);
|
|
18870
|
-
const recentToolNames = [...new Set(toolEvents.slice(-8).map((tool) => tool.tool_name).filter(Boolean))];
|
|
18871
|
-
const recentToolCommands = [...new Set(toolEvents.slice(-5).map((tool) => (tool.command ?? tool.file_path ?? "").trim()).filter(Boolean))];
|
|
18872
|
-
const hotFiles = [...new Set(observations.flatMap((obs) => [
|
|
18873
|
-
...parseJsonArray3(obs.files_modified),
|
|
18874
|
-
...parseJsonArray3(obs.files_read)
|
|
18875
|
-
]).filter(Boolean))].slice(0, 6);
|
|
18876
|
-
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);
|
|
18877
|
-
const captureState = prompts.length > 0 && toolEvents.length > 0 ? "rich" : prompts.length > 0 || toolEvents.length > 0 ? "partial" : "summary-only";
|
|
18878
|
-
const observationSourceTools = Array.from(observations.reduce((acc, obs) => {
|
|
18879
|
-
if (!obs.source_tool)
|
|
18880
|
-
return acc;
|
|
18881
|
-
acc.set(obs.source_tool, (acc.get(obs.source_tool) ?? 0) + 1);
|
|
18882
|
-
return acc;
|
|
18883
|
-
}, new Map).entries()).map(([tool, count]) => ({ tool, count })).sort((a, b) => b.count - a.count || a.tool.localeCompare(b.tool)).slice(0, 6);
|
|
18884
|
-
const latestObservationPromptNumber = observations.map((obs) => obs.source_prompt_number).filter((value) => typeof value === "number").sort((a, b) => b - a)[0] ?? null;
|
|
18885
|
-
return {
|
|
18886
|
-
prompt_count: prompts.length,
|
|
18887
|
-
tool_event_count: toolEvents.length,
|
|
18888
|
-
recent_request_prompts: recentRequestPrompts,
|
|
18889
|
-
latest_request: latestRequest,
|
|
18890
|
-
recent_tool_names: recentToolNames,
|
|
18891
|
-
recent_tool_commands: recentToolCommands,
|
|
18892
|
-
capture_state: captureState,
|
|
18893
|
-
hot_files: hotFiles,
|
|
18894
|
-
recent_outcomes: recentOutcomes,
|
|
18895
|
-
observation_source_tools: observationSourceTools,
|
|
18896
|
-
latest_observation_prompt_number: latestObservationPromptNumber
|
|
18897
|
-
};
|
|
18898
|
-
}
|
|
18899
|
-
function parseJsonArray3(value) {
|
|
18900
|
-
if (!value)
|
|
18901
|
-
return [];
|
|
18902
|
-
try {
|
|
18903
|
-
const parsed = JSON.parse(value);
|
|
18904
|
-
return Array.isArray(parsed) ? parsed.filter((item) => typeof item === "string" && item.trim().length > 0) : [];
|
|
18905
|
-
} catch {
|
|
18906
|
-
return [];
|
|
18907
|
-
}
|
|
18908
|
-
}
|
|
18909
18991
|
|
|
18910
18992
|
// src/sync/pull.ts
|
|
18911
18993
|
var PULL_CURSOR_KEY = "pull_cursor";
|
|
@@ -19011,10 +19093,20 @@ function mergeRemoteSummary(db, config2, change, projectId) {
|
|
|
19011
19093
|
investigated: typeof change.metadata?.investigated === "string" ? change.metadata.investigated : null,
|
|
19012
19094
|
learned: typeof change.metadata?.learned === "string" ? change.metadata.learned : null,
|
|
19013
19095
|
completed: typeof change.metadata?.completed === "string" ? change.metadata.completed : null,
|
|
19014
|
-
next_steps: typeof change.metadata?.next_steps === "string" ? change.metadata.next_steps : null
|
|
19096
|
+
next_steps: typeof change.metadata?.next_steps === "string" ? change.metadata.next_steps : null,
|
|
19097
|
+
capture_state: typeof change.metadata?.capture_state === "string" ? change.metadata.capture_state : null,
|
|
19098
|
+
recent_tool_names: encodeStringArray(change.metadata?.recent_tool_names),
|
|
19099
|
+
hot_files: encodeStringArray(change.metadata?.hot_files),
|
|
19100
|
+
recent_outcomes: encodeStringArray(change.metadata?.recent_outcomes)
|
|
19015
19101
|
});
|
|
19016
19102
|
return Boolean(summary);
|
|
19017
19103
|
}
|
|
19104
|
+
function encodeStringArray(value) {
|
|
19105
|
+
if (!Array.isArray(value))
|
|
19106
|
+
return null;
|
|
19107
|
+
const normalized = value.filter((item) => typeof item === "string").map((item) => item.trim()).filter(Boolean);
|
|
19108
|
+
return normalized.length > 0 ? JSON.stringify(normalized) : null;
|
|
19109
|
+
}
|
|
19018
19110
|
function normalizeRemoteObservationType(rawType, sourceId) {
|
|
19019
19111
|
const type = typeof rawType === "string" ? rawType.trim().toLowerCase() : "";
|
|
19020
19112
|
if (type === "bugfix" || type === "discovery" || type === "decision" || type === "pattern" || type === "change" || type === "feature" || type === "refactor" || type === "digest" || type === "standard" || type === "message") {
|
|
@@ -19817,7 +19909,7 @@ process.on("SIGTERM", () => {
|
|
|
19817
19909
|
});
|
|
19818
19910
|
var server = new McpServer({
|
|
19819
19911
|
name: "engrm",
|
|
19820
|
-
version: "0.4.
|
|
19912
|
+
version: "0.4.22"
|
|
19821
19913
|
});
|
|
19822
19914
|
server.tool("save_observation", "Save an observation to memory", {
|
|
19823
19915
|
type: exports_external.enum([
|