optimize codex skills

This commit is contained in:
swe-agent[bot]
2025-11-11 16:47:04 +08:00
parent 19aa237d47
commit 05e32203ee
3 changed files with 206 additions and 22 deletions

View File

@@ -18,11 +18,18 @@ Execute Codex CLI commands and parse structured JSON responses. Supports file re
## Usage
通过 Bash tool 调用:
```bash
bash scripts/codex.sh "<task>" [model] [working_dir]
node ~/.claude/skills/codex/scripts/codex.js "<task>" [model] [working_dir]
```
**Timeout**: Set `timeout: 7200000` (2 hours) in Bash tool for long tasks.
## Timeout Control
- **Built-in**: Script enforces 2-hour timeout by default
- **Override**: Set `CODEX_TIMEOUT` environment variable (in milliseconds, e.g., `CODEX_TIMEOUT=3600000` for 1 hour)
- **Behavior**: On timeout, sends SIGTERM, then SIGKILL after 5s if process doesn't exit
- **Exit code**: Returns 124 on timeout (consistent with GNU timeout)
- **Bash tool**: Always set `timeout: 7200000` parameter for double protection
### Parameters
@@ -47,26 +54,41 @@ Error format:
ERROR: Error message
```
### Invocation Pattern
When calling via Bash tool, always include the timeout parameter:
```
Bash tool parameters:
- command: node ~/.claude/skills/codex/scripts/codex.js "<task>" [model] [working_dir]
- timeout: 7200000
- description: <brief description of the task>
```
### Examples
**Basic code analysis:**
```bash
bash scripts/codex.sh "explain @src/main.ts"
# Via Bash tool with timeout parameter
node ~/.claude/skills/codex/scripts/codex.js "explain @src/main.ts"
# timeout: 7200000
```
**Refactoring with specific model:**
```bash
bash scripts/codex.sh "refactor @src/utils for performance" "gpt-5"
node ~/.claude/skills/codex/scripts/codex.js "refactor @src/utils for performance" "gpt-5"
# timeout: 7200000
```
**Multi-file analysis:**
```bash
bash scripts/codex.sh "analyze @. and find security issues" "gpt-5-codex" "/path/to/project"
node ~/.claude/skills/codex/scripts/codex.js "analyze @. and find security issues" "gpt-5-codex" "/path/to/project"
# timeout: 7200000
```
**Quick task:**
```bash
bash scripts/codex.sh "add comments to @utils.js" "gpt-5-codex"
node ~/.claude/skills/codex/scripts/codex.js "add comments to @utils.js" "gpt-5-codex"
# timeout: 7200000
```
## Notes
@@ -75,4 +97,3 @@ bash scripts/codex.sh "add comments to @utils.js" "gpt-5-codex"
- Uses `--skip-git-repo-check` to work in any directory
- Streams progress, returns only final agent message
- Requires Codex CLI installed and authenticated
- Use `timeout: 7200000` (2 hours) for complex tasks that may take longer

178
skills/codex/scripts/codex.js Executable file
View File

@@ -0,0 +1,178 @@
#!/usr/bin/env node
import { spawn } from 'node:child_process';
const DEFAULT_MODEL = 'gpt-5-codex';
const DEFAULT_WORKDIR = '.';
const DEFAULT_TIMEOUT_MS = 7_200_000; // 2 hours
const FORCE_KILL_DELAY_MS = 5_000;
const args = process.argv.slice(2);
const [task, modelArg, workdirArg] = args;
const logError = (message) => {
process.stderr.write(`ERROR: ${message}\n`);
};
const logWarn = (message) => {
process.stderr.write(`WARN: ${message}\n`);
};
if (!task) {
logError('Task required');
process.exit(1);
}
const model = modelArg || DEFAULT_MODEL;
const workdir = workdirArg || DEFAULT_WORKDIR;
const resolveTimeout = () => {
const raw = process.env.CODEX_TIMEOUT;
if (raw == null || raw === '') {
return DEFAULT_TIMEOUT_MS;
}
const parsed = Number(raw);
if (!Number.isFinite(parsed) || parsed <= 0) {
logWarn(`Invalid CODEX_TIMEOUT "${raw}", falling back to ${DEFAULT_TIMEOUT_MS}ms`);
return DEFAULT_TIMEOUT_MS;
}
return parsed;
};
const codexArgs = [
'e',
'-m',
model,
'--dangerously-bypass-approvals-and-sandbox',
'--skip-git-repo-check',
'-C',
workdir,
'--json',
task,
];
const child = spawn('codex', codexArgs, {
stdio: ['ignore', 'pipe', 'inherit'],
});
let timedOut = false;
let lastAgentMessage = null;
let stdoutBuffer = '';
let forceKillTimer = null;
const timeoutMs = resolveTimeout();
const forceTerminate = () => {
if (!child.killed) {
child.kill('SIGTERM');
forceKillTimer = setTimeout(() => {
if (!child.killed) {
child.kill('SIGKILL');
}
}, FORCE_KILL_DELAY_MS);
}
};
const timeoutHandle = setTimeout(() => {
timedOut = true;
logError('Codex execution timeout');
forceTerminate();
}, timeoutMs);
const normalizeText = (text) => {
if (typeof text === 'string') {
return text;
}
if (Array.isArray(text)) {
return text.join('');
}
return null;
};
const handleJsonLine = (line) => {
const trimmed = line.trim();
if (!trimmed) {
return;
}
let event;
try {
event = JSON.parse(trimmed);
} catch (err) {
logWarn(`Failed to parse Codex output line: ${trimmed}`);
return;
}
if (
event &&
event.type === 'item.completed' &&
event.item &&
event.item.type === 'agent_message'
) {
const text = normalizeText(event.item.text);
if (text != null) {
lastAgentMessage = text;
}
}
};
child.stdout.on('data', (chunk) => {
stdoutBuffer += chunk.toString('utf8');
let newlineIndex = stdoutBuffer.indexOf('\n');
while (newlineIndex !== -1) {
const line = stdoutBuffer.slice(0, newlineIndex);
stdoutBuffer = stdoutBuffer.slice(newlineIndex + 1);
handleJsonLine(line);
newlineIndex = stdoutBuffer.indexOf('\n');
}
});
child.stdout.on('end', () => {
if (stdoutBuffer) {
handleJsonLine(stdoutBuffer);
stdoutBuffer = '';
}
});
child.on('error', (err) => {
clearTimeout(timeoutHandle);
if (forceKillTimer) {
clearTimeout(forceKillTimer);
}
logError(`Failed to start Codex CLI: ${err.message}`);
process.exit(1);
});
child.on('close', (code, signal) => {
clearTimeout(timeoutHandle);
if (forceKillTimer) {
clearTimeout(forceKillTimer);
}
if (timedOut) {
process.exit(124);
return;
}
if (code === 0) {
if (lastAgentMessage != null) {
process.stdout.write(`${lastAgentMessage}\n`);
process.exit(0);
} else {
logError('Codex completed without an agent_message output');
process.exit(1);
}
return;
}
if (signal) {
logError(`Codex terminated with signal ${signal}`);
process.exit(code ?? 1);
return;
}
logError(`Codex exited with status ${code}`);
process.exit(code ?? 1);
});

View File

@@ -1,15 +0,0 @@
#!/bin/bash
set -euo pipefail
TASK="${1:-}"
MODEL="${2:-gpt-5-codex}"
WORKDIR="${3:-.}"
if [ -z "$TASK" ]; then
echo "ERROR: Task required" >&2
exit 1
fi
codex e -m "$MODEL" --dangerously-bypass-approvals-and-sandbox --skip-git-repo-check -C "$WORKDIR" --json "$TASK" 2>/dev/null | \
jq -r 'select(.type == "item.completed" and .item.type == "agent_message") | .item.text' | \
tail -n 1