@hasna/terminal 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/dist/ai.js CHANGED
@@ -198,8 +198,27 @@ export async function translateToCommand(nl, perms, sessionEntries, onToken) {
198
198
  }
199
199
  if (text.startsWith("BLOCKED:"))
200
200
  throw new Error(text);
201
- cacheSet(nl, text);
202
- return text;
201
+ // Strip AI reasoning — extract only the shell command
202
+ // AI sometimes prefixes with "Based on..." or wraps in backticks
203
+ let cleaned = text.trim();
204
+ // Remove markdown code blocks
205
+ cleaned = cleaned.replace(/^```(?:bash|sh|shell)?\n?/m, "").replace(/\n?```$/m, "");
206
+ // Remove lines that look like AI reasoning (start with capital letter, contain "I ", "Based on", etc.)
207
+ const lines = cleaned.split("\n");
208
+ const commandLines = lines.filter(l => {
209
+ const t = l.trim();
210
+ if (!t)
211
+ return false;
212
+ // Skip obvious reasoning lines
213
+ if (/^(Based on|I |This |The |Let me|Here|Note:|Since|Looking|To )/.test(t))
214
+ return false;
215
+ if (/^[A-Z][a-z].*\.$/.test(t))
216
+ return false; // English sentence ending with period
217
+ return true;
218
+ });
219
+ cleaned = commandLines.join("\n").trim() || cleaned;
220
+ cacheSet(nl, cleaned);
221
+ return cleaned;
203
222
  }
204
223
  // ── prefetch ──────────────────────────────────────────────────────────────────
205
224
  export function prefetchNext(lastNl, perms, sessionEntries) {
package/dist/cli.js CHANGED
@@ -462,18 +462,11 @@ else if (args.length > 0) {
462
462
  console.log(formatWatchResult(result));
463
463
  process.exit(0);
464
464
  }
465
- // Lazy mode
466
- if (shouldBeLazy(clean, actualCmd)) {
467
- const lazy = toLazy(clean, actualCmd);
468
- const saved = rawTokens - estimateTokens(JSON.stringify(lazy));
469
- if (saved > 0)
470
- recordSaving("compressed", saved);
471
- console.log(JSON.stringify(lazy, null, 2));
472
- process.exit(0);
473
- }
474
- // AI answer framing — ALWAYS use in NL mode (even for small output)
475
- // The AI needs to ANSWER the question, not just pass through data
465
+ // Frame-first pipeline: AI answers the question, lazy is fallback
466
+ // For question-type prompts, answer framing runs BEFORE lazy mode
467
+ const isQuestion = /^(what|which|how|is|are|does|do|can|should|where|who|why|am|was|were|has|have|will)\b/i.test(prompt) || prompt.includes("?");
476
468
  if (clean.length > 10) {
469
+ // Try AI answer framing first (especially for questions)
477
470
  const processed = await processOutput(actualCmd, clean, prompt);
478
471
  if (processed.aiProcessed) {
479
472
  if (processed.tokensSaved > 0)
@@ -484,6 +477,15 @@ else if (args.length > 0) {
484
477
  process.exit(0);
485
478
  }
486
479
  }
480
+ // Lazy mode — fallback when AI framing didn't run or failed
481
+ if (shouldBeLazy(clean, actualCmd)) {
482
+ const lazy = toLazy(clean, actualCmd);
483
+ const saved = rawTokens - estimateTokens(JSON.stringify(lazy));
484
+ if (saved > 0)
485
+ recordSaving("compressed", saved);
486
+ console.log(JSON.stringify(lazy, null, 2));
487
+ process.exit(0);
488
+ }
487
489
  // Fallback: AI unavailable — pass through clean
488
490
  console.log(clean);
489
491
  const saved = rawTokens - estimateTokens(clean);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hasna/terminal",
3
- "version": "1.3.0",
3
+ "version": "1.3.2",
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
@@ -239,8 +239,26 @@ export async function translateToCommand(
239
239
  }
240
240
 
241
241
  if (text.startsWith("BLOCKED:")) throw new Error(text);
242
- cacheSet(nl, text);
243
- return text;
242
+
243
+ // Strip AI reasoning — extract only the shell command
244
+ // AI sometimes prefixes with "Based on..." or wraps in backticks
245
+ let cleaned = text.trim();
246
+ // Remove markdown code blocks
247
+ cleaned = cleaned.replace(/^```(?:bash|sh|shell)?\n?/m, "").replace(/\n?```$/m, "");
248
+ // Remove lines that look like AI reasoning (start with capital letter, contain "I ", "Based on", etc.)
249
+ const lines = cleaned.split("\n");
250
+ const commandLines = lines.filter(l => {
251
+ const t = l.trim();
252
+ if (!t) return false;
253
+ // Skip obvious reasoning lines
254
+ if (/^(Based on|I |This |The |Let me|Here|Note:|Since|Looking|To )/.test(t)) return false;
255
+ if (/^[A-Z][a-z].*\.$/.test(t)) return false; // English sentence ending with period
256
+ return true;
257
+ });
258
+ cleaned = commandLines.join("\n").trim() || cleaned;
259
+
260
+ cacheSet(nl, cleaned);
261
+ return cleaned;
244
262
  }
245
263
 
246
264
  // ── prefetch ──────────────────────────────────────────────────────────────────
package/src/cli.tsx CHANGED
@@ -446,18 +446,12 @@ else if (args.length > 0) {
446
446
  process.exit(0);
447
447
  }
448
448
 
449
- // Lazy mode
450
- if (shouldBeLazy(clean, actualCmd)) {
451
- const lazy = toLazy(clean, actualCmd);
452
- const saved = rawTokens - estimateTokens(JSON.stringify(lazy));
453
- if (saved > 0) recordSaving("compressed", saved);
454
- console.log(JSON.stringify(lazy, null, 2));
455
- process.exit(0);
456
- }
449
+ // Frame-first pipeline: AI answers the question, lazy is fallback
450
+ // For question-type prompts, answer framing runs BEFORE lazy mode
451
+ const isQuestion = /^(what|which|how|is|are|does|do|can|should|where|who|why|am|was|were|has|have|will)\b/i.test(prompt) || prompt.includes("?");
457
452
 
458
- // AI answer framing — ALWAYS use in NL mode (even for small output)
459
- // The AI needs to ANSWER the question, not just pass through data
460
453
  if (clean.length > 10) {
454
+ // Try AI answer framing first (especially for questions)
461
455
  const processed = await processOutput(actualCmd, clean, prompt);
462
456
  if (processed.aiProcessed) {
463
457
  if (processed.tokensSaved > 0) recordSaving("compressed", processed.tokensSaved);
@@ -467,6 +461,15 @@ else if (args.length > 0) {
467
461
  }
468
462
  }
469
463
 
464
+ // Lazy mode — fallback when AI framing didn't run or failed
465
+ if (shouldBeLazy(clean, actualCmd)) {
466
+ const lazy = toLazy(clean, actualCmd);
467
+ const saved = rawTokens - estimateTokens(JSON.stringify(lazy));
468
+ if (saved > 0) recordSaving("compressed", saved);
469
+ console.log(JSON.stringify(lazy, null, 2));
470
+ process.exit(0);
471
+ }
472
+
470
473
  // Fallback: AI unavailable — pass through clean
471
474
  console.log(clean);
472
475
  const saved = rawTokens - estimateTokens(clean);