sapper-iq 1.3.0 → 1.3.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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "sapper-iq",
3
- "version": "1.3.0",
3
+ "version": "1.3.2",
4
4
  "description": "AI-powered development assistant that executes commands and builds projects",
5
5
  "main": "sapper.mjs",
6
6
  "bin": {
package/sapper-ui.mjs CHANGED
@@ -1806,14 +1806,21 @@ function sendPasteToTerm(text) {
1806
1806
  showToast('Terminal not connected', 'err');
1807
1807
  return false;
1808
1808
  }
1809
- // xterm bracketed-paste sequence + Enter
1810
- var BEGIN = '\\u001b[200~';
1811
- var END = '\\u001b[201~';
1812
- // Decode escape literals; pty sees raw bytes
1813
- ws.send('\\u001b[200~'); // ESC [ 200 ~ (start paste)
1809
+ // Sapper's readline does NOT advertise bracketed-paste mode (no ESC[?2004h),
1810
+ // so wrapping in ESC[200~ … ESC[201~ would leak the literal "^[[200~" into
1811
+ // the prompt. Only use bracket-paste when we truly need multi-line atomicity
1812
+ // (Ask AI feature). For single-line content, send it raw and submit with \\r.
1813
+ var LF = String.fromCharCode(10);
1814
+ var CR = String.fromCharCode(13);
1815
+ if (text.indexOf(LF) < 0 && text.indexOf(CR) < 0) {
1816
+ ws.send(text + CR);
1817
+ return true;
1818
+ }
1819
+ // Multi-line: bracketed paste so readline treats it as one input.
1820
+ ws.send('\\u001b[200~');
1814
1821
  ws.send(text);
1815
- ws.send('\\u001b[201~'); // end paste
1816
- ws.send('\\r');
1822
+ ws.send('\\u001b[201~');
1823
+ ws.send(CR);
1817
1824
  return true;
1818
1825
  }
1819
1826
 
@@ -1910,10 +1917,14 @@ window.sendIndexToChat = function() {
1910
1917
  paths.forEach(function(p){
1911
1918
  if (state.indexSet[p] && state.indexSet[p].isDir) dirs.push(p); else files.push(p);
1912
1919
  });
1920
+ // Helper: quote paths that contain spaces, leave plain otherwise.
1921
+ function quoteIfNeeded(p) {
1922
+ return /\\s/.test(p) ? '"' + p.replace(/"/g, '\\\\"') + '"' : p;
1923
+ }
1913
1924
  // 1) /scan each folder (each sent as its own command + Enter)
1914
- dirs.forEach(function(d){ sendPasteToTerm('/scan ' + d); });
1925
+ dirs.forEach(function(d){ sendPasteToTerm('/scan ' + quoteIfNeeded(d)); });
1915
1926
  // 2) Build attachments token for files
1916
- var atTokens = files.map(function(f){ return '@' + f; }).join(' ');
1927
+ var atTokens = files.map(function(f){ return '@' + quoteIfNeeded(f); }).join(' ');
1917
1928
  var comment = (document.getElementById('idxComment') || {}).value || '';
1918
1929
  comment = comment.trim();
1919
1930
  if (comment) {
package/sapper.mjs CHANGED
@@ -8364,10 +8364,33 @@ async function runSapper() {
8364
8364
  continue;
8365
8365
  }
8366
8366
 
8367
- // Handle codebase scan command
8368
- if (input.toLowerCase() === '/scan') {
8369
- console.log(uiCleanMode() ? chalk.cyan('\nScanning codebase...') : chalk.cyan('\nšŸ” Scanning codebase...'));
8370
- const scanResult = scanCodebase('.');
8367
+ // Handle codebase scan command — accepts an optional path argument
8368
+ // (single quoted path or single bare path; spaces are allowed in bare paths)
8369
+ if (input.toLowerCase() === '/scan' || input.toLowerCase().startsWith('/scan ')) {
8370
+ let scanTarget = '.';
8371
+ const rest = input.slice(5).trim();
8372
+ if (rest) {
8373
+ // Strip surrounding quotes if present
8374
+ if ((rest.startsWith('"') && rest.endsWith('"')) ||
8375
+ (rest.startsWith("'") && rest.endsWith("'"))) {
8376
+ scanTarget = rest.slice(1, -1);
8377
+ } else {
8378
+ scanTarget = rest;
8379
+ }
8380
+ }
8381
+ if (!fs.existsSync(scanTarget)) {
8382
+ console.log(chalk.red(`Path not found: ${scanTarget}`));
8383
+ continue;
8384
+ }
8385
+ const scanStat = fs.statSync(scanTarget);
8386
+ if (!scanStat.isDirectory()) {
8387
+ console.log(chalk.red(`Not a directory: ${scanTarget}`));
8388
+ continue;
8389
+ }
8390
+ console.log(uiCleanMode()
8391
+ ? chalk.cyan(`\nScanning ${scanTarget}...`)
8392
+ : chalk.cyan(`\nšŸ” Scanning ${scanTarget}...`));
8393
+ const scanResult = scanCodebase(scanTarget);
8371
8394
 
8372
8395
  if (scanResult.files.length === 0) {
8373
8396
  console.log(chalk.yellow('No code files found in current directory.'));
@@ -8477,13 +8500,17 @@ async function runSapper() {
8477
8500
  // Continue to AI response (don't use 'continue' here)
8478
8501
  } else {
8479
8502
  // Process @file attachments in prompt (e.g., "analyze @package.json" or "fix @src/index.js")
8503
+ // Supports three forms:
8504
+ // @path/no/spaces — plain bare path
8505
+ // @"path with spaces/file.md" — double-quoted path
8506
+ // @'path with spaces/file.md' — single-quoted path
8480
8507
  let processedInput = input.startsWith('//') ? input.slice(1) : input;
8481
8508
  const fileAttachments = [];
8482
- const attachRegex = /@([\w.\/\-_]+)/g;
8509
+ const attachRegex = /@(?:"([^"]+)"|'([^']+)'|([\w.\/\-_]+))/g;
8483
8510
  let attachMatch;
8484
8511
 
8485
8512
  while ((attachMatch = attachRegex.exec(input)) !== null) {
8486
- const filePath = attachMatch[1];
8513
+ const filePath = attachMatch[1] || attachMatch[2] || attachMatch[3];
8487
8514
  try {
8488
8515
  if (fs.existsSync(filePath)) {
8489
8516
  // Check .sapperignore