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.
Files changed (2) hide show
  1. package/package.json +1 -1
  2. package/sapper.mjs +79 -2
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "sapper-iq",
3
- "version": "1.0.26",
3
+ "version": "1.1.0",
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
@@ -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:LIST]./src[/TOOL] then read all files found
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
- ❌ WRONG: [TOOL:LIST]/[/TOOL] - wrong directory!
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}` });