sapper-iq 1.0.24 → 1.0.26

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 +51 -9
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "sapper-iq",
3
- "version": "1.0.24",
3
+ "version": "1.0.26",
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
@@ -11,6 +11,14 @@ import { dirname, join } from 'path';
11
11
  const __filename = fileURLToPath(import.meta.url);
12
12
  const __dirname = dirname(__filename);
13
13
 
14
+ // Prevent process from exiting on unhandled errors
15
+ process.on('uncaughtException', (err) => {
16
+ console.error(chalk.red('\n❌ Uncaught exception:'), err.message);
17
+ });
18
+ process.on('unhandledRejection', (reason) => {
19
+ console.error(chalk.red('\n❌ Unhandled rejection:'), reason);
20
+ });
21
+
14
22
  // Initialize versioning
15
23
  let CURRENT_VERSION = "1.1.0";
16
24
  try {
@@ -30,9 +38,19 @@ let rl = readline.createInterface({
30
38
  });
31
39
 
32
40
  async function safeQuestion(query) {
33
- return new Promise((resolve) => {
41
+ return new Promise((resolve, reject) => {
34
42
  process.stdout.write(query);
35
43
  rl.once('line', (answer) => { resolve(answer.trim()); });
44
+ rl.once('close', () => {
45
+ // Readline was closed - recreate it and try again
46
+ recreateReadline();
47
+ resolve(''); // Return empty string to continue the loop
48
+ });
49
+ rl.once('error', (err) => {
50
+ console.error(chalk.red('Readline error:'), err.message);
51
+ recreateReadline();
52
+ resolve('');
53
+ });
36
54
  });
37
55
  }
38
56
 
@@ -46,16 +64,30 @@ function recreateReadline() {
46
64
  });
47
65
  }
48
66
 
67
+ // Directories to ignore when listing files
68
+ const IGNORE_DIRS = new Set([
69
+ 'node_modules', '.git', '.svn', '.hg', 'dist', 'build',
70
+ '.next', '.nuxt', '__pycache__', '.cache', 'coverage',
71
+ '.idea', '.vscode', 'vendor', 'target', '.gradle'
72
+ ]);
73
+
49
74
  const tools = {
50
75
  read: (path) => {
51
76
  try { return fs.readFileSync(path.trim(), 'utf8'); }
52
77
  catch (error) { return `Error reading file: ${error.message}`; }
53
78
  },
54
- write: (path, content) => {
55
- try {
56
- fs.writeFileSync(path.trim(), content);
57
- return `Successfully saved changes to ${path}`;
58
- } catch (error) { return `Error writing file: ${error.message}`; }
79
+ write: async (path, content) => {
80
+ const trimmedPath = path.trim();
81
+ console.log(chalk.yellow.bold(`\n[WRITE] Sapper wants to write to: `) + chalk.white(trimmedPath));
82
+ console.log(chalk.gray(`Content preview (first 200 chars):\n${content?.substring(0, 200)}${content?.length > 200 ? '...' : ''}`));
83
+ const confirm = await safeQuestion(chalk.yellow('Allow write? (y/n): '));
84
+ if (confirm.toLowerCase() === 'y') {
85
+ try {
86
+ fs.writeFileSync(trimmedPath, content);
87
+ return `Successfully saved changes to ${trimmedPath}`;
88
+ } catch (error) { return `Error writing file: ${error.message}`; }
89
+ }
90
+ return "Write blocked by user.";
59
91
  },
60
92
  mkdir: (path) => {
61
93
  try {
@@ -82,8 +114,18 @@ const tools = {
82
114
  return "Command blocked by user.";
83
115
  },
84
116
  list: (path) => {
85
- try { return fs.readdirSync(path.trim() || '.').join('\n'); }
86
- catch (e) { return `Error: ${e.message}`; }
117
+ try {
118
+ const dir = path.trim() || '.';
119
+ const entries = fs.readdirSync(dir);
120
+ // Filter out ignored directories
121
+ const filtered = entries.filter(entry => {
122
+ if (IGNORE_DIRS.has(entry)) return false;
123
+ // Also skip hidden files/folders (starting with .) except current dir
124
+ if (entry.startsWith('.') && entry !== '.') return false;
125
+ return true;
126
+ });
127
+ return filtered.length > 0 ? filtered.join('\n') : '(empty or all files filtered)';
128
+ } catch (e) { return `Error: ${e.message}`; }
87
129
  }
88
130
  };
89
131
 
@@ -240,7 +282,7 @@ WORKFLOW:
240
282
  if (type.toLowerCase() === 'list') result = tools.list(path);
241
283
  else if (type.toLowerCase() === 'read') result = tools.read(path);
242
284
  else if (type.toLowerCase() === 'mkdir') result = tools.mkdir(path);
243
- else if (type.toLowerCase() === 'write') result = tools.write(path, content);
285
+ else if (type.toLowerCase() === 'write') result = await tools.write(path, content);
244
286
  else if (type.toLowerCase() === 'shell') result = await tools.shell(path);
245
287
 
246
288
  messages.push({ role: 'user', content: `RESULT (${path}): ${result}` });