Compare commits
7 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 6360899e1c | |||
| 65486f2878 | |||
| 64dc174d83 | |||
| a1266348d6 | |||
| 1a79ae57ec | |||
| 19642ad95f | |||
| 9cb4fe4099 |
@@ -5,13 +5,13 @@
|
||||
},
|
||||
"metadata": {
|
||||
"description": "Codex plugins to use in Claude Code for delegation and code review.",
|
||||
"version": "1.0.0"
|
||||
"version": "1.0.2"
|
||||
},
|
||||
"plugins": [
|
||||
{
|
||||
"name": "codex",
|
||||
"description": "Use Codex from Claude Code to review code or delegate tasks.",
|
||||
"version": "1.0.0",
|
||||
"version": "1.0.2",
|
||||
"author": {
|
||||
"name": "OpenAI"
|
||||
},
|
||||
|
||||
Generated
+2
-2
@@ -1,12 +1,12 @@
|
||||
{
|
||||
"name": "@openai/codex-plugin-cc",
|
||||
"version": "1.0.0",
|
||||
"version": "1.0.2",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "@openai/codex-plugin-cc",
|
||||
"version": "1.0.0",
|
||||
"version": "1.0.2",
|
||||
"license": "Apache-2.0",
|
||||
"devDependencies": {
|
||||
"@types/node": "^25.5.0",
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@openai/codex-plugin-cc",
|
||||
"version": "1.0.0",
|
||||
"version": "1.0.2",
|
||||
"private": true,
|
||||
"type": "module",
|
||||
"description": "Use Codex from Claude Code to review code or delegate tasks.",
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
{
|
||||
"name": "codex",
|
||||
"version": "1.0.2",
|
||||
"description": "Use Codex from Claude Code to review code or delegate tasks.",
|
||||
"author": {
|
||||
"name": "OpenAI"
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
description: Delegate investigation, an explicit fix request, or follow-up rescue work to the Codex rescue subagent
|
||||
argument-hint: "[--background|--wait] [--resume|--fresh] [--model <model|spark>] [--effort <none|minimal|low|medium|high|xhigh>] [what Codex should investigate, solve, or continue]"
|
||||
context: fork
|
||||
allowed-tools: Bash(node:*)
|
||||
allowed-tools: Bash(node:*), AskUserQuestion
|
||||
---
|
||||
|
||||
Route this request to the `codex:codex-rescue` subagent.
|
||||
|
||||
@@ -14,6 +14,7 @@ import { spawn } from "node:child_process";
|
||||
import readline from "node:readline";
|
||||
import { parseBrokerEndpoint } from "./broker-endpoint.mjs";
|
||||
import { ensureBrokerSession } from "./broker-lifecycle.mjs";
|
||||
import { terminateProcessTree } from "./process.mjs";
|
||||
|
||||
const PLUGIN_MANIFEST_URL = new URL("../../.claude-plugin/plugin.json", import.meta.url);
|
||||
const PLUGIN_MANIFEST = JSON.parse(fs.readFileSync(PLUGIN_MANIFEST_URL, "utf8"));
|
||||
@@ -188,7 +189,9 @@ class SpawnedCodexAppServerClient extends AppServerClientBase {
|
||||
this.proc = spawn("codex", ["app-server"], {
|
||||
cwd: this.cwd,
|
||||
env: this.options.env,
|
||||
stdio: ["pipe", "pipe", "pipe"]
|
||||
stdio: ["pipe", "pipe", "pipe"],
|
||||
shell: process.platform === "win32",
|
||||
windowsHide: true
|
||||
});
|
||||
|
||||
this.proc.stdout.setEncoding("utf8");
|
||||
@@ -237,8 +240,20 @@ class SpawnedCodexAppServerClient extends AppServerClientBase {
|
||||
if (this.proc && !this.proc.killed) {
|
||||
this.proc.stdin.end();
|
||||
setTimeout(() => {
|
||||
if (this.proc && !this.proc.killed) {
|
||||
this.proc.kill("SIGTERM");
|
||||
if (this.proc && !this.proc.killed && this.proc.exitCode === null) {
|
||||
// On Windows with shell: true, the direct child is cmd.exe.
|
||||
// Use terminateProcessTree to kill the entire tree including
|
||||
// the grandchild node process.
|
||||
if (process.platform === "win32") {
|
||||
try {
|
||||
terminateProcessTree(this.proc.pid);
|
||||
} catch {
|
||||
// Best-effort cleanup inside an unref'd timer — swallow errors
|
||||
// to avoid crashing the host process during shutdown.
|
||||
}
|
||||
} else {
|
||||
this.proc.kill("SIGTERM");
|
||||
}
|
||||
}
|
||||
}, 50).unref?.();
|
||||
}
|
||||
|
||||
@@ -8,7 +8,8 @@ export function runCommand(command, args = [], options = {}) {
|
||||
encoding: "utf8",
|
||||
input: options.input,
|
||||
stdio: options.stdio ?? "pipe",
|
||||
shell: process.platform === "win32"
|
||||
shell: process.platform === "win32",
|
||||
windowsHide: true
|
||||
});
|
||||
|
||||
return {
|
||||
|
||||
@@ -2,8 +2,9 @@ import fs from "node:fs";
|
||||
import path from "node:path";
|
||||
import test from "node:test";
|
||||
import assert from "node:assert/strict";
|
||||
import { fileURLToPath } from "node:url";
|
||||
|
||||
const ROOT = "/Users/dkundel/code/codex-plugin";
|
||||
const ROOT = path.resolve(path.dirname(fileURLToPath(import.meta.url)), "..");
|
||||
const PLUGIN_ROOT = path.join(ROOT, "plugins", "codex");
|
||||
|
||||
function read(relativePath) {
|
||||
@@ -89,6 +90,7 @@ test("rescue command absorbs continue semantics", () => {
|
||||
const runtimeSkill = read("skills/codex-cli-runtime/SKILL.md");
|
||||
|
||||
assert.match(rescue, /The final user-visible response must be Codex's output verbatim/i);
|
||||
assert.match(rescue, /allowed-tools:\s*Bash\(node:\*\),\s*AskUserQuestion/);
|
||||
assert.match(rescue, /--background\|--wait/);
|
||||
assert.match(rescue, /--resume\|--fresh/);
|
||||
assert.match(rescue, /--model <model\|spark>/);
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
import fs from "node:fs";
|
||||
import path from "node:path";
|
||||
import process from "node:process";
|
||||
|
||||
import { writeExecutable } from "./helpers.mjs";
|
||||
|
||||
@@ -507,11 +509,19 @@ rl.on("line", (line) => {
|
||||
});
|
||||
`;
|
||||
writeExecutable(scriptPath, source);
|
||||
|
||||
// On Windows, npm global binaries are invoked via .cmd wrappers.
|
||||
// Create a codex.cmd so the fake binary is discoverable by spawn with shell: true.
|
||||
if (process.platform === "win32") {
|
||||
const cmdWrapper = `@echo off\r\nnode "%~dp0codex" %*\r\n`;
|
||||
fs.writeFileSync(path.join(binDir, "codex.cmd"), cmdWrapper, { encoding: "utf8" });
|
||||
}
|
||||
}
|
||||
|
||||
export function buildEnv(binDir) {
|
||||
const sep = process.platform === "win32" ? ";" : ":";
|
||||
return {
|
||||
...process.env,
|
||||
PATH: `${binDir}:${process.env.PATH}`
|
||||
PATH: `${binDir}${sep}${process.env.PATH}`
|
||||
};
|
||||
}
|
||||
|
||||
+4
-1
@@ -1,6 +1,7 @@
|
||||
import fs from "node:fs";
|
||||
import os from "node:os";
|
||||
import path from "node:path";
|
||||
import process from "node:process";
|
||||
import { spawnSync } from "node:child_process";
|
||||
|
||||
export function makeTempDir(prefix = "codex-plugin-test-") {
|
||||
@@ -16,7 +17,9 @@ export function run(command, args, options = {}) {
|
||||
cwd: options.cwd,
|
||||
env: options.env,
|
||||
encoding: "utf8",
|
||||
input: options.input
|
||||
input: options.input,
|
||||
shell: process.platform === "win32" && !path.isAbsolute(command),
|
||||
windowsHide: true
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -3,13 +3,14 @@ import path from "node:path";
|
||||
import test from "node:test";
|
||||
import assert from "node:assert/strict";
|
||||
import { spawn } from "node:child_process";
|
||||
import { fileURLToPath } from "node:url";
|
||||
|
||||
import { buildEnv, installFakeCodex } from "./fake-codex-fixture.mjs";
|
||||
import { initGitRepo, makeTempDir, run } from "./helpers.mjs";
|
||||
import { loadBrokerSession } from "../plugins/codex/scripts/lib/broker-lifecycle.mjs";
|
||||
import { resolveStateDir } from "../plugins/codex/scripts/lib/state.mjs";
|
||||
|
||||
const ROOT = "/Users/dkundel/code/codex-plugin";
|
||||
const ROOT = path.resolve(path.dirname(fileURLToPath(import.meta.url)), "..");
|
||||
const PLUGIN_ROOT = path.join(ROOT, "plugins", "codex");
|
||||
const SCRIPT = path.join(PLUGIN_ROOT, "scripts", "codex-companion.mjs");
|
||||
const STOP_HOOK = path.join(PLUGIN_ROOT, "scripts", "stop-review-gate-hook.mjs");
|
||||
|
||||
Reference in New Issue
Block a user