kiro-spec-engine 1.45.6 → 1.45.7

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/CHANGELOG.md CHANGED
@@ -7,6 +7,11 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
7
7
 
8
8
  ## [Unreleased]
9
9
 
10
+ ## [1.45.7] - 2026-02-13
11
+
12
+ ### Fixed
13
+ - **Windows CMD 8191 character limit**: `AgentSpawner.spawn()` now writes bootstrap prompt to temp file and spawns via PowerShell on Windows, bypassing cmd.exe's 8191 character command line limit (fixes `The command line is too long` error when bootstrap prompt exceeds 8K characters)
14
+
10
15
  ## [1.45.6] - 2026-02-13
11
16
 
12
17
  ### Fixed
@@ -83,12 +83,41 @@ class AgentSpawner extends EventEmitter {
83
83
  const { command, prependArgs } = this._resolveCodexCommand(config);
84
84
 
85
85
  // Spawn the child process (Req 1.1, 1.2)
86
- const needsShell = process.platform === 'win32' || command === 'npx';
86
+ //
87
+ // On Windows we must use a shell to execute .cmd/.ps1 wrappers, but
88
+ // cmd.exe has an 8191-character command-line limit which the bootstrap
89
+ // prompt easily exceeds. To avoid this we write the prompt to a temp
90
+ // file and pass the file path to codex via a shell read expression.
91
+ //
92
+ // Strategy per platform:
93
+ // Windows → write prompt to temp file, spawn via cmd.exe with
94
+ // `type <file>` piped through a FOR /F or via PowerShell.
95
+ // Simplest: use stdin pipe (stdio[0] = 'pipe') so the
96
+ // prompt never appears on the command line at all.
97
+ // Others → pass prompt directly as argument (no length issue).
98
+ const isWindows = process.platform === 'win32';
99
+ const needsShell = isWindows || command === 'npx';
100
+
101
+ // On Windows, remove the prompt from args and pipe it via stdin instead,
102
+ // completely bypassing the cmd.exe 8191-char command-line limit.
103
+ let useStdinPrompt = false;
104
+ let stdinPrompt = null;
87
105
  const finalArgs = [...prependArgs, ...args];
88
106
 
107
+ if (isWindows) {
108
+ // Remove the prompt (last element of args portion) from command line
109
+ // and deliver it via stdin to avoid cmd.exe length limit.
110
+ stdinPrompt = finalArgs.pop(); // remove prompt
111
+ // If the prompt was quoted by the escaping below, unwrap it
112
+ if (stdinPrompt.startsWith('"') && stdinPrompt.endsWith('"')) {
113
+ stdinPrompt = stdinPrompt.slice(1, -1).replace(/\\"/g, '"');
114
+ }
115
+ useStdinPrompt = true;
116
+ }
117
+
89
118
  // When shell: true, Node.js concatenates args into a single string without
90
- // escaping. Arguments containing spaces (e.g. the prompt) must be quoted so
91
- // the shell does not split them into separate tokens.
119
+ // escaping. Arguments containing spaces must be quoted so the shell does
120
+ // not split them into separate tokens.
92
121
  if (needsShell) {
93
122
  for (let i = 0; i < finalArgs.length; i++) {
94
123
  if (/\s/.test(finalArgs[i])) {
@@ -98,12 +127,45 @@ class AgentSpawner extends EventEmitter {
98
127
  }
99
128
 
100
129
  const env = { ...process.env, [apiKeyEnvVar]: apiKey };
101
- const child = spawn(command, finalArgs, {
130
+
131
+ // When using stdin for the prompt, write it to a temp file and pass
132
+ // the file path as the last argument using a short placeholder.
133
+ // Codex exec reads the prompt from argv, so we use a temp-file approach:
134
+ // write prompt → pass file path via shell read.
135
+ let promptTmpFile = null;
136
+ if (useStdinPrompt) {
137
+ promptTmpFile = path.join(os.tmpdir(), `kse-prompt-${agentId}-${Date.now()}.txt`);
138
+ fs.writeFileSync(promptTmpFile, stdinPrompt, 'utf-8');
139
+ }
140
+
141
+ // Build the final spawn arguments
142
+ let spawnCommand = command;
143
+ let spawnArgs = finalArgs;
144
+ let spawnShell = needsShell;
145
+
146
+ if (promptTmpFile) {
147
+ // On Windows, use PowerShell to read the temp file as the last argument.
148
+ // PowerShell does not have the 8191-char limit of cmd.exe.
149
+ // We construct: powershell -NoProfile -Command "& <command> <args> (Get-Content -Raw '<tmpfile>')"
150
+ const cmdParts = [command, ...finalArgs].map(a => {
151
+ if (a.startsWith('"') && a.endsWith('"')) return a;
152
+ return /\s/.test(a) ? `"${a}"` : a;
153
+ });
154
+ const psPromptExpr = `(Get-Content -Raw '${promptTmpFile.replace(/'/g, "''")}')`;
155
+ cmdParts.push(psPromptExpr);
156
+
157
+ const psCommand = `& ${cmdParts.join(' ')}`;
158
+ spawnCommand = 'powershell.exe';
159
+ spawnArgs = ['-NoProfile', '-Command', psCommand];
160
+ spawnShell = false; // spawning powershell.exe directly
161
+ }
162
+
163
+ const child = spawn(spawnCommand, spawnArgs, {
102
164
  cwd: this._workspaceRoot,
103
165
  env,
104
166
  stdio: ['ignore', 'pipe', 'pipe'],
105
167
  windowsHide: true,
106
- shell: needsShell,
168
+ shell: spawnShell,
107
169
  });
108
170
 
109
171
  const now = new Date().toISOString();
@@ -120,6 +182,7 @@ class AgentSpawner extends EventEmitter {
120
182
  retryCount: 0,
121
183
  stderr: '',
122
184
  events: [],
185
+ _promptTmpFile: promptTmpFile,
123
186
  };
124
187
 
125
188
  this._agents.set(agentId, agent);
@@ -231,6 +294,9 @@ class AgentSpawner extends EventEmitter {
231
294
  */
232
295
  _setupCloseHandler(agent) {
233
296
  agent.process.on('close', async (code) => {
297
+ // Clean up prompt temp file if used
298
+ this._cleanupPromptTmpFile(agent);
299
+
234
300
  // Clear timeout timer if still pending
235
301
  if (agent._timeoutTimer) {
236
302
  clearTimeout(agent._timeoutTimer);
@@ -274,6 +340,9 @@ class AgentSpawner extends EventEmitter {
274
340
 
275
341
  // Handle spawn errors (e.g. command not found)
276
342
  agent.process.on('error', async (err) => {
343
+ // Clean up prompt temp file if used
344
+ this._cleanupPromptTmpFile(agent);
345
+
277
346
  if (agent._timeoutTimer) {
278
347
  clearTimeout(agent._timeoutTimer);
279
348
  agent._timeoutTimer = null;
@@ -311,6 +380,9 @@ class AgentSpawner extends EventEmitter {
311
380
  agent._timeoutTimer = setTimeout(async () => {
312
381
  if (agent.status !== 'running') return;
313
382
 
383
+ // Clean up prompt temp file if used
384
+ this._cleanupPromptTmpFile(agent);
385
+
314
386
  agent.status = 'timeout';
315
387
  agent.completedAt = new Date().toISOString();
316
388
 
@@ -399,6 +471,23 @@ class AgentSpawner extends EventEmitter {
399
471
  }
400
472
  }
401
473
 
474
+ /**
475
+ * Remove the temporary prompt file created for Windows spawns.
476
+ * Silently ignores errors (file may already be gone).
477
+ * @param {object} agent
478
+ * @private
479
+ */
480
+ _cleanupPromptTmpFile(agent) {
481
+ if (agent._promptTmpFile) {
482
+ try {
483
+ fs.unlinkSync(agent._promptTmpFile);
484
+ } catch (_err) {
485
+ // Ignore — file may already be deleted
486
+ }
487
+ agent._promptTmpFile = null;
488
+ }
489
+ }
490
+
402
491
  /**
403
492
  * Read API key from Codex CLI's native auth file (~/.codex/auth.json).
404
493
  * Returns the key string or null if not found.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "kiro-spec-engine",
3
- "version": "1.45.6",
3
+ "version": "1.45.7",
4
4
  "description": "kiro-spec-engine (kse) - A CLI tool and npm package for spec-driven development with AI coding assistants. NOT the Kiro IDE desktop application.",
5
5
  "main": "index.js",
6
6
  "bin": {