@hasna/terminal 3.3.0 → 3.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.
@@ -24,15 +24,15 @@ function installClaude(bin) {
24
24
  return false;
25
25
  }
26
26
  try {
27
- execSync(`claude mcp add --transport stdio --scope user open-terminal -- ${bin} mcp serve`, { stdio: ["pipe", "pipe", "pipe"] });
27
+ execSync(`claude mcp add --transport stdio --scope user terminal -- ${bin} mcp serve`, { stdio: ["pipe", "pipe", "pipe"] });
28
28
  log("✓", "Claude Code");
29
29
  return true;
30
30
  }
31
31
  catch {
32
32
  // May already exist
33
33
  try {
34
- execSync(`claude mcp remove open-terminal -s user`, { stdio: ["pipe", "pipe", "pipe"] });
35
- execSync(`claude mcp add --transport stdio --scope user open-terminal -- ${bin} mcp serve`, { stdio: ["pipe", "pipe", "pipe"] });
34
+ execSync(`claude mcp remove terminal -s user`, { stdio: ["pipe", "pipe", "pipe"] });
35
+ execSync(`claude mcp add --transport stdio --scope user terminal -- ${bin} mcp serve`, { stdio: ["pipe", "pipe", "pipe"] });
36
36
  log("✓", "Claude Code (updated)");
37
37
  return true;
38
38
  }
@@ -55,8 +55,8 @@ function installCodex(bin) {
55
55
  mkdirSync(dir, { recursive: true });
56
56
  let content = existsSync(configPath) ? readFileSync(configPath, "utf8") : "";
57
57
  // Remove old entry if exists
58
- content = content.replace(/\n?\[mcp_servers\.open-terminal\][^\[]*/g, "");
59
- content += `\n[mcp_servers.open-terminal]\ncommand = "${bin}"\nargs = ["mcp", "serve"]\n`;
58
+ content = content.replace(/\n?\[mcp_servers\.terminal\][^\[]*/g, "");
59
+ content += `\n[mcp_servers.terminal]\ncommand = "${bin}"\nargs = ["mcp", "serve"]\n`;
60
60
  writeFileSync(configPath, content);
61
61
  log("✓", "Codex");
62
62
  return true;
@@ -86,7 +86,7 @@ function installGemini(bin) {
86
86
  }
87
87
  if (!config.mcpServers)
88
88
  config.mcpServers = {};
89
- config.mcpServers["open-terminal"] = { command: bin, args: ["mcp", "serve"] };
89
+ config.mcpServers["terminal"] = { command: bin, args: ["mcp", "serve"] };
90
90
  writeFileSync(configPath, JSON.stringify(config, null, 2));
91
91
  log("✓", "Gemini CLI");
92
92
  return true;
@@ -101,7 +101,7 @@ function uninstallClaude() {
101
101
  if (!hasClaude())
102
102
  return false;
103
103
  try {
104
- execSync(`claude mcp remove open-terminal -s user`, { stdio: ["pipe", "pipe", "pipe"] });
104
+ execSync(`claude mcp remove terminal -s user`, { stdio: ["pipe", "pipe", "pipe"] });
105
105
  log("✓", "Removed from Claude Code");
106
106
  return true;
107
107
  }
@@ -115,9 +115,9 @@ function uninstallCodex() {
115
115
  return false;
116
116
  try {
117
117
  let content = readFileSync(configPath, "utf8");
118
- if (!content.includes("open-terminal"))
118
+ if (!content.includes("terminal"))
119
119
  return false;
120
- content = content.replace(/\n?\[mcp_servers\.open-terminal\][^\[]*/g, "");
120
+ content = content.replace(/\n?\[mcp_servers\.terminal\][^\[]*/g, "");
121
121
  writeFileSync(configPath, content);
122
122
  log("✓", "Removed from Codex");
123
123
  return true;
@@ -132,9 +132,9 @@ function uninstallGemini() {
132
132
  return false;
133
133
  try {
134
134
  const config = JSON.parse(readFileSync(configPath, "utf8"));
135
- if (!config.mcpServers?.["open-terminal"])
135
+ if (!config.mcpServers?.["terminal"])
136
136
  return false;
137
- delete config.mcpServers["open-terminal"];
137
+ delete config.mcpServers["terminal"];
138
138
  writeFileSync(configPath, JSON.stringify(config, null, 2));
139
139
  log("✓", "Removed from Gemini CLI");
140
140
  return true;
@@ -148,7 +148,7 @@ export function handleInstall(args) {
148
148
  const flags = new Set(args);
149
149
  // Uninstall
150
150
  if (flags.has("uninstall") || flags.has("--uninstall")) {
151
- console.log("\n Removing open-terminal MCP server...\n");
151
+ console.log("\n Removing terminal MCP server...\n");
152
152
  uninstallClaude();
153
153
  uninstallCodex();
154
154
  uninstallGemini();
@@ -170,14 +170,7 @@ export function handleInstall(args) {
170
170
  }
171
171
  // ── Default: install everything ─────────────────────────────────────────
172
172
  const bin = which("terminal") ?? which("t") ?? "npx @hasna/terminal";
173
- console.log(`
174
- ┌─────────────────────────────────────┐
175
- │ open-terminal │
176
- │ Smart terminal for AI agents │
177
- └─────────────────────────────────────┘
178
-
179
- Setting up MCP server for all agents...
180
- `);
173
+ console.log(`\n terminal — setting up MCP...\n`);
181
174
  let count = 0;
182
175
  if (installClaude(bin))
183
176
  count++;
@@ -186,30 +179,10 @@ export function handleInstall(args) {
186
179
  if (installGemini(bin))
187
180
  count++;
188
181
  if (count === 0) {
189
- console.log(`
190
- No AI agents found. Install one first:
191
-
192
- npm i -g @anthropic-ai/claude-code # Claude Code
193
- npm i -g @openai/codex # Codex
194
- npm i -g @anthropic-ai/gemini-cli # Gemini CLI
195
-
196
- Then run: terminal install
197
- `);
182
+ console.log(`\n No agents found. Install Claude Code, Codex, or Gemini CLI first.\n`);
198
183
  }
199
184
  else {
200
- console.log(`
201
- Done. ${count} agent${count > 1 ? "s" : ""} configured.
202
- Restart your agent to start using open-terminal.
203
-
204
- Your AI agent now has these tools:
205
- execute_smart Run any command, get AI-summarized output
206
- execute_diff Run command, see only what changed
207
- search_content Smart grep with file grouping
208
- search_files Find files by pattern
209
- read_symbol Read a function by name (not whole file)
210
- boot Full project context in one call
211
- repo_state Git status + log + diff in one call
212
- `);
185
+ console.log(`\n ${count} agent${count > 1 ? "s" : ""} ready. Restart to apply.\n`);
213
186
  }
214
187
  }
215
188
  // Re-export individual installers for programmatic use
@@ -1,4 +1,4 @@
1
- // MCP Server for open-terminal — exposes terminal capabilities to AI agents
1
+ // MCP Server for terminal — exposes terminal capabilities to AI agents
2
2
  import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
3
3
  import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
4
4
  import { z } from "zod";
@@ -21,9 +21,9 @@ import { shouldBeLazy, toLazy } from "../lazy-executor.js";
21
21
  import { getEconomyStats, recordSaving } from "../economy.js";
22
22
  import { captureSnapshot } from "../snapshots.js";
23
23
  // ── helpers ──────────────────────────────────────────────────────────────────
24
- function exec(command, cwd, timeout) {
25
- // Auto-optimize command before execution
26
- const rw = rewriteCommand(command);
24
+ function exec(command, cwd, timeout, allowRewrite = false) {
25
+ // Only rewrite when explicitly allowed (execute_smart, not raw execute)
26
+ const rw = allowRewrite ? rewriteCommand(command) : { changed: false, rewritten: command };
27
27
  const actualCommand = rw.changed ? rw.rewritten : command;
28
28
  return new Promise((resolve) => {
29
29
  const start = Date.now();
@@ -56,7 +56,7 @@ function exec(command, cwd, timeout) {
56
56
  // ── server ───────────────────────────────────────────────────────────────────
57
57
  export function createServer() {
58
58
  const server = new McpServer({
59
- name: "open-terminal",
59
+ name: "terminal",
60
60
  version: "0.2.0",
61
61
  });
62
62
  // ── execute: run a command, return structured result ──────────────────────
@@ -135,7 +135,7 @@ export function createServer() {
135
135
  cwd: z.string().optional().describe("Working directory"),
136
136
  timeout: z.number().optional().describe("Timeout in ms (default: 30000)"),
137
137
  }, async ({ command, cwd, timeout }) => {
138
- const result = await exec(command, cwd, timeout ?? 30000);
138
+ const result = await exec(command, cwd, timeout ?? 30000, true); // allow rewrite for smart mode
139
139
  const output = (result.stdout + result.stderr).trim();
140
140
  const processed = await processOutput(command, output);
141
141
  // Progressive disclosure: store full output, return summary + expand key
@@ -202,10 +202,10 @@ export function createServer() {
202
202
  };
203
203
  });
204
204
  // ── status: show server info ──────────────────────────────────────────────
205
- server.tool("status", "Get open-terminal server status, capabilities, and available parsers.", async () => {
205
+ server.tool("status", "Get terminal server status, capabilities, and available parsers.", async () => {
206
206
  return {
207
207
  content: [{ type: "text", text: JSON.stringify({
208
- name: "open-terminal", version: "0.3.0", cwd: process.cwd(),
208
+ name: "terminal", version: "3.3.0", cwd: process.cwd(),
209
209
  features: ["ai-output-processing", "token-compression", "noise-filtering", "diff-caching", "lazy-execution", "progressive-disclosure"],
210
210
  }) }],
211
211
  };
@@ -541,5 +541,5 @@ export async function startMcpServer() {
541
541
  const server = createServer();
542
542
  const transport = new StdioServerTransport();
543
543
  await server.connect(transport);
544
- console.error("open-terminal MCP server running on stdio");
544
+ console.error("terminal MCP server running on stdio");
545
545
  }
@@ -33,15 +33,23 @@ const NOISE_PATTERNS = [
33
33
  // Generic download/upload progress
34
34
  /^\s*\d+(\.\d+)?\s*[KMG]?B\s*\/\s*\d+(\.\d+)?\s*[KMG]?B\b/,
35
35
  ];
36
- // Sensitive env var patterns — redact values, keep names only if needed
36
+ // Sensitive env var patterns — ONLY match actual env var assignments (export X=val, X=val at line start)
37
+ // NOT code lines like `const API_KEY = process.env.API_KEY` or `this.token = config.token`
37
38
  const SENSITIVE_PATTERNS = [
38
- /^(.*(?:KEY|TOKEN|SECRET|PASSWORD|CREDENTIAL|AUTH).*?)=(.+)$/i,
39
- /^(.*(?:API_KEY|ACCESS_KEY|PRIVATE_KEY|CLIENT_SECRET).*?)=(.+)$/i,
39
+ // export KEY_NAME="value" or KEY_NAME=value (shell env vars only)
40
+ /^(export\s+[A-Z_]*(?:KEY|TOKEN|SECRET|PASSWORD|CREDENTIAL)[A-Z_]*)=(.+)$/,
41
+ // Plain env assignment at start of line (no leading whitespace = not code)
42
+ /^([A-Z_]*(?:API_KEY|ACCESS_KEY|PRIVATE_KEY|CLIENT_SECRET|AUTH_TOKEN)[A-Z_]*)=(.+)$/,
40
43
  ];
41
- /** Redact sensitive values in output (env vars, credentials) */
44
+ /** Redact sensitive values in output (env vars only, not code) */
42
45
  function redactSensitive(line) {
46
+ const trimmed = line.trim();
47
+ // Skip lines that look like code (have leading whitespace, semicolons, const/let/var, etc.)
48
+ if (/^\s*(const|let|var|this\.|private|public|protected|import|export\s+(default|const|let|function|class)|\/\/|\/\*|\*)/.test(line)) {
49
+ return line; // Code — never redact
50
+ }
43
51
  for (const pattern of SENSITIVE_PATTERNS) {
44
- const match = line.match(pattern);
52
+ const match = trimmed.match(pattern);
45
53
  if (match) {
46
54
  return `${match[1]}=[REDACTED]`;
47
55
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hasna/terminal",
3
- "version": "3.3.0",
3
+ "version": "3.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
  "files": [
@@ -23,14 +23,14 @@ function hasGemini(): boolean { return !!which("gemini"); }
23
23
  function installClaude(bin: string): boolean {
24
24
  if (!hasClaude()) { log("–", "Claude Code not found, skipping"); return false; }
25
25
  try {
26
- execSync(`claude mcp add --transport stdio --scope user open-terminal -- ${bin} mcp serve`, { stdio: ["pipe", "pipe", "pipe"] });
26
+ execSync(`claude mcp add --transport stdio --scope user terminal -- ${bin} mcp serve`, { stdio: ["pipe", "pipe", "pipe"] });
27
27
  log("✓", "Claude Code");
28
28
  return true;
29
29
  } catch {
30
30
  // May already exist
31
31
  try {
32
- execSync(`claude mcp remove open-terminal -s user`, { stdio: ["pipe", "pipe", "pipe"] });
33
- execSync(`claude mcp add --transport stdio --scope user open-terminal -- ${bin} mcp serve`, { stdio: ["pipe", "pipe", "pipe"] });
32
+ execSync(`claude mcp remove terminal -s user`, { stdio: ["pipe", "pipe", "pipe"] });
33
+ execSync(`claude mcp add --transport stdio --scope user terminal -- ${bin} mcp serve`, { stdio: ["pipe", "pipe", "pipe"] });
34
34
  log("✓", "Claude Code (updated)");
35
35
  return true;
36
36
  } catch (e) {
@@ -50,8 +50,8 @@ function installCodex(bin: string): boolean {
50
50
  if (!existsSync(dir)) mkdirSync(dir, { recursive: true });
51
51
  let content = existsSync(configPath) ? readFileSync(configPath, "utf8") : "";
52
52
  // Remove old entry if exists
53
- content = content.replace(/\n?\[mcp_servers\.open-terminal\][^\[]*/g, "");
54
- content += `\n[mcp_servers.open-terminal]\ncommand = "${bin}"\nargs = ["mcp", "serve"]\n`;
53
+ content = content.replace(/\n?\[mcp_servers\.terminal\][^\[]*/g, "");
54
+ content += `\n[mcp_servers.terminal]\ncommand = "${bin}"\nargs = ["mcp", "serve"]\n`;
55
55
  writeFileSync(configPath, content);
56
56
  log("✓", "Codex");
57
57
  return true;
@@ -74,7 +74,7 @@ function installGemini(bin: string): boolean {
74
74
  try { config = JSON.parse(readFileSync(configPath, "utf8")); } catch {}
75
75
  }
76
76
  if (!config.mcpServers) config.mcpServers = {};
77
- config.mcpServers["open-terminal"] = { command: bin, args: ["mcp", "serve"] };
77
+ config.mcpServers["terminal"] = { command: bin, args: ["mcp", "serve"] };
78
78
  writeFileSync(configPath, JSON.stringify(config, null, 2));
79
79
  log("✓", "Gemini CLI");
80
80
  return true;
@@ -88,7 +88,7 @@ function installGemini(bin: string): boolean {
88
88
 
89
89
  function uninstallClaude(): boolean {
90
90
  if (!hasClaude()) return false;
91
- try { execSync(`claude mcp remove open-terminal -s user`, { stdio: ["pipe", "pipe", "pipe"] }); log("✓", "Removed from Claude Code"); return true; } catch { return false; }
91
+ try { execSync(`claude mcp remove terminal -s user`, { stdio: ["pipe", "pipe", "pipe"] }); log("✓", "Removed from Claude Code"); return true; } catch { return false; }
92
92
  }
93
93
 
94
94
  function uninstallCodex(): boolean {
@@ -96,8 +96,8 @@ function uninstallCodex(): boolean {
96
96
  if (!existsSync(configPath)) return false;
97
97
  try {
98
98
  let content = readFileSync(configPath, "utf8");
99
- if (!content.includes("open-terminal")) return false;
100
- content = content.replace(/\n?\[mcp_servers\.open-terminal\][^\[]*/g, "");
99
+ if (!content.includes("terminal")) return false;
100
+ content = content.replace(/\n?\[mcp_servers\.terminal\][^\[]*/g, "");
101
101
  writeFileSync(configPath, content);
102
102
  log("✓", "Removed from Codex");
103
103
  return true;
@@ -109,8 +109,8 @@ function uninstallGemini(): boolean {
109
109
  if (!existsSync(configPath)) return false;
110
110
  try {
111
111
  const config = JSON.parse(readFileSync(configPath, "utf8"));
112
- if (!config.mcpServers?.["open-terminal"]) return false;
113
- delete config.mcpServers["open-terminal"];
112
+ if (!config.mcpServers?.["terminal"]) return false;
113
+ delete config.mcpServers["terminal"];
114
114
  writeFileSync(configPath, JSON.stringify(config, null, 2));
115
115
  log("✓", "Removed from Gemini CLI");
116
116
  return true;
@@ -124,7 +124,7 @@ export function handleInstall(args: string[]): void {
124
124
 
125
125
  // Uninstall
126
126
  if (flags.has("uninstall") || flags.has("--uninstall")) {
127
- console.log("\n Removing open-terminal MCP server...\n");
127
+ console.log("\n Removing terminal MCP server...\n");
128
128
  uninstallClaude();
129
129
  uninstallCodex();
130
130
  uninstallGemini();
@@ -147,14 +147,7 @@ export function handleInstall(args: string[]): void {
147
147
 
148
148
  const bin = which("terminal") ?? which("t") ?? "npx @hasna/terminal";
149
149
 
150
- console.log(`
151
- ┌─────────────────────────────────────┐
152
- │ open-terminal │
153
- │ Smart terminal for AI agents │
154
- └─────────────────────────────────────┘
155
-
156
- Setting up MCP server for all agents...
157
- `);
150
+ console.log(`\n terminal — setting up MCP...\n`);
158
151
 
159
152
  let count = 0;
160
153
  if (installClaude(bin)) count++;
@@ -162,29 +155,9 @@ export function handleInstall(args: string[]): void {
162
155
  if (installGemini(bin)) count++;
163
156
 
164
157
  if (count === 0) {
165
- console.log(`
166
- No AI agents found. Install one first:
167
-
168
- npm i -g @anthropic-ai/claude-code # Claude Code
169
- npm i -g @openai/codex # Codex
170
- npm i -g @anthropic-ai/gemini-cli # Gemini CLI
171
-
172
- Then run: terminal install
173
- `);
158
+ console.log(`\n No agents found. Install Claude Code, Codex, or Gemini CLI first.\n`);
174
159
  } else {
175
- console.log(`
176
- Done. ${count} agent${count > 1 ? "s" : ""} configured.
177
- Restart your agent to start using open-terminal.
178
-
179
- Your AI agent now has these tools:
180
- execute_smart Run any command, get AI-summarized output
181
- execute_diff Run command, see only what changed
182
- search_content Smart grep with file grouping
183
- search_files Find files by pattern
184
- read_symbol Read a function by name (not whole file)
185
- boot Full project context in one call
186
- repo_state Git status + log + diff in one call
187
- `);
160
+ console.log(`\n ${count} agent${count > 1 ? "s" : ""} ready. Restart to apply.\n`);
188
161
  }
189
162
  }
190
163
 
package/src/mcp/server.ts CHANGED
@@ -1,4 +1,4 @@
1
- // MCP Server for open-terminal — exposes terminal capabilities to AI agents
1
+ // MCP Server for terminal — exposes terminal capabilities to AI agents
2
2
 
3
3
  import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
4
4
  import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
@@ -24,9 +24,9 @@ import { captureSnapshot } from "../snapshots.js";
24
24
 
25
25
  // ── helpers ──────────────────────────────────────────────────────────────────
26
26
 
27
- function exec(command: string, cwd?: string, timeout?: number): Promise<{ exitCode: number; stdout: string; stderr: string; duration: number; rewritten?: string }> {
28
- // Auto-optimize command before execution
29
- const rw = rewriteCommand(command);
27
+ function exec(command: string, cwd?: string, timeout?: number, allowRewrite: boolean = false): Promise<{ exitCode: number; stdout: string; stderr: string; duration: number; rewritten?: string }> {
28
+ // Only rewrite when explicitly allowed (execute_smart, not raw execute)
29
+ const rw = allowRewrite ? rewriteCommand(command) : { changed: false, rewritten: command };
30
30
  const actualCommand = rw.changed ? rw.rewritten : command;
31
31
  return new Promise((resolve) => {
32
32
  const start = Date.now();
@@ -61,7 +61,7 @@ function exec(command: string, cwd?: string, timeout?: number): Promise<{ exitCo
61
61
 
62
62
  export function createServer(): McpServer {
63
63
  const server = new McpServer({
64
- name: "open-terminal",
64
+ name: "terminal",
65
65
  version: "0.2.0",
66
66
  });
67
67
 
@@ -156,7 +156,7 @@ export function createServer(): McpServer {
156
156
  timeout: z.number().optional().describe("Timeout in ms (default: 30000)"),
157
157
  },
158
158
  async ({ command, cwd, timeout }) => {
159
- const result = await exec(command, cwd, timeout ?? 30000);
159
+ const result = await exec(command, cwd, timeout ?? 30000, true); // allow rewrite for smart mode
160
160
  const output = (result.stdout + result.stderr).trim();
161
161
  const processed = await processOutput(command, output);
162
162
 
@@ -251,11 +251,11 @@ export function createServer(): McpServer {
251
251
 
252
252
  server.tool(
253
253
  "status",
254
- "Get open-terminal server status, capabilities, and available parsers.",
254
+ "Get terminal server status, capabilities, and available parsers.",
255
255
  async () => {
256
256
  return {
257
257
  content: [{ type: "text" as const, text: JSON.stringify({
258
- name: "open-terminal", version: "0.3.0", cwd: process.cwd(),
258
+ name: "terminal", version: "3.3.0", cwd: process.cwd(),
259
259
  features: ["ai-output-processing", "token-compression", "noise-filtering", "diff-caching", "lazy-execution", "progressive-disclosure"],
260
260
  }) }],
261
261
  };
@@ -762,5 +762,5 @@ export async function startMcpServer(): Promise<void> {
762
762
  const server = createServer();
763
763
  const transport = new StdioServerTransport();
764
764
  await server.connect(transport);
765
- console.error("open-terminal MCP server running on stdio");
765
+ console.error("terminal MCP server running on stdio");
766
766
  }
@@ -41,16 +41,24 @@ const NOISE_PATTERNS: RegExp[] = [
41
41
  /^\s*\d+(\.\d+)?\s*[KMG]?B\s*\/\s*\d+(\.\d+)?\s*[KMG]?B\b/,
42
42
  ];
43
43
 
44
- // Sensitive env var patterns — redact values, keep names only if needed
44
+ // Sensitive env var patterns — ONLY match actual env var assignments (export X=val, X=val at line start)
45
+ // NOT code lines like `const API_KEY = process.env.API_KEY` or `this.token = config.token`
45
46
  const SENSITIVE_PATTERNS = [
46
- /^(.*(?:KEY|TOKEN|SECRET|PASSWORD|CREDENTIAL|AUTH).*?)=(.+)$/i,
47
- /^(.*(?:API_KEY|ACCESS_KEY|PRIVATE_KEY|CLIENT_SECRET).*?)=(.+)$/i,
47
+ // export KEY_NAME="value" or KEY_NAME=value (shell env vars only)
48
+ /^(export\s+[A-Z_]*(?:KEY|TOKEN|SECRET|PASSWORD|CREDENTIAL)[A-Z_]*)=(.+)$/,
49
+ // Plain env assignment at start of line (no leading whitespace = not code)
50
+ /^([A-Z_]*(?:API_KEY|ACCESS_KEY|PRIVATE_KEY|CLIENT_SECRET|AUTH_TOKEN)[A-Z_]*)=(.+)$/,
48
51
  ];
49
52
 
50
- /** Redact sensitive values in output (env vars, credentials) */
53
+ /** Redact sensitive values in output (env vars only, not code) */
51
54
  function redactSensitive(line: string): string {
55
+ const trimmed = line.trim();
56
+ // Skip lines that look like code (have leading whitespace, semicolons, const/let/var, etc.)
57
+ if (/^\s*(const|let|var|this\.|private|public|protected|import|export\s+(default|const|let|function|class)|\/\/|\/\*|\*)/.test(line)) {
58
+ return line; // Code — never redact
59
+ }
52
60
  for (const pattern of SENSITIVE_PATTERNS) {
53
- const match = line.match(pattern);
61
+ const match = trimmed.match(pattern);
54
62
  if (match) {
55
63
  return `${match[1]}=[REDACTED]`;
56
64
  }