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.
Port of openai/codex-plugin-cc#129 (open, novel workflow feature)
Problem
/opencode:reviewand/opencode:rescuedon'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:Proposed UX
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 contextDescribe a new task— ask what to investigateImplementation — 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
handleReviewandhandleAdversarialReviewinopencode-companion.mjs, afterrunTrackedJobreturns successfully, write the rendered output to a per-repo file:2. New subcommand
last-reviewthat reports availability and content:3.
commands/rescue.mdadditions:Before the existing "if the user did not supply a request, ask what OpenCode should investigate" line, add:
LAST_REVIEW_AVAILABLE: useAskUserQuestiononce with two options:Fix issues from last review (Recommended)— read the review content and prepend it as contextDescribe a new task— ask what OpenCode should investigate or fixnode "${CLAUDE_PLUGIN_ROOT}/scripts/opencode-companion.mjs" last-review --contentNO_LAST_REVIEW: ask what OpenCode should investigate or fix.