Turn repeated AI review comments into durable repository memory.
Audience · Why · How · Quick start · Features · Examples · Privacy
CodeRabbit, Copilot, Claude, Cursor, and other AI reviewers can be useful, but the same suggestions often come back across PRs.
promote-cli scans your PR review history, clusters repeated comments, and helps you promote those patterns into durable repo memory:
CLAUDE.md/AGENTS.md- GitHub Copilot instructions
- Cursor / Windsurf / Gemini rules
- path-scoped rules
- ADRs
- test recommendations
It runs locally, uses your own LLM API key, and asks for human approval before writing.
npm i -g promote-cli
promote init
promote scan --since 30dpromote-cli is for teams that:
- use AI reviewers such as CodeRabbit, Copilot, Claude, Cursor, Greptile, Qodo, Devin, or Sourcery
- keep repo instructions in
CLAUDE.md,AGENTS.md,.github/copilot-instructions.md,.cursor/rules/, or similar files - see the same review comments repeated across PRs
- want a human-approved way to turn repeated review feedback into durable rules, ADRs, or tests
It is probably not useful if your repo does not use PR review comments or coding-agent instruction files.
AI review tools leave comments on PRs. Developers resolve them and move on. The decision disappears into a closed PR thread.
But some of those comments aren't just about the current diff — they reveal implicit knowledge that the repository doesn't have written down. A convention no one documented. An architectural decision no one recorded. An invariant no one tested.
If the same comment appears next week, the team pays the same review cost again. AI agents now read your repo's instructions (CLAUDE.md, AGENTS.md, .cursor/rules/) — but no tool helps you keep those instructions in sync with what your reviewers actually flag.
"The human reviewer's role is no longer to trace code details, but to measure the distance between decisions and implementation."
The name promote reflects that shift: review comments aren't disposable noise — they're knowledge waiting to be elevated into your repo's durable memory. The decision is still yours: whether and where each pattern belongs.
| Without any tool | Hand-writing AGENTS.md | With promote-cli | |
|---|---|---|---|
| Capture repeated patterns | ❌ Lost in closed PRs | ✅ Mined from review history | |
| Cluster duplicates | ❌ Same comment, weekly | ✅ Embedding + LLM hybrid | |
| Route to the right file | — | ✅ Suggested target | |
| Human approval | — | ✅ | ✅ Required per candidate |
| Evidence trail to PRs | — | ❌ | ✅ Links to source comments |
| Cost | — | Free / hours of yours | $0.07–$0.47 per scan |
flowchart LR
A[Ingest<br/>PR comments] --> B[Cluster<br/>similar items]
B --> C[Classify<br/>suggested target]
C --> D[Promote<br/>you approve]
- Ingest — pulls AI bot review comments + human replies + 👍/👎 reactions from your repo's PR history
- Cluster — groups similar comments using embedding+HAC (
quick) or LLM-direct (broad) — pick with--mode - Classify — picks a target: agents-level rule, path-scoped rule, ADR, test recommendation, or
none - Promote — drafts a patch, links the source comments, hands it to you to approve
Conservative by default — every promotion is human-confirmed, and clusters dropped during classify (low confidence, not promotable, already handled) surface in the digest's Filtered out appendix with the classifier's reasoning, instead of being silently discarded.
Install.
npm i -g promote-cli1. Initialize. Interactive setup — pick provider (OpenAI / Anthropic / Google), AI tool target (Claude Code / Codex / Copilot / Cursor / Windsurf / Gemini), and output language.
promote init2. Scan. Ingest comments, cluster, classify, then enter interactive review.
promote scan --since 30d3. Review. Walk through each candidate — promote or skip per item. Approved ones land in your chosen target file immediately. See Examples for what a real digest and patch look like.
─── Candidate 1/7 ───
React hooks should use named imports instead of default imports
Target path_scoped_rule → .claude/rules/react-imports.instructions.md
Confidence 0.85
Occurrences 3 across 2 PRs
> Promote → path_scoped_rule
> Promote (different target)
> Show full patch
> Skip
After the candidates, if anything was filtered out during classify you can browse those too, and any candidates you skipped can be appended to the digest for team review:
Also walk through 4 skipped item(s)?
> Walk through them one by one
> Skip all
─── Skipped 1/4 ───
Tests should import vitest helpers explicitly
Reason below confidence threshold
Target agents
Confidence 0.62
Detail Pattern only appeared in 2 PRs; below minConfidence threshold
> Next
> Skip remaining
Add 2 skipped candidate(s) to digest for team review? (Y/n)
For permanent dismissal or deferral outside the interactive flow, use promote ignore <id> or promote snooze <id> directly.
BYOK — you bring your own API key. promote never proxies through a server.
4. (optional) Save a digest. Every promote scan writes a digest to docs/promote/digests/{date}.md by default — pass --out to override the path. The directory sits inside docs/ so digests can be committed alongside the memory PR (the --create-pr flow bundles the digest into the same commit). The digest carries the promotion candidates plus a Filtered out appendix listing every cluster the classifier dropped (with its reason), and a Skipped during review section if you deferred candidates during interactive review. Handy for PR descriptions, weekly team reviews, or CI artifacts.
promote scan --since 30d --out promote-digest.mdOutput is localized per language.preferredOutput in .promote.yml (en / ko / ja).
- Hybrid clustering. Embedding+HAC pre-cluster is cheap (~$0.07/scan); LLM refinement only on borderline pairs — accurate without paying for LLM-on-every-comment. Switch with
--mode quick|broad. (details) - Multi-tool aware. Routes the same finding to
CLAUDE.md,AGENTS.md,.github/copilot-instructions.md,.cursor/rules/,.windsurf/rules/, orGEMINI.md— pick atinit, change anytime. - Multi-provider BYOK. OpenAI, Anthropic, or Google. No hosted backend, no proxy. Free tier available on Google.
- Reads human signal. Picks up reply threads ("this is intentional"), 👍/👎 reactions, and reviewer agreement; boosts confidence when 2+ reviewers concurred, flags
needsHumanDecisionwhen the original commenter walked it back. - Filter transparency. Clusters dropped by the classifier (low confidence, not promotable, already handled, classify error) are captured with the LLM's reason and listed under
Filtered outin the digest. Optionally browsable interactively (Next / Skip remaining) — tune thresholds or sanity-check edge cases in team review. - Evidence trail. Every promoted rule links back to the PR comments it came from — auditable, not vibes.
- Stable candidate IDs. Same pattern keeps the same ID across rescans, so deferred decisions don't get lost on the next run.
- Secret redaction. AWS keys, tokens, JWTs stripped before any LLM call.
Want to see representative output from real scans before installing?
- Sample quick digest (trpc/trpc, OpenAI
quick) - Sample broad digest (trpc/trpc, Anthropic
broad) CLAUDE.mdpatch example- Cursor rule patch example
Both digests come from real promote scan runs against the public trpc/trpc repository.
promote-cli is designed as a local BYOK tool.
- No hosted backend. Your comments are never proxied through a promote server.
- Your provider key. LLM calls go directly from your machine or CI runner to OpenAI, Anthropic, or Google.
- Secret redaction. Tokens, AWS keys, JWTs, and similar secrets are stripped before any LLM call (
privacy.redactSecrets: trueby default). - No diff hunks by default.
privacy.sendDiffHunksToLLMdefaults tofalse— comments are analyzed without sending the surrounding code diff. - Human approval. In interactive mode, every candidate is reviewed before anything is written.
- CI guardrail. Headless mode can open a PR, but candidates flagged
needs_human_decisionare never auto-applied.
See SECURITY.md for how to report a vulnerability.
Measured on trpc/trpc, 120-day window, 380 actionable AI comments:
| Mode + provider | Candidates | Cost | Wall time | Output style |
|---|---|---|---|---|
OpenAI quick |
24 | $0.07 | 2m 14s | Narrow, file-specific |
OpenAI broad |
8 | $0.10 | 2m 39s | Core conventions only |
Anthropic broad (Haiku 4.5) |
17 | $0.45 | 4m 55s | Convention / principle / ADR mix |
Picking a mode by cadence:
| Cadence | Recommended | Why |
|---|---|---|
| Weekly / biweekly | OpenAI quick |
Cheap, fast, catches narrow code patterns as they emerge |
| Monthly | Anthropic broad |
Higher cost but extracts conventions worth memorializing |
| Quarterly / sprint-end | Anthropic broad + optional --mode quick follow-up |
Combined coverage: principles from broad, code-level from quick |
No Anthropic key? OpenAI broad is the "budget" alternative — reliably catches the 6–8 core repo-wide conventions at ~5× lower cost than Anthropic broad, though without the same depth of convention / principle / ADR-style candidates.
Full breakdown with examples from each mode → docs/clustering.md.
| Command | What it does |
|---|---|
promote init |
Interactive setup — provider, tool, language, memory paths |
promote scan [--since 30d] [--mode quick|broad] [--repo owner/repo] [--out file.md] |
Fetch → cluster → classify → interactive review (--out writes a markdown digest) |
promote scan --no-interactive --min-confidence 0.85 --create-pr |
Headless CI mode: auto-apply candidates above the floor, open one bundled PR |
promote review |
Re-review all pending (snoozed/deferred) candidates |
promote <id> [--target adr] [--create-pr] |
Apply one specific candidate; with --create-pr also opens a PR |
promote ignore <id> [--reason "..."] |
Dismiss permanently |
promote snooze <id> [--days 30] |
Resurface later |
promote --help |
All flags |
promote init writes a minimal .promote.yml you rarely need to touch.
version: 2
language:
preferredOutput: en
memoryTargets:
agents:
preferredFiles: [CLAUDE.md]
pathScoped:
preferredDir: .claude/rules
thresholds:
minOccurrences: 2
windowDays: 60
minConfidence: 0.75
llm:
provider: anthropic
classificationModel: claude-haiku-4-5Full schema, per-provider defaults, env vars, and routing taxonomy → docs/config.md.
promote scan becomes fully headless when you pass --no-interactive (auto-enabled in CI environments or non-TTY shells):
promote scan --no-interactive --min-confidence 0.85 --create-prThis auto-applies every candidate whose status is candidate and confidence is at least --min-confidence, then opens a single bundled PR containing all touched memory files plus the run's digest (committed to docs/promote/digests/{date}.md).
promote init is interactive only — you can't run it inside a workflow. Do this once locally, then commit the result:
promote init # picks your tool (Claude Code / Codex / Copilot / etc),
# provider, language, memory file locations
git add .promote.yml
git commit -m "chore: add promote-cli config".promote.yml is what the CI run reads to know your memory file layout, provider, and thresholds. If you skip this step, loadConfig falls back to baked-in defaults (provider: openai, gpt-4.1-mini, language: en, primary memory file AGENTS.md, path-scoped dir .github/instructions/). Those defaults are reasonable for an OSS repo using AGENTS.md, but if your team is on CLAUDE.md or another preset, commit .promote.yml or your PR will land in the wrong file.
Then set the relevant secret in Settings → Secrets and variables → Actions: ANTHROPIC_API_KEY, OPENAI_API_KEY, or GOOGLE_API_KEY depending on the provider you picked. GITHUB_TOKEN is provided by Actions automatically.
- PR creation uses
ghwhen available, with a transparent fallback to the GitHub REST API viaGITHUB_TOKEN— so it works on both GitHub-hosted and self-hosted runners. - PR template aware. If your repo has a
PULL_REQUEST_TEMPLATE.md(in.github/, repo root, ordocs/), the LLM fills the template's section bodies (preserving checkboxes and unknown sections) before opening the PR. A## Memory promotion detailsappendix with raw evidence is always appended for verifiability. - The promote branch is always cut from the base branch. Even if you ran the command from a feature branch, the resulting branch's only diff is the memory updates — no unrelated changes leak in. PR target is always your local
origin, never the scanned repo. - Atomic — rollback on failure. Files are written on the cut branch and the DB only flips to
promotedaftergh pr createsucceeds. If anything between branch cut and PR open fails, the working tree, the promote branch, and the DB are all restored to their pre-run state. Re-running the next scan picks up where it left off. - You're returned to your original branch on success. The memory changes live on the promote branch and the PR; your working tree goes back to wherever you were.
needs_human_decisioncandidates are never auto-applied — they're surfaced in the headless run's output (N candidate(s) flagged needs_human_decision — review locally with 'promote review'.) and stay in the digest for human review.- Target file paths come from
.promote.yml, not the LLM. The classifier'ssuggestedFileis only accepted if it matches a known instruction file (AGENTS.md,CLAUDE.md, …) or sits undermemoryTargets.pathScoped.preferredDir. Anything else (e.g. source paths leaked from the scanned repo in--allow-foreign-scanmode) falls back to the configured destination.
A ready-to-copy weekly workflow lives at examples/github-actions/weekly-digest.yml. See examples/github-actions/README.md for the secrets and permissions it expects.
By default promote refuses to scan one repo and write changes against a different local repo — it's almost always a mistake. Pass --allow-foreign-scan to opt in explicitly:
# In your own test repo (where you have write access), pull comments from an
# upstream OSS project and open the resulting memory PR against YOUR repo.
promote scan \
--repo upstream-owner/upstream-repo --since 60d \
--no-interactive --min-confidence 0.85 \
--create-pr --allow-foreign-scan--allow-foreign-scan decouples where evidence comes from (the scanned repo's comments) from where the PR lands (your current working tree's origin). Useful for dogfooding the headless flow safely or for teams who want to mirror conventions from an upstream into their fork.
No. It does not review new code. It mines review history and proposes durable memory updates for patterns that already appeared in PR comments.
Interactive mode asks for approval per candidate. Headless CI mode can open a single bundled PR, but candidates that require human judgment (needs_human_decision) are never auto-applied — they stay in the digest for local review with promote review.
By default, promote-cli analyzes review comments without sending the surrounding diff hunks (privacy.sendDiffHunksToLLM: false). You can opt into sending diff context if your team wants higher classification accuracy.
Yes, as long as your GitHub token can read the repository's PR review comments. The tool itself is local-first and never sends comments to a promote-hosted server.
It can write to memory targets used by Claude Code, OpenAI Codex, GitHub Copilot, Cursor, Windsurf, and Gemini CLI. See docs/config.md for the full targets matrix.
No. promote-cli runs entirely on your machine or your own CI runner. LLM calls go from your environment to your configured provider using your key.
Shipping today — Personal CLI, multi-tool routing, hybrid clustering, human reply/reaction signal, filter transparency, stable IDs, secret redaction, i18n digest (en / ko / ja), atomic headless --create-pr flow (cuts from base, rollback on failure, LLM-filled PR template, gh/octokit dual transport), GitHub Action template for scheduled weekly digest PRs.
Next — MCP server for agent-native workflows (Claude Code / Codex / compatible MCP clients), machine-readable JSON output for scripted pipelines, per-candidate PR mode (split a bundled run into one PR per candidate when teams prefer atomic memory PRs).
Later — promote eval for classification accuracy, memory health checks (stale rules, conflicting instructions, oversized files), hosted GitHub App.
MIT
promote doesn't generate more review comments. It reduces repeated ones over time.

