skill-codex 0.2.0
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/LICENSE +21 -0
- package/README.md +202 -0
- package/commands/codex-consult.md +36 -0
- package/commands/codex-do.md +44 -0
- package/commands/codex-review.md +35 -0
- package/dist/bin/skill-codex.js +467 -0
- package/dist/bin/skill-codex.js.map +1 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +722 -0
- package/dist/index.js.map +1 -0
- package/hooks/post-tool-use-review.ps1 +50 -0
- package/hooks/post-tool-use-review.sh +49 -0
- package/package.json +81 -0
|
@@ -0,0 +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"]}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
# skill-codex: PostToolUse hook for auto-review suggestions (Windows PowerShell)
|
|
2
|
+
# Triggered after Write/Edit tool usage in Claude Code
|
|
3
|
+
|
|
4
|
+
$input = $input | ConvertFrom-Json -ErrorAction SilentlyContinue
|
|
5
|
+
$toolName = $input.tool_name
|
|
6
|
+
|
|
7
|
+
# Only trigger for write/edit tools
|
|
8
|
+
if ($toolName -notin @("Write", "Edit", "MultiEdit", "NotebookEdit")) {
|
|
9
|
+
exit 0
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
# Skip if inside a bridge call
|
|
13
|
+
if ($env:SKILL_CODEX_DEPTH) {
|
|
14
|
+
exit 0
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
# Check git repo
|
|
18
|
+
try {
|
|
19
|
+
git rev-parse --is-inside-work-tree 2>$null | Out-Null
|
|
20
|
+
if ($LASTEXITCODE -ne 0) { exit 0 }
|
|
21
|
+
} catch {
|
|
22
|
+
exit 0
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
# Get change stats
|
|
26
|
+
$changedFiles = git diff --name-only 2>$null
|
|
27
|
+
$changedCount = ($changedFiles | Measure-Object -Line).Lines
|
|
28
|
+
$stat = git diff --stat 2>$null | Select-Object -Last 1
|
|
29
|
+
$totalLines = 0
|
|
30
|
+
if ($stat -match "(\d+) insertion") { $totalLines += [int]$Matches[1] }
|
|
31
|
+
if ($stat -match "(\d+) deletion") { $totalLines += [int]$Matches[1] }
|
|
32
|
+
|
|
33
|
+
# Check security paths
|
|
34
|
+
$securityHit = $false
|
|
35
|
+
$keywords = @("auth", "security", "crypto", "password", "secret", "token")
|
|
36
|
+
foreach ($keyword in $keywords) {
|
|
37
|
+
if ($changedFiles -match $keyword) {
|
|
38
|
+
$securityHit = $true
|
|
39
|
+
break
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
# Suggest review
|
|
44
|
+
if ($securityHit) {
|
|
45
|
+
Write-Output "[skill-codex] Security-sensitive files modified. Consider running /codex-review before committing."
|
|
46
|
+
} elseif ($changedCount -ge 3 -or $totalLines -ge 100) {
|
|
47
|
+
Write-Output "[skill-codex] Significant changes detected ($changedCount files, ~$totalLines lines). Consider running /codex-review."
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
exit 0
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# skill-codex: PostToolUse hook for auto-review suggestions
|
|
3
|
+
# Triggered after Write/Edit tool usage in Claude Code
|
|
4
|
+
# Outputs a suggestion for Claude to consider — does NOT auto-call MCP
|
|
5
|
+
|
|
6
|
+
# Read hook input from stdin (JSON with tool info)
|
|
7
|
+
INPUT=$(cat)
|
|
8
|
+
TOOL_NAME=$(echo "$INPUT" | grep -o '"tool_name":"[^"]*"' | head -1 | cut -d'"' -f4)
|
|
9
|
+
|
|
10
|
+
# Only trigger for write/edit tools
|
|
11
|
+
case "$TOOL_NAME" in
|
|
12
|
+
Write|Edit|MultiEdit|NotebookEdit) ;;
|
|
13
|
+
*) exit 0 ;;
|
|
14
|
+
esac
|
|
15
|
+
|
|
16
|
+
# Skip if we're already inside a bridge call (prevent recursion)
|
|
17
|
+
if [ -n "$SKILL_CODEX_DEPTH" ]; then
|
|
18
|
+
exit 0
|
|
19
|
+
fi
|
|
20
|
+
|
|
21
|
+
# Check if git is available and we're in a repo
|
|
22
|
+
if ! git rev-parse --is-inside-work-tree >/dev/null 2>&1; then
|
|
23
|
+
exit 0
|
|
24
|
+
fi
|
|
25
|
+
|
|
26
|
+
# Get summary of uncommitted changes
|
|
27
|
+
CHANGED_FILES=$(git diff --name-only 2>/dev/null)
|
|
28
|
+
CHANGED_COUNT=$(echo "$CHANGED_FILES" | grep -c . 2>/dev/null || echo "0")
|
|
29
|
+
LINES_CHANGED=$(git diff --stat 2>/dev/null | tail -1 | grep -o '[0-9]* insertion' | grep -o '[0-9]*' || echo "0")
|
|
30
|
+
LINES_DELETED=$(git diff --stat 2>/dev/null | tail -1 | grep -o '[0-9]* deletion' | grep -o '[0-9]*' || echo "0")
|
|
31
|
+
TOTAL_LINES=$((LINES_CHANGED + LINES_DELETED))
|
|
32
|
+
|
|
33
|
+
# Check for security-sensitive paths
|
|
34
|
+
SECURITY_HIT=""
|
|
35
|
+
for keyword in auth security crypto password secret token; do
|
|
36
|
+
if echo "$CHANGED_FILES" | grep -qi "$keyword"; then
|
|
37
|
+
SECURITY_HIT="yes"
|
|
38
|
+
break
|
|
39
|
+
fi
|
|
40
|
+
done
|
|
41
|
+
|
|
42
|
+
# Decide whether to suggest review
|
|
43
|
+
if [ -n "$SECURITY_HIT" ]; then
|
|
44
|
+
echo "[skill-codex] Security-sensitive files modified. Consider running /codex-review before committing."
|
|
45
|
+
elif [ "$CHANGED_COUNT" -ge 3 ] || [ "$TOTAL_LINES" -ge 100 ]; then
|
|
46
|
+
echo "[skill-codex] Significant changes detected ($CHANGED_COUNT files, ~$TOTAL_LINES lines). Consider running /codex-review."
|
|
47
|
+
fi
|
|
48
|
+
|
|
49
|
+
exit 0
|
package/package.json
ADDED
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "skill-codex",
|
|
3
|
+
"version": "0.2.0",
|
|
4
|
+
"description": "Cross-platform Claude Code skill that integrates OpenAI Codex CLI for code review, task delegation, and consultation via MCP",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "./dist/index.js",
|
|
7
|
+
"types": "./dist/index.d.ts",
|
|
8
|
+
"exports": {
|
|
9
|
+
".": {
|
|
10
|
+
"import": "./dist/index.js",
|
|
11
|
+
"types": "./dist/index.d.ts"
|
|
12
|
+
}
|
|
13
|
+
},
|
|
14
|
+
"bin": {
|
|
15
|
+
"skill-codex": "./dist/bin/skill-codex.js"
|
|
16
|
+
},
|
|
17
|
+
"scripts": {
|
|
18
|
+
"build": "tsup",
|
|
19
|
+
"dev": "tsup --watch",
|
|
20
|
+
"test": "vitest run",
|
|
21
|
+
"test:watch": "vitest",
|
|
22
|
+
"test:coverage": "vitest run --coverage",
|
|
23
|
+
"typecheck": "tsc --noEmit",
|
|
24
|
+
"prepublishOnly": "npm run build"
|
|
25
|
+
},
|
|
26
|
+
"files": [
|
|
27
|
+
"dist",
|
|
28
|
+
"commands",
|
|
29
|
+
"hooks",
|
|
30
|
+
"README.md",
|
|
31
|
+
"LICENSE"
|
|
32
|
+
],
|
|
33
|
+
"keywords": [
|
|
34
|
+
"claude-code",
|
|
35
|
+
"codex",
|
|
36
|
+
"openai",
|
|
37
|
+
"anthropic",
|
|
38
|
+
"mcp",
|
|
39
|
+
"ai-coding",
|
|
40
|
+
"code-review",
|
|
41
|
+
"developer-tools",
|
|
42
|
+
"mcp-server",
|
|
43
|
+
"model-context-protocol",
|
|
44
|
+
"codex-cli",
|
|
45
|
+
"llm-tools",
|
|
46
|
+
"ai-tools",
|
|
47
|
+
"code-review-automation",
|
|
48
|
+
"openai-codex",
|
|
49
|
+
"skill"
|
|
50
|
+
],
|
|
51
|
+
"author": "Arystos",
|
|
52
|
+
"license": "MIT",
|
|
53
|
+
"repository": {
|
|
54
|
+
"type": "git",
|
|
55
|
+
"url": "git+https://github.com/Arystos/skill-codex.git"
|
|
56
|
+
},
|
|
57
|
+
"homepage": "https://github.com/Arystos/skill-codex#readme",
|
|
58
|
+
"bugs": {
|
|
59
|
+
"url": "https://github.com/Arystos/skill-codex/issues"
|
|
60
|
+
},
|
|
61
|
+
"funding": {
|
|
62
|
+
"type": "ko-fi",
|
|
63
|
+
"url": "https://ko-fi.com/arystos"
|
|
64
|
+
},
|
|
65
|
+
"engines": {
|
|
66
|
+
"node": ">=18"
|
|
67
|
+
},
|
|
68
|
+
"dependencies": {
|
|
69
|
+
"@modelcontextprotocol/sdk": "^1.26.0",
|
|
70
|
+
"which": "^4.0.0",
|
|
71
|
+
"zod": "^3.23.0"
|
|
72
|
+
},
|
|
73
|
+
"devDependencies": {
|
|
74
|
+
"@types/node": "^22.0.0",
|
|
75
|
+
"@types/which": "^3.0.0",
|
|
76
|
+
"tsup": "^8.0.0",
|
|
77
|
+
"typescript": "^5.7.0",
|
|
78
|
+
"vitest": "^2.0.0",
|
|
79
|
+
"@vitest/coverage-v8": "^2.0.0"
|
|
80
|
+
}
|
|
81
|
+
}
|