tachibot-mcp 2.17.0 → 2.18.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.
- package/CHANGELOG.md +36 -0
- package/dist/src/tools/advanced-modes.js +31 -6
- package/dist/src/tools/gemini-tools.js +31 -10
- package/dist/src/tools/grok-enhanced.js +6 -3
- package/dist/src/tools/grok-tools.js +28 -7
- package/dist/src/tools/openai-tools.js +22 -5
- package/dist/src/tools/openrouter-tools.js +195 -60
- package/dist/src/tools/perplexity-tools.js +11 -2
- package/dist/src/tools/planner-tools.js +351 -85
- package/dist/src/utils/file-reader.js +96 -0
- package/docs/superpowers/plans/2026-03-21-files-param-all-tools.md +364 -0
- package/docs/superpowers/plans/2026-03-21-files-param-and-website-bump.md +504 -0
- package/docs/superpowers/plans/2026-03-21-goal-oriented-checkpoints.md +584 -0
- package/docs/superpowers/plans/2026-03-21-kimi-decompose-readability.md +365 -0
- package/package.json +1 -1
- package/skills/blueprint/SKILL.md +55 -8
package/CHANGELOG.md
CHANGED
|
@@ -5,6 +5,42 @@ All notable changes to TachiBot MCP will be documented in this file.
|
|
|
5
5
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
|
6
6
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
7
|
|
|
8
|
+
## [2.18.0] - 2026-03-21
|
|
9
|
+
|
|
10
|
+
### Added
|
|
11
|
+
- **Goal-oriented checkpoints** — `planner_maker` and `planner_runner` now accept `goal` parameter for success criteria tracking
|
|
12
|
+
- **6 checkpoint gates** with 5 different models (no adjacent repeats): step1 (Gemini Sherlock), 10% (Grok), 25% (GPT + amendment protocol), 50% (Qwen), 80% (Kimi decompose), 100% (GPT+Gemini dual judge)
|
|
13
|
+
- **Reflexion Lite** — at 100%, Gemini reflects on what worked/failed, lesson saved to devlog
|
|
14
|
+
- **Amendment protocol** — at 25%, structured plan revision (evidence + proposed changes + impact) with human gate
|
|
15
|
+
- **Unblinded checkpoints** — `diff`, `testResults`, `modifiedFiles` params replace blind `code.substring(0,1500)` with real evidence
|
|
16
|
+
- **`files` param on all analysis tools** — 39 tools across 9 files can now read ACTUAL CODE from disk via `readFilesIntoContext()`
|
|
17
|
+
- **Shared `src/utils/file-reader.ts`** — reusable file reader with line range support (`file.ts:100-200`), size limits, directory expansion
|
|
18
|
+
- **Blueprint skill updated** — `goal` param, prompt template, `planner_runner` as default execution path
|
|
19
|
+
|
|
20
|
+
### Fixed
|
|
21
|
+
- **Step index reset bug** — filtered arrays used local index instead of original step number (found by 3-model consensus: Kimi + Gemini + Qwen reading actual code)
|
|
22
|
+
- **Truncation indicators** — `code.substring()` now adds `[truncated]` so judge models know they're seeing partial code
|
|
23
|
+
|
|
24
|
+
## [2.17.2] - 2026-03-21
|
|
25
|
+
|
|
26
|
+
### Added
|
|
27
|
+
- **`files` parameter on 13+ more tools** — grok_architect, grok_brainstorm, grok_reason_v4, openai_explain, kimi_code, kimi_long_context, gemini_judge, gemini_brainstorm, gemini_query, gemini_summarize, qwq_reason, qwen_competitive, qwen_general (38 tools now support `files`)
|
|
28
|
+
- **Directory expansion in file reader** — pass `src/tools/` to read all code files in a directory (non-recursive, capped at 20 files)
|
|
29
|
+
- **Smart char budget** — multi-file reads distribute token budget across files to prevent context overflow
|
|
30
|
+
|
|
31
|
+
## [2.17.1] - 2026-03-21
|
|
32
|
+
|
|
33
|
+
### Fixed
|
|
34
|
+
- **kimi_decompose readability overhaul** — output now uses OVERVIEW/STRUCTURE/DETAILS/RISKS sections instead of dense inline metadata
|
|
35
|
+
- **Reasoning leak stripped** — Kimi K2.5 dumps CoT into content; now extracted via `<output>` tags with OVERVIEW fallback
|
|
36
|
+
- **Conflicting FORMAT_INSTRUCTION removed** — emoji headers and verdict lines no longer clash with decomposition formatting
|
|
37
|
+
- **Heartbeat interval fixed** — was incorrectly set to 240s instead of default 5s; network timeout now correctly passed to callOpenRouter (360s)
|
|
38
|
+
- **Type safety** — args typed from zod schema, unused `log` removed, `||` replaced with `??`
|
|
39
|
+
|
|
40
|
+
### Changed
|
|
41
|
+
- **Smart decomposition** — model now infers context, constraints, risks, and measurable criteria even when user doesn't state them
|
|
42
|
+
- **Tuned for format adherence** — temperature 0.3 (was 0.5), maxTokens 4500 (was 6000), timeout 360s (was 180s default)
|
|
43
|
+
|
|
8
44
|
## [2.17.0] - 2026-03-21
|
|
9
45
|
|
|
10
46
|
### Changed
|
|
@@ -5,6 +5,7 @@ import { Architect } from "../modes/architect.js";
|
|
|
5
5
|
import { CodeReviewer } from "../modes/code-reviewer.js";
|
|
6
6
|
import { DocumentationWriter } from "../modes/documentation-writer.js";
|
|
7
7
|
import { TestArchitect } from "../modes/test-architect.js";
|
|
8
|
+
import { readFilesIntoContext } from "../utils/file-reader.js";
|
|
8
9
|
// Workflow tools removed - using workflow-runner.ts instead
|
|
9
10
|
const auditor = new Auditor();
|
|
10
11
|
const commitGuardian = new CommitGuardian();
|
|
@@ -18,11 +19,15 @@ export const auditorTool = {
|
|
|
18
19
|
description: "Evidence-based audit. Put the CONTEXT in the 'context' parameter.",
|
|
19
20
|
parameters: z.object({
|
|
20
21
|
context: z.string().describe("What to audit (REQUIRED - put your audit request here)"),
|
|
22
|
+
files: z.array(z.string()).optional().describe("File paths to read as code context. Supports line ranges: 'src/foo.ts:100-200'. Model sees ACTUAL CODE."),
|
|
21
23
|
evidenceRequired: z.boolean().optional().describe("Require evidence for claims")
|
|
22
24
|
}),
|
|
23
25
|
execute: async (args, { log }) => {
|
|
24
26
|
log.info("Starting audit");
|
|
25
|
-
const
|
|
27
|
+
const fileContext = args.files?.length
|
|
28
|
+
? `\n\nSOURCE CODE:\n${readFilesIntoContext(args.files)}`
|
|
29
|
+
: "";
|
|
30
|
+
const result = await auditor.audit(args.context + fileContext, {
|
|
26
31
|
evidenceRequired: args.evidenceRequired
|
|
27
32
|
});
|
|
28
33
|
log.info("Audit complete", {
|
|
@@ -38,6 +43,7 @@ export const commitGuardianTool = {
|
|
|
38
43
|
description: "Pre-commit validation. Put the CONTEXT in the 'context' parameter.",
|
|
39
44
|
parameters: z.object({
|
|
40
45
|
context: z.string().describe("Code changes to validate (REQUIRED - put your diff/changes here)"),
|
|
46
|
+
files: z.array(z.string()).optional().describe("File paths to read as code context. Supports line ranges: 'src/foo.ts:100-200'. Model sees ACTUAL CODE."),
|
|
41
47
|
strict: z.boolean().optional().describe("Use strict validation rules"),
|
|
42
48
|
checkSecurity: z.boolean().optional().describe("Check for security issues"),
|
|
43
49
|
checkQuality: z.boolean().optional().describe("Check code quality"),
|
|
@@ -45,7 +51,10 @@ export const commitGuardianTool = {
|
|
|
45
51
|
}),
|
|
46
52
|
execute: async (args, { log }) => {
|
|
47
53
|
log.info("Validating commit");
|
|
48
|
-
const
|
|
54
|
+
const fileContext = args.files?.length
|
|
55
|
+
? `\n\nSOURCE CODE:\n${readFilesIntoContext(args.files)}`
|
|
56
|
+
: "";
|
|
57
|
+
const result = await commitGuardian.validate(args.context + fileContext, {
|
|
49
58
|
strict: args.strict,
|
|
50
59
|
checkSecurity: args.checkSecurity,
|
|
51
60
|
checkQuality: args.checkQuality,
|
|
@@ -66,6 +75,7 @@ export const architectTool = {
|
|
|
66
75
|
parameters: z.object({
|
|
67
76
|
query: z.string().describe("What to analyze in the codebase (REQUIRED - put your question here)"),
|
|
68
77
|
path: z.string().optional().describe("Path to the codebase to analyze"),
|
|
78
|
+
files: z.array(z.string()).optional().describe("File paths to read as code context. Supports line ranges: 'src/foo.ts:100-200'. Model sees ACTUAL CODE."),
|
|
69
79
|
depth: z.enum(["shallow", "normal", "deep"])
|
|
70
80
|
.optional()
|
|
71
81
|
.describe("Analysis depth - must be one of: shallow, normal, deep"),
|
|
@@ -76,7 +86,10 @@ export const architectTool = {
|
|
|
76
86
|
depth: args.depth || "normal",
|
|
77
87
|
path: args.path
|
|
78
88
|
});
|
|
79
|
-
const
|
|
89
|
+
const fileContext = args.files?.length
|
|
90
|
+
? `\n\nSOURCE CODE:\n${readFilesIntoContext(args.files)}`
|
|
91
|
+
: "";
|
|
92
|
+
const result = await architect.analyze(args.query + fileContext, {
|
|
80
93
|
path: args.path,
|
|
81
94
|
depth: args.depth,
|
|
82
95
|
focusAreas: args.focusAreas
|
|
@@ -98,6 +111,7 @@ export const codeReviewerTool = {
|
|
|
98
111
|
parameters: z.object({
|
|
99
112
|
code: z.string().describe("The actual source code to review (REQUIRED - put your code here)"),
|
|
100
113
|
language: z.string().optional().describe("Programming language (e.g., 'typescript', 'python')"),
|
|
114
|
+
files: z.array(z.string()).optional().describe("File paths to read as code context. Supports line ranges: 'src/foo.ts:100-200'. Model sees ACTUAL CODE."),
|
|
101
115
|
focusAreas: z.array(z.enum(['security', 'performance', 'readability', 'bugs', 'best-practices']))
|
|
102
116
|
.optional()
|
|
103
117
|
.describe("Focus areas - array of: security, performance, readability, bugs, best-practices"),
|
|
@@ -111,7 +125,10 @@ export const codeReviewerTool = {
|
|
|
111
125
|
language: args.language || 'auto-detect',
|
|
112
126
|
focusAreas: args.focusAreas || 'all'
|
|
113
127
|
});
|
|
114
|
-
const
|
|
128
|
+
const fileContext = args.files?.length
|
|
129
|
+
? `\n\nSOURCE CODE:\n${readFilesIntoContext(args.files)}`
|
|
130
|
+
: "";
|
|
131
|
+
const result = await codeReviewer.review(args.code + fileContext, {
|
|
115
132
|
language: args.language,
|
|
116
133
|
focusAreas: args.focusAreas,
|
|
117
134
|
severity: args.severity,
|
|
@@ -131,6 +148,7 @@ export const documentationWriterTool = {
|
|
|
131
148
|
description: "Documentation generation. Put the CODE in the 'code' parameter.",
|
|
132
149
|
parameters: z.object({
|
|
133
150
|
code: z.string().describe("The source code to document (REQUIRED - put your code here)"),
|
|
151
|
+
files: z.array(z.string()).optional().describe("File paths to read as code context. Supports line ranges: 'src/foo.ts:100-200'. Model sees ACTUAL CODE."),
|
|
134
152
|
style: z.enum(['narrative', 'technical', 'beginner-friendly', 'api-reference'])
|
|
135
153
|
.optional()
|
|
136
154
|
.describe("Documentation style - must be one of: narrative, technical, beginner-friendly, api-reference"),
|
|
@@ -145,7 +163,10 @@ export const documentationWriterTool = {
|
|
|
145
163
|
style: args.style || 'narrative',
|
|
146
164
|
format: args.format || 'markdown'
|
|
147
165
|
});
|
|
148
|
-
const
|
|
166
|
+
const fileContext = args.files?.length
|
|
167
|
+
? `\n\nSOURCE CODE:\n${readFilesIntoContext(args.files)}`
|
|
168
|
+
: "";
|
|
169
|
+
const result = await documentationWriter.generateDocs(args.code + fileContext, {
|
|
149
170
|
style: args.style,
|
|
150
171
|
includeExamples: args.includeExamples,
|
|
151
172
|
generateToc: args.generateToc,
|
|
@@ -166,6 +187,7 @@ export const testArchitectTool = {
|
|
|
166
187
|
description: "Test suite design. Put the CODE in the 'code' parameter.",
|
|
167
188
|
parameters: z.object({
|
|
168
189
|
code: z.string().describe("The source code to create tests for (REQUIRED - put your code here)"),
|
|
190
|
+
files: z.array(z.string()).optional().describe("File paths to read as code context. Supports line ranges: 'src/foo.ts:100-200'. Model sees ACTUAL CODE."),
|
|
169
191
|
testFramework: z.enum(['jest', 'mocha', 'vitest', 'cypress', 'playwright'])
|
|
170
192
|
.optional()
|
|
171
193
|
.describe("Test framework - must be one of: jest, mocha, vitest, cypress, playwright"),
|
|
@@ -183,7 +205,10 @@ export const testArchitectTool = {
|
|
|
183
205
|
testTypes: args.testTypes || ['unit', 'integration', 'e2e'],
|
|
184
206
|
coverage: args.coverage || 'thorough'
|
|
185
207
|
});
|
|
186
|
-
const
|
|
208
|
+
const fileContext = args.files?.length
|
|
209
|
+
? `\n\nSOURCE CODE:\n${readFilesIntoContext(args.files)}`
|
|
210
|
+
: "";
|
|
211
|
+
const result = await testArchitect.architectTests(args.code + fileContext, {
|
|
187
212
|
testFramework: args.testFramework,
|
|
188
213
|
testTypes: args.testTypes,
|
|
189
214
|
coverage: args.coverage,
|
|
@@ -11,6 +11,7 @@ import { stripFormatting } from "../utils/format-stripper.js";
|
|
|
11
11
|
import { FORMAT_INSTRUCTION } from "../utils/format-constants.js";
|
|
12
12
|
import { withHeartbeat } from "../utils/streaming-helper.js";
|
|
13
13
|
import { getTimeoutConfig } from "../config/timeout-config.js";
|
|
14
|
+
import { readFilesIntoContext } from "../utils/file-reader.js";
|
|
14
15
|
// Note: renderOutput is applied centrally in server.ts safeAddTool() - no need to import here
|
|
15
16
|
// NOTE: dotenv is loaded in server.ts before any imports
|
|
16
17
|
// No need to reload here - just read from process.env
|
|
@@ -171,7 +172,8 @@ export const geminiQueryTool = {
|
|
|
171
172
|
model: z.enum(["gemini-3", "pro", "flash"])
|
|
172
173
|
.optional()
|
|
173
174
|
.default("gemini-3")
|
|
174
|
-
.describe("Model variant - must be one of: gemini-3 (default), pro, flash")
|
|
175
|
+
.describe("Model variant - must be one of: gemini-3 (default), pro, flash"),
|
|
176
|
+
files: z.array(z.string()).optional().describe("File paths to read as code context. Supports line ranges: 'src/foo.ts:100-200'. Model sees ACTUAL CODE.")
|
|
175
177
|
}),
|
|
176
178
|
execute: async (args, { log, reportProgress }) => {
|
|
177
179
|
let model = GEMINI_MODELS.GEMINI_3_PRO; // Default to Gemini 3
|
|
@@ -182,8 +184,9 @@ export const geminiQueryTool = {
|
|
|
182
184
|
model = GEMINI_MODELS.PRO;
|
|
183
185
|
}
|
|
184
186
|
// Skip validation - queries may contain code or LLM-generated content
|
|
187
|
+
const fileContext = args.files?.length ? `\n\nSOURCE CODE:\n${readFilesIntoContext(args.files)}` : "";
|
|
185
188
|
const reportFn = reportProgress ?? (async () => { });
|
|
186
|
-
const result = await withHeartbeat(() => callGemini(args.prompt, model, undefined, 0.7, 'llm-orchestration'), reportFn);
|
|
189
|
+
const result = await withHeartbeat(() => callGemini(args.prompt + fileContext, model, undefined, 0.7, 'llm-orchestration'), reportFn);
|
|
187
190
|
return stripFormatting(result);
|
|
188
191
|
}
|
|
189
192
|
};
|
|
@@ -200,7 +203,8 @@ export const geminiBrainstormTool = {
|
|
|
200
203
|
parameters: z.object({
|
|
201
204
|
prompt: z.string().describe("The ideas or topic to organize and refine (REQUIRED - put raw ideas or topic here)"),
|
|
202
205
|
claudeThoughts: z.string().optional().describe("Claude's initial thoughts or raw ideas to cluster and refine"),
|
|
203
|
-
maxClusters: z.number().optional().default(5).describe("Number of idea clusters to create (default: 5)")
|
|
206
|
+
maxClusters: z.number().optional().default(5).describe("Number of idea clusters to create (default: 5)"),
|
|
207
|
+
files: z.array(z.string()).optional().describe("File paths to read as code context. Supports line ranges: 'src/foo.ts:100-200'. Model sees ACTUAL CODE.")
|
|
204
208
|
}),
|
|
205
209
|
execute: async (args, { log, reportProgress }) => {
|
|
206
210
|
const systemPrompt = `Convergent synthesis engine. Output consumed by automated toolchain.
|
|
@@ -226,8 +230,11 @@ Per cluster: Name | Score (1-10) | Top ideas (ranked) | Key insight
|
|
|
226
230
|
Final: Which cluster has highest expected value and why. State the meta-pattern.
|
|
227
231
|
No preamble. Structured output only.
|
|
228
232
|
${FORMAT_INSTRUCTION}`;
|
|
233
|
+
const fileContext = args.files?.length
|
|
234
|
+
? `\n\nSOURCE CODE:\n${readFilesIntoContext(args.files)}`
|
|
235
|
+
: "";
|
|
229
236
|
const reportFn = reportProgress ?? (async () => { });
|
|
230
|
-
const response = await withHeartbeat(() => callGemini(args.prompt, GEMINI_MODELS.GEMINI_3_PRO, systemPrompt, 0.7, 'llm-orchestration'), reportFn);
|
|
237
|
+
const response = await withHeartbeat(() => callGemini(args.prompt + fileContext, GEMINI_MODELS.GEMINI_3_PRO, systemPrompt, 0.7, 'llm-orchestration'), reportFn);
|
|
231
238
|
return stripFormatting(response);
|
|
232
239
|
}
|
|
233
240
|
};
|
|
@@ -241,6 +248,7 @@ export const geminiAnalyzeCodeTool = {
|
|
|
241
248
|
parameters: z.object({
|
|
242
249
|
code: z.string().describe("The actual source code to analyze (REQUIRED - put your code here)"),
|
|
243
250
|
language: z.string().optional().describe("Programming language (e.g., 'typescript', 'python')"),
|
|
251
|
+
files: z.array(z.string()).optional().describe("File paths to read as code context. Supports line ranges: 'src/foo.ts:100-200'. Model sees ACTUAL CODE."),
|
|
244
252
|
focus: z.string().optional().default("general").describe("Analysis focus (e.g., quality, security, performance, bugs, general)")
|
|
245
253
|
}),
|
|
246
254
|
execute: async (args, { log, reportProgress }) => {
|
|
@@ -255,9 +263,12 @@ export const geminiAnalyzeCodeTool = {
|
|
|
255
263
|
const systemPrompt = `Expert code reviewer. ${args.language || ''} code.
|
|
256
264
|
${focusText}.
|
|
257
265
|
${FORMAT_INSTRUCTION}`;
|
|
266
|
+
const fileContext = args.files?.length
|
|
267
|
+
? `\n\nSOURCE CODE:\n${readFilesIntoContext(args.files)}`
|
|
268
|
+
: "";
|
|
258
269
|
// Skip validation - code analysis naturally contains patterns that trigger false positives
|
|
259
270
|
const reportFn = reportProgress ?? (async () => { });
|
|
260
|
-
const result = await withHeartbeat(() => callGemini(`Analyze this code:\n\n\`\`\`${args.language || ''}\n${args.code}\n
|
|
271
|
+
const result = await withHeartbeat(() => callGemini(`Analyze this code:\n\n\`\`\`${args.language || ''}\n${args.code}\n\`\`\`${fileContext}`, GEMINI_MODELS.GEMINI_3_PRO, systemPrompt, 0.3, 'llm-orchestration'), reportFn);
|
|
261
272
|
return stripFormatting(result);
|
|
262
273
|
}
|
|
263
274
|
};
|
|
@@ -270,6 +281,7 @@ export const geminiAnalyzeTextTool = {
|
|
|
270
281
|
description: "Rhetorical analysis: dissect arguments for bias, logical fallacies, and persuasion tactics. Use for evaluating claims, detecting manipulation, or understanding argument structure. Put the TEXT in the 'text' parameter.",
|
|
271
282
|
parameters: z.object({
|
|
272
283
|
text: z.string().describe("The text to analyze (REQUIRED - put your text here)"),
|
|
284
|
+
files: z.array(z.string()).optional().describe("File paths to read as code context. Supports line ranges: 'src/foo.ts:100-200'. Model sees ACTUAL CODE."),
|
|
273
285
|
type: z.string()
|
|
274
286
|
.optional()
|
|
275
287
|
.default("rhetoric")
|
|
@@ -306,8 +318,11 @@ OUTPUT:
|
|
|
306
318
|
${analysisText}
|
|
307
319
|
No preamble. Structured output only.
|
|
308
320
|
${FORMAT_INSTRUCTION}`;
|
|
321
|
+
const fileContext = args.files?.length
|
|
322
|
+
? `\n\nSOURCE CODE:\n${readFilesIntoContext(args.files)}`
|
|
323
|
+
: "";
|
|
309
324
|
const reportFn = reportProgress ?? (async () => { });
|
|
310
|
-
const result = await withHeartbeat(() => callGemini(`Analyze this text:\n\n${args.text}`, GEMINI_MODELS.GEMINI_3_PRO, systemPrompt, 0.3, 'llm-orchestration'), reportFn);
|
|
325
|
+
const result = await withHeartbeat(() => callGemini(`Analyze this text:\n\n${args.text}${fileContext}`, GEMINI_MODELS.GEMINI_3_PRO, systemPrompt, 0.3, 'llm-orchestration'), reportFn);
|
|
311
326
|
return stripFormatting(result);
|
|
312
327
|
}
|
|
313
328
|
};
|
|
@@ -327,7 +342,8 @@ export const geminiSummarizeTool = {
|
|
|
327
342
|
format: z.enum(["paragraph", "bullet-points", "outline"])
|
|
328
343
|
.optional()
|
|
329
344
|
.default("paragraph")
|
|
330
|
-
.describe("Output format - must be one of: paragraph, bullet-points, outline")
|
|
345
|
+
.describe("Output format - must be one of: paragraph, bullet-points, outline"),
|
|
346
|
+
files: z.array(z.string()).optional().describe("File paths to read as code context. Supports line ranges: 'src/foo.ts:100-200'. Model sees ACTUAL CODE.")
|
|
331
347
|
}),
|
|
332
348
|
execute: async (args, { log, reportProgress }) => {
|
|
333
349
|
const lengthGuides = {
|
|
@@ -349,8 +365,9 @@ Focus on:
|
|
|
349
365
|
- Conclusions and implications
|
|
350
366
|
${FORMAT_INSTRUCTION}`;
|
|
351
367
|
// Skip validation for internal summarization calls
|
|
368
|
+
const fileContext = args.files?.length ? `\n\nSOURCE CODE:\n${readFilesIntoContext(args.files)}` : "";
|
|
352
369
|
const reportFn = reportProgress ?? (async () => { });
|
|
353
|
-
const result = await withHeartbeat(() => callGemini(`Summarize this content:\n\n${args.content}
|
|
370
|
+
const result = await withHeartbeat(() => callGemini(`Summarize this content:\n\n${args.content}` + fileContext, GEMINI_MODELS.GEMINI_3_PRO, systemPrompt, 0.3, 'llm-orchestration'), reportFn);
|
|
354
371
|
return stripFormatting(result);
|
|
355
372
|
}
|
|
356
373
|
};
|
|
@@ -411,7 +428,8 @@ export const geminiJudgeTool = {
|
|
|
411
428
|
mode: z.enum(["synthesize", "evaluate", "rank", "resolve"])
|
|
412
429
|
.optional()
|
|
413
430
|
.default("synthesize")
|
|
414
|
-
.describe("Judge mode: synthesize (merge best), evaluate (score each), rank (order by quality), resolve (settle conflicts)")
|
|
431
|
+
.describe("Judge mode: synthesize (merge best), evaluate (score each), rank (order by quality), resolve (settle conflicts)"),
|
|
432
|
+
files: z.array(z.string()).optional().describe("File paths to read as code context. Supports line ranges: 'src/foo.ts:100-200'. Model sees ACTUAL CODE.")
|
|
415
433
|
}),
|
|
416
434
|
execute: async (args, { log, reportProgress }) => {
|
|
417
435
|
// Resolve perspectives from fallback params (AI clients sometimes use wrong param name)
|
|
@@ -475,8 +493,11 @@ ${FORMAT_INSTRUCTION}`;
|
|
|
475
493
|
const userPrompt = args.question
|
|
476
494
|
? `QUESTION: ${args.question}\n\nPERSPECTIVES TO JUDGE:\n${args.perspectives}`
|
|
477
495
|
: args.perspectives;
|
|
496
|
+
const fileContext = args.files?.length
|
|
497
|
+
? `\n\nSOURCE CODE:\n${readFilesIntoContext(args.files)}`
|
|
498
|
+
: "";
|
|
478
499
|
const reportFn = reportProgress ?? (async () => { });
|
|
479
|
-
const result = await withHeartbeat(() => callGemini(userPrompt, GEMINI_MODELS.GEMINI_3_PRO, systemPrompt, 0.3, 'llm-orchestration'), reportFn);
|
|
500
|
+
const result = await withHeartbeat(() => callGemini(userPrompt + fileContext, GEMINI_MODELS.GEMINI_3_PRO, systemPrompt, 0.3, 'llm-orchestration'), reportFn);
|
|
480
501
|
return stripFormatting(result);
|
|
481
502
|
}
|
|
482
503
|
};
|
|
@@ -7,6 +7,7 @@ import { config } from "dotenv";
|
|
|
7
7
|
import * as path from 'path';
|
|
8
8
|
import { fileURLToPath } from 'url';
|
|
9
9
|
import { getGrokApiKey, hasGrokApiKey } from "../utils/api-keys.js";
|
|
10
|
+
import { readFilesIntoContext } from "../utils/file-reader.js";
|
|
10
11
|
import { link } from "../utils/ansi-renderer.js";
|
|
11
12
|
import { stripFormatting } from "../utils/format-stripper.js";
|
|
12
13
|
import { FORMAT_INSTRUCTION } from "../utils/format-constants.js";
|
|
@@ -222,10 +223,11 @@ export const grokReasonEnhanced = {
|
|
|
222
223
|
context: z.string().optional(),
|
|
223
224
|
useHeavy: z.boolean().optional(),
|
|
224
225
|
enableLiveSearch: z.boolean().optional(),
|
|
225
|
-
maxSteps: z.number().optional()
|
|
226
|
+
maxSteps: z.number().optional(),
|
|
227
|
+
files: z.array(z.string()).optional().describe("File paths to read as code context. Supports line ranges: 'src/foo.ts:100-200'. Model sees ACTUAL CODE.")
|
|
226
228
|
}),
|
|
227
229
|
execute: async (args, { log }) => {
|
|
228
|
-
const { problem, approach = "first-principles", context, useHeavy = false, enableLiveSearch = false, maxSteps = 5 } = args;
|
|
230
|
+
const { problem, approach = "first-principles", context, useHeavy = false, enableLiveSearch = false, maxSteps = 5, files } = args;
|
|
229
231
|
const approachPrompts = {
|
|
230
232
|
analytical: "Break down the problem systematically and analyze each component",
|
|
231
233
|
creative: "Think outside the box and consider unconventional solutions",
|
|
@@ -233,6 +235,7 @@ export const grokReasonEnhanced = {
|
|
|
233
235
|
"first-principles": "Break down to fundamental truths and build up from there",
|
|
234
236
|
"multi-agent": "Consider multiple perspectives and synthesize them"
|
|
235
237
|
};
|
|
238
|
+
const fileContext = files?.length ? `\n\nSOURCE CODE:\n${readFilesIntoContext(files)}` : "";
|
|
236
239
|
const messages = [
|
|
237
240
|
{
|
|
238
241
|
role: "system",
|
|
@@ -245,7 +248,7 @@ ${FORMAT_INSTRUCTION}`
|
|
|
245
248
|
},
|
|
246
249
|
{
|
|
247
250
|
role: "user",
|
|
248
|
-
content: problem
|
|
251
|
+
content: problem + fileContext
|
|
249
252
|
}
|
|
250
253
|
];
|
|
251
254
|
const modelName = useHeavy ? 'Grok-4-Heavy' : 'Grok-4.1';
|
|
@@ -13,6 +13,7 @@ import { stripFormatting } from "../utils/format-stripper.js";
|
|
|
13
13
|
import { FORMAT_INSTRUCTION } from "../utils/format-constants.js";
|
|
14
14
|
import { tryOpenRouterGateway, isGatewayEnabled } from "../utils/openrouter-gateway.js";
|
|
15
15
|
import { withHeartbeat } from "../utils/streaming-helper.js";
|
|
16
|
+
import { readFilesIntoContext } from "../utils/file-reader.js";
|
|
16
17
|
// Note: renderOutput is applied centrally in server.ts safeAddTool() - no need to import here
|
|
17
18
|
const __filename = fileURLToPath(import.meta.url);
|
|
18
19
|
const __dirname = path.dirname(__filename);
|
|
@@ -130,6 +131,7 @@ export const grokReasonTool = {
|
|
|
130
131
|
.optional()
|
|
131
132
|
.describe("Reasoning approach (e.g., analytical, creative, systematic, first-principles)"),
|
|
132
133
|
context: z.string().optional().describe("Additional context for the problem"),
|
|
134
|
+
files: z.array(z.string()).optional().describe("File paths to read as code context. Supports line ranges: 'src/foo.ts:100-200'. Model sees ACTUAL CODE."),
|
|
133
135
|
useHeavy: z.boolean().optional().describe("Use expensive Grok 4 Heavy model ($3/$15) for complex tasks")
|
|
134
136
|
}),
|
|
135
137
|
execute: async (args, { log, reportProgress }) => {
|
|
@@ -140,6 +142,9 @@ export const grokReasonTool = {
|
|
|
140
142
|
systematic: "Follow a step-by-step logical process",
|
|
141
143
|
"first-principles": "Break down to fundamental truths and build up from there"
|
|
142
144
|
};
|
|
145
|
+
const fileContext = args.files?.length
|
|
146
|
+
? `\n\nSOURCE CODE:\n${readFilesIntoContext(args.files)}`
|
|
147
|
+
: "";
|
|
143
148
|
const messages = [
|
|
144
149
|
{
|
|
145
150
|
role: "system",
|
|
@@ -150,7 +155,7 @@ ${FORMAT_INSTRUCTION}`
|
|
|
150
155
|
},
|
|
151
156
|
{
|
|
152
157
|
role: "user",
|
|
153
|
-
content: problem
|
|
158
|
+
content: problem + fileContext
|
|
154
159
|
}
|
|
155
160
|
];
|
|
156
161
|
// Use GROK_4_1_FAST_REASONING by default (latest with enhanced reasoning!), GROK_4_HEAVY only if explicitly requested
|
|
@@ -175,6 +180,7 @@ export const grokCodeTool = {
|
|
|
175
180
|
.describe("Code task (e.g., analyze, optimize, debug, review, refactor)"),
|
|
176
181
|
code: z.string().describe("The actual source code to analyze (REQUIRED - put your code here)"),
|
|
177
182
|
language: z.string().optional().describe("Programming language (e.g., 'typescript', 'python')"),
|
|
183
|
+
files: z.array(z.string()).optional().describe("File paths to read as code context. Supports line ranges: 'src/foo.ts:100-200'. Model sees ACTUAL CODE."),
|
|
178
184
|
requirements: z.string().optional().describe("Specific requirements or focus areas")
|
|
179
185
|
}),
|
|
180
186
|
execute: async (args, { log, reportProgress }) => {
|
|
@@ -186,6 +192,9 @@ export const grokCodeTool = {
|
|
|
186
192
|
review: "Review this code for best practices and improvements",
|
|
187
193
|
refactor: "Refactor this code for better maintainability and clarity"
|
|
188
194
|
};
|
|
195
|
+
const fileContext = args.files?.length
|
|
196
|
+
? `\n\nSOURCE CODE:\n${readFilesIntoContext(args.files)}`
|
|
197
|
+
: "";
|
|
189
198
|
const messages = [
|
|
190
199
|
{
|
|
191
200
|
role: "system",
|
|
@@ -197,7 +206,7 @@ ${FORMAT_INSTRUCTION}`
|
|
|
197
206
|
},
|
|
198
207
|
{
|
|
199
208
|
role: "user",
|
|
200
|
-
content: `Code:\n\`\`\`${language || ''}\n${code}\n\`\`\``
|
|
209
|
+
content: `Code:\n\`\`\`${language || ''}\n${code}\n\`\`\`` + fileContext
|
|
201
210
|
}
|
|
202
211
|
];
|
|
203
212
|
log?.info(`Using Grok 4.1 Fast Non-Reasoning (2M context, tool-calling optimized, $0.20/$0.50)`);
|
|
@@ -218,6 +227,7 @@ export const grokDebugTool = {
|
|
|
218
227
|
issue: z.string().describe("Description of the issue or bug (REQUIRED - put your problem here)"),
|
|
219
228
|
code: z.string().optional().describe("Relevant code that has the issue"),
|
|
220
229
|
error: z.string().optional().describe("Error message or stack trace"),
|
|
230
|
+
files: z.array(z.string()).optional().describe("File paths to read as code context. Supports line ranges: 'src/foo.ts:100-200'. Model sees ACTUAL CODE."),
|
|
221
231
|
context: z.string().optional().describe("Additional context about the environment or conditions")
|
|
222
232
|
}),
|
|
223
233
|
execute: async (args, { log, reportProgress }) => {
|
|
@@ -232,6 +242,9 @@ export const grokDebugTool = {
|
|
|
232
242
|
if (context) {
|
|
233
243
|
prompt += `\nContext: ${context}\n`;
|
|
234
244
|
}
|
|
245
|
+
const fileContext = args.files?.length
|
|
246
|
+
? `\n\nSOURCE CODE:\n${readFilesIntoContext(args.files)}`
|
|
247
|
+
: "";
|
|
235
248
|
const messages = [
|
|
236
249
|
{
|
|
237
250
|
role: "system",
|
|
@@ -245,7 +258,7 @@ ${FORMAT_INSTRUCTION}`
|
|
|
245
258
|
},
|
|
246
259
|
{
|
|
247
260
|
role: "user",
|
|
248
|
-
content: prompt
|
|
261
|
+
content: prompt + fileContext
|
|
249
262
|
}
|
|
250
263
|
];
|
|
251
264
|
log?.info(`Using Grok 4.1 Fast Non-Reasoning for debugging (tool-calling optimized, $0.20/$0.50)`);
|
|
@@ -267,10 +280,14 @@ export const grokArchitectTool = {
|
|
|
267
280
|
constraints: z.string().optional().describe("Technical or business constraints to consider"),
|
|
268
281
|
scale: z.string()
|
|
269
282
|
.optional()
|
|
270
|
-
.describe("Expected scale (e.g., small, medium, large, enterprise)")
|
|
283
|
+
.describe("Expected scale (e.g., small, medium, large, enterprise)"),
|
|
284
|
+
files: z.array(z.string()).optional().describe("File paths to read as code context. Supports line ranges: 'src/foo.ts:100-200'. Model sees ACTUAL CODE."),
|
|
271
285
|
}),
|
|
272
286
|
execute: async (args, { log, reportProgress }) => {
|
|
273
287
|
const { requirements, constraints, scale } = args;
|
|
288
|
+
const fileContext = args.files?.length
|
|
289
|
+
? `\n\nSOURCE CODE:\n${readFilesIntoContext(args.files)}`
|
|
290
|
+
: "";
|
|
274
291
|
const messages = [
|
|
275
292
|
{
|
|
276
293
|
role: "system",
|
|
@@ -282,7 +299,7 @@ ${FORMAT_INSTRUCTION}`
|
|
|
282
299
|
},
|
|
283
300
|
{
|
|
284
301
|
role: "user",
|
|
285
|
-
content: requirements
|
|
302
|
+
content: requirements + fileContext
|
|
286
303
|
}
|
|
287
304
|
];
|
|
288
305
|
log?.info(`Using Grok 4.1 Fast Reasoning for architecture (latest model, $0.20/$0.50)`);
|
|
@@ -303,10 +320,14 @@ export const grokBrainstormTool = {
|
|
|
303
320
|
topic: z.string().describe("The topic to brainstorm about (REQUIRED - put your idea/topic here)"),
|
|
304
321
|
constraints: z.string().optional().describe("Any constraints or requirements to consider"),
|
|
305
322
|
numIdeas: z.number().optional().describe("Number of radical rebuilds to generate (default: 5)"),
|
|
306
|
-
forceHeavy: z.boolean().optional().describe("Use expensive Grok 4 Heavy model ($3/$15) for deeper creativity")
|
|
323
|
+
forceHeavy: z.boolean().optional().describe("Use expensive Grok 4 Heavy model ($3/$15) for deeper creativity"),
|
|
324
|
+
files: z.array(z.string()).optional().describe("File paths to read as code context. Supports line ranges: 'src/foo.ts:100-200'. Model sees ACTUAL CODE."),
|
|
307
325
|
}),
|
|
308
326
|
execute: async (args, { log, reportProgress }) => {
|
|
309
327
|
const { topic, constraints, numIdeas = 5, forceHeavy = false } = args;
|
|
328
|
+
const fileContext = args.files?.length
|
|
329
|
+
? `\n\nSOURCE CODE:\n${readFilesIntoContext(args.files)}`
|
|
330
|
+
: "";
|
|
310
331
|
const messages = [
|
|
311
332
|
{
|
|
312
333
|
role: "system",
|
|
@@ -332,7 +353,7 @@ ${FORMAT_INSTRUCTION}`
|
|
|
332
353
|
},
|
|
333
354
|
{
|
|
334
355
|
role: "user",
|
|
335
|
-
content: topic
|
|
356
|
+
content: topic + fileContext
|
|
336
357
|
}
|
|
337
358
|
];
|
|
338
359
|
const model = forceHeavy ? GrokModel.GROK_4_HEAVY : GrokModel.GROK_4_1_FAST_REASONING;
|
|
@@ -13,6 +13,7 @@ import { OPENAI_MODELS } from "../config/model-constants.js";
|
|
|
13
13
|
import { FORMAT_INSTRUCTION } from "../utils/format-constants.js";
|
|
14
14
|
import { stripFormatting } from "../utils/format-stripper.js";
|
|
15
15
|
import { withHeartbeat } from "../utils/streaming-helper.js";
|
|
16
|
+
import { readFilesIntoContext } from "../utils/file-reader.js";
|
|
16
17
|
const __filename = fileURLToPath(import.meta.url);
|
|
17
18
|
const __dirname = path.dirname(__filename);
|
|
18
19
|
config({ path: path.resolve(__dirname, '../../../.env') });
|
|
@@ -342,6 +343,7 @@ export const openaiGpt5ReasonTool = {
|
|
|
342
343
|
parameters: z.object({
|
|
343
344
|
query: z.string().describe("The question or problem to reason about (REQUIRED - put your question here)"),
|
|
344
345
|
context: z.string().optional().describe("Additional context for the reasoning task"),
|
|
346
|
+
files: z.array(z.string()).optional().describe("File paths to read as code context. Supports line ranges: 'src/foo.ts:100-200'. Model sees ACTUAL CODE."),
|
|
345
347
|
mode: z.string()
|
|
346
348
|
.optional()
|
|
347
349
|
.default("analytical")
|
|
@@ -354,6 +356,9 @@ export const openaiGpt5ReasonTool = {
|
|
|
354
356
|
logical: "Use formal logic and systematic deduction",
|
|
355
357
|
analytical: "Break down complex problems into components"
|
|
356
358
|
};
|
|
359
|
+
const fileContext = args.files?.length
|
|
360
|
+
? `\n\nSOURCE CODE:\n${readFilesIntoContext(args.files)}`
|
|
361
|
+
: "";
|
|
357
362
|
const messages = [
|
|
358
363
|
{
|
|
359
364
|
role: "system",
|
|
@@ -365,7 +370,7 @@ ${FORMAT_INSTRUCTION}`
|
|
|
365
370
|
},
|
|
366
371
|
{
|
|
367
372
|
role: "user",
|
|
368
|
-
content: args.query
|
|
373
|
+
content: args.query + fileContext
|
|
369
374
|
}
|
|
370
375
|
];
|
|
371
376
|
// Use heartbeat to prevent MCP timeout during reasoning
|
|
@@ -383,6 +388,7 @@ export const openAIBrainstormTool = {
|
|
|
383
388
|
parameters: z.object({
|
|
384
389
|
problem: z.string().describe("The engineering problem or design tradeoff to brainstorm about (REQUIRED)"),
|
|
385
390
|
constraints: z.string().optional().describe("Technical constraints: language, framework, performance requirements, team size"),
|
|
391
|
+
files: z.array(z.string()).optional().describe("File paths to read as code context. Supports line ranges: 'src/foo.ts:100-200'. Model sees ACTUAL CODE."),
|
|
386
392
|
quantity: z.number().optional().describe("Number of approaches to generate (default: 5)"),
|
|
387
393
|
model: z.enum(["gpt-5.4", "gpt-5.4-mini", "gpt-5.4-pro"])
|
|
388
394
|
.optional()
|
|
@@ -396,6 +402,9 @@ export const openAIBrainstormTool = {
|
|
|
396
402
|
const { problem, constraints, quantity = 5, model = OPENAI_MODELS.DEFAULT, reasoning_effort = "medium", max_tokens = 6000 } = args;
|
|
397
403
|
// GPT-5.4 reasoning tokens eat into max_output_tokens — enforce minimum
|
|
398
404
|
const effectiveMaxTokens = Math.max(max_tokens, 4000);
|
|
405
|
+
const fileContext = args.files?.length
|
|
406
|
+
? `\n\nSOURCE CODE:\n${readFilesIntoContext(args.files)}`
|
|
407
|
+
: "";
|
|
399
408
|
const messages = [
|
|
400
409
|
{
|
|
401
410
|
role: "system",
|
|
@@ -430,7 +439,7 @@ ${FORMAT_INSTRUCTION}`
|
|
|
430
439
|
},
|
|
431
440
|
{
|
|
432
441
|
role: "user",
|
|
433
|
-
content: problem
|
|
442
|
+
content: problem + fileContext
|
|
434
443
|
}
|
|
435
444
|
];
|
|
436
445
|
const modelEnum = model;
|
|
@@ -447,6 +456,7 @@ export const openaiCodeReviewTool = {
|
|
|
447
456
|
parameters: z.object({
|
|
448
457
|
code: z.string().describe("The actual source code to review (REQUIRED - put your code here)"),
|
|
449
458
|
language: z.string().optional().describe("Programming language (e.g., 'typescript', 'python')"),
|
|
459
|
+
files: z.array(z.string()).optional().describe("File paths to read as code context. Supports line ranges: 'src/foo.ts:100-200'. Model sees ACTUAL CODE."),
|
|
450
460
|
focusAreas: z.array(z.enum(["security", "performance", "readability", "bugs", "best-practices"]))
|
|
451
461
|
.optional()
|
|
452
462
|
.describe("Focus areas - array of: security, performance, readability, bugs, best-practices")
|
|
@@ -455,6 +465,9 @@ export const openaiCodeReviewTool = {
|
|
|
455
465
|
const focusText = args.focusAreas
|
|
456
466
|
? `Focus especially on: ${args.focusAreas.join(', ')}`
|
|
457
467
|
: "Review all aspects: security, performance, readability, bugs, and best practices";
|
|
468
|
+
const fileContext = args.files?.length
|
|
469
|
+
? `\n\nSOURCE CODE:\n${readFilesIntoContext(args.files)}`
|
|
470
|
+
: "";
|
|
458
471
|
const messages = [
|
|
459
472
|
{
|
|
460
473
|
role: "system",
|
|
@@ -467,7 +480,7 @@ ${FORMAT_INSTRUCTION}`
|
|
|
467
480
|
},
|
|
468
481
|
{
|
|
469
482
|
role: "user",
|
|
470
|
-
content: `Review this code:\n\`\`\`${args.language || ''}\n${args.code}\n\`\`\``
|
|
483
|
+
content: `Review this code:\n\`\`\`${args.language || ''}\n${args.code}\n\`\`\`` + fileContext
|
|
471
484
|
}
|
|
472
485
|
];
|
|
473
486
|
// Use heartbeat to prevent MCP timeout
|
|
@@ -491,7 +504,8 @@ export const openaiExplainTool = {
|
|
|
491
504
|
style: z.enum(["technical", "simple", "analogy", "visual"])
|
|
492
505
|
.optional()
|
|
493
506
|
.default("simple")
|
|
494
|
-
.describe("Explanation style - must be one of: technical, simple, analogy, visual")
|
|
507
|
+
.describe("Explanation style - must be one of: technical, simple, analogy, visual"),
|
|
508
|
+
files: z.array(z.string()).optional().describe("File paths to read as code context. Supports line ranges: 'src/foo.ts:100-200'. Model sees ACTUAL CODE."),
|
|
495
509
|
}),
|
|
496
510
|
execute: async (args, { log }) => {
|
|
497
511
|
const levelPrompts = {
|
|
@@ -505,6 +519,9 @@ export const openaiExplainTool = {
|
|
|
505
519
|
analogy: "Use analogies and metaphors",
|
|
506
520
|
visual: "Describe with visual concepts and diagrams"
|
|
507
521
|
};
|
|
522
|
+
const fileContext = args.files?.length
|
|
523
|
+
? `\n\nSOURCE CODE:\n${readFilesIntoContext(args.files)}`
|
|
524
|
+
: "";
|
|
508
525
|
const messages = [
|
|
509
526
|
{
|
|
510
527
|
role: "system",
|
|
@@ -516,7 +533,7 @@ ${FORMAT_INSTRUCTION}`
|
|
|
516
533
|
},
|
|
517
534
|
{
|
|
518
535
|
role: "user",
|
|
519
|
-
content: `Explain: ${args.topic}`
|
|
536
|
+
content: `Explain: ${args.topic}` + fileContext
|
|
520
537
|
}
|
|
521
538
|
];
|
|
522
539
|
return await callOpenAI(messages, OPENAI_MODELS.DEFAULT, 0.7, 2500, "low");
|