@hasna/terminal 1.3.6 → 1.3.8

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/dist/ai.js CHANGED
@@ -211,23 +211,27 @@ export async function translateToCommand(nl, perms, sessionEntries, onToken) {
211
211
  let cleaned = text.trim();
212
212
  // Remove ALL markdown code blocks and their content markers
213
213
  cleaned = cleaned.replace(/```(?:bash|sh|shell)?\n?/g, "").replace(/```/g, "");
214
- // Split into lines and find the FIRST one that looks like a command
214
+ // Split into lines and find the FIRST one that looks like a SHELL COMMAND
215
215
  const lines = cleaned.split("\n");
216
216
  let command = "";
217
217
  for (const line of lines) {
218
218
  const t = line.trim();
219
219
  if (!t)
220
220
  continue;
221
- // Skip reasoning lines
222
- if (/^(Based on|I |This |The |Let me|Here|Note:|Since|Looking|To |However|BLOCKED:|If |You |We |For )/.test(t))
221
+ // Skip lines that are clearly English prose, not commands
222
+ if (/^(Based on|I |This |The |Let me|Here|Note:|Since|Looking|To |However|BLOCKED:|If |You |We |For |It |A |An |That )/.test(t))
223
223
  continue;
224
- if (/^[A-Z][a-z].*[.;:!?]$/.test(t))
225
- continue;
226
- // Found a command line take it and stop
227
- command = t;
228
- break;
224
+ if (/^[A-Z][a-z].*[.;:!?,]/.test(t))
225
+ continue; // English sentence with punctuation anywhere
226
+ if (t.split(" ").length > 15 && !/[|&;><$]/.test(t))
227
+ continue; // Long line without shell operators = prose
228
+ // Must start with a plausible command character (lowercase, /, ., $, or common tool)
229
+ if (/^[a-z./$~(]/.test(t) || /^[A-Z]+[_=]/.test(t)) {
230
+ command = t;
231
+ break;
232
+ }
229
233
  }
230
- cleaned = command || cleaned.split("\n")[0].trim();
234
+ cleaned = command || lines[0]?.trim() || cleaned;
231
235
  cacheSet(nl, cleaned);
232
236
  return cleaned;
233
237
  }
package/dist/cli.js CHANGED
@@ -421,6 +421,21 @@ else if (args.length > 0) {
421
421
  command = await translateToCommand(prompt, perms, []);
422
422
  }
423
423
  catch (e) {
424
+ // If BLOCKED, try fallback: read README or package.json for conceptual questions
425
+ if (e.message?.startsWith("BLOCKED:")) {
426
+ try {
427
+ const { existsSync, readFileSync } = await import("fs");
428
+ if (existsSync("README.md")) {
429
+ const readme = readFileSync("README.md", "utf8").slice(0, 3000);
430
+ const processed = await processOutput("cat README.md", readme, prompt);
431
+ if (processed.aiProcessed) {
432
+ console.log(processed.summary);
433
+ process.exit(0);
434
+ }
435
+ }
436
+ }
437
+ catch { }
438
+ }
424
439
  console.error(e.message);
425
440
  process.exit(1);
426
441
  }
@@ -434,7 +449,7 @@ else if (args.length > 0) {
434
449
  if (isIrreversible(command)) {
435
450
  console.error(`⚠ IRREVERSIBLE: $ ${command}`);
436
451
  console.error(` This command may kill processes, push code, or delete data.`);
437
- console.error(` Run with terminal exec "${command}" to bypass, or use the TUI for confirmation.`);
452
+ console.error(` Run directly in your shell if you're sure: ${command}`);
438
453
  process.exit(1);
439
454
  }
440
455
  // Show what we're running
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hasna/terminal",
3
- "version": "1.3.6",
3
+ "version": "1.3.8",
4
4
  "description": "Smart terminal wrapper for AI agents and humans — structured output, token compression, MCP server, natural language",
5
5
  "type": "module",
6
6
  "bin": {
package/src/ai.ts CHANGED
@@ -253,20 +253,23 @@ export async function translateToCommand(
253
253
  let cleaned = text.trim();
254
254
  // Remove ALL markdown code blocks and their content markers
255
255
  cleaned = cleaned.replace(/```(?:bash|sh|shell)?\n?/g, "").replace(/```/g, "");
256
- // Split into lines and find the FIRST one that looks like a command
256
+ // Split into lines and find the FIRST one that looks like a SHELL COMMAND
257
257
  const lines = cleaned.split("\n");
258
258
  let command = "";
259
259
  for (const line of lines) {
260
260
  const t = line.trim();
261
261
  if (!t) continue;
262
- // Skip reasoning lines
263
- if (/^(Based on|I |This |The |Let me|Here|Note:|Since|Looking|To |However|BLOCKED:|If |You |We |For )/.test(t)) continue;
264
- if (/^[A-Z][a-z].*[.;:!?]$/.test(t)) continue;
265
- // Found a command line take it and stop
266
- command = t;
267
- break;
262
+ // Skip lines that are clearly English prose, not commands
263
+ if (/^(Based on|I |This |The |Let me|Here|Note:|Since|Looking|To |However|BLOCKED:|If |You |We |For |It |A |An |That )/.test(t)) continue;
264
+ if (/^[A-Z][a-z].*[.;:!?,]/.test(t)) continue; // English sentence with punctuation anywhere
265
+ if (t.split(" ").length > 15 && !/[|&;><$]/.test(t)) continue; // Long line without shell operators = prose
266
+ // Must start with a plausible command character (lowercase, /, ., $, or common tool)
267
+ if (/^[a-z./$~(]/.test(t) || /^[A-Z]+[_=]/.test(t)) {
268
+ command = t;
269
+ break;
270
+ }
268
271
  }
269
- cleaned = command || cleaned.split("\n")[0].trim();
272
+ cleaned = command || lines[0]?.trim() || cleaned;
270
273
 
271
274
  cacheSet(nl, cleaned);
272
275
  return cleaned;
package/src/cli.tsx CHANGED
@@ -402,6 +402,20 @@ else if (args.length > 0) {
402
402
  try {
403
403
  command = await translateToCommand(prompt, perms, []);
404
404
  } catch (e: any) {
405
+ // If BLOCKED, try fallback: read README or package.json for conceptual questions
406
+ if (e.message?.startsWith("BLOCKED:")) {
407
+ try {
408
+ const { existsSync, readFileSync } = await import("fs");
409
+ if (existsSync("README.md")) {
410
+ const readme = readFileSync("README.md", "utf8").slice(0, 3000);
411
+ const processed = await processOutput("cat README.md", readme, prompt);
412
+ if (processed.aiProcessed) {
413
+ console.log(processed.summary);
414
+ process.exit(0);
415
+ }
416
+ }
417
+ } catch {}
418
+ }
405
419
  console.error(e.message);
406
420
  process.exit(1);
407
421
  }
@@ -414,7 +428,7 @@ else if (args.length > 0) {
414
428
  if (isIrreversible(command)) {
415
429
  console.error(`⚠ IRREVERSIBLE: $ ${command}`);
416
430
  console.error(` This command may kill processes, push code, or delete data.`);
417
- console.error(` Run with terminal exec "${command}" to bypass, or use the TUI for confirmation.`);
431
+ console.error(` Run directly in your shell if you're sure: ${command}`);
418
432
  process.exit(1);
419
433
  }
420
434