mirror of
https://github.com/cexll/myclaude.git
synced 2026-02-11 03:23:50 +08:00
Compare commits
10 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
9dc3e8f43d | ||
|
|
e9faa0bc2d | ||
|
|
70caa8d7fc | ||
|
|
4f74d5afa1 | ||
|
|
7f61437eea | ||
|
|
ed604f6db7 | ||
|
|
fb66b52b68 | ||
|
|
05e32203ee | ||
|
|
1bf7dd9a83 | ||
|
|
19aa237d47 |
139
skills/codex/SKILL.md
Normal file
139
skills/codex/SKILL.md
Normal file
@@ -0,0 +1,139 @@
|
|||||||
|
---
|
||||||
|
name: codex
|
||||||
|
description: Execute Codex CLI for code analysis, refactoring, and automated code changes. Use when you need to delegate complex code tasks to Codex AI with file references (@syntax) and structured output.
|
||||||
|
---
|
||||||
|
|
||||||
|
# Codex CLI Integration
|
||||||
|
|
||||||
|
## Overview
|
||||||
|
|
||||||
|
Execute Codex CLI commands and parse structured JSON responses. Supports file references via `@` syntax, multiple models, and sandbox controls.
|
||||||
|
|
||||||
|
## When to Use
|
||||||
|
|
||||||
|
- Complex code analysis requiring deep understanding
|
||||||
|
- Large-scale refactoring across multiple files
|
||||||
|
- Automated code generation with safety controls
|
||||||
|
- Tasks requiring specialized reasoning models (o3, gpt-5)
|
||||||
|
|
||||||
|
## Usage
|
||||||
|
|
||||||
|
**推荐方式**(使用 uv run,自动管理 Python 环境):
|
||||||
|
```bash
|
||||||
|
uv run ~/.claude/skills/codex/scripts/codex.py "<task>" [model] [working_dir]
|
||||||
|
```
|
||||||
|
|
||||||
|
**备选方式**(直接执行或使用 Python):
|
||||||
|
```bash
|
||||||
|
~/.claude/skills/codex/scripts/codex.py "<task>" [model] [working_dir]
|
||||||
|
# 或
|
||||||
|
python3 ~/.claude/skills/codex/scripts/codex.py "<task>" [model] [working_dir]
|
||||||
|
```
|
||||||
|
|
||||||
|
恢复会话:
|
||||||
|
```bash
|
||||||
|
uv run ~/.claude/skills/codex/scripts/codex.py resume <session_id> "<task>" [model] [working_dir]
|
||||||
|
```
|
||||||
|
|
||||||
|
## 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
|
||||||
|
|
||||||
|
- `task` (required): Task description, supports `@file` references
|
||||||
|
- `model` (optional): Model to use (default: gpt-5.1-codex)
|
||||||
|
- `gpt-5.1-codex`: Default, optimized for code
|
||||||
|
- `gpt-5`: Fast general purpose
|
||||||
|
- `working_dir` (optional): Working directory (default: current)
|
||||||
|
|
||||||
|
### Return Format
|
||||||
|
|
||||||
|
Extracts `agent_message` from Codex JSON stream and appends session ID:
|
||||||
|
```
|
||||||
|
Agent response text here...
|
||||||
|
|
||||||
|
---
|
||||||
|
SESSION_ID: 019a7247-ac9d-71f3-89e2-a823dbd8fd14
|
||||||
|
```
|
||||||
|
|
||||||
|
Error format (stderr):
|
||||||
|
```
|
||||||
|
ERROR: Error message
|
||||||
|
```
|
||||||
|
|
||||||
|
### Invocation Pattern
|
||||||
|
|
||||||
|
When calling via Bash tool, always include the timeout parameter:
|
||||||
|
```
|
||||||
|
Bash tool parameters:
|
||||||
|
- command: uv run ~/.claude/skills/codex/scripts/codex.py "<task>" [model] [working_dir]
|
||||||
|
- timeout: 7200000
|
||||||
|
- description: <brief description of the task>
|
||||||
|
```
|
||||||
|
|
||||||
|
Alternatives:
|
||||||
|
```
|
||||||
|
# Direct execution (simplest)
|
||||||
|
- command: ~/.claude/skills/codex/scripts/codex.py "<task>" [model] [working_dir]
|
||||||
|
|
||||||
|
# Using python3
|
||||||
|
- command: python3 ~/.claude/skills/codex/scripts/codex.py "<task>" [model] [working_dir]
|
||||||
|
```
|
||||||
|
|
||||||
|
### Examples
|
||||||
|
|
||||||
|
**Basic code analysis:**
|
||||||
|
```bash
|
||||||
|
# Recommended: via uv run (auto-manages Python environment)
|
||||||
|
uv run ~/.claude/skills/codex/scripts/codex.py "explain @src/main.ts"
|
||||||
|
# timeout: 7200000
|
||||||
|
|
||||||
|
# Alternative: direct execution
|
||||||
|
~/.claude/skills/codex/scripts/codex.py "explain @src/main.ts"
|
||||||
|
```
|
||||||
|
|
||||||
|
**Refactoring with specific model:**
|
||||||
|
```bash
|
||||||
|
uv run ~/.claude/skills/codex/scripts/codex.py "refactor @src/utils for performance" "gpt-5"
|
||||||
|
# timeout: 7200000
|
||||||
|
```
|
||||||
|
|
||||||
|
**Multi-file analysis:**
|
||||||
|
```bash
|
||||||
|
uv run ~/.claude/skills/codex/scripts/codex.py "analyze @. and find security issues" "gpt-5-codex" "/path/to/project"
|
||||||
|
# timeout: 7200000
|
||||||
|
```
|
||||||
|
|
||||||
|
**Resume previous session:**
|
||||||
|
```bash
|
||||||
|
# First session
|
||||||
|
uv run ~/.claude/skills/codex/scripts/codex.py "add comments to @utils.js" "gpt-5-codex"
|
||||||
|
# Output includes: SESSION_ID: 019a7247-ac9d-71f3-89e2-a823dbd8fd14
|
||||||
|
|
||||||
|
# Continue the conversation
|
||||||
|
uv run ~/.claude/skills/codex/scripts/codex.py resume 019a7247-ac9d-71f3-89e2-a823dbd8fd14 "now add type hints"
|
||||||
|
# timeout: 7200000
|
||||||
|
```
|
||||||
|
|
||||||
|
**Using python3 directly (alternative):**
|
||||||
|
```bash
|
||||||
|
python3 ~/.claude/skills/codex/scripts/codex.py "your task here"
|
||||||
|
```
|
||||||
|
|
||||||
|
## Notes
|
||||||
|
|
||||||
|
- **Recommended**: Use `uv run` for automatic Python environment management (requires uv installed)
|
||||||
|
- **Alternative**: Direct execution `./codex.py` (uses system Python via shebang)
|
||||||
|
- Python implementation using standard library (zero dependencies)
|
||||||
|
- Cross-platform compatible (Windows/macOS/Linux)
|
||||||
|
- PEP 723 compliant (inline script metadata)
|
||||||
|
- Runs with `--dangerously-bypass-approvals-and-sandbox` for automation (new sessions only)
|
||||||
|
- Uses `--skip-git-repo-check` to work in any directory
|
||||||
|
- Streams progress, returns only final agent message
|
||||||
|
- Every execution returns a session ID for resuming conversations
|
||||||
|
- Requires Codex CLI installed and authenticated
|
||||||
236
skills/codex/scripts/codex.py
Executable file
236
skills/codex/scripts/codex.py
Executable file
@@ -0,0 +1,236 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
# /// script
|
||||||
|
# requires-python = ">=3.8"
|
||||||
|
# dependencies = []
|
||||||
|
# ///
|
||||||
|
"""
|
||||||
|
Codex CLI wrapper with cross-platform support and session management.
|
||||||
|
**FIXED**: Auto-detect long inputs and use stdin mode to avoid shell argument issues.
|
||||||
|
|
||||||
|
Usage:
|
||||||
|
New session: uv run codex.py "task" [model] [workdir]
|
||||||
|
Resume: uv run codex.py resume <session_id> "task" [model] [workdir]
|
||||||
|
Alternative: python3 codex.py "task"
|
||||||
|
Direct exec: ./codex.py "task"
|
||||||
|
"""
|
||||||
|
import subprocess
|
||||||
|
import json
|
||||||
|
import sys
|
||||||
|
import os
|
||||||
|
from typing import Optional
|
||||||
|
|
||||||
|
DEFAULT_MODEL = 'gpt-5.1-codex'
|
||||||
|
DEFAULT_WORKDIR = '.'
|
||||||
|
DEFAULT_TIMEOUT = 7200 # 2 hours in seconds
|
||||||
|
FORCE_KILL_DELAY = 5
|
||||||
|
STDIN_THRESHOLD = 800 # Auto-switch to stdin for prompts longer than 800 chars
|
||||||
|
|
||||||
|
|
||||||
|
def log_error(message: str):
|
||||||
|
"""输出错误信息到 stderr"""
|
||||||
|
sys.stderr.write(f"ERROR: {message}\n")
|
||||||
|
|
||||||
|
|
||||||
|
def log_warn(message: str):
|
||||||
|
"""输出警告信息到 stderr"""
|
||||||
|
sys.stderr.write(f"WARN: {message}\n")
|
||||||
|
|
||||||
|
|
||||||
|
def resolve_timeout() -> int:
|
||||||
|
"""解析超时配置(秒)"""
|
||||||
|
raw = os.environ.get('CODEX_TIMEOUT', '')
|
||||||
|
if not raw:
|
||||||
|
return DEFAULT_TIMEOUT
|
||||||
|
|
||||||
|
try:
|
||||||
|
parsed = int(raw)
|
||||||
|
if parsed <= 0:
|
||||||
|
log_warn(f"Invalid CODEX_TIMEOUT '{raw}', falling back to {DEFAULT_TIMEOUT}s")
|
||||||
|
return DEFAULT_TIMEOUT
|
||||||
|
# 环境变量是毫秒,转换为秒
|
||||||
|
return parsed // 1000 if parsed > 10000 else parsed
|
||||||
|
except ValueError:
|
||||||
|
log_warn(f"Invalid CODEX_TIMEOUT '{raw}', falling back to {DEFAULT_TIMEOUT}s")
|
||||||
|
return DEFAULT_TIMEOUT
|
||||||
|
|
||||||
|
|
||||||
|
def normalize_text(text) -> Optional[str]:
|
||||||
|
"""规范化文本:字符串或字符串数组"""
|
||||||
|
if isinstance(text, str):
|
||||||
|
return text
|
||||||
|
if isinstance(text, list):
|
||||||
|
return ''.join(text)
|
||||||
|
return None
|
||||||
|
|
||||||
|
|
||||||
|
def parse_args():
|
||||||
|
"""解析命令行参数"""
|
||||||
|
if len(sys.argv) < 2:
|
||||||
|
log_error('Task required')
|
||||||
|
sys.exit(1)
|
||||||
|
|
||||||
|
# 检测是否为 resume 模式
|
||||||
|
if sys.argv[1] == 'resume':
|
||||||
|
if len(sys.argv) < 4:
|
||||||
|
log_error('Resume mode requires: resume <session_id> <task>')
|
||||||
|
sys.exit(1)
|
||||||
|
return {
|
||||||
|
'mode': 'resume',
|
||||||
|
'session_id': sys.argv[2],
|
||||||
|
'task': sys.argv[3],
|
||||||
|
'model': sys.argv[4] if len(sys.argv) > 4 else DEFAULT_MODEL,
|
||||||
|
'workdir': sys.argv[5] if len(sys.argv) > 5 else DEFAULT_WORKDIR
|
||||||
|
}
|
||||||
|
else:
|
||||||
|
return {
|
||||||
|
'mode': 'new',
|
||||||
|
'task': sys.argv[1],
|
||||||
|
'model': sys.argv[2] if len(sys.argv) > 2 else DEFAULT_MODEL,
|
||||||
|
'workdir': sys.argv[3] if len(sys.argv) > 3 else DEFAULT_WORKDIR
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
def build_codex_args(params: dict, use_stdin: bool) -> list:
|
||||||
|
"""
|
||||||
|
构建 codex CLI 参数
|
||||||
|
|
||||||
|
Args:
|
||||||
|
params: 参数字典
|
||||||
|
use_stdin: 是否使用 stdin 模式(不在命令行参数中传递 task)
|
||||||
|
"""
|
||||||
|
if params['mode'] == 'resume':
|
||||||
|
if use_stdin:
|
||||||
|
return [
|
||||||
|
'codex', 'e',
|
||||||
|
'--skip-git-repo-check',
|
||||||
|
'--json',
|
||||||
|
'resume',
|
||||||
|
params['session_id'],
|
||||||
|
'-' # 从 stdin 读取
|
||||||
|
]
|
||||||
|
else:
|
||||||
|
return [
|
||||||
|
'codex', 'e',
|
||||||
|
'--skip-git-repo-check',
|
||||||
|
'--json',
|
||||||
|
'resume',
|
||||||
|
params['session_id'],
|
||||||
|
params['task']
|
||||||
|
]
|
||||||
|
else:
|
||||||
|
base_args = [
|
||||||
|
'codex', 'e',
|
||||||
|
'-m', params['model'],
|
||||||
|
'--dangerously-bypass-approvals-and-sandbox',
|
||||||
|
'--skip-git-repo-check',
|
||||||
|
'-C', params['workdir'],
|
||||||
|
'--json'
|
||||||
|
]
|
||||||
|
|
||||||
|
if use_stdin:
|
||||||
|
base_args.append('-') # 从 stdin 读取
|
||||||
|
else:
|
||||||
|
base_args.append(params['task'])
|
||||||
|
|
||||||
|
return base_args
|
||||||
|
|
||||||
|
|
||||||
|
def main():
|
||||||
|
params = parse_args()
|
||||||
|
timeout_sec = resolve_timeout()
|
||||||
|
|
||||||
|
# **FIX: Auto-detect long inputs and enable stdin mode**
|
||||||
|
task_length = len(params['task'])
|
||||||
|
use_stdin = task_length > STDIN_THRESHOLD
|
||||||
|
|
||||||
|
if use_stdin:
|
||||||
|
log_warn(f"Task length ({task_length} chars) exceeds threshold, using stdin mode to avoid shell escaping issues")
|
||||||
|
|
||||||
|
codex_args = build_codex_args(params, use_stdin)
|
||||||
|
|
||||||
|
thread_id: Optional[str] = None
|
||||||
|
last_agent_message: Optional[str] = None
|
||||||
|
|
||||||
|
try:
|
||||||
|
# 启动 codex 子进程
|
||||||
|
process = subprocess.Popen(
|
||||||
|
codex_args,
|
||||||
|
stdin=subprocess.PIPE if use_stdin else None, # **FIX: Enable stdin**
|
||||||
|
stdout=subprocess.PIPE,
|
||||||
|
stderr=sys.stderr, # 错误直接透传到 stderr
|
||||||
|
text=True,
|
||||||
|
bufsize=1 # 行缓冲
|
||||||
|
)
|
||||||
|
|
||||||
|
# **FIX: 如果使用 stdin 模式,写入任务到 stdin**
|
||||||
|
if use_stdin:
|
||||||
|
process.stdin.write(params['task'])
|
||||||
|
process.stdin.close()
|
||||||
|
|
||||||
|
# 逐行解析 JSON 输出
|
||||||
|
for line in process.stdout:
|
||||||
|
line = line.strip()
|
||||||
|
if not line:
|
||||||
|
continue
|
||||||
|
|
||||||
|
try:
|
||||||
|
event = json.loads(line)
|
||||||
|
|
||||||
|
# 捕获 thread_id
|
||||||
|
if event.get('type') == 'thread.started':
|
||||||
|
thread_id = event.get('thread_id')
|
||||||
|
|
||||||
|
# 捕获 agent_message
|
||||||
|
if (event.get('type') == 'item.completed' and
|
||||||
|
event.get('item', {}).get('type') == 'agent_message'):
|
||||||
|
text = normalize_text(event['item'].get('text'))
|
||||||
|
if text:
|
||||||
|
last_agent_message = text
|
||||||
|
|
||||||
|
except json.JSONDecodeError:
|
||||||
|
log_warn(f"Failed to parse line: {line}")
|
||||||
|
|
||||||
|
# 等待进程结束
|
||||||
|
returncode = process.wait(timeout=timeout_sec)
|
||||||
|
|
||||||
|
if returncode == 0:
|
||||||
|
if last_agent_message:
|
||||||
|
# 输出 agent_message
|
||||||
|
sys.stdout.write(f"{last_agent_message}\n")
|
||||||
|
|
||||||
|
# 输出 session_id(如果存在)
|
||||||
|
if thread_id:
|
||||||
|
sys.stdout.write(f"\n---\nSESSION_ID: {thread_id}\n")
|
||||||
|
|
||||||
|
sys.exit(0)
|
||||||
|
else:
|
||||||
|
log_error('Codex completed without agent_message output')
|
||||||
|
sys.exit(1)
|
||||||
|
else:
|
||||||
|
log_error(f'Codex exited with status {returncode}')
|
||||||
|
sys.exit(returncode)
|
||||||
|
|
||||||
|
except subprocess.TimeoutExpired:
|
||||||
|
log_error('Codex execution timeout')
|
||||||
|
process.kill()
|
||||||
|
try:
|
||||||
|
process.wait(timeout=FORCE_KILL_DELAY)
|
||||||
|
except subprocess.TimeoutExpired:
|
||||||
|
pass
|
||||||
|
sys.exit(124)
|
||||||
|
|
||||||
|
except FileNotFoundError:
|
||||||
|
log_error("codex command not found in PATH")
|
||||||
|
sys.exit(127)
|
||||||
|
|
||||||
|
except KeyboardInterrupt:
|
||||||
|
process.terminate()
|
||||||
|
try:
|
||||||
|
process.wait(timeout=FORCE_KILL_DELAY)
|
||||||
|
except subprocess.TimeoutExpired:
|
||||||
|
process.kill()
|
||||||
|
sys.exit(130)
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
main()
|
||||||
Reference in New Issue
Block a user