@bazilio-san/glm-cc 1.0.17 → 1.0.19

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 (2) hide show
  1. package/bin/glm.js +60 -2
  2. package/package.json +1 -1
package/bin/glm.js CHANGED
@@ -95,6 +95,54 @@ function showConfig(config) {
95
95
  console.log(lines.join('\n'));
96
96
  }
97
97
 
98
+ // Resolve `claude` into { cmd, args, useShell } without an extra cmd.exe
99
+ // layer on Windows. A nested cmd.exe breaks ConPty stdin for Ink-based TUIs
100
+ // (e.g. when glm is spawned via node-pty from a test harness).
101
+ function resolveClaude() {
102
+ if (process.platform !== 'win32') {
103
+ return { cmd: 'claude', prefix: [], useShell: false };
104
+ }
105
+
106
+ const pathExts = (process.env.PATHEXT || '.COM;.EXE;.BAT;.CMD')
107
+ .split(';').map(e => e.toLowerCase()).filter(Boolean);
108
+ const pathDirs = (process.env.PATH || '').split(path.delimiter).filter(Boolean);
109
+
110
+ let exePath = null;
111
+ let cmdPath = null;
112
+ for (const dir of pathDirs) {
113
+ for (const ext of pathExts) {
114
+ const full = path.join(dir, 'claude' + ext);
115
+ if (!fs.existsSync(full)) continue;
116
+ if (ext === '.exe' && !exePath) exePath = full;
117
+ else if ((ext === '.cmd' || ext === '.bat') && !cmdPath) cmdPath = full;
118
+ }
119
+ if (exePath) break;
120
+ }
121
+
122
+ // Prefer native .exe — spawn directly, no shell layer.
123
+ if (exePath) {
124
+ return { cmd: exePath, prefix: [], useShell: false };
125
+ }
126
+
127
+ // npm shim on Windows looks like:
128
+ // @"C:\Program Files\nodejs\node.exe" "...\cli.js" %*
129
+ // Parse it and spawn node directly so we skip the shim's cmd.exe.
130
+ if (cmdPath) {
131
+ try {
132
+ const shim = fs.readFileSync(cmdPath, 'utf8');
133
+ const m = shim.match(/"([^"]+\\node\.exe)"\s+"([^"]+\.(?:js|mjs|cjs))"/i);
134
+ if (m) {
135
+ return { cmd: m[1], prefix: [m[2]], useShell: false };
136
+ }
137
+ } catch { /* fall through */ }
138
+ // Can't parse — must go through shell for .cmd execution.
139
+ return { cmd: cmdPath, prefix: [], useShell: true };
140
+ }
141
+
142
+ // Nothing found — let cmd.exe resolve at runtime (legacy behavior).
143
+ return { cmd: 'claude', prefix: [], useShell: true };
144
+ }
145
+
98
146
  function launchClaude(config, args) {
99
147
  const env = { ...process.env };
100
148
  for (const field of CONFIG_FIELDS) {
@@ -102,11 +150,21 @@ function launchClaude(config, args) {
102
150
  env[field.key] = config[field.key];
103
151
  }
104
152
  }
105
- const proc = spawn('claude', args, {
153
+
154
+ const { cmd, prefix, useShell } = resolveClaude();
155
+ const proc = spawn(cmd, [...prefix, ...args], {
106
156
  env,
107
157
  stdio: 'inherit',
108
- shell: process.platform === 'win32',
158
+ shell: useShell,
109
159
  });
160
+
161
+ const signals = ['SIGINT', 'SIGTERM', 'SIGHUP', 'SIGBREAK'];
162
+ for (const sig of signals) {
163
+ process.on(sig, () => {
164
+ try { proc.kill(sig); } catch { /* */ }
165
+ });
166
+ }
167
+
110
168
  proc.on('exit', code => process.exit(code ?? 0));
111
169
  proc.on('error', err => {
112
170
  console.error(`Failed to launch claude: ${err.message}`);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bazilio-san/glm-cc",
3
- "version": "1.0.17",
3
+ "version": "1.0.19",
4
4
  "description": "CLI tool for running claude with custom GLM configuration",
5
5
  "main": "bin/glm.js",
6
6
  "bin": {