@hasna/terminal 0.6.2 → 0.7.0

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/cli.js CHANGED
@@ -51,6 +51,64 @@ if (args[0] === "--version" || args[0] === "-v") {
51
51
  console.log("0.6.1");
52
52
  process.exit(0);
53
53
  }
54
+ // ── Exec command — smart execution for agents ────────────────────────────────
55
+ if (args[0] === "exec") {
56
+ const command = args.slice(1).join(" ");
57
+ if (!command) {
58
+ console.error("Usage: terminal exec <command>");
59
+ process.exit(1);
60
+ }
61
+ const { execSync } = await import("child_process");
62
+ const { stripAnsi } = await import("./compression.js");
63
+ const { stripNoise } = await import("./noise-filter.js");
64
+ const { processOutput, shouldProcess } = await import("./output-processor.js");
65
+ const { rewriteCommand } = await import("./command-rewriter.js");
66
+ const { shouldBeLazy, toLazy } = await import("./lazy-executor.js");
67
+ const { estimateTokens } = await import("./parsers/index.js");
68
+ // Rewrite command if possible
69
+ const rw = rewriteCommand(command);
70
+ const actualCmd = rw.changed ? rw.rewritten : command;
71
+ if (rw.changed)
72
+ console.error(`[open-terminal] rewritten: ${actualCmd} (${rw.reason})`);
73
+ try {
74
+ const start = Date.now();
75
+ const raw = execSync(actualCmd, { encoding: "utf8", maxBuffer: 10 * 1024 * 1024, cwd: process.cwd() });
76
+ const duration = Date.now() - start;
77
+ const clean = stripNoise(stripAnsi(raw)).cleaned;
78
+ const rawTokens = estimateTokens(raw);
79
+ // Lazy mode for huge output
80
+ if (shouldBeLazy(clean)) {
81
+ const lazy = toLazy(clean, actualCmd);
82
+ const savedTokens = rawTokens - estimateTokens(JSON.stringify(lazy));
83
+ console.log(JSON.stringify({ ...lazy, duration, tokensSaved: savedTokens }));
84
+ process.exit(0);
85
+ }
86
+ // AI summary for medium-large output
87
+ if (shouldProcess(clean)) {
88
+ const processed = await processOutput(actualCmd, clean);
89
+ if (processed.aiProcessed && processed.tokensSaved > 30) {
90
+ console.log(processed.summary);
91
+ const savedTokens = rawTokens - estimateTokens(processed.summary);
92
+ console.error(`[open-terminal] ${rawTokens} → ${rawTokens - savedTokens} tokens (saved ${savedTokens}, ${Math.round(savedTokens / rawTokens * 100)}%)`);
93
+ process.exit(0);
94
+ }
95
+ }
96
+ // Small/medium output — just noise-strip and return
97
+ console.log(clean);
98
+ const savedTokens = rawTokens - estimateTokens(clean);
99
+ if (savedTokens > 10) {
100
+ console.error(`[open-terminal] saved ${savedTokens} tokens (noise filter)`);
101
+ }
102
+ }
103
+ catch (e) {
104
+ // Command failed — show error output
105
+ const stderr = e.stderr?.toString() ?? "";
106
+ const stdout = e.stdout?.toString() ?? "";
107
+ console.log(stripNoise(stripAnsi(stdout + stderr)).cleaned);
108
+ process.exit(e.status ?? 1);
109
+ }
110
+ process.exit(0);
111
+ }
54
112
  // ── MCP commands ─────────────────────────────────────────────────────────────
55
113
  if (args[0] === "mcp") {
56
114
  if (args[1] === "serve" || args.length === 1) {
@@ -41,10 +41,10 @@ const rules = [
41
41
  rewrite: () => "npm ls --depth=0",
42
42
  reason: "full tree is massive, top-level usually enough",
43
43
  },
44
- // ps aux without filter → add sort and head
44
+ // ps aux without filter → sort by memory and head (macOS compatible)
45
45
  {
46
46
  pattern: /^ps\s+aux\s*$/,
47
- rewrite: () => "ps aux --sort=-%mem | head -20",
47
+ rewrite: () => "ps aux | sort -k4 -rn | head -20",
48
48
  reason: "full process list is noise, show top consumers",
49
49
  },
50
50
  ];
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hasna/terminal",
3
- "version": "0.6.2",
3
+ "version": "0.7.0",
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/cli.tsx CHANGED
@@ -56,6 +56,67 @@ if (args[0] === "--version" || args[0] === "-v") {
56
56
  process.exit(0);
57
57
  }
58
58
 
59
+ // ── Exec command — smart execution for agents ────────────────────────────────
60
+
61
+ if (args[0] === "exec") {
62
+ const command = args.slice(1).join(" ");
63
+ if (!command) { console.error("Usage: terminal exec <command>"); process.exit(1); }
64
+
65
+ const { execSync } = await import("child_process");
66
+ const { stripAnsi } = await import("./compression.js");
67
+ const { stripNoise } = await import("./noise-filter.js");
68
+ const { processOutput, shouldProcess } = await import("./output-processor.js");
69
+ const { rewriteCommand } = await import("./command-rewriter.js");
70
+ const { shouldBeLazy, toLazy } = await import("./lazy-executor.js");
71
+ const { estimateTokens } = await import("./parsers/index.js");
72
+
73
+ // Rewrite command if possible
74
+ const rw = rewriteCommand(command);
75
+ const actualCmd = rw.changed ? rw.rewritten : command;
76
+ if (rw.changed) console.error(`[open-terminal] rewritten: ${actualCmd} (${rw.reason})`);
77
+
78
+ try {
79
+ const start = Date.now();
80
+ const raw = execSync(actualCmd, { encoding: "utf8", maxBuffer: 10 * 1024 * 1024, cwd: process.cwd() });
81
+ const duration = Date.now() - start;
82
+ const clean = stripNoise(stripAnsi(raw)).cleaned;
83
+ const rawTokens = estimateTokens(raw);
84
+
85
+ // Lazy mode for huge output
86
+ if (shouldBeLazy(clean)) {
87
+ const lazy = toLazy(clean, actualCmd);
88
+ const savedTokens = rawTokens - estimateTokens(JSON.stringify(lazy));
89
+ console.log(JSON.stringify({ ...lazy, duration, tokensSaved: savedTokens }));
90
+ process.exit(0);
91
+ }
92
+
93
+ // AI summary for medium-large output
94
+ if (shouldProcess(clean)) {
95
+ const processed = await processOutput(actualCmd, clean);
96
+ if (processed.aiProcessed && processed.tokensSaved > 30) {
97
+ console.log(processed.summary);
98
+ const savedTokens = rawTokens - estimateTokens(processed.summary);
99
+ console.error(`[open-terminal] ${rawTokens} → ${rawTokens - savedTokens} tokens (saved ${savedTokens}, ${Math.round(savedTokens/rawTokens*100)}%)`);
100
+ process.exit(0);
101
+ }
102
+ }
103
+
104
+ // Small/medium output — just noise-strip and return
105
+ console.log(clean);
106
+ const savedTokens = rawTokens - estimateTokens(clean);
107
+ if (savedTokens > 10) {
108
+ console.error(`[open-terminal] saved ${savedTokens} tokens (noise filter)`);
109
+ }
110
+ } catch (e: any) {
111
+ // Command failed — show error output
112
+ const stderr = e.stderr?.toString() ?? "";
113
+ const stdout = e.stdout?.toString() ?? "";
114
+ console.log(stripNoise(stripAnsi(stdout + stderr)).cleaned);
115
+ process.exit(e.status ?? 1);
116
+ }
117
+ process.exit(0);
118
+ }
119
+
59
120
  // ── MCP commands ─────────────────────────────────────────────────────────────
60
121
 
61
122
  if (args[0] === "mcp") {
@@ -47,10 +47,10 @@ const rules: RewriteRule[] = [
47
47
  rewrite: () => "npm ls --depth=0",
48
48
  reason: "full tree is massive, top-level usually enough",
49
49
  },
50
- // ps aux without filter → add sort and head
50
+ // ps aux without filter → sort by memory and head (macOS compatible)
51
51
  {
52
52
  pattern: /^ps\s+aux\s*$/,
53
- rewrite: () => "ps aux --sort=-%mem | head -20",
53
+ rewrite: () => "ps aux | sort -k4 -rn | head -20",
54
54
  reason: "full process list is noise, show top consumers",
55
55
  },
56
56
  ];