diff --git a/.gitignore b/.gitignore
index af8ea34..785f4b4 100644
--- a/.gitignore
+++ b/.gitignore
@@ -7,6 +7,9 @@ dist/
# this repo's own dogfooding digests — end-user repos should commit these
docs/promote/digests/
+# internal-only product spec — not for npm or public history
+docs/prd.md
+
# claude code
.claude/
diff --git a/README.md b/README.md
index e8846a1..12ee4cc 100644
--- a/README.md
+++ b/README.md
@@ -5,12 +5,18 @@
Turn repeated AI review comments into durable repository memory.
+ Audience ·
Why ·
How ·
Quick start ·
Features ·
+ Examples ·
+ Privacy
+
+
Cost ·
CLI ·
+ FAQ ·
Roadmap
@@ -25,7 +31,18 @@
-CodeRabbit, Copilot, and Claude review your PRs — and the same suggestions keep coming back. **promote-cli** mines repeated review comments across your PR history, clusters them into patterns, and helps you promote each into a rule your AI tools will read on the next review.
+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.
@@ -46,6 +63,19 @@ promote scan --since 30d
+## Who this is for
+
+promote-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.
+
+
+
## Why promote-cli
AI review tools leave comments on PRs. Developers resolve them and move on. The decision disappears into a closed PR thread.
@@ -56,7 +86,7 @@ If the same comment appears next week, the team pays the same review cost again.
> *"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 still yours is *where* it belongs.
+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 |
| ---------------------------- | ------------------------- | -------------------------- | ------------------------------- |
@@ -107,7 +137,7 @@ promote init
promote scan --since 30d
```
-**3. Review.** Walk through each candidate — promote or skip per item. Approved ones land in your chosen target file immediately.
+**3. Review.** Walk through each candidate — promote or skip per item. Approved ones land in your chosen target file immediately. See [Examples](#examples) for what a real digest and patch look like.
```
─── Candidate 1/7 ───
@@ -173,15 +203,43 @@ Output is localized per `language.preferredOutput` in `.promote.yml` (en / ko /
+## Examples
+
+Want to see representative output from real scans before installing?
+
+- [Sample quick digest (trpc/trpc, OpenAI `quick`)](examples/digests/sample-quick.md)
+- [Sample broad digest (trpc/trpc, Anthropic `broad`)](examples/digests/sample-broad.md)
+- [`CLAUDE.md` patch example](examples/patches/claude-rule.patch)
+- [Cursor rule patch example](examples/patches/cursor-rule.patch)
+
+Both digests come from real `promote scan` runs against the public [`trpc/trpc`](https://github.com/trpc/trpc) repository.
+
+
+
+## Privacy & security
+
+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: true` by default).
+- **No diff hunks by default.** `privacy.sendDiffHunksToLLM` defaults to `false` — 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_decision` are never auto-applied.
+
+See [`SECURITY.md`](SECURITY.md) for how to report a vulnerability.
+
+
+
## Cost & mode trade-off
Measured on trpc/trpc, 120-day window, 380 actionable AI comments:
-| Mode + provider | Candidates | Cost | Wall time | Output style |
-| -------------------------------------- | ---------- | ------- | --------- | --------------------------------------------- |
-| OpenAI `quick` (gpt-4.1-mini + nano) | **24** | $0.07 | 2m 14s | Narrow, file-specific |
-| OpenAI `broad` (gpt-4.1-mini cluster) | 8 | $0.10 | 2m 39s | Core conventions only — subset of Anthropic |
-| **Anthropic `broad` (Haiku 4.5)** | **21** | $0.47 | 8m 17s | **Convention / principle / ADR mix** |
+| 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:
@@ -191,7 +249,7 @@ Picking a mode by cadence:
| **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 full depth (20+ conventions including ADR-worthy decisions).
+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](docs/clustering.md).
@@ -292,6 +350,34 @@ promote scan \
+## FAQ
+
+### Does promote-cli replace code review?
+
+No. It does not review new code. It mines review history and proposes durable memory updates for patterns that already appeared in PR comments.
+
+### Does it write to my repo automatically?
+
+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`.
+
+### Does it send my source code to an LLM?
+
+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.
+
+### Does it work with private repositories?
+
+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.
+
+### Which AI tools does it support?
+
+It can write to memory targets used by Claude Code, OpenAI Codex, GitHub Copilot, Cursor, Windsurf, and Gemini CLI. See [`docs/config.md`](docs/config.md) for the full targets matrix.
+
+### Is this a hosted service?
+
+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.
+
+
+
## Roadmap
**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.
diff --git a/SECURITY.md b/SECURITY.md
new file mode 100644
index 0000000..8bd8ebb
--- /dev/null
+++ b/SECURITY.md
@@ -0,0 +1,30 @@
+# Security Policy
+
+## Supported versions
+
+| Version | Supported |
+| ------- | --------- |
+| 0.7.x | ✅ |
+| < 0.7 | ❌ |
+
+## Reporting a vulnerability
+
+Please report security issues privately through GitHub Security Advisories
+rather than a public issue:
+
+
+
+We aim to acknowledge reports within 7 days.
+
+## Scope
+
+promote-cli is a local CLI that calls third-party LLM providers with your own
+API key. Reports that affect installed users or CI consumers are in scope:
+
+- Issues that could exfiltrate API keys, repository tokens, or PR content
+- Bypasses of secret redaction (`privacy.redactSecrets`)
+- Issues that cause unintended writes to your repository
+- Supply-chain issues in the published npm package
+
+Issues in the third-party services we call (OpenAI, Anthropic, Google,
+GitHub) should be reported to those providers directly.
diff --git a/docs/clustering.md b/docs/clustering.md
index 17e161d..1012ad8 100644
--- a/docs/clustering.md
+++ b/docs/clustering.md
@@ -25,7 +25,7 @@ Two paths for grouping repeated comments, with very different output character.
- "Internal implementation details must not be imported from `src/`; use public unstable API exports"
- "Cache invalidation must include generator/tsconfig/artifact existence checks"
- "SSE retry budget must not be consumed on initial connection; exclude AbortError"
-- **Cost / time**: ~$0.47 / ~8 min on 380 comments (Anthropic, concurrency=1)
+- **Cost / time**: ~$0.45 / ~5 min on 380 comments (Anthropic, concurrency=1)
- **Provider recommendation: Anthropic Claude.** OpenAI broad on tier-1 keys hits rate limits on the larger models (gpt-4.1, gpt-4o) and the mini variants don't produce reliable semantic groupings; Anthropic Haiku 4.5 handles both depth and reliability in one tier.
## Picking a mode
@@ -52,6 +52,6 @@ trpc/trpc, 120d window, 380 actionable AI comments:
| OpenAI quick (gpt-4.1-mini + nano) | **24** | $0.07 | 2m 14s | Narrow, file-specific |
| OpenAI broad (gpt-4.1-mini cluster) | 8 | $0.10 | 2m 39s | Core conventions only — agents-level subset |
| OpenAI broad (gpt-4.1 full cluster) | — | — | — | Needs OpenAI tier 2+; tier 1 hits rate limits |
-| **Anthropic broad (Haiku 4.5)** | **21** | $0.47 | 8m 17s | **Convention / principle / ADR mix — recommended depth** |
+| **Anthropic broad (Haiku 4.5)** | **17** | $0.45 | 4m 55s | **Convention / principle / ADR mix — recommended depth** |
-OpenAI broad is positioned as a "no-Anthropic-key" budget alternative — it reliably catches the 6–8 core repo-wide conventions (non-null assertion bans, function-param destructure rules, test-utility imports, CLI version pinning, etc.) at 5× lower cost than Anthropic broad. For full depth (20+ conventions including ADR-worthy decisions) use Anthropic broad.
+OpenAI broad is positioned as a "no-Anthropic-key" budget alternative — it reliably catches the 6–8 core repo-wide conventions (non-null assertion bans, function-param destructure rules, test-utility imports, CLI version pinning, etc.) at ~5× lower cost than Anthropic broad. For the same depth of convention / principle / ADR-style candidates, use Anthropic broad.
diff --git a/examples/README.md b/examples/README.md
new file mode 100644
index 0000000..52a809d
--- /dev/null
+++ b/examples/README.md
@@ -0,0 +1,30 @@
+# Examples
+
+Real output and ready-to-use templates for `promote-cli`.
+
+## What's here
+
+| Folder | What it shows |
+| --- | --- |
+| [`digests/`](./digests/) | Sample markdown digests from real `promote scan` runs against [`trpc/trpc`](https://github.com/trpc/trpc) |
+| [`patches/`](./patches/) | Illustrative diffs of what `promote` writes to your memory files |
+| [`github-actions/`](./github-actions/) | A weekly scheduled workflow that opens a bundled memory PR |
+
+## Suggested reading order
+
+1. **[`digests/sample-quick.md`](./digests/sample-quick.md)** — what a fast scan ($0.07, 2m) looks like. Three representative candidates from a 24-candidate run.
+2. **[`digests/sample-broad.md`](./digests/sample-broad.md)** — what a deeper scan ($0.45, 5m) looks like. Same repo, different mode; surfaces convention-level patterns.
+3. **[`patches/claude-rule.patch`](./patches/claude-rule.patch)** and **[`patches/cursor-rule.patch`](./patches/cursor-rule.patch)** — what gets written when you `Promote` a candidate.
+4. **[`github-actions/weekly-digest.yml`](./github-actions/weekly-digest.yml)** — drop-in workflow for running the whole flow on a schedule.
+
+## Generating your own
+
+To produce a digest like these against your own repository:
+
+```bash
+npm i -g promote-cli
+promote init
+promote scan --since 60d --mode quick --out my-digest.md # or --mode broad
+```
+
+The digest will land at `my-digest.md` (or `docs/promote/digests/{date}.md` if you skip `--out`). It carries the same structure as the samples above — candidates, evidence trails to source PR comments, suggested patches, and a `Filtered out` appendix for clusters the classifier dropped.
diff --git a/examples/digests/sample-broad.md b/examples/digests/sample-broad.md
new file mode 100644
index 0000000..1a0c0cb
--- /dev/null
+++ b/examples/digests/sample-broad.md
@@ -0,0 +1,160 @@
+# promote digest — sample (broad mode)
+
+> A snapshot of a real `promote scan` run. Three representative candidates are shown — the full run produced 17.
+
+- **Repo:** [`trpc/trpc`](https://github.com/trpc/trpc)
+- **Generated:** 2026-05-22
+- **Mode:** `broad` — LLM-direct semantic clustering (no embedding API)
+- **Provider:** Anthropic (`claude-haiku-4-5` classify, cluster, draft)
+- **Window:** 120 days (overridden via `--since`)
+- **Privacy:** `redactSecrets=true`, `sendDiffHunksToLLM=false`
+
+## Summary
+
+- Scanned: **454** review comments across **72** PRs
+- AI reviewer comments: **384**
+- Clusters: **117** (83 repeated → 17 promotion candidates)
+- Cost: **$0.45**
+- Wall time: **4m 55s**
+
+## Reproduce
+
+```bash
+promote scan --repo trpc/trpc --since 120d --mode broad
+```
+
+---
+
+## Candidate 1 — Standardize test setup with `testServerAndClientResource` + `await using`
+
+- **Target:** `path_scoped_rule` → `.claude/rules/test-setup.instructions.md`
+- **Confidence:** 0.85
+- **Scope:** cross-PR (4 PRs)
+- **Occurrences:** 4 comments
+- **Path scope:** `**/*.test.ts`
+- **Human signal:** 1 reviewer agreed (`@rbxict`)
+
+### Why this was promoted
+
+The same convention shows up across four PRs: tests should call
+`testServerAndClientResource` directly (with `await using`) and use the
+`client` callback config pattern, rather than wrapping with `run(...)`
+or `konn(...)`. A maintainer also followed up agreeing this is the
+preferred direction — broad mode picked up on that human signal.
+
+### Evidence
+
+- PR [#7262](https://github.com/trpc/trpc/pull/7262#discussion_r2948708218) — `packages/tests/server/regression/batchStreamErrorCallIndex.test.ts`
+- PR [#7207](https://github.com/trpc/trpc/pull/7207#discussion_r2879708223) — `packages/tests/server/httpSubscriptionLink.test.ts`
+- PR [#7304](https://github.com/trpc/trpc/pull/7304#discussion_r3012813221) — `packages/tests/server/httpSubscriptionLink.fetch.test.ts`
+- PR [#7231](https://github.com/trpc/trpc/pull/7231#discussion_r2902034597) — `packages/openapi/test/generate.test.ts`
+
+### Suggested rule
+
+```md
+---
+applyTo: "**/*.test.ts"
+---
+# Test setup
+
+- Call `testServerAndClientResource` directly with `await using` — both
+ server and client lifecycles are managed in one place.
+- Use the `client` callback config pattern instead of passing `clientLink`
+ directly. Keeps setup consistent across the suite.
+- Do not wrap the helper in `run(...)` or `konn(...)`.
+- `await using` ensures resources are released on test exit even on throw.
+```
+
+---
+
+## Candidate 2 — `bin.intent` points to a non-existent directory across packages
+
+- **Target:** `path_scoped_rule` → `.claude/rules/package-bin.instructions.md`
+- **Confidence:** 0.90
+- **Scope:** cross-PR (3 PRs)
+- **Occurrences:** 6 comments
+- **Path scope:** `packages/**/package.json`
+- **Human signal:** 1 reviewer agreed (`@orbisai0security`)
+
+### Why this was promoted
+
+Several `@trpc/*` packages declare `bin.intent`, but the referenced files
+either don't exist or were never added to `files`. Installations create
+broken executables. The cluster crosses three PRs (#7252, #7371, …) and
+has explicit reviewer agreement.
+
+### Evidence (selected)
+
+- PR [#7371](https://github.com/trpc/trpc/pull/7371#discussion_r3195158582) — `packages/tests/package.json`
+- PR [#7252](https://github.com/trpc/trpc/pull/7252#discussion_r2937362632) — `packages/openapi/package.json`
+- PR [#7252](https://github.com/trpc/trpc/pull/7252#discussion_r2937362635) — `packages/tanstack-react-query/package.json`
+- PR [#7252](https://github.com/trpc/trpc/pull/7252#discussion_r2937362630) — `packages/next/package.json`
+- (2 more occurrences across the same set of PRs)
+
+### Suggested rule
+
+```md
+---
+applyTo: "packages/**/package.json"
+---
+# `bin` entry hygiene
+
+- Every `bin` entry must point to a file that actually ships in the
+ published tarball — verify with `npm pack` locally.
+- Include the `bin/` directory in `files` whenever you add a `bin` entry.
+- When multiple `@trpc/*` packages share the same binary name, scope it
+ (e.g. `@trpc-intent`) to avoid `node_modules/.bin` collisions.
+- If a binary depends on an external package, declare it in `dependencies`
+ or `peerDependencies` rather than relying on transitive resolution.
+```
+
+---
+
+## Candidate 3 — Import from public entrypoints, not `src/internals/`
+
+- **Target:** `path_scoped_rule` → `.claude/rules/internal-api-boundaries.instructions.md`
+- **Confidence:** 0.85
+- **Scope:** cross-PR (2 PRs)
+- **Occurrences:** 2 comments
+- **Path scope:** `packages/*/src/**`
+- **Human signal:** 1 reviewer agreed (`@rbxict`)
+
+### Why this was promoted
+
+Two PRs across different packages reached for internal implementation
+paths (`src/internals/…`) instead of the published `unstable-*` or `public`
+entrypoints. The pattern is repo-wide architectural intent and a
+maintainer agreed in the follow-up review.
+
+### Evidence
+
+- PR [#7304](https://github.com/trpc/trpc/pull/7304#discussion_r3006306706) — `packages/tests/server/httpSubscriptionLink.fetch.test.ts`
+- PR [#7228](https://github.com/trpc/trpc/pull/7228#discussion_r2901567590) — `packages/tanstack-react-query/src/createOptionsProxy.ts`
+
+### Suggested rule
+
+```md
+---
+applyTo: "packages/*/src/**"
+---
+# Internal API boundaries
+
+- Do not import from `src/internals/**` directly.
+- Use the public entrypoints (`unstable-internals`, `public`, or
+ `src/index.ts`) so the surface stays controlled.
+- For cross-package imports, route through each package's public exports
+ rather than relative paths into another package's internals.
+- New internal capability needed externally? Expose it through a public
+ entrypoint first.
+```
+
+---
+
+## Filtered out (excerpt)
+
+The classifier dropped 19 clusters with explicit reasons. Examples:
+
+- *"Address minor typo in JSDoc"* — dropped (`not_promotable`): one-off, no policy implication.
+- *"Consider TanStack Query v5 migration"* — dropped (`already_handled`): tracked in a separate roadmap issue.
+
+The full digest carries the complete `Filtered out` appendix.
diff --git a/examples/digests/sample-quick.md b/examples/digests/sample-quick.md
new file mode 100644
index 0000000..075ee1e
--- /dev/null
+++ b/examples/digests/sample-quick.md
@@ -0,0 +1,142 @@
+# promote digest — sample (quick mode)
+
+> A snapshot of a real `promote scan` run. Three representative candidates are shown — the full run produced 24.
+
+- **Repo:** [`trpc/trpc`](https://github.com/trpc/trpc)
+- **Generated:** 2026-05-22
+- **Mode:** `quick` — embedding + HAC + `llmRefine`
+- **Provider:** OpenAI (`gpt-4.1-mini` classify, `gpt-4.1-nano` draft, `text-embedding-3-small` embed)
+- **Window:** 120 days (overridden via `--since`)
+- **Privacy:** `redactSecrets=true`, `sendDiffHunksToLLM=false`
+
+## Summary
+
+- Scanned: **454** review comments across **72** PRs
+- AI reviewer comments: **384**
+- Clusters: **295** (47 repeated → 24 promotion candidates)
+- Cost: **$0.07**
+- Wall time: **2m 14s**
+
+## Reproduce
+
+```bash
+promote scan --repo trpc/trpc --since 120d --mode quick
+```
+
+---
+
+## Candidate 1 — Standardize integration test setup on `testServerAndClientResource`
+
+- **Target:** `agents` → `AGENTS.md`
+- **Confidence:** 0.90
+- **Scope:** cross-PR (3 PRs)
+- **Occurrences:** 3 comments
+
+### Why this was promoted
+
+Three independent PRs flagged the same pattern: integration tests should
+use the `testServerAndClientResource` helper with `await using` rather
+than bespoke wrappers (`run(...)`, `konn(...)`). This is a repeated
+repo-wide test convention, not a single-PR cleanup.
+
+### Evidence
+
+- PR [#7231](https://github.com/trpc/trpc/pull/7231#discussion_r2902034597) — `packages/openapi/test/generate.test.ts`
+- PR [#7207](https://github.com/trpc/trpc/pull/7207#discussion_r2879708223) — `packages/tests/server/httpSubscriptionLink.test.ts`
+- PR [#7304](https://github.com/trpc/trpc/pull/7304#discussion_r3012813221) — `packages/tests/server/httpSubscriptionLink.fetch.test.ts`
+
+### Suggested instruction
+
+```md
+## Integration test setup
+
+- Use the `testServerAndClientResource` helper with `await using` for any
+ test that needs both a server and a client.
+- Do not wrap the server lifecycle in `run(...)` or `konn(...)` — call
+ the standard helper directly.
+- `await using` guarantees the resource is released even if the test throws.
+```
+
+---
+
+## Candidate 2 — Include `bin/` in `package.json#files` for binary entries
+
+- **Target:** `agents` → `AGENTS.md`
+- **Confidence:** 0.95
+- **Scope:** within-PR (1 PR, multi-package)
+- **Occurrences:** 10 comments across 5 packages
+
+> `within-PR` candidates are shown separately from `cross-PR` candidates because some repeated patterns are broad within a single large change, but may not yet justify a repo-wide rule.
+
+### Why this was promoted
+
+A single PR (#7252) added a `bin.intent` entry across multiple packages,
+but the `files` allowlist in each `package.json` was not updated — the
+binary would be missing from the published tarball. The same comment
+fired in every affected package, indicating a recurring oversight worth
+codifying.
+
+### Evidence (selected)
+
+- PR [#7252](https://github.com/trpc/trpc/pull/7252#discussion_r2968125965) — `packages/server/package.json`
+- PR [#7252](https://github.com/trpc/trpc/pull/7252#discussion_r2968125922) — `packages/tanstack-react-query/package.json`
+- PR [#7252](https://github.com/trpc/trpc/pull/7252#discussion_r2968125972) — `packages/client/package.json`
+- (7 more occurrences in the same PR)
+
+### Suggested instruction
+
+```md
+## Publishing binaries
+
+- When adding a `bin` entry to `package.json`, also add the `bin/`
+ directory to the `files` allowlist — otherwise the binary is omitted
+ from the published tarball.
+- Verify locally with `npm pack` before publishing.
+```
+
+---
+
+## Candidate 3 — Remove non-null assertions in `www/scripts/check-twoslash.ts`
+
+- **Target:** `path_scoped_rule` → `.claude/rules/check-twoslash.instructions.md`
+- **Confidence:** 0.95
+- **Scope:** cross-PR (3 PRs)
+- **Occurrences:** 3 comments
+- **Path scope:** `www/scripts/**`
+
+### Why this was promoted
+
+Three separate PRs flagged non-null assertion (`!`) usage on indexed and
+RegExp capture access within the same file. The matches are not
+statically known to be non-null at runtime. This is a path-scoped style
+issue, not a repo-wide policy.
+
+### Evidence
+
+- PR [#7196](https://github.com/trpc/trpc/pull/7196#discussion_r2869759038)
+- PR [#7176](https://github.com/trpc/trpc/pull/7176#discussion_r2876202469)
+- PR [#7195](https://github.com/trpc/trpc/pull/7195#discussion_r2869717508)
+
+### Suggested rule
+
+```md
+---
+applyTo: "www/scripts/**"
+---
+# www/scripts/check-twoslash.ts
+
+- Replace non-null assertions (`!`) on indexed or RegExp-capture access
+ with explicit null checks or optional chaining.
+- Match results are not statically guaranteed to be present at runtime.
+```
+
+---
+
+## Filtered out (excerpt)
+
+The classifier also dropped 12 clusters from the candidate set with explicit reasons. Examples:
+
+- *"Add tests for edge cases in error handling"* — dropped (`not_promotable`): too generic, no specific invariant to enforce.
+- *"Consider extracting this into a helper"* — dropped (`low_confidence`): single occurrence, refactor suggestion rather than policy.
+
+The full digest carries the complete `Filtered out` appendix and a `Skipped during review` section for anything deferred in interactive mode.
diff --git a/examples/github-actions/README.md b/examples/github-actions/README.md
index 1ce986a..9ad2d3f 100644
--- a/examples/github-actions/README.md
+++ b/examples/github-actions/README.md
@@ -64,6 +64,7 @@ the Octokit REST API using `GITHUB_TOKEN`, so no extra setup is needed.
## Cost control
`promote-cli` only calls the LLM for repeated clusters (≥ `minOccurrences`
-members). For a mid-size repo with ~200 PR review comments over 60 days,
-expect a handful of dollars per scan on default Claude models. See
-[`docs/clustering.md`](../../docs/clustering.md) for the cost breakdown.
+members). For a mid-size repo, expect costs to range from cents to low
+dollars per scan depending on provider, mode, review volume, and whether
+diff context is enabled. See [`docs/clustering.md`](../../docs/clustering.md)
+for the measured breakdown.
diff --git a/examples/patches/claude-rule.patch b/examples/patches/claude-rule.patch
new file mode 100644
index 0000000..6cb9746
--- /dev/null
+++ b/examples/patches/claude-rule.patch
@@ -0,0 +1,20 @@
+diff --git a/CLAUDE.md b/CLAUDE.md
+index 11a22bb..33c44dd 100644
+--- a/CLAUDE.md
++++ b/CLAUDE.md
+@@ -12,3 +12,15 @@
+ ## Testing
+
+ - Run unit tests before changing shared utilities.
++
++## Internal API boundaries
++
++- Do not import from `src/internals/**` directly.
++- Use the public entrypoints (`unstable-internals`, `public`, or `src/index.ts`)
++ so the surface stays controlled.
++- For cross-package imports, route through each package's public exports
++ rather than relative paths into another package's internals.
++- If a new internal capability is needed externally, expose it through a
++ public entrypoint first.
++
++
diff --git a/examples/patches/cursor-rule.patch b/examples/patches/cursor-rule.patch
new file mode 100644
index 0000000..5803ff0
--- /dev/null
+++ b/examples/patches/cursor-rule.patch
@@ -0,0 +1,20 @@
+diff --git a/.cursor/rules/react-imports.mdc b/.cursor/rules/react-imports.mdc
+new file mode 100644
+index 0000000..1234567
+--- /dev/null
++++ b/.cursor/rules/react-imports.mdc
+@@ -0,0 +1,14 @@
++---
++description: React import conventions in test files
++globs:
++ - "packages/tanstack-react-query/test/**/*.test.tsx"
++---
++
++# React import conventions
++
++- Use the namespace form: `import * as React from 'react'`.
++- Do not use individual named imports (`import { useState } from 'react'`)
++ in TanStack React Query test files.
++- Reference React explicitly (`React.createElement`, `React.useState`).
++
++
diff --git a/package.json b/package.json
index 9f3b8dd..9245f5a 100644
--- a/package.json
+++ b/package.json
@@ -8,7 +8,10 @@
"promote-cli": "dist/index.js"
},
"files": [
- "dist"
+ "dist",
+ "docs",
+ "examples",
+ "SECURITY.md"
],
"repository": {
"type": "git",
@@ -30,10 +33,19 @@
"keywords": [
"ai",
"code-review",
+ "ai-review",
+ "github",
+ "github-actions",
+ "pull-request",
"agents-md",
"claude-md",
+ "claude-code",
+ "copilot-instructions",
+ "cursor",
+ "adr",
"repository-memory",
- "developer-tools"
+ "developer-tools",
+ "cli"
],
"license": "MIT",
"dependencies": {