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.
Files changed (2) hide show
  1. package/package.json +1 -1
  2. package/sapper.mjs +86 -24
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "sapper-iq",
3
- "version": "1.0.1",
3
+ "version": "1.0.2",
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
@@ -95,14 +95,28 @@ function recreateReadline() {
95
95
 
96
96
  // --- Tool Logic ---
97
97
  const tools = {
98
- read: (path) => fs.readFileSync(path.trim(), 'utf8'),
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
- fs.writeFileSync(path.trim(), content);
101
- return `Successfully saved changes to ${path}`;
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
- fs.mkdirSync(path.trim(), { recursive: true });
105
- return `Directory created: ${path}`;
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) => fs.readdirSync(path || '.').join('\n'),
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) { return "No matches found."; }
177
+ } catch (e) {
178
+ return "No matches found.";
179
+ }
158
180
  }
159
181
  };
160
182
 
161
183
  async function selectModel() {
162
- const localModels = await ollama.list();
163
- if (localModels.models.length === 0) process.exit(1);
164
- console.log(chalk.magenta.bold("\nAvailable Models:"));
165
- localModels.models.forEach((m, i) => console.log(`${i + 1}. ${chalk.white(m.name)}`));
166
- const choice = await safeQuestion(chalk.yellow('\nChoose model: '));
167
- const index = parseInt(choice) - 1;
168
- return localModels.models[index]?.name || localModels.models[0].name;
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
- const response = await ollama.chat({
329
- model: selectedModel,
330
- messages,
331
- stream: true,
332
- options: { num_ctx: 16384 }
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
- for await (const chunk of response) {
339
- if (chunk.message && chunk.message.content) {
340
- process.stdout.write(chunk.message.content);
341
- msg += chunk.message.content;
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