@eric0117/agentforge 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (40) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +275 -0
  3. package/dist/add-agent.js +145 -0
  4. package/dist/add-skill.js +185 -0
  5. package/dist/agent-prompt.js +211 -0
  6. package/dist/agentforge-config.js +106 -0
  7. package/dist/agents/claude.js +46 -0
  8. package/dist/agents/codex.js +67 -0
  9. package/dist/agents/cursor.js +54 -0
  10. package/dist/agents/index.js +15 -0
  11. package/dist/agents/io.js +252 -0
  12. package/dist/agents/types.js +1 -0
  13. package/dist/cli.js +374 -0
  14. package/dist/confirm.js +20 -0
  15. package/dist/doctor.js +223 -0
  16. package/dist/enter.js +85 -0
  17. package/dist/init.js +272 -0
  18. package/dist/lang-prompt.js +88 -0
  19. package/dist/list-skills.js +120 -0
  20. package/dist/logo.js +181 -0
  21. package/dist/path-prompt.js +148 -0
  22. package/dist/remove-agent.js +63 -0
  23. package/dist/remove-skill.js +88 -0
  24. package/dist/rename.js +222 -0
  25. package/dist/skill-prompt.js +199 -0
  26. package/dist/skills-data.js +727 -0
  27. package/dist/sync-skills.js +59 -0
  28. package/dist/templates/CLAUDE.md.tpl +141 -0
  29. package/dist/templates/context-handoff.SKILL.md.tpl +222 -0
  30. package/dist/templates/cross-repo-impact.SKILL.md.tpl +241 -0
  31. package/dist/templates/feature-retro.SKILL.md.tpl +312 -0
  32. package/dist/templates/feature-start.SKILL.md.tpl +631 -0
  33. package/dist/templates/history.SKILL.md.tpl +165 -0
  34. package/dist/templates/incident-context.SKILL.md.tpl +260 -0
  35. package/dist/templates/pr-create.SKILL.md.tpl +403 -0
  36. package/dist/templates/pr-review-analyze.SKILL.md.tpl +303 -0
  37. package/dist/templates/pre-deploy-check.SKILL.md.tpl +350 -0
  38. package/dist/templates/project-router.SKILL.md.tpl +55 -0
  39. package/dist/templates/release-coordinate.SKILL.md.tpl +209 -0
  40. package/package.json +54 -0
@@ -0,0 +1,303 @@
1
+ ---
2
+ name: agentforge-pr-review-analyze
3
+ description: Audits the review comments on a pull request — pulls every inline thread, top-level review body, and PR comment, verifies each against the live code, classifies impact, traces the call path, notes test coverage, and returns a prioritized action list. Triggers when the user says things like "audit the PR comments", "go through the review feedback", "what do we need to fix from the review", "summarize the review comments". Runs analysis only; never edits code.
4
+ ---
5
+
6
+ # pr-review-analyze
7
+
8
+ Pulls every review comment on a PR, verifies each one against the actual code, and gives
9
+ back a prioritized action list grouped by impact.
10
+
11
+ **This skill never edits code.** Analysis only. The user applies fixes separately.
12
+
13
+ ## When to apply
14
+
15
+ The user is asking for a review of the review comments themselves — e.g.:
16
+ - "Audit the PR comments."
17
+ - "Go through the review feedback and tell me what to do."
18
+ - "Summarize the reviewer's comments."
19
+ - "What's left to address from the review?"
20
+
21
+ If the user instead asks to *apply* a specific fix, do not trigger this skill — just edit
22
+ the code as asked.
23
+
24
+ ## Preconditions
25
+
26
+ - `gh` CLI installed and authenticated (`gh auth status`). If not, surface a clear
27
+ message and stop.
28
+ - The cwd has access to the code the PR was opened against (so you can read the actual
29
+ files at `path:line`).
30
+
31
+ ## Rate limits
32
+
33
+ `gh` commands hit the GitHub API. For a large PR with hundreds of comments + threads,
34
+ calls can pile up and trigger:
35
+
36
+ ```
37
+ gh: API rate limit exceeded for user
38
+ ```
39
+
40
+ If you see that:
41
+ 1. Report it to the user verbatim along with the reset time (`gh api rate_limit`
42
+ shows it).
43
+ 2. **Do not retry in a tight loop.** Suggest waiting or using a PAT with higher
44
+ limits.
45
+ 3. If you got partial data before the limit, analyze what you have and tell the user
46
+ which comments are unanalyzed (so they know the report is incomplete).
47
+
48
+ ## Resolving the PR
49
+
50
+ 1. **Determine the working repo from cwd** (using the agentforge workspace layout when
51
+ present):
52
+ - `…/anvil/<slug>/<repo>/` → single repo; analyze the PR for this repo.
53
+ - `…/anvil/<slug>/` → multiple worktrees may live here. `ls` the subdirs and ask the
54
+ user which one to analyze.
55
+ - `…/repos/<name>/` → this repo's currently-checked-out branch.
56
+ - Anywhere else → use cwd as-is.
57
+ 2. **PR number**:
58
+ - If the user gave one, use it.
59
+ - Otherwise `gh pr view --json number,title,url,headRefName,baseRefName` for the
60
+ current branch. If none, tell the user and stop.
61
+ 3. Capture `owner`, `repo`, and PR number via `gh repo view --json owner,name`.
62
+
63
+ ## Pulling comments
64
+
65
+ Fetch all three classes of comments, then merge:
66
+
67
+ ```bash
68
+ # (a) inline review comments (attached to specific lines — the main signal)
69
+ gh api repos/{owner}/{repo}/pulls/{num}/comments --paginate
70
+
71
+ # (b) review bodies + states (APPROVED / CHANGES_REQUESTED / COMMENTED / DISMISSED)
72
+ gh api repos/{owner}/{repo}/pulls/{num}/reviews --paginate
73
+
74
+ # (c) general PR conversation comments
75
+ gh api repos/{owner}/{repo}/issues/{num}/comments --paginate
76
+ ```
77
+
78
+ For `isResolved` / `isOutdated` flags (REST API does not expose these), use GraphQL:
79
+
80
+ ```bash
81
+ gh api graphql -f query='
82
+ query($owner:String!, $repo:String!, $num:Int!) {
83
+ repository(owner:$owner, name:$repo) {
84
+ pullRequest(number:$num) {
85
+ reviewThreads(first:100) {
86
+ nodes {
87
+ id
88
+ isResolved
89
+ isOutdated
90
+ comments(first:20) {
91
+ nodes { databaseId author{login} body path line }
92
+ }
93
+ }
94
+ }
95
+ }
96
+ }
97
+ }' -f owner=<owner> -f repo=<repo> -F num=<num>
98
+ ```
99
+
100
+ Always use `--paginate` — large PRs blow past 30 comments per page.
101
+
102
+ ## Grouping
103
+
104
+ - Group by **review thread** (`in_reply_to_id`) so the conversation is preserved.
105
+ - Label threads:
106
+ - `✅ Resolved` if `isResolved` — still analyze but deprioritize.
107
+ - `⚠️ Outdated` if `isOutdated` — code at that line may have changed; **re-read the
108
+ current file** before judging.
109
+ - `self` if the comment author matches the PR author.
110
+ - Skip pure `APPROVED` review bodies that contain only LGTM/praise. If an `APPROVED`
111
+ review body still contains a code remark, treat it as a comment.
112
+
113
+ ## Detect the language(s)
114
+
115
+ Before tracing call paths, infer the languages in play from the PR's changed files:
116
+
117
+ ```bash
118
+ gh pr view {num} --json files --jq '.files[].path' | sed -E 's/.*\.//' | sort -u
119
+ ```
120
+
121
+ Pick search idioms per language for call-site tracing:
122
+
123
+ | Language | Definition pattern | Caller search |
124
+ |---|---|---|
125
+ | TypeScript/JavaScript | `function X` / `const X = ` / `class X` / `export …` | `\bX\(` and `import {.*X.*}` |
126
+ | Kotlin/Java | `fun X` / `X(…)` / `class X` | `\.X\(` and `import …\.X` |
127
+ | Python | `def X` / `class X` | `\bX\(` and `from .* import .*X` |
128
+ | Go | `func X` / `func (\w+) X` | `\.X\(` |
129
+ | Rust | `fn X` / `impl …` | `::X(` / `\.X\(` |
130
+
131
+ For other languages, fall back to: identify the symbol name, grep for it across the
132
+ repo, filter to plausible call sites.
133
+
134
+ ## Per-comment analysis (the 4 axes)
135
+
136
+ For each thread (or standalone comment), fill all four — leave none empty.
137
+
138
+ ### (1) Verify against the live code
139
+
140
+ - Open `path:line` with Read.
141
+ - Confirm the issue actually exists in the current code (not just in the review's
142
+ snapshot).
143
+ - For Outdated comments, explicitly state "already addressed" or "still present".
144
+ - Classify the comment's intent:
145
+ - 🐛 Bug — concrete defect
146
+ - ⚠️ Latent issue — fails under specific conditions
147
+ - 💡 Improvement — works, but a better option exists
148
+ - ❓ Question — clarifying intent
149
+ - 🎨 Style — naming, formatting, idioms
150
+
151
+ ### (2) Impact (5 levels)
152
+
153
+ Pick one and justify it in one line.
154
+
155
+ | Level | Meaning | Examples |
156
+ |---|---|---|
157
+ | 🔴 Critical | Direct outage | Null-deref, data integrity, transaction leak, security, infinite loop |
158
+ | 🟠 High | Clear runtime bug | Wrong response under condition X, missing validation, race condition |
159
+ | 🟡 Medium | Latent risk / perf | N+1, inefficient query, missing error handling (log-only) |
160
+ | 🟢 Low | Readability / idiom | Naming, formatting, minor refactor |
161
+ | 💬 Discussion | Not a change request | Pure question, info sharing |
162
+
163
+ ### (3) Call path
164
+
165
+ - Identify the enclosing function/method.
166
+ - Grep for call sites using the language-appropriate idiom from the table above.
167
+ - Describe its role in the call chain in generic terms:
168
+ *entry point → orchestration → domain logic → persistence*.
169
+ - Show the chain when meaningful, e.g.
170
+ `FooController.doSomething → FooService.doSomething → FooRepository.findById`.
171
+ - If the symbol has many callers (50+), show the principal entry points and "+N other
172
+ call sites".
173
+ - **Async entry points** matter — flag them separately. Look for things like:
174
+ - **Backend**: queue / stream listeners (`@KafkaListener`, `@RabbitListener`,
175
+ SQS / Pub/Sub consumer), background tasks (`@Async`, `@Scheduled`, cron,
176
+ BullMQ worker, Celery task, Sidekiq job), webhook handlers
177
+ - **Frontend (web)**: `useEffect` cleanup paths, Service Workers,
178
+ `requestIdleCallback`, RxJS subscriptions, IntersectionObserver callbacks
179
+ - **Mobile**: background tasks (iOS `BGTask`, Android `WorkManager`),
180
+ notification handlers, deeplink intent receivers
181
+ These often run outside a request's transaction / MDC / React render context,
182
+ which can change the impact assessment. Re-evaluate the impact level once an
183
+ async entry is found.
184
+
185
+ ### (4) Resolution
186
+
187
+ - Concrete fix: ideally a small before/after snippet.
188
+ - If the reviewer suggested a specific fix, decide whether to follow it or propose an
189
+ alternative — and why.
190
+ - Trade off impact vs. effort. A small comment with Critical impact still ranks first.
191
+ - If the conclusion is "already fixed" or "no change needed", state the reason
192
+ explicitly.
193
+
194
+ ### Bonus axis: test coverage
195
+
196
+ For each finding, check whether the touched code is covered by tests:
197
+
198
+ - Look for sibling test files by convention:
199
+ - `*.test.ts` / `*.spec.ts` / `__tests__/`
200
+ - `*_test.go`
201
+ - `test_*.py` / `*_test.py`
202
+ - `*Test.kt` / `*Test.java` / `src/test/`
203
+ - `tests/` directories
204
+ - If no test references the symbol, mark `🧪 No coverage` — this raises regression risk
205
+ and may bump the impact level up by one.
206
+ - If a test exists but does not exercise the failing path, note `🧪 Partial`.
207
+
208
+ ## Output format
209
+
210
+ Render to chat as Markdown. Group by impact (🔴 → 💬).
211
+
212
+ ```markdown
213
+ # PR #{num} — Review Audit
214
+
215
+ > **{title}**
216
+ > {url}
217
+ > Inline: {N} · Review bodies: {M} · General: {K} · Resolved: {L} · Outdated: {O}
218
+ > Languages: TypeScript, Kotlin
219
+
220
+ ---
221
+
222
+ ## 🔴 Critical
223
+
224
+ ### 1. `path/to/file.ext:42` — @reviewer
225
+ > Comment body, quoted verbatim.
226
+
227
+ - **📍 Location**: `ClassOrModule.symbol` (domain logic)
228
+ - **🔎 Verified**: Issue still present in the current code. {one-line analysis}
229
+ - **📞 Call path**: `Controller.foo → Service.bar → this`. 4 call sites total. ⚡ Also reached via Kafka listener `SomeConsumer.onMessage` — no surrounding transaction.
230
+ - **🧪 Coverage**: No test reference (🧪 No coverage) — regression risk.
231
+ - **💡 Resolution**:
232
+ ```diff
233
+ - before
234
+ + after
235
+ ```
236
+
237
+ ---
238
+
239
+ ## 🟠 High
240
+
241
+ ### 2. …
242
+
243
+ ## 🟡 Medium
244
+ ## 🟢 Low
245
+ ## 💬 Discussion
246
+
247
+ ---
248
+
249
+ ## Priority summary
250
+
251
+ | # | Impact | Location | One-liner | Coverage | Resolved |
252
+ |---|---|---|---|---|---|
253
+ | 1 | 🔴 Critical | file.ext:42 | NPE on empty list | ❌ | ❌ |
254
+ | 2 | 🟠 High | other.ext:88 | Missing transaction | ✅ | ❌ |
255
+
256
+ ### Same-function clusters
257
+
258
+ - `Foo.bar` — items #2, #4, #7 (three reviewers flagged the same function)
259
+
260
+ ## Next actions
261
+
262
+ - **Fix in this PR**: #1, #2
263
+ - **Split into a follow-up PR**: #5, #7
264
+ - **Reply only (no code change)**: #6, #8
265
+ ```
266
+
267
+ Every numbered item must include all five lines (location / verified / call path /
268
+ coverage / resolution). Do not skip any.
269
+
270
+ ## Saving to a file (optional)
271
+
272
+ Default is chat output only. If the user asks to save:
273
+
274
+ - Inside an agentforge worktree (`…/anvil/<slug>/…`): write to
275
+ `anvil/<slug>/pr-reviews/pr-{num}-{repo}.md`.
276
+ - Otherwise: write to `docs/pr-reviews/pr-{num}.md` relative to cwd (create the
277
+ directory if missing).
278
+
279
+ ## Handling large PRs
280
+
281
+ If there are 30+ threads, ask the user upfront:
282
+ > "There are {N} threads. Want me to start with Critical/High only and continue on
283
+ > request?"
284
+
285
+ If they say yes, deliver only those tiers first, then continue in batches.
286
+
287
+ ## Rules
288
+
289
+ - **Read the code, don't trust the comment**: always open `path:line` and confirm
290
+ before judging.
291
+ - **Outdated comments**: re-read the current file; the comment's snapshot may be
292
+ stale. State explicitly whether the issue still exists.
293
+ - **Self-comments**: the PR author may leave intentional TODOs/notes. Label them
294
+ `(self)` and analyze as usual.
295
+ - **Approved bodies**: skip if pure praise; analyze if they contain code remarks.
296
+ - **No secret leakage**: if a comment contains a token, password, or internal URL,
297
+ do not echo it verbatim — redact it as `[REDACTED]`.
298
+ - **No code changes**: this skill is read-only. If the user wants a fix, they will
299
+ ask separately.
300
+
301
+ ## Output language
302
+
303
+ {{OUTPUT_LANGUAGE_INSTRUCTION}}
@@ -0,0 +1,350 @@
1
+ ---
2
+ name: agentforge-pre-deploy-check
3
+ description: Right before merging or deploying a feature worktree, scans the diff for non-code changes that ops or infra needs to handle — DB schema / migrations, environment variables, cache keys (Redis etc.), message-queue topics or event schemas, dependency bumps, Dockerfile / k8s / Helm / Terraform changes, feature flags, new HTTP endpoints, scheduled jobs. Surfaces gaps (e.g. entity changed but no migration), proposes a deploy order, and notes rollback caveats. Triggers on "pre-deploy check", "deploy readiness", "anything ops needs before I ship this?", "ready to merge?". Read-only.
4
+ ---
5
+
6
+ # pre-deploy-check
7
+
8
+ The last sanity gate before merging or deploying a feature worktree. Code gets reviewed
9
+ in PRs, but the things alongside it — migrations, env vars, cache keys, message
10
+ contracts, infra files — often slip through and cause incidents after deploy. This
11
+ skill walks the diff and surfaces those.
12
+
13
+ **Read-only.** Reports only; never modifies code.
14
+
15
+ ## When to apply
16
+
17
+ Trigger phrases:
18
+ - "Pre-deploy check."
19
+ - "Anything ops needs before I ship this?"
20
+ - "Ready to merge?"
21
+ - "Deploy readiness."
22
+ - "Did I miss any config / migration / env change?"
23
+
24
+ Apply once the user has finished the implementation and is approaching merge — not in
25
+ the middle of active development.
26
+
27
+ ## Resolve scope
28
+
29
+ The skill needs a clear before/after to diff against. Resolve scope from cwd:
30
+
31
+ - `…/anvil/<slug>/<repo>/` → one worktree, one repo.
32
+ - `…/anvil/<slug>/` → walk every `<repo>/` subdirectory and check each.
33
+ - `…/repos/<name>/` → that repo's currently checked-out branch.
34
+ - Anywhere else with a git repo → use cwd; ask the user for the base branch if it's not
35
+ obviously `main` / `master` / `develop`.
36
+
37
+ For the **base branch**, in order of preference: `origin/main`, `origin/master`,
38
+ `origin/develop`. Confirm with the user if none is obvious.
39
+
40
+ Capture the diff for each repo in scope:
41
+
42
+ ```bash
43
+ git -C <repo> diff --name-status <base>...HEAD # changed file list
44
+ git -C <repo> diff <base>...HEAD # full hunks (read in chunks)
45
+ ```
46
+
47
+ ## Categorized checks
48
+
49
+ Run each category against the diff. For each category, output one of:
50
+ ✅ no concern · ⚠️ heads-up · ❌ action required.
51
+
52
+ **Stack coverage**: this checklist spans backend, frontend, mobile, and
53
+ infrastructure concerns. Not every category applies to every workspace:
54
+ - A pure frontend repo will usually skip **A. DB schema**, **C. Cache keys**
55
+ (server-side), **D. Message queues**, **I. Cron**.
56
+ - A pure backend repo will usually skip frontend-specific bundle / asset
57
+ checks (folded into **E. Dependencies** / **F. Infra** here).
58
+ - Mobile app repos focus mostly on **E. Dependencies**, **G. Feature flags**,
59
+ **H. HTTP API surface** (from the consumer side), and platform-specific
60
+ config (Info.plist, AndroidManifest) — covered under **F. Infra**.
61
+
62
+ If a category has no matching diff signal in this workspace, mark it ✅ and
63
+ move on rather than warning.
64
+
65
+ ### A. Database schema / entities
66
+
67
+ Look for changes in files matching ORM / schema conventions:
68
+
69
+ | Framework | Entity / model files | Migration dir conventions |
70
+ |---|---|---|
71
+ | JPA / Hibernate (Kotlin/Java) | `@Entity` annotated classes | `src/main/resources/db/migration/V*.sql` (Flyway), Liquibase changelogs |
72
+ | TypeORM | classes with `@Entity()` | `migrations/*.ts` (`Migration1700...`) |
73
+ | Prisma | `schema.prisma` | `prisma/migrations/*/migration.sql` |
74
+ | Sequelize | `models/*.ts|js` | `migrations/*-*.js` |
75
+ | Django | `models.py` | `*/migrations/00*.py` |
76
+ | SQLAlchemy / Alembic | declarative model classes | `alembic/versions/*.py` |
77
+ | Knex | model usage | `knex/migrations/*.js` |
78
+ | GORM | structs with `gorm:` tags | hand-rolled SQL migrations |
79
+
80
+ For each entity / model change:
81
+ 1. Identify what changed: field added / removed / type changed / nullable flipped /
82
+ index added / unique constraint changed / FK added.
83
+ 2. **Match to a migration file in the same diff.** If the entity changed but no
84
+ migration shows up → ❌ "Entity changed without migration".
85
+ 3. Check the migration for production safety:
86
+ - Adding a `NOT NULL` column without default on a large table → ❌ blocking on
87
+ write traffic.
88
+ - Renaming a column → ⚠️ readers/writers must be deployed in the right order.
89
+ - Dropping a column or table → ❌ irreversible; flag for explicit confirmation.
90
+ - Adding an index on a hot table → ⚠️ may need `CONCURRENTLY` / online-DDL tooling.
91
+ 4. Note rollback story: is the migration reversible? Most "drop column" migrations are
92
+ not.
93
+
94
+ ### B. Environment variables
95
+
96
+ Find references to env vars in the diff. Patterns by language:
97
+
98
+ - TS/JS: `process.env.X` / `import.meta.env.X`
99
+ - Java/Kotlin: `System.getenv("X")` / `@Value("${X}")` / `${X}` in `application*.yml`
100
+ - Python: `os.getenv("X")` / `os.environ["X"]` / Pydantic `BaseSettings` fields
101
+ - Go: `os.Getenv("X")` / `viper.GetString("X")`
102
+ - Rust: `std::env::var("X")` / `dotenv` macros
103
+
104
+ For each env var newly **referenced** in the diff:
105
+
106
+ 0. **Filter out dev-only references** to avoid false positives. Look at the
107
+ surrounding context (a few lines before / after the hit):
108
+ - Inside a `process.env.NODE_ENV === 'development'` / `if dev:` /
109
+ `@Profile("local")` / `getenv("APP_ENV") == "test"` style branch → dev-only.
110
+ - In `*.test.*` / `*.spec.*` / `__tests__/` / `test/` / `tests/` directory →
111
+ test-only.
112
+ - In a documentation block or commented-out code → ignore.
113
+ Mark these `✓ dev/test-only` and do not flag them as missing in production
114
+ templates.
115
+
116
+ 1. Check whether the corresponding key is also added to one of the template files in
117
+ the same repo:
118
+ - `.env.example`, `.env.sample`, `.env.template`
119
+ - `application.yml` / `application.properties` (Spring)
120
+ - `config/*.yml`
121
+ - Helm `values.yaml`, K8s ConfigMap/Secret manifests, `kustomization.yaml`
122
+ - Terraform `*.tf` (for cloud env injection)
123
+ 2. If referenced but missing from any template / sample → ❌ "New env var X has no
124
+ template entry".
125
+ 3. If a key was **removed** from code but still present in templates → ⚠️ "Dead env
126
+ var X still in templates".
127
+ 4. Sensitive-looking values (matches `*_SECRET`, `*_TOKEN`, `*_KEY`, `*_PASSWORD`)
128
+ must go to secret storage (Vault / AWS SSM / k8s Secret), not plain ConfigMap →
129
+ ⚠️ flag if it appears to live in a non-secret location.
130
+
131
+ ### C. Cache keys (Redis / Memcached / in-process)
132
+
133
+ Look for cache key construction patterns in the diff:
134
+
135
+ - `redis.get|set|del|expire|hget|...` calls
136
+ - `redisson` / `lettuce` / `spring-data-redis` patterns
137
+ - Generic cache APIs: `cache.set`, `Cache.put`, `@Cacheable("name")`
138
+ - Memcached: `memcache.get|set`
139
+
140
+ For each cache touch in the diff:
141
+ 1. **Key format change** (string template, prefix, separator) → ❌ "Existing cache
142
+ entries with the old key are now orphaned and the read path will miss." Action:
143
+ schedule cache invalidation or backfill before / after deploy.
144
+ 2. **Value schema change** (what's stored at the key) → ❌ "Old cached values are no
145
+ longer compatible with the new reader." Action: bump key prefix or invalidate.
146
+ 3. **TTL change** → ⚠️ memory footprint / load pattern may shift.
147
+ 4. **New cache introduced** → ⚠️ confirm memory headroom and eviction policy.
148
+
149
+ ### D. Message queues / event schemas
150
+
151
+ Look for queue / topic / event references:
152
+
153
+ - Kafka: `@KafkaListener`, `KafkaTemplate.send`, topic literals
154
+ - RabbitMQ: `@RabbitListener`, `rabbitTemplate.convertAndSend`, exchange/queue names
155
+ - SQS: `sqs.sendMessage`, queue URLs / names
156
+ - Pub/Sub / EventBridge / NATS: equivalent client calls
157
+ - Internal event bus: enum / sealed class additions in event modules
158
+
159
+ For each:
160
+ 1. **New topic / queue** → ❌ "Provision required before deploy; consumers must be
161
+ running first."
162
+ 2. **Renamed topic** → ❌ "Producers and consumers must roll in lockstep; old topic
163
+ may need to be drained."
164
+ 3. **Event payload schema change** → ❌ check consumer compatibility. Schema registry
165
+ (Confluent, Buf) updates needed?
166
+ 4. **New consumer** for an existing topic → ⚠️ consumer group / offset reset policy
167
+ to confirm (`earliest` will replay history).
168
+
169
+ ### E. External dependencies / lock files
170
+
171
+ Look for changes in:
172
+ - `package.json` / `package-lock.json` / `pnpm-lock.yaml` / `yarn.lock`
173
+ - `build.gradle` / `build.gradle.kts` / `pom.xml`
174
+ - `go.mod` / `go.sum`
175
+ - `Cargo.toml` / `Cargo.lock`
176
+ - `requirements.txt` / `Pipfile.lock` / `poetry.lock` / `uv.lock`
177
+ - `composer.json` / `composer.lock`
178
+
179
+ For each:
180
+ 1. **Lock file changed but manifest didn't** (or vice versa) → ❌ they're out of sync.
181
+ 2. **Major version bump** (semver `X.0.0`) → ⚠️ "Read release notes; potential
182
+ breaking changes."
183
+ 3. **New dependency** → ⚠️ note license / security expectations; flag if from an
184
+ unfamiliar org.
185
+ 4. **Removed dependency** still imported somewhere → ❌ build will fail.
186
+
187
+ ### F. Infrastructure / deploy config
188
+
189
+ Look for diffs in:
190
+ - `Dockerfile` / `*.dockerfile` / `.dockerignore`
191
+ - `docker-compose.yml`
192
+ - Kubernetes: `*.yaml` in `k8s/`, `manifests/`, `deploy/`
193
+ - Helm: `Chart.yaml`, `values*.yaml`, `templates/*.yaml`
194
+ - Terraform: `*.tf`, `*.tfvars`
195
+ - Pulumi
196
+ - CI: `.github/workflows/*.yml`, `.gitlab-ci.yml`, `Jenkinsfile`, `circleci/`
197
+
198
+ For each:
199
+ 1. **Base image change** → ⚠️ confirm image registry availability, vulnerability scan.
200
+ 2. **New port exposed** → ❌ load balancer / security group / service definition may
201
+ need updating.
202
+ 3. **Resource requests/limits changed** → ⚠️ capacity / autoscaling impact.
203
+ 4. **New mounted secret** → ❌ secret must exist in cluster before pod schedules.
204
+ 5. **CI workflow change** → ⚠️ first run may behave unexpectedly; tell the user.
205
+
206
+ ### G. Feature flags
207
+
208
+ Look for SDK / patterns:
209
+ - LaunchDarkly: `ldClient.variation(...)`, `boolVariation(...)`
210
+ - Unleash: `unleash.isEnabled("...")`
211
+ - GrowthBook: `growthbook.isOn("...")`
212
+ - Statsig: `statsig.checkGate("...")`
213
+ - Custom in-house flags: any constant matching `FEATURE_*` / `*_FLAG`
214
+
215
+ For each:
216
+ 1. **New flag referenced** → ❌ "Flag X must be created in the flag service before
217
+ this rolls out, otherwise the default (typically `false`) governs."
218
+ 2. **Flag removed from code** → ⚠️ "Stale flag can be archived in the flag service."
219
+
220
+ ### H. HTTP API surface
221
+
222
+ Look for route / handler changes:
223
+ - Spring: `@RequestMapping`, `@GetMapping`, etc.
224
+ - Express / Fastify: `app.get('/...', ...)`, `router.post('/...', ...)`
225
+ - NestJS: `@Controller`, `@Get`, etc.
226
+ - FastAPI: `@app.get`, `@router.post`
227
+ - Gin / Echo / Fiber: `r.GET("/...", ...)`
228
+ - Rails routes: `config/routes.rb`
229
+
230
+ For each:
231
+ 1. **New endpoint** → ⚠️ confirm whether gateway / reverse proxy / API documentation
232
+ (OpenAPI, Postman) needs updating.
233
+ 2. **Removed endpoint** → ❌ "Clients calling this will 404; coordinate or
234
+ deprecate."
235
+ 3. **Response shape change** → ⚠️ if not versioned, callers break. Cross-reference
236
+ with `cross-repo-impact` if available.
237
+ 4. **Auth requirements change** (`@PreAuthorize`, middleware adjustments) → ❌ ensure
238
+ intended; surface plainly.
239
+
240
+ ### I. Scheduled jobs / cron
241
+
242
+ Look for:
243
+ - Spring: `@Scheduled(cron = "...")`
244
+ - Quartz: cron expressions
245
+ - Node-cron / BullMQ repeating jobs
246
+ - Celery beat schedules
247
+ - K8s `CronJob` resources
248
+
249
+ For each:
250
+ 1. **New job** → ❌ "Confirm cluster timezone, single-instance behavior, monitoring
251
+ wired up."
252
+ 2. **Schedule change** → ⚠️ load profile changes (e.g. moving a job to peak hours).
253
+ 3. **Removed job** → ⚠️ outstanding work / cleanup needed.
254
+
255
+ ## Output format
256
+
257
+ ```markdown
258
+ # Pre-deploy check: <slug or branch>
259
+
260
+ > Worktree: `anvil/<slug>/<repo>/`
261
+ > Base: `origin/main` (HEAD ahead by N commits)
262
+ > Files changed: 42
263
+
264
+ ---
265
+
266
+ ## ❌ Action required
267
+
268
+ ### A. Database — entity changed without migration
269
+ - `src/main/.../FooEntity.kt:88` — added `someField` column
270
+ - No matching migration in `db/migration/`
271
+ - **Action**: create Flyway migration adding the column; default value or NOT NULL?
272
+
273
+ ### B. Env var — `KAFKA_BROKER_SASL_USER` referenced, no template entry
274
+ - Added in `KafkaConfig.kt:42`
275
+ - Not present in `application.yml` or `helm/values.yaml`
276
+ - **Action**: add to Helm secret + chart values; bump to k8s before deploy.
277
+
278
+ ### C. Cache — key format changed for `user:profile:<id>`
279
+ - `UserProfileCache.kt:18` now uses `user:profile:v2:<id>`
280
+ - Old entries are orphaned
281
+ - **Action**: either bump prefix is fine if cache is stale-tolerant; otherwise
282
+ invalidate the `user:profile:*` namespace right after deploy.
283
+
284
+ ---
285
+
286
+ ## ⚠️ Heads-up
287
+
288
+ ### F. Infra — Dockerfile base image bumped to `eclipse-temurin:21-jre-jammy`
289
+ - Was `17-jre-jammy`
290
+ - Confirm JVM args / GC behavior; image size +120 MB.
291
+
292
+ ### G. Feature flag — new flag `search-ranking-v2`
293
+ - `FooController.kt:55`
294
+ - **Action**: create the flag in LaunchDarkly with default `false` before merging
295
+ this PR.
296
+
297
+ ---
298
+
299
+ ## ✅ Clean
300
+
301
+ - E. Dependencies (only patch bumps; lock file in sync)
302
+ - D. Message queues (no topic / payload changes)
303
+ - H. HTTP API (no public surface changes)
304
+ - I. Scheduled jobs (no changes)
305
+
306
+ ---
307
+
308
+ ## Suggested deploy order
309
+
310
+ 1. Apply DB migration (`V42__add_some_field.sql`).
311
+ 2. Update Helm values + k8s Secret with `KAFKA_BROKER_SASL_USER`.
312
+ 3. Create `search-ranking-v2` flag in LaunchDarkly (default `false`).
313
+ 4. Deploy backend.
314
+ 5. Run cache invalidation for `user:profile:*` (optional if stale-tolerant).
315
+ 6. Smoke test, then ramp the flag.
316
+
317
+ ## Rollback notes
318
+
319
+ - DB migration adds a nullable column — safe to leave in place if you roll back code.
320
+ - Env var addition has no rollback action (extra env is ignored).
321
+ - Cache key bump — rolling back code reads from the old `user:profile:<id>` keys;
322
+ those still exist (we only added a new namespace), so rollback is safe.
323
+ - Feature flag — flip to `false` on rollback.
324
+ ```
325
+
326
+ Structure the report so the **❌ Action required** section is impossible to miss. Even
327
+ if the change is small, include the "Suggested deploy order" section — it's the part
328
+ people forget.
329
+
330
+ ## Rules
331
+
332
+ - **Workspace-aware**: if you're in `anvil/<slug>/`, walk every `<repo>/` subdirectory
333
+ in scope and check each. Don't escape to unrelated repos.
334
+ - **Diff-driven**: only check things that actually changed. Don't audit the whole
335
+ codebase.
336
+ - **Acknowledge unknowns**: if a framework or pattern isn't recognized, say so
337
+ explicitly — "no env-var references detected by known patterns; double-check
338
+ manually."
339
+ - **Never edit**: this skill never writes code, migration files, env templates, or
340
+ config. It reports.
341
+ - **Don't invent migrations**: if a migration is missing, say so. Do not write one
342
+ unless the user asks separately.
343
+ - **Be specific**: every ❌ / ⚠️ item must point at a file/line in the diff. No vague
344
+ warnings.
345
+ - **Mind the deploy order**: a list of risks without an ordering is half a deliverable.
346
+ Always include the "Suggested deploy order" section.
347
+
348
+ ## Output language
349
+
350
+ {{OUTPUT_LANGUAGE_INSTRUCTION}}