@hasna/terminal 1.3.4 → 1.3.6

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
@@ -207,25 +207,27 @@ export async function translateToCommand(nl, perms, sessionEntries, onToken) {
207
207
  }
208
208
  if (text.startsWith("BLOCKED:"))
209
209
  throw new Error(text);
210
- // Strip AI reasoning — extract only the shell command
211
- // AI sometimes prefixes with "Based on..." or wraps in backticks
210
+ // Strip AI reasoning — extract ONLY the shell command (first line)
212
211
  let cleaned = text.trim();
213
- // Remove markdown code blocks
214
- cleaned = cleaned.replace(/^```(?:bash|sh|shell)?\n?/m, "").replace(/\n?```$/m, "");
215
- // Remove lines that look like AI reasoning (start with capital letter, contain "I ", "Based on", etc.)
212
+ // Remove ALL markdown code blocks and their content markers
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
216
215
  const lines = cleaned.split("\n");
217
- const commandLines = lines.filter(l => {
218
- const t = l.trim();
216
+ let command = "";
217
+ for (const line of lines) {
218
+ const t = line.trim();
219
219
  if (!t)
220
- return false;
221
- // Skip obvious reasoning lines
222
- if (/^(Based on|I |This |The |Let me|Here|Note:|Since|Looking|To |However|BLOCKED:)/.test(t))
223
- return false;
224
- if (/^[A-Z][a-z].*[.;:]$/.test(t))
225
- return false; // English sentence ending with period/semicolon/colon
226
- return true;
227
- });
228
- cleaned = commandLines.join("\n").trim() || cleaned;
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))
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;
229
+ }
230
+ cleaned = command || cleaned.split("\n")[0].trim();
229
231
  cacheSet(nl, cleaned);
230
232
  return cleaned;
231
233
  }
package/dist/cli.js CHANGED
@@ -457,11 +457,8 @@ else if (args.length > 0) {
457
457
  const rawTokens = estimateTokens(raw);
458
458
  recordUsage(rawTokens);
459
459
  // Test output detection
460
- if (isTestOutput(clean, actualCmd)) {
461
- const result = trackTests(process.cwd(), clean);
462
- console.log(formatWatchResult(result));
463
- process.exit(0);
464
- }
460
+ // Test output: skip watchlist, let AI framing handle it
461
+ // The AI reads "42 pass, 0 fail" better than regex parsing bun's mixed output
465
462
  // Frame-first pipeline: AI answers the question, lazy is fallback
466
463
  // For question-type prompts, answer framing runs BEFORE lazy mode
467
464
  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("?");
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hasna/terminal",
3
- "version": "1.3.4",
3
+ "version": "1.3.6",
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
@@ -249,22 +249,24 @@ export async function translateToCommand(
249
249
 
250
250
  if (text.startsWith("BLOCKED:")) throw new Error(text);
251
251
 
252
- // Strip AI reasoning — extract only the shell command
253
- // AI sometimes prefixes with "Based on..." or wraps in backticks
252
+ // Strip AI reasoning — extract ONLY the shell command (first line)
254
253
  let cleaned = text.trim();
255
- // Remove markdown code blocks
256
- cleaned = cleaned.replace(/^```(?:bash|sh|shell)?\n?/m, "").replace(/\n?```$/m, "");
257
- // Remove lines that look like AI reasoning (start with capital letter, contain "I ", "Based on", etc.)
254
+ // Remove ALL markdown code blocks and their content markers
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
258
257
  const lines = cleaned.split("\n");
259
- const commandLines = lines.filter(l => {
260
- const t = l.trim();
261
- if (!t) return false;
262
- // Skip obvious reasoning lines
263
- if (/^(Based on|I |This |The |Let me|Here|Note:|Since|Looking|To |However|BLOCKED:)/.test(t)) return false;
264
- if (/^[A-Z][a-z].*[.;:]$/.test(t)) return false; // English sentence ending with period/semicolon/colon
265
- return true;
266
- });
267
- cleaned = commandLines.join("\n").trim() || cleaned;
258
+ let command = "";
259
+ for (const line of lines) {
260
+ const t = line.trim();
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;
268
+ }
269
+ cleaned = command || cleaned.split("\n")[0].trim();
268
270
 
269
271
  cacheSet(nl, cleaned);
270
272
  return cleaned;
package/src/cli.tsx CHANGED
@@ -440,11 +440,8 @@ else if (args.length > 0) {
440
440
  recordUsage(rawTokens);
441
441
 
442
442
  // Test output detection
443
- if (isTestOutput(clean, actualCmd)) {
444
- const result = trackTests(process.cwd(), clean);
445
- console.log(formatWatchResult(result));
446
- process.exit(0);
447
- }
443
+ // Test output: skip watchlist, let AI framing handle it
444
+ // The AI reads "42 pass, 0 fail" better than regex parsing bun's mixed output
448
445
 
449
446
  // Frame-first pipeline: AI answers the question, lazy is fallback
450
447
  // For question-type prompts, answer framing runs BEFORE lazy mode