@hasna/terminal 1.3.3 → 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 +19 -16
  2. package/package.json +1 -1
  3. package/src/ai.ts +17 -14
package/dist/ai.js CHANGED
@@ -172,6 +172,7 @@ RULES:
172
172
  - For "show who changed each line" use git blame, for "show remote urls" use git remote -v
173
173
  - For text search in code, use grep -rn, NOT nm or objdump (those are for compiled binaries)
174
174
  - On macOS: for memory use vm_stat or top -l 1, for disk use df -h, for processes use ps aux
175
+ - macOS uses BSD tools, NOT GNU. Use: du -d 1 (not --max-depth), ls (not ls --color), sort -r (not sort --reverse), ps aux (not ps --sort)
175
176
  - NEVER invent commands that don't exist. Stick to standard Unix/macOS commands.
176
177
  - NEVER install packages (npx, npm install, pip install, brew install). This is a READ-ONLY terminal.
177
178
  - NEVER modify source code (sed -i, codemod, awk with redirect). Only observe, never change.
@@ -206,25 +207,27 @@ export async function translateToCommand(nl, perms, sessionEntries, onToken) {
206
207
  }
207
208
  if (text.startsWith("BLOCKED:"))
208
209
  throw new Error(text);
209
- // Strip AI reasoning — extract only the shell command
210
- // AI sometimes prefixes with "Based on..." or wraps in backticks
210
+ // Strip AI reasoning — extract ONLY the shell command (first line)
211
211
  let cleaned = text.trim();
212
- // Remove markdown code blocks
213
- cleaned = cleaned.replace(/^```(?:bash|sh|shell)?\n?/m, "").replace(/\n?```$/m, "");
214
- // 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
215
215
  const lines = cleaned.split("\n");
216
- const commandLines = lines.filter(l => {
217
- const t = l.trim();
216
+ let command = "";
217
+ for (const line of lines) {
218
+ const t = line.trim();
218
219
  if (!t)
219
- return false;
220
- // Skip obvious reasoning lines
221
- if (/^(Based on|I |This |The |Let me|Here|Note:|Since|Looking|To |However|BLOCKED:)/.test(t))
222
- return false;
223
- if (/^[A-Z][a-z].*[.;:]$/.test(t))
224
- return false; // English sentence ending with period/semicolon/colon
225
- return true;
226
- });
227
- 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();
228
231
  cacheSet(nl, cleaned);
229
232
  return cleaned;
230
233
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hasna/terminal",
3
- "version": "1.3.3",
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
@@ -207,6 +207,7 @@ RULES:
207
207
  - For "show who changed each line" use git blame, for "show remote urls" use git remote -v
208
208
  - For text search in code, use grep -rn, NOT nm or objdump (those are for compiled binaries)
209
209
  - On macOS: for memory use vm_stat or top -l 1, for disk use df -h, for processes use ps aux
210
+ - macOS uses BSD tools, NOT GNU. Use: du -d 1 (not --max-depth), ls (not ls --color), sort -r (not sort --reverse), ps aux (not ps --sort)
210
211
  - NEVER invent commands that don't exist. Stick to standard Unix/macOS commands.
211
212
  - NEVER install packages (npx, npm install, pip install, brew install). This is a READ-ONLY terminal.
212
213
  - NEVER modify source code (sed -i, codemod, awk with redirect). Only observe, never change.
@@ -248,22 +249,24 @@ export async function translateToCommand(
248
249
 
249
250
  if (text.startsWith("BLOCKED:")) throw new Error(text);
250
251
 
251
- // Strip AI reasoning — extract only the shell command
252
- // AI sometimes prefixes with "Based on..." or wraps in backticks
252
+ // Strip AI reasoning — extract ONLY the shell command (first line)
253
253
  let cleaned = text.trim();
254
- // Remove markdown code blocks
255
- cleaned = cleaned.replace(/^```(?:bash|sh|shell)?\n?/m, "").replace(/\n?```$/m, "");
256
- // 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
257
257
  const lines = cleaned.split("\n");
258
- const commandLines = lines.filter(l => {
259
- const t = l.trim();
260
- if (!t) return false;
261
- // Skip obvious reasoning lines
262
- if (/^(Based on|I |This |The |Let me|Here|Note:|Since|Looking|To |However|BLOCKED:)/.test(t)) return false;
263
- if (/^[A-Z][a-z].*[.;:]$/.test(t)) return false; // English sentence ending with period/semicolon/colon
264
- return true;
265
- });
266
- 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();
267
270
 
268
271
  cacheSet(nl, cleaned);
269
272
  return cleaned;