engrm 0.4.13 → 0.4.15

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.
@@ -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.13";
1157
+ var CLIENT_VERSION = "0.4.15";
1158
1158
  function hashFile(filePath) {
1159
1159
  try {
1160
1160
  if (!existsSync3(filePath))
@@ -3135,7 +3135,7 @@ function formatSplashScreen(data) {
3135
3135
  const brief = formatVisibleStartupBrief(data.context);
3136
3136
  if (brief.length > 0) {
3137
3137
  lines.push("");
3138
- lines.push(` ${c2.bold}Startup context${c2.reset}`);
3138
+ lines.push(` ${c2.bold}Handoff${c2.reset}`);
3139
3139
  for (const line of brief) {
3140
3140
  lines.push(` ${line}`);
3141
3141
  }
@@ -3147,6 +3147,20 @@ function formatSplashScreen(data) {
3147
3147
  lines.push(` ${line}`);
3148
3148
  }
3149
3149
  }
3150
+ const legend = formatLegend();
3151
+ if (legend.length > 0) {
3152
+ lines.push("");
3153
+ for (const line of legend) {
3154
+ lines.push(` ${line}`);
3155
+ }
3156
+ }
3157
+ const contextIndex = formatContextIndex(data.context);
3158
+ if (contextIndex.length > 0) {
3159
+ lines.push("");
3160
+ for (const line of contextIndex) {
3161
+ lines.push(` ${line}`);
3162
+ }
3163
+ }
3150
3164
  const inspectHints = formatInspectHints(data.context);
3151
3165
  if (inspectHints.length > 0) {
3152
3166
  lines.push("");
@@ -3172,7 +3186,7 @@ function formatVisibleStartupBrief(context) {
3172
3186
  const projectSignals = buildProjectSignalLine(context);
3173
3187
  const shownItems = new Set;
3174
3188
  if (promptLines.length > 0) {
3175
- lines.push(`${c2.cyan}Recent Requests:${c2.reset}`);
3189
+ lines.push(`${c2.cyan}Asked recently:${c2.reset}`);
3176
3190
  for (const item of promptLines) {
3177
3191
  lines.push(` - ${truncateInline(item, 160)}`);
3178
3192
  rememberShownItem(shownItems, item);
@@ -3204,13 +3218,13 @@ function formatVisibleStartupBrief(context) {
3204
3218
  }
3205
3219
  }
3206
3220
  } else if (currentRequest && !duplicatesPromptLine(currentRequest, latestPromptLine)) {
3207
- lines.push(`${c2.cyan}Current Request:${c2.reset}`);
3221
+ lines.push(`${c2.cyan}What you're on:${c2.reset}`);
3208
3222
  lines.push(` - ${truncateInline(currentRequest, 160)}`);
3209
3223
  rememberShownItem(shownItems, currentRequest);
3210
3224
  if (toolFallbacks.length > 0) {
3211
3225
  const additiveTools = filterAdditiveToolFallbacks(toolFallbacks, shownItems);
3212
3226
  if (additiveTools.length > 0) {
3213
- lines.push(`${c2.cyan}Recent Tools:${c2.reset}`);
3227
+ lines.push(`${c2.cyan}Tool trail:${c2.reset}`);
3214
3228
  for (const item of additiveTools) {
3215
3229
  lines.push(` - ${truncateInline(item, 160)}`);
3216
3230
  rememberShownItem(shownItems, item);
@@ -3219,12 +3233,12 @@ function formatVisibleStartupBrief(context) {
3219
3233
  }
3220
3234
  }
3221
3235
  if (latest && currentRequest && !hasRequestSection(lines) && !duplicatesPromptLine(currentRequest, latestPromptLine)) {
3222
- lines.push(`${c2.cyan}Current Request:${c2.reset}`);
3236
+ lines.push(`${c2.cyan}What you're on:${c2.reset}`);
3223
3237
  lines.push(` - ${truncateInline(currentRequest, 160)}`);
3224
3238
  rememberShownItem(shownItems, currentRequest);
3225
3239
  }
3226
3240
  if (recentOutcomeLines.length > 0) {
3227
- lines.push(`${c2.cyan}Recent Work:${c2.reset}`);
3241
+ lines.push(`${c2.cyan}What's moved:${c2.reset}`);
3228
3242
  for (const item of recentOutcomeLines) {
3229
3243
  lines.push(` - ${truncateInline(item, 160)}`);
3230
3244
  rememberShownItem(shownItems, item);
@@ -3233,7 +3247,7 @@ function formatVisibleStartupBrief(context) {
3233
3247
  if (toolFallbacks.length > 0 && latest) {
3234
3248
  const additiveTools = filterAdditiveToolFallbacks(toolFallbacks, shownItems);
3235
3249
  if (additiveTools.length > 0) {
3236
- lines.push(`${c2.cyan}Recent Tools:${c2.reset}`);
3250
+ lines.push(`${c2.cyan}Tool trail:${c2.reset}`);
3237
3251
  for (const item of additiveTools) {
3238
3252
  lines.push(` - ${truncateInline(item, 160)}`);
3239
3253
  rememberShownItem(shownItems, item);
@@ -3241,13 +3255,13 @@ function formatVisibleStartupBrief(context) {
3241
3255
  }
3242
3256
  }
3243
3257
  if (sessionFallbacks.length > 0) {
3244
- lines.push(`${c2.cyan}Recent Sessions:${c2.reset}`);
3258
+ lines.push(`${c2.cyan}Recent threads:${c2.reset}`);
3245
3259
  for (const item of sessionFallbacks) {
3246
3260
  lines.push(` - ${truncateInline(item, 160)}`);
3247
3261
  }
3248
3262
  }
3249
3263
  if (projectSignals) {
3250
- lines.push(`${c2.cyan}Project Signals:${c2.reset}`);
3264
+ lines.push(`${c2.cyan}Signal mix:${c2.reset}`);
3251
3265
  lines.push(` - ${truncateInline(projectSignals, 160)}`);
3252
3266
  }
3253
3267
  const stale = pickRelevantStaleDecision(context, latest);
@@ -3271,10 +3285,31 @@ function formatContextEconomics(data) {
3271
3285
  if (data.estimatedReadTokens > 0) {
3272
3286
  parts.push(`read now ~${data.estimatedReadTokens.toLocaleString()}t`);
3273
3287
  }
3288
+ if (data.context.observations.length > 0) {
3289
+ parts.push(`${data.context.observations.length} observations loaded`);
3290
+ }
3274
3291
  if (parts.length === 0)
3275
3292
  return [];
3276
3293
  return [`${c2.dim}Context economics:${c2.reset} ${parts.join(" \xB7 ")}`];
3277
3294
  }
3295
+ function formatLegend() {
3296
+ return [
3297
+ `${c2.dim}Legend:${c2.reset} #id | \u25A0 bugfix | \u25B2 feature | \u2248 refactor | \u25CF change | \u25A1 discovery | \u25C7 decision`
3298
+ ];
3299
+ }
3300
+ function formatContextIndex(context) {
3301
+ const rows = context.observations.filter((obs) => obs.type !== "digest").slice(0, 6).map((obs) => {
3302
+ const icon = observationIcon(obs.type);
3303
+ const fileHint = extractPrimaryFileHint(obs);
3304
+ return `${icon} #${obs.id} ${truncateInline(obs.title, 110)}${fileHint ? ` ${c2.dim}(${fileHint})${c2.reset}` : ""}`;
3305
+ });
3306
+ if (rows.length === 0)
3307
+ return [];
3308
+ return [
3309
+ `${c2.dim}Handoff index:${c2.reset} use IDs when you want the deeper thread`,
3310
+ ...rows
3311
+ ];
3312
+ }
3278
3313
  function formatInspectHints(context) {
3279
3314
  const hints = [];
3280
3315
  if ((context.recentSessions?.length ?? 0) > 0) {
@@ -3290,7 +3325,12 @@ function formatInspectHints(context) {
3290
3325
  const unique = Array.from(new Set(hints)).slice(0, 4);
3291
3326
  if (unique.length === 0)
3292
3327
  return [];
3293
- return [`${c2.dim}Inspect:${c2.reset} ${unique.join(" \xB7 ")}`];
3328
+ const ids = context.observations.slice(0, 5).map((obs) => obs.id);
3329
+ const fetchHint = ids.length > 0 ? `get_observations([${ids.join(", ")}])` : null;
3330
+ return [
3331
+ `${c2.dim}Next look:${c2.reset} ${unique.join(" \xB7 ")}`,
3332
+ ...fetchHint ? [`${c2.dim}Pull detail:${c2.reset} ${fetchHint}`] : []
3333
+ ];
3294
3334
  }
3295
3335
  function rememberShownItem(shown, value) {
3296
3336
  if (!value)
@@ -3412,9 +3452,60 @@ function chooseMeaningfulSessionSummary(request, completed) {
3412
3452
  function buildProjectSignalLine(context) {
3413
3453
  if (!context.projectTypeCounts)
3414
3454
  return null;
3415
- 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("; ");
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]) => `${signalGlyph(type)} ${type} ${count}`).join("; ");
3416
3456
  return top || null;
3417
3457
  }
3458
+ function signalGlyph(type) {
3459
+ switch (type) {
3460
+ case "bugfix":
3461
+ return "\u25A0";
3462
+ case "feature":
3463
+ return "\u25B2";
3464
+ case "refactor":
3465
+ return "\u2248";
3466
+ case "change":
3467
+ return "\u25CF";
3468
+ case "discovery":
3469
+ return "\u25A1";
3470
+ case "decision":
3471
+ return "\u25C7";
3472
+ default:
3473
+ return "\xB7";
3474
+ }
3475
+ }
3476
+ function observationIcon(type) {
3477
+ switch (type) {
3478
+ case "bugfix":
3479
+ return "\u25A0";
3480
+ case "feature":
3481
+ return "\u25B2";
3482
+ case "refactor":
3483
+ return "\u2248";
3484
+ case "change":
3485
+ return "\u25CF";
3486
+ case "discovery":
3487
+ return "\u25A1";
3488
+ case "decision":
3489
+ return "\u25C7";
3490
+ default:
3491
+ return "\u2022";
3492
+ }
3493
+ }
3494
+ function extractPrimaryFileHint(obs) {
3495
+ const firstRead = parseJsonArraySafe(obs.files_read)[0];
3496
+ const firstModified = parseJsonArraySafe(obs.files_modified)[0];
3497
+ return firstModified ?? firstRead ?? null;
3498
+ }
3499
+ function parseJsonArraySafe(value) {
3500
+ if (!value)
3501
+ return [];
3502
+ try {
3503
+ const parsed = JSON.parse(value);
3504
+ return Array.isArray(parsed) ? parsed.filter((item) => typeof item === "string" && item.trim().length > 0) : [];
3505
+ } catch {
3506
+ return [];
3507
+ }
3508
+ }
3418
3509
  function toSplashLines(value, maxItems) {
3419
3510
  if (!value)
3420
3511
  return [];
@@ -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.13",
2538
+ client_version: "0.4.15",
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.13"
19767
+ version: "0.4.15"
19768
19768
  });
19769
19769
  server.tool("save_observation", "Save an observation to memory", {
19770
19770
  type: exports_external.enum([
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "engrm",
3
- "version": "0.4.13",
3
+ "version": "0.4.15",
4
4
  "description": "Shared memory across devices, sessions, and coding agents",
5
5
  "mcpName": "io.github.dr12hes/engrm",
6
6
  "type": "module",