@sylphx/flow 3.24.1 → 3.24.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.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,11 @@
1
1
  # @sylphx/flow
2
2
 
3
+ ## 3.24.2 (2026-02-17)
4
+
5
+ ### 🐛 Bug Fixes
6
+
7
+ - **flow:** graceful exit when Claude Code exits with non-zero code ([44892b5](https://github.com/SylphxAI/flow/commit/44892b51beda34b49020ed1d0324cfcc3e4b28f9))
8
+
3
9
  ## 3.24.1 (2026-02-15)
4
10
 
5
11
  ### 🐛 Bug Fixes
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sylphx/flow",
3
- "version": "3.24.1",
3
+ "version": "3.24.2",
4
4
  "description": "One CLI to rule them all. Unified orchestration layer for AI coding assistants. Auto-detection, auto-installation, auto-upgrade.",
5
5
  "type": "module",
6
6
  "bin": {
@@ -19,7 +19,7 @@ import { TargetInstaller } from '../../services/target-installer.js';
19
19
  import type { RunCommandOptions } from '../../types.js';
20
20
  import { extractAgentInstructions, loadAgentContent } from '../../utils/agent-enhancer.js';
21
21
  import { showAttachSummary, showHeader } from '../../utils/display/banner.js';
22
- import { CLIError, UserCancelledError } from '../../utils/errors.js';
22
+ import { CLIError, ChildProcessExitError, UserCancelledError } from '../../utils/errors.js';
23
23
  import { log, promptConfirm, promptSelect } from '../../utils/prompts/index.js';
24
24
  import { ensureTargetInstalled, promptForTargetSelection } from '../../utils/target-selection.js';
25
25
  import { resolvePrompt } from './prompt.js';
@@ -340,6 +340,17 @@ export async function executeFlowV2(
340
340
  process.exit(0);
341
341
  }
342
342
 
343
+ // Child process already displayed its own error via inherited stdio —
344
+ // cleanup silently and exit with the same code.
345
+ if (error instanceof ChildProcessExitError) {
346
+ try {
347
+ await executor.cleanup(projectPath);
348
+ } catch (cleanupError) {
349
+ debug('cleanup after child exit failed:', cleanupError);
350
+ }
351
+ process.exit(error.exitCode);
352
+ }
353
+
343
354
  console.error(chalk.red('\n Error:'), error);
344
355
 
345
356
  try {
@@ -11,7 +11,7 @@ import {
11
11
  pathUtils,
12
12
  yamlUtils,
13
13
  } from '../utils/config/target-utils.js';
14
- import { CLIError } from '../utils/errors.js';
14
+ import { CLIError, ChildProcessExitError } from '../utils/errors.js';
15
15
  import { sanitize } from '../utils/security/security.js';
16
16
  import { DEFAULT_CLAUDE_CODE_ENV } from './functional/claude-code-logic.js';
17
17
  import {
@@ -32,11 +32,6 @@ interface ClaudeCodeAgentMetadata {
32
32
  model?: string;
33
33
  }
34
34
 
35
- /** Error with exit code from child process */
36
- interface ProcessExitError extends Error {
37
- code: number | null;
38
- }
39
-
40
35
  /** Type guard for Node.js errors with errno/code properties */
41
36
  function isNodeError(error: unknown): error is NodeJS.ErrnoException {
42
37
  return error instanceof Error && 'code' in error;
@@ -299,9 +294,9 @@ Please begin your response with a comprehensive summary of all the instructions
299
294
  if (code === 0) {
300
295
  resolve();
301
296
  } else {
302
- const error = new Error(`Claude Code exited with code ${code}`) as ProcessExitError;
303
- error.code = code;
304
- reject(error);
297
+ // Child already displayed its error via inherited stdio use
298
+ // ChildProcessExitError so upstream handlers exit silently.
299
+ reject(new ChildProcessExitError(code ?? 1));
305
300
  }
306
301
  });
307
302
 
@@ -313,13 +308,15 @@ Please begin your response with a comprehensive summary of all the instructions
313
308
  });
314
309
  });
315
310
  } catch (error: unknown) {
311
+ // ChildProcessExitError means the child already printed its own error —
312
+ // let it propagate unmodified so upstream can exit silently.
313
+ if (error instanceof ChildProcessExitError) {
314
+ throw error;
315
+ }
316
316
  if (isNodeError(error)) {
317
317
  if (error.code === 'ENOENT') {
318
318
  throw new CLIError('Claude Code not found. Please install it first.', 'CLAUDE_NOT_FOUND');
319
319
  }
320
- if (error.code !== undefined) {
321
- throw new CLIError(`Claude Code exited with code ${error.code}`, 'CLAUDE_ERROR');
322
- }
323
320
  throw new CLIError(`Failed to execute Claude Code: ${error.message}`, 'CLAUDE_ERROR');
324
321
  }
325
322
  if (error instanceof Error) {
@@ -20,3 +20,15 @@ export class CLIError extends Error {
20
20
  this.name = 'CLIError';
21
21
  }
22
22
  }
23
+
24
+ /**
25
+ * Child process exited with non-zero code.
26
+ * Since stdio is inherited, the child already displayed its own error —
27
+ * upstream handlers should cleanup and exit silently without printing anything.
28
+ */
29
+ export class ChildProcessExitError extends Error {
30
+ constructor(public exitCode: number) {
31
+ super(`Child process exited with code ${exitCode}`);
32
+ this.name = 'ChildProcessExitError';
33
+ }
34
+ }