sapper-iq 1.0.26 → 1.1.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/package.json +1 -1
- package/sapper.mjs +79 -2
package/package.json
CHANGED
package/sapper.mjs
CHANGED
|
@@ -76,6 +76,28 @@ const tools = {
|
|
|
76
76
|
try { return fs.readFileSync(path.trim(), 'utf8'); }
|
|
77
77
|
catch (error) { return `Error reading file: ${error.message}`; }
|
|
78
78
|
},
|
|
79
|
+
patch: async (path, oldText, newText) => {
|
|
80
|
+
const trimmedPath = path.trim();
|
|
81
|
+
try {
|
|
82
|
+
const content = fs.readFileSync(trimmedPath, 'utf8');
|
|
83
|
+
if (!content.includes(oldText)) {
|
|
84
|
+
return `Error: Could not find the text to replace in ${trimmedPath}. Make sure oldText matches exactly (including whitespace).`;
|
|
85
|
+
}
|
|
86
|
+
const newContent = content.replace(oldText, newText);
|
|
87
|
+
|
|
88
|
+
// Show diff preview
|
|
89
|
+
console.log(chalk.yellow.bold(`\n[PATCH] ${trimmedPath}`));
|
|
90
|
+
console.log(chalk.red('- ' + oldText.split('\n').join('\n- ')));
|
|
91
|
+
console.log(chalk.green('+ ' + newText.split('\n').join('\n+ ')));
|
|
92
|
+
|
|
93
|
+
const confirm = await safeQuestion(chalk.yellow('Apply this patch? (y/n): '));
|
|
94
|
+
if (confirm.toLowerCase() === 'y') {
|
|
95
|
+
fs.writeFileSync(trimmedPath, newContent);
|
|
96
|
+
return `Successfully patched ${trimmedPath}`;
|
|
97
|
+
}
|
|
98
|
+
return 'Patch rejected by user.';
|
|
99
|
+
} catch (error) { return `Error patching file: ${error.message}`; }
|
|
100
|
+
},
|
|
79
101
|
write: async (path, content) => {
|
|
80
102
|
const trimmedPath = path.trim();
|
|
81
103
|
console.log(chalk.yellow.bold(`\n[WRITE] Sapper wants to write to: `) + chalk.white(trimmedPath));
|
|
@@ -199,9 +221,13 @@ READING GUIDELINES:
|
|
|
199
221
|
TOOL FORMAT (CRITICAL - FOLLOW EXACTLY):
|
|
200
222
|
✅ CORRECT: [TOOL:LIST].[/TOOL]
|
|
201
223
|
✅ CORRECT: [TOOL:READ]./file.js[/TOOL]
|
|
202
|
-
✅ CORRECT: [TOOL:
|
|
224
|
+
✅ CORRECT: [TOOL:WRITE]./file.js]full content here[/TOOL]
|
|
225
|
+
✅ CORRECT: [TOOL:PATCH]./file.js]old code|||new code[/TOOL]
|
|
203
226
|
❌ WRONG: [TOOL:LIST].[/] - missing TOOL at end!
|
|
204
|
-
|
|
227
|
+
|
|
228
|
+
PATCH vs WRITE:
|
|
229
|
+
- Use PATCH for small changes (1-10 lines): [TOOL:PATCH]path]old|||new[/TOOL]
|
|
230
|
+
- Use WRITE only for new files or complete rewrites
|
|
205
231
|
|
|
206
232
|
WORKFLOW:
|
|
207
233
|
1. LIST directory → 2. READ files (as many as needed) → 3. ANALYZE and RESPOND`
|
|
@@ -228,6 +254,48 @@ WORKFLOW:
|
|
|
228
254
|
continue;
|
|
229
255
|
}
|
|
230
256
|
|
|
257
|
+
// Handle prune command - summarize and clear old context
|
|
258
|
+
if (input.toLowerCase() === '/prune') {
|
|
259
|
+
if (messages.length <= 5) {
|
|
260
|
+
console.log(chalk.yellow('Context is already small, nothing to prune.'));
|
|
261
|
+
continue;
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
// Keep system prompt + last 4 messages
|
|
265
|
+
const systemPrompt = messages[0];
|
|
266
|
+
const recentMessages = messages.slice(-4);
|
|
267
|
+
|
|
268
|
+
// Count what we're removing
|
|
269
|
+
const removedCount = messages.length - 5;
|
|
270
|
+
|
|
271
|
+
messages = [systemPrompt, ...recentMessages];
|
|
272
|
+
fs.writeFileSync(CONTEXT_FILE, JSON.stringify(messages));
|
|
273
|
+
console.log(chalk.green(`✅ Pruned ${removedCount} old messages. Kept system prompt + last 4 messages.`));
|
|
274
|
+
console.log(chalk.gray(`Context size: ${messages.length} messages\n`));
|
|
275
|
+
continue;
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
// Handle help command
|
|
279
|
+
if (input.toLowerCase() === '/help') {
|
|
280
|
+
console.log(chalk.cyan('\n📚 SAPPER COMMANDS:'));
|
|
281
|
+
console.log(chalk.white(' /reset, /clear') + chalk.gray(' - Clear all context and start fresh'));
|
|
282
|
+
console.log(chalk.white(' /prune') + chalk.gray(' - Remove old messages, keep last 4'));
|
|
283
|
+
console.log(chalk.white(' /context') + chalk.gray(' - Show current context size'));
|
|
284
|
+
console.log(chalk.white(' /help') + chalk.gray(' - Show this help message'));
|
|
285
|
+
console.log(chalk.white(' exit') + chalk.gray(' - Exit Sapper\n'));
|
|
286
|
+
continue;
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
// Handle context size command
|
|
290
|
+
if (input.toLowerCase() === '/context') {
|
|
291
|
+
const contextSize = JSON.stringify(messages).length;
|
|
292
|
+
console.log(chalk.cyan(`\n📊 Context: ${messages.length} messages, ~${Math.round(contextSize/1024)}KB`));
|
|
293
|
+
if (contextSize > 50000) {
|
|
294
|
+
console.log(chalk.yellow('⚠️ Context is large! Consider using /prune'));
|
|
295
|
+
}
|
|
296
|
+
continue;
|
|
297
|
+
}
|
|
298
|
+
|
|
231
299
|
messages.push({ role: 'user', content: input });
|
|
232
300
|
|
|
233
301
|
let toolRounds = 0; // Prevent infinite loops
|
|
@@ -283,6 +351,15 @@ WORKFLOW:
|
|
|
283
351
|
else if (type.toLowerCase() === 'read') result = tools.read(path);
|
|
284
352
|
else if (type.toLowerCase() === 'mkdir') result = tools.mkdir(path);
|
|
285
353
|
else if (type.toLowerCase() === 'write') result = await tools.write(path, content);
|
|
354
|
+
else if (type.toLowerCase() === 'patch') {
|
|
355
|
+
// PATCH format: [TOOL:PATCH]path]OLD_TEXT|||NEW_TEXT[/TOOL]
|
|
356
|
+
const parts = content?.split('|||');
|
|
357
|
+
if (parts && parts.length === 2) {
|
|
358
|
+
result = await tools.patch(path, parts[0], parts[1]);
|
|
359
|
+
} else {
|
|
360
|
+
result = 'Error: PATCH requires format [TOOL:PATCH]path]OLD_TEXT|||NEW_TEXT[/TOOL]';
|
|
361
|
+
}
|
|
362
|
+
}
|
|
286
363
|
else if (type.toLowerCase() === 'shell') result = await tools.shell(path);
|
|
287
364
|
|
|
288
365
|
messages.push({ role: 'user', content: `RESULT (${path}): ${result}` });
|