From df16bd5d1a4b19b1fcec53312b52dc1cf9b2a0b5 Mon Sep 17 00:00:00 2001 From: Yelban Date: Thu, 21 May 2026 16:25:44 +0800 Subject: [PATCH] feat(codex-imagegen): cwd-drift immunity (path resolve + skip-git-repo-check) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Two improvements so the wrapper works the same regardless of caller cwd or whether cwd is a git repo: 1. main.ts: resolve all filesystem path args (--prompt-file, --ref, --cache-dir, --log-file) to absolute paths up front. Previously only --image was resolved. Agents passing relative paths from arbitrary working directories (e.g. when SKILL.md is interpreted from a plugin install dir) now produce correct file lookups. 2. spawn.ts: pass --skip-git-repo-check to 'codex exec'. Without it, codex refuses to run outside a trusted directory: 'Not inside a trusted directory and --skip-git-repo-check was not specified.' This made the wrapper fail when invoked from /tmp or from a non-git project tree. 3. baoyu-cover-image/SKILL.md: explicit path resolution note — scripts/codex-imagegen.sh lives at plugin/repo root (not shell cwd-relative). From the skill base directory it is at '../../scripts/codex-imagegen.sh'. Agents should resolve to an absolute path before invoking. Verified: - 16 unit tests pass - e2e from /tmp (non-git, relative-path args) → 45s, 1.6MB PNG, status:ok Composes with the existing cover-image wiring (commit e3932e4). --- scripts/codex-imagegen/main.ts | 17 +++++++++++++---- scripts/codex-imagegen/spawn.ts | 11 ++++++++++- skills/baoyu-cover-image/SKILL.md | 14 +++++++------- 3 files changed, 30 insertions(+), 12 deletions(-) diff --git a/scripts/codex-imagegen/main.ts b/scripts/codex-imagegen/main.ts index 76dfbc4..5de567f 100644 --- a/scripts/codex-imagegen/main.ts +++ b/scripts/codex-imagegen/main.ts @@ -71,13 +71,22 @@ function parseArgs(argv: string[]): CliOptions { } if (!opts.outputPath) throw new GenError("invalid_args", "--image is required", false); if (!opts.prompt && !promptFile) throw new GenError("invalid_args", "--prompt or --prompt-file required", false); + + // Resolve every filesystem path to absolute up front, so behavior is + // independent of the caller's cwd. This matters when the wrapper is + // invoked from a skill running in an arbitrary working directory. + const cwd = process.cwd(); + const toAbs = (p: string) => (path.isAbsolute(p) ? p : path.resolve(cwd, p)); + + opts.outputPath = toAbs(opts.outputPath); if (promptFile) { opts.prompt = ""; // will be loaded later - (opts as any).__promptFile = promptFile; - } - if (!path.isAbsolute(opts.outputPath)) { - opts.outputPath = path.resolve(process.cwd(), opts.outputPath); + (opts as any).__promptFile = toAbs(promptFile); } + opts.refImages = opts.refImages.map(toAbs); + if (opts.cacheDir) opts.cacheDir = toAbs(opts.cacheDir); + if (opts.logFile) opts.logFile = toAbs(opts.logFile); + return opts; } diff --git a/scripts/codex-imagegen/spawn.ts b/scripts/codex-imagegen/spawn.ts index 1e79caf..f422c54 100644 --- a/scripts/codex-imagegen/spawn.ts +++ b/scripts/codex-imagegen/spawn.ts @@ -16,7 +16,16 @@ export async function runCodexExec(input: SpawnInput): Promise { const logDir = await mkdtemp(path.join(tmpdir(), "codex-imggen-")); const rawLogPath = path.join(logDir, "stream.jsonl"); - const args = ["exec", "--json", "--sandbox", "danger-full-access"]; + // --skip-git-repo-check: lets the wrapper run from non-git cwds + // (e.g. /tmp, or a skill installed under ~/.claude/plugins/...). + // Without it, codex refuses with "Not inside a trusted directory". + const args = [ + "exec", + "--json", + "--sandbox", + "danger-full-access", + "--skip-git-repo-check", + ]; for (const img of input.refImages ?? []) { args.push("--image", img); } diff --git a/skills/baoyu-cover-image/SKILL.md b/skills/baoyu-cover-image/SKILL.md index b4effdb..d62b0a7 100644 --- a/skills/baoyu-cover-image/SKILL.md +++ b/skills/baoyu-cover-image/SKILL.md @@ -29,17 +29,17 @@ When this skill needs to render an image, resolve the backend in this order: 2. **Saved preference** — if `EXTEND.md` sets `preferred_image_backend` to a backend available right now, use it. 3. **Auto-select** (when the preference is `auto`, unset, or the pinned backend isn't available): - **Codex (`imagegen`)** — first, inspect your available-skills / tool inventory. If a skill named `imagegen` is listed, you are running inside Codex and MUST use it: invoke via the `Skill` tool with `skill: "imagegen"`, passing the saved prompt file's content (plus output path and aspect ratio per Codex `imagegen`'s own args). Codex `imagegen` is the official raster backend in that runtime and outranks any non-native skill (e.g., `baoyu-imagine`) unless the user has explicitly pinned a different `preferred_image_backend`. - - **Codex via `codex exec` (`codex-imagegen`)** — if the current runtime does NOT expose a native `imagegen` skill but the `codex` CLI is on `PATH` and `codex login` is active (e.g., Claude Code with Codex CLI installed), invoke the `codex-imagegen` wrapper. Command shape: + - **Codex via `codex exec` (`codex-imagegen`)** — if the current runtime does NOT expose a native `imagegen` skill but the `codex` CLI is on `PATH` and `codex login` is active (e.g., Claude Code with Codex CLI installed), invoke the `codex-imagegen` wrapper. **Path resolution**: `scripts/codex-imagegen.sh` lives at the plugin/repo root, NOT relative to your shell cwd. From this `SKILL.md`'s base directory, the wrapper is at `../../scripts/codex-imagegen.sh` — resolve to an absolute path before invoking. Command shape: ```bash - ./scripts/codex-imagegen.sh \ + /scripts/codex-imagegen.sh \ --image \ - --prompt-file \ + --prompt-file \ --aspect \ - [--ref ]... \ + [--ref ]... \ [--cache-dir ~/.cache/baoyu-codex-imagegen] \ - [--log-file ] + [--log-file ] ``` - Parse the single-line JSON on stdout. On `{"status":"ok",...}` proceed to Step 5. On `{"status":"error","error_kind":...}` report the `error_kind` to the user and (if retryable) ask whether to retry or fall back to another backend. The wrapper uses the user's Codex subscription — no `OPENAI_API_KEY` needed. + All input paths to the wrapper are auto-resolved against the wrapper's `process.cwd()` if you pass relative ones, but agents should pass absolute paths to be robust against cwd drift. Parse the single-line JSON on stdout. On `{"status":"ok",...}` proceed to Step 5. On `{"status":"error","error_kind":...}` report the `error_kind` to the user and (if retryable) ask whether to retry or fall back to another backend. The wrapper uses the user's Codex subscription — no `OPENAI_API_KEY` needed. - **Other runtime-native tools** — if the runtime exposes a different native image tool (e.g., Hermes `image_generate`), use it the same way. - Otherwise, if exactly one non-native backend is installed (e.g., `baoyu-imagine`), use it. - Otherwise (multiple non-native backends with no runtime-native tool), ask the user once — batch with any other initial questions. @@ -226,7 +226,7 @@ Save to `prompts/cover.md`. Template: [references/workflow/prompt-template.md](r - `direct` usage → pass via `--ref` (use ref-capable backend) - `style`/`palette` → extract traits, append to prompt 5. **Generate**: Call the chosen backend with the prompt file, output path, aspect ratio. - - **`codex-imagegen`**: run `./scripts/codex-imagegen.sh --image --prompt-file prompts/01-cover-[slug].md --aspect ` (add `--ref ` per reference, `--cache-dir ~/.cache/baoyu-codex-imagegen` to enable the idempotency cache). Read the stdout JSON; act on `status` and `error_kind`. + - **`codex-imagegen`**: invoke `/scripts/codex-imagegen.sh` (NOT a cwd-relative `./scripts/...` — resolve the absolute path from this skill's base directory: `../../scripts/codex-imagegen.sh`) with `--image ` `--prompt-file ` `--aspect ` (add `--ref ` per reference, `--cache-dir ~/.cache/baoyu-codex-imagegen` to enable the idempotency cache). All input paths to the wrapper are auto-resolved against its `process.cwd()` if relative, but passing absolutes is more robust. Read the stdout JSON; act on `status` and `error_kind`. - **Codex `imagegen` (native)** or other runtime-native tools / `baoyu-imagine` skill: per the rule in `## Image Generation Tools` above. 6. On failure: auto-retry once