Skip to content

Port: pass last review findings to rescue automatically #44

@JohnnyVicious

Description

@JohnnyVicious

Port of openai/codex-plugin-cc#129 (open, novel workflow feature)

Problem

/opencode:review and /opencode:rescue don't share context today. After running a review, users either copy-paste findings into the rescue prompt or describe them from memory. The most common workflow is broken by default:

/opencode:review                   ← finds issues
/opencode:rescue                   ← has no idea what was found

Proposed UX

/opencode:review                   ← finds issues, saves to ~/.opencode-companion/last-review-<repo-hash>.md
/opencode:rescue                   ← detects saved review, offers "Fix issues from last review (Recommended)"

With explicit task text, rescue behaves as today. Without task text, rescue checks for a saved review for the current repo and asks via AskUserQuestion:

  • Fix issues from last review (Recommended) — prepend the saved review as context
  • Describe a new task — ask what to investigate

Implementation — cleaner than upstream's one-liner

Upstream codex-plugin-cc#129 did this via an inline node -e "..." one-liner in each command file. That's ugly and hard to audit. For opencode, push the save/load logic into the companion script itself:

1. Save after a successful review. In handleReview and handleAdversarialReview in opencode-companion.mjs, after runTrackedJob returns successfully, write the rendered output to a per-repo file:

import crypto from "node:crypto";
import os from "node:os";

function lastReviewPath(workspace) {
  const hash = crypto.createHash("sha256").update(workspace).digest("hex").slice(0, 16);
  const dir = path.join(os.homedir(), ".opencode-companion");
  return { dir, file: path.join(dir, `last-review-${hash}.md`) };
}

// After successful runTrackedJob in handleReview / handleAdversarialReview:
try {
  const { dir, file } = lastReviewPath(workspace);
  fs.mkdirSync(dir, { recursive: true });
  fs.writeFileSync(file, result.rendered, "utf8");
} catch {
  // Non-fatal: review succeeded, persistence is best-effort.
}

2. New subcommand last-review that reports availability and content:

// Dispatch table addition:
"last-review": handleLastReview,

async function handleLastReview(argv) {
  const { options } = parseArgs(argv, { booleanOptions: ["json", "content"] });
  const workspace = await resolveWorkspace();
  const { file } = lastReviewPath(workspace);

  if (!fs.existsSync(file)) {
    if (options.json) console.log(JSON.stringify({ available: false }));
    else console.log("NO_LAST_REVIEW");
    return;
  }

  if (options.content) {
    process.stdout.write(fs.readFileSync(file, "utf8"));
    return;
  }

  if (options.json) {
    const stat = fs.statSync(file);
    console.log(JSON.stringify({ available: true, updatedAt: stat.mtime.toISOString(), path: file }));
  } else {
    console.log("LAST_REVIEW_AVAILABLE");
  }
}

3. commands/rescue.md additions:

Before the existing "if the user did not supply a request, ask what OpenCode should investigate" line, add:

- If the user did not supply a request, check for a saved review from `/opencode:review` or `/opencode:adversarial-review`:
```bash
node "${CLAUDE_PLUGIN_ROOT}/scripts/opencode-companion.mjs" last-review
  • If stdout is LAST_REVIEW_AVAILABLE: use AskUserQuestion once with two options:
    • Fix issues from last review (Recommended) — read the review content and prepend it as context
    • Describe a new task — ask what OpenCode should investigate or fix
  • If the user chooses to fix from last review, read the review file via:
node "${CLAUDE_PLUGIN_ROOT}/scripts/opencode-companion.mjs" last-review --content
and include the output verbatim in the rescue prompt, prefixed with:
"The following issues were found in a prior OpenCode review. Please fix them:\n\n"
  • If stdout is NO_LAST_REVIEW: ask what OpenCode should investigate or fix.

### Why this is better than the upstream one-liner

- The logic lives in tested node code, not a shell heredoc.
- One canonical implementation of the per-repo hash (upstream uses MD5; use SHA-256 here to match the rest of opencode's state-path hashing in `lib/state.mjs`).
- No `os.homedir()` computation inside an embedded shell expression — cleaner Windows behavior.
- The per-repo last-review file can be extended later (e.g. multi-review history, timestamps) without touching command files.

### Test plan

1. Run a review, verify `~/.opencode-companion/last-review-<hash>.md` is created.
2. `last-review` returns `LAST_REVIEW_AVAILABLE` after a successful review, `NO_LAST_REVIEW` before any review.
3. `last-review --content` streams the review content verbatim.
4. `last-review --json` returns `{ available, updatedAt, path }`.
5. Failed review does **not** overwrite a previous good review file.
6. Two different repos have independent last-review files (different hashes).

### Upstream reference

openai/codex-plugin-cc#129 (open) — adapt the workflow, skip the one-liner shell implementation.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions