fix: handle Windows command line length limit for --agents option (#245)
Some checks are pending
Lint / lint (push) Waiting to run
Test / test (ubuntu-latest, 3.13) (push) Waiting to run
Test / test (macos-latest, 3.10) (push) Waiting to run
Test / test (macos-latest, 3.11) (push) Waiting to run
Test / test (macos-latest, 3.13) (push) Waiting to run
Test / test (ubuntu-latest, 3.10) (push) Waiting to run
Test / test (ubuntu-latest, 3.11) (push) Waiting to run
Test / test (windows-latest, 3.10) (push) Waiting to run
Test / test-e2e (macos-latest, 3.10) (push) Blocked by required conditions
Test / test-e2e (windows-latest, 3.11) (push) Blocked by required conditions
Test / test (ubuntu-latest, 3.12) (push) Waiting to run
Test / test (macos-latest, 3.12) (push) Waiting to run
Test / test (windows-latest, 3.11) (push) Waiting to run
Test / test (windows-latest, 3.12) (push) Waiting to run
Test / test (windows-latest, 3.13) (push) Waiting to run
Test / test-examples (3.12) (push) Blocked by required conditions
Test / test-e2e (macos-latest, 3.11) (push) Blocked by required conditions
Test / test-e2e (macos-latest, 3.12) (push) Blocked by required conditions
Test / test-e2e (macos-latest, 3.13) (push) Blocked by required conditions
Test / test-e2e (ubuntu-latest, 3.10) (push) Blocked by required conditions
Test / test-e2e (ubuntu-latest, 3.11) (push) Blocked by required conditions
Test / test-e2e (ubuntu-latest, 3.12) (push) Blocked by required conditions
Test / test-e2e (ubuntu-latest, 3.13) (push) Blocked by required conditions
Test / test-e2e (windows-latest, 3.10) (push) Blocked by required conditions
Test / test-e2e (windows-latest, 3.12) (push) Blocked by required conditions
Test / test-e2e (windows-latest, 3.13) (push) Blocked by required conditions
Test / test-examples (3.10) (push) Blocked by required conditions
Test / test-examples (3.11) (push) Blocked by required conditions
Test / test-examples (3.13) (push) Blocked by required conditions

## Summary

Fixes #238 - Resolves "command line too long" error on Windows when
using multiple subagents with long prompts.

## Problem

On Windows, the command line length is limited to 8191 characters
(cmd.exe). When using multiple subagents with long prompts, the
`--agents` JSON argument can easily exceed this limit, causing the
error:
```
命令行太长。 (command line too long)
Fatal error in message reader: Command failed with exit code 1
```

## Solution

This PR implements automatic detection and handling of command line
length limits:

1. **Platform-specific limits**: 
   - Windows: 8000 characters (safe margin below 8191)
   - Other platforms: 100,000 characters

2. **Automatic fallback**: When the command line would exceed the limit:
   - Write agents JSON to a temporary file
   - Use Claude CLI's `@filepath` syntax to reference the file
   - Clean up temp files when transport is closed

3. **Zero breaking changes**: The fix is transparent to users - it
automatically activates only when needed

## Changes

- Add `platform` and `tempfile` imports
- Add `_CMD_LENGTH_LIMIT` constant with platform-specific values
- Track temporary files in `self._temp_files` list
- Modify `_build_command()` to detect long command lines and use temp
files
- Clean up temp files in `close()` method

## Testing

-  All existing tests pass (122 tests)
-  Linting and type checking pass
-  Minimal changes - only 47 lines added/modified
-  Solution transparently handles the Windows command line limit

## Test plan

- [x] Test on Windows with multiple subagents and long prompts
- [x] Verify temp files are created and cleaned up properly
- [x] Verify normal operation (short command lines) is unaffected
- [x] Test cross-platform compatibility (limit only applies on Windows)

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-authored-by: Claude <noreply@anthropic.com>
This commit is contained in:
KuaaMU 2025-10-24 01:16:47 +08:00 committed by GitHub
parent 923d3d4620
commit 41e220cc2c
No known key found for this signature in database
GPG key ID: B5690EEEBB952194

View file

@ -3,9 +3,11 @@
import json
import logging
import os
import platform
import re
import shutil
import sys
import tempfile
from collections.abc import AsyncIterable, AsyncIterator
from contextlib import suppress
from dataclasses import asdict
@ -29,6 +31,11 @@ logger = logging.getLogger(__name__)
_DEFAULT_MAX_BUFFER_SIZE = 1024 * 1024 # 1MB buffer limit
MINIMUM_CLAUDE_CODE_VERSION = "2.0.0"
# Platform-specific command line length limits
# Windows cmd.exe has a limit of 8191 characters, use 8000 for safety
# Other platforms have much higher limits
_CMD_LENGTH_LIMIT = 8000 if platform.system() == "Windows" else 100000
class SubprocessCLITransport(Transport):
"""Subprocess transport using Claude Code CLI."""
@ -57,6 +64,7 @@ class SubprocessCLITransport(Transport):
if options.max_buffer_size is not None
else _DEFAULT_MAX_BUFFER_SIZE
)
self._temp_files: list[str] = [] # Track temporary files for cleanup
def _find_cli(self) -> str:
"""Find Claude Code CLI binary."""
@ -173,7 +181,8 @@ class SubprocessCLITransport(Transport):
name: {k: v for k, v in asdict(agent_def).items() if v is not None}
for name, agent_def in self._options.agents.items()
}
cmd.extend(["--agents", json.dumps(agents_dict)])
agents_json = json.dumps(agents_dict)
cmd.extend(["--agents", agents_json])
sources_value = (
",".join(self._options.setting_sources)
@ -199,6 +208,37 @@ class SubprocessCLITransport(Transport):
# String mode: use --print with the prompt
cmd.extend(["--print", "--", str(self._prompt)])
# Check if command line is too long (Windows limitation)
cmd_str = " ".join(cmd)
if len(cmd_str) > _CMD_LENGTH_LIMIT and self._options.agents:
# Command is too long - use temp file for agents
# Find the --agents argument and replace its value with @filepath
try:
agents_idx = cmd.index("--agents")
agents_json_value = cmd[agents_idx + 1]
# Create a temporary file
# ruff: noqa: SIM115
temp_file = tempfile.NamedTemporaryFile(
mode="w", suffix=".json", delete=False, encoding="utf-8"
)
temp_file.write(agents_json_value)
temp_file.close()
# Track for cleanup
self._temp_files.append(temp_file.name)
# Replace agents JSON with @filepath reference
cmd[agents_idx + 1] = f"@{temp_file.name}"
logger.info(
f"Command line length ({len(cmd_str)}) exceeds limit ({_CMD_LENGTH_LIMIT}). "
f"Using temp file for --agents: {temp_file.name}"
)
except (ValueError, IndexError) as e:
# This shouldn't happen, but log it just in case
logger.warning(f"Failed to optimize command line length: {e}")
return cmd
async def connect(self) -> None:
@ -309,6 +349,12 @@ class SubprocessCLITransport(Transport):
"""Close the transport and clean up resources."""
self._ready = False
# Clean up temporary files first (before early return)
for temp_file in self._temp_files:
with suppress(Exception):
Path(temp_file).unlink(missing_ok=True)
self._temp_files.clear()
if not self._process:
return