mirror of
https://github.com/cexll/myclaude.git
synced 2026-02-14 03:31:58 +08:00
optimize codex skills
This commit is contained in:
@@ -18,11 +18,18 @@ Execute Codex CLI commands and parse structured JSON responses. Supports file re
|
|||||||
|
|
||||||
## Usage
|
## Usage
|
||||||
|
|
||||||
|
通过 Bash tool 调用:
|
||||||
```bash
|
```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
|
### Parameters
|
||||||
|
|
||||||
@@ -47,26 +54,41 @@ Error format:
|
|||||||
ERROR: Error message
|
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
|
### Examples
|
||||||
|
|
||||||
**Basic code analysis:**
|
**Basic code analysis:**
|
||||||
```bash
|
```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:**
|
**Refactoring with specific model:**
|
||||||
```bash
|
```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:**
|
**Multi-file analysis:**
|
||||||
```bash
|
```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:**
|
**Quick task:**
|
||||||
```bash
|
```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
|
## 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
|
- Uses `--skip-git-repo-check` to work in any directory
|
||||||
- Streams progress, returns only final agent message
|
- Streams progress, returns only final agent message
|
||||||
- Requires Codex CLI installed and authenticated
|
- 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
178
skills/codex/scripts/codex.js
Executable 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);
|
||||||
|
});
|
||||||
@@ -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
|
|
||||||
Reference in New Issue
Block a user