sapper-iq 1.1.11 → 1.1.13

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 (2) hide show
  1. package/package.json +1 -1
  2. package/sapper.mjs +172 -55
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "sapper-iq",
3
- "version": "1.1.11",
3
+ "version": "1.1.13",
4
4
  "description": "AI-powered development assistant that executes commands and builds projects",
5
5
  "main": "sapper.mjs",
6
6
  "bin": {
package/sapper.mjs CHANGED
@@ -99,6 +99,104 @@ const IGNORE_DIRS = new Set([
99
99
  '.idea', '.vscode', 'vendor', 'target', '.gradle'
100
100
  ]);
101
101
 
102
+ // File extensions to include when scanning codebase
103
+ const CODE_EXTENSIONS = new Set([
104
+ '.js', '.ts', '.jsx', '.tsx', '.py', '.java', '.go', '.rs', '.rb', '.php',
105
+ '.c', '.cpp', '.h', '.hpp', '.cs', '.swift', '.kt', '.scala', '.vue', '.svelte',
106
+ '.css', '.scss', '.sass', '.less', '.html', '.htm', '.json', '.yaml', '.yml',
107
+ '.toml', '.xml', '.md', '.txt', '.sh', '.bash', '.zsh', '.sql', '.graphql',
108
+ '.env.example', '.gitignore', '.dockerignore', 'Dockerfile', 'Makefile',
109
+ '.prisma', '.proto'
110
+ ]);
111
+
112
+ // Max file size to include (skip large files like bundled/minified)
113
+ const MAX_FILE_SIZE = 100000; // 100KB per file
114
+ const MAX_TOTAL_SCAN_SIZE = 1000000; // 1000KB total scan limit
115
+
116
+ // Scan entire codebase and return summary
117
+ function scanCodebase(dir = '.', depth = 0, maxDepth = 5) {
118
+ if (depth > maxDepth) return { files: [], totalSize: 0 };
119
+
120
+ let files = [];
121
+ let totalSize = 0;
122
+
123
+ try {
124
+ const entries = fs.readdirSync(dir, { withFileTypes: true });
125
+
126
+ for (const entry of entries) {
127
+ const fullPath = dir === '.' ? entry.name : `${dir}/${entry.name}`;
128
+
129
+ // Skip ignored directories
130
+ if (entry.isDirectory()) {
131
+ if (IGNORE_DIRS.has(entry.name) || entry.name.startsWith('.')) continue;
132
+ const subResult = scanCodebase(fullPath, depth + 1, maxDepth);
133
+ files = files.concat(subResult.files);
134
+ totalSize += subResult.totalSize;
135
+ } else {
136
+ // Check if file should be included
137
+ const ext = entry.name.includes('.') ? '.' + entry.name.split('.').pop() : entry.name;
138
+ const isCodeFile = CODE_EXTENSIONS.has(ext.toLowerCase()) || CODE_EXTENSIONS.has(entry.name);
139
+
140
+ if (!isCodeFile) continue;
141
+
142
+ try {
143
+ const stats = fs.statSync(fullPath);
144
+ if (stats.size > MAX_FILE_SIZE) {
145
+ files.push({ path: fullPath, size: stats.size, skipped: true, reason: 'too large' });
146
+ continue;
147
+ }
148
+ if (totalSize + stats.size > MAX_TOTAL_SCAN_SIZE) {
149
+ files.push({ path: fullPath, size: stats.size, skipped: true, reason: 'total limit reached' });
150
+ continue;
151
+ }
152
+
153
+ const content = fs.readFileSync(fullPath, 'utf8');
154
+ files.push({ path: fullPath, size: stats.size, content });
155
+ totalSize += stats.size;
156
+ } catch (e) {
157
+ files.push({ path: fullPath, skipped: true, reason: e.message });
158
+ }
159
+ }
160
+ }
161
+ } catch (e) {
162
+ // Directory not readable
163
+ }
164
+
165
+ return { files, totalSize };
166
+ }
167
+
168
+ // Format scan results for AI context
169
+ function formatScanResults(scanResult) {
170
+ let output = `\n══════════════════════════════════════\n`;
171
+ output += `📁 CODEBASE SCAN (${scanResult.files.length} files, ~${Math.round(scanResult.totalSize/1024)}KB)\n`;
172
+ output += `══════════════════════════════════════\n\n`;
173
+
174
+ // First list all files
175
+ output += `FILE TREE:\n`;
176
+ for (const file of scanResult.files) {
177
+ if (file.skipped) {
178
+ output += ` ⏭️ ${file.path} (skipped: ${file.reason})\n`;
179
+ } else {
180
+ output += ` 📄 ${file.path} (${Math.round(file.size/1024)}KB)\n`;
181
+ }
182
+ }
183
+
184
+ output += `\n══════════════════════════════════════\n`;
185
+ output += `FILE CONTENTS:\n`;
186
+ output += `══════════════════════════════════════\n\n`;
187
+
188
+ // Then include contents
189
+ for (const file of scanResult.files) {
190
+ if (file.skipped) continue;
191
+ output += `┌─── ${file.path} ───\n`;
192
+ output += file.content;
193
+ if (!file.content.endsWith('\n')) output += '\n';
194
+ output += `└─── END ${file.path} ───\n\n`;
195
+ }
196
+
197
+ return output;
198
+ }
199
+
102
200
  const tools = {
103
201
  read: (path) => {
104
202
  try { return fs.readFileSync(path.trim(), 'utf8'); }
@@ -252,63 +350,51 @@ async function runSapper() {
252
350
  if (messages.length === 0) {
253
351
  messages = [{
254
352
  role: 'system',
255
- content: `You are Sapper, a senior engineer.
256
-
257
- CRITICAL: You are working in the CURRENT DIRECTORY. Always use relative paths!
258
- - Use . or ./ for current directory
259
- - NEVER use / (that's the root directory)
260
- - Use relative paths like ./file.js or subfolder/file.js
261
-
262
- STRATEGY FOR FILE READING:
263
- 1. Start with [TOOL:LIST].[/TOOL] to see what exists
264
- 2. READ FILES BASED ON TASK:
265
- - Quick overview: Read 2-8 key files (README, package.json, main entry)
266
- - Deep analysis: Read ALL relevant files (entire src/ folder, all components)
267
- - User asks "read all": Read ALL files they mention
268
- 3. Use format: [TOOL:TYPE]path]content[/TOOL]
269
- 4. MANDATORY: You MUST finish reading ALL requested files before providing ANY analysis or summary. Do NOT stop to explain - keep reading until done!
270
-
271
- READING GUIDELINES:
272
- - If user says "analyze src folder" Read ALL files in src/
273
- - If user says "read everything" → List directory, then read all files
274
- - If < 20 files total: Read them all
275
- - If > 20 files: Ask user which area to focus on
276
-
277
- TOOL FORMAT (CRITICAL - FOLLOW EXACTLY):
278
- ✅ CORRECT: [TOOL:LIST].[/TOOL]
279
- ✅ CORRECT: [TOOL:READ]./file.js[/TOOL]
280
- CORRECT: [TOOL:SEARCH]functionName[/TOOL]
281
- CORRECT: [TOOL:WRITE]./file.js]full content here[/TOOL]
282
- ✅ CORRECT: [TOOL:PATCH]./file.js]old code|||new code[/TOOL]
283
- ❌ WRONG: [TOOL:LIST].[/] - missing TOOL at end!
284
-
285
- AVAILABLE TOOLS:
286
- - LIST: List directory contents
287
- - READ: Read file contents
288
- - SEARCH: Find text/code across all files (grep-like, returns file:line:match)
289
- - WRITE: Create or overwrite entire file (requires confirmation)
290
- - PATCH: Make small edits to existing file (requires confirmation)
291
- - MKDIR: Create directory
292
- - SHELL: Run terminal command (requires confirmation)
293
-
294
- SMART WORKFLOW:
295
- 1. For unknown codebases: [TOOL:SEARCH]main|index|app[/TOOL] to find entry points
296
- 2. To find where something is defined: [TOOL:SEARCH]function myFunc[/TOOL]
297
- 3. SEARCH returns file paths + line numbers - then READ specific files
298
-
299
- PATCH vs WRITE:
300
- - Use PATCH for small changes (1-10 lines): [TOOL:PATCH]path]old|||new[/TOOL]
301
- - Use WRITE only for new files or complete rewrites
353
+ content: `You are Sapper, a coding assistant that ONLY does what the user asks.
354
+
355
+ GOLDEN RULE: Do EXACTLY what the user asks. Nothing more, nothing less.
356
+ - NEVER add features the user didn't ask for.
357
+ - ALWAYS confirm with the user before writing/patching files or running shell commands.
358
+ - KEEP responses concise and to the point.
359
+ TOOLS (use these to interact with files):
360
+
361
+ [TOOL:LIST]path[/TOOL]
362
+ List files in a directory
363
+ Example: [TOOL:LIST].[/TOOL]
364
+
365
+ [TOOL:READ]path[/TOOL]
366
+ Read a file's contents
367
+ Example: [TOOL:READ]./package.json[/TOOL]
368
+
369
+ [TOOL:WRITE]path]content[/TOOL]
370
+ Create or overwrite a file (needs user confirmation)
371
+ Example: [TOOL:WRITE]./index.js]console.log("hello")[/TOOL]
372
+
373
+ [TOOL:PATCH]path]old_text|||new_text[/TOOL]
374
+ → Replace specific text in a file (needs user confirmation)
375
+ Example: [TOOL:PATCH]./app.js]old code|||new code[/TOOL]
376
+
377
+ [TOOL:SEARCH]pattern[/TOOL]
378
+ Search for text across all files
379
+ Example: [TOOL:SEARCH]function login[/TOOL]
380
+
381
+ [TOOL:SHELL]command[/TOOL]
382
+ → Run a terminal command (needs user confirmation)
383
+ Example: [TOOL:SHELL]npm install express[/TOOL]
384
+
385
+ PATH RULES:
386
+ - Always use relative paths: ./file.js, ./src/app.js
387
+ - NEVER use absolute paths like /file.js
388
+ - Use . for current directory
302
389
 
303
390
  WORKFLOW:
304
- 1. LIST or SEARCH 2. READ relevant files → 3. ANALYZE and RESPOND
305
-
306
- IMPORTANT RULES:
307
- - Be concise. Do not generate repetitive lists or filler text.
308
- - If a list exceeds 10 items, summarize instead of listing everything.
309
- - Never repeat the same content multiple times.
310
- - Stop writing when you've made your point.
311
- - BATCH READING: When asked to read multiple files, call ALL [TOOL:READ] commands in ONE response. Do NOT stop to analyze between files.`
391
+ 1. Understand exactly what user wants
392
+ 2. Use LIST to see existing files if needed
393
+ 3. Use READ to check existing code if needed
394
+ 4. Use WRITE/PATCH to make changes
395
+ 5. Be concise in explanations
396
+
397
+ CRITICAL: Stay focused. If user asks for X, deliver X only.`
312
398
  }];
313
399
  }
314
400
 
@@ -379,6 +465,7 @@ Do NOT just display content. Actually WRITE files using the tool.`
379
465
  // Handle help command
380
466
  if (input.toLowerCase() === '/help') {
381
467
  console.log(chalk.cyan('\n📚 SAPPER COMMANDS:'));
468
+ console.log(chalk.white(' /scan') + chalk.gray(' - Scan entire codebase and add to context'));
382
469
  console.log(chalk.white(' /reset, /clear') + chalk.gray(' - Clear all context and start fresh'));
383
470
  console.log(chalk.white(' /prune') + chalk.gray(' - Remove old messages, keep last 4'));
384
471
  console.log(chalk.white(' /context') + chalk.gray(' - Show current context size'));
@@ -408,6 +495,36 @@ Do NOT just display content. Actually WRITE files using the tool.`
408
495
  continue;
409
496
  }
410
497
 
498
+ // Handle codebase scan command
499
+ if (input.toLowerCase() === '/scan') {
500
+ console.log(chalk.cyan('\n🔍 Scanning codebase...'));
501
+ const scanResult = scanCodebase('.');
502
+
503
+ if (scanResult.files.length === 0) {
504
+ console.log(chalk.yellow('No code files found in current directory.'));
505
+ continue;
506
+ }
507
+
508
+ const formattedScan = formatScanResults(scanResult);
509
+ const includedCount = scanResult.files.filter(f => !f.skipped).length;
510
+ const skippedCount = scanResult.files.filter(f => f.skipped).length;
511
+
512
+ console.log(chalk.green(`✅ Scanned ${includedCount} files (~${Math.round(scanResult.totalSize/1024)}KB)`));
513
+ if (skippedCount > 0) {
514
+ console.log(chalk.yellow(`⏭️ Skipped ${skippedCount} files (too large or limit reached)`));
515
+ }
516
+
517
+ // Add scan to context
518
+ messages.push({
519
+ role: 'user',
520
+ content: `I've scanned the entire codebase. Here are all the files:\n${formattedScan}\n\nYou now have the full codebase context. Use this information to help me.`
521
+ });
522
+
523
+ fs.writeFileSync(CONTEXT_FILE, JSON.stringify(messages));
524
+ console.log(chalk.gray('📝 Codebase added to context. AI now has full picture.\n'));
525
+ continue;
526
+ }
527
+
411
528
  messages.push({ role: 'user', content: input });
412
529
 
413
530
  let toolRounds = 0; // Prevent infinite loops