@hasna/terminal 1.3.4 → 1.3.5

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.
Files changed (3) hide show
  1. package/dist/ai.js +18 -16
  2. package/package.json +1 -1
  3. package/src/ai.ts +16 -14
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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hasna/terminal",
3
- "version": "1.3.4",
3
+ "version": "1.3.5",
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;