OpenCode Repository Memory: A Dumb PR Review Workflow
The general memory model is capture, distill, and approve. This page turns that model into a small repository-level OpenCode setup. It intentionally starts with a dumb proof: plain files, shell scripts, and explicit prompts. There is no automatic rule promotion and no database.
Research scope: This is a general workflow pattern documented by
myblog. The.opencode/,scripts/, and.agent-memory/paths below are illustrative files for a separate target repository or workspace. They are not part ofmyblog's own agent harness.
What the proof should demonstrate
The workflow has separate processes with separate responsibilities:
- Collect raw OpenCode events and PR review notes without interpreting them.
- Categorize an inbox item into a small, human-selected category.
- Propose a short candidate rule without editing the repository harness.
- Integrate each candidate interactively into
AGENTS.md, a skill, or another instruction file.
The first version can be run by hand. Once the file flow is trustworthy, a hook can automate only the collection step.
Repository layout
.opencode/
commands/review-memory.md
plugins/review-capture.ts
skills/pr-review-memory/SKILL.md
scripts/
review-memory/
collect-event.sh
collect-review.sh
categorize.sh
integrate.sh
.agent-memory/
reviews/
inbox/
categories/
proposals/
Keep .agent-memory/reviews/inbox/ and generated session material ignored. Keep
approved changes in normal repository files so they can be reviewed in the same
PR as any other change.
Step 1: prove collection with local files
Start without OpenCode. Export or paste a PR review into a Markdown file, then copy it into the inbox:
gh pr view 123 --comments > /tmp/pr-123-review.md
./scripts/review-memory/collect-review.sh /tmp/pr-123-review.md
The collector should do nothing except copy the source and add a timestamp:
#!/usr/bin/env bash
set -euo pipefail
source_file="${1:?usage: collect-review.sh REVIEW_FILE}"
root="$(git rev-parse --show-toplevel)"
stamp="$(date -u +%Y%m%dT%H%M%SZ)"
out="$root/.agent-memory/reviews/inbox/pr-review-$stamp.md"
mkdir -p "$(dirname "$out")"
cp -- "$source_file" "$out"
printf 'Collected %s\n' "$out"
At this stage, a human can inspect the copied file and delete bad or sensitive input before any later process sees it.
Step 2: add a repository-level OpenCode hook
OpenCode loads JavaScript or TypeScript plugins from .opencode/plugins/.
A minimal project plugin can record that a session became idle and delegate
the actual file write to the same shell-based collector:
import type { Plugin } from "@opencode-ai/plugin";
export const ReviewCapture: Plugin = async ({ $, worktree }) => ({
"session.idle": async () => {
await $`${worktree}/scripts/review-memory/collect-event.sh opencode session.idle`;
},
});
The event collector remains intentionally boring:
#!/usr/bin/env bash
set -euo pipefail
source_name="${1:?usage: collect-event.sh SOURCE EVENT}"
event_name="${2:?usage: collect-event.sh SOURCE EVENT}"
root="$(git rev-parse --show-toplevel)"
out="$root/.agent-memory/reviews/inbox/events.jsonl"
mkdir -p "$(dirname "$out")"
printf '{"source":"%s","event":"%s","at":"%s"}\n' \
"$source_name" "$event_name" "$(date -u +%Y-%m-%dT%H:%M:%SZ)" >> "$out"
This hook does not summarize conversations or edit instructions. It only leaves an auditable marker that a separate process can inspect later. Verify the hook against the installed OpenCode version because plugin event payloads and available event names can change.
Step 3: categorize in a separate process
Do not classify while collecting. Use a small interactive script after the inbox has been inspected:
#!/usr/bin/env bash
set -euo pipefail
input="${1:?usage: categorize.sh INBOX_FILE}"
root="$(git rev-parse --show-toplevel)"
name="$(basename "$input")"
PS3="Choose a category: "
select category in rules workflow testing security docs other; do
if [[ -n "$category" ]]; then
mkdir -p "$root/.agent-memory/reviews/categories/$category"
cp -- "$input" \
"$root/.agent-memory/reviews/categories/$category/$name"
printf 'Categorized as %s\n' "$category"
break
fi
done
Run it explicitly:
./scripts/review-memory/categorize.sh \
.agent-memory/reviews/inbox/pr-review-20260728T040000Z.md
The category list is deliberately small. Add a category only when repeated reviews show that the current list is insufficient.
Step 4: write a proposal, not a harness edit
A proposal can be written by a person or by an OpenCode session. The output is
just a short Markdown file in proposals/:
# Candidate rule
- Run `make build` before deploying documentation changes.
Evidence: PR #123, repeated reviewer comment about missing build validation.
Suggested target: AGENTS.md
An optional project command can constrain OpenCode to this behavior:
---
description: Propose repository memory from a reviewed PR note
---
Read the supplied PR review note. Write only one or more candidate rules to
.agent-memory/reviews/proposals/. Do not edit AGENTS.md, skills, or any other
harness file. Include the evidence and a suggested target for each rule.
This command is a proposal step, not an approval step.
Step 5: integrate with a human in the loop
The integration script shows each candidate, asks whether it should be kept, and asks where it belongs. It refuses to silently choose a target:
#!/usr/bin/env bash
set -euo pipefail
proposal="${1:?usage: integrate.sh PROPOSAL_FILE}"
root="$(git rev-parse --show-toplevel)"
while IFS= read -r line; do
[[ -z "$line" || "$line" == \#* || "$line" != "- "* ]] && continue
printf '\nCandidate:\n%s\n' "$line"
read -r -p "Add this candidate? [y/N] " approve
[[ "$approve" =~ ^[Yy]$ ]] || continue
PS3="Choose a harness target: "
select target in "AGENTS.md" \
".opencode/skills/pr-review-memory/SKILL.md" \
".github/copilot-instructions.md" "skip"; do
[[ -n "$target" ]] || continue
[[ "$target" == "skip" ]] && break
if [[ ! -f "$root/$target" ]]; then
printf 'Target does not exist; skipping: %s\n' "$target"
break
fi
printf '%s\n' "$line" >> "$root/$target"
printf 'Added to %s\n' "$target"
break
done
done < "$proposal"
The resulting edits are ordinary Git changes:
git diff -- AGENTS.md .opencode/skills .github/copilot-instructions.md
A maintainer can still reject, rewrite, or move a rule in the PR. The script is only a mechanical prompt around that decision; it is not a policy engine.
A minimal skill target
If the integration target is an OpenCode skill, create and review its normal frontmatter before allowing the script to append rules:
---
name: pr-review-memory
description: Apply approved project rules learned from PR review
---
## Approved rules
Project skills live under .opencode/skills/<skill-name>/SKILL.md. Keep the
skill focused on repeatable procedure; keep project facts and stable commands
in AGENTS.md when they should be loaded for every task.
Guardrails for the dumb version
- Raw inbox files are input, not instructions.
- The collector never categorizes or summarizes.
- The proposal process never edits harness files.
- The integration process asks about every candidate and target.
- Approved rules are reviewed as normal Git diffs.
- Redact credentials, customer data, and prompt-injected text before proposal.
Only after this proof is useful should a team consider automatic PR retrieval, semantic search, or a hosted memory service.