engrm 0.4.14 → 0.4.16
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/README.md +34 -0
- package/dist/hooks/session-start.js +125 -25
- package/dist/hooks/stop.js +1 -1
- package/dist/server.js +29 -29
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -228,6 +228,28 @@ The MCP server exposes tools that supported agents can call directly:
|
|
|
228
228
|
| `capture_repo_scan` | Run a lightweight repo scan and save reduced findings as memory |
|
|
229
229
|
| `capture_openclaw_content` | Save OpenClaw content, research, and follow-up work as plugin memory |
|
|
230
230
|
|
|
231
|
+
### Public MCP Starter Set
|
|
232
|
+
|
|
233
|
+
If you are evaluating Engrm as an MCP server, start with this small set first:
|
|
234
|
+
|
|
235
|
+
- `capture_git_worktree`
|
|
236
|
+
- save a meaningful local git diff before it disappears into commit history
|
|
237
|
+
- `capture_repo_scan`
|
|
238
|
+
- capture a quick architecture, implementation, or risk scan as reusable memory
|
|
239
|
+
- `capture_openclaw_content`
|
|
240
|
+
- save posted content, research, outcomes, and next actions from OpenClaw work
|
|
241
|
+
- `tool_memory_index`
|
|
242
|
+
- verify which tools are actually producing durable memory and which plugins they exercise
|
|
243
|
+
- `capture_quality`
|
|
244
|
+
- check whether raw chronology is healthy across the workspace before judging memory quality
|
|
245
|
+
|
|
246
|
+
These are the tools we should be comfortable pointing people to publicly first:
|
|
247
|
+
|
|
248
|
+
- thin input surface
|
|
249
|
+
- local-first execution
|
|
250
|
+
- durable memory output instead of raw transcript dumping
|
|
251
|
+
- easy local inspection after capture
|
|
252
|
+
|
|
231
253
|
### Thin Tools, Thick Memory
|
|
232
254
|
|
|
233
255
|
Engrm now has a real thin-tool layer, not just a plugin spec.
|
|
@@ -301,6 +323,18 @@ That lets you:
|
|
|
301
323
|
- capture the current repo state with a thin tool
|
|
302
324
|
- verify whether that tool produced reusable memory
|
|
303
325
|
|
|
326
|
+
### MCP Examples
|
|
327
|
+
|
|
328
|
+
These are the kinds of prompts Engrm's current MCP slice is designed for:
|
|
329
|
+
|
|
330
|
+
- "Capture this current git worktree as memory before I switch tasks."
|
|
331
|
+
- "Run a lightweight repo scan focused on auth and validation."
|
|
332
|
+
- "Show which tools are actually creating durable memory in this repo."
|
|
333
|
+
- "Tell me whether raw prompt/tool capture is healthy on this machine."
|
|
334
|
+
- "Save this OpenClaw research/posting run as reusable memory."
|
|
335
|
+
|
|
336
|
+
For concrete example flows and reducer outputs, see [MCP_EXAMPLES.md](/Volumes/Data/devs/candengo-mem/MCP_EXAMPLES.md).
|
|
337
|
+
|
|
304
338
|
### Observation Types
|
|
305
339
|
|
|
306
340
|
| Type | What it captures |
|
|
@@ -1154,7 +1154,7 @@ import { existsSync as existsSync3, readFileSync as readFileSync2, writeFileSync
|
|
|
1154
1154
|
import { join as join3 } from "node:path";
|
|
1155
1155
|
import { homedir } from "node:os";
|
|
1156
1156
|
var STATE_PATH = join3(homedir(), ".engrm", "config-fingerprint.json");
|
|
1157
|
-
var CLIENT_VERSION = "0.4.
|
|
1157
|
+
var CLIENT_VERSION = "0.4.16";
|
|
1158
1158
|
function hashFile(filePath) {
|
|
1159
1159
|
try {
|
|
1160
1160
|
if (!existsSync3(filePath))
|
|
@@ -3133,11 +3133,13 @@ function formatSplashScreen(data) {
|
|
|
3133
3133
|
lines.push("");
|
|
3134
3134
|
lines.push(` ${c2.dim}Dashboard: https://engrm.dev/dashboard${c2.reset}`);
|
|
3135
3135
|
const brief = formatVisibleStartupBrief(data.context);
|
|
3136
|
+
const handoffShownItems = new Set;
|
|
3136
3137
|
if (brief.length > 0) {
|
|
3137
3138
|
lines.push("");
|
|
3138
|
-
lines.push(` ${c2.bold}
|
|
3139
|
+
lines.push(` ${c2.bold}Handoff${c2.reset}`);
|
|
3139
3140
|
for (const line of brief) {
|
|
3140
3141
|
lines.push(` ${line}`);
|
|
3142
|
+
rememberShownItem(handoffShownItems, line);
|
|
3141
3143
|
}
|
|
3142
3144
|
}
|
|
3143
3145
|
const economics = formatContextEconomics(data);
|
|
@@ -3154,7 +3156,7 @@ function formatSplashScreen(data) {
|
|
|
3154
3156
|
lines.push(` ${line}`);
|
|
3155
3157
|
}
|
|
3156
3158
|
}
|
|
3157
|
-
const contextIndex = formatContextIndex(data.context);
|
|
3159
|
+
const contextIndex = formatContextIndex(data.context, handoffShownItems);
|
|
3158
3160
|
if (contextIndex.length > 0) {
|
|
3159
3161
|
lines.push("");
|
|
3160
3162
|
for (const line of contextIndex) {
|
|
@@ -3186,7 +3188,7 @@ function formatVisibleStartupBrief(context) {
|
|
|
3186
3188
|
const projectSignals = buildProjectSignalLine(context);
|
|
3187
3189
|
const shownItems = new Set;
|
|
3188
3190
|
if (promptLines.length > 0) {
|
|
3189
|
-
lines.push(`${c2.cyan}
|
|
3191
|
+
lines.push(`${c2.cyan}Asked recently:${c2.reset}`);
|
|
3190
3192
|
for (const item of promptLines) {
|
|
3191
3193
|
lines.push(` - ${truncateInline(item, 160)}`);
|
|
3192
3194
|
rememberShownItem(shownItems, item);
|
|
@@ -3218,13 +3220,13 @@ function formatVisibleStartupBrief(context) {
|
|
|
3218
3220
|
}
|
|
3219
3221
|
}
|
|
3220
3222
|
} else if (currentRequest && !duplicatesPromptLine(currentRequest, latestPromptLine)) {
|
|
3221
|
-
lines.push(`${c2.cyan}
|
|
3223
|
+
lines.push(`${c2.cyan}What you're on:${c2.reset}`);
|
|
3222
3224
|
lines.push(` - ${truncateInline(currentRequest, 160)}`);
|
|
3223
3225
|
rememberShownItem(shownItems, currentRequest);
|
|
3224
3226
|
if (toolFallbacks.length > 0) {
|
|
3225
3227
|
const additiveTools = filterAdditiveToolFallbacks(toolFallbacks, shownItems);
|
|
3226
3228
|
if (additiveTools.length > 0) {
|
|
3227
|
-
lines.push(`${c2.cyan}
|
|
3229
|
+
lines.push(`${c2.cyan}Tool trail:${c2.reset}`);
|
|
3228
3230
|
for (const item of additiveTools) {
|
|
3229
3231
|
lines.push(` - ${truncateInline(item, 160)}`);
|
|
3230
3232
|
rememberShownItem(shownItems, item);
|
|
@@ -3233,12 +3235,12 @@ function formatVisibleStartupBrief(context) {
|
|
|
3233
3235
|
}
|
|
3234
3236
|
}
|
|
3235
3237
|
if (latest && currentRequest && !hasRequestSection(lines) && !duplicatesPromptLine(currentRequest, latestPromptLine)) {
|
|
3236
|
-
lines.push(`${c2.cyan}
|
|
3238
|
+
lines.push(`${c2.cyan}What you're on:${c2.reset}`);
|
|
3237
3239
|
lines.push(` - ${truncateInline(currentRequest, 160)}`);
|
|
3238
3240
|
rememberShownItem(shownItems, currentRequest);
|
|
3239
3241
|
}
|
|
3240
3242
|
if (recentOutcomeLines.length > 0) {
|
|
3241
|
-
lines.push(`${c2.cyan}
|
|
3243
|
+
lines.push(`${c2.cyan}What's moved:${c2.reset}`);
|
|
3242
3244
|
for (const item of recentOutcomeLines) {
|
|
3243
3245
|
lines.push(` - ${truncateInline(item, 160)}`);
|
|
3244
3246
|
rememberShownItem(shownItems, item);
|
|
@@ -3247,7 +3249,7 @@ function formatVisibleStartupBrief(context) {
|
|
|
3247
3249
|
if (toolFallbacks.length > 0 && latest) {
|
|
3248
3250
|
const additiveTools = filterAdditiveToolFallbacks(toolFallbacks, shownItems);
|
|
3249
3251
|
if (additiveTools.length > 0) {
|
|
3250
|
-
lines.push(`${c2.cyan}
|
|
3252
|
+
lines.push(`${c2.cyan}Tool trail:${c2.reset}`);
|
|
3251
3253
|
for (const item of additiveTools) {
|
|
3252
3254
|
lines.push(` - ${truncateInline(item, 160)}`);
|
|
3253
3255
|
rememberShownItem(shownItems, item);
|
|
@@ -3255,13 +3257,13 @@ function formatVisibleStartupBrief(context) {
|
|
|
3255
3257
|
}
|
|
3256
3258
|
}
|
|
3257
3259
|
if (sessionFallbacks.length > 0) {
|
|
3258
|
-
lines.push(`${c2.cyan}Recent
|
|
3260
|
+
lines.push(`${c2.cyan}Recent threads:${c2.reset}`);
|
|
3259
3261
|
for (const item of sessionFallbacks) {
|
|
3260
3262
|
lines.push(` - ${truncateInline(item, 160)}`);
|
|
3261
3263
|
}
|
|
3262
3264
|
}
|
|
3263
3265
|
if (projectSignals) {
|
|
3264
|
-
lines.push(`${c2.cyan}
|
|
3266
|
+
lines.push(`${c2.cyan}Signal mix:${c2.reset}`);
|
|
3265
3267
|
lines.push(` - ${truncateInline(projectSignals, 160)}`);
|
|
3266
3268
|
}
|
|
3267
3269
|
const stale = pickRelevantStaleDecision(context, latest);
|
|
@@ -3294,11 +3296,11 @@ function formatContextEconomics(data) {
|
|
|
3294
3296
|
}
|
|
3295
3297
|
function formatLegend() {
|
|
3296
3298
|
return [
|
|
3297
|
-
`${c2.dim}Legend:${c2.reset} #id | \
|
|
3299
|
+
`${c2.dim}Legend:${c2.reset} #id | \u25A0 bugfix | \u25B2 feature | \u2248 refactor | \u25CF change | \u25A1 discovery | \u25C7 decision`
|
|
3298
3300
|
];
|
|
3299
3301
|
}
|
|
3300
|
-
function formatContextIndex(context) {
|
|
3301
|
-
const rows = context
|
|
3302
|
+
function formatContextIndex(context, shownItems) {
|
|
3303
|
+
const rows = pickContextIndexObservations(context, shownItems).map((obs) => {
|
|
3302
3304
|
const icon = observationIcon(obs.type);
|
|
3303
3305
|
const fileHint = extractPrimaryFileHint(obs);
|
|
3304
3306
|
return `${icon} #${obs.id} ${truncateInline(obs.title, 110)}${fileHint ? ` ${c2.dim}(${fileHint})${c2.reset}` : ""}`;
|
|
@@ -3306,7 +3308,7 @@ function formatContextIndex(context) {
|
|
|
3306
3308
|
if (rows.length === 0)
|
|
3307
3309
|
return [];
|
|
3308
3310
|
return [
|
|
3309
|
-
`${c2.dim}
|
|
3311
|
+
`${c2.dim}Handoff index:${c2.reset} use IDs when you want the deeper thread`,
|
|
3310
3312
|
...rows
|
|
3311
3313
|
];
|
|
3312
3314
|
}
|
|
@@ -3328,8 +3330,8 @@ function formatInspectHints(context) {
|
|
|
3328
3330
|
const ids = context.observations.slice(0, 5).map((obs) => obs.id);
|
|
3329
3331
|
const fetchHint = ids.length > 0 ? `get_observations([${ids.join(", ")}])` : null;
|
|
3330
3332
|
return [
|
|
3331
|
-
`${c2.dim}
|
|
3332
|
-
...fetchHint ? [`${c2.dim}
|
|
3333
|
+
`${c2.dim}Next look:${c2.reset} ${unique.join(" \xB7 ")}`,
|
|
3334
|
+
...fetchHint ? [`${c2.dim}Pull detail:${c2.reset} ${fetchHint}`] : []
|
|
3333
3335
|
];
|
|
3334
3336
|
}
|
|
3335
3337
|
function rememberShownItem(shown, value) {
|
|
@@ -3452,23 +3454,41 @@ function chooseMeaningfulSessionSummary(request, completed) {
|
|
|
3452
3454
|
function buildProjectSignalLine(context) {
|
|
3453
3455
|
if (!context.projectTypeCounts)
|
|
3454
3456
|
return null;
|
|
3455
|
-
const top = Object.entries(context.projectTypeCounts).sort((a, b) => b[1] - a[1] || a[0].localeCompare(b[0])).slice(0, 4).map(([type, count]) => `${type} ${count}`).join("; ");
|
|
3457
|
+
const top = Object.entries(context.projectTypeCounts).sort((a, b) => b[1] - a[1] || a[0].localeCompare(b[0])).slice(0, 4).map(([type, count]) => `${signalGlyph(type)} ${type} ${count}`).join("; ");
|
|
3456
3458
|
return top || null;
|
|
3457
3459
|
}
|
|
3460
|
+
function signalGlyph(type) {
|
|
3461
|
+
switch (type) {
|
|
3462
|
+
case "bugfix":
|
|
3463
|
+
return "\u25A0";
|
|
3464
|
+
case "feature":
|
|
3465
|
+
return "\u25B2";
|
|
3466
|
+
case "refactor":
|
|
3467
|
+
return "\u2248";
|
|
3468
|
+
case "change":
|
|
3469
|
+
return "\u25CF";
|
|
3470
|
+
case "discovery":
|
|
3471
|
+
return "\u25A1";
|
|
3472
|
+
case "decision":
|
|
3473
|
+
return "\u25C7";
|
|
3474
|
+
default:
|
|
3475
|
+
return "\xB7";
|
|
3476
|
+
}
|
|
3477
|
+
}
|
|
3458
3478
|
function observationIcon(type) {
|
|
3459
3479
|
switch (type) {
|
|
3460
3480
|
case "bugfix":
|
|
3461
|
-
return "\
|
|
3481
|
+
return "\u25A0";
|
|
3462
3482
|
case "feature":
|
|
3463
|
-
return "\
|
|
3483
|
+
return "\u25B2";
|
|
3464
3484
|
case "refactor":
|
|
3465
|
-
return "\
|
|
3485
|
+
return "\u2248";
|
|
3466
3486
|
case "change":
|
|
3467
|
-
return "\
|
|
3487
|
+
return "\u25CF";
|
|
3468
3488
|
case "discovery":
|
|
3469
|
-
return "\
|
|
3489
|
+
return "\u25A1";
|
|
3470
3490
|
case "decision":
|
|
3471
|
-
return "\
|
|
3491
|
+
return "\u25C7";
|
|
3472
3492
|
default:
|
|
3473
3493
|
return "\u2022";
|
|
3474
3494
|
}
|
|
@@ -3478,6 +3498,69 @@ function extractPrimaryFileHint(obs) {
|
|
|
3478
3498
|
const firstModified = parseJsonArraySafe(obs.files_modified)[0];
|
|
3479
3499
|
return firstModified ?? firstRead ?? null;
|
|
3480
3500
|
}
|
|
3501
|
+
function pickContextIndexObservations(context, shownItems) {
|
|
3502
|
+
const now = Date.now();
|
|
3503
|
+
const hidden = shownItems ?? new Set;
|
|
3504
|
+
const picked = [];
|
|
3505
|
+
const scoreObservation = (obs) => {
|
|
3506
|
+
let score = 0;
|
|
3507
|
+
const ageMs = Math.max(0, now - new Date(obs.created_at).getTime());
|
|
3508
|
+
const ageDays = ageMs / 86400000;
|
|
3509
|
+
score += Math.max(0, 30 - ageDays) * 0.2;
|
|
3510
|
+
score += obs.quality * 2;
|
|
3511
|
+
switch (obs.type) {
|
|
3512
|
+
case "bugfix":
|
|
3513
|
+
score += 2.4;
|
|
3514
|
+
break;
|
|
3515
|
+
case "feature":
|
|
3516
|
+
score += 2.2;
|
|
3517
|
+
break;
|
|
3518
|
+
case "change":
|
|
3519
|
+
score += 1.6;
|
|
3520
|
+
break;
|
|
3521
|
+
case "discovery":
|
|
3522
|
+
score += 1.4;
|
|
3523
|
+
break;
|
|
3524
|
+
case "refactor":
|
|
3525
|
+
score += 1.2;
|
|
3526
|
+
break;
|
|
3527
|
+
case "decision":
|
|
3528
|
+
if (ageDays <= 7)
|
|
3529
|
+
score += 1.1;
|
|
3530
|
+
else if (ageDays <= 21)
|
|
3531
|
+
score += 0.2;
|
|
3532
|
+
else if (ageDays <= 45)
|
|
3533
|
+
score -= 1.2;
|
|
3534
|
+
else
|
|
3535
|
+
score -= 2.8;
|
|
3536
|
+
break;
|
|
3537
|
+
default:
|
|
3538
|
+
score += 0.4;
|
|
3539
|
+
break;
|
|
3540
|
+
}
|
|
3541
|
+
if (extractPrimaryFileHint(obs))
|
|
3542
|
+
score += 0.4;
|
|
3543
|
+
if (context.recentOutcomes?.some((item) => titlesRoughlyMatch(item, obs.title)))
|
|
3544
|
+
score += 2.5;
|
|
3545
|
+
return score;
|
|
3546
|
+
};
|
|
3547
|
+
for (const obs of context.observations.filter((obs2) => obs2.type !== "digest").filter((obs2) => {
|
|
3548
|
+
const normalized = normalizeStartupItem(obs2.title);
|
|
3549
|
+
return normalized && !hidden.has(normalized);
|
|
3550
|
+
}).sort((a, b) => {
|
|
3551
|
+
const scoreDiff = scoreObservation(b) - scoreObservation(a);
|
|
3552
|
+
if (scoreDiff !== 0)
|
|
3553
|
+
return scoreDiff;
|
|
3554
|
+
return new Date(b.created_at).getTime() - new Date(a.created_at).getTime();
|
|
3555
|
+
})) {
|
|
3556
|
+
if (picked.some((existing) => titlesRoughlyMatch(existing.title, obs.title)))
|
|
3557
|
+
continue;
|
|
3558
|
+
picked.push(obs);
|
|
3559
|
+
if (picked.length >= 6)
|
|
3560
|
+
break;
|
|
3561
|
+
}
|
|
3562
|
+
return picked;
|
|
3563
|
+
}
|
|
3481
3564
|
function parseJsonArraySafe(value) {
|
|
3482
3565
|
if (!value)
|
|
3483
3566
|
return [];
|
|
@@ -3548,7 +3631,24 @@ function hasRequestSection(lines) {
|
|
|
3548
3631
|
return lines.some((line) => line.includes("Request:"));
|
|
3549
3632
|
}
|
|
3550
3633
|
function normalizeStartupItem(value) {
|
|
3551
|
-
return stripInlineSectionLabel2(value).replace(/^#?\d+:\s*/, "").replace(/^-\s*/, "").toLowerCase().replace(/\s+/g, " ").trim();
|
|
3634
|
+
return stripInlineSectionLabel2(value).replace(/^#?\d+:\s*/, "").replace(/^-\s*/, "").replace(/\([^)]*\)/g, " ").replace(/[^a-z0-9\s]/gi, " ").toLowerCase().replace(/\s+/g, " ").trim();
|
|
3635
|
+
}
|
|
3636
|
+
function titlesRoughlyMatch(left, right) {
|
|
3637
|
+
const a = normalizeStartupItem(left ?? "");
|
|
3638
|
+
const b = normalizeStartupItem(right ?? "");
|
|
3639
|
+
if (!a || !b)
|
|
3640
|
+
return false;
|
|
3641
|
+
if (a === b)
|
|
3642
|
+
return true;
|
|
3643
|
+
if (a.includes(b) || b.includes(a))
|
|
3644
|
+
return true;
|
|
3645
|
+
const aTokens = a.split(" ").filter((token) => token.length >= 4);
|
|
3646
|
+
const bTokens = b.split(" ").filter((token) => token.length >= 4);
|
|
3647
|
+
if (!aTokens.length || !bTokens.length)
|
|
3648
|
+
return false;
|
|
3649
|
+
const shared = aTokens.filter((token) => bTokens.includes(token));
|
|
3650
|
+
const minSize = Math.min(aTokens.length, bTokens.length);
|
|
3651
|
+
return shared.length >= Math.max(3, Math.ceil(minSize * 0.6));
|
|
3552
3652
|
}
|
|
3553
3653
|
function isMeaningfulPrompt2(value) {
|
|
3554
3654
|
if (!value)
|
package/dist/hooks/stop.js
CHANGED
|
@@ -2535,7 +2535,7 @@ function buildBeacon(db, config, sessionId, metrics) {
|
|
|
2535
2535
|
sentinel_used: valueSignals.security_findings_count > 0,
|
|
2536
2536
|
risk_score: riskScore,
|
|
2537
2537
|
stacks_detected: stacks,
|
|
2538
|
-
client_version: "0.4.
|
|
2538
|
+
client_version: "0.4.16",
|
|
2539
2539
|
context_observations_injected: metrics?.contextObsInjected ?? 0,
|
|
2540
2540
|
context_total_available: metrics?.contextTotalAvailable ?? 0,
|
|
2541
2541
|
recall_attempts: metrics?.recallAttempts ?? 0,
|
package/dist/server.js
CHANGED
|
@@ -19764,7 +19764,7 @@ process.on("SIGTERM", () => {
|
|
|
19764
19764
|
});
|
|
19765
19765
|
var server = new McpServer({
|
|
19766
19766
|
name: "engrm",
|
|
19767
|
-
version: "0.4.
|
|
19767
|
+
version: "0.4.16"
|
|
19768
19768
|
});
|
|
19769
19769
|
server.tool("save_observation", "Save an observation to memory", {
|
|
19770
19770
|
type: exports_external.enum([
|
|
@@ -19940,11 +19940,11 @@ Facts: ${reduced.facts.join("; ")}` : "";
|
|
|
19940
19940
|
]
|
|
19941
19941
|
};
|
|
19942
19942
|
});
|
|
19943
|
-
server.tool("capture_git_worktree", "
|
|
19944
|
-
cwd: exports_external.string().optional().describe("Git repo path
|
|
19945
|
-
staged: exports_external.boolean().optional().describe("
|
|
19946
|
-
summary: exports_external.string().optional().describe("Optional human summary or commit-style title"),
|
|
19947
|
-
session_id: exports_external.string().optional()
|
|
19943
|
+
server.tool("capture_git_worktree", "Capture the current git worktree as durable memory. Best for saving a meaningful local diff before context is lost.", {
|
|
19944
|
+
cwd: exports_external.string().optional().describe("Git repo path. Defaults to the current working directory."),
|
|
19945
|
+
staged: exports_external.boolean().optional().describe("If true, capture staged changes instead of unstaged worktree changes."),
|
|
19946
|
+
summary: exports_external.string().optional().describe("Optional human summary or commit-style title to steer the saved memory."),
|
|
19947
|
+
session_id: exports_external.string().optional().describe("Optional session ID to link this capture to active work.")
|
|
19948
19948
|
}, async (params) => {
|
|
19949
19949
|
let worktree;
|
|
19950
19950
|
try {
|
|
@@ -20000,12 +20000,12 @@ server.tool("capture_git_worktree", "Read the current git worktree diff, reduce
|
|
|
20000
20000
|
]
|
|
20001
20001
|
};
|
|
20002
20002
|
});
|
|
20003
|
-
server.tool("capture_repo_scan", "Run a lightweight repository scan
|
|
20004
|
-
cwd: exports_external.string().optional().describe("Repo path to scan
|
|
20005
|
-
focus: exports_external.array(exports_external.string()).optional().describe("Optional
|
|
20006
|
-
max_findings: exports_external.number().optional().describe("Maximum findings to keep"),
|
|
20007
|
-
summary: exports_external.string().optional().describe("Optional human summary for the
|
|
20008
|
-
session_id: exports_external.string().optional()
|
|
20003
|
+
server.tool("capture_repo_scan", "Run a lightweight repository scan and save reduced findings as durable memory. Best for quick architecture, risk, or implementation scans.", {
|
|
20004
|
+
cwd: exports_external.string().optional().describe("Repo path to scan. Defaults to the current working directory."),
|
|
20005
|
+
focus: exports_external.array(exports_external.string()).optional().describe("Optional topics to bias the scan toward, for example 'billing', 'auth', or 'validation'."),
|
|
20006
|
+
max_findings: exports_external.number().optional().describe("Maximum findings to keep before reduction."),
|
|
20007
|
+
summary: exports_external.string().optional().describe("Optional human summary for the saved memory."),
|
|
20008
|
+
session_id: exports_external.string().optional().describe("Optional session ID to link this scan to active work.")
|
|
20009
20009
|
}, async (params) => {
|
|
20010
20010
|
let scan;
|
|
20011
20011
|
try {
|
|
@@ -20063,15 +20063,15 @@ Findings: ${findingSummary}` : ""}`
|
|
|
20063
20063
|
]
|
|
20064
20064
|
};
|
|
20065
20065
|
});
|
|
20066
|
-
server.tool("capture_openclaw_content", "
|
|
20067
|
-
title: exports_external.string().optional().describe("Short content or
|
|
20068
|
-
posted: exports_external.array(exports_external.string()).optional().describe("Concrete posted items or shipped content outcomes"),
|
|
20069
|
-
researched: exports_external.array(exports_external.string()).optional().describe("Research or discovery items"),
|
|
20070
|
-
outcomes: exports_external.array(exports_external.string()).optional().describe("Meaningful outcomes from the run"),
|
|
20071
|
-
next_actions: exports_external.array(exports_external.string()).optional().describe("Real follow-up actions"),
|
|
20072
|
-
links: exports_external.array(exports_external.string()).optional().describe("Thread or source URLs"),
|
|
20073
|
-
session_id: exports_external.string().optional(),
|
|
20074
|
-
cwd: exports_external.string().optional()
|
|
20066
|
+
server.tool("capture_openclaw_content", "Capture OpenClaw content, research, and follow-up work as durable memory. Best for preserving posted outcomes, discoveries, and next actions.", {
|
|
20067
|
+
title: exports_external.string().optional().describe("Short content, campaign, or research title."),
|
|
20068
|
+
posted: exports_external.array(exports_external.string()).optional().describe("Concrete posted items or shipped content outcomes."),
|
|
20069
|
+
researched: exports_external.array(exports_external.string()).optional().describe("Research or discovery items worth retaining."),
|
|
20070
|
+
outcomes: exports_external.array(exports_external.string()).optional().describe("Meaningful outcomes from the run."),
|
|
20071
|
+
next_actions: exports_external.array(exports_external.string()).optional().describe("Real follow-up actions that remain."),
|
|
20072
|
+
links: exports_external.array(exports_external.string()).optional().describe("Thread or source URLs tied to the work."),
|
|
20073
|
+
session_id: exports_external.string().optional().describe("Optional session ID to link this memory to active work."),
|
|
20074
|
+
cwd: exports_external.string().optional().describe("Optional project path for attribution.")
|
|
20075
20075
|
}, async (params) => {
|
|
20076
20076
|
const reduced = reduceOpenClawContentToMemory({
|
|
20077
20077
|
...params,
|
|
@@ -20536,9 +20536,9 @@ server.tool("capture_status", "Show whether Engrm hook registration and recent p
|
|
|
20536
20536
|
]
|
|
20537
20537
|
};
|
|
20538
20538
|
});
|
|
20539
|
-
server.tool("capture_quality", "Show
|
|
20540
|
-
limit: exports_external.number().optional(),
|
|
20541
|
-
user_id: exports_external.string().optional()
|
|
20539
|
+
server.tool("capture_quality", "Show how healthy Engrm capture is across the workspace: raw chronology coverage, checkpoints, and provenance by tool.", {
|
|
20540
|
+
limit: exports_external.number().optional().describe("Maximum projects to include in the top-projects section."),
|
|
20541
|
+
user_id: exports_external.string().optional().describe("Optional user override; defaults to the configured user.")
|
|
20542
20542
|
}, async (params) => {
|
|
20543
20543
|
const result = getCaptureQuality(db, {
|
|
20544
20544
|
limit: params.limit,
|
|
@@ -20575,11 +20575,11 @@ ${projectLines}`
|
|
|
20575
20575
|
]
|
|
20576
20576
|
};
|
|
20577
20577
|
});
|
|
20578
|
-
server.tool("tool_memory_index", "Show which
|
|
20579
|
-
cwd: exports_external.string().optional(),
|
|
20580
|
-
project_scoped: exports_external.boolean().optional(),
|
|
20581
|
-
limit: exports_external.number().optional(),
|
|
20582
|
-
user_id: exports_external.string().optional()
|
|
20578
|
+
server.tool("tool_memory_index", "Show which tools are actually producing durable memory, which plugins they exercise, and what memory types they create.", {
|
|
20579
|
+
cwd: exports_external.string().optional().describe("Project path to inspect. Defaults to the current working directory."),
|
|
20580
|
+
project_scoped: exports_external.boolean().optional().describe("If true, limit results to the current project instead of the whole workspace."),
|
|
20581
|
+
limit: exports_external.number().optional().describe("Maximum tools to include."),
|
|
20582
|
+
user_id: exports_external.string().optional().describe("Optional user override; defaults to the configured user.")
|
|
20583
20583
|
}, async (params) => {
|
|
20584
20584
|
const result = getToolMemoryIndex(db, {
|
|
20585
20585
|
cwd: params.cwd ?? process.cwd(),
|