sapper-iq 1.1.11 → 1.1.12

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 +129 -0
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "sapper-iq",
3
- "version": "1.1.11",
3
+ "version": "1.1.12",
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'); }
@@ -379,6 +477,7 @@ Do NOT just display content. Actually WRITE files using the tool.`
379
477
  // Handle help command
380
478
  if (input.toLowerCase() === '/help') {
381
479
  console.log(chalk.cyan('\n📚 SAPPER COMMANDS:'));
480
+ console.log(chalk.white(' /scan') + chalk.gray(' - Scan entire codebase and add to context'));
382
481
  console.log(chalk.white(' /reset, /clear') + chalk.gray(' - Clear all context and start fresh'));
383
482
  console.log(chalk.white(' /prune') + chalk.gray(' - Remove old messages, keep last 4'));
384
483
  console.log(chalk.white(' /context') + chalk.gray(' - Show current context size'));
@@ -408,6 +507,36 @@ Do NOT just display content. Actually WRITE files using the tool.`
408
507
  continue;
409
508
  }
410
509
 
510
+ // Handle codebase scan command
511
+ if (input.toLowerCase() === '/scan') {
512
+ console.log(chalk.cyan('\n🔍 Scanning codebase...'));
513
+ const scanResult = scanCodebase('.');
514
+
515
+ if (scanResult.files.length === 0) {
516
+ console.log(chalk.yellow('No code files found in current directory.'));
517
+ continue;
518
+ }
519
+
520
+ const formattedScan = formatScanResults(scanResult);
521
+ const includedCount = scanResult.files.filter(f => !f.skipped).length;
522
+ const skippedCount = scanResult.files.filter(f => f.skipped).length;
523
+
524
+ console.log(chalk.green(`✅ Scanned ${includedCount} files (~${Math.round(scanResult.totalSize/1024)}KB)`));
525
+ if (skippedCount > 0) {
526
+ console.log(chalk.yellow(`⏭️ Skipped ${skippedCount} files (too large or limit reached)`));
527
+ }
528
+
529
+ // Add scan to context
530
+ messages.push({
531
+ role: 'user',
532
+ 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.`
533
+ });
534
+
535
+ fs.writeFileSync(CONTEXT_FILE, JSON.stringify(messages));
536
+ console.log(chalk.gray('📝 Codebase added to context. AI now has full picture.\n'));
537
+ continue;
538
+ }
539
+
411
540
  messages.push({ role: 'user', content: input });
412
541
 
413
542
  let toolRounds = 0; // Prevent infinite loops