@agentbrain/mcp-server 1.4.67 → 1.4.74

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.
@@ -1,9 +1,9 @@
1
1
  // MCP tool: load_context - load context docs at session start
2
- import { readFile, writeFile } from 'node:fs/promises';
2
+ import { readFile, writeFile, readdir, mkdir } from 'node:fs/promises';
3
3
  import { existsSync } from 'node:fs';
4
4
  import { join, resolve } from 'node:path';
5
5
  import { homedir } from 'node:os';
6
- import { loadAIConfig, generateContext, getCachedDoc, getGitHash, loadCache, getPendingDoomForMCP, ensureGitignore, installPostCommitHook, isGitRepository, createAgentFilesShared, } from '@agentbrain/core';
6
+ import { loadAIConfig, generateContext, getGitHash, loadCache, getPendingDoomForMCP, ensureGitignore, installPostCommitHook, isGitRepository, createAgentFilesShared, generateContextFiles, } from '@agentbrain/core';
7
7
  /**
8
8
  * Expand path: handles ~, relative paths, etc.
9
9
  */
@@ -13,118 +13,483 @@ function expandPath(path) {
13
13
  }
14
14
  return resolve(path);
15
15
  }
16
+ /**
17
+ * Order context files: specific order for known files, alphabetical for others, errors.md last
18
+ */
19
+ function orderContextFiles(files) {
20
+ const order = [
21
+ 'context.md',
22
+ 'branch-context.md',
23
+ 'agent-hints.md',
24
+ 'run-commands.md',
25
+ 'schema.md',
26
+ 'dependencies.md',
27
+ 'dependency-map.md',
28
+ 'patterns.md',
29
+ 'decisions.md',
30
+ 'inventory.md',
31
+ 'testing.md',
32
+ 'environment.md',
33
+ ];
34
+ const ordered = [];
35
+ const remaining = [];
36
+ let errorsFile = null;
37
+ for (const file of files) {
38
+ if (file === 'errors.md') {
39
+ errorsFile = file;
40
+ }
41
+ else if (order.includes(file)) {
42
+ ordered.push(file);
43
+ }
44
+ else {
45
+ remaining.push(file);
46
+ }
47
+ }
48
+ // Sort known files by order array
49
+ ordered.sort((a, b) => order.indexOf(a) - order.indexOf(b));
50
+ // Sort remaining alphabetically
51
+ remaining.sort();
52
+ // Combine: ordered + remaining + errors.md
53
+ const result = [...ordered, ...remaining];
54
+ if (errorsFile) {
55
+ result.push(errorsFile);
56
+ }
57
+ return result;
58
+ }
59
+ /**
60
+ * Get description for a context file
61
+ */
62
+ function getFileDescription(filename) {
63
+ const descriptions = {
64
+ 'context.md': 'architecture overview',
65
+ 'schema.md': 'database models',
66
+ 'patterns.md': 'coding conventions',
67
+ 'dependencies.md': 'package versions',
68
+ 'dependency-map.md': 'module relationships',
69
+ 'testing.md': 'test patterns',
70
+ 'decisions.md': 'tech stack decisions',
71
+ 'errors.md': 'known issues and solutions',
72
+ 'branch-context.md': 'current branch and recent commits',
73
+ 'inventory.md': 'components and utilities',
74
+ 'environment.md': 'environment variables',
75
+ 'run-commands.md': 'available commands',
76
+ 'agent-hints.md': 'entry points and stack',
77
+ };
78
+ return descriptions[filename] || 'additional context';
79
+ }
80
+ /**
81
+ * Build a slim summary from context.md, run-commands.md, and agent-hints.md
82
+ * Extracts key sections without full content. Keep under 60 lines total.
83
+ */
84
+ function buildSlimSummary(contextContent, availableFiles, runCommandsContent, agentHintsContent) {
85
+ const lines = contextContent.split('\n');
86
+ let projectName = 'Project';
87
+ let techStack = [];
88
+ let structure = [];
89
+ let modules = [];
90
+ // Extract project name (first # heading)
91
+ for (const line of lines) {
92
+ if (line.startsWith('# ') && !projectName) {
93
+ projectName = line.replace('# ', '').trim();
94
+ break;
95
+ }
96
+ }
97
+ // Extract tech stack (look for "Tech Stack", "Stack", or "Technologies" section)
98
+ let inTechStack = false;
99
+ for (let i = 0; i < lines.length; i++) {
100
+ const line = lines[i];
101
+ if (/^##\s+(Tech Stack|Stack|Technologies|Technology)/i.test(line)) {
102
+ inTechStack = true;
103
+ continue;
104
+ }
105
+ if (inTechStack) {
106
+ if (line.startsWith('##'))
107
+ break; // Next section
108
+ if (line.trim().startsWith('-') || line.trim().startsWith('*')) {
109
+ const item = line.replace(/^[\s\-\*]+/, '').split(':')[0].trim();
110
+ if (item && techStack.length < 6) {
111
+ // Reduced from 8 to keep summary compact
112
+ techStack.push(item);
113
+ }
114
+ }
115
+ }
116
+ }
117
+ // Extract entry points from agent-hints.md if available
118
+ const entryPoints = [];
119
+ if (agentHintsContent) {
120
+ const hintLines = agentHintsContent.split('\n');
121
+ let inEntryPoints = false;
122
+ for (const line of hintLines) {
123
+ if (line.includes('## Entry Points')) {
124
+ inEntryPoints = true;
125
+ continue;
126
+ }
127
+ if (inEntryPoints) {
128
+ if (line.startsWith('##'))
129
+ break;
130
+ if (line.trim().startsWith('-')) {
131
+ const entry = line.replace(/^[\s\-\*]+/, '').trim();
132
+ if (entry && !entry.startsWith('*') && entryPoints.length < 3) {
133
+ entryPoints.push(entry);
134
+ }
135
+ }
136
+ }
137
+ }
138
+ }
139
+ // Extract run commands from run-commands.md if available
140
+ const runCommands = [];
141
+ if (runCommandsContent) {
142
+ const cmdLines = runCommandsContent.split('\n');
143
+ let currentCategory = '';
144
+ for (const line of cmdLines) {
145
+ if (line.startsWith('### ')) {
146
+ currentCategory = line.replace('### ', '').trim();
147
+ runCommands.push({ category: currentCategory, commands: [] });
148
+ }
149
+ else if (line.trim().startsWith('-') && currentCategory) {
150
+ const cmd = line.replace(/^[\s\-\*]+/, '').trim();
151
+ if (cmd) {
152
+ const lastCategory = runCommands[runCommands.length - 1];
153
+ if (lastCategory && lastCategory.commands.length < 3) {
154
+ // Max 3 per category to keep compact
155
+ lastCategory.commands.push(cmd);
156
+ }
157
+ }
158
+ }
159
+ }
160
+ }
161
+ // Build slim summary (keep under 60 lines)
162
+ let summary = `# ${projectName} - Context Summary\n\n`;
163
+ // Entry points (compact, high-value)
164
+ if (entryPoints.length > 0) {
165
+ summary += `## Entry Points\n${entryPoints.join(', ')}\n\n`;
166
+ }
167
+ // Tech stack
168
+ if (techStack.length > 0) {
169
+ summary += `## Tech Stack\n${techStack.join(', ')}\n\n`;
170
+ }
171
+ // Run commands (compact format)
172
+ if (runCommands.length > 0) {
173
+ summary += `## Run Commands\n`;
174
+ for (const { category, commands } of runCommands.slice(0, 3)) {
175
+ // Max 3 categories
176
+ if (commands.length > 0) {
177
+ summary += `**${category}**: ${commands.slice(0, 2).join(', ')}\n`; // Max 2 per category
178
+ }
179
+ }
180
+ summary += '\n';
181
+ }
182
+ // Available files (compact)
183
+ summary += `## Available Context Files\n`;
184
+ const importantFiles = availableFiles.filter((f) => ['context.md', 'patterns.md', 'decisions.md', 'schema.md'].includes(f));
185
+ const otherFiles = availableFiles.filter((f) => !importantFiles.includes(f));
186
+ for (const file of importantFiles) {
187
+ summary += `- .agentbrain/${file} — ${getFileDescription(file)}\n`;
188
+ }
189
+ if (otherFiles.length > 0) {
190
+ summary += `... and ${otherFiles.length} more files\n`;
191
+ }
192
+ summary += `\n## How to Load More\n`;
193
+ summary += `- Full context: load_context({ full: true })\n`;
194
+ summary += `- Specific file: load_context({ files: ['schema.md'] })\n`;
195
+ summary += `- Generate new context: generate_context({ focus: "area name" })\n`;
196
+ return summary;
197
+ }
198
+ /**
199
+ * Bootstrap a repository with auto-generated context files
200
+ * Uses AST parser to generate factual files in ~1 second
201
+ * Agent only needs to write 3 interpretive files
202
+ */
203
+ async function bootstrapRepo(repoPath) {
204
+ const contextDir = join(repoPath, '.agentbrain');
205
+ // 1. Create .agentbrain/ directory
206
+ if (!existsSync(contextDir)) {
207
+ await mkdir(contextDir, { recursive: true });
208
+ }
209
+ // 2. Auto-generate factual context files using parser (fast, ~1 second)
210
+ const generatedFiles = await generateContextFiles(repoPath);
211
+ // 3. Save generated files to .agentbrain/
212
+ for (const file of generatedFiles) {
213
+ await writeFile(join(contextDir, file.name), file.content, 'utf-8');
214
+ }
215
+ // 4. Get current git hash and write cache.json
216
+ const gitHash = await getGitHash(repoPath);
217
+ const docs = {};
218
+ for (const file of generatedFiles) {
219
+ const baseName = file.name.replace('.md', '');
220
+ docs[baseName] = {
221
+ type: baseName,
222
+ content: file.content,
223
+ savedAt: new Date().toISOString(),
224
+ };
225
+ }
226
+ const cachePath = join(contextDir, 'cache.json');
227
+ const cache = {
228
+ gitHash,
229
+ docs,
230
+ savedAt: new Date().toISOString(),
231
+ };
232
+ await writeFile(cachePath, JSON.stringify(cache, null, 2), 'utf-8');
233
+ // Write setup-source marker
234
+ const setupSourcePath = join(contextDir, 'setup-source.json');
235
+ await writeFile(setupSourcePath, JSON.stringify({ source: 'mcp-bootstrap', setupAt: new Date().toISOString() }, null, 2), 'utf-8');
236
+ // 5. Update .gitignore
237
+ try {
238
+ await ensureGitignore(repoPath);
239
+ }
240
+ catch (err) {
241
+ console.error('[bootstrap] .gitignore update failed:', err);
242
+ }
243
+ // 6. Install git hook if this is a git repository
244
+ if (isGitRepository(repoPath)) {
245
+ try {
246
+ await installPostCommitHook(repoPath);
247
+ }
248
+ catch (err) {
249
+ console.error('[bootstrap] Git hook installation failed:', err);
250
+ }
251
+ }
252
+ // 7. Update AGENTS.md with file list
253
+ const fileNames = generatedFiles.map((f) => f.name);
254
+ try {
255
+ await createAgentFilesShared(repoPath, fileNames);
256
+ }
257
+ catch (err) {
258
+ console.error('[bootstrap] AGENTS.md update failed:', err);
259
+ }
260
+ // 8. Return instruction for agent to write the 3 interpretive files only
261
+ const fileList = generatedFiles.map((f) => `✓ .agentbrain/${f.name}`).join('\n');
262
+ return `AgentBrain has auto-generated ${generatedFiles.length} context files from your codebase in ~2 seconds:
263
+
264
+ ${fileList}
265
+
266
+ These cover ~80% of what you need to work effectively in this codebase.
267
+
268
+ Now generate 3 short interpretive files. Call save_context() when done.
269
+
270
+ IMPORTANT: Keep each file SHORT and focused on NON-OBVIOUS information only. The factual files already exist. Don't repeat what's already in schema.md, dependencies.md, etc.
271
+
272
+ ---
273
+
274
+ 1. context.md (target: 20-30 lines)
275
+ Only include:
276
+ - What this product does (2 sentences max)
277
+ - Key modules list (name + 1 line purpose each)
278
+ - 1-2 non-obvious data flows
279
+ Skip: tech stack (in dependencies.md), file structure (in inventory.md)
280
+
281
+ 2. patterns.md (target: 20-40 lines)
282
+ Only include:
283
+ - Key utilities to ALWAYS use (not reinvent)
284
+ - 3-5 anti-patterns specific to this codebase
285
+ - Error handling approach
286
+ - 1-2 non-obvious conventions
287
+ Skip: standard framework patterns, obvious conventions
288
+
289
+ 3. decisions.md (target: 10-20 lines)
290
+ Only include:
291
+ - What must NOT be changed (compliance, legacy)
292
+ - Why key non-obvious libraries were chosen
293
+ - 1-2 architectural choices that surprise devs
294
+ Skip: obvious choices, anything self-evident from the code
295
+
296
+ Total target: under 80 lines across all 3 files.
297
+ Short, opinionated, non-obvious = high signal.
298
+ Long, comprehensive, obvious = noise.`;
299
+ }
16
300
  export async function loadContext(input) {
17
- const { repo_path, force_refresh = false } = input;
301
+ const { repo_path, force_refresh = false, full = false, files: requestedFiles } = input;
18
302
  // Expand path to handle ~, relative paths, etc.
19
303
  const expandedPath = expandPath(repo_path);
20
304
  const contextDir = join(expandedPath, '.agentbrain');
305
+ // Bootstrap if .agentbrain/ doesn't exist
306
+ if (!existsSync(contextDir)) {
307
+ const bootstrapMessage = await bootstrapRepo(expandedPath);
308
+ return {
309
+ content: bootstrapMessage,
310
+ fromCache: false,
311
+ tokensUsed: 0,
312
+ doom_warning: null,
313
+ };
314
+ }
21
315
  // Get current git hash for staleness check
22
316
  const currentGitHash = await getGitHash(expandedPath);
23
- // Try to load from disk first
24
- const contextPath = join(contextDir, 'context.md');
25
- const depMapPath = join(contextDir, 'dependency-map.md');
26
- const patternsPath = join(contextDir, 'patterns.md');
27
- const allExist = existsSync(contextPath) && existsSync(depMapPath) && existsSync(patternsPath);
28
- if (!force_refresh && allExist) {
29
- // Load from disk
30
- const context = await readFile(contextPath, 'utf-8');
31
- const depMap = await readFile(depMapPath, 'utf-8');
32
- const patterns = await readFile(patternsPath, 'utf-8');
33
- const combined = `# Repository Context\n\n${context}\n\n---\n\n# Dependency Map\n\n${depMap}\n\n---\n\n# Patterns\n\n${patterns}`;
34
- // Post-processing: If cache.json or setup-source.json is missing, agent wrote files directly
35
- const cachePath = join(contextDir, 'cache.json');
36
- const setupSourcePath = join(contextDir, 'setup-source.json');
37
- const cacheExists = existsSync(cachePath);
38
- const setupSourceExists = existsSync(setupSourcePath);
39
- if (!cacheExists || !setupSourceExists) {
40
- // Agent wrote files directly — run post-processing
41
- try {
42
- // Write cache.json
43
- if (!cacheExists) {
44
- const cache = {
45
- gitHash: currentGitHash,
46
- docs: {
47
- context: {
48
- type: 'context',
49
- content: context,
50
- savedAt: new Date().toISOString(),
51
- },
52
- 'dependency-map': {
53
- type: 'dependency-map',
54
- content: depMap,
55
- savedAt: new Date().toISOString(),
56
- },
57
- patterns: {
58
- type: 'patterns',
59
- content: patterns,
317
+ // Try to load from disk first - read ALL .md files
318
+ if (!force_refresh && existsSync(contextDir)) {
319
+ try {
320
+ const allFiles = await readdir(contextDir);
321
+ const mdFiles = allFiles.filter((f) => f.endsWith('.md'));
322
+ if (mdFiles.length > 0) {
323
+ // Order files properly
324
+ const orderedFiles = orderContextFiles(mdFiles);
325
+ // Determine which files to load based on mode
326
+ let filesToLoad = orderedFiles;
327
+ if (requestedFiles && requestedFiles.length > 0) {
328
+ // Load only requested files
329
+ filesToLoad = orderedFiles.filter((f) => requestedFiles.includes(f));
330
+ }
331
+ else if (!full) {
332
+ // Slim mode: load context.md, run-commands.md, and agent-hints.md for summary
333
+ filesToLoad = orderedFiles.filter((f) => ['context.md', 'run-commands.md', 'agent-hints.md'].includes(f));
334
+ }
335
+ // Load files
336
+ const fileContents = [];
337
+ for (const file of filesToLoad) {
338
+ const filePath = join(contextDir, file);
339
+ const content = await readFile(filePath, 'utf-8');
340
+ const title = file.replace('.md', '').replace(/-/g, ' ').replace(/\b\w/g, (l) => l.toUpperCase());
341
+ fileContents.push({ name: file, content: `# ${title}\n\n${content}` });
342
+ }
343
+ // Build output based on mode
344
+ let combined;
345
+ if (!full && !requestedFiles) {
346
+ // Slim mode: build summary from context.md, run-commands.md, and agent-hints.md
347
+ const contextContent = fileContents.find((f) => f.name === 'context.md');
348
+ const runCommandsContent = fileContents.find((f) => f.name === 'run-commands.md');
349
+ const agentHintsContent = fileContents.find((f) => f.name === 'agent-hints.md');
350
+ if (contextContent) {
351
+ combined = buildSlimSummary(contextContent.content, orderedFiles, runCommandsContent?.content, agentHintsContent?.content);
352
+ }
353
+ else {
354
+ // Fallback if context.md doesn't exist
355
+ combined = buildSlimSummary('# Project\n\n', orderedFiles, runCommandsContent?.content, agentHintsContent?.content);
356
+ }
357
+ }
358
+ else {
359
+ // Full mode or specific files: concatenate all loaded files
360
+ combined = fileContents.map((f) => f.content).join('\n\n---\n\n');
361
+ }
362
+ // Post-processing: If cache.json or setup-source.json is missing, agent wrote files directly
363
+ const cachePath = join(contextDir, 'cache.json');
364
+ const setupSourcePath = join(contextDir, 'setup-source.json');
365
+ const cacheExists = existsSync(cachePath);
366
+ const setupSourceExists = existsSync(setupSourcePath);
367
+ if (!cacheExists || !setupSourceExists) {
368
+ // Agent wrote files directly — run post-processing
369
+ try {
370
+ // Write cache.json with ALL files
371
+ if (!cacheExists) {
372
+ const docs = {};
373
+ for (const file of orderedFiles) {
374
+ const filePath = join(contextDir, file);
375
+ const content = await readFile(filePath, 'utf-8');
376
+ const baseName = file.replace('.md', '');
377
+ docs[baseName] = {
378
+ type: baseName,
379
+ content,
380
+ savedAt: new Date().toISOString(),
381
+ };
382
+ }
383
+ const cache = {
384
+ gitHash: currentGitHash,
385
+ docs,
60
386
  savedAt: new Date().toISOString(),
61
- },
62
- },
63
- savedAt: new Date().toISOString(),
64
- };
65
- await writeFile(cachePath, JSON.stringify(cache, null, 2), 'utf-8');
387
+ };
388
+ await writeFile(cachePath, JSON.stringify(cache, null, 2), 'utf-8');
389
+ }
390
+ // Write setup-source.json
391
+ if (!setupSourceExists) {
392
+ await writeFile(setupSourcePath, JSON.stringify({ source: 'mcp', setupAt: new Date().toISOString() }, null, 2), 'utf-8');
393
+ }
394
+ // Update .gitignore
395
+ await ensureGitignore(expandedPath);
396
+ // Install git hook if this is a git repository
397
+ if (isGitRepository(expandedPath)) {
398
+ await installPostCommitHook(expandedPath);
399
+ }
400
+ // Create agent files with ALL context files
401
+ await createAgentFilesShared(expandedPath, orderedFiles);
402
+ // Silent success - only log errors
403
+ }
404
+ catch (err) {
405
+ console.error('[load_context] Post-processing failed:', err);
406
+ }
66
407
  }
67
- // Write setup-source.json
68
- if (!setupSourceExists) {
69
- await writeFile(setupSourcePath, JSON.stringify({ source: 'mcp', setupAt: new Date().toISOString() }, null, 2), 'utf-8');
408
+ // Check if cached git hash matches current HEAD
409
+ const cache = await loadCache(expandedPath);
410
+ const isStale = cache && cache.gitHash !== currentGitHash;
411
+ // Check for doom loop warnings
412
+ const doomWarning = await getPendingDoomForMCP(expandedPath);
413
+ const footer = '\n\n---\n_AgentBrain context loaded. Check doom_warning field for any detected issues._';
414
+ return {
415
+ content: combined + footer,
416
+ fromCache: true,
417
+ tokensUsed: 0,
418
+ ...(isStale && {
419
+ stale: true,
420
+ cached_sha: cache.gitHash,
421
+ current_sha: currentGitHash,
422
+ message: 'Context may be outdated. Run agentbrain init to refresh.',
423
+ }),
424
+ doom_warning: doomWarning,
425
+ };
426
+ }
427
+ }
428
+ catch (err) {
429
+ // If there's an error reading files, fall through to cache check
430
+ console.error('[load_context] Error reading .agentbrain files:', err);
431
+ }
432
+ }
433
+ // Check cache validity - load from cache.json if available
434
+ const cache = await loadCache(expandedPath);
435
+ if (!force_refresh && cache && cache.docs) {
436
+ // Extract all doc names and order them
437
+ const docNames = Object.keys(cache.docs).map((name) => `${name}.md`);
438
+ const orderedDocs = orderContextFiles(docNames);
439
+ // Determine which docs to load based on mode
440
+ let docsToLoad = orderedDocs;
441
+ if (requestedFiles && requestedFiles.length > 0) {
442
+ // Load only requested files
443
+ docsToLoad = orderedDocs.filter((f) => requestedFiles.includes(f));
444
+ }
445
+ else if (!full) {
446
+ // Slim mode: only load context for summary
447
+ docsToLoad = orderedDocs.filter((f) => f === 'context.md');
448
+ }
449
+ // Build content from cache
450
+ const fileContents = [];
451
+ for (const docFile of docsToLoad) {
452
+ const docName = docFile.replace('.md', '');
453
+ const doc = cache.docs[docName];
454
+ if (doc) {
455
+ const title = docName.replace(/-/g, ' ').replace(/\b\w/g, (l) => l.toUpperCase());
456
+ fileContents.push(`# ${title}\n\n${doc.content}`);
457
+ }
458
+ }
459
+ if (fileContents.length > 0) {
460
+ // Build output based on mode
461
+ let combined;
462
+ if (!full && !requestedFiles) {
463
+ // Slim mode: build summary
464
+ const contextContent = fileContents.find((c) => c.startsWith('# Context'));
465
+ if (contextContent) {
466
+ combined = buildSlimSummary(contextContent, orderedDocs);
70
467
  }
71
- // Update .gitignore
72
- await ensureGitignore(expandedPath);
73
- // Install git hook if this is a git repository
74
- if (isGitRepository(expandedPath)) {
75
- await installPostCommitHook(expandedPath);
468
+ else {
469
+ combined = buildSlimSummary('# Project\n\n', orderedDocs);
76
470
  }
77
- // Create agent files
78
- await createAgentFilesShared(expandedPath);
79
- console.error('[load_context] Post-processing completed for manually-written files');
80
471
  }
81
- catch (err) {
82
- console.error('[load_context] Post-processing failed:', err);
472
+ else {
473
+ // Full mode or specific files
474
+ combined = fileContents.join('\n\n---\n\n');
83
475
  }
476
+ const isStale = cache.gitHash !== currentGitHash;
477
+ // Check for doom loop warnings
478
+ const doomWarning = await getPendingDoomForMCP(expandedPath);
479
+ const footer = '\n\n---\n_AgentBrain context loaded. Check doom_warning field for any detected issues._';
480
+ return {
481
+ content: combined + footer,
482
+ fromCache: true,
483
+ tokensUsed: 0,
484
+ ...(isStale && {
485
+ stale: true,
486
+ cached_sha: cache.gitHash,
487
+ current_sha: currentGitHash,
488
+ message: 'Context may be outdated. Run agentbrain init to refresh.',
489
+ }),
490
+ doom_warning: doomWarning,
491
+ };
84
492
  }
85
- // Check if cached git hash matches current HEAD
86
- const cache = await loadCache(expandedPath);
87
- const isStale = cache && cache.gitHash !== currentGitHash;
88
- // Check for doom loop warnings
89
- const doomWarning = await getPendingDoomForMCP(expandedPath);
90
- const footer = '\n\n---\n_AgentBrain context loaded. Check doom_warning field for any detected issues._';
91
- return {
92
- content: combined + footer,
93
- fromCache: true,
94
- tokensUsed: 0,
95
- ...(isStale && {
96
- stale: true,
97
- cached_sha: cache.gitHash,
98
- current_sha: currentGitHash,
99
- message: 'Context may be outdated. Run agentbrain init to refresh.',
100
- }),
101
- doom_warning: doomWarning,
102
- };
103
- }
104
- // Check cache validity (using currentGitHash from above)
105
- const cachedContext = await getCachedDoc(expandedPath, currentGitHash, 'context');
106
- const cachedDepMap = await getCachedDoc(expandedPath, currentGitHash, 'dependency-map');
107
- const cachedPatterns = await getCachedDoc(expandedPath, currentGitHash, 'patterns');
108
- if (!force_refresh && cachedContext && cachedDepMap && cachedPatterns) {
109
- const combined = `# Repository Context\n\n${cachedContext.content}\n\n---\n\n# Dependency Map\n\n${cachedDepMap.content}\n\n---\n\n# Patterns\n\n${cachedPatterns.content}`;
110
- // Check if cached git hash matches current HEAD
111
- const cache = await loadCache(expandedPath);
112
- const isStale = cache && cache.gitHash !== currentGitHash;
113
- // Check for doom loop warnings
114
- const doomWarning = await getPendingDoomForMCP(expandedPath);
115
- const footer = '\n\n---\n_AgentBrain context loaded. Check doom_warning field for any detected issues._';
116
- return {
117
- content: combined + footer,
118
- fromCache: true,
119
- tokensUsed: 0,
120
- ...(isStale && {
121
- stale: true,
122
- cached_sha: cache.gitHash,
123
- current_sha: currentGitHash,
124
- message: 'Context may be outdated. Run agentbrain init to refresh.',
125
- }),
126
- doom_warning: doomWarning,
127
- };
128
493
  }
129
494
  // Need to generate - no API key path returns bootstrap message
130
495
  let aiConfig = null;
@@ -167,7 +532,13 @@ Call: setup_repo({ repo_path: '${expandedPath}' })`,
167
532
  }
168
533
  export const loadContextSchema = {
169
534
  name: 'load_context',
170
- description: 'Load codebase context. If first time setup, automatically scans repo and returns file tree + key files with friendly instructions. Agent then generates context documents. No API key needed.',
535
+ description: 'Load codebase context.\n\n' +
536
+ 'Default: Returns slim summary showing available context files. Fast, low token usage (~100 lines).\n\n' +
537
+ 'Options:\n' +
538
+ ' full: true → load all context files (large, 1000+ lines)\n' +
539
+ ' files: ["schema.md"] → load specific files only\n' +
540
+ ' force_refresh: true → regenerate context\n\n' +
541
+ 'If first time setup, automatically scans repo and returns setup instructions.',
171
542
  inputSchema: {
172
543
  type: 'object',
173
544
  properties: {
@@ -179,6 +550,17 @@ export const loadContextSchema = {
179
550
  type: 'boolean',
180
551
  description: 'Force regeneration even if cache is valid (default: false)',
181
552
  },
553
+ full: {
554
+ type: 'boolean',
555
+ description: 'Load all context files instead of slim summary (default: false)',
556
+ },
557
+ files: {
558
+ type: 'array',
559
+ description: 'Load specific files only (e.g., ["schema.md", "patterns.md"])',
560
+ items: {
561
+ type: 'string',
562
+ },
563
+ },
182
564
  },
183
565
  required: ['repo_path'],
184
566
  },