Skip to content

Commit ce4ef4a

Browse files
committed
fix: Use smart file selection to stay under 200K token limit
- Changed from including all files to keyword-based selection - Detects relevant files based on task keywords - Falls back to key architecture files if no match - Keeps context under 200K tokens to avoid API errors - Includes full file list for reference Previous approach sent 210K tokens, exceeding Claude's limit. New approach sends only relevant files, typically 5-20K tokens.
1 parent ec1ab78 commit ce4ef4a

1 file changed

Lines changed: 66 additions & 32 deletions

File tree

.github/agent/agent.mjs

Lines changed: 66 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -170,47 +170,78 @@ try {
170170
console.log("[AGENT] No .cursorrules file found");
171171
}
172172

173-
// Read all relevant source files for complete context
174-
// This ensures the AI has accurate file content for generating diffs
175-
let codebaseContent = "";
173+
// Smart file selection: Include only the most relevant files based on task
174+
// This keeps us under the 200K token limit while providing accurate context
175+
let relevantFiles = "";
176+
const taskLower = task.toLowerCase();
176177
const fileList = files.split("\n").filter(f => f.trim());
177178

178-
// Define which files to include (exclude large binaries, dependencies, etc.)
179-
const includedExtensions = ['.js', '.mjs', '.json', '.txt', '.html', '.css', '.md', '.yml', '.yaml'];
180-
const excludedPaths = ['node_modules/', 'package-lock.json', '.git/', 'coverage/', 'dist/', 'build/'];
179+
// File patterns to check based on task keywords
180+
const filePatterns = [
181+
{ keywords: ["help", "admin", "command"], files: ["templates/help/helpTextAdmin.txt", "templates/help/helpText.txt"] },
182+
{ keywords: ["config", "setting"], files: ["config/config.json", "src/config-handler.js"] },
183+
{ keywords: ["slack", "message", "notification"], files: ["src/slack-handler.js", "src/notification-handler.js"] },
184+
{ keywords: ["discord"], files: ["src/discord-handler.js"] },
185+
{ keywords: ["sonos", "speaker", "playback", "volume"], files: ["src/sonos-handler.js"] },
186+
{ keywords: ["web", "admin panel", "webui", "route", "endpoint"], files: ["src/webserver.js", "public/admin.html"] },
187+
{ keywords: ["queue", "track"], files: ["src/queue-handler.js"] },
188+
{ keywords: ["vote", "voting"], files: ["src/voting-handler.js"] },
189+
{ keywords: ["ai", "openai", "gemini"], files: ["src/ai-handler.js"] },
190+
{ keywords: ["test"], files: ["test/unit/"] },
191+
];
181192

182-
console.log("[AGENT] Reading codebase files...");
193+
console.log("[AGENT] Identifying relevant files based on task...");
183194
let filesIncluded = 0;
184195

185-
for (const filePath of fileList) {
186-
// Skip if file is in excluded paths
187-
if (excludedPaths.some(excluded => filePath.includes(excluded))) {
188-
continue;
189-
}
190-
191-
// Skip if file extension is not in allowed list
192-
if (!includedExtensions.some(ext => filePath.endsWith(ext))) {
193-
continue;
196+
// First, include files that match task keywords
197+
for (const pattern of filePatterns) {
198+
if (pattern.keywords.some(keyword => taskLower.includes(keyword))) {
199+
for (const filePath of pattern.files) {
200+
try {
201+
// Handle directory patterns
202+
if (filePath.endsWith('/')) {
203+
const dirFiles = fileList.filter(f => f.startsWith(filePath));
204+
for (const dirFile of dirFiles.slice(0, 5)) { // Limit to 5 files per directory
205+
const content = fs.readFileSync(dirFile, "utf8");
206+
relevantFiles += `\n\n=== ${dirFile} ===\n${content}`;
207+
filesIncluded++;
208+
console.log(`[AGENT] Including ${dirFile}`);
209+
}
210+
} else {
211+
const content = fs.readFileSync(filePath, "utf8");
212+
relevantFiles += `\n\n=== ${filePath} ===\n${content}`;
213+
filesIncluded++;
214+
console.log(`[AGENT] Including ${filePath}`);
215+
}
216+
} catch (e) {
217+
// File doesn't exist, skip it
218+
}
219+
}
194220
}
221+
}
195222

196-
try {
197-
const stats = fs.statSync(filePath);
198-
// Skip files larger than 100KB to avoid token limits
199-
if (stats.size > 100000) {
200-
console.log(`[AGENT] Skipping large file: ${filePath} (${stats.size} bytes)`);
201-
continue;
223+
// If no specific files matched, include a summary of available files
224+
if (filesIncluded === 0) {
225+
console.log("[AGENT] No keyword match - including key architecture files");
226+
const keyFiles = [
227+
"src/webserver.js",
228+
"src/slack-handler.js",
229+
"src/discord-handler.js",
230+
"templates/help/helpTextAdmin.txt"
231+
];
232+
233+
for (const filePath of keyFiles) {
234+
try {
235+
const content = fs.readFileSync(filePath, "utf8");
236+
relevantFiles += `\n\n=== ${filePath} ===\n${content}`;
237+
filesIncluded++;
238+
} catch (e) {
239+
// Skip if doesn't exist
202240
}
203-
204-
const content = fs.readFileSync(filePath, "utf8");
205-
codebaseContent += `\n\n=== ${filePath} ===\n${content}`;
206-
filesIncluded++;
207-
} catch (e) {
208-
// File read error, skip it
209-
console.log(`[AGENT] Could not read ${filePath}: ${e.message}`);
210241
}
211242
}
212243

213-
console.log(`[AGENT] Included ${filesIncluded} files for AI context`);
244+
console.log(`[AGENT] Included ${filesIncluded} relevant files for AI context`);
214245

215246
// Build specialized prompt for SlackONOS
216247
const prompt = `You are an autonomous coding agent for SlackONOS, a democratic music bot for Discord and Slack that controls Sonos speakers.
@@ -233,8 +264,11 @@ ${cursorRules}
233264
Recent Commits:
234265
${recentCommits}
235266
236-
COMPLETE CODEBASE CONTENT (use these exact contents for accurate diffs):
237-
${codebaseContent}
267+
Repository Files (for reference):
268+
${files}
269+
270+
RELEVANT FILE CONTENTS (use these exact contents for accurate diffs):
271+
${relevantFiles}
238272
239273
TASK FROM ADMIN (${requester}):
240274
${task}

0 commit comments

Comments
 (0)