skill-codex 0.2.0 → 0.7.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/server.ts","../src/tools/codex-exec.ts","../src/errors/errors.ts","../src/config/constants.ts","../src/guards/check-recursion.ts","../src/guards/check-binary.ts","../src/guards/check-auth.ts","../src/lock/lock-file.ts","../src/guards/check-lock.ts","../src/guards/check-git.ts","../src/guards/preflight.ts","../src/runner/exec-runner.ts","../src/util/platform.ts","../src/runner/timeout.ts","../src/util/truncate.ts","../src/runner/output-parser.ts","../src/runner/retry.ts","../src/index.ts"],"sourcesContent":["import { Server } from \"@modelcontextprotocol/sdk/server/index.js\";\nimport { StdioServerTransport } from \"@modelcontextprotocol/sdk/server/stdio.js\";\nimport {\n CallToolRequestSchema,\n ListToolsRequestSchema,\n} from \"@modelcontextprotocol/sdk/types.js\";\nimport { TOOL_NAME, TOOL_DESCRIPTION, inputSchema, handleCodexExec } from \"./tools/codex-exec.js\";\n\nexport function createServer(cwd: string): Server {\n const server = new Server(\n { name: \"skill-codex\", version: \"0.2.0\" },\n { capabilities: { tools: {} } },\n );\n\n server.setRequestHandler(ListToolsRequestSchema, async () => ({\n tools: [\n {\n name: TOOL_NAME,\n description: TOOL_DESCRIPTION,\n inputSchema: {\n type: \"object\" as const,\n properties: {\n prompt: { type: \"string\", description: \"The task description for Codex\" },\n mode: {\n type: \"string\",\n enum: [\"exec\", \"full-auto\"],\n default: \"exec\",\n description: \"exec = read-only, full-auto = can write files\",\n },\n cwd: { type: \"string\", description: \"Working directory (defaults to server cwd)\" },\n timeoutMs: { type: \"number\", description: \"Override default timeout in milliseconds\" },\n requireGit: {\n type: \"boolean\",\n default: false,\n description: \"Fail if not inside a git repository\",\n },\n },\n required: [\"prompt\"],\n },\n },\n ],\n }));\n\n server.setRequestHandler(CallToolRequestSchema, async (request) => {\n if (request.params.name !== TOOL_NAME) {\n return {\n content: [{ type: \"text\", text: `Unknown tool: ${request.params.name}` }],\n isError: true,\n };\n }\n\n const parsed = inputSchema.safeParse(request.params.arguments);\n if (!parsed.success) {\n return {\n content: [\n {\n type: \"text\",\n text: `Invalid input: ${parsed.error.issues.map((i) => i.message).join(\", \")}`,\n },\n ],\n isError: true,\n };\n }\n\n return handleCodexExec(parsed.data, cwd);\n });\n\n return server;\n}\n\nexport async function startServer(): Promise<void> {\n const cwd = process.cwd();\n const server = createServer(cwd);\n const transport = new StdioServerTransport();\n\n process.stderr.write(\"[skill-codex] MCP server starting...\\n\");\n\n await server.connect(transport);\n\n process.stderr.write(\"[skill-codex] MCP server connected via stdio\\n\");\n\n process.on(\"uncaughtException\", (err) => {\n process.stderr.write(`[skill-codex] Uncaught exception: ${err.message}\\n`);\n });\n\n process.on(\"unhandledRejection\", (reason) => {\n process.stderr.write(`[skill-codex] Unhandled rejection: ${String(reason)}\\n`);\n });\n}\n","import fs from \"node:fs\";\nimport path from \"node:path\";\nimport { z } from \"zod\";\nimport { runPreflight } from \"../guards/preflight.js\";\nimport { execCodex } from \"../runner/exec-runner.js\";\nimport { withRetry } from \"../runner/retry.js\";\nimport { BridgeError } from \"../errors/errors.js\";\n\nexport const TOOL_NAME = \"codex_exec\";\n\nexport const TOOL_DESCRIPTION =\n \"Execute a task using OpenAI Codex CLI. Use for code review, implementation tasks, or getting a second opinion. Codex output is a SUGGESTION — evaluate it critically before applying.\";\n\nexport const inputSchema = z.object({\n prompt: z.string().describe(\"The task description for Codex\"),\n mode: z\n .enum([\"exec\", \"full-auto\"])\n .default(\"exec\")\n .describe(\"exec = read-only with confirmation, full-auto = can write files\"),\n cwd: z.string().optional().describe(\"Working directory (defaults to server cwd)\"),\n timeoutMs: z.number().optional().describe(\"Override default timeout in milliseconds\"),\n requireGit: z.boolean().default(false).describe(\"Fail if not inside a git repository\"),\n});\n\nexport type CodexExecInput = z.infer<typeof inputSchema>;\n\nfunction formatError(err: unknown): string {\n if (err instanceof BridgeError) {\n return `[skill-codex error: ${err.code}] ${err.message}`;\n }\n if (err instanceof Error) {\n return `[skill-codex error] ${err.message}`;\n }\n return `[skill-codex error] Unknown error: ${String(err)}`;\n}\n\nexport async function handleCodexExec(\n input: CodexExecInput,\n serverCwd: string,\n): Promise<{ content: Array<{ type: \"text\"; text: string }>; isError?: boolean }> {\n const rawCwd = input.cwd ?? serverCwd;\n const cwd = path.resolve(rawCwd);\n\n // Validate cwd is an existing directory (prevent path traversal to arbitrary locations)\n if (!fs.existsSync(cwd) || !fs.statSync(cwd).isDirectory()) {\n return {\n content: [{ type: \"text\", text: `[skill-codex error: INVALID_CWD] cwd is not an existing directory: ${cwd}` }],\n isError: true,\n };\n }\n\n let lockRelease: (() => void) | null = null;\n\n try {\n const { lockHandle } = await runPreflight({\n cwd,\n requireGit: input.requireGit,\n });\n lockRelease = lockHandle?.release ?? null;\n\n const result = await withRetry(() =>\n execCodex({\n prompt: input.prompt,\n cwd,\n mode: input.mode,\n timeoutMs: input.timeoutMs,\n }),\n );\n\n return {\n content: [{ type: \"text\", text: result.content }],\n };\n } catch (err) {\n return {\n content: [{ type: \"text\", text: formatError(err) }],\n isError: true,\n };\n } finally {\n lockRelease?.();\n }\n}\n","export class BridgeError extends Error {\n readonly code: string;\n readonly retryable: boolean;\n\n constructor(message: string, code: string, retryable: boolean) {\n super(message);\n this.name = \"BridgeError\";\n this.code = code;\n this.retryable = retryable;\n }\n}\n\nexport class CliNotFoundError extends BridgeError {\n constructor(binary: string = \"codex\") {\n super(\n `${binary} CLI not found on PATH. Install it with: npm i -g @openai/codex`,\n \"CLI_NOT_FOUND\",\n false,\n );\n this.name = \"CliNotFoundError\";\n }\n}\n\nexport class AuthExpiredError extends BridgeError {\n constructor() {\n super(\n \"Codex authentication expired or not found. Run `codex login` to re-authenticate.\",\n \"AUTH_EXPIRED\",\n false,\n );\n this.name = \"AuthExpiredError\";\n }\n}\n\nexport class RecursionLimitError extends BridgeError {\n constructor(depth: number, max: number) {\n super(\n `Maximum bridge nesting depth reached (${depth} >= ${max}). This prevents infinite recursion between Claude and Codex.`,\n \"RECURSION_LIMIT\",\n false,\n );\n this.name = \"RecursionLimitError\";\n }\n}\n\nexport class LockConflictError extends BridgeError {\n constructor(pid: number) {\n super(\n `Another skill-codex instance is running (PID ${pid}). Wait for it to finish or delete the lock file.`,\n \"LOCK_CONFLICT\",\n false,\n );\n this.name = \"LockConflictError\";\n }\n}\n\nexport class TimeoutError extends BridgeError {\n constructor(timeoutMs: number) {\n super(\n `Codex timed out after ${Math.round(timeoutMs / 1000)}s. Increase SKILL_CODEX_TIMEOUT_MS if needed.`,\n \"TIMEOUT\",\n true,\n );\n this.name = \"TimeoutError\";\n }\n}\n\nexport class RateLimitError extends BridgeError {\n constructor() {\n super(\n \"Codex rate limited (429). Will retry with backoff.\",\n \"RATE_LIMIT\",\n true,\n );\n this.name = \"RateLimitError\";\n }\n}\n\nexport class ServerError extends BridgeError {\n constructor(detail: string = \"\") {\n super(\n `Codex server error${detail ? `: ${detail}` : \"\"}. Will retry.`,\n \"SERVER_ERROR\",\n true,\n );\n this.name = \"ServerError\";\n }\n}\n\nexport class NetworkError extends BridgeError {\n constructor(detail: string = \"\") {\n super(\n `Network error connecting to Codex${detail ? `: ${detail}` : \"\"}. Check your connection.`,\n \"NETWORK_ERROR\",\n true,\n );\n this.name = \"NetworkError\";\n }\n}\n\nexport class EmptyOutputError extends BridgeError {\n constructor() {\n super(\n \"Codex returned empty output. This may be a transient issue.\",\n \"EMPTY_OUTPUT\",\n true,\n );\n this.name = \"EmptyOutputError\";\n }\n}\n\nexport class NotGitRepoError extends BridgeError {\n constructor(cwd: string) {\n super(\n `Not a git repository: ${cwd}. This operation requires a git repo.`,\n \"NOT_GIT_REPO\",\n false,\n );\n this.name = \"NotGitRepoError\";\n }\n}\n\nexport class ParseError extends BridgeError {\n constructor(detail: string = \"\") {\n super(\n `Failed to parse Codex output${detail ? `: ${detail}` : \"\"}. The Codex CLI format may have changed — please update skill-codex.`,\n \"PARSE_ERROR\",\n false,\n );\n this.name = \"ParseError\";\n }\n}\n","export const MAX_BRIDGE_DEPTH = 2;\nexport const BRIDGE_DEPTH_ENV = \"SKILL_CODEX_DEPTH\";\n\nexport const DEFAULT_TIMEOUT_MS = 600_000; // 10 minutes\nexport const TIMEOUT_ENV = \"SKILL_CODEX_TIMEOUT_MS\";\nexport const KILL_GRACE_MS = 5_000;\n\nexport const MAX_RETRIES = 3;\nexport const MAX_RETRIES_ENV = \"SKILL_CODEX_MAX_RETRIES\";\nexport const RETRY_DELAYS_MS = [1_000, 2_000, 4_000];\nexport const RETRY_CAP_MS = 10_000;\n\nexport const MAX_RESPONSE_CHARS = 80_000;\n\nexport const LOCK_STALE_MS = 900_000; // 15 minutes\nexport const LOCK_FILENAME = \".skill-codex.lock\";\n\nexport const TRIVIAL_DIFF_THRESHOLD = 5; // lines\nexport const DOCS_ONLY_EXTENSIONS = [\".md\", \".txt\", \".rst\", \".adoc\"];\nexport const SECURITY_PATH_KEYWORDS = [\"security\", \"auth\", \"crypto\", \"password\", \"secret\", \"token\"];\nexport const FORCE_REVIEW_LINES = 100;\nexport const FORCE_REVIEW_FILES = 3;\n\nexport const CONFIG_ONLY_FILES = [\".gitignore\", \".eslintrc\", \".prettierrc\", \".editorconfig\"];\n\nexport const DEBUG_ENV = \"SKILL_CODEX_DEBUG\";\n\nexport const TRANSIENT_PATTERNS = [\n \"rate limit\", \"too many requests\", \"429\",\n \"500\", \"502\", \"503\", \"504\",\n \"internal server error\", \"bad gateway\", \"service unavailable\", \"gateway timeout\",\n \"connection reset\", \"connection refused\",\n \"econnreset\", \"econnrefused\", \"etimedout\",\n \"network error\", \"fetch failed\", \"socket hang up\",\n] as const;\n\nexport const AUTH_ERROR_PATTERNS = [\n \"api key\", \"authentication\", \"unauthorized\", \"401\", \"auth\",\n] as const;\n","import { BRIDGE_DEPTH_ENV, MAX_BRIDGE_DEPTH } from \"../config/constants.js\";\nimport { RecursionLimitError } from \"../errors/errors.js\";\n\nexport function getCurrentDepth(): number {\n return parseInt(process.env[BRIDGE_DEPTH_ENV] ?? \"0\", 10);\n}\n\nexport function getNextDepth(): number {\n return getCurrentDepth() + 1;\n}\n\nexport function checkRecursion(): void {\n const depth = getCurrentDepth();\n if (depth >= MAX_BRIDGE_DEPTH) {\n throw new RecursionLimitError(depth, MAX_BRIDGE_DEPTH);\n }\n}\n","import which from \"which\";\nimport { CliNotFoundError } from \"../errors/errors.js\";\n\nexport interface BinaryCheckResult {\n readonly found: boolean;\n readonly path: string;\n}\n\nlet cachedBinaryPath: string | null = null;\n\nexport function getCachedBinaryPath(): string | null {\n return cachedBinaryPath;\n}\n\nexport function resetBinaryCache(): void {\n cachedBinaryPath = null;\n}\n\nexport async function checkBinary(\n binary: string = \"codex\",\n): Promise<BinaryCheckResult> {\n if (cachedBinaryPath !== null) {\n return { found: true, path: cachedBinaryPath };\n }\n\n try {\n const resolved = await which(binary);\n cachedBinaryPath = resolved;\n return { found: true, path: resolved };\n } catch {\n throw new CliNotFoundError(binary);\n }\n}\n","import { execFile } from \"node:child_process\";\nimport { AuthExpiredError, NetworkError, CliNotFoundError } from \"../errors/errors.js\";\nimport { getCachedBinaryPath } from \"./check-binary.js\";\n\nconst AUTH_CACHE_TTL_MS = 60_000;\n\nlet authCachedAt: number | null = null;\n\nexport function resetAuthCache(): void {\n authCachedAt = null;\n}\n\nexport async function checkAuth(): Promise<void> {\n const now = Date.now();\n\n if (authCachedAt !== null && now - authCachedAt < AUTH_CACHE_TTL_MS) {\n return;\n }\n\n const binary = getCachedBinaryPath() ?? \"codex\";\n\n return new Promise((resolve, reject) => {\n const child = execFile(\n binary,\n [\"exec\", \"--sandbox\", \"read-only\", \"--skip-git-repo-check\", \"--ephemeral\", \"echo ok\"],\n { timeout: 15_000 },\n (error, _stdout, stderr) => {\n if (!error) {\n authCachedAt = Date.now();\n resolve();\n return;\n }\n\n const lower = (stderr ?? error.message ?? \"\").toLowerCase();\n\n // Distinguish error types instead of masking everything as auth\n if (error.code === \"ENOENT\" || (error as NodeJS.ErrnoException).code === \"ENOENT\") {\n reject(new CliNotFoundError());\n return;\n }\n\n if (error.killed) {\n reject(new NetworkError(\"Auth check timed out — check your network connection\"));\n return;\n }\n\n if ([\"econnrefused\", \"econnreset\", \"etimedout\", \"network error\", \"fetch failed\"].some((p) => lower.includes(p))) {\n reject(new NetworkError(\"Network error during auth check\"));\n return;\n }\n\n // Default: treat as auth issue\n reject(new AuthExpiredError());\n },\n );\n child.stdin?.end();\n });\n}\n","import fs from \"node:fs\";\nimport path from \"node:path\";\nimport os from \"node:os\";\nimport { LOCK_FILENAME, LOCK_STALE_MS } from \"../config/constants.js\";\nimport { LockConflictError } from \"../errors/errors.js\";\n\ninterface LockData {\n readonly pid: number;\n readonly timestamp: number;\n readonly hostname: string;\n}\n\nexport interface LockHandle {\n readonly release: () => void;\n}\n\nfunction getLockPath(cwd: string): string {\n return path.join(cwd, LOCK_FILENAME);\n}\n\nfunction isProcessAlive(pid: number): boolean {\n try {\n process.kill(pid, 0);\n return true;\n } catch {\n return false;\n }\n}\n\nfunction isLockStale(data: LockData): boolean {\n const age = Date.now() - data.timestamp;\n if (age > LOCK_STALE_MS) return true;\n if (!isProcessAlive(data.pid)) return true;\n return false;\n}\n\nfunction tryRemoveStaleLock(lockPath: string): boolean {\n try {\n const raw = fs.readFileSync(lockPath, \"utf-8\");\n const data: LockData = JSON.parse(raw);\n if (isLockStale(data)) {\n fs.unlinkSync(lockPath);\n return true;\n }\n throw new LockConflictError(data.pid);\n } catch (err) {\n if (err instanceof LockConflictError) throw err;\n // File disappeared or is unreadable — try to acquire\n try { fs.unlinkSync(lockPath); } catch { /* ignore */ }\n return true;\n }\n}\n\nexport function acquireLock(cwd: string): LockHandle {\n const lockPath = getLockPath(cwd);\n const lockData: LockData = {\n pid: process.pid,\n timestamp: Date.now(),\n hostname: os.hostname(),\n };\n const content = JSON.stringify(lockData, null, 2);\n\n try {\n fs.writeFileSync(lockPath, content, { flag: \"wx\" });\n } catch (err: unknown) {\n const fsErr = err as NodeJS.ErrnoException;\n if (fsErr.code === \"EEXIST\") {\n tryRemoveStaleLock(lockPath);\n // Retry once after stale removal — wrap in try/catch for TOCTOU race\n try {\n fs.writeFileSync(lockPath, content, { flag: \"wx\" });\n } catch (retryErr: unknown) {\n const retryFsErr = retryErr as NodeJS.ErrnoException;\n if (retryFsErr.code === \"EEXIST\") {\n // Another process won the race\n throw new LockConflictError(0);\n }\n throw retryErr;\n }\n } else {\n throw err;\n }\n }\n\n // Signal handlers — stored for removal in release()\n const onExit = (): void => {\n try { fs.unlinkSync(lockPath); } catch { /* ignore */ }\n };\n\n process.on(\"exit\", onExit);\n process.on(\"SIGINT\", onExit);\n process.on(\"SIGTERM\", onExit);\n\n const release = (): void => {\n // Remove signal handlers first to prevent double-cleanup\n process.removeListener(\"exit\", onExit);\n process.removeListener(\"SIGINT\", onExit);\n process.removeListener(\"SIGTERM\", onExit);\n try { fs.unlinkSync(lockPath); } catch { /* ignore */ }\n };\n\n return { release };\n}\n","import { acquireLock, type LockHandle } from \"../lock/lock-file.js\";\n\nexport function checkLock(cwd: string): LockHandle {\n return acquireLock(cwd);\n}\n","import { execFileSync } from \"node:child_process\";\n\nexport interface GitCheckResult {\n readonly isGitRepo: boolean;\n}\n\nexport function checkGit(cwd: string): GitCheckResult {\n try {\n execFileSync(\"git\", [\"rev-parse\", \"--is-inside-work-tree\"], {\n cwd,\n stdio: \"pipe\",\n timeout: 5_000,\n });\n return { isGitRepo: true };\n } catch {\n return { isGitRepo: false };\n }\n}\n","import type { LockHandle } from \"../lock/lock-file.js\";\nimport { NotGitRepoError } from \"../errors/errors.js\";\nimport { checkRecursion } from \"./check-recursion.js\";\nimport { checkBinary } from \"./check-binary.js\";\nimport { checkAuth } from \"./check-auth.js\";\nimport { checkLock } from \"./check-lock.js\";\nimport { checkGit } from \"./check-git.js\";\n\nexport interface PreflightOptions {\n readonly cwd: string;\n readonly requireGit: boolean;\n readonly skipAuth?: boolean;\n readonly skipLock?: boolean;\n}\n\nexport interface PreflightResult {\n readonly lockHandle: LockHandle | null;\n}\n\nexport async function runPreflight(\n options: PreflightOptions,\n): Promise<PreflightResult> {\n // Order: cheapest checks first (fail-fast)\n // 1. Recursion (env read — instant)\n checkRecursion();\n\n // 2. Binary exists (filesystem lookup)\n await checkBinary();\n\n // 3. Auth valid (spawns a quick process)\n if (!options.skipAuth) {\n await checkAuth();\n }\n\n // 4. Lock file (filesystem write)\n let lockHandle: LockHandle | null = null;\n if (!options.skipLock) {\n lockHandle = checkLock(options.cwd);\n }\n\n // 5. Git repo (if required)\n if (options.requireGit) {\n const { isGitRepo } = checkGit(options.cwd);\n if (!isGitRepo) {\n lockHandle?.release();\n throw new NotGitRepoError(options.cwd);\n }\n }\n\n return { lockHandle };\n}\n","import { spawn } from \"node:child_process\";\nimport { BRIDGE_DEPTH_ENV, DEFAULT_TIMEOUT_MS, TIMEOUT_ENV } from \"../config/constants.js\";\nimport { getNextDepth } from \"../guards/check-recursion.js\";\nimport { getCachedBinaryPath } from \"../guards/check-binary.js\";\nimport { setupTimeout } from \"./timeout.js\";\nimport { parseCodexOutput, type CodexResult } from \"./output-parser.js\";\nimport {\n BridgeError,\n CliNotFoundError,\n AuthExpiredError,\n RateLimitError,\n ServerError,\n NetworkError,\n} from \"../errors/errors.js\";\n\nexport interface ExecParams {\n readonly prompt: string;\n readonly cwd: string;\n readonly mode: \"exec\" | \"full-auto\";\n readonly timeoutMs?: number;\n}\n\nfunction getTimeout(override?: number): number {\n if (override !== undefined) return override;\n const envVal = process.env[TIMEOUT_ENV];\n if (envVal) {\n const parsed = parseInt(envVal, 10);\n if (!isNaN(parsed) && parsed > 0) return parsed;\n }\n return DEFAULT_TIMEOUT_MS;\n}\n\nfunction classifyError(exitCode: number, stderr: string): BridgeError {\n const lower = stderr.toLowerCase();\n\n // Check auth errors first but exclude generic \"auth\" in context\n if (lower.includes(\"unauthorized\") || lower.includes(\"401\") || lower.includes(\"api key\")) {\n return new AuthExpiredError();\n }\n if (lower.includes(\"rate limit\") || lower.includes(\"429\") || lower.includes(\"too many requests\")) {\n return new RateLimitError();\n }\n if ([\"500\", \"502\", \"503\", \"504\", \"internal server error\", \"bad gateway\", \"service unavailable\"].some((p) => lower.includes(p))) {\n return new ServerError(stderr.slice(0, 200));\n }\n if ([\"econnreset\", \"econnrefused\", \"etimedout\", \"network error\", \"fetch failed\", \"socket hang up\"].some((p) => lower.includes(p))) {\n return new NetworkError(stderr.slice(0, 200));\n }\n\n return new BridgeError(\n `Codex exited with code ${exitCode}: ${stderr.slice(0, 300)}`,\n \"EXEC_FAILED\",\n false,\n );\n}\n\nexport async function execCodex(params: ExecParams): Promise<CodexResult> {\n // Use cached binary path from checkBinary — throws if not found\n const codexPath = getCachedBinaryPath();\n if (codexPath === null) {\n throw new CliNotFoundError();\n }\n\n return new Promise((resolve, reject) => {\n const timeoutMs = getTimeout(params.timeoutMs);\n const args: string[] = [\"exec\", \"--json\", \"--skip-git-repo-check\"];\n\n if (params.mode === \"full-auto\") {\n args.push(\"--full-auto\");\n } else {\n args.push(\"--sandbox\", \"read-only\");\n }\n\n // Prompt passed via stdin to avoid shell injection — NOT as a positional arg\n const stdinPrompt = params.prompt;\n\n // Use \"-\" to tell codex to read prompt from stdin\n args.push(\"-\");\n\n const env = {\n ...process.env,\n [BRIDGE_DEPTH_ENV]: String(getNextDepth()),\n };\n\n // shell: false — codexPath is the resolved absolute path, no shell resolution needed\n const child = spawn(codexPath, args, {\n cwd: params.cwd,\n env,\n stdio: [\"pipe\", \"pipe\", \"pipe\"],\n shell: false,\n windowsHide: true,\n });\n\n const { clear: clearTimeout_, promise: timeoutPromise } = setupTimeout(child, timeoutMs);\n\n const stdoutChunks: Buffer[] = [];\n let stderr = \"\";\n\n child.stdout?.on(\"data\", (chunk: Buffer) => {\n stdoutChunks.push(chunk);\n });\n\n child.stderr?.on(\"data\", (chunk: Buffer) => {\n stderr += chunk.toString();\n });\n\n // Write prompt via stdin then close — safe from shell injection\n child.stdin?.write(stdinPrompt);\n child.stdin?.end();\n\n const onClose = (exitCode: number | null): void => {\n clearTimeout_();\n const stdout = Buffer.concat(stdoutChunks).toString();\n\n if (exitCode === 0 || exitCode === null) {\n try {\n const result = parseCodexOutput(stdout);\n resolve(result);\n } catch (err) {\n reject(err);\n }\n return;\n }\n\n reject(classifyError(exitCode, stderr));\n };\n\n child.on(\"close\", onClose);\n\n child.on(\"error\", (err: NodeJS.ErrnoException) => {\n clearTimeout_();\n if (err.code === \"ENOENT\") {\n reject(new CliNotFoundError());\n } else {\n reject(new BridgeError(`Failed to spawn codex: ${err.message}`, \"SPAWN_ERROR\", false));\n }\n });\n\n // Race with timeout\n timeoutPromise.catch((err) => {\n reject(err);\n });\n });\n}\n","import os from \"node:os\";\nimport path from \"node:path\";\n\nexport type Platform = \"win32\" | \"darwin\" | \"linux\";\n\nexport function getPlatform(): Platform {\n const p = os.platform();\n if (p === \"win32\" || p === \"darwin\" || p === \"linux\") return p;\n return \"linux\"; // default fallback for other unix-like\n}\n\nexport function isWindows(): boolean {\n return getPlatform() === \"win32\";\n}\n\nexport function normalizePath(p: string): string {\n return p.replace(/\\\\/g, \"/\");\n}\n\nexport function getHomeDir(): string {\n return os.homedir();\n}\n\nexport function getClaudeDir(): string {\n return path.join(getHomeDir(), \".claude\");\n}\n\nexport function getTempDir(): string {\n return os.tmpdir();\n}\n","import type { ChildProcess } from \"node:child_process\";\nimport { KILL_GRACE_MS } from \"../config/constants.js\";\nimport { TimeoutError } from \"../errors/errors.js\";\nimport { isWindows } from \"../util/platform.js\";\n\nexport function setupTimeout(\n child: ChildProcess,\n timeoutMs: number,\n): { clear: () => void; promise: Promise<never> } {\n let timer: ReturnType<typeof setTimeout> | null = null;\n let graceTimer: ReturnType<typeof setTimeout> | null = null;\n\n const promise = new Promise<never>((_resolve, reject) => {\n timer = setTimeout(() => {\n if (isWindows()) {\n // On Windows, SIGTERM is silently ignored by Node.js — it maps to\n // TerminateProcess regardless of signal name. Call kill() with no\n // argument so the intent is explicit and no grace timer is needed.\n child.kill();\n } else {\n // Phase 1: graceful kill\n child.kill(\"SIGTERM\");\n\n // Phase 2: force kill after grace period\n graceTimer = setTimeout(() => {\n try {\n if (!child.killed) {\n child.kill(\"SIGKILL\");\n }\n } catch {\n // Process may already be gone — ignore\n }\n }, KILL_GRACE_MS);\n }\n\n reject(new TimeoutError(timeoutMs));\n }, timeoutMs);\n });\n\n const clear = (): void => {\n if (timer) clearTimeout(timer);\n if (graceTimer) clearTimeout(graceTimer);\n };\n\n return { clear, promise };\n}\n","import { MAX_RESPONSE_CHARS } from \"../config/constants.js\";\n\nexport function truncateResponse(\n text: string,\n maxChars: number = MAX_RESPONSE_CHARS,\n): string {\n if (text.length <= maxChars) return text;\n\n const omitted = text.length - maxChars;\n return (\n text.slice(0, maxChars) +\n `\\n\\n[Response truncated at ${maxChars} characters. ${omitted} characters omitted.]`\n );\n}\n","import { EmptyOutputError } from \"../errors/errors.js\";\nimport { truncateResponse } from \"../util/truncate.js\";\n\nexport interface CodexResult {\n readonly content: string;\n readonly raw: string;\n}\n\nexport function parseCodexOutput(raw: string): CodexResult {\n if (!raw.trim()) {\n throw new EmptyOutputError();\n }\n\n const lines = raw.split(\"\\n\").filter((line) => line.trim());\n const messages: string[] = [];\n let resultContent: string | null = null;\n\n for (const line of lines) {\n try {\n const parsed = JSON.parse(line);\n\n // Handle result type — takes priority over all other messages\n if (parsed.type === \"result\" && typeof parsed.content === \"string\") {\n resultContent = parsed.content;\n continue;\n }\n\n // Handle standard event format\n if (parsed.type === \"message\" && typeof parsed.content === \"string\") {\n messages.push(parsed.content);\n continue;\n }\n\n // Handle nested item format (Codex JSONL)\n if (parsed.item?.type === \"agent_message\" && typeof parsed.item.text === \"string\") {\n messages.push(parsed.item.text);\n continue;\n }\n\n // Handle flat legacy format\n if (parsed.itemType === \"agent_message\" && typeof parsed.text === \"string\") {\n messages.push(parsed.text);\n continue;\n }\n } catch {\n // Non-JSON line — could be preamble or status output. Skip.\n }\n }\n\n let agentMessage: string;\n\n if (resultContent !== null) {\n agentMessage = resultContent;\n } else if (messages.length > 0) {\n agentMessage = messages.join(\"\\n\\n\");\n } else {\n // If no structured output found, use the raw text (minus any obvious preamble)\n const substantiveLines = lines.filter(\n (line) => !line.startsWith(\"OpenAI Codex\") && !line.startsWith(\"---\") && !line.startsWith(\"tokens used\"),\n );\n agentMessage = substantiveLines.join(\"\\n\").trim();\n }\n\n if (!agentMessage) {\n throw new EmptyOutputError();\n }\n\n return {\n content: truncateResponse(agentMessage),\n raw,\n };\n}\n","import { BridgeError } from \"../errors/errors.js\";\nimport { MAX_RETRIES, MAX_RETRIES_ENV, RETRY_DELAYS_MS, RETRY_CAP_MS } from \"../config/constants.js\";\n\nexport interface RetryOptions {\n readonly maxRetries?: number;\n readonly shouldRetry?: (err: Error) => boolean;\n}\n\nfunction getMaxRetries(override?: number): number {\n if (override !== undefined) return override;\n const envVal = process.env[MAX_RETRIES_ENV];\n if (envVal) {\n const parsed = parseInt(envVal, 10);\n if (!isNaN(parsed) && parsed >= 0) return parsed;\n }\n return MAX_RETRIES;\n}\n\nfunction getDelay(attempt: number): number {\n const base = RETRY_DELAYS_MS[attempt] ?? RETRY_CAP_MS;\n const capped = Math.min(base, RETRY_CAP_MS);\n // Add jitter: 50-150% of base delay\n const jitter = 0.5 + Math.random();\n return Math.round(capped * jitter);\n}\n\nfunction defaultShouldRetry(err: Error): boolean {\n return err instanceof BridgeError && err.retryable;\n}\n\nfunction sleep(ms: number): Promise<void> {\n return new Promise((resolve) => setTimeout(resolve, ms));\n}\n\nexport async function withRetry<T>(\n fn: () => Promise<T>,\n options: RetryOptions = {},\n): Promise<T> {\n const maxRetries = getMaxRetries(options.maxRetries);\n const shouldRetry = options.shouldRetry ?? defaultShouldRetry;\n\n for (let attempt = 0; ; attempt++) {\n try {\n return await fn();\n } catch (err) {\n const isRetryable = err instanceof Error && shouldRetry(err);\n if (attempt < maxRetries && isRetryable) {\n const delay = getDelay(attempt);\n const errorName = err instanceof Error ? err.constructor.name : \"UnknownError\";\n process.stderr.write(\n `[skill-codex] ${errorName} (attempt ${attempt + 1}/${maxRetries}), retrying in ${delay}ms...\\n`,\n );\n await sleep(delay);\n continue;\n }\n throw err;\n }\n }\n}\n","import { startServer } from \"./server.js\";\n\nstartServer().catch((err) => {\n process.stderr.write(`[skill-codex] Fatal: ${err instanceof Error ? err.message : String(err)}\\n`);\n process.exit(1);\n});\n"],"mappings":"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","names":["fs","path","os","path","path","fs"]}
1
+ {"version":3,"sources":["../src/server.ts","../src/tools/codex-exec.ts","../src/errors/errors.ts","../src/config/constants.ts","../src/guards/check-recursion.ts","../src/guards/check-binary.ts","../src/guards/check-auth.ts","../src/runner/sandbox-args.ts","../src/lock/lock-file.ts","../src/guards/check-lock.ts","../src/guards/check-git.ts","../src/guards/preflight.ts","../src/runner/exec-runner.ts","../src/util/platform.ts","../src/runner/timeout.ts","../src/util/truncate.ts","../src/runner/output-parser.ts","../src/util/text.ts","../src/runner/progress.ts","../src/util/live-logger.ts","../src/runner/retry.ts","../src/index.ts"],"sourcesContent":["import { Server } from \"@modelcontextprotocol/sdk/server/index.js\";\nimport { StdioServerTransport } from \"@modelcontextprotocol/sdk/server/stdio.js\";\nimport {\n CallToolRequestSchema,\n ListToolsRequestSchema,\n} from \"@modelcontextprotocol/sdk/types.js\";\nimport {\n TOOL_NAME,\n TOOL_DESCRIPTION,\n TOOL_INPUT_JSON_SCHEMA,\n inputSchema,\n handleCodexExec,\n} from \"./tools/codex-exec.js\";\n\nexport function createServer(cwd: string): Server {\n const server = new Server(\n { name: \"skill-codex\", version: \"0.7.1\" },\n { capabilities: { tools: {} } },\n );\n\n server.setRequestHandler(ListToolsRequestSchema, async () => ({\n tools: [\n {\n name: TOOL_NAME,\n description: TOOL_DESCRIPTION,\n inputSchema: TOOL_INPUT_JSON_SCHEMA,\n },\n ],\n }));\n\n server.setRequestHandler(CallToolRequestSchema, async (request, extra) => {\n if (request.params.name !== TOOL_NAME) {\n return {\n content: [{ type: \"text\", text: `Unknown tool: ${request.params.name}` }],\n isError: true,\n };\n }\n\n const parsed = inputSchema.safeParse(request.params.arguments);\n if (!parsed.success) {\n return {\n content: [\n {\n type: \"text\",\n text: `Invalid input: ${parsed.error.issues.map((i) => i.message).join(\", \")}`,\n },\n ],\n isError: true,\n };\n }\n\n // Stream live progress only if the client requested it (sent a progressToken).\n // MCP requires the `progress` value to monotonically increase per request.\n const progressToken = request.params._meta?.progressToken;\n let progressCounter = 0;\n const onProgress =\n progressToken === undefined\n ? undefined\n : (message: string): void => {\n progressCounter += 1;\n void extra\n .sendNotification({\n method: \"notifications/progress\",\n params: { progressToken, progress: progressCounter, message },\n })\n .catch(() => {\n // never let a dropped notification break the run\n });\n };\n\n return handleCodexExec(parsed.data, cwd, onProgress);\n });\n\n return server;\n}\n\nexport async function startServer(): Promise<void> {\n const cwd = process.cwd();\n const server = createServer(cwd);\n const transport = new StdioServerTransport();\n\n process.stderr.write(\"[skill-codex] MCP server starting...\\n\");\n\n await server.connect(transport);\n\n process.stderr.write(\"[skill-codex] MCP server connected via stdio\\n\");\n\n process.on(\"uncaughtException\", (err) => {\n process.stderr.write(`[skill-codex] Uncaught exception: ${err.message}\\n`);\n });\n\n process.on(\"unhandledRejection\", (reason) => {\n process.stderr.write(`[skill-codex] Unhandled rejection: ${String(reason)}\\n`);\n });\n}\n","import fs from \"node:fs\";\nimport path from \"node:path\";\nimport { z } from \"zod\";\nimport { runPreflight } from \"../guards/preflight.js\";\nimport { execCodex } from \"../runner/exec-runner.js\";\nimport { withRetry } from \"../runner/retry.js\";\nimport { BridgeError } from \"../errors/errors.js\";\nimport type { CodexResult } from \"../runner/output-parser.js\";\n\nexport const TOOL_NAME = \"codex_exec\";\n\nexport const TOOL_DESCRIPTION =\n \"Execute a task using OpenAI Codex CLI. Use for code review, implementation tasks, or getting a second opinion. Codex output is a SUGGESTION — evaluate it critically before applying.\";\n\nexport const inputSchema = z.object({\n prompt: z.string().optional().describe(\"The task description for Codex\"),\n mode: z\n .enum([\"exec\", \"full-auto\"])\n .default(\"exec\")\n .describe(\"exec = read-only with confirmation, full-auto = can write files\"),\n sandbox: z\n .enum([\"read-only\", \"workspace-write\", \"danger-full-access\"])\n .optional()\n .describe(\n \"Explicit Codex sandbox policy; overrides mode. read-only = no writes, workspace-write = write within cwd, danger-full-access = unrestricted (use with care).\",\n ),\n sessionId: z\n .string()\n .regex(/^[A-Za-z0-9_-]{1,128}$/, \"sessionId must be a Codex thread id (letters, digits, '-', '_')\")\n .optional()\n .describe(\n \"Resume a prior Codex session by its thread id (returned in a previous response) so Codex retains context across calls.\",\n ),\n model: z\n .string()\n .regex(/^[A-Za-z0-9._-]{1,64}$/)\n .optional()\n .describe(\n \"Codex model to use (e.g. gpt-5.5, gpt-5.4, gpt-5.4-mini). Omit to use Codex's configured default.\",\n ),\n reasoningEffort: z\n .enum([\"minimal\", \"low\", \"medium\", \"high\", \"xhigh\"])\n .optional()\n .describe(\"How much reasoning effort Codex spends. Omit for the model's default.\"),\n review: z\n .boolean()\n .optional()\n .describe(\n \"Run Codex's native diff-scoped review (`codex exec review`) instead of a freeform prompt. The prompt becomes optional custom review instructions.\",\n ),\n reviewBase: z\n .string()\n .regex(/^[A-Za-z0-9._\\/-]{1,128}$/)\n .optional()\n .describe(\"With review: diff against this base branch (default: uncommitted changes).\"),\n reviewCommit: z\n .string()\n .regex(/^[0-9a-fA-F]{4,64}$/)\n .optional()\n .describe(\"With review: review the changes introduced by this commit SHA.\"),\n cwd: z.string().optional().describe(\"Working directory (defaults to server cwd)\"),\n timeoutMs: z.number().optional().describe(\"Override default timeout in milliseconds\"),\n requireGit: z.boolean().default(false).describe(\"Fail if not inside a git repository\"),\n});\n\nexport type CodexExecInput = z.infer<typeof inputSchema>;\n\n/**\n * JSON Schema advertised to MCP clients in the `tools/list` response. Kept here\n * next to the zod `inputSchema` (the runtime validator) so the two can't drift —\n * a sync test asserts their property sets match. The MCP SDK wants a plain JSON\n * Schema object, so we hand-write it rather than deriving it at runtime.\n */\nexport const TOOL_INPUT_JSON_SCHEMA = {\n type: \"object\" as const,\n properties: {\n prompt: { type: \"string\", description: \"The task description for Codex\" },\n mode: {\n type: \"string\",\n enum: [\"exec\", \"full-auto\"],\n default: \"exec\",\n description: \"exec = read-only, full-auto = can write files\",\n },\n sandbox: {\n type: \"string\",\n enum: [\"read-only\", \"workspace-write\", \"danger-full-access\"],\n description:\n \"Explicit Codex sandbox policy; overrides mode. read-only = no writes, workspace-write = write within cwd, danger-full-access = unrestricted (use with care).\",\n },\n sessionId: {\n type: \"string\",\n pattern: \"^[A-Za-z0-9_-]{1,128}$\",\n description:\n \"Resume a prior Codex session by its thread id (returned in a previous response) so Codex retains context across calls.\",\n },\n model: {\n type: \"string\",\n pattern: \"^[A-Za-z0-9._-]{1,64}$\",\n description:\n \"Codex model to use (e.g. gpt-5.5, gpt-5.4, gpt-5.4-mini). Omit to use Codex's configured default.\",\n },\n reasoningEffort: {\n type: \"string\",\n enum: [\"minimal\", \"low\", \"medium\", \"high\", \"xhigh\"],\n description: \"How much reasoning effort Codex spends. Omit for the model's default.\",\n },\n review: {\n type: \"boolean\",\n description:\n \"Run Codex's native diff-scoped review (`codex exec review`) instead of a freeform prompt. The prompt becomes optional custom review instructions.\",\n },\n reviewBase: {\n type: \"string\",\n pattern: \"^[A-Za-z0-9._\\\\/-]{1,128}$\",\n description: \"With review: diff against this base branch (default: uncommitted changes).\",\n },\n reviewCommit: {\n type: \"string\",\n pattern: \"^[0-9a-fA-F]{4,64}$\",\n description: \"With review: review the changes introduced by this commit SHA.\",\n },\n cwd: { type: \"string\", description: \"Working directory (defaults to server cwd)\" },\n timeoutMs: { type: \"number\", description: \"Override default timeout in milliseconds\" },\n requireGit: {\n type: \"boolean\",\n default: false,\n description: \"Fail if not inside a git repository\",\n },\n },\n required: [],\n};\n\nfunction formatError(err: unknown): string {\n if (err instanceof BridgeError) {\n return `[skill-codex error: ${err.code}] ${err.message}`;\n }\n if (err instanceof Error) {\n return `[skill-codex error] ${err.message}`;\n }\n return `[skill-codex error] Unknown error: ${String(err)}`;\n}\n\nfunction formatRichResponse(\n result: CodexResult,\n input: CodexExecInput,\n cwd: string,\n): string {\n const lines: string[] = [];\n\n // On resume, no --sandbox is sent (the session keeps its original policy), so\n // labelling it with a specific mode would be misleading — show \"resumed\" instead.\n const sandboxLabel = input.sandbox ?? (input.mode === \"full-auto\" ? \"workspace-write\" : \"read-only\");\n const label = input.review ? \"review\" : input.sessionId ? \"resumed\" : sandboxLabel;\n const metaParts: string[] = [label];\n\n if (input.model) {\n metaParts.push(input.model);\n }\n\n if (input.reasoningEffort) {\n metaParts.push(`effort:${input.reasoningEffort}`);\n }\n\n metaParts.push(cwd);\n\n if (typeof result.durationMs === \"number\") {\n metaParts.push(`${(result.durationMs / 1000).toFixed(1)}s`);\n }\n\n if (result.usage) {\n const {\n input_tokens: inp,\n output_tokens: out,\n cached_input_tokens: cached,\n reasoning_output_tokens: reasoning,\n } = result.usage;\n metaParts.push(\n `${inp} tok in${cached > 0 ? ` (${cached} cached)` : \"\"} \\u2192 ${out} out${reasoning > 0 ? ` (+${reasoning} reasoning)` : \"\"}`,\n );\n }\n\n lines.push(`[${metaParts.join(\" \\u2502 \")}]`);\n\n if (result.sessionId) {\n lines.push(` session: ${result.sessionId} (pass as sessionId to continue this conversation)`);\n }\n\n if (result.logPath) {\n lines.push(` live log: ${result.logPath}`);\n }\n\n if (result.activity.length > 0) {\n for (const a of result.activity) {\n if (a.type === \"exec\") {\n lines.push(` ${a.icon} exec: ${a.command} (${a.status})`);\n } else if (a.type === \"read\") {\n lines.push(` \\u25B6 read: ${a.path}`);\n } else if (a.type === \"write\") {\n lines.push(` \\u270E write: ${a.path}`);\n }\n }\n }\n\n lines.push(\"\");\n lines.push(result.content);\n\n return lines.join(\"\\n\");\n}\n\nexport async function handleCodexExec(\n input: CodexExecInput,\n serverCwd: string,\n onProgress?: (message: string) => void,\n): Promise<{ content: Array<{ type: \"text\"; text: string }>; isError?: boolean }> {\n const rawCwd = input.cwd ?? serverCwd;\n const cwd = path.resolve(rawCwd);\n\n // Validate cwd is an existing directory (prevent path traversal to arbitrary locations)\n if (!fs.existsSync(cwd) || !fs.statSync(cwd).isDirectory()) {\n return {\n content: [{ type: \"text\", text: `[skill-codex error: INVALID_CWD] cwd is not an existing directory: ${cwd}` }],\n isError: true,\n };\n }\n\n // Reject conflicting/stray options instead of silently ignoring them.\n const optError = (msg: string): { content: Array<{ type: \"text\"; text: string }>; isError: boolean } => ({\n content: [{ type: \"text\", text: `[skill-codex error: INVALID_OPTIONS] ${msg}` }],\n isError: true,\n });\n if (input.review && input.sessionId) return optError(\"review and sessionId are mutually exclusive\");\n if (input.reviewBase && input.reviewCommit) return optError(\"reviewBase and reviewCommit are mutually exclusive\");\n if ((input.reviewBase ?? input.reviewCommit) && !input.review) {\n return optError(\"reviewBase/reviewCommit require review: true\");\n }\n // The codex CLI forbids combining a review scope flag with a PROMPT.\n if (input.review && (input.reviewBase ?? input.reviewCommit) && input.prompt?.trim()) {\n return optError(\n \"a review target (reviewBase/reviewCommit) can't be combined with a prompt — Codex review takes a target OR instructions, not both\",\n );\n }\n\n // `prompt` is optional only for native review (where it's optional custom\n // instructions). Every other call must supply a prompt.\n if (!input.review && !input.prompt?.trim()) {\n return {\n content: [{ type: \"text\", text: \"[skill-codex error: MISSING_PROMPT] prompt is required unless review is set\" }],\n isError: true,\n };\n }\n\n let lockRelease: (() => void) | null = null;\n\n try {\n const { lockHandle } = await runPreflight({\n cwd,\n requireGit: input.requireGit,\n });\n lockRelease = lockHandle?.release ?? null;\n\n const result = await withRetry(() =>\n execCodex({\n prompt: input.prompt ?? \"\",\n cwd,\n mode: input.mode,\n sandbox: input.sandbox,\n sessionId: input.sessionId,\n model: input.model,\n reasoningEffort: input.reasoningEffort,\n review: input.review,\n reviewBase: input.reviewBase,\n reviewCommit: input.reviewCommit,\n timeoutMs: input.timeoutMs,\n onProgress,\n }),\n );\n\n const formatted = formatRichResponse(result, input, cwd);\n return {\n content: [{ type: \"text\", text: formatted }],\n };\n } catch (err) {\n return {\n content: [{ type: \"text\", text: formatError(err) }],\n isError: true,\n };\n } finally {\n lockRelease?.();\n }\n}\n","export class BridgeError extends Error {\n readonly code: string;\n readonly retryable: boolean;\n\n constructor(message: string, code: string, retryable: boolean) {\n super(message);\n this.name = \"BridgeError\";\n this.code = code;\n this.retryable = retryable;\n }\n}\n\nexport class CliNotFoundError extends BridgeError {\n constructor(binary: string = \"codex\") {\n super(\n `${binary} CLI not found on PATH. Install it with: npm i -g @openai/codex`,\n \"CLI_NOT_FOUND\",\n false,\n );\n this.name = \"CliNotFoundError\";\n }\n}\n\nexport class AuthExpiredError extends BridgeError {\n constructor() {\n super(\n \"Codex authentication expired or not found. Run `codex login` to re-authenticate.\",\n \"AUTH_EXPIRED\",\n false,\n );\n this.name = \"AuthExpiredError\";\n }\n}\n\nexport class RecursionLimitError extends BridgeError {\n constructor(depth: number, max: number) {\n super(\n `Maximum bridge nesting depth reached (${depth} >= ${max}). This prevents infinite recursion between Claude and Codex.`,\n \"RECURSION_LIMIT\",\n false,\n );\n this.name = \"RecursionLimitError\";\n }\n}\n\nexport class LockConflictError extends BridgeError {\n constructor(pid: number) {\n super(\n `Another skill-codex instance is running (PID ${pid}). Wait for it to finish or delete the lock file.`,\n \"LOCK_CONFLICT\",\n false,\n );\n this.name = \"LockConflictError\";\n }\n}\n\nexport class TimeoutError extends BridgeError {\n constructor(timeoutMs: number) {\n super(\n `Codex timed out after ${Math.round(timeoutMs / 1000)}s. Increase SKILL_CODEX_TIMEOUT_MS if needed.`,\n \"TIMEOUT\",\n true,\n );\n this.name = \"TimeoutError\";\n }\n}\n\nexport class RateLimitError extends BridgeError {\n constructor() {\n super(\n \"Codex rate limited (429). Will retry with backoff.\",\n \"RATE_LIMIT\",\n true,\n );\n this.name = \"RateLimitError\";\n }\n}\n\nexport class ServerError extends BridgeError {\n constructor(detail: string = \"\") {\n super(\n `Codex server error${detail ? `: ${detail}` : \"\"}. Will retry.`,\n \"SERVER_ERROR\",\n true,\n );\n this.name = \"ServerError\";\n }\n}\n\nexport class NetworkError extends BridgeError {\n constructor(detail: string = \"\") {\n super(\n `Network error connecting to Codex${detail ? `: ${detail}` : \"\"}. Check your connection.`,\n \"NETWORK_ERROR\",\n true,\n );\n this.name = \"NetworkError\";\n }\n}\n\nexport class EmptyOutputError extends BridgeError {\n constructor() {\n super(\n \"Codex returned empty output. This may be a transient issue.\",\n \"EMPTY_OUTPUT\",\n true,\n );\n this.name = \"EmptyOutputError\";\n }\n}\n\nexport class NotGitRepoError extends BridgeError {\n constructor(cwd: string) {\n super(\n `Not a git repository: ${cwd}. This operation requires a git repo.`,\n \"NOT_GIT_REPO\",\n false,\n );\n this.name = \"NotGitRepoError\";\n }\n}\n\nexport class ParseError extends BridgeError {\n constructor(detail: string = \"\") {\n super(\n `Failed to parse Codex output${detail ? `: ${detail}` : \"\"}. The Codex CLI format may have changed — please update skill-codex.`,\n \"PARSE_ERROR\",\n false,\n );\n this.name = \"ParseError\";\n }\n}\n","export const MAX_BRIDGE_DEPTH = 2;\nexport const BRIDGE_DEPTH_ENV = \"SKILL_CODEX_DEPTH\";\n\nexport const DEFAULT_TIMEOUT_MS = 300_000; // 5 minutes (override via SKILL_CODEX_TIMEOUT_MS or per-call timeoutMs)\nexport const TIMEOUT_ENV = \"SKILL_CODEX_TIMEOUT_MS\";\nexport const KILL_GRACE_MS = 5_000;\n\n/** How often to emit a heartbeat progress message during quiet stretches. */\nexport const HEARTBEAT_INTERVAL_MS = 10_000;\n\nexport const MAX_RETRIES = 3;\nexport const MAX_RETRIES_ENV = \"SKILL_CODEX_MAX_RETRIES\";\nexport const RETRY_DELAYS_MS = [1_000, 2_000, 4_000];\nexport const RETRY_CAP_MS = 10_000;\n\nexport const MAX_RESPONSE_CHARS = 80_000;\n\nexport const LOCK_STALE_MS = 900_000; // 15 minutes\nexport const LOCK_FILENAME = \".skill-codex.lock\";\n\n// Live, human-readable per-run log. Codex emits JSONL that the MCP transport\n// buffers until completion; tailing this file shows progress in real time.\n// Defaults to a per-workspace file under the OS temp dir so a run never writes\n// a growing log into the user's working repo. Override with an absolute path\n// via SKILL_CODEX_LOG. The resolved path is printed at run start and returned\n// in the tool response, so it stays discoverable.\nexport const LOG_ENV = \"SKILL_CODEX_LOG\";\n\nexport const TRIVIAL_DIFF_THRESHOLD = 5; // lines\nexport const DOCS_ONLY_EXTENSIONS = [\".md\", \".txt\", \".rst\", \".adoc\"];\nexport const SECURITY_PATH_KEYWORDS = [\"security\", \"auth\", \"crypto\", \"password\", \"secret\", \"token\"];\nexport const FORCE_REVIEW_LINES = 100;\nexport const FORCE_REVIEW_FILES = 3;\n\nexport const CONFIG_ONLY_FILES = [\".gitignore\", \".eslintrc\", \".prettierrc\", \".editorconfig\"];\n\nexport const DEBUG_ENV = \"SKILL_CODEX_DEBUG\";\n\n// Codex's default (\"elevated\") Windows sandbox fails to spawn shells with\n// \"windows sandbox: spawn setup refresh\" on many setups (openai/codex#24098,\n// #24259). The \"unelevated\" sandbox spawns reliably, so we pin to it on Windows.\n// Override with SKILL_CODEX_WINDOWS_SANDBOX=elevated if a machine needs it.\nexport const WINDOWS_SANDBOX_ENV = \"SKILL_CODEX_WINDOWS_SANDBOX\";\nexport const WINDOWS_SANDBOX_DEFAULT = \"unelevated\";\n\nexport const TRANSIENT_PATTERNS = [\n \"rate limit\", \"too many requests\", \"429\",\n \"500\", \"502\", \"503\", \"504\",\n \"internal server error\", \"bad gateway\", \"service unavailable\", \"gateway timeout\",\n \"connection reset\", \"connection refused\",\n \"econnreset\", \"econnrefused\", \"etimedout\",\n \"network error\", \"fetch failed\", \"socket hang up\",\n] as const;\n\nexport const AUTH_ERROR_PATTERNS = [\n \"api key\", \"authentication\", \"unauthorized\", \"401\", \"auth\",\n] as const;\n","import { BRIDGE_DEPTH_ENV, MAX_BRIDGE_DEPTH } from \"../config/constants.js\";\nimport { RecursionLimitError } from \"../errors/errors.js\";\n\nexport function getCurrentDepth(): number {\n return parseInt(process.env[BRIDGE_DEPTH_ENV] ?? \"0\", 10);\n}\n\nexport function getNextDepth(): number {\n return getCurrentDepth() + 1;\n}\n\nexport function checkRecursion(): void {\n const depth = getCurrentDepth();\n if (depth >= MAX_BRIDGE_DEPTH) {\n throw new RecursionLimitError(depth, MAX_BRIDGE_DEPTH);\n }\n}\n","import which from \"which\";\nimport { CliNotFoundError } from \"../errors/errors.js\";\n\nexport interface BinaryCheckResult {\n readonly found: boolean;\n readonly path: string;\n}\n\nlet cachedBinaryPath: string | null = null;\n\nexport function getCachedBinaryPath(): string | null {\n return cachedBinaryPath;\n}\n\nexport function resetBinaryCache(): void {\n cachedBinaryPath = null;\n}\n\nexport async function checkBinary(\n binary: string = \"codex\",\n): Promise<BinaryCheckResult> {\n if (cachedBinaryPath !== null) {\n return { found: true, path: cachedBinaryPath };\n }\n\n try {\n const resolved = await which(binary);\n cachedBinaryPath = resolved;\n return { found: true, path: resolved };\n } catch {\n throw new CliNotFoundError(binary);\n }\n}\n","import { execFile } from \"node:child_process\";\nimport { AuthExpiredError, NetworkError, CliNotFoundError } from \"../errors/errors.js\";\nimport { getCachedBinaryPath } from \"./check-binary.js\";\nimport { getSandboxConfigArgs } from \"../runner/sandbox-args.js\";\n\nconst AUTH_CACHE_TTL_MS = 60_000;\n\nlet authCachedAt: number | null = null;\n\nexport function resetAuthCache(): void {\n authCachedAt = null;\n}\n\nexport async function checkAuth(): Promise<void> {\n const now = Date.now();\n\n if (authCachedAt !== null && now - authCachedAt < AUTH_CACHE_TTL_MS) {\n return;\n }\n\n const binary = getCachedBinaryPath() ?? \"codex\";\n\n return new Promise((resolve, reject) => {\n const child = execFile(\n binary,\n [\"exec\", \"--sandbox\", \"read-only\", ...getSandboxConfigArgs(), \"--skip-git-repo-check\", \"--ephemeral\", \"echo ok\"],\n { timeout: 30_000, shell: process.platform === \"win32\" },\n (error, _stdout, stderr) => {\n if (!error) {\n authCachedAt = Date.now();\n resolve();\n return;\n }\n\n const lower = (stderr ?? error.message ?? \"\").toLowerCase();\n\n // Distinguish error types instead of masking everything as auth\n if (error.code === \"ENOENT\" || (error as NodeJS.ErrnoException).code === \"ENOENT\") {\n reject(new CliNotFoundError());\n return;\n }\n\n if (error.killed) {\n reject(new NetworkError(\"Auth check timed out — check your network connection\"));\n return;\n }\n\n if ([\"econnrefused\", \"econnreset\", \"etimedout\", \"network error\", \"fetch failed\"].some((p) => lower.includes(p))) {\n reject(new NetworkError(\"Network error during auth check\"));\n return;\n }\n\n // Default: treat as auth issue\n reject(new AuthExpiredError());\n },\n );\n child.stdin?.end();\n });\n}\n","import { WINDOWS_SANDBOX_ENV, WINDOWS_SANDBOX_DEFAULT } from \"../config/constants.js\";\n\n/**\n * Extra `codex` CLI args needed to make the sandbox usable on the current\n * platform.\n *\n * On Windows, Codex's default elevated sandbox fails to spawn shell processes\n * (\"windows sandbox: spawn setup refresh\" — openai/codex#24098, #24259), which\n * blocks every command the model runs, including read-only ones. Pinning\n * `windows.sandbox=unelevated` restores reliable spawning. The mode can be\n * overridden via SKILL_CODEX_WINDOWS_SANDBOX (e.g. \"elevated\") for machines\n * where the elevated sandbox does work.\n *\n * Returns an empty array on non-Windows platforms, where Codex's native\n * sandbox (Seatbelt/Landlock) is used as-is.\n */\nexport function getSandboxConfigArgs(): readonly string[] {\n if (process.platform !== \"win32\") return [];\n const raw = process.env[WINDOWS_SANDBOX_ENV]?.trim() || WINDOWS_SANDBOX_DEFAULT;\n // Allowlist: Windows spawns via shell:true, so reject anything but a bare mode\n // token to keep a stray env value from injecting extra shell args.\n const mode = /^[a-z-]+$/.test(raw) ? raw : WINDOWS_SANDBOX_DEFAULT;\n return [\"-c\", `windows.sandbox=${mode}`];\n}\n","import fs from \"node:fs\";\nimport path from \"node:path\";\nimport os from \"node:os\";\nimport { LOCK_FILENAME, LOCK_STALE_MS } from \"../config/constants.js\";\nimport { LockConflictError } from \"../errors/errors.js\";\n\ninterface LockData {\n readonly pid: number;\n readonly timestamp: number;\n readonly hostname: string;\n}\n\nexport interface LockHandle {\n readonly release: () => void;\n}\n\nfunction getLockPath(cwd: string): string {\n return path.join(cwd, LOCK_FILENAME);\n}\n\nfunction isProcessAlive(pid: number): boolean {\n try {\n process.kill(pid, 0);\n return true;\n } catch {\n return false;\n }\n}\n\nfunction isLockStale(data: LockData): boolean {\n const age = Date.now() - data.timestamp;\n if (age > LOCK_STALE_MS) return true;\n if (!isProcessAlive(data.pid)) return true;\n return false;\n}\n\nfunction tryRemoveStaleLock(lockPath: string): boolean {\n try {\n const raw = fs.readFileSync(lockPath, \"utf-8\");\n const data: LockData = JSON.parse(raw);\n if (isLockStale(data)) {\n fs.unlinkSync(lockPath);\n return true;\n }\n throw new LockConflictError(data.pid);\n } catch (err) {\n if (err instanceof LockConflictError) throw err;\n // File disappeared or is unreadable — try to acquire\n try { fs.unlinkSync(lockPath); } catch { /* ignore */ }\n return true;\n }\n}\n\nexport function acquireLock(cwd: string): LockHandle {\n const lockPath = getLockPath(cwd);\n const lockData: LockData = {\n pid: process.pid,\n timestamp: Date.now(),\n hostname: os.hostname(),\n };\n const content = JSON.stringify(lockData, null, 2);\n\n try {\n fs.writeFileSync(lockPath, content, { flag: \"wx\" });\n } catch (err: unknown) {\n const fsErr = err as NodeJS.ErrnoException;\n if (fsErr.code === \"EEXIST\") {\n tryRemoveStaleLock(lockPath);\n // Retry once after stale removal — wrap in try/catch for TOCTOU race\n try {\n fs.writeFileSync(lockPath, content, { flag: \"wx\" });\n } catch (retryErr: unknown) {\n const retryFsErr = retryErr as NodeJS.ErrnoException;\n if (retryFsErr.code === \"EEXIST\") {\n // Another process won the race\n throw new LockConflictError(0);\n }\n throw retryErr;\n }\n } else {\n throw err;\n }\n }\n\n // Signal handlers — stored for removal in release()\n const onExit = (): void => {\n try { fs.unlinkSync(lockPath); } catch { /* ignore */ }\n };\n\n process.on(\"exit\", onExit);\n process.on(\"SIGINT\", onExit);\n process.on(\"SIGTERM\", onExit);\n\n const release = (): void => {\n // Remove signal handlers first to prevent double-cleanup\n process.removeListener(\"exit\", onExit);\n process.removeListener(\"SIGINT\", onExit);\n process.removeListener(\"SIGTERM\", onExit);\n try { fs.unlinkSync(lockPath); } catch { /* ignore */ }\n };\n\n return { release };\n}\n","import { acquireLock, type LockHandle } from \"../lock/lock-file.js\";\n\nexport function checkLock(cwd: string): LockHandle {\n return acquireLock(cwd);\n}\n","import { execFileSync } from \"node:child_process\";\n\nexport interface GitCheckResult {\n readonly isGitRepo: boolean;\n}\n\nexport function checkGit(cwd: string): GitCheckResult {\n try {\n execFileSync(\"git\", [\"rev-parse\", \"--is-inside-work-tree\"], {\n cwd,\n stdio: \"pipe\",\n timeout: 5_000,\n });\n return { isGitRepo: true };\n } catch {\n return { isGitRepo: false };\n }\n}\n","import type { LockHandle } from \"../lock/lock-file.js\";\nimport { NotGitRepoError } from \"../errors/errors.js\";\nimport { checkRecursion } from \"./check-recursion.js\";\nimport { checkBinary } from \"./check-binary.js\";\nimport { checkAuth } from \"./check-auth.js\";\nimport { checkLock } from \"./check-lock.js\";\nimport { checkGit } from \"./check-git.js\";\n\nexport interface PreflightOptions {\n readonly cwd: string;\n readonly requireGit: boolean;\n readonly skipAuth?: boolean;\n readonly skipLock?: boolean;\n}\n\nexport interface PreflightResult {\n readonly lockHandle: LockHandle | null;\n}\n\nexport async function runPreflight(\n options: PreflightOptions,\n): Promise<PreflightResult> {\n // Order: cheapest checks first (fail-fast)\n // 1. Recursion (env read — instant)\n checkRecursion();\n\n // 2. Binary exists (filesystem lookup)\n await checkBinary();\n\n // 3. Auth valid (spawns a quick process)\n // Skipped on Windows: execFile auth check fails due to PowerShell profile\n // errors causing false AuthExpiredError. The real execCodex handles auth.\n if (!options.skipAuth && process.platform !== \"win32\") {\n await checkAuth();\n }\n\n // 4. Lock file (filesystem write)\n let lockHandle: LockHandle | null = null;\n if (!options.skipLock) {\n lockHandle = checkLock(options.cwd);\n }\n\n // 5. Git repo (if required)\n if (options.requireGit) {\n const { isGitRepo } = checkGit(options.cwd);\n if (!isGitRepo) {\n lockHandle?.release();\n throw new NotGitRepoError(options.cwd);\n }\n }\n\n return { lockHandle };\n}\n","import { spawn } from \"node:child_process\";\nimport { StringDecoder } from \"node:string_decoder\";\nimport {\n BRIDGE_DEPTH_ENV,\n DEFAULT_TIMEOUT_MS,\n TIMEOUT_ENV,\n HEARTBEAT_INTERVAL_MS,\n} from \"../config/constants.js\";\nimport { getNextDepth } from \"../guards/check-recursion.js\";\nimport { getCachedBinaryPath } from \"../guards/check-binary.js\";\nimport { getSandboxConfigArgs } from \"./sandbox-args.js\";\nimport { setupTimeout } from \"./timeout.js\";\nimport { parseCodexOutput, type CodexResult } from \"./output-parser.js\";\nimport { formatProgressMessage } from \"./progress.js\";\nimport { createLiveLogger } from \"../util/live-logger.js\";\nimport {\n BridgeError,\n CliNotFoundError,\n AuthExpiredError,\n RateLimitError,\n ServerError,\n NetworkError,\n} from \"../errors/errors.js\";\n\nexport interface ExecParams {\n readonly prompt: string;\n readonly cwd: string;\n readonly mode: \"exec\" | \"full-auto\";\n readonly sandbox?: \"read-only\" | \"workspace-write\" | \"danger-full-access\";\n readonly sessionId?: string;\n readonly model?: string;\n readonly reasoningEffort?: \"minimal\" | \"low\" | \"medium\" | \"high\" | \"xhigh\";\n readonly review?: boolean;\n readonly reviewBase?: string;\n readonly reviewCommit?: string;\n readonly timeoutMs?: number;\n /**\n * Optional live-progress sink. Called with a short status line each time\n * Codex emits a meaningful JSONL event, plus a periodic heartbeat during\n * quiet stretches. Used to drive MCP progress notifications so a long run\n * doesn't look frozen. Best-effort — callbacks must not throw.\n */\n readonly onProgress?: (message: string) => void;\n}\n\nfunction getTimeout(override?: number): number {\n if (override !== undefined) return override;\n const envVal = process.env[TIMEOUT_ENV];\n if (envVal) {\n const parsed = parseInt(envVal, 10);\n if (!isNaN(parsed) && parsed > 0) return parsed;\n }\n return DEFAULT_TIMEOUT_MS;\n}\n\nfunction classifyError(exitCode: number, stderr: string): BridgeError {\n const lower = stderr.toLowerCase();\n\n // Check auth errors first but exclude generic \"auth\" in context\n if (lower.includes(\"unauthorized\") || lower.includes(\"401\") || lower.includes(\"api key\")) {\n return new AuthExpiredError();\n }\n if (lower.includes(\"rate limit\") || lower.includes(\"429\") || lower.includes(\"too many requests\")) {\n return new RateLimitError();\n }\n if ([\"500\", \"502\", \"503\", \"504\", \"internal server error\", \"bad gateway\", \"service unavailable\"].some((p) => lower.includes(p))) {\n return new ServerError(stderr.slice(0, 200));\n }\n if ([\"econnreset\", \"econnrefused\", \"etimedout\", \"network error\", \"fetch failed\", \"socket hang up\"].some((p) => lower.includes(p))) {\n return new NetworkError(stderr.slice(0, 200));\n }\n\n return new BridgeError(\n `Codex exited with code ${exitCode}: ${stderr.slice(0, 300)}`,\n \"EXEC_FAILED\",\n false,\n );\n}\n\nexport async function execCodex(params: ExecParams): Promise<CodexResult> {\n // Use cached binary path from checkBinary — throws if not found\n const codexPath = getCachedBinaryPath();\n if (codexPath === null) {\n throw new CliNotFoundError();\n }\n\n return new Promise((resolve, reject) => {\n const timeoutMs = getTimeout(params.timeoutMs);\n let args: string[];\n // Whether to send the prompt over stdin (the `-` sentinel). For native\n // review, the codex CLI forbids combining a scope flag\n // (--uncommitted/--base/--commit) with a PROMPT, so we only pass the prompt\n // when reviewing with custom instructions and no scope flag.\n let sendStdinPrompt: boolean;\n if (params.review) {\n args = [\"exec\", \"review\", \"--json\", \"--skip-git-repo-check\"];\n if (params.reviewBase) {\n args.push(\"--base\", params.reviewBase);\n sendStdinPrompt = false;\n } else if (params.reviewCommit) {\n args.push(\"--commit\", params.reviewCommit);\n sendStdinPrompt = false;\n } else if (params.prompt?.trim()) {\n // Custom review instructions only — review defaults to the working tree.\n sendStdinPrompt = true;\n } else {\n args.push(\"--uncommitted\");\n sendStdinPrompt = false;\n }\n } else if (params.sessionId) {\n // Resume keeps the original session's sandbox policy; `resume` has no --sandbox flag.\n args = [\"exec\", \"resume\", params.sessionId, \"--json\", \"--skip-git-repo-check\"];\n sendStdinPrompt = true;\n } else {\n const sandbox =\n params.sandbox ?? (params.mode === \"full-auto\" ? \"workspace-write\" : \"read-only\");\n args = [\"exec\", \"--json\", \"--skip-git-repo-check\", \"--sandbox\", sandbox];\n sendStdinPrompt = true;\n }\n\n if (params.model) {\n args.push(\"-m\", params.model);\n }\n\n if (params.reasoningEffort) {\n args.push(\"-c\", `model_reasoning_effort=${params.reasoningEffort}`);\n }\n\n // Platform-specific sandbox config (e.g. windows.sandbox=unelevated to work\n // around Codex's broken elevated Windows sandbox). Empty on non-Windows.\n args.push(...getSandboxConfigArgs());\n\n // Prompt passed via stdin to avoid shell injection — NOT as a positional arg\n const stdinPrompt = params.prompt ?? \"\";\n\n // Use \"-\" to tell codex to read the prompt from stdin. Omitted for review\n // runs that use a scope flag, where the CLI rejects a PROMPT.\n if (sendStdinPrompt) {\n args.push(\"-\");\n }\n\n const env = {\n ...process.env,\n [BRIDGE_DEPTH_ENV]: String(getNextDepth()),\n };\n\n // On Windows, npm-installed CLIs are .cmd shims that require a shell to execute.\n const child = spawn(codexPath, args, {\n cwd: params.cwd,\n env,\n stdio: [\"pipe\", \"pipe\", \"pipe\"],\n shell: process.platform === \"win32\",\n windowsHide: true,\n });\n\n const { clear: clearTimeout_, promise: timeoutPromise } = setupTimeout(child, timeoutMs);\n\n const startedAt = Date.now();\n\n // Persistent, tail-able per-run log (recovers progress the transport buffers).\n const logger = createLiveLogger({\n cwd: params.cwd,\n mode: params.mode,\n prompt: params.prompt,\n });\n process.stderr.write(`[skill-codex] live log: ${logger.path}\\n`);\n\n // Heartbeat so quiet reasoning stretches still show the run is alive.\n let heartbeat: ReturnType<typeof setInterval> | null = null;\n if (params.onProgress) {\n heartbeat = setInterval(() => {\n const secs = Math.round((Date.now() - startedAt) / 1000);\n params.onProgress?.(`Codex working… ${secs}s elapsed`);\n }, HEARTBEAT_INTERVAL_MS);\n if (typeof heartbeat.unref === \"function\") heartbeat.unref();\n }\n const stopHeartbeat = (): void => {\n if (heartbeat) {\n clearInterval(heartbeat);\n heartbeat = null;\n }\n };\n\n // Incrementally decode stdout: feed the file logger, and emit a progress\n // message per complete JSONL line.\n const decoder = new StringDecoder(\"utf8\");\n let progressBuf = \"\";\n const consumeForProgress = (text: string): void => {\n if (!params.onProgress) return;\n progressBuf += text;\n let idx: number;\n while ((idx = progressBuf.indexOf(\"\\n\")) >= 0) {\n const lineStr = progressBuf.slice(0, idx).trim();\n progressBuf = progressBuf.slice(idx + 1);\n if (!lineStr) continue;\n try {\n const msg = formatProgressMessage(JSON.parse(lineStr));\n if (msg) params.onProgress(msg);\n } catch {\n // non-JSON line — ignore\n }\n }\n };\n\n let logFinished = false;\n const finishLog = (summary: string): void => {\n stopHeartbeat();\n if (logFinished) return;\n logFinished = true;\n try {\n logger.write(decoder.end());\n logger.finish(summary);\n } catch {\n // best-effort\n }\n };\n\n const stdoutChunks: Buffer[] = [];\n let stderr = \"\";\n\n child.stdout?.on(\"data\", (chunk: Buffer) => {\n stdoutChunks.push(chunk);\n const text = decoder.write(chunk);\n try {\n logger.write(text);\n } catch {\n // best-effort logging\n }\n consumeForProgress(text);\n });\n\n child.stderr?.on(\"data\", (chunk: Buffer) => {\n stderr += chunk.toString();\n });\n\n // Write prompt via stdin then close — safe from shell injection. Skipped\n // for review-with-scope runs that don't pass `-` (the CLI rejects a PROMPT).\n if (sendStdinPrompt) {\n child.stdin?.write(stdinPrompt);\n }\n child.stdin?.end();\n\n const onClose = (exitCode: number | null): void => {\n clearTimeout_();\n finishLog(exitCode === 0 || exitCode === null ? \"ok\" : `exit ${exitCode}`);\n const stdout = Buffer.concat(stdoutChunks).toString();\n\n if (exitCode === 0 || exitCode === null) {\n try {\n const result = parseCodexOutput(stdout);\n resolve({ ...result, logPath: logger.path, durationMs: Date.now() - startedAt });\n } catch (err) {\n reject(err);\n }\n return;\n }\n\n reject(classifyError(exitCode, stderr));\n };\n\n child.on(\"close\", onClose);\n\n child.on(\"error\", (err: NodeJS.ErrnoException) => {\n clearTimeout_();\n finishLog(\"spawn error\");\n if (err.code === \"ENOENT\") {\n reject(new CliNotFoundError());\n } else {\n reject(new BridgeError(`Failed to spawn codex: ${err.message}`, \"SPAWN_ERROR\", false));\n }\n });\n\n // Race with timeout\n timeoutPromise.catch((err) => {\n finishLog(\"timeout\");\n reject(err);\n });\n });\n}\n","import os from \"node:os\";\nimport path from \"node:path\";\n\nexport type Platform = \"win32\" | \"darwin\" | \"linux\";\n\nexport function getPlatform(): Platform {\n const p = os.platform();\n if (p === \"win32\" || p === \"darwin\" || p === \"linux\") return p;\n return \"linux\"; // default fallback for other unix-like\n}\n\nexport function isWindows(): boolean {\n return getPlatform() === \"win32\";\n}\n\nexport function normalizePath(p: string): string {\n return p.replace(/\\\\/g, \"/\");\n}\n\nexport function getHomeDir(): string {\n return os.homedir();\n}\n\nexport function getClaudeDir(): string {\n return path.join(getHomeDir(), \".claude\");\n}\n\nexport function getTempDir(): string {\n return os.tmpdir();\n}\n","import type { ChildProcess } from \"node:child_process\";\nimport { KILL_GRACE_MS } from \"../config/constants.js\";\nimport { TimeoutError } from \"../errors/errors.js\";\nimport { isWindows } from \"../util/platform.js\";\n\nexport function setupTimeout(\n child: ChildProcess,\n timeoutMs: number,\n): { clear: () => void; promise: Promise<never> } {\n let timer: ReturnType<typeof setTimeout> | null = null;\n let graceTimer: ReturnType<typeof setTimeout> | null = null;\n\n const promise = new Promise<never>((_resolve, reject) => {\n timer = setTimeout(() => {\n if (isWindows()) {\n // On Windows, SIGTERM is silently ignored by Node.js — it maps to\n // TerminateProcess regardless of signal name. Call kill() with no\n // argument so the intent is explicit and no grace timer is needed.\n child.kill();\n } else {\n // Phase 1: graceful kill\n child.kill(\"SIGTERM\");\n\n // Phase 2: force kill after grace period\n graceTimer = setTimeout(() => {\n try {\n if (!child.killed) {\n child.kill(\"SIGKILL\");\n }\n } catch {\n // Process may already be gone — ignore\n }\n }, KILL_GRACE_MS);\n }\n\n reject(new TimeoutError(timeoutMs));\n }, timeoutMs);\n });\n\n const clear = (): void => {\n if (timer) clearTimeout(timer);\n if (graceTimer) clearTimeout(graceTimer);\n };\n\n return { clear, promise };\n}\n","import { MAX_RESPONSE_CHARS } from \"../config/constants.js\";\n\nexport function truncateResponse(\n text: string,\n maxChars: number = MAX_RESPONSE_CHARS,\n): string {\n if (text.length <= maxChars) return text;\n\n const omitted = text.length - maxChars;\n return (\n text.slice(0, maxChars) +\n `\\n\\n[Response truncated at ${maxChars} characters. ${omitted} characters omitted.]`\n );\n}\n","import { EmptyOutputError } from \"../errors/errors.js\";\nimport { truncateResponse } from \"../util/truncate.js\";\n\nexport interface ActivityEntry {\n readonly type: \"exec\" | \"read\" | \"write\";\n readonly command?: string;\n readonly path?: string;\n readonly icon: string;\n readonly status: string;\n}\n\nexport interface TokenUsage {\n readonly input_tokens: number;\n readonly cached_input_tokens: number;\n readonly output_tokens: number;\n readonly reasoning_output_tokens: number;\n}\n\nexport interface CodexResult {\n readonly content: string;\n readonly activity: ActivityEntry[];\n readonly usage: TokenUsage | null;\n readonly raw: string;\n readonly sessionId?: string;\n /** Path to the live run log, attached by execCodex. */\n readonly logPath?: string;\n /** Wall-clock duration of the Codex run in ms, attached by execCodex. */\n readonly durationMs?: number;\n}\n\nexport function parseCodexOutput(raw: string): CodexResult {\n if (!raw.trim()) {\n throw new EmptyOutputError();\n }\n\n const lines = raw.split(\"\\n\").filter((line) => line.trim());\n const messages: string[] = [];\n const activity: ActivityEntry[] = [];\n let resultContent: string | null = null;\n let sessionId: string | undefined;\n let usage: TokenUsage | null = null;\n\n for (const line of lines) {\n try {\n const parsed = JSON.parse(line);\n\n if (parsed.type === \"thread.started\" && typeof parsed.thread_id === \"string\") {\n sessionId = parsed.thread_id;\n continue;\n }\n\n // Extract token usage from turn.completed events\n if (parsed.type === \"turn.completed\" && parsed.usage) {\n usage = {\n input_tokens: parsed.usage.input_tokens ?? 0,\n cached_input_tokens: parsed.usage.cached_input_tokens ?? 0,\n output_tokens: parsed.usage.output_tokens ?? 0,\n reasoning_output_tokens: parsed.usage.reasoning_output_tokens ?? 0,\n };\n continue;\n }\n\n // Handle result type — takes priority over all other messages\n if (parsed.type === \"result\" && typeof parsed.content === \"string\") {\n resultContent = parsed.content;\n continue;\n }\n\n // Handle standard event format\n if (parsed.type === \"message\" && typeof parsed.content === \"string\") {\n messages.push(parsed.content);\n continue;\n }\n\n // Track command executions\n if (parsed.item?.type === \"command_execution\") {\n const cmd = parsed.item;\n const shortCmd =\n cmd.command?.length > 80\n ? cmd.command.slice(0, 77) + \"...\"\n : cmd.command;\n const statusIcon =\n cmd.status === \"declined\"\n ? \"\\u2718\"\n : cmd.exit_code === 0\n ? \"\\u2714\"\n : cmd.exit_code !== null\n ? \"\\u2718\"\n : \"\\u25B6\";\n const statusLabel =\n cmd.status === \"declined\"\n ? \"blocked\"\n : cmd.status === \"in_progress\"\n ? \"running\"\n : cmd.exit_code === 0\n ? \"ok\"\n : `exit ${cmd.exit_code}`;\n // Only record completed/declined events, not in_progress starts\n if (cmd.status !== \"in_progress\") {\n activity.push({\n type: \"exec\",\n command: shortCmd,\n icon: statusIcon,\n status: statusLabel,\n });\n }\n continue;\n }\n\n // Handle nested item format (Codex JSONL). The current schema emits the\n // final text on `item.completed`; skip `item.started`/`item.updated`\n // partials so streamed chunks aren't double-counted.\n if (\n parsed.item?.type === \"agent_message\" &&\n typeof parsed.item.text === \"string\" &&\n parsed.type !== \"item.started\" &&\n parsed.type !== \"item.updated\"\n ) {\n messages.push(parsed.item.text);\n continue;\n }\n\n // Handle flat legacy format\n if (parsed.itemType === \"agent_message\" && typeof parsed.text === \"string\") {\n messages.push(parsed.text);\n continue;\n }\n\n // Track file reads\n if (parsed.item?.type === \"file_read\") {\n activity.push({\n type: \"read\",\n path: parsed.item.path || \"file\",\n icon: \"\\u25B6\",\n status: \"read\",\n });\n continue;\n }\n\n // Track file writes/edits (legacy item types, retained for back-compat)\n if (parsed.item?.type === \"file_write\" || parsed.item?.type === \"file_edit\") {\n activity.push({\n type: \"write\",\n path: parsed.item.path || \"file\",\n icon: \"\\u270E\",\n status: \"write\",\n });\n continue;\n }\n\n // Current schema collapses file activity into a single `file_change` item.\n // It may carry a top-level `path` or a `changes` array of { path, kind }.\n if (parsed.item?.type === \"file_change\") {\n const changes = Array.isArray(parsed.item.changes) ? parsed.item.changes : null;\n if (changes && changes.length > 0) {\n for (const change of changes) {\n activity.push({\n type: \"write\",\n path: change?.path || \"file\",\n icon: \"\\u270E\",\n status: change?.kind || \"write\",\n });\n }\n } else {\n activity.push({\n type: \"write\",\n path: parsed.item.path || \"file\",\n icon: \"\\u270E\",\n status: \"write\",\n });\n }\n continue;\n }\n } catch {\n // Non-JSON line — could be preamble or status output. Skip.\n }\n }\n\n let agentMessage: string;\n\n if (resultContent !== null) {\n agentMessage = resultContent;\n } else if (messages.length > 0) {\n agentMessage = messages.join(\"\\n\\n\");\n } else {\n // If no structured output found, use the raw text (minus any obvious preamble)\n const substantiveLines = lines.filter(\n (line) =>\n !line.startsWith(\"OpenAI Codex\") &&\n !line.startsWith(\"---\") &&\n !line.startsWith(\"tokens used\"),\n );\n agentMessage = substantiveLines.join(\"\\n\").trim();\n }\n\n if (!agentMessage) {\n throw new EmptyOutputError();\n }\n\n return {\n content: truncateResponse(agentMessage),\n activity,\n usage,\n raw,\n sessionId,\n };\n}\n","/**\n * Collapse all whitespace runs to single spaces, trim, and truncate to `max`\n * characters with an ellipsis. Shared by the live file logger and the MCP\n * progress formatter so a single codex event renders consistently in both.\n */\nexport function oneLine(text: string, max: number): string {\n const collapsed = text.replace(/\\s+/g, \" \").trim();\n return collapsed.length > max ? collapsed.slice(0, max - 1) + \"…\" : collapsed;\n}\n\n/** Last path segment, for compact progress lines. Falls back to the input. */\nexport function baseName(p: string): string {\n if (!p) return \"file\";\n const parts = p.split(/[\\\\/]/).filter(Boolean);\n return parts.length > 0 ? parts[parts.length - 1] : p;\n}\n","import { oneLine, baseName } from \"../util/text.js\";\n\n/**\n * Map a parsed Codex JSONL event to a concise, human-readable status line for\n * MCP `notifications/progress`, or `null` for events that shouldn't surface as\n * progress (turn starts, partial item streams, unknown shapes).\n *\n * Kept pure (no I/O) so it is unit-testable in isolation and reusable by any\n * caller that wants a one-line summary of \"what is Codex doing right now\".\n */\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\nexport function formatProgressMessage(evt: any): string | null {\n if (!evt || typeof evt !== \"object\") return null;\n\n const item = evt.item;\n\n if (item?.type === \"command_execution\") {\n const cmd = oneLine(String(item.command ?? \"\"), 60);\n if (item.status === \"in_progress\") return `running: ${cmd}`;\n if (item.status === \"declined\") return `blocked: ${cmd}`;\n if (item.exit_code === 0) return `ran: ${cmd}`;\n return `failed (exit ${String(item.exit_code)}): ${cmd}`;\n }\n\n if (item?.type === \"file_read\") return `reading ${baseName(String(item.path ?? \"file\"))}`;\n\n if (item?.type === \"file_write\" || item?.type === \"file_edit\") {\n return `editing ${baseName(String(item.path ?? \"file\"))}`;\n }\n\n if (item?.type === \"file_change\") {\n const changes = Array.isArray(item.changes) ? item.changes : null;\n if (changes && changes.length > 0) {\n return changes.length === 1\n ? `editing ${baseName(String(changes[0]?.path ?? \"file\"))}`\n : `editing ${changes.length} files`;\n }\n return `editing ${baseName(String(item.path ?? \"file\"))}`;\n }\n\n if (item?.type === \"reasoning\" || evt.type === \"turn.started\") return \"thinking…\";\n\n if (\n item?.type === \"agent_message\" &&\n typeof item.text === \"string\" &&\n evt.type !== \"item.started\" &&\n evt.type !== \"item.updated\"\n ) {\n return \"writing response…\";\n }\n\n return null;\n}\n","import fs from \"node:fs\";\nimport os from \"node:os\";\nimport path from \"node:path\";\nimport { createHash } from \"node:crypto\";\nimport { LOG_ENV } from \"../config/constants.js\";\nimport { oneLine } from \"./text.js\";\n\nexport interface LiveLoggerOptions {\n readonly cwd: string;\n readonly mode: string;\n readonly prompt: string;\n}\n\nexport interface LiveLogger {\n readonly path: string;\n /** Feed a raw stdout fragment; complete JSONL lines are rendered as they arrive. */\n write(fragment: string): void;\n /** Flush any trailing partial line and write the footer. */\n finish(summary: string): void;\n}\n\n/**\n * Resolve the live-log path:\n * 1. `SKILL_CODEX_LOG` override (absolute path), else\n * 2. a per-workspace file under the OS temp dir — so a run never writes a\n * growing log file into the user's working repo. The filename is the\n * workspace basename plus a short hash of the full path, so the same\n * workspace appends to one tail-able log and distinct workspaces don't\n * collide. The resolved path is printed at run start and returned in the\n * tool response, so it stays discoverable.\n */\nexport function resolveLogPath(cwd: string): string {\n const override = process.env[LOG_ENV];\n if (override && override.trim()) return path.resolve(override.trim());\n const base = path.basename(cwd).replace(/[^a-zA-Z0-9._-]/g, \"_\") || \"run\";\n const hash = createHash(\"sha1\").update(cwd).digest(\"hex\").slice(0, 8);\n return path.join(os.tmpdir(), \"skill-codex\", `${base}-${hash}.log`);\n}\n\n/**\n * Render a parsed Codex JSONL event as zero or more human-readable log lines.\n * Pure (no I/O) so it can be unit-tested in isolation; `createLiveLogger`\n * wraps it with file buffering.\n */\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\nexport function formatLogLines(evt: any): string[] {\n if (!evt || typeof evt !== \"object\") return [];\n\n const item = evt.item;\n\n if (item?.type === \"command_execution\") {\n if (item.status === \"in_progress\") return [` $ ${oneLine(String(item.command ?? \"\"), 120)}`];\n if (item.status === \"declined\") return [\" ✘ blocked\"];\n if (item.exit_code === 0) return [\" ✔ ok\"];\n return [` ✘ exit ${String(item.exit_code)}`];\n }\n\n if (item?.type === \"file_read\") return [` read ${String(item.path ?? \"file\")}`];\n\n if (item?.type === \"file_write\" || item?.type === \"file_edit\") {\n return [` write ${String(item.path ?? \"file\")}`];\n }\n\n if (item?.type === \"file_change\") {\n const changes = Array.isArray(item.changes) ? item.changes : null;\n if (changes && changes.length > 0) {\n return changes.map(\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n (change: any) => ` write ${String(change?.path ?? \"file\")} (${String(change?.kind ?? \"write\")})`,\n );\n }\n return [` write ${String(item.path ?? \"file\")}`];\n }\n\n if (\n item?.type === \"agent_message\" &&\n typeof item.text === \"string\" &&\n evt.type !== \"item.started\" &&\n evt.type !== \"item.updated\"\n ) {\n return [` msg ${oneLine(item.text, 400)}`];\n }\n\n if (evt.type === \"message\" && typeof evt.content === \"string\") {\n return [` msg ${oneLine(evt.content, 400)}`];\n }\n\n if (evt.type === \"turn.completed\" && evt.usage) {\n const u = evt.usage;\n const reasoning = u.reasoning_output_tokens ?? 0;\n return [\n ` tokens: ${u.input_tokens ?? 0} in → ${u.output_tokens ?? 0} out${reasoning > 0 ? ` (+${reasoning} reasoning)` : \"\"}`,\n ];\n }\n\n return [];\n}\n\n/**\n * Human-readable, append-only log of a single Codex run. Codex emits JSONL on\n * stdout; this tails each event to a file so the user can `tail -f` a run that\n * the MCP transport would otherwise buffer until completion. Best-effort: any\n * filesystem error is swallowed so logging never breaks an actual Codex call.\n */\nexport function createLiveLogger(opts: LiveLoggerOptions): LiveLogger {\n const logPath = resolveLogPath(opts.cwd);\n\n let stream: fs.WriteStream | null = null;\n try {\n fs.mkdirSync(path.dirname(logPath), { recursive: true });\n stream = fs.createWriteStream(logPath, { flags: \"a\" });\n } catch {\n stream = null;\n }\n\n const line = (text: string): void => {\n try {\n stream?.write(text + \"\\n\");\n } catch {\n // best-effort logging — never throw\n }\n };\n\n const startedAt = new Date().toISOString();\n line(\"\");\n line(\"=\".repeat(60));\n line(`> codex ${opts.mode} ${startedAt}`);\n line(` cwd: ${opts.cwd}`);\n line(` task: ${oneLine(opts.prompt, 200)}`);\n line(\"-\".repeat(60));\n\n const handleEvent = (raw: string): void => {\n const trimmed = raw.trim();\n if (!trimmed) return;\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n let evt: any;\n try {\n evt = JSON.parse(trimmed);\n } catch {\n return; // non-JSON preamble — skip\n }\n for (const l of formatLogLines(evt)) line(l);\n };\n\n let buffer = \"\";\n\n return {\n path: logPath,\n write(fragment: string): void {\n buffer += fragment;\n let idx: number;\n while ((idx = buffer.indexOf(\"\\n\")) >= 0) {\n const lineStr = buffer.slice(0, idx);\n buffer = buffer.slice(idx + 1);\n handleEvent(lineStr);\n }\n },\n finish(summary: string): void {\n if (buffer.trim()) {\n handleEvent(buffer);\n buffer = \"\";\n }\n line(\"-\".repeat(60));\n line(`# done ${new Date().toISOString()} ${summary}`);\n try {\n stream?.end();\n } catch {\n // ignore\n }\n },\n };\n}\n","import { BridgeError } from \"../errors/errors.js\";\nimport { MAX_RETRIES, MAX_RETRIES_ENV, RETRY_DELAYS_MS, RETRY_CAP_MS } from \"../config/constants.js\";\n\nexport interface RetryOptions {\n readonly maxRetries?: number;\n readonly shouldRetry?: (err: Error) => boolean;\n}\n\nfunction getMaxRetries(override?: number): number {\n if (override !== undefined) return override;\n const envVal = process.env[MAX_RETRIES_ENV];\n if (envVal) {\n const parsed = parseInt(envVal, 10);\n if (!isNaN(parsed) && parsed >= 0) return parsed;\n }\n return MAX_RETRIES;\n}\n\nfunction getDelay(attempt: number): number {\n const base = RETRY_DELAYS_MS[attempt] ?? RETRY_CAP_MS;\n const capped = Math.min(base, RETRY_CAP_MS);\n // Add jitter: 50-150% of base delay\n const jitter = 0.5 + Math.random();\n return Math.round(capped * jitter);\n}\n\nfunction defaultShouldRetry(err: Error): boolean {\n return err instanceof BridgeError && err.retryable;\n}\n\nfunction sleep(ms: number): Promise<void> {\n return new Promise((resolve) => setTimeout(resolve, ms));\n}\n\nexport async function withRetry<T>(\n fn: () => Promise<T>,\n options: RetryOptions = {},\n): Promise<T> {\n const maxRetries = getMaxRetries(options.maxRetries);\n const shouldRetry = options.shouldRetry ?? defaultShouldRetry;\n\n for (let attempt = 0; ; attempt++) {\n try {\n return await fn();\n } catch (err) {\n const isRetryable = err instanceof Error && shouldRetry(err);\n if (attempt < maxRetries && isRetryable) {\n const delay = getDelay(attempt);\n const errorName = err instanceof Error ? err.constructor.name : \"UnknownError\";\n process.stderr.write(\n `[skill-codex] ${errorName} (attempt ${attempt + 1}/${maxRetries}), retrying in ${delay}ms...\\n`,\n );\n await sleep(delay);\n continue;\n }\n throw err;\n }\n }\n}\n","import { startServer } from \"./server.js\";\n\nstartServer().catch((err) => {\n process.stderr.write(`[skill-codex] Fatal: ${err instanceof Error ? err.message : String(err)}\\n`);\n process.exit(1);\n});\n"],"mappings":"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","names":["fs","path","os","path","fs","os","path","path","os","fs","path","fs"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "skill-codex",
3
- "version": "0.2.0",
3
+ "version": "0.7.1",
4
4
  "description": "Cross-platform Claude Code skill that integrates OpenAI Codex CLI for code review, task delegation, and consultation via MCP",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -12,7 +12,7 @@
12
12
  }
13
13
  },
14
14
  "bin": {
15
- "skill-codex": "./dist/bin/skill-codex.js"
15
+ "skill-codex": "dist/bin/skill-codex.js"
16
16
  },
17
17
  "scripts": {
18
18
  "build": "tsup",
@@ -27,6 +27,7 @@
27
27
  "dist",
28
28
  "commands",
29
29
  "hooks",
30
+ "skills",
30
31
  "README.md",
31
32
  "LICENSE"
32
33
  ],
@@ -0,0 +1,217 @@
1
+ ---
2
+ name: codex-bridge
3
+ description: Delegates bounded implementation work to OpenAI Codex via the codex_exec MCP tool, and uses Codex as an independent reviewer or second opinion. Use when the user asks to implement, write, generate, scaffold, port, refactor, migrate, or add tests for a well-scoped code task — especially phrases like "implement X", "write a function that", "generate tests for", "port this to", "refactor this file", "convert this", "add boilerplate", "create a script", "write a migration". Also use when the user asks for a second opinion, independent review, code review, or to double-check logic, security, or correctness of a diff. Do NOT use for architecture decisions, brainstorming, cross-module refactors needing deep context, or tasks under ~50 lines that are faster to do directly.
4
+ license: MIT
5
+ metadata:
6
+ author: Arystos
7
+ version: 0.7.1
8
+ mcp-server: skill-codex
9
+ category: developer-tools
10
+ ---
11
+
12
+ # Codex Bridge
13
+
14
+ This skill turns OpenAI Codex into a peer engineer you can delegate bounded implementation work to, and a second reviewer you can call for independent code review or technical consultation. It uses the `codex_exec` MCP tool provided by the `skill-codex` MCP server.
15
+
16
+ ## When to use this skill
17
+
18
+ Load this skill automatically when the user's request matches any of the following patterns:
19
+
20
+ **Delegation triggers** (use `delegate` workflow below):
21
+ - "implement X" / "write a function that X" / "create a script that X"
22
+ - "generate tests for X" / "add unit tests" / "scaffold tests"
23
+ - "port X to Y" / "convert X to Y" / "rewrite X in Y"
24
+ - "refactor this file" (single-file, clear target)
25
+ - "write a migration" / "create boilerplate for X"
26
+ - Any bounded, single-file or small multi-file code task with a clear spec
27
+
28
+ **Review triggers** (use `review` workflow below):
29
+ - "review my changes" / "review this diff" / "code review"
30
+ - "second opinion on this code" / "independent review"
31
+ - "check this for bugs" / "audit this for security issues"
32
+ - Before committing critical code (auth, payments, crypto, concurrency)
33
+
34
+ **Consult triggers** (use `consult` workflow below):
35
+ - "what do you think about this approach"
36
+ - "is this the right way to do X"
37
+ - "second opinion on my plan"
38
+ - Debugging when Claude has failed 3+ times on the same issue
39
+
40
+ ## When NOT to use this skill
41
+
42
+ Skip Codex and do the work yourself when:
43
+ - Task is <50 lines and faster to do directly
44
+ - Task requires deep conversation context Codex won't have
45
+ - Cross-module architectural refactor
46
+ - Ambiguous requirements needing user clarification
47
+ - Planning, brainstorming, or design decisions (Claude handles these)
48
+ - User explicitly asks you to do the work yourself
49
+
50
+ ## Workflows
51
+
52
+ ### Workflow 1: Delegate implementation (`delegate`)
53
+
54
+ Use when the user asks for a bounded implementation task with a clear spec.
55
+
56
+ **Steps:**
57
+
58
+ 1. **Verify the task is well-scoped.** If the request is vague, ask one clarifying question before delegating. A good delegation prompt has: exact file paths, specific change required, constraints (language, style, naming), and a clear "done" condition.
59
+
60
+ 2. **Announce the delegation.** Tell the user: `Delegating [task] to Codex.` Never invoke Codex silently.
61
+
62
+ 3. **Prepare a self-contained prompt.** Include:
63
+ - Exact file paths to create or modify (absolute or repo-relative)
64
+ - The specific change, with before/after examples if helpful
65
+ - Coding style constraints (e.g., "TypeScript strict, no `any`, immutable patterns")
66
+ - What success looks like (e.g., "tests pass, no new lint errors")
67
+
68
+ 4. **Call the `codex_exec` MCP tool** with:
69
+ ```
70
+ prompt: <the self-contained prompt>
71
+ mode: "full-auto"
72
+ requireGit: true
73
+ ```
74
+
75
+ 5. **Review Codex's output critically.** Run `git status --short` (catches new files that `git diff` misses), then `git diff`, to see exactly what changed. Check for:
76
+ - Introduced bugs, logic errors, or regressions
77
+ - Style violations against the project's conventions
78
+ - Files modified outside the requested scope
79
+ - Missing test coverage if tests were part of the task
80
+
81
+ 6. **Present the result with your assessment.** State what Codex did correctly, what needs adjustment, and either accept the changes or fix/re-delegate. Codex is a **peer, not an authority** — you own the final quality.
82
+
83
+ ### Workflow 2: Independent code review (`review`)
84
+
85
+ Use when the user wants a second-opinion review of a diff, branch, or commit.
86
+
87
+ **Steps:**
88
+
89
+ 1. **Determine the scope of the review:**
90
+ - No argument → run `git status --short` first to catch new untracked files (`??`), then collect `git diff` (unstaged) + `git diff --cached` (staged), and include the contents of any new untracked files — plain `git diff` is blind to them
91
+ - Branch name → `git diff <branch>...HEAD`
92
+ - SHA → `git show <sha>`
93
+ - If there are no changes at all, tell the user and stop.
94
+
95
+ 2. **Check diff size.** If the diff exceeds ~50,000 characters, summarize it first and warn the user that Codex will see a truncated version.
96
+
97
+ 3. **Call the `codex_exec` MCP tool** in read-only mode:
98
+ ```
99
+ prompt: "Review the following code changes. For each finding, specify: severity (CRITICAL/HIGH/MEDIUM/LOW), file and line, description, and suggested fix. Focus on: bugs, security, performance, error handling, readability.\n\n```diff\n<diff>\n```"
100
+ mode: "exec"
101
+ requireGit: true
102
+ ```
103
+
104
+ 4. **Assign an overall verdict** (you are the final judge, not a relay): **BLOCKED** (≥1 CRITICAL/HIGH), **WARNING** (only MEDIUM/LOW), or **APPROVED** (none). State it explicitly, e.g. `Verdict: BLOCKED — 1 CRITICAL`.
105
+
106
+ 5. **Present findings grouped by severity.** For each finding, add your own assessment: agree, disagree with reasoning, or add nuance. Note anything Codex missed that you think is important. End with a summary of actionable items.
107
+
108
+ 6. **If BLOCKED or WARNING, offer a bounded fix loop** (with the user's go-ahead): fix the issues you agree are real, re-run `codex_exec` on the updated diff to confirm the fixes and catch regressions, and repeat until APPROVED or **3 rounds max** — then stop and summarize what's left. The cap prevents runaway Codex quota use.
109
+
110
+ ### Workflow 3: Consult for a second opinion (`consult`)
111
+
112
+ Use when you want Codex's perspective on a plan, hypothesis, or technical decision — no files modified.
113
+
114
+ **Steps:**
115
+
116
+ 1. **Frame the question precisely.** Include:
117
+ - The specific question or plan to evaluate
118
+ - Relevant context: file paths, code snippets, constraints
119
+ - What kind of feedback you want (validation, alternatives, risks)
120
+
121
+ 2. **Call `codex_exec`** in read-only mode:
122
+ ```
123
+ prompt: "Provide your analysis and recommendation on the following question. Consider tradeoffs, alternatives, and risks. Be specific.\n\n<question with context>"
124
+ mode: "exec"
125
+ ```
126
+
127
+ 3. **Synthesize both perspectives.** Present Codex's analysis, then your own independent analysis. State where you agree/disagree with evidence. Give a recommended path forward with reasoning.
128
+
129
+ ## The `codex_exec` tool
130
+
131
+ The skill-codex MCP server exposes exactly one tool: `codex_exec`.
132
+
133
+ **Parameters:**
134
+
135
+ | Param | Type | Required | Description |
136
+ |-------|------|----------|-------------|
137
+ | `prompt` | string | yes | The prompt to send to Codex |
138
+ | `mode` | `"exec"` \| `"full-auto"` | no (default `"exec"`) | `exec` is read-only; `full-auto` allows file writes |
139
+ | `sandbox` | `"read-only"` \| `"workspace-write"` \| `"danger-full-access"` | no | Explicit sandbox policy; overrides `mode`. Use `danger-full-access` only when you understand the risk |
140
+ | `sessionId` | string | no | Resume a prior Codex session (the thread id from a previous response) so Codex keeps context across calls |
141
+ | `model` | string | no | Codex model to use. Omit to use Codex's configured default |
142
+ | `reasoningEffort` | `"minimal"` \| `"low"` \| `"medium"` \| `"high"` \| `"xhigh"` | no | How much reasoning effort Codex spends. Omit for the model's default |
143
+ | `review` | boolean | no | Run Codex's native diff-scoped review (`codex exec review`) instead of a freeform prompt |
144
+ | `reviewBase` | string | no | With `review`: diff against this base branch |
145
+ | `reviewCommit` | string | no | With `review`: review the changes introduced by this commit SHA |
146
+ | `cwd` | string | no | Working directory for Codex |
147
+ | `timeoutMs` | number | no | Override default 5min timeout |
148
+ | `requireGit` | boolean | no | Refuse to run if cwd is not a git repo (recommended `true` for `full-auto`) |
149
+
150
+ **Modes:**
151
+ - `exec` → read-only. Use for review and consult.
152
+ - `full-auto` → can modify files. Use only for delegate, and always with `requireGit: true`.
153
+
154
+ ## Examples
155
+
156
+ ### Example 1: Delegate — generate tests
157
+
158
+ **User says:** "Write unit tests for `src/util/platform.ts`. Use vitest. Cover the `getClaudeDir` and `getHomeDir` functions including Windows/macOS/Linux branches."
159
+
160
+ **Actions:**
161
+ 1. Announce: `Delegating test generation to Codex.`
162
+ 2. Call `codex_exec` with `mode: "full-auto"`, `requireGit: true`, and a prompt specifying: the target file, test framework (vitest), test file path (`__tests__/util/platform.test.ts`), the two functions to cover, the three OS branches, and the project convention (TS strict, no `any`).
163
+ 3. `git diff` → verify only the new test file was created, imports are correct, no existing files touched.
164
+ 4. Report: `Codex generated 6 tests covering all three OS branches. All pass locally. No files outside scope modified.`
165
+
166
+ ### Example 2: Review — pre-commit diff
167
+
168
+ **User says:** "Review my changes before I commit."
169
+
170
+ **Actions:**
171
+ 1. Run `git status --short` (to catch new untracked files), then `git diff` → capture unstaged changes (fall back to `git diff --cached` if empty).
172
+ 2. Call `codex_exec` with `mode: "exec"` and the review prompt.
173
+ 3. Present: 1 CRITICAL (missing null check, `src/runner/runner.ts:47` — confirmed), 2 MEDIUM (disagree with one, agree with the other), 1 LOW (style nit, skip).
174
+ 4. Offer to fix the CRITICAL and the confirmed MEDIUM.
175
+
176
+ ### Example 3: Consult — architecture question
177
+
178
+ **User says:** "Should I use a lock file or a mutex for the codex subprocess?"
179
+
180
+ **Actions:**
181
+ 1. Frame the question with context: existing `src/lock/` module, single-machine usage, Windows compatibility requirement.
182
+ 2. Call `codex_exec` with `mode: "exec"`.
183
+ 3. Synthesize: Codex recommends lock file for cross-process safety; your analysis confirms it's the right choice given Windows named-mutex quirks; recommend proceeding with lock file approach.
184
+
185
+ ## Troubleshooting
186
+
187
+ **Error: `codex` binary not found**
188
+ - Cause: Codex CLI not installed or not on PATH.
189
+ - Solution: Tell the user to install it (`npm i -g @openai/codex` or equivalent) and re-run.
190
+
191
+ **Error: Authentication required**
192
+ - Cause: User hasn't run `codex login`.
193
+ - Solution: Tell the user to run `codex login` (uses ChatGPT Plus/Codex subscription) or set `OPENAI_API_KEY`.
194
+
195
+ **Error: Lock file conflict**
196
+ - Cause: Another Codex subprocess is already running in the same workspace.
197
+ - Solution: Wait for it to finish, or check for stale lock (PID dead, >15 min old) and retry.
198
+
199
+ **Error: Recursion limit reached**
200
+ - Cause: Codex is running inside Codex (nested `codex_exec` calls).
201
+ - Solution: Do not delegate from within a Codex-initiated session. Claude should handle the task directly.
202
+
203
+ **Error: Timeout (5 min default)**
204
+ - Cause: Task is too large or Codex is stuck.
205
+ - Solution: Break the task into smaller delegations, or override `timeoutMs` for genuinely long-running work.
206
+
207
+ **Codex returns but made wrong changes**
208
+ - Do not accept blindly. Run `git diff`, evaluate, then either fix the issue yourself or re-delegate with a refined prompt that addresses the specific mistake.
209
+
210
+ ## Critical rules
211
+
212
+ - **Never invoke Codex silently.** Always tell the user what you are delegating and why.
213
+ - **After `full-auto` runs, check `git status --short` then `git diff`.** `git status` catches new files `git diff` misses; verify scope and correctness before presenting results.
214
+ - **Fail soft.** If `codex_exec` errors (Codex missing, auth expired, offline), say so and continue with Claude-only work — never block the user on a missing Codex.
215
+ - **Codex is a peer, not an authority.** Every output must pass your review.
216
+ - **Do not delegate trivial tasks** (<50 lines or <2 minutes of direct work). Overhead exceeds benefit.
217
+ - **Pair `full-auto` with `requireGit: true`** so Codex refuses to touch non-git directories.