sapper-iq 1.0.1 → 1.0.2
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 +86 -24
package/package.json
CHANGED
package/sapper.mjs
CHANGED
|
@@ -95,14 +95,28 @@ function recreateReadline() {
|
|
|
95
95
|
|
|
96
96
|
// --- Tool Logic ---
|
|
97
97
|
const tools = {
|
|
98
|
-
read: (path) =>
|
|
98
|
+
read: (path) => {
|
|
99
|
+
try {
|
|
100
|
+
return fs.readFileSync(path.trim(), 'utf8');
|
|
101
|
+
} catch (error) {
|
|
102
|
+
return `Error reading file: ${error.message}`;
|
|
103
|
+
}
|
|
104
|
+
},
|
|
99
105
|
write: (path, content) => {
|
|
100
|
-
|
|
101
|
-
|
|
106
|
+
try {
|
|
107
|
+
fs.writeFileSync(path.trim(), content);
|
|
108
|
+
return `Successfully saved changes to ${path}`;
|
|
109
|
+
} catch (error) {
|
|
110
|
+
return `Error writing file: ${error.message}`;
|
|
111
|
+
}
|
|
102
112
|
},
|
|
103
113
|
mkdir: (path) => {
|
|
104
|
-
|
|
105
|
-
|
|
114
|
+
try {
|
|
115
|
+
fs.mkdirSync(path.trim(), { recursive: true });
|
|
116
|
+
return `Directory created: ${path}`;
|
|
117
|
+
} catch (error) {
|
|
118
|
+
return `Error creating directory: ${error.message}`;
|
|
119
|
+
}
|
|
106
120
|
},
|
|
107
121
|
shell: async (cmd) => {
|
|
108
122
|
console.log(chalk.red.bold(`\n[SECURITY] Sapper wants to execute: `) + chalk.white(cmd));
|
|
@@ -148,24 +162,48 @@ const tools = {
|
|
|
148
162
|
}
|
|
149
163
|
return "Command blocked by user.";
|
|
150
164
|
},
|
|
151
|
-
list: (path) =>
|
|
165
|
+
list: (path) => {
|
|
166
|
+
try {
|
|
167
|
+
return fs.readdirSync(path || '.').join('\n');
|
|
168
|
+
} catch (error) {
|
|
169
|
+
return `Error listing directory: ${error.message}`;
|
|
170
|
+
}
|
|
171
|
+
},
|
|
152
172
|
search: (pattern) => {
|
|
153
173
|
try {
|
|
154
174
|
const { execSync } = require('child_process');
|
|
155
175
|
const cmd = `grep -rnEi "${pattern.trim()}" . --exclude-dir=node_modules --exclude-dir=.git`;
|
|
156
176
|
return execSync(cmd, { encoding: 'utf8' }) || "No matches found.";
|
|
157
|
-
} catch (e) {
|
|
177
|
+
} catch (e) {
|
|
178
|
+
return "No matches found.";
|
|
179
|
+
}
|
|
158
180
|
}
|
|
159
181
|
};
|
|
160
182
|
|
|
161
183
|
async function selectModel() {
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
184
|
+
try {
|
|
185
|
+
const localModels = await ollama.list();
|
|
186
|
+
if (localModels.models.length === 0) {
|
|
187
|
+
console.log(chalk.red('❌ No Ollama models found!'));
|
|
188
|
+
console.log(chalk.yellow('Please install at least one model:'));
|
|
189
|
+
console.log(chalk.gray(' ollama pull llama2'));
|
|
190
|
+
console.log(chalk.gray(' ollama pull codellama'));
|
|
191
|
+
process.exit(1);
|
|
192
|
+
}
|
|
193
|
+
console.log(chalk.magenta.bold("\nAvailable Models:"));
|
|
194
|
+
localModels.models.forEach((m, i) => console.log(`${i + 1}. ${chalk.white(m.name)}`));
|
|
195
|
+
const choice = await safeQuestion(chalk.yellow('\nChoose model: '));
|
|
196
|
+
const index = parseInt(choice) - 1;
|
|
197
|
+
return localModels.models[index]?.name || localModels.models[0].name;
|
|
198
|
+
} catch (error) {
|
|
199
|
+
console.log(chalk.red('❌ Failed to connect to Ollama!'));
|
|
200
|
+
console.log(chalk.yellow('Please make sure Ollama is running:'));
|
|
201
|
+
console.log(chalk.gray(' 1. Install Ollama: https://ollama.ai'));
|
|
202
|
+
console.log(chalk.gray(' 2. Start Ollama: ollama serve'));
|
|
203
|
+
console.log(chalk.gray(' 3. Install a model: ollama pull llama2'));
|
|
204
|
+
console.log(chalk.red(`\nError details: ${error.message}`));
|
|
205
|
+
process.exit(1);
|
|
206
|
+
}
|
|
169
207
|
}
|
|
170
208
|
|
|
171
209
|
async function runSapper() {
|
|
@@ -176,6 +214,9 @@ async function runSapper() {
|
|
|
176
214
|
// Check for updates on startup
|
|
177
215
|
await checkForUpdates();
|
|
178
216
|
|
|
217
|
+
// Early Ollama connectivity check
|
|
218
|
+
console.log(chalk.gray('🔍 Checking Ollama connection...'));
|
|
219
|
+
|
|
179
220
|
let messages = [];
|
|
180
221
|
if (fs.existsSync(CONTEXT_FILE)) {
|
|
181
222
|
const resume = await safeQuestion(chalk.green('Resume previous session? (y/n): '));
|
|
@@ -325,21 +366,42 @@ async function runSapper() {
|
|
|
325
366
|
spinner.stop();
|
|
326
367
|
console.log(chalk.blue(`\n${selectedModel} is thinking...`));
|
|
327
368
|
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
369
|
+
let response;
|
|
370
|
+
try {
|
|
371
|
+
response = await ollama.chat({
|
|
372
|
+
model: selectedModel,
|
|
373
|
+
messages,
|
|
374
|
+
stream: true,
|
|
375
|
+
options: { num_ctx: 16384 }
|
|
376
|
+
});
|
|
377
|
+
} catch (error) {
|
|
378
|
+
console.log(chalk.red('\n❌ Failed to communicate with Ollama!'));
|
|
379
|
+
console.log(chalk.yellow('Possible issues:'));
|
|
380
|
+
console.log(chalk.gray(' - Ollama service stopped'));
|
|
381
|
+
console.log(chalk.gray(' - Model was removed'));
|
|
382
|
+
console.log(chalk.gray(' - Network connection issue'));
|
|
383
|
+
console.log(chalk.red(`Error: ${error.message}`));
|
|
384
|
+
console.log(chalk.cyan('\n💡 Try restarting Sapper or check Ollama status'));
|
|
385
|
+
active = false;
|
|
386
|
+
ask();
|
|
387
|
+
return;
|
|
388
|
+
}
|
|
334
389
|
|
|
335
390
|
let msg = '';
|
|
336
391
|
process.stdout.write(chalk.white('Sapper: '));
|
|
337
392
|
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
393
|
+
try {
|
|
394
|
+
for await (const chunk of response) {
|
|
395
|
+
if (chunk.message && chunk.message.content) {
|
|
396
|
+
process.stdout.write(chunk.message.content);
|
|
397
|
+
msg += chunk.message.content;
|
|
398
|
+
}
|
|
342
399
|
}
|
|
400
|
+
} catch (error) {
|
|
401
|
+
console.log(chalk.red('\n\n❌ Connection interrupted while streaming response!'));
|
|
402
|
+
console.log(chalk.yellow(`Error: ${error.message}`));
|
|
403
|
+
console.log(chalk.cyan('💡 The conversation will continue, but you may want to restart Sapper'));
|
|
404
|
+
msg += `\n[ERROR: Response interrupted - ${error.message}]`;
|
|
343
405
|
}
|
|
344
406
|
console.log();
|
|
345
407
|
|