agentscamp 0.1.0 → 0.2.1
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.
- package/README.md +3 -3
- package/content/commands/create-skill.md +84 -0
- package/content/commands/create-slash-command.md +80 -0
- package/content/commands/create-subagent.md +101 -0
- package/content/commands/estimate-effort.md +96 -0
- package/content/commands/find-n-plus-one.md +67 -0
- package/content/commands/flaky-test-hunt.md +112 -0
- package/content/commands/git-bisect.md +126 -0
- package/content/commands/rename-symbol.md +131 -0
- package/content/commands/resolve-conflict.md +127 -0
- package/content/commands/scaffold-rag-pipeline.md +76 -0
- package/content/commands/trace-data-flow.md +102 -0
- package/content/manifest.json +320 -3
- package/content/skills/agent-memory-designer.md +42 -0
- package/content/skills/auth-flow-reviewer.md +73 -0
- package/content/skills/extract-module.md +50 -0
- package/content/skills/migration-writer.md +41 -0
- package/content/skills/prompt-regression-tester.md +89 -0
- package/content/skills/rate-limiter-designer.md +56 -0
- package/content/skills/react-render-profiler.md +37 -0
- package/content/skills/semver-advisor.md +81 -0
- package/content/skills/type-coverage-improver.md +81 -0
- package/content/skills/version-bumper.md +91 -0
- package/content/skills/webhook-handler-scaffolder.md +37 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# agentscamp
|
|
2
2
|
|
|
3
|
-
>
|
|
3
|
+
> 138 ready-to-use Claude Code agents, skills, and slash commands — installable in one command.
|
|
4
4
|
|
|
5
5
|
[AgentsCamp](https://agentscamp.com) is a curated, format-validated directory of AI coding artifacts. This CLI bundles the full catalog and installs items straight into your `.claude/` directory.
|
|
6
6
|
|
|
@@ -43,8 +43,8 @@ These are Claude Code's standard locations — agents get delegated to automatic
|
|
|
43
43
|
## What's inside
|
|
44
44
|
|
|
45
45
|
- **49 agents** — specialized subagents for development, data/AI, infra, security, and more → [browse agents](https://agentscamp.com/agents)
|
|
46
|
-
- **
|
|
47
|
-
- **
|
|
46
|
+
- **49 skills** — on-demand capabilities for testing, databases, refactoring, releases → [browse skills](https://agentscamp.com/skills)
|
|
47
|
+
- **40 commands** — reusable slash commands for planning, review, git, scaffolding → [browse commands](https://agentscamp.com/commands)
|
|
48
48
|
|
|
49
49
|
Every item has a full page with docs, examples, and related picks at [agentscamp.com](https://agentscamp.com).
|
|
50
50
|
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: "Scaffold a new Claude Code skill into .claude/skills/<name>/SKILL.md — a model-invoked capability with a trigger-rich description, scoped tools, and a lean body that pushes detail into resource files."
|
|
3
|
+
argument-hint: "<what the skill should do>"
|
|
4
|
+
allowed-tools: "Read, Write, Glob, Grep"
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
## Scope
|
|
8
|
+
|
|
9
|
+
Treat `$ARGUMENTS` as the skill's purpose — the capability you want Claude to reach for automatically. Restate it in one sentence, then derive a **kebab-case skill name** (verb-led, e.g. `migrate-sql-schema`, `summarize-pr`) that will be both the folder name and the `name` field.
|
|
10
|
+
|
|
11
|
+
If `$ARGUMENTS` is empty, ask one focused question: *"What capability should this skill add, and roughly when should Claude reach for it?"* Do not invent a skill.
|
|
12
|
+
|
|
13
|
+
Default to **project scope**: `.claude/skills/<name>/SKILL.md`. Mention `~/.claude/skills/<name>/` as the alternative for skills the user wants across every project, and let them pick.
|
|
14
|
+
|
|
15
|
+
> [!WARNING]
|
|
16
|
+
> A skill is **model-invoked**, not user-invoked. Claude decides to load it by matching the conversation against the `description`. If the description is vague, the skill never fires — so the description is the most important thing you write, not the body.
|
|
17
|
+
|
|
18
|
+
## Step 1 — Understand the capability and check for collisions
|
|
19
|
+
|
|
20
|
+
Pin down what triggers the skill, what it does, and what it produces. Then `Glob .claude/skills/*/SKILL.md` (and `~/.claude/skills/*/SKILL.md`) and `Grep` the existing `name:` / `description:` lines. If a near-duplicate exists, say so and offer to extend it instead of creating an overlapping skill — two skills with similar descriptions make activation ambiguous.
|
|
21
|
+
|
|
22
|
+
## Step 2 — Skill vs. slash command sanity check
|
|
23
|
+
|
|
24
|
+
Confirm a skill is the right artifact before writing one:
|
|
25
|
+
|
|
26
|
+
- **Skill** — Claude should invoke it *on its own* whenever a matching situation arises (a recurring task it should recognize, e.g. "writing a migration", "reviewing Terraform"). Activation is the `description`.
|
|
27
|
+
- **Slash command** — the user wants to *trigger it explicitly by name* (`/create-skill`). If that's what they described, point them at `create-slash-command` instead and stop.
|
|
28
|
+
|
|
29
|
+
## Step 3 — Write the description (lead with what, then "Use when")
|
|
30
|
+
|
|
31
|
+
This is the load-bearing field. Format: one clause stating **what the skill does**, then a sentence starting **"Use when ..."** listing concrete triggers — the phrasings, file types, or intents that should activate it.
|
|
32
|
+
|
|
33
|
+
```
|
|
34
|
+
description: "Convert REST endpoint handlers into typed OpenAPI 3.1 specs. Use when the user adds or edits an Express/Fastify route, asks for API docs, or mentions an openapi.yaml / swagger file."
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
Name the real cues (file extensions, library names, task verbs). Avoid first-person and avoid "helps with" — write triggers Claude can pattern-match against.
|
|
38
|
+
|
|
39
|
+
## Step 4 — Write the SKILL.md (lean body, progressive disclosure)
|
|
40
|
+
|
|
41
|
+
Create `.claude/skills/<name>/SKILL.md` with `Write`. Frontmatter, then a body that fits comfortably on one screen:
|
|
42
|
+
|
|
43
|
+
```markdown
|
|
44
|
+
---
|
|
45
|
+
name: <kebab-name> # MUST equal the folder name
|
|
46
|
+
description: "<from Step 3>"
|
|
47
|
+
allowed-tools: Read, Grep, Glob # least privilege; omit to inherit the session's tools
|
|
48
|
+
user-invocable: true # also lets the user call it like /<name>
|
|
49
|
+
---
|
|
50
|
+
|
|
51
|
+
# <Title Case Name>
|
|
52
|
+
|
|
53
|
+
## When to use
|
|
54
|
+
Restate the triggers as a short checklist so Claude self-confirms before acting.
|
|
55
|
+
|
|
56
|
+
## Instructions
|
|
57
|
+
1. First concrete step, referencing the inputs the skill works on.
|
|
58
|
+
2. ...
|
|
59
|
+
3. ...
|
|
60
|
+
|
|
61
|
+
## Output
|
|
62
|
+
Exactly what the skill produces and where (file path, message, diff).
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
Rules that keep the skill reliable:
|
|
66
|
+
|
|
67
|
+
- `name` must be identical to the folder name — a mismatch makes the skill fail to register.
|
|
68
|
+
- Scope `allowed-tools` to the minimum the procedure needs. A skill that only inspects code gets `Read, Grep, Glob`; add `Write`/`Edit`/`Bash(...)` only if it must change things.
|
|
69
|
+
- Keep `SKILL.md` focused on the *procedure*. The body is loaded into context every time the skill fires, so every extra line is a recurring token cost.
|
|
70
|
+
|
|
71
|
+
## Step 5 — Decide whether it needs resource files
|
|
72
|
+
|
|
73
|
+
Single-file is the default. Add sibling files only when warranted, and reference them by **relative path** so Claude loads them on demand (progressive disclosure), not eagerly:
|
|
74
|
+
|
|
75
|
+
- Long reference material (lookup tables, style rules, schemas) → `reference.md`, linked from SKILL.md as *"see ./reference.md for the full mapping"*.
|
|
76
|
+
- Runnable helpers → `scripts/<tool>.py` (or `.sh`), invoked from the Instructions; this requires a `Bash(...)` entry in `allowed-tools`.
|
|
77
|
+
- Reusable boilerplate the skill emits → `templates/<name>.tmpl`.
|
|
78
|
+
|
|
79
|
+
> [!NOTE]
|
|
80
|
+
> Progressive disclosure is the whole point of the multi-file layout: a one-paragraph pointer in SKILL.md costs a few tokens, while the linked file is read only when the task actually needs it. Don't paste a 300-line table into SKILL.md "to be safe."
|
|
81
|
+
|
|
82
|
+
## Report
|
|
83
|
+
|
|
84
|
+
Confirm the absolute path of the created `SKILL.md` (and any resource files), echo the `name` and `description`, and state the chosen scope (project vs. user). Tell the user the skill activates automatically when the conversation matches its triggers — and, if `user-invocable: true`, that they can also call it as `/<name>`. End with the single most useful next step: open a fresh session and try a prompt that should trip the trigger, to confirm the skill loads.
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: "Scaffold a new Claude Code slash command into .claude/commands/ — a valid Markdown file with frontmatter, a least-privilege allowed-tools allowlist, and a $ARGUMENTS-driven body of numbered steps ending in a Report."
|
|
3
|
+
argument-hint: "<what the command should do>"
|
|
4
|
+
allowed-tools: "Read, Write, Glob, Grep"
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
## Scope
|
|
8
|
+
|
|
9
|
+
Treat `$ARGUMENTS` as the **purpose of the command you are about to create** — for example, "review a PR for security issues" or "summarize today's git log". Restate it in one sentence, then commit to a slug and a tool allowlist before writing anything.
|
|
10
|
+
|
|
11
|
+
If `$ARGUMENTS` is empty, ask one focused question: *"What should the new command do?"* Do not scaffold a placeholder command from a guessed purpose — an empty stub is worse than no file.
|
|
12
|
+
|
|
13
|
+
Your deliverable is one valid file at `.claude/commands/<slug>.md` plus instructions for running it. You write exactly one new file; you do not modify existing commands.
|
|
14
|
+
|
|
15
|
+
## Step 1 — Derive the slug and check for collisions
|
|
16
|
+
|
|
17
|
+
Turn the purpose into a short, kebab-case verb-phrase slug (`review-pr`, `summarize-log`, `audit-deps`) — that filename *is* the command name. Then `Glob` `.claude/commands/**/*.md` and `~/.claude/commands/**/*.md`.
|
|
18
|
+
|
|
19
|
+
- If `<slug>.md` already exists, stop and report it. Propose a distinct slug or ask whether to overwrite — never silently clobber a command the team relies on.
|
|
20
|
+
- To group related commands, use a subfolder: `.claude/commands/git/review.md` is invoked as `/git:review` (the folder becomes a `:` namespace).
|
|
21
|
+
|
|
22
|
+
> [!WARNING]
|
|
23
|
+
> Default to the **project** scope `.claude/commands/`, which is committed and shared. Only write to `~/.claude/commands/` if the user explicitly asks for a personal, all-repos command.
|
|
24
|
+
|
|
25
|
+
## Step 2 — Choose the minimum allowed-tools
|
|
26
|
+
|
|
27
|
+
Pick the smallest tool set the command's job actually needs — this is the command's permission boundary, and Claude cannot exceed it at runtime.
|
|
28
|
+
|
|
29
|
+
- Read-only analysis (review, summarize, explain): `Read, Grep, Glob`.
|
|
30
|
+
- Generates a file: add `Write`. Edits in place: add `Edit`.
|
|
31
|
+
- Needs to run commands (tests, git, build): add `Bash` — the heaviest grant; justify it.
|
|
32
|
+
|
|
33
|
+
Omit `allowed-tools` only if the command should inherit the session's full tool access; for a shareable command, prefer an explicit allowlist.
|
|
34
|
+
|
|
35
|
+
## Step 3 — Write the frontmatter
|
|
36
|
+
|
|
37
|
+
Emit only the keys Claude Code recognizes. A command frontmatter block uses:
|
|
38
|
+
|
|
39
|
+
```yaml
|
|
40
|
+
---
|
|
41
|
+
description: <one sentence — shown in the /help list>
|
|
42
|
+
argument-hint: <e.g. "<pr number>" — omit if the command takes no args>
|
|
43
|
+
allowed-tools: <comma list from Step 2 — omit to inherit>
|
|
44
|
+
model: <haiku|sonnet|opus — omit to inherit; set opus only for heavy reasoning>
|
|
45
|
+
---
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
> [!NOTE]
|
|
49
|
+
> The command name comes from the **filename**, not a `name:` key. There is no `name` field in command frontmatter — adding one is just dead text.
|
|
50
|
+
|
|
51
|
+
## Step 4 — Write the body
|
|
52
|
+
|
|
53
|
+
Structure the body the same way this command is structured. It must:
|
|
54
|
+
|
|
55
|
+
1. Open with a **Scope** section that restates `$ARGUMENTS` as the command's real input and defines the deliverable.
|
|
56
|
+
2. **Handle empty `$ARGUMENTS` explicitly** — ask one specific question instead of guessing.
|
|
57
|
+
3. Lay out the work as **numbered Steps** (`## Step 1 — …`), each concrete and referencing only the tools declared in `allowed-tools`.
|
|
58
|
+
4. Reference `$ARGUMENTS` wherever the user's input drives behavior.
|
|
59
|
+
5. End with a **## Report** section stating exactly what the command outputs (a written answer, a created path, a diff summary).
|
|
60
|
+
|
|
61
|
+
Keep instructions decision-dense: prefer "Grep for `TODO(` and list each with file:line" over "look for issues".
|
|
62
|
+
|
|
63
|
+
## Step 5 — Write the file and self-check
|
|
64
|
+
|
|
65
|
+
`Write` the assembled frontmatter + body to the chosen path. Before reporting, verify:
|
|
66
|
+
|
|
67
|
+
- YAML frontmatter is fenced by `---` lines and parses.
|
|
68
|
+
- Every tool the body tells Claude to use appears in `allowed-tools`.
|
|
69
|
+
- `$ARGUMENTS` appears in the body and the empty case is handled.
|
|
70
|
+
- There is a final Report section.
|
|
71
|
+
|
|
72
|
+
## Report
|
|
73
|
+
|
|
74
|
+
Report:
|
|
75
|
+
|
|
76
|
+
1. The **absolute path** of the file you created (or the collision you stopped on).
|
|
77
|
+
2. The exact **invocation**, including namespace if any: `/<slug> <args>` (e.g. `/review-pr 482` or `/git:review HEAD~1`).
|
|
78
|
+
3. The `allowed-tools` you granted and the one-line reason.
|
|
79
|
+
|
|
80
|
+
End with a reminder to commit the file (project scope) so the team picks it up, and a suggested first test invocation.
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: "Scaffold a new Claude Code subagent definition file into .claude/agents/ with a routing-ready description, scoped tools, and a system prompt."
|
|
3
|
+
argument-hint: "<what the subagent should do>"
|
|
4
|
+
allowed-tools: "Read, Write, Glob, Grep"
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
## Scope
|
|
8
|
+
|
|
9
|
+
Treat `$ARGUMENTS` as the **purpose** of a new subagent — what specialized job it should own (`review database migrations for lock risk`, `write conventional-commit messages from a diff`, `triage incoming Sentry errors`). A subagent is a single-purpose persona the main agent can delegate to, with its own context window, tool allowlist, and system prompt. Restate the purpose in one sentence and narrow it to **one job** before writing anything — broad agents ("does backend stuff") never get routed to reliably.
|
|
10
|
+
|
|
11
|
+
If `$ARGUMENTS` is empty or vague, ask **one** focused round of clarifying questions, then proceed:
|
|
12
|
+
|
|
13
|
+
1. **Domain & job** — what is the one task this agent should be the expert at?
|
|
14
|
+
2. **Tools** — does it need to write/edit files and run commands, or is it read-only (review, analysis, planning)?
|
|
15
|
+
3. **Model tier** — `haiku` (fast, mechanical), `sonnet` (default, most work), or `opus` (deep reasoning / architecture)?
|
|
16
|
+
|
|
17
|
+
Do not ask more than once. If the user is terse, pick sane defaults (read-only, `sonnet`), state them, and write the file.
|
|
18
|
+
|
|
19
|
+
> [!WARNING]
|
|
20
|
+
> You only create the agent definition. Do not invoke the new subagent, run its workflow, or modify other files. Your single output is `.claude/agents/<slug>.md`.
|
|
21
|
+
|
|
22
|
+
## Step 1 — Derive the slug and check for collisions
|
|
23
|
+
|
|
24
|
+
Convert the purpose into a kebab-case `name` that reads like a role, not a sentence: `migration-reviewer`, `commit-message-writer`, `sentry-triager`. The filename **is** the slug, so `name` must match `<slug>.md`.
|
|
25
|
+
|
|
26
|
+
Use `Glob` on `.claude/agents/*.md` and `~/.claude/agents/*.md` to check the slug is not already taken. If it collides, `Read` the existing file: if it covers the same job, tell the user it already exists and stop; otherwise pick a more specific slug rather than overwriting.
|
|
27
|
+
|
|
28
|
+
## Step 2 — Decide the tool allowlist (least privilege)
|
|
29
|
+
|
|
30
|
+
The `tools` field restricts what the subagent can do; **omit it to inherit all tools**, or list the minimum it needs. Match tools to the job from Step 1:
|
|
31
|
+
|
|
32
|
+
- **Read-only roles** (reviewers, analyzers, planners): `Read, Grep, Glob`. No `Write`, `Edit`, or `Bash`.
|
|
33
|
+
- **Editing roles** (fixers, refactorers, scaffolders): add `Write, Edit` and only the `Bash` commands they need (e.g. the test runner).
|
|
34
|
+
- **Investigators** that run commands but never edit: `Read, Grep, Glob, Bash`.
|
|
35
|
+
|
|
36
|
+
> [!NOTE]
|
|
37
|
+
> A subagent runs in its **own** context window — it does not see your conversation, only the prompt the orchestrator hands it plus what its tools surface. That is why the system prompt below must be self-contained: state the workflow and conventions explicitly; the agent cannot rely on chat history.
|
|
38
|
+
|
|
39
|
+
## Step 3 — Write a description that triggers delegation
|
|
40
|
+
|
|
41
|
+
This is the most important field. The main agent scans every subagent's `description` to decide what to route where, so it must read as a routing rule, not a label. Pack it with **concrete trigger phrases**:
|
|
42
|
+
|
|
43
|
+
- Lead with the capability, then add explicit `Use this agent when...` / `Use proactively when...` cues tied to real situations and keywords a user would say.
|
|
44
|
+
- Name the inputs it expects (a diff, a file path, an error payload) so the orchestrator delegates with the right context.
|
|
45
|
+
|
|
46
|
+
A weak description (`"Reviews code."`) gets ignored. A strong one — `"Reviews database migration files for locking and downtime risk. Use this agent when the user adds or edits files under db/migrations, mentions ALTER TABLE, adding columns/indexes, or asks 'is this migration safe to run in prod?'"` — gets routed reliably.
|
|
47
|
+
|
|
48
|
+
## Step 4 — Write the agent file
|
|
49
|
+
|
|
50
|
+
`Write` `.claude/agents/<slug>.md` (create `.claude/agents/` if missing) in exactly this shape. Fill every placeholder with content specific to the purpose — no generic filler.
|
|
51
|
+
|
|
52
|
+
```markdown
|
|
53
|
+
---
|
|
54
|
+
name: <slug>
|
|
55
|
+
description: <capability + concrete "Use this agent when..." triggers from Step 3>
|
|
56
|
+
tools: <minimum allowlist from Step 2, or omit the line to inherit all>
|
|
57
|
+
model: <haiku | sonnet | opus>
|
|
58
|
+
color: <a distinct color, e.g. cyan, green, orange>
|
|
59
|
+
---
|
|
60
|
+
|
|
61
|
+
You are <role> — a focused specialist in <domain>. <One sentence on the
|
|
62
|
+
standard you hold and the value you add.>
|
|
63
|
+
|
|
64
|
+
## When to use me
|
|
65
|
+
- <concrete situation 1>
|
|
66
|
+
- <concrete situation 2>
|
|
67
|
+
|
|
68
|
+
## When NOT to use me
|
|
69
|
+
- <adjacent job that belongs to a different agent / the main agent>
|
|
70
|
+
- <scope you must refuse and hand back instead of guessing>
|
|
71
|
+
|
|
72
|
+
## Workflow
|
|
73
|
+
1. <first concrete step, naming the tools you'll use>
|
|
74
|
+
2. <gather context: which files/patterns to Read/Grep before acting>
|
|
75
|
+
3. <do the core work>
|
|
76
|
+
4. <self-check against the standard in your role statement>
|
|
77
|
+
|
|
78
|
+
## Output contract
|
|
79
|
+
- <exact format you return: a review with severity-tagged findings, a
|
|
80
|
+
unified diff, a checklist, a single recommendation — be specific>
|
|
81
|
+
- <what you must NOT do: e.g. never edit beyond the named files; flag,
|
|
82
|
+
don't fix, anything outside scope>
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
Field rationale to honor while filling it in:
|
|
86
|
+
|
|
87
|
+
- **`name`** must equal the filename slug — that is how the agent is addressed.
|
|
88
|
+
- **`description`** is the delegation trigger (Step 3); spend your effort here.
|
|
89
|
+
- **`model`** controls cost vs. depth — don't default everything to `opus`.
|
|
90
|
+
- **`color`** is just the UI label color; pick one not already used by a sibling agent.
|
|
91
|
+
- **`tools`** is the security boundary — narrower is safer and keeps the agent on task.
|
|
92
|
+
|
|
93
|
+
## Report
|
|
94
|
+
|
|
95
|
+
Tell the user, in your message:
|
|
96
|
+
|
|
97
|
+
1. The exact path written — `.claude/agents/<slug>.md` (and whether it's project- or user-scoped).
|
|
98
|
+
2. How to invoke it — it triggers automatically when a request matches its `description`, or explicitly via *"use the `<slug>` subagent to ..."*.
|
|
99
|
+
3. The one knob most likely to need tuning: if the agent isn't getting picked up, sharpen the `description` triggers (Step 3) — that is almost always the cause.
|
|
100
|
+
|
|
101
|
+
Note that moving the file to `~/.claude/agents/` makes the subagent available across all projects.
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: "Produce a grounded effort and complexity estimate for a task by exploring the codebase read-only."
|
|
3
|
+
argument-hint: "<task or feature to estimate>"
|
|
4
|
+
allowed-tools: "Read, Grep, Glob"
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
Estimate the effort to do the task in `$ARGUMENTS` by grounding it in the real codebase, not in vibes. This is an analysis pass only — read to understand, then deliver an estimate. Do not edit, run, or create anything.
|
|
8
|
+
|
|
9
|
+
## Scope
|
|
10
|
+
|
|
11
|
+
Treat `$ARGUMENTS` as the task to size — a feature, a refactor, a migration, a bug ("add SSO via SAML", "split the monolith's billing module into a service", "fix the flaky checkout test"). If it names files, symbols, or routes, those are where your investigation starts.
|
|
12
|
+
|
|
13
|
+
If `$ARGUMENTS` is empty, do not invent a task. Ask one question — *"What task should I estimate?"* — and stop until they answer.
|
|
14
|
+
|
|
15
|
+
> [!WARNING]
|
|
16
|
+
> Read-only mode. Your only output is the written estimate. An estimate is a range under stated assumptions, never a commitment or a deadline — say so explicitly in the report so nobody quotes the midpoint as a promise.
|
|
17
|
+
|
|
18
|
+
## Step 1 — Pin down scope and non-scope
|
|
19
|
+
|
|
20
|
+
Restate the task in one sentence. Then list what is explicitly **out of scope** — the adjacent work a reader might assume is included but isn't (migrations of old data, the admin UI, rollback tooling, docs). Unbounded scope is the single biggest source of estimate error; naming the boundary is half the job.
|
|
21
|
+
|
|
22
|
+
If the task hides a fork that changes the size by an order of magnitude (rewrite vs. patch? backward-compatible vs. breaking? one provider vs. a pluggable abstraction?), surface it as an open question — do not silently pick the cheap reading.
|
|
23
|
+
|
|
24
|
+
## Step 2 — Explore the code to ground the estimate
|
|
25
|
+
|
|
26
|
+
Read enough of the repo to size against reality. Do not guess the structure — find it.
|
|
27
|
+
|
|
28
|
+
```bash
|
|
29
|
+
# Find the symbols, routes, or modules the task touches
|
|
30
|
+
grep -rn "checkoutHandler" src/
|
|
31
|
+
|
|
32
|
+
# Map the surrounding structure and blast radius
|
|
33
|
+
find src -path '*billing*' -name '*.ts'
|
|
34
|
+
|
|
35
|
+
# Gauge how much code already exists vs. needs writing
|
|
36
|
+
grep -rln "PaymentProvider" src/
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
Open the entry points, trace one level of callers and callees, and note the seams the change crosses: shared types, config, public APIs, tests, and anything with many inbound references. A change behind a clean interface is small; one that ripples through dozens of call sites is not.
|
|
40
|
+
|
|
41
|
+
> [!NOTE]
|
|
42
|
+
> Existing tests are an effort multiplier in both directions. Good coverage on the touched area shrinks the estimate (you can refactor safely); zero coverage on a critical path inflates it (you must write characterization tests before you dare touch it). Check before you size.
|
|
43
|
+
|
|
44
|
+
## Step 3 — Decompose into independently-shippable subtasks
|
|
45
|
+
|
|
46
|
+
Cut the work into subtasks that each land on their own and deliver a checkable result — schema, then store, then the wiring, then tests, then docs. Estimating the whole as one lump is where guesses hide; estimating parts forces you to confront each one. Anything you can't decompose is a sign you don't understand it yet — flag it as a spike.
|
|
47
|
+
|
|
48
|
+
## Step 4 — Size each subtask and sum
|
|
49
|
+
|
|
50
|
+
Give each subtask a T-shirt size with a rough hands-on range (these are illustrative — calibrate to the team and stack):
|
|
51
|
+
|
|
52
|
+
| Size | Rough range | Looks like |
|
|
53
|
+
| ---- | ----------- | ---------- |
|
|
54
|
+
| S | < ~0.5 day | localized edit, pattern already exists in the repo |
|
|
55
|
+
| M | ~0.5–2 days | new code across a couple of files, clear approach |
|
|
56
|
+
| L | ~2–5 days | new seam or interface, touches several modules |
|
|
57
|
+
| XL | > ~1 week | unknown approach, cross-cutting, or needs a spike first — decompose further |
|
|
58
|
+
|
|
59
|
+
An XL is a smell: split it until the pieces are L or smaller, or call it a spike whose only deliverable is a smaller estimate. Sum the subtask ranges into a **total range** (low to high), not a single number.
|
|
60
|
+
|
|
61
|
+
> [!WARNING]
|
|
62
|
+
> Do not just add the optimistic ends. Integration, review, and the inevitable "while I was in there" overhead are real — fold them in as their own line or a percentage, and let the high end of the range carry the unknowns rather than burying them.
|
|
63
|
+
|
|
64
|
+
## Step 5 — Surface risks, dependencies, and assumptions
|
|
65
|
+
|
|
66
|
+
The estimate is only as good as what could blow it up. List, specifically:
|
|
67
|
+
|
|
68
|
+
- **Risks / unknowns** that would inflate the number — undocumented behavior, a flaky area, a third-party API you haven't read the docs for, a refactor that might cascade. For each, note roughly how much it could add.
|
|
69
|
+
- **Dependencies / sequencing** — what must land first, what's blocked on another team, what can run in parallel.
|
|
70
|
+
- **Assumptions** — every "I'm assuming X" you relied on to size it (env exists, no data migration, the happy path only). If an assumption is wrong, the estimate changes — that's the whole point of writing them down.
|
|
71
|
+
|
|
72
|
+
## Report
|
|
73
|
+
|
|
74
|
+
Deliver the estimate as your message — it is the whole deliverable.
|
|
75
|
+
|
|
76
|
+
```markdown
|
|
77
|
+
## Effort estimate — <task>
|
|
78
|
+
|
|
79
|
+
**Scope:** <one sentence>
|
|
80
|
+
**Out of scope:** <the boundary>
|
|
81
|
+
|
|
82
|
+
| # | Subtask | Size | Range |
|
|
83
|
+
| - | ------- | ---- | ----- |
|
|
84
|
+
| 1 | Define provider config + types | S | < 0.5d |
|
|
85
|
+
| 2 | Add SAML assertion parser | L | 2–4d |
|
|
86
|
+
| 3 | Wire into the auth middleware | M | 1–2d |
|
|
87
|
+
| 4 | Characterization tests for the login path | M | 1–2d |
|
|
88
|
+
|
|
89
|
+
**Total range:** ~4.5–8.5 days (incl. review + integration)
|
|
90
|
+
|
|
91
|
+
**Top risks:** <each with how much it could add — e.g. "no tests on auth path: +1–2d if parsing breaks something">
|
|
92
|
+
**Dependencies / sequencing:** <what blocks what>
|
|
93
|
+
**Assumptions:** <the list — if any is wrong, the estimate moves>
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
End with the **recommended first slice**: the smallest subtask that retires the biggest unknown (usually a spike or the riskiest interface). Shipping it first tightens the whole range — call out which assumptions it will confirm or kill. Remind the reader the total is a range under these assumptions, not a date.
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: "Scan code read-only for N+1 query patterns — loops that query per iteration and handlers that fan out per-row — and report each with a location, why it is N+1, and the concrete eager-load/batch/set-based fix."
|
|
3
|
+
argument-hint: "<path or area to scan (optional)>"
|
|
4
|
+
allowed-tools: "Read, Grep, Glob"
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
## Scope
|
|
8
|
+
|
|
9
|
+
Treat `$ARGUMENTS` as the path or area to scan — a directory, a file, a feature ("the orders list endpoint"), or a layer ("the serializers"). Restate the scope in one sentence before scanning.
|
|
10
|
+
|
|
11
|
+
If `$ARGUMENTS` is empty, scan the data-access surface of the repo: ORM models/repositories, serializers/resolvers, and request handlers. Say which paths you chose so the user can narrow it.
|
|
12
|
+
|
|
13
|
+
> [!WARNING]
|
|
14
|
+
> Read-only mode. Do not edit, run migrations, or execute queries. Your only output is the prioritized findings report. The `Bash`/`Edit` tools are deliberately not granted — if a fix needs verifying, tell the user the exact command to run, don't run it.
|
|
15
|
+
|
|
16
|
+
## Step 1 — Identify the data-access vocabulary
|
|
17
|
+
|
|
18
|
+
Before grepping for loops, learn how *this* codebase talks to the database, because the lazy-load that triggers the extra query is often invisible. Grep for the ORM/query primitives in use:
|
|
19
|
+
|
|
20
|
+
- **ActiveRecord (Rails):** `.where`, `.find`, `.find_by`, association accessors inside `.each`/`.map`; missing `includes`/`preload`/`eager_load`.
|
|
21
|
+
- **Django:** `.objects.`, `.filter`, related-field access in a loop without `select_related`/`prefetch_related`.
|
|
22
|
+
- **SQLAlchemy:** `session.query`/`select`, relationship access with default `lazy="select"`; missing `selectinload`/`joinedload`.
|
|
23
|
+
- **Prisma/TypeORM/Sequelize:** `findMany`/`findOne`/`findByPk` inside `map`/`for`; missing `include`/`relations`/`eager`.
|
|
24
|
+
- **Raw SQL / micro-ORMs:** a `SELECT … WHERE id = ?` helper called inside a loop.
|
|
25
|
+
|
|
26
|
+
Note which one is in play; the recommended fix differs per ORM.
|
|
27
|
+
|
|
28
|
+
## Step 2 — Find queries issued per iteration
|
|
29
|
+
|
|
30
|
+
Grep for loop constructs (`for`, `forEach`, `.map`, `.each`, list/dict comprehensions, `Promise.all([...].map(...))`) and inspect the body of each for a data-access call from Step 1. Flag any case where the query depends on the loop variable (`fetch(item.id)`, `item.author.name`) — that's the per-row query.
|
|
31
|
+
|
|
32
|
+
> [!NOTE]
|
|
33
|
+
> The most dangerous N+1s are the invisible ones: a property access like `order.customer.email` that *looks* free but silently fires a lazy SELECT each time. Don't only grep for `.query()` — flag relationship/foreign-key attribute access inside any loop.
|
|
34
|
+
|
|
35
|
+
## Step 3 — Find handlers that fan out per row
|
|
36
|
+
|
|
37
|
+
Trace request handlers / GraphQL resolvers / serializers that return a collection. A field resolver or a serializer method that loads a related record runs once **per item in the response** even when no explicit loop is visible in the handler. Flag list endpoints whose per-item shape includes a related lookup, and GraphQL resolvers without a batching layer.
|
|
38
|
+
|
|
39
|
+
## Step 4 — Rank by blast radius
|
|
40
|
+
|
|
41
|
+
Order findings worst-first. Severity is roughly *(how large N gets) x (how hot the path is)*:
|
|
42
|
+
|
|
43
|
+
- **Critical:** unbounded/paginated collections on a hot path (list endpoints, dashboards, exports) — N grows with data.
|
|
44
|
+
- **High:** loops over user-controlled or large fixed sets.
|
|
45
|
+
- **Low:** loops over a small bounded set (a 3-item enum) — note it, but don't alarm.
|
|
46
|
+
|
|
47
|
+
## Step 5 — For each finding, prescribe the concrete fix
|
|
48
|
+
|
|
49
|
+
Per finding, give: the **file:line**, a one-line **why it's N+1**, and the **fix matched to the ORM** — not a generic "optimize this":
|
|
50
|
+
|
|
51
|
+
- **Eager load / preload** when you need the related rows: `includes`/`preload` (Rails), `select_related` (1:1/FK) or `prefetch_related` (1:many) (Django), `selectinload`/`joinedload` (SQLAlchemy), `include`/`relations` (Prisma/TypeORM).
|
|
52
|
+
- **Single set-based query** when you only need an aggregate or a filtered subset: replace the loop with one `WHERE id IN (...)` / `GROUP BY` / `JOIN` instead of looping.
|
|
53
|
+
- **Batch / DataLoader** for GraphQL resolvers or service boundaries where you can't restructure the caller — collect the keys, resolve them in one batched call per tick.
|
|
54
|
+
- **Map in memory** when the related set is small and reused: load once, index by key, look up in the loop.
|
|
55
|
+
|
|
56
|
+
Include a compact **before/after sketch** (4-8 lines) so the fix is unambiguous.
|
|
57
|
+
|
|
58
|
+
## Step 6 — Tell them how to confirm
|
|
59
|
+
|
|
60
|
+
Close each finding with the verification step the user runs themselves (read-only command guidance only):
|
|
61
|
+
|
|
62
|
+
- Enable query logging for the path (Rails: watch the dev log / `ActiveRecord::Base.logger`; Django: `django.db` logger or `django-debug-toolbar`; SQLAlchemy: `echo=True`; Prisma: `log: ['query']`) and confirm the count drops from ~N to 1-2.
|
|
63
|
+
- Or `EXPLAIN`/`EXPLAIN ANALYZE` the new set-based query to confirm it's a single plan, not a loop.
|
|
64
|
+
|
|
65
|
+
## Report
|
|
66
|
+
|
|
67
|
+
Deliver a prioritized findings list (worst offenders first) as your message — it is the whole deliverable. For each: **severity · file:line · why it's N+1 · the fix · before/after sketch · how to confirm**. If you found nothing, say so plainly and name the paths you scanned. End with the single highest-leverage finding to fix first.
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: "Reproduce a flaky test, find the real source of nondeterminism, and fix the cause."
|
|
3
|
+
argument-hint: "<suspected test or area (optional)>"
|
|
4
|
+
allowed-tools: "Bash, Read, Edit"
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
Find why a test passes sometimes and fails other times, then remove the **nondeterminism** — don't make it fail less often. The whole job is turning an intermittent failure into either a reliable pass or a reliable fail you can debug. Follow the steps in order; the judgment call is Step 3, classifying *which* source of flakiness you're looking at.
|
|
8
|
+
|
|
9
|
+
## Scope
|
|
10
|
+
|
|
11
|
+
`$ARGUMENTS` optionally names the suspect: a test file, a single test name/pattern, or just an area ("the checkout integration tests"). Use it to scope the reproduction loop so each run stays fast and one failure is readable.
|
|
12
|
+
|
|
13
|
+
If `$ARGUMENTS` is empty, find the suspect first. Look for the usual tells: tests skipped/quarantined in config, `retry`/`flaky`/`@flaky` annotations, CI history if available, and tests that touch time, randomness, ordering, the network, or the filesystem. Pick the most-cited or most-recently-failed one and confirm with the user if several are equally likely.
|
|
14
|
+
|
|
15
|
+
> [!WARNING]
|
|
16
|
+
> Reproduce flakiness *before* you change anything. A single green run proves nothing — flaky tests pass most of the time by definition. If you can't make it fail, you can't prove you fixed it.
|
|
17
|
+
|
|
18
|
+
## Step 1 — Reproduce the intermittency
|
|
19
|
+
|
|
20
|
+
Detect the runner from the manifest, then **loop the suspect** until you observe both a pass and a failure. One run is worthless here; volume is the tool.
|
|
21
|
+
|
|
22
|
+
```bash
|
|
23
|
+
# Loop a single test N times, stop on first failure (shell, runner-agnostic)
|
|
24
|
+
for i in $(seq 1 50); do
|
|
25
|
+
npx vitest run -t "applies the discount" || { echo "FAILED on run $i"; break; }
|
|
26
|
+
done
|
|
27
|
+
|
|
28
|
+
# Native repeat flags where they exist
|
|
29
|
+
npx jest path/to/file.test.ts --runInBand # jest: run serially to expose order coupling
|
|
30
|
+
pytest tests/test_cart.py -k discount --count=50 # pytest-repeat
|
|
31
|
+
go test ./cart -run TestDiscount -count=50 # go: -count disables the test cache
|
|
32
|
+
cargo test discount -- --test-threads=1 # rust: serialize to surface shared state
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
Then attack the two biggest flake sources directly — **order and seed** — because a test that's stable in isolation but flaky in the suite is order-dependent:
|
|
36
|
+
|
|
37
|
+
```bash
|
|
38
|
+
npx vitest run --sequence.shuffle # randomize test order
|
|
39
|
+
npx jest --shuffle
|
|
40
|
+
pytest -p randomly # pytest-randomly randomizes order + seed
|
|
41
|
+
go test ./... -shuffle=on
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
Record the failure rate (e.g. "3/50 with shuffle on, 0/50 in isolation"). That ratio is your signal that the fix worked later.
|
|
45
|
+
|
|
46
|
+
> [!NOTE]
|
|
47
|
+
> If it fails only with randomized order but never alone, the bug is almost certainly **shared mutable state** leaking between tests — jump straight to that category in Step 3. If it fails in isolation too, the test owns its own nondeterminism (time, RNG, async).
|
|
48
|
+
|
|
49
|
+
## Step 2 — Capture the failing run's details
|
|
50
|
+
|
|
51
|
+
When you catch a red run, read the *whole* failure, not the summary line — and note what differs from the green runs.
|
|
52
|
+
|
|
53
|
+
- The assertion and its **expected vs. actual**, plus how actual varies between failures (off by milliseconds? different element order? a `null` that's sometimes set?).
|
|
54
|
+
- The first project frame in the stack — not framework internals.
|
|
55
|
+
- Whether the failure correlates with order (which test ran before it), wall-clock time (midnight, month boundary, DST), or machine load (only fails under `--test-threads`/parallel CI).
|
|
56
|
+
|
|
57
|
+
If the failure is rare, increase the loop count and add diagnostics inside the test temporarily (log the value, the timestamp, the seed) rather than guessing.
|
|
58
|
+
|
|
59
|
+
## Step 3 — Classify the source of nondeterminism
|
|
60
|
+
|
|
61
|
+
This is the decision that determines the fix. **State the category explicitly** with the evidence from Steps 1–2. Flakiness almost always falls into one of these:
|
|
62
|
+
|
|
63
|
+
- **Test-order / shared mutable state** — a module-level variable, singleton, cache, DB row, env var, or temp file mutated by one test and read by another. *Tell:* fails only with shuffle on, or only after a specific sibling.
|
|
64
|
+
- **Real time / `Date`** — assertions on `Date.now()`, `new Date()`, durations, `setTimeout`, or "expires in 1h" math that crosses a tick/second/day boundary. *Tell:* fails near boundaries, or expected/actual differ by a small time delta.
|
|
65
|
+
- **Unseeded randomness** — `Math.random()`, `uuid()`, `faker` without a fixed seed, `Set`/`Map` insertion order, hash ordering. *Tell:* actual value changes every run with no other input change.
|
|
66
|
+
- **Async races / missing awaits** — an un-awaited promise, a fire-and-forget effect, polling without synchronization, assertions running before state settles. *Tell:* fails under load/parallelism, passes when serialized or with an added (bad) sleep.
|
|
67
|
+
- **Network / external deps** — real HTTP, a live DB, a clock-skewed API, DNS. *Tell:* fails on timeout, on CI but not locally, or when offline.
|
|
68
|
+
- **Resource leaks** — unclosed servers/sockets/file handles, leaked timers, growing in-memory state across tests, port collisions. *Tell:* fails late in the run, or only after the Nth iteration.
|
|
69
|
+
- **Locale / timezone** — date formatting, number/currency parsing, string collation that assumes the dev's `LANG`/`TZ`. *Tell:* fails in CI with a different `TZ`/`LC_ALL`. Reproduce with `TZ=UTC LC_ALL=C npx vitest run ...`.
|
|
70
|
+
|
|
71
|
+
If two categories are plausible, fix the one your repro evidence most directly supports, then re-loop before assuming you're done.
|
|
72
|
+
|
|
73
|
+
## Step 4 — Fix the cause, not the symptom
|
|
74
|
+
|
|
75
|
+
Apply the smallest change that removes the nondeterminism. Match the fix to the category:
|
|
76
|
+
|
|
77
|
+
- **Shared state:** isolate it — reset the singleton/cache in `beforeEach`/`afterEach`, use a fresh fixture/transaction per test, stop relying on cross-test ordering. Make each test set up everything it needs.
|
|
78
|
+
- **Real time:** inject the clock. Use fake timers (`vi.useFakeTimers()` / `jest.useFakeTimers()`, `freezegun` in Python) or pass a `now()` function the test controls. Never assert against the live clock.
|
|
79
|
+
- **Unseeded RNG:** seed it or inject the random source (`faker.seed(1)`, a stubbed `Math.random`, a fixed UUID factory). For collection ordering, sort before asserting or assert set-membership, not index.
|
|
80
|
+
- **Async races:** `await` the actual promise; wait on the real condition (`waitFor`/`findBy`, a returned promise, an event) instead of a timer. Remove un-awaited side effects.
|
|
81
|
+
- **Network / external:** mock at the boundary (`msw`, `nock`, `responses`, a fake) so the test is hermetic. If it's a genuine integration test, gate it behind a tag and keep it out of the unit loop.
|
|
82
|
+
- **Leaks:** close what you open in teardown — servers, handles, timers; bind to an ephemeral port (`:0`) instead of a fixed one.
|
|
83
|
+
- **Locale/TZ:** pin `TZ` and locale in the test setup, or pass an explicit locale to the formatter under test.
|
|
84
|
+
|
|
85
|
+
> [!WARNING]
|
|
86
|
+
> Do **not** "fix" flakiness by wrapping the test in a retry loop, adding `sleep`/`setTimeout` to "give it time", widening a time tolerance, or quarantining/`.skip`-ing it. Those hide nondeterminism — the test still fails for real and the underlying race ships to production. A `sleep` that makes it pass is proof you found an async race; replace the sleep with a real await on the condition.
|
|
87
|
+
|
|
88
|
+
## Step 5 — Prove it's stable
|
|
89
|
+
|
|
90
|
+
Re-run the **same loop and randomization** that exposed the flake. The bar is no failures across a high iteration count with order and seed randomized:
|
|
91
|
+
|
|
92
|
+
```bash
|
|
93
|
+
for i in $(seq 1 100); do
|
|
94
|
+
npx vitest run -t "applies the discount" --sequence.shuffle || { echo "STILL FLAKY on run $i"; break; }
|
|
95
|
+
done
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
Then run the full suite (also shuffled) to confirm your isolation/teardown change didn't break a test that was silently relying on the old shared state. Reproduce the original failing condition specifically (e.g. `TZ=UTC`, parallel threads) if that's what triggered it.
|
|
99
|
+
|
|
100
|
+
> [!NOTE]
|
|
101
|
+
> If it now passes 100/100 in the loop but CI still flakes, the source is environmental (parallelism level, CI `TZ`, slower disk) — reproduce under those exact conditions locally before declaring victory.
|
|
102
|
+
|
|
103
|
+
## Report
|
|
104
|
+
|
|
105
|
+
Summarize for the user, concisely:
|
|
106
|
+
|
|
107
|
+
- **Category:** which source of nondeterminism it was, and the one-line evidence (e.g. "shared module cache — failed 4/50 only with shuffle on").
|
|
108
|
+
- **Offending code:** the file and the exact line/pattern that was nondeterministic.
|
|
109
|
+
- **Fix:** what you changed and why it removes the cause (not masks it).
|
|
110
|
+
- **Proof:** the before/after loop result (e.g. "was 4/50 failing → now 100/100 green with shuffle + `TZ=UTC`").
|
|
111
|
+
|
|
112
|
+
Do not commit or push — leave the change staged for the user to review unless they explicitly ask you to commit.
|