fix codex skill eof

This commit is contained in:
cexll
2025-11-25 21:00:12 +08:00
parent c4021cf58a
commit 71305da77e
2 changed files with 99 additions and 55 deletions

View File

@@ -17,25 +17,40 @@ Execute Codex CLI commands and parse structured JSON responses. Supports file re
## Usage ## Usage
**Mandatory**: Run every automated invocation through the Bash tool in the foreground with the command below, keeping the `timeout` parameter fixed at `7200000` milliseconds (do not change it or use any other entry point). **Mandatory**: Run every automated invocation through the Bash tool in the foreground with **HEREDOC syntax** to avoid shell quoting issues, keeping the `timeout` parameter fixed at `7200000` milliseconds (do not change it or use any other entry point).
```bash ```bash
uv run ~/.claude/skills/codex/scripts/codex.py "<task>" [working_dir] uv run ~/.claude/skills/codex/scripts/codex.py - [working_dir] <<'EOF'
<task content here>
EOF
``` ```
**Foreground only (no background/BashOutput)**: Never set `background: true`, never accept Claude's “Running in the background” mode, and avoid `BashOutput` streaming loops. Keep a single foreground Bash call per Codex task; if work might be long, split it into smaller foreground runs instead of offloading to background execution. **Why HEREDOC?** Tasks often contain code blocks, nested quotes, shell metacharacters (`$`, `` ` ``, `\`), and multiline text. HEREDOC (Here Document) syntax passes these safely without shell interpretation, eliminating quote-escaping nightmares.
**Optional methods** (direct execution or via Python): **Foreground only (no background/BashOutput)**: Never set `background: true`, never accept Claude's "Running in the background" mode, and avoid `BashOutput` streaming loops. Keep a single foreground Bash call per Codex task; if work might be long, split it into smaller foreground runs instead of offloading to background execution.
**Simple tasks** (backward compatibility):
For simple single-line tasks without special characters, you can still use direct quoting:
```bash ```bash
~/.claude/skills/codex/scripts/codex.py "<task>" [working_dir] uv run ~/.claude/skills/codex/scripts/codex.py "simple task here" [working_dir]
# or
python3 ~/.claude/skills/codex/scripts/codex.py "<task>" [working_dir]
``` ```
Resume a session: **Resume a session with HEREDOC:**
```bash ```bash
uv run ~/.claude/skills/codex/scripts/codex.py resume <session_id> "<task>" [working_dir] uv run ~/.claude/skills/codex/scripts/codex.py resume <session_id> - [working_dir] <<'EOF'
<task content>
EOF
``` ```
**Cross-platform notes:**
- **Bash/Zsh**: Use `<<'EOF'` (single quotes prevent variable expansion)
- **PowerShell 5.1+**: Use `@'` and `'@` (here-string syntax)
```powershell
uv run ~/.claude/skills/codex/scripts/codex.py - @'
task content
'@
```
## Environment Variables ## Environment Variables
- **CODEX_TIMEOUT**: Override timeout in milliseconds (default: 7200000 = 2 hours) - **CODEX_TIMEOUT**: Override timeout in milliseconds (default: 7200000 = 2 hours)
- Example: `export CODEX_TIMEOUT=3600000` for 1 hour - Example: `export CODEX_TIMEOUT=3600000` for 1 hour
@@ -72,63 +87,82 @@ Return only the final agent message and session ID—do not paste raw `BashOutpu
### Invocation Pattern ### Invocation Pattern
All automated executions may only invoke `uv run ~/.claude/skills/codex/scripts/codex.py "<task>" ...` through the Bash tool in the foreground, and the `timeout` must remain fixed at `7200000` (non-negotiable): All automated executions must use HEREDOC syntax through the Bash tool in the foreground, with `timeout` fixed at `7200000` (non-negotiable):
``` ```
Bash tool parameters: Bash tool parameters:
- command: uv run ~/.claude/skills/codex/scripts/codex.py "<task>" [working_dir] - command: uv run ~/.claude/skills/codex/scripts/codex.py - [working_dir] <<'EOF'
<task content>
EOF
- timeout: 7200000 - timeout: 7200000
- description: <brief description of the task> - description: <brief description of the task>
``` ```
Run every call in the foreground—never append `&` to background it—so logs and errors stay visible for timely interruption or diagnosis. Run every call in the foreground—never append `&` to background it—so logs and errors stay visible for timely interruption or diagnosis.
Alternatives: **Important:** Use HEREDOC (`<<'EOF'`) for all but the simplest tasks. This prevents shell interpretation of quotes, variables, and special characters.
```
# Direct execution (simplest)
- command: ~/.claude/skills/codex/scripts/codex.py "<task>" [working_dir]
# Using python3
- command: python3 ~/.claude/skills/codex/scripts/codex.py "<task>" [working_dir]
```
### Examples ### Examples
**Basic code analysis:** **Basic code analysis:**
```bash ```bash
# Recommended: via uv run (auto-manages Python environment) # Recommended: via uv run with HEREDOC (handles any special characters)
uv run ~/.claude/skills/codex/scripts/codex.py "explain @src/main.ts" uv run ~/.claude/skills/codex/scripts/codex.py - <<'EOF'
explain @src/main.ts
EOF
# timeout: 7200000 # timeout: 7200000
# Alternative: direct execution # Alternative: simple direct quoting (if task is simple)
~/.claude/skills/codex/scripts/codex.py "explain @src/main.ts" uv run ~/.claude/skills/codex/scripts/codex.py "explain @src/main.ts"
``` ```
**Refactoring with custom model (via environment variable):** **Refactoring with multiline instructions:**
```bash ```bash
# Set model via environment variable uv run ~/.claude/skills/codex/scripts/codex.py - <<'EOF'
uv run ~/.claude/skills/codex/scripts/codex.py "refactor @src/utils for performance" refactor @src/utils for performance:
- Extract duplicate code into helpers
- Use memoization for expensive calculations
- Add inline comments for non-obvious logic
EOF
# timeout: 7200000 # timeout: 7200000
``` ```
**Multi-file analysis:** **Multi-file analysis:**
```bash ```bash
uv run ~/.claude/skills/codex/scripts/codex.py "analyze @. and find security issues" "/path/to/project" uv run ~/.claude/skills/codex/scripts/codex.py - "/path/to/project" <<'EOF'
analyze @. and find security issues:
1. Check for SQL injection vulnerabilities
2. Identify XSS risks in templates
3. Review authentication/authorization logic
4. Flag hardcoded credentials or secrets
EOF
# timeout: 7200000 # timeout: 7200000
``` ```
**Resume previous session:** **Resume previous session:**
```bash ```bash
# First session # First session
uv run ~/.claude/skills/codex/scripts/codex.py "add comments to @utils.js" uv run ~/.claude/skills/codex/scripts/codex.py - <<'EOF'
add comments to @utils.js explaining the caching logic
EOF
# Output includes: SESSION_ID: 019a7247-ac9d-71f3-89e2-a823dbd8fd14 # Output includes: SESSION_ID: 019a7247-ac9d-71f3-89e2-a823dbd8fd14
# Continue the conversation # Continue the conversation with more context
uv run ~/.claude/skills/codex/scripts/codex.py resume 019a7247-ac9d-71f3-89e2-a823dbd8fd14 "now add type hints" uv run ~/.claude/skills/codex/scripts/codex.py resume 019a7247-ac9d-71f3-89e2-a823dbd8fd14 - <<'EOF'
now add TypeScript type hints and handle edge cases where cache is null
EOF
# timeout: 7200000 # timeout: 7200000
``` ```
**Using python3 directly (alternative):** **Task with code snippets and special characters:**
```bash ```bash
python3 ~/.claude/skills/codex/scripts/codex.py "your task here" uv run ~/.claude/skills/codex/scripts/codex.py - <<'EOF'
Fix the bug in @app.js where the regex /\d+/ doesn't match "123"
The current code is:
const re = /\d+/;
if (re.test(input)) { ... }
Add proper escaping and handle $variables correctly.
EOF
``` ```
### Large Task Protocol ### Large Task Protocol
@@ -139,8 +173,8 @@ python3 ~/.claude/skills/codex/scripts/codex.py "your task here"
| ID | Description | Scope | Dependencies | Tests | Command | | ID | Description | Scope | Dependencies | Tests | Command |
| --- | --- | --- | --- | --- | --- | | --- | --- | --- | --- | --- | --- |
| T1 | Review @spec.md to extract requirements | docs/, @spec.md | None | None | uv run ~/.claude/skills/codex/scripts/codex.py "analyze requirements @spec.md" | | T1 | Review @spec.md to extract requirements | docs/, @spec.md | None | None | `uv run ~/.claude/skills/codex/scripts/codex.py - <<'EOF'`<br/>`analyze requirements @spec.md`<br/>`EOF` |
| T2 | Implement the module and add test cases | src/module | T1 | npm test -- --runInBand | uv run ~/.claude/skills/codex/scripts/codex.py "implement and test @src/module" | | T2 | Implement the module and add test cases | src/module | T1 | npm test -- --runInBand | `uv run ~/.claude/skills/codex/scripts/codex.py - <<'EOF'`<br/>`implement and test @src/module`<br/>`EOF` |
## Notes ## Notes

View File

@@ -9,7 +9,9 @@ Codex CLI wrapper with cross-platform support and session management.
Usage: Usage:
New session: uv run codex.py "task" [workdir] New session: uv run codex.py "task" [workdir]
Stdin mode: uv run codex.py - [workdir]
Resume: uv run codex.py resume <session_id> "task" [workdir] Resume: uv run codex.py resume <session_id> "task" [workdir]
Resume stdin: uv run codex.py resume <session_id> - [workdir]
Alternative: python3 codex.py "task" Alternative: python3 codex.py "task"
Direct exec: ./codex.py "task" Direct exec: ./codex.py "task"
@@ -80,19 +82,23 @@ def parse_args():
if len(sys.argv) < 4: if len(sys.argv) < 4:
log_error('Resume mode requires: resume <session_id> <task>') log_error('Resume mode requires: resume <session_id> <task>')
sys.exit(1) sys.exit(1)
task_arg = sys.argv[3]
return { return {
'mode': 'resume', 'mode': 'resume',
'session_id': sys.argv[2], 'session_id': sys.argv[2],
'task': sys.argv[3], 'task': task_arg,
'workdir': sys.argv[4] if len(sys.argv) > 4 else DEFAULT_WORKDIR 'explicit_stdin': task_arg == '-',
} 'workdir': sys.argv[4] if len(sys.argv) > 4 else DEFAULT_WORKDIR,
else:
return {
'mode': 'new',
'task': sys.argv[1],
'workdir': sys.argv[2] if len(sys.argv) > 2 else DEFAULT_WORKDIR
} }
task_arg = sys.argv[1]
return {
'mode': 'new',
'task': task_arg,
'explicit_stdin': task_arg == '-',
'workdir': sys.argv[2] if len(sys.argv) > 2 else DEFAULT_WORKDIR,
}
def read_piped_task() -> Optional[str]: def read_piped_task() -> Optional[str]:
""" """
@@ -100,19 +106,10 @@ def read_piped_task() -> Optional[str]:
- 如果 stdin 是管道(非 tty且存在内容返回读取到的字符串 - 如果 stdin 是管道(非 tty且存在内容返回读取到的字符串
- 否则返回 None - 否则返回 None
""" """
import select
stdin = sys.stdin stdin = sys.stdin
if stdin is None or stdin.isatty(): if stdin is None or stdin.isatty():
log_info("Stdin is tty or None, skipping pipe read") log_info("Stdin is tty or None, skipping pipe read")
return None return None
# 使用 select 检查是否有数据可读0 秒超时,非阻塞)
readable, _, _ = select.select([stdin], [], [], 0)
if not readable:
log_info("No data available on stdin")
return None
log_info("Reading from stdin pipe...") log_info("Reading from stdin pipe...")
data = stdin.read() data = stdin.read()
if not data: if not data:
@@ -153,6 +150,7 @@ def build_codex_args(params: dict, target_arg: str) -> list:
if params['mode'] == 'resume': if params['mode'] == 'resume':
return [ return [
'codex', 'e', 'codex', 'e',
'-m', DEFAULT_MODEL,
'--skip-git-repo-check', '--skip-git-repo-check',
'--json', '--json',
'resume', 'resume',
@@ -276,16 +274,28 @@ def main():
timeout_sec = resolve_timeout() timeout_sec = resolve_timeout()
log_info(f"Timeout: {timeout_sec}s") log_info(f"Timeout: {timeout_sec}s")
piped_task = read_piped_task() explicit_stdin = params.get('explicit_stdin', False)
piped = piped_task is not None
task_text = piped_task if piped else params['task']
use_stdin = should_stream_via_stdin(task_text, piped) if explicit_stdin:
log_info("Explicit stdin mode: reading task from stdin")
task_text = sys.stdin.read()
if not task_text:
log_error("Explicit stdin mode requires task input from stdin")
sys.exit(1)
piped = not sys.stdin.isatty()
else:
piped_task = read_piped_task()
piped = piped_task is not None
task_text = piped_task if piped else params['task']
use_stdin = explicit_stdin or should_stream_via_stdin(task_text, piped)
if use_stdin: if use_stdin:
reasons = [] reasons = []
if piped: if piped:
reasons.append('piped input') reasons.append('piped input')
if explicit_stdin:
reasons.append('explicit "-"')
if '\n' in task_text: if '\n' in task_text:
reasons.append('newline') reasons.append('newline')
if '\\' in task_text: if '\\' in task_text: