nodebench-mcp 2.11.0 → 2.13.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.
Files changed (36) hide show
  1. package/NODEBENCH_AGENTS.md +809 -809
  2. package/README.md +443 -431
  3. package/STYLE_GUIDE.md +477 -477
  4. package/dist/__tests__/gaiaCapabilityMediaEval.test.js +153 -5
  5. package/dist/__tests__/gaiaCapabilityMediaEval.test.js.map +1 -1
  6. package/dist/__tests__/helpers/textLlm.d.ts +1 -1
  7. package/dist/__tests__/presetRealWorldBench.test.d.ts +1 -0
  8. package/dist/__tests__/presetRealWorldBench.test.js +839 -0
  9. package/dist/__tests__/presetRealWorldBench.test.js.map +1 -0
  10. package/dist/__tests__/tools.test.js +8 -5
  11. package/dist/__tests__/tools.test.js.map +1 -1
  12. package/dist/__tests__/toolsetGatingEval.test.js +11 -11
  13. package/dist/__tests__/toolsetGatingEval.test.js.map +1 -1
  14. package/dist/index.js +397 -327
  15. package/dist/index.js.map +1 -1
  16. package/dist/tools/agentBootstrapTools.js +258 -258
  17. package/dist/tools/boilerplateTools.js +144 -144
  18. package/dist/tools/cCompilerBenchmarkTools.js +33 -33
  19. package/dist/tools/documentationTools.js +59 -59
  20. package/dist/tools/flywheelTools.js +6 -6
  21. package/dist/tools/learningTools.js +26 -26
  22. package/dist/tools/localFileTools.d.ts +3 -0
  23. package/dist/tools/localFileTools.js +3164 -125
  24. package/dist/tools/localFileTools.js.map +1 -1
  25. package/dist/tools/reconTools.js +31 -31
  26. package/dist/tools/selfEvalTools.js +44 -44
  27. package/dist/tools/sessionMemoryTools.d.ts +15 -0
  28. package/dist/tools/sessionMemoryTools.js +348 -0
  29. package/dist/tools/sessionMemoryTools.js.map +1 -0
  30. package/dist/tools/toolRegistry.d.ts +4 -0
  31. package/dist/tools/toolRegistry.js +229 -0
  32. package/dist/tools/toolRegistry.js.map +1 -1
  33. package/dist/tools/verificationTools.js +41 -41
  34. package/dist/tools/visionTools.js +17 -17
  35. package/dist/tools/webTools.js +18 -18
  36. package/package.json +101 -101
@@ -443,21 +443,21 @@ export const reconTools = [
443
443
  let learnings = [];
444
444
  try {
445
445
  learnings = db
446
- .prepare(`SELECT l.key, l.content, l.category, l.tags, l.source_cycle, l.created_at
447
- FROM learnings_fts
448
- JOIN learnings l ON l.id = learnings_fts.rowid
449
- WHERE learnings_fts MATCH ?
450
- ORDER BY rank
446
+ .prepare(`SELECT l.key, l.content, l.category, l.tags, l.source_cycle, l.created_at
447
+ FROM learnings_fts
448
+ JOIN learnings l ON l.id = learnings_fts.rowid
449
+ WHERE learnings_fts MATCH ?
450
+ ORDER BY rank
451
451
  LIMIT ?`)
452
452
  .all(query, limit);
453
453
  }
454
454
  catch {
455
455
  // FTS5 syntax error (e.g., special chars in query) — fall back to LIKE
456
456
  learnings = db
457
- .prepare(`SELECT key, content, category, tags, source_cycle, created_at
458
- FROM learnings
459
- WHERE content LIKE ? OR key LIKE ?
460
- ORDER BY created_at DESC
457
+ .prepare(`SELECT key, content, category, tags, source_cycle, created_at
458
+ FROM learnings
459
+ WHERE content LIKE ? OR key LIKE ?
460
+ ORDER BY created_at DESC
461
461
  LIMIT ?`)
462
462
  .all(`%${query}%`, `%${query}%`, limit);
463
463
  }
@@ -470,38 +470,38 @@ export const reconTools = [
470
470
  if (categories && categories.length > 0) {
471
471
  const placeholders = categories.map(() => "?").join(", ");
472
472
  reconFindings = db
473
- .prepare(`SELECT f.id, f.session_id, f.source_url, f.category, f.summary,
474
- f.relevance, f.action_items, f.created_at,
475
- s.target as session_target
476
- FROM recon_findings f
477
- JOIN recon_sessions s ON s.id = f.session_id
478
- WHERE (f.summary LIKE ? OR f.relevance LIKE ? OR f.action_items LIKE ?)
479
- AND f.category IN (${placeholders})
480
- ORDER BY f.created_at DESC
473
+ .prepare(`SELECT f.id, f.session_id, f.source_url, f.category, f.summary,
474
+ f.relevance, f.action_items, f.created_at,
475
+ s.target as session_target
476
+ FROM recon_findings f
477
+ JOIN recon_sessions s ON s.id = f.session_id
478
+ WHERE (f.summary LIKE ? OR f.relevance LIKE ? OR f.action_items LIKE ?)
479
+ AND f.category IN (${placeholders})
480
+ ORDER BY f.created_at DESC
481
481
  LIMIT ?`)
482
482
  .all(`%${query}%`, `%${query}%`, `%${query}%`, ...categories, limit);
483
483
  }
484
484
  else {
485
485
  reconFindings = db
486
- .prepare(`SELECT f.id, f.session_id, f.source_url, f.category, f.summary,
487
- f.relevance, f.action_items, f.created_at,
488
- s.target as session_target
489
- FROM recon_findings f
490
- JOIN recon_sessions s ON s.id = f.session_id
491
- WHERE f.summary LIKE ? OR f.relevance LIKE ? OR f.action_items LIKE ?
492
- ORDER BY f.created_at DESC
486
+ .prepare(`SELECT f.id, f.session_id, f.source_url, f.category, f.summary,
487
+ f.relevance, f.action_items, f.created_at,
488
+ s.target as session_target
489
+ FROM recon_findings f
490
+ JOIN recon_sessions s ON s.id = f.session_id
491
+ WHERE f.summary LIKE ? OR f.relevance LIKE ? OR f.action_items LIKE ?
492
+ ORDER BY f.created_at DESC
493
493
  LIMIT ?`)
494
494
  .all(`%${query}%`, `%${query}%`, `%${query}%`, limit);
495
495
  }
496
496
  // 3. Search ALL gaps from past verification cycles (resolved + open)
497
497
  const matchedGaps = db
498
- .prepare(`SELECT g.id, g.cycle_id, g.title, g.description, g.severity,
499
- g.status, g.fix_strategy as resolution, g.resolved_at,
500
- c.title as cycle_target
501
- FROM gaps g
502
- JOIN verification_cycles c ON c.id = g.cycle_id
503
- WHERE (g.description LIKE ? OR g.fix_strategy LIKE ? OR g.title LIKE ?)
504
- ORDER BY g.created_at DESC
498
+ .prepare(`SELECT g.id, g.cycle_id, g.title, g.description, g.severity,
499
+ g.status, g.fix_strategy as resolution, g.resolved_at,
500
+ c.title as cycle_target
501
+ FROM gaps g
502
+ JOIN verification_cycles c ON c.id = g.cycle_id
503
+ WHERE (g.description LIKE ? OR g.fix_strategy LIKE ? OR g.title LIKE ?)
504
+ ORDER BY g.created_at DESC
505
505
  LIMIT ?`)
506
506
  .all(`%${query}%`, `%${query}%`, `%${query}%`, limit);
507
507
  const totalResults = learnings.length + reconFindings.length + matchedGaps.length;
@@ -1160,28 +1160,28 @@ export const selfEvalTools = [
1160
1160
  handler: async (args) => {
1161
1161
  const db = getDb();
1162
1162
  // Ensure table exists
1163
- db.exec(`CREATE TABLE IF NOT EXISTS eval_task_bank (
1164
- id TEXT PRIMARY KEY,
1165
- task_id TEXT UNIQUE NOT NULL,
1166
- title TEXT NOT NULL,
1167
- category TEXT NOT NULL,
1168
- difficulty TEXT NOT NULL,
1169
- prompt TEXT NOT NULL,
1170
- initial_state TEXT,
1171
- success_criteria TEXT NOT NULL,
1172
- forbidden_behaviors TEXT,
1173
- expected_tools TEXT,
1174
- time_budget_minutes INTEGER DEFAULT 30,
1175
- token_budget INTEGER,
1176
- created_at TEXT DEFAULT (datetime('now')),
1177
- run_count INTEGER DEFAULT 0,
1178
- pass_count INTEGER DEFAULT 0
1163
+ db.exec(`CREATE TABLE IF NOT EXISTS eval_task_bank (
1164
+ id TEXT PRIMARY KEY,
1165
+ task_id TEXT UNIQUE NOT NULL,
1166
+ title TEXT NOT NULL,
1167
+ category TEXT NOT NULL,
1168
+ difficulty TEXT NOT NULL,
1169
+ prompt TEXT NOT NULL,
1170
+ initial_state TEXT,
1171
+ success_criteria TEXT NOT NULL,
1172
+ forbidden_behaviors TEXT,
1173
+ expected_tools TEXT,
1174
+ time_budget_minutes INTEGER DEFAULT 30,
1175
+ token_budget INTEGER,
1176
+ created_at TEXT DEFAULT (datetime('now')),
1177
+ run_count INTEGER DEFAULT 0,
1178
+ pass_count INTEGER DEFAULT 0
1179
1179
  )`);
1180
1180
  const existing = db.prepare("SELECT * FROM eval_task_bank WHERE task_id = ?").get(args.taskId);
1181
1181
  if (existing) {
1182
1182
  // Update existing task
1183
- db.prepare(`UPDATE eval_task_bank SET title = ?, category = ?, difficulty = ?, prompt = ?,
1184
- initial_state = ?, success_criteria = ?, forbidden_behaviors = ?, expected_tools = ?,
1183
+ db.prepare(`UPDATE eval_task_bank SET title = ?, category = ?, difficulty = ?, prompt = ?,
1184
+ initial_state = ?, success_criteria = ?, forbidden_behaviors = ?, expected_tools = ?,
1185
1185
  time_budget_minutes = ?, token_budget = ? WHERE task_id = ?`).run(args.title, args.category, args.difficulty, args.prompt, args.initialState ?? null, JSON.stringify(args.successCriteria), args.forbiddenBehaviors ? JSON.stringify(args.forbiddenBehaviors) : null, args.expectedTools ? JSON.stringify(args.expectedTools) : null, args.timeBudgetMinutes ?? 30, args.tokenBudget ?? null, args.taskId);
1186
1186
  return {
1187
1187
  action: "updated",
@@ -1190,8 +1190,8 @@ export const selfEvalTools = [
1190
1190
  };
1191
1191
  }
1192
1192
  // Insert new task
1193
- db.prepare(`INSERT INTO eval_task_bank (id, task_id, title, category, difficulty, prompt,
1194
- initial_state, success_criteria, forbidden_behaviors, expected_tools, time_budget_minutes, token_budget)
1193
+ db.prepare(`INSERT INTO eval_task_bank (id, task_id, title, category, difficulty, prompt,
1194
+ initial_state, success_criteria, forbidden_behaviors, expected_tools, time_budget_minutes, token_budget)
1195
1195
  VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`).run(genId("etb"), args.taskId, args.title, args.category, args.difficulty, args.prompt, args.initialState ?? null, JSON.stringify(args.successCriteria), args.forbiddenBehaviors ? JSON.stringify(args.forbiddenBehaviors) : null, args.expectedTools ? JSON.stringify(args.expectedTools) : null, args.timeBudgetMinutes ?? 30, args.tokenBudget ?? null);
1196
1196
  const total = db.prepare("SELECT COUNT(*) as cnt FROM eval_task_bank").get().cnt;
1197
1197
  return {
@@ -1275,30 +1275,30 @@ export const selfEvalTools = [
1275
1275
  handler: async (args) => {
1276
1276
  const db = getDb();
1277
1277
  // Ensure results table exists
1278
- db.exec(`CREATE TABLE IF NOT EXISTS eval_run_results (
1279
- id TEXT PRIMARY KEY,
1280
- task_id TEXT NOT NULL,
1281
- session_id TEXT,
1282
- condition TEXT NOT NULL,
1283
- trial_number INTEGER DEFAULT 1,
1284
- outcome_score REAL,
1285
- process_score REAL,
1286
- combined_score REAL,
1287
- outcome_results TEXT,
1288
- forbidden_violations TEXT,
1289
- duration_minutes REAL,
1290
- total_tool_calls INTEGER,
1291
- total_tokens INTEGER,
1292
- notes TEXT,
1293
- created_at TEXT DEFAULT (datetime('now'))
1278
+ db.exec(`CREATE TABLE IF NOT EXISTS eval_run_results (
1279
+ id TEXT PRIMARY KEY,
1280
+ task_id TEXT NOT NULL,
1281
+ session_id TEXT,
1282
+ condition TEXT NOT NULL,
1283
+ trial_number INTEGER DEFAULT 1,
1284
+ outcome_score REAL,
1285
+ process_score REAL,
1286
+ combined_score REAL,
1287
+ outcome_results TEXT,
1288
+ forbidden_violations TEXT,
1289
+ duration_minutes REAL,
1290
+ total_tool_calls INTEGER,
1291
+ total_tokens INTEGER,
1292
+ notes TEXT,
1293
+ created_at TEXT DEFAULT (datetime('now'))
1294
1294
  )`);
1295
1295
  // Load task from bank (optional — if it exists, validate)
1296
- db.exec(`CREATE TABLE IF NOT EXISTS eval_task_bank (
1297
- id TEXT PRIMARY KEY, task_id TEXT UNIQUE NOT NULL, title TEXT NOT NULL,
1298
- category TEXT NOT NULL, difficulty TEXT NOT NULL, prompt TEXT NOT NULL,
1299
- initial_state TEXT, success_criteria TEXT NOT NULL, forbidden_behaviors TEXT,
1300
- expected_tools TEXT, time_budget_minutes INTEGER DEFAULT 30, token_budget INTEGER,
1301
- created_at TEXT DEFAULT (datetime('now')), run_count INTEGER DEFAULT 0, pass_count INTEGER DEFAULT 0
1296
+ db.exec(`CREATE TABLE IF NOT EXISTS eval_task_bank (
1297
+ id TEXT PRIMARY KEY, task_id TEXT UNIQUE NOT NULL, title TEXT NOT NULL,
1298
+ category TEXT NOT NULL, difficulty TEXT NOT NULL, prompt TEXT NOT NULL,
1299
+ initial_state TEXT, success_criteria TEXT NOT NULL, forbidden_behaviors TEXT,
1300
+ expected_tools TEXT, time_budget_minutes INTEGER DEFAULT 30, token_budget INTEGER,
1301
+ created_at TEXT DEFAULT (datetime('now')), run_count INTEGER DEFAULT 0, pass_count INTEGER DEFAULT 0
1302
1302
  )`);
1303
1303
  const task = db.prepare("SELECT * FROM eval_task_bank WHERE task_id = ?").get(args.taskId);
1304
1304
  // ── Outcome scoring (50pts) ──
@@ -1383,8 +1383,8 @@ export const selfEvalTools = [
1383
1383
  grade = "F (Non-Compliant)";
1384
1384
  // Persist result
1385
1385
  const resultId = genId("err");
1386
- db.prepare(`INSERT INTO eval_run_results (id, task_id, session_id, condition, trial_number,
1387
- outcome_score, process_score, combined_score, outcome_results, forbidden_violations,
1386
+ db.prepare(`INSERT INTO eval_run_results (id, task_id, session_id, condition, trial_number,
1387
+ outcome_score, process_score, combined_score, outcome_results, forbidden_violations,
1388
1388
  duration_minutes, total_tool_calls, total_tokens, notes) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`).run(resultId, args.taskId, args.sessionId ?? null, args.condition, args.trialNumber ?? 1, totalOutcomeScore, totalProcessScore, combinedScore, JSON.stringify(outcomes), violations.length > 0 ? JSON.stringify(violations) : null, args.durationMinutes ?? null, args.totalToolCalls ?? null, args.totalTokens ?? null, args.notes ?? null);
1389
1389
  // Update task bank stats
1390
1390
  if (task) {
@@ -0,0 +1,15 @@
1
+ /**
2
+ * Session Memory Tools — Compaction-resilient notes, attention refresh, and session recovery.
3
+ *
4
+ * Inspired by:
5
+ * - claude-mem (thedotmack): 3-layer progressive disclosure, compaction-resilient notepad
6
+ * - planning-with-files (OthmanAdi): Manus-style filesystem-as-memory, attention refresh
7
+ * - oh-my-claudecode (Yeachan-Heo): .omc/notepad.md compaction-resilient state
8
+ *
9
+ * 3 tools:
10
+ * - save_session_note: Persist findings to filesystem (survives context compaction)
11
+ * - load_session_notes: Retrieve notes from filesystem
12
+ * - refresh_task_context: Re-inject current goals mid-session (attention management)
13
+ */
14
+ import type { McpTool } from "../types.js";
15
+ export declare const sessionMemoryTools: McpTool[];
@@ -0,0 +1,348 @@
1
+ /**
2
+ * Session Memory Tools — Compaction-resilient notes, attention refresh, and session recovery.
3
+ *
4
+ * Inspired by:
5
+ * - claude-mem (thedotmack): 3-layer progressive disclosure, compaction-resilient notepad
6
+ * - planning-with-files (OthmanAdi): Manus-style filesystem-as-memory, attention refresh
7
+ * - oh-my-claudecode (Yeachan-Heo): .omc/notepad.md compaction-resilient state
8
+ *
9
+ * 3 tools:
10
+ * - save_session_note: Persist findings to filesystem (survives context compaction)
11
+ * - load_session_notes: Retrieve notes from filesystem
12
+ * - refresh_task_context: Re-inject current goals mid-session (attention management)
13
+ */
14
+ import { getDb, genId } from "../db.js";
15
+ import * as fs from "fs";
16
+ import * as path from "path";
17
+ import * as os from "os";
18
+ const NOTES_DIR = path.join(os.homedir(), ".nodebench", "notes");
19
+ function ensureNotesDir() {
20
+ if (!fs.existsSync(NOTES_DIR)) {
21
+ fs.mkdirSync(NOTES_DIR, { recursive: true });
22
+ }
23
+ }
24
+ export const sessionMemoryTools = [
25
+ // ─── Tool 1: save_session_note ───────────────────────────────────────────
26
+ {
27
+ name: "save_session_note",
28
+ description: "Persist a critical finding, decision, or progress note to the filesystem. Notes survive context compaction — call this after every major finding or decision so state is never lost. Writes to ~/.nodebench/notes/ as dated markdown files.",
29
+ inputSchema: {
30
+ type: "object",
31
+ properties: {
32
+ title: {
33
+ type: "string",
34
+ description: "Short headline for the note (e.g. 'Auth bug root cause', 'Migration decision: JWT over sessions')",
35
+ },
36
+ content: {
37
+ type: "string",
38
+ description: "Full note content. Include facts, decisions, file paths, error messages — anything needed to reconstruct context after compaction.",
39
+ },
40
+ category: {
41
+ type: "string",
42
+ enum: [
43
+ "finding",
44
+ "decision",
45
+ "progress",
46
+ "blocker",
47
+ "learning",
48
+ "architecture",
49
+ "debugging",
50
+ ],
51
+ description: "Type of note (default: finding)",
52
+ },
53
+ tags: {
54
+ type: "array",
55
+ items: { type: "string" },
56
+ description: "Tags for searchability (e.g. ['auth', 'jwt', 'security'])",
57
+ },
58
+ },
59
+ required: ["title", "content"],
60
+ },
61
+ handler: async (args) => {
62
+ ensureNotesDir();
63
+ const { title, content, category = "finding", tags = [], } = args;
64
+ const now = new Date();
65
+ const dateStr = now.toISOString().slice(0, 10);
66
+ const timeStr = now.toISOString().slice(11, 19).replace(/:/g, "");
67
+ const safeTitle = title
68
+ .toLowerCase()
69
+ .replace(/[^a-z0-9]+/g, "-")
70
+ .slice(0, 50);
71
+ const filename = `${dateStr}-${timeStr}-${safeTitle}.md`;
72
+ const filePath = path.join(NOTES_DIR, filename);
73
+ const markdown = [
74
+ `# ${title}`,
75
+ "",
76
+ `**Category:** ${category}`,
77
+ `**Date:** ${now.toISOString()}`,
78
+ tags.length > 0 ? `**Tags:** ${tags.join(", ")}` : "",
79
+ "",
80
+ "---",
81
+ "",
82
+ content,
83
+ "",
84
+ ]
85
+ .filter(Boolean)
86
+ .join("\n");
87
+ fs.writeFileSync(filePath, markdown, "utf-8");
88
+ // Also persist to SQLite for cross-session search
89
+ try {
90
+ const db = getDb();
91
+ db.prepare(`INSERT INTO learnings (id, key, content, category, tags, source_cycle_id, created_at)
92
+ VALUES (?, ?, ?, ?, ?, ?, datetime('now'))`).run(genId("note"), `session_note:${safeTitle}`, `[Session Note] ${title}\n\n${content}`, category === "finding" ? "pattern" : category === "debugging" ? "gotcha" : "convention", JSON.stringify(tags), null);
93
+ }
94
+ catch {
95
+ /* SQLite backup is best-effort */
96
+ }
97
+ return {
98
+ saved: true,
99
+ filePath,
100
+ filename,
101
+ title,
102
+ category,
103
+ tip: "Notes persist to filesystem and survive context compaction. Call load_session_notes to retrieve after /clear or /compact.",
104
+ };
105
+ },
106
+ },
107
+ // ─── Tool 2: load_session_notes ──────────────────────────────────────────
108
+ {
109
+ name: "load_session_notes",
110
+ description: "Load session notes from the filesystem. Use after context compaction, /clear, or session resume to recover state. Returns notes sorted by recency. Supports filtering by date, category, or keyword.",
111
+ inputSchema: {
112
+ type: "object",
113
+ properties: {
114
+ date: {
115
+ type: "string",
116
+ description: "Filter by date (YYYY-MM-DD). Default: today. Use 'all' for all notes.",
117
+ },
118
+ category: {
119
+ type: "string",
120
+ enum: [
121
+ "finding",
122
+ "decision",
123
+ "progress",
124
+ "blocker",
125
+ "learning",
126
+ "architecture",
127
+ "debugging",
128
+ ],
129
+ description: "Filter by note category (optional)",
130
+ },
131
+ keyword: {
132
+ type: "string",
133
+ description: "Search notes containing this keyword (case-insensitive)",
134
+ },
135
+ limit: {
136
+ type: "number",
137
+ description: "Max notes to return (default: 10)",
138
+ },
139
+ },
140
+ required: [],
141
+ },
142
+ handler: async (args) => {
143
+ ensureNotesDir();
144
+ const { date, category, keyword, limit = 10 } = args;
145
+ const files = fs.readdirSync(NOTES_DIR).filter((f) => f.endsWith(".md"));
146
+ // Sort by filename (date-time prefix) descending (most recent first)
147
+ files.sort((a, b) => b.localeCompare(a));
148
+ // Filter by date
149
+ let filtered = files;
150
+ if (date && date !== "all") {
151
+ filtered = filtered.filter((f) => f.startsWith(date));
152
+ }
153
+ else if (!date) {
154
+ // Default: today
155
+ const today = new Date().toISOString().slice(0, 10);
156
+ filtered = filtered.filter((f) => f.startsWith(today));
157
+ }
158
+ // Read file contents and apply filters
159
+ const notes = [];
160
+ for (const file of filtered) {
161
+ if (notes.length >= limit)
162
+ break;
163
+ const content = fs.readFileSync(path.join(NOTES_DIR, file), "utf-8");
164
+ // Extract metadata from markdown
165
+ const titleMatch = content.match(/^# (.+)$/m);
166
+ const categoryMatch = content.match(/\*\*Category:\*\* (.+)$/m);
167
+ const dateMatch = content.match(/\*\*Date:\*\* (.+)$/m);
168
+ const noteCategory = categoryMatch?.[1] || "unknown";
169
+ const noteTitle = titleMatch?.[1] || file;
170
+ const noteDate = dateMatch?.[1] || "";
171
+ // Category filter
172
+ if (category && noteCategory !== category)
173
+ continue;
174
+ // Keyword filter
175
+ if (keyword && !content.toLowerCase().includes(keyword.toLowerCase()))
176
+ continue;
177
+ notes.push({
178
+ filename: file,
179
+ title: noteTitle,
180
+ category: noteCategory,
181
+ content,
182
+ date: noteDate,
183
+ });
184
+ }
185
+ return {
186
+ noteCount: notes.length,
187
+ totalFiles: files.length,
188
+ notes: notes.map((n) => ({
189
+ filename: n.filename,
190
+ title: n.title,
191
+ category: n.category,
192
+ preview: n.content.slice(0, 300),
193
+ fullContent: n.content,
194
+ })),
195
+ tip: notes.length === 0
196
+ ? "No notes found. Use save_session_note to persist findings during your session."
197
+ : "Notes loaded. Review findings and continue where you left off.",
198
+ };
199
+ },
200
+ },
201
+ // ─── Tool 3: refresh_task_context ────────────────────────────────────────
202
+ {
203
+ name: "refresh_task_context",
204
+ description: "Re-inject the current task context to combat attention drift. After 30+ tool calls, models lose sight of original goals ('lost in the middle' problem). This tool gathers your active verification cycle, open gaps, recent learnings, and session notes into a compact refresher. Based on Manus's 'Manipulate Attention Through Recitation' principle.",
205
+ inputSchema: {
206
+ type: "object",
207
+ properties: {
208
+ taskDescription: {
209
+ type: "string",
210
+ description: "What you're currently working on (re-state the original goal to anchor attention)",
211
+ },
212
+ includeNotes: {
213
+ type: "boolean",
214
+ description: "Include today's session notes in the refresher (default: true)",
215
+ },
216
+ includeGaps: {
217
+ type: "boolean",
218
+ description: "Include open verification gaps (default: true)",
219
+ },
220
+ includeLearnings: {
221
+ type: "boolean",
222
+ description: "Include recent relevant learnings (default: true)",
223
+ },
224
+ },
225
+ required: [],
226
+ },
227
+ handler: async (args) => {
228
+ const { taskDescription = "", includeNotes = true, includeGaps = true, includeLearnings = true, } = args;
229
+ const db = getDb();
230
+ const context = {};
231
+ // 1. Active verification cycle
232
+ try {
233
+ const activeCycle = db
234
+ .prepare("SELECT * FROM verification_cycles WHERE status = 'active' ORDER BY created_at DESC LIMIT 1")
235
+ .get();
236
+ if (activeCycle) {
237
+ const phases = db
238
+ .prepare("SELECT phase, status, summary FROM phase_findings WHERE cycle_id = ? ORDER BY phase ASC")
239
+ .all(activeCycle.id);
240
+ context.activeCycle = {
241
+ id: activeCycle.id,
242
+ title: activeCycle.title,
243
+ currentPhase: activeCycle.current_phase,
244
+ status: activeCycle.status,
245
+ phases: phases.map((p) => ({
246
+ phase: p.phase,
247
+ status: p.status,
248
+ summary: p.summary?.slice(0, 100),
249
+ })),
250
+ };
251
+ }
252
+ }
253
+ catch {
254
+ /* table may not exist yet */
255
+ }
256
+ // 2. Open gaps
257
+ if (includeGaps) {
258
+ try {
259
+ const gaps = db
260
+ .prepare("SELECT id, description, severity, status FROM gaps WHERE status = 'open' ORDER BY CASE severity WHEN 'critical' THEN 1 WHEN 'high' THEN 2 WHEN 'medium' THEN 3 ELSE 4 END LIMIT 10")
261
+ .all();
262
+ if (gaps.length > 0) {
263
+ context.openGaps = gaps.map((g) => ({
264
+ id: g.id,
265
+ description: g.description?.slice(0, 100),
266
+ severity: g.severity,
267
+ }));
268
+ }
269
+ }
270
+ catch {
271
+ /* table may not exist yet */
272
+ }
273
+ }
274
+ // 3. Recent learnings
275
+ if (includeLearnings) {
276
+ try {
277
+ const learnings = db
278
+ .prepare("SELECT key, content, category FROM learnings ORDER BY created_at DESC LIMIT 5")
279
+ .all();
280
+ if (learnings.length > 0) {
281
+ context.recentLearnings = learnings.map((l) => ({
282
+ key: l.key,
283
+ category: l.category,
284
+ preview: l.content?.slice(0, 100),
285
+ }));
286
+ }
287
+ }
288
+ catch {
289
+ /* table may not exist yet */
290
+ }
291
+ }
292
+ // 4. Today's session notes
293
+ if (includeNotes) {
294
+ ensureNotesDir();
295
+ const today = new Date().toISOString().slice(0, 10);
296
+ const files = fs
297
+ .readdirSync(NOTES_DIR)
298
+ .filter((f) => f.endsWith(".md") && f.startsWith(today))
299
+ .sort((a, b) => b.localeCompare(a))
300
+ .slice(0, 5);
301
+ if (files.length > 0) {
302
+ context.todayNotes = files.map((f) => {
303
+ const content = fs.readFileSync(path.join(NOTES_DIR, f), "utf-8");
304
+ const titleMatch = content.match(/^# (.+)$/m);
305
+ return {
306
+ title: titleMatch?.[1] || f,
307
+ preview: content.slice(0, 200),
308
+ };
309
+ });
310
+ }
311
+ }
312
+ // 5. Tool call stats for this session
313
+ try {
314
+ const stats = db
315
+ .prepare("SELECT COUNT(*) as total, SUM(CASE WHEN result_status = 'error' THEN 1 ELSE 0 END) as errors FROM tool_call_log WHERE session_id = (SELECT session_id FROM tool_call_log ORDER BY created_at DESC LIMIT 1)")
316
+ .get();
317
+ if (stats) {
318
+ context.sessionStats = {
319
+ totalToolCalls: stats.total,
320
+ errors: stats.errors,
321
+ attentionWarning: stats.total > 30
322
+ ? "HIGH DRIFT RISK: 30+ tool calls. Recite your original goal before proceeding."
323
+ : stats.total > 15
324
+ ? "MODERATE: 15+ tool calls. Consider re-reading your task plan."
325
+ : "LOW: Fresh context, proceed normally.",
326
+ };
327
+ }
328
+ }
329
+ catch {
330
+ /* table may not exist yet */
331
+ }
332
+ return {
333
+ taskDescription: taskDescription ||
334
+ "(Not specified — re-state your original goal to anchor attention)",
335
+ context,
336
+ refreshedAt: new Date().toISOString(),
337
+ guideline: "Re-read the taskDescription and activeCycle goal. Focus on open gaps by severity. Check todayNotes for decisions made earlier. Avoid repeating solved problems.",
338
+ antiDrift: [
339
+ "Does my current action serve the original goal?",
340
+ "Am I repeating something I already tried?",
341
+ "Have I checked learnings before implementing?",
342
+ "Am I within scope or drifting to tangents?",
343
+ ],
344
+ };
345
+ },
346
+ },
347
+ ];
348
+ //# sourceMappingURL=sessionMemoryTools.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sessionMemoryTools.js","sourceRoot":"","sources":["../../src/tools/sessionMemoryTools.ts"],"names":[],"mappings":"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"}
@@ -26,6 +26,8 @@ export interface ToolRegistryEntry {
26
26
  quickRef: ToolQuickRef;
27
27
  /** Where this tool sits in a typical workflow: research, implement, test, verify, ship */
28
28
  phase: "research" | "implement" | "test" | "verify" | "ship" | "meta" | "utility";
29
+ /** Recommended model tier: low=Haiku, medium=Sonnet, high=Opus. Used for cost-aware routing. */
30
+ complexity?: "low" | "medium" | "high";
29
31
  }
30
32
  /** Map of tool name → registry entry for O(1) lookup */
31
33
  export declare const TOOL_REGISTRY: Map<string, ToolRegistryEntry>;
@@ -37,6 +39,8 @@ export declare function getQuickRef(toolName: string): ToolQuickRef | null;
37
39
  export declare function getToolsByCategory(category: string): ToolRegistryEntry[];
38
40
  /** Get all tools in a workflow phase */
39
41
  export declare function getToolsByPhase(phase: ToolRegistryEntry["phase"]): ToolRegistryEntry[];
42
+ /** Get the recommended model complexity tier for a tool */
43
+ export declare function getToolComplexity(toolName: string): "low" | "medium" | "high";
40
44
  export interface SearchResult {
41
45
  name: string;
42
46
  description: string;