nothumanallowed 16.0.47 → 16.0.49

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": "nothumanallowed",
3
- "version": "16.0.47",
3
+ "version": "16.0.49",
4
4
  "description": "Local AI assistant: 80 tools (Gmail, Calendar, Drive, GitHub, Slack, browser, code, files), 38 agents, visual workflows (Studio, AWF, WebCraft). Install with `npm i -g nothumanallowed`, run with `nha ui`. Free tier built-in (Liara), no API key required. Your data stays on your PC — OAuth tokens local, no cloud. Open-source MIT.",
5
5
  "type": "module",
6
6
  "bin": {
package/src/constants.mjs CHANGED
@@ -5,7 +5,7 @@ import { fileURLToPath } from 'url';
5
5
  const __filename = fileURLToPath(import.meta.url);
6
6
  const __dirname = path.dirname(__filename);
7
7
 
8
- export const VERSION = '16.0.47';
8
+ export const VERSION = '16.0.49';
9
9
  export const BASE_URL = 'https://nothumanallowed.com/cli';
10
10
  export const API_BASE = 'https://nothumanallowed.com/api/v1';
11
11
 
@@ -1286,7 +1286,9 @@ RULES:
1286
1286
  return 'OK — edit applied (fuzzy match)';
1287
1287
  }
1288
1288
  emit({ type: 'tool', op: 'edit', path: relPath, result: 'old_not_found' });
1289
- return 'Error: old_text not found in file. Use read_file to see the EXACT current content, copy the exact lines, and retry.';
1289
+ // Include the actual file content in the error response so the LLM can
1290
+ // produce a correct old_text on retry without needing a separate read_file.
1291
+ return `Error: old_text not found in file.\n\nCURRENT CONTENT OF ${relPath}:\n\`\`\`\n${src.slice(0, 16000)}\n\`\`\`\n\nPick the EXACT lines you want to replace from above, copy them as old_text, and retry edit_file.`;
1290
1292
  }
1291
1293
 
1292
1294
  if (toolName === 'create_file') {
@@ -1562,8 +1564,18 @@ After ALL fixes are done, emit <done/> on its own line.
1562
1564
  toolResults.push({ op: 'edit', path: relPath, result: 'ok' });
1563
1565
  emit({ type: 'tool', op: 'edit', path: relPath, result: 'ok', oldSnippet: oldStr.slice(0, 2000), newSnippet: newStr?.slice(0, 2000) });
1564
1566
  } else {
1565
- // No match — return error, let the agent retry with correct text
1566
- toolResults.push({ op: 'edit', path: relPath, result: 'old_not_found — your old text does not match the file. Use read tool to see the EXACT current content, then copy-paste the exact lines and retry.' });
1567
+ // No match — auto-read the file and INCLUDE its content in the
1568
+ // feedback. Without this, the LLM at the next step is blind and
1569
+ // can't correct the old_text. Critical fix for text-based mode
1570
+ // where the LLM doesn't see tool results between calls.
1571
+ const fileContentForRetry = src.slice(0, 16000);
1572
+ toolResults.push({
1573
+ op: 'edit',
1574
+ path: relPath,
1575
+ result: 'old_not_found',
1576
+ hint: 'Your old_text did NOT match the file. The CURRENT file content is included below — copy the EXACT lines you want to replace and retry edit with that exact text as old.',
1577
+ content: fileContentForRetry,
1578
+ });
1567
1579
  emit({ type: 'tool', op: 'edit', path: relPath, result: 'old_not_found', oldSnippet: oldStr.slice(0, 200) });
1568
1580
  }
1569
1581
  }
@@ -1825,11 +1837,16 @@ After ALL fixes are done, emit <done/> on its own line.
1825
1837
  break;
1826
1838
  }
1827
1839
 
1828
- // Build tool results feedback for next iteration
1840
+ // Build tool results feedback for next iteration.
1841
+ // CRITICAL: when edit fails with old_not_found, include the CURRENT
1842
+ // file content so the LLM can produce a correct old_text on retry.
1843
+ // Without this, text-based mode loops forever on the same wrong old_text.
1829
1844
  const feedbackParts = toolResults.map((r) => {
1830
1845
  let msg = `[${r.op}] ${r.path || ''}: ${r.result}`;
1831
- if (r.op === 'read' && r.content) msg += `\n\`\`\`\n${r.content}\n\`\`\``;
1832
- if (r.hint) msg += ` — ${r.hint}`;
1846
+ if (r.hint) msg += `\n HINT: ${r.hint}`;
1847
+ if (r.content) {
1848
+ msg += `\n\nCURRENT CONTENT OF ${r.path}:\n\`\`\`\n${r.content}\n\`\`\`\n\nTo fix: pick the exact lines you want to replace from above, use them as "old", and retry the edit.`;
1849
+ }
1833
1850
  return msg;
1834
1851
  });
1835
1852
 
@@ -3146,48 +3163,59 @@ export function register(router) {
3146
3163
  }
3147
3164
 
3148
3165
  router.post('/api/studio/webcraft/lint', async (req, res) => {
3166
+ // ROBUST lint endpoint — NEVER returns 500. If any linter crashes,
3167
+ // returns 200 with empty diagnostics + the error logged. A failed lint
3168
+ // must not break the IDE streaming flow.
3149
3169
  try {
3150
3170
  const { projectName, path: relPath } = await parseBody(req);
3151
3171
  if (!projectName || !relPath) return sendError(res, 400, 'projectName and path required');
3152
- const content = ProjectStore.readFile(projectName, relPath);
3153
- if (content === null) return sendJSON(res, 200, { diagnostics: [] });
3172
+ let content;
3173
+ try { content = ProjectStore.readFile(projectName, relPath); }
3174
+ catch { content = null; }
3175
+ if (content === null || content === undefined) return sendJSON(res, 200, { diagnostics: [] });
3154
3176
 
3155
3177
  const ext = (relPath.split('.').pop() || '').toLowerCase();
3156
3178
  let diagnostics = [];
3157
3179
 
3158
- // JavaScript / JSXtry TypeScript checkJs first, fallback to acorn
3180
+ // Each linter wrapped individually a crash in one doesn't kill the others
3159
3181
  if (['js', 'mjs', 'jsx', 'cjs'].includes(ext)) {
3160
- const projectDir = ProjectStore.dir(projectName);
3161
- const tsDiags = await lintJSWithTypeScript(projectDir, relPath);
3162
- diagnostics = tsDiags || lintJS(content, relPath, projectName);
3163
- }
3164
-
3165
- // JSON parse errors with precise location
3166
- if (ext === 'json') {
3167
- try { JSON.parse(content); } catch (e) {
3168
- const posMatch = e.message.match(/position (\d+)/i);
3169
- const pos = posMatch ? parseInt(posMatch[1]) : 0;
3170
- const before = content.slice(0, pos).split('\n');
3171
- diagnostics.push({
3172
- from: { line: before.length, col: (before[before.length - 1] || '').length },
3173
- severity: 'error',
3174
- message: e.message,
3175
- });
3182
+ try {
3183
+ const projectDir = ProjectStore.dir(projectName);
3184
+ const tsDiags = await lintJSWithTypeScript(projectDir, relPath).catch(() => null);
3185
+ diagnostics = tsDiags || lintJS(content, relPath, projectName) || [];
3186
+ } catch (e) {
3187
+ console.error('[lint] JS linter crashed for', relPath, ':', e.message);
3188
+ diagnostics = [];
3176
3189
  }
3177
- }
3178
-
3179
- // CSS — brace balance + property validation
3180
- if (ext === 'css') {
3181
- diagnostics = lintCSS(content);
3182
- }
3183
-
3184
- // HTML/HTM tag balance + reference validation
3185
- if (ext === 'html' || ext === 'htm') {
3186
- diagnostics = lintHTML(content, relPath, projectName);
3190
+ } else if (ext === 'json') {
3191
+ try {
3192
+ JSON.parse(content);
3193
+ } catch (e) {
3194
+ try {
3195
+ const posMatch = e.message.match(/position (\d+)/i);
3196
+ const pos = posMatch ? parseInt(posMatch[1]) : 0;
3197
+ const before = content.slice(0, pos).split('\n');
3198
+ diagnostics.push({
3199
+ from: { line: before.length, col: (before[before.length - 1] || '').length },
3200
+ severity: 'error',
3201
+ message: e.message,
3202
+ });
3203
+ } catch {}
3204
+ }
3205
+ } else if (ext === 'css') {
3206
+ try { diagnostics = lintCSS(content) || []; }
3207
+ catch (e) { console.error('[lint] CSS linter crashed:', e.message); diagnostics = []; }
3208
+ } else if (ext === 'html' || ext === 'htm') {
3209
+ try { diagnostics = lintHTML(content, relPath, projectName) || []; }
3210
+ catch (e) { console.error('[lint] HTML linter crashed:', e.message); diagnostics = []; }
3187
3211
  }
3188
3212
 
3189
3213
  sendJSON(res, 200, { diagnostics });
3190
- } catch (e) { sendError(res, 500, e.message); }
3214
+ } catch (e) {
3215
+ // Even the outer catch returns 200 — lint failures must not break the IDE
3216
+ console.error('[lint] outer error:', e.message);
3217
+ sendJSON(res, 200, { diagnostics: [], error: e.message });
3218
+ }
3191
3219
  });
3192
3220
 
3193
3221
  // ── File write (from IDE editor) ──────────────────────────────────────────