agentscamp 0.2.1 → 0.4.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.
- package/README.md +4 -4
- package/content/agents/ci-cd-engineer.md +95 -0
- package/content/agents/cli-tooling-engineer.md +47 -0
- package/content/agents/context-engineer.md +68 -0
- package/content/agents/csharp-pro.md +73 -0
- package/content/agents/database-architect.md +90 -0
- package/content/agents/eval-driven-developer.md +47 -0
- package/content/agents/incident-responder.md +77 -0
- package/content/agents/java-pro.md +73 -0
- package/content/agents/qa-automation-engineer.md +92 -0
- package/content/commands/add-caching.md +79 -0
- package/content/commands/audit-accessibility.md +101 -0
- package/content/commands/clean-branches.md +113 -0
- package/content/commands/generate-e2e-test.md +98 -0
- package/content/commands/review-tests.md +98 -0
- package/content/commands/scaffold-dockerfile.md +111 -0
- package/content/commands/scaffold-github-action.md +94 -0
- package/content/commands/seed-data.md +63 -0
- package/content/commands/setup-precommit-hooks.md +72 -0
- package/content/commands/write-design-doc.md +78 -0
- package/content/manifest.json +436 -4
- package/content/skills/architecture-diagram-generator.md +78 -0
- package/content/skills/connection-pool-tuner.md +46 -0
- package/content/skills/dependency-upgrade-planner.md +42 -0
- package/content/skills/github-actions-optimizer.md +45 -0
- package/content/skills/load-test-designer.md +87 -0
- package/content/skills/memory-leak-hunter.md +35 -0
- package/content/skills/pagination-designer.md +51 -0
- package/content/skills/property-test-designer.md +63 -0
- package/content/skills/security-headers-hardener.md +79 -0
- package/content/skills/slo-definer.md +38 -0
- package/content/skills/structured-logging-designer.md +42 -0
- package/package.json +1 -1
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: "Review the quality of a test suite, not just whether it passes — find weak assertions, missing edge cases, and tests coupled to implementation."
|
|
3
|
+
argument-hint: "<test file or area to review>"
|
|
4
|
+
allowed-tools: "Read, Grep, Glob"
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
Review the **quality** of the tests in `$ARGUMENTS`, not whether they pass. A green suite tells you the tests ran; it does not tell you they verify the right behavior, cover the failure paths, or survive a refactor. Your job is to find the gap between "passes" and "actually protects the contract," then report it. Follow the steps in order — the judgment is in Steps 3–4.
|
|
8
|
+
|
|
9
|
+
## Scope
|
|
10
|
+
|
|
11
|
+
`$ARGUMENTS` names what to review: a test file, a directory of tests, or an area ("the auth tests", "the cart reducer specs"). Use it to bound which test files you read and which production code you trace them against.
|
|
12
|
+
|
|
13
|
+
If `$ARGUMENTS` is empty, ask one focused question: *which test file or area should I review?* Do not review the whole suite by default — a vague review produces vague findings.
|
|
14
|
+
|
|
15
|
+
> [!WARNING]
|
|
16
|
+
> Read-only. Use only Read, Grep, and Glob. Do not edit tests, run the suite, or change coverage config. You are diagnosing quality and reporting it, not fixing it.
|
|
17
|
+
|
|
18
|
+
## Step 1 — Read the tests and the code under test
|
|
19
|
+
|
|
20
|
+
Find the test files in scope, then read each one **alongside the production code it exercises**. You cannot judge whether an assertion is right without knowing the contract it's supposed to enforce.
|
|
21
|
+
|
|
22
|
+
```bash
|
|
23
|
+
# Find the tests in scope
|
|
24
|
+
# (Glob) **/*.{test,spec}.{ts,tsx,js,jsx} or **/test_*.py or **/*_test.go
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
For each test, identify: what unit/behavior it claims to cover, what it asserts, what it mocks or stubs, and what setup/teardown it relies on. Then open the corresponding source so you know the real branches, error paths, and edge inputs that *should* be tested.
|
|
28
|
+
|
|
29
|
+
> [!NOTE]
|
|
30
|
+
> Map tests to behaviors, not to files. A `cart.test.ts` with twelve tests can still leave the "apply expired coupon" branch completely unverified. Coverage of files is not coverage of behavior.
|
|
31
|
+
|
|
32
|
+
## Step 2 — Inventory what's actually asserted
|
|
33
|
+
|
|
34
|
+
Before judging, list the concrete claims. For each test, write down the assertion(s) and the branch of production code they pin down. This surfaces two failure modes immediately: tests that assert almost nothing, and large swaths of source with no test pointing at them.
|
|
35
|
+
|
|
36
|
+
Use Grep to find the tells fast across the scope:
|
|
37
|
+
|
|
38
|
+
```bash
|
|
39
|
+
# Weak / non-assertions: a test that only checks it didn't throw
|
|
40
|
+
grep -rnE 'expect\([^)]*\)\.(toBeDefined|toBeTruthy|not\.toThrow)\(\)|assert\s+\w+\s*$' <scope>
|
|
41
|
+
|
|
42
|
+
# Snapshot tests (often assert "it looks like last time", not correct behavior)
|
|
43
|
+
grep -rnE 'toMatchSnapshot|toMatchInlineSnapshot' <scope>
|
|
44
|
+
|
|
45
|
+
# Mock-heavy tests (count mocks per file — high counts hint the test verifies wiring, not behavior)
|
|
46
|
+
grep -rcE 'jest\.mock|vi\.mock|mock\.|MagicMock|patch\(|nock\(|when\(' <scope>
|
|
47
|
+
|
|
48
|
+
# Determinism hazards inside tests
|
|
49
|
+
grep -rnE 'Date\.now|new Date\(\)|Math\.random|setTimeout|sleep\(|fetch\(|axios|http' <scope>
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
## Step 3 — Judge each weakness category
|
|
53
|
+
|
|
54
|
+
This is the core of the review. For every test file, decide which of these it suffers from, and back the call with the specific line. State the category explicitly per finding.
|
|
55
|
+
|
|
56
|
+
- **Change-detector tests (coupled to implementation).** The test asserts on private internals, call order of mocked collaborators, exact prop trees, or full snapshots — so any refactor that preserves behavior turns it red. *Tell:* the test would break if you renamed a private method or reordered two internal calls without changing output. These punish refactoring and train the team to regenerate snapshots blindly.
|
|
57
|
+
- **Happy-path only.** The test covers the success case and skips the failure paths the code clearly handles — invalid input, empty/null, boundary values, the `catch` block, the early-return guard, concurrent access. *Tell:* the production function has 4 branches; the tests exercise 1.
|
|
58
|
+
- **Weak assertions.** The test runs the code but asserts something trivially true: `toBeDefined`, `toBeTruthy`, "didn't throw", `status === 200` without checking the body, or asserting a mock was called without checking the *arguments* or the *effect*. *Tell:* you could break the real logic and the test stays green.
|
|
59
|
+
- **Non-deterministic / non-isolated.** The test depends on real wall-clock time, unseeded randomness, network or a live DB, the filesystem, or on another test having run first (shared module state, ordering). *Tell:* it would flake under shuffle, in a different timezone, or offline. (Hand these to `/flaky-test-hunt`.)
|
|
60
|
+
- **Over-mocking.** So much is mocked that the test exercises only the mocks — e.g. mocking the function under test, or stubbing every collaborator so the only thing verified is that you wired the stubs together. *Tell:* the assertions check mock call counts, and the real code could be deleted without failing the test.
|
|
61
|
+
- **Coverage theatre.** Lines are executed (driving the coverage number up) but no meaningful assertion checks the result, or branch/edge coverage is missing under a high line-coverage headline. *Tell:* a test that calls a function inside a loop "for coverage" with no assertion on the outputs.
|
|
62
|
+
|
|
63
|
+
> [!WARNING]
|
|
64
|
+
> High line-coverage with weak assertions is **false confidence** — it reports that lines ran, not that behavior is correct. Call this out explicitly when you see it. A suite at 90% line coverage whose assertions are mostly `toBeTruthy` protects almost nothing.
|
|
65
|
+
|
|
66
|
+
> [!NOTE]
|
|
67
|
+
> Distinguish *coupled to implementation* from *testing behavior*. A good test pins the observable contract (inputs → outputs/side effects) and survives any internal rewrite that keeps that contract. If renaming a private helper would break the test, it's testing the implementation, not the behavior.
|
|
68
|
+
|
|
69
|
+
## Step 4 — Find the highest-value missing tests
|
|
70
|
+
|
|
71
|
+
The most valuable output is often a test that doesn't exist. From the production code you read in Step 1, list the behaviors and branches with **no** meaningful assertion, then rank them by blast radius: error/failure paths, security-relevant branches, boundary values, and concurrency before cosmetic gaps. Name the specific input and the expected result for each, so the suggestion is directly actionable.
|
|
72
|
+
|
|
73
|
+
## Report
|
|
74
|
+
|
|
75
|
+
Deliver as your message, ordered by severity. For every finding cite `file:line`, name the category, explain *why* it's weak, and give the concrete fix:
|
|
76
|
+
|
|
77
|
+
```markdown
|
|
78
|
+
## Test review — <scope>
|
|
79
|
+
|
|
80
|
+
**Overall:** <2-3 sentences: does this suite verify behavior or just execute code? Biggest risk?>
|
|
81
|
+
|
|
82
|
+
### High — weak or misleading tests
|
|
83
|
+
- `cart.test.ts:42` — [Weak assertion] only asserts `toBeDefined()`; the discount math is never checked. Assert the exact total for a known cart. Breaking the formula currently keeps this green.
|
|
84
|
+
- `auth.test.ts:88` — [Over-mocking] mocks `verifyToken`, the function under test; the test proves only that the mock returns its stub. Test the real verifier against a known-good and a tampered token.
|
|
85
|
+
|
|
86
|
+
### Medium — coupled / non-deterministic
|
|
87
|
+
- `render.test.tsx:15` — [Change-detector] full `toMatchSnapshot`; any markup refactor breaks it. Assert the visible text/role instead.
|
|
88
|
+
- `expiry.test.ts:30` — [Non-deterministic] asserts against `Date.now()`; flakes near boundaries. Inject a fixed clock.
|
|
89
|
+
|
|
90
|
+
### Missing tests (highest value first)
|
|
91
|
+
- `refund()` error path — refund exceeding the original charge is never tested. Expect it to reject with `AmountExceedsCharge`.
|
|
92
|
+
- `parseRange()` boundary — empty and single-element inputs untested.
|
|
93
|
+
|
|
94
|
+
### Coverage note
|
|
95
|
+
<If line coverage looks high but branches/assertions are thin, say so plainly and name the false-confidence risk.>
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
End with the single highest-value change: the one missing test or one weak assertion that, fixed first, removes the most risk.
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: "Scaffold a production-grade multi-stage Dockerfile and .dockerignore for the current project."
|
|
3
|
+
argument-hint: "<optional: stack/runtime hint>"
|
|
4
|
+
allowed-tools: "Read, Write, Glob, Grep"
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
Scaffold a production Dockerfile and `.dockerignore` for this repository. Treat `$ARGUMENTS` as an optional stack/runtime hint (e.g. `node 22`, `go`, `python 3.12 fastapi`, `bun`). If `$ARGUMENTS` is empty, detect the stack from the repo's manifests — never ask the user a question you can answer by reading a file.
|
|
8
|
+
|
|
9
|
+
## Scope
|
|
10
|
+
|
|
11
|
+
Produce exactly two files at the repo root: `Dockerfile` and `.dockerignore`. The Dockerfile must be **multi-stage** (a builder stage that installs build/dev dependencies, a final stage that copies only runtime artifacts), run as a **non-root user**, pin a **specific minimal base image**, and order layers so dependency installs cache across source-only changes.
|
|
12
|
+
|
|
13
|
+
> [!WARNING]
|
|
14
|
+
> If a `Dockerfile` already exists, do not silently overwrite it. Read it, and either propose targeted improvements in your report or write the new one to `Dockerfile.new` and say so. Never clobber working infra.
|
|
15
|
+
|
|
16
|
+
## Step 1 — Detect the stack
|
|
17
|
+
|
|
18
|
+
Use the `$ARGUMENTS` hint if given, then confirm it against the repo. With no hint, identify the stack from manifests with `Glob`/`Read`:
|
|
19
|
+
|
|
20
|
+
- **Node/Bun/Deno** — `package.json` (read `engines.node`, `packageManager`, and `scripts.build`/`scripts.start`), `bun.lockb`, `deno.json`. The lockfile (`package-lock.json` / `pnpm-lock.yaml` / `yarn.lock` / `bun.lockb`) decides the package manager and the deterministic install command.
|
|
21
|
+
- **Go** — `go.mod` (read the `go` directive for the version); produces a static binary, so the final stage can be `distroless/static` or `scratch`.
|
|
22
|
+
- **Python** — `requirements.txt`, `pyproject.toml` (+ `poetry.lock`/`uv.lock`), `Pipfile`. Note the entrypoint (`uvicorn`, `gunicorn`, `python app.py`).
|
|
23
|
+
- **Rust** — `Cargo.toml`; final stage can be `distroless/cc` or `debian:*-slim`.
|
|
24
|
+
- **JVM** — `pom.xml` / `build.gradle`; build a jar in the builder, run on a JRE-only base.
|
|
25
|
+
|
|
26
|
+
Record: the **language + version**, the **package manager + lockfile**, the **build command**, the **start command**, and the **listening port** (grep source/config for `listen`, `PORT`, `EXPOSE`, framework defaults).
|
|
27
|
+
|
|
28
|
+
> [!NOTE]
|
|
29
|
+
> Pin the base image to a specific minor + digest-able tag (e.g. `node:22.12-slim`, `python:3.12-slim`, `golang:1.23-alpine`). Match the major/minor to the version declared in the manifest — do not invent a version the project does not use.
|
|
30
|
+
|
|
31
|
+
## Step 2 — Write the multi-stage Dockerfile
|
|
32
|
+
|
|
33
|
+
Builder stage installs dependencies first (copy only manifests + lockfile), then copies source and builds. The final stage starts from a clean minimal base and copies only what runtime needs. The snippet below is illustrative for Node — adapt the base, install, build, and CMD to the stack found in Step 1.
|
|
34
|
+
|
|
35
|
+
```dockerfile
|
|
36
|
+
# syntax=docker/dockerfile:1
|
|
37
|
+
|
|
38
|
+
# --- builder ---
|
|
39
|
+
FROM node:22.12-slim AS builder
|
|
40
|
+
WORKDIR /app
|
|
41
|
+
# Copy manifests first so deps cache survives source-only changes
|
|
42
|
+
COPY package.json package-lock.json ./
|
|
43
|
+
RUN --mount=type=cache,target=/root/.npm npm ci
|
|
44
|
+
COPY . .
|
|
45
|
+
RUN npm run build && npm prune --omit=dev
|
|
46
|
+
|
|
47
|
+
# --- runtime ---
|
|
48
|
+
FROM node:22.12-slim AS runtime
|
|
49
|
+
ENV NODE_ENV=production
|
|
50
|
+
WORKDIR /app
|
|
51
|
+
# Run as the unprivileged user the base image already ships
|
|
52
|
+
USER node
|
|
53
|
+
COPY --chown=node:node --from=builder /app/node_modules ./node_modules
|
|
54
|
+
COPY --chown=node:node --from=builder /app/dist ./dist
|
|
55
|
+
COPY --chown=node:node --from=builder /app/package.json ./
|
|
56
|
+
EXPOSE 3000
|
|
57
|
+
HEALTHCHECK --interval=30s --timeout=3s --start-period=10s --retries=3 \
|
|
58
|
+
CMD node -e "fetch('http://localhost:3000/health').then(r=>process.exit(r.ok?0:1)).catch(()=>process.exit(1))"
|
|
59
|
+
CMD ["node", "dist/server.js"]
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
Rules for whatever stack you target:
|
|
63
|
+
|
|
64
|
+
- **Copy manifests + lockfile before source**, install, then `COPY` the rest. This is the single most important line-ordering decision for cache reuse.
|
|
65
|
+
- Use the **deterministic install** for the detected package manager (`npm ci`, `pnpm install --frozen-lockfile`, `pip install --no-cache-dir -r requirements.txt`, `go mod download`).
|
|
66
|
+
- **Final stage carries artifacts only** — built binary/`dist`/wheel + runtime deps, never the compiler, dev dependencies, or source tree. For Go/Rust static binaries, copy the single binary into `distroless`/`scratch`.
|
|
67
|
+
- **Non-root**: use the base image's built-in unprivileged user (`USER node`, distroless `nonroot`) or create one (`RUN adduser -D app && USER app`). `COPY --chown` so the runtime user owns its files.
|
|
68
|
+
- **`HEALTHCHECK`** only when the container exposes a port and has (or can have) a health endpoint. For a one-shot/CLI image, omit it rather than faking one.
|
|
69
|
+
- **`EXPOSE`** the detected port and use the **exec-form `CMD`** (`["node","dist/server.js"]`) so signals reach PID 1.
|
|
70
|
+
|
|
71
|
+
> [!WARNING]
|
|
72
|
+
> Never bake secrets into the image. Do not `COPY .env`, and do not pass tokens via `ARG`/`ENV` — build args land in the image history and `docker history` will expose them. For private registry installs, use `RUN --mount=type=secret` so the credential never persists in a layer.
|
|
73
|
+
|
|
74
|
+
## Step 3 — Write the .dockerignore
|
|
75
|
+
|
|
76
|
+
Write `.dockerignore` before relying on `COPY . .` — without it the whole working tree (including `.git` and local secrets) ships into the build context and into layers.
|
|
77
|
+
|
|
78
|
+
```
|
|
79
|
+
.git
|
|
80
|
+
.gitignore
|
|
81
|
+
node_modules
|
|
82
|
+
dist
|
|
83
|
+
build
|
|
84
|
+
.next
|
|
85
|
+
target
|
|
86
|
+
__pycache__
|
|
87
|
+
*.pyc
|
|
88
|
+
.venv
|
|
89
|
+
.env
|
|
90
|
+
.env.*
|
|
91
|
+
*.log
|
|
92
|
+
.DS_Store
|
|
93
|
+
Dockerfile
|
|
94
|
+
.dockerignore
|
|
95
|
+
README.md
|
|
96
|
+
coverage
|
|
97
|
+
.cache
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
- Always exclude `.git`, `node_modules`/`target`/`.venv`, build output, `.env*`, and editor/OS cruft.
|
|
101
|
+
- Tailor it to the detected stack (Python: `__pycache__`, `*.pyc`; Go: vendored caches; JS: `.next`, `coverage`).
|
|
102
|
+
- Excluding heavy/irrelevant paths shrinks the build context, speeds uploads, and removes a whole class of accidental secret leaks.
|
|
103
|
+
|
|
104
|
+
## Step 4 — Report
|
|
105
|
+
|
|
106
|
+
Deliver the result as your message:
|
|
107
|
+
|
|
108
|
+
- **Files written** — `Dockerfile` and `.dockerignore` (or `Dockerfile.new` if you avoided overwriting), and the detected stack + version + package manager they were built for.
|
|
109
|
+
- **Key decisions** — base image and why (slim vs. distroless vs. alpine), the runtime user, the cache-ordering choice, and whether a `HEALTHCHECK` was included or skipped.
|
|
110
|
+
- **Build & run** — the exact commands, e.g. `docker build -t myapp .` then `docker run --rm -p 3000:3000 myapp`. Note any required secrets/env (`docker run -e ...` or `--secret`).
|
|
111
|
+
- **Follow-ups** — anything the user must supply (a `/health` endpoint for the healthcheck, the real start command if it was ambiguous) and a one-line check to confirm non-root: `docker run --rm myapp id`.
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: "Scaffold a hardened GitHub Actions workflow for a stated goal, wired to the project's real test/lint/build commands."
|
|
3
|
+
argument-hint: "<what the workflow should do — e.g. CI test on PR, lint, release/publish, nightly cron>"
|
|
4
|
+
allowed-tools: "Read, Write, Glob, Grep"
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
Scaffold a GitHub Actions workflow for this repository. Treat `$ARGUMENTS` as the goal of the workflow — what it should do and when it should run (e.g. `CI test on PR`, `lint + typecheck`, `publish to npm on tag`, `nightly dependency audit`). If `$ARGUMENTS` is empty, ask exactly one question: *"What should this workflow do, and on what event should it run (PR, push to main, tag, schedule)?"* — then proceed.
|
|
8
|
+
|
|
9
|
+
## Scope
|
|
10
|
+
|
|
11
|
+
Produce one file: `.github/workflows/<name>.yml`, where `<name>` is a short kebab-case slug derived from the goal (`ci`, `lint`, `release`, `nightly-audit`). The workflow must run the project's **real** commands, declare **least-privilege** `permissions`, **pin** every third-party action to a commit SHA, **cache** dependencies, and **cancel** superseded runs via `concurrency`. Reference all credentials through `secrets.*`.
|
|
12
|
+
|
|
13
|
+
> [!WARNING]
|
|
14
|
+
> If `.github/workflows/<name>.yml` already exists, do not overwrite it. Read it, then either propose targeted edits in your report or write the new file as `<name>.new.yml` and say so. Never clobber a workflow that may be gating merges or shipping releases.
|
|
15
|
+
|
|
16
|
+
## Step 1 — Map the goal to a trigger
|
|
17
|
+
|
|
18
|
+
Classify `$ARGUMENTS` into one of these and set `on:` accordingly — do not add triggers the goal does not call for:
|
|
19
|
+
|
|
20
|
+
- **CI / test / lint / typecheck** → `on: pull_request` (validate PRs) plus `push:` to the default branch only if post-merge runs are wanted. Gate jobs that touch credentials behind `pull_request`, not `pull_request_target`.
|
|
21
|
+
- **Release / publish** → `on: push: tags: ['v*']` or `on: release: types: [published]`. Publishing on every `main` push is almost never what you want — prefer a tag/release trigger.
|
|
22
|
+
- **Scheduled job** (audit, refresh, backup) → `on: schedule: - cron: '...'`. Cron runs in **UTC**; pick an off-peak minute (avoid `0 * * * *` — top-of-hour is heavily throttled and queued). Add `workflow_dispatch` so it can be run manually too.
|
|
23
|
+
|
|
24
|
+
Detect the repo's default branch by `Read`ing `.git/HEAD` or any existing workflow; default to `main` if unknown and note the assumption.
|
|
25
|
+
|
|
26
|
+
## Step 2 — Detect the stack and real commands
|
|
27
|
+
|
|
28
|
+
Never invent `npm test`. Find what the project actually runs with `Glob`/`Read`/`Grep`:
|
|
29
|
+
|
|
30
|
+
- **Node / Bun / Deno** — `package.json`: read `packageManager`, `engines.node`, and `scripts` (`test`, `lint`, `typecheck`, `build`). The lockfile picks the manager and the deterministic install + cache: `package-lock.json` → `npm ci`; `pnpm-lock.yaml` → `pnpm install --frozen-lockfile`; `yarn.lock` → `yarn install --immutable`; `bun.lockb` → `bun install --frozen-lockfile`.
|
|
31
|
+
- **Python** — `pyproject.toml` / `requirements.txt` / `uv.lock` / `poetry.lock`; commands like `pytest`, `ruff check`, `mypy`.
|
|
32
|
+
- **Go** — `go.mod`: `go test ./...`, `go vet ./...`, `go build ./...`; read the `go` directive for the version.
|
|
33
|
+
- **Rust** — `Cargo.toml`: `cargo test`, `cargo clippy -- -D warnings`, `cargo build --release`.
|
|
34
|
+
|
|
35
|
+
Record the **language + version**, **package manager + lockfile path**, and the **exact script names** that exist. If the goal asks for a step the project has no script for (e.g. no `lint`), say so in the report rather than fabricating one.
|
|
36
|
+
|
|
37
|
+
## Step 3 — Write the hardened workflow
|
|
38
|
+
|
|
39
|
+
Use the project's commands and the trigger from Step 1. The snippet below is illustrative for a Node CI workflow — adapt `setup-*`, the cache, and the run steps to the stack from Step 2.
|
|
40
|
+
|
|
41
|
+
```yaml
|
|
42
|
+
name: CI
|
|
43
|
+
on:
|
|
44
|
+
pull_request:
|
|
45
|
+
push:
|
|
46
|
+
branches: [main]
|
|
47
|
+
|
|
48
|
+
# Least privilege: read-only by default; add scopes per job only as needed.
|
|
49
|
+
permissions:
|
|
50
|
+
contents: read
|
|
51
|
+
|
|
52
|
+
# Cancel superseded runs for the same ref to save minutes.
|
|
53
|
+
concurrency:
|
|
54
|
+
group: ${{ github.workflow }}-${{ github.ref }}
|
|
55
|
+
cancel-in-progress: true
|
|
56
|
+
|
|
57
|
+
jobs:
|
|
58
|
+
test:
|
|
59
|
+
runs-on: ubuntu-latest
|
|
60
|
+
steps:
|
|
61
|
+
# Pin third-party actions to a full commit SHA, not a moving tag.
|
|
62
|
+
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
|
63
|
+
- uses: actions/setup-node@39370e3970a6d050c480ffad4ff0ed4d3fdee5af # v4.1.0
|
|
64
|
+
with:
|
|
65
|
+
node-version: 22
|
|
66
|
+
cache: npm # built-in dependency cache keyed on the lockfile
|
|
67
|
+
- run: npm ci
|
|
68
|
+
- run: npm run lint --if-present
|
|
69
|
+
- run: npm test
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
Rules for whatever stack and goal you target:
|
|
73
|
+
|
|
74
|
+
- **`permissions:` is least-privilege.** Set a top-level `permissions: contents: read` baseline, then grant the minimum each job needs: `pull-requests: write` to comment on PRs, `packages: write` to push images, `id-token: write` for OIDC publishing. Never use a blanket `permissions: write-all`.
|
|
75
|
+
- **Pin every third-party action to a 40-char commit SHA**, with a trailing `# vX.Y.Z` comment for readability. A moving tag like `@v4` lets a compromised or retagged release run arbitrary code with your token. First-party `actions/*` are still safer pinned.
|
|
76
|
+
- **Cache dependencies** — prefer the `cache:` option built into `setup-node`/`setup-go`/`setup-python` (keyed on the lockfile) over a hand-rolled `actions/cache` unless you need a custom path.
|
|
77
|
+
- **Reference secrets only as `${{ secrets.NAME }}`** — never paste a token literal, and never `echo` a secret. Pass them as `env:` on the single step that needs them, not workflow-wide.
|
|
78
|
+
- **Concurrency** — for CI, cancel superseded runs (`cancel-in-progress: true`). For a release/publish workflow, set `cancel-in-progress: false` so an in-flight publish is never killed mid-upload.
|
|
79
|
+
|
|
80
|
+
> [!WARNING]
|
|
81
|
+
> Do not use `pull_request_target` to "fix" a workflow that needs secrets on fork PRs. It runs with the base repo's write token **and** the fork's untrusted code/`with:` inputs in the same context — a classic token-exfiltration vector. If a fork PR genuinely needs a secret, split into a privileged `workflow_run` job that never checks out untrusted code.
|
|
82
|
+
|
|
83
|
+
> [!NOTE]
|
|
84
|
+
> For npm/PyPI publishing, prefer **OIDC trusted publishing** (`permissions: id-token: write`) over a long-lived `NPM_TOKEN`/`PYPI_TOKEN` secret — it removes the standing credential entirely. Fall back to a `secrets.*` token only if the registry does not support OIDC.
|
|
85
|
+
|
|
86
|
+
## Step 4 — Report
|
|
87
|
+
|
|
88
|
+
Deliver the result as your message:
|
|
89
|
+
|
|
90
|
+
- **File written** — `.github/workflows/<name>.yml` (or `<name>.new.yml` if you avoided overwriting), and the detected stack + package manager it targets.
|
|
91
|
+
- **Triggers** — the exact `on:` events and, for a schedule, the cron expression in plain English ("daily at 07:00 UTC").
|
|
92
|
+
- **Permissions** — the `GITHUB_TOKEN` scopes granted and why each is needed.
|
|
93
|
+
- **Secrets to configure** — every `secrets.*` referenced, where to add it (`Settings → Secrets and variables → Actions`, or an Environment for protected deploys), and whether OIDC could replace it.
|
|
94
|
+
- **Follow-ups** — any missing project script the goal assumed, and how to verify the pinned SHAs (e.g. `gh api repos/actions/checkout/git/refs/tags/v4.2.2` to confirm the SHA matches the tag) and re-pin them later with Dependabot's `package-ecosystem: github-actions`.
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: "Generate realistic, referentially-consistent seed data and a re-runnable seed script from your actual schema — types and constraints respected, plausible values, FK-dependency insert order, idempotent, never aimed at production."
|
|
3
|
+
argument-hint: "<optional: tables and row volume>"
|
|
4
|
+
allowed-tools: "Read, Grep, Glob, Write"
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
## Scope
|
|
8
|
+
|
|
9
|
+
Treat `$ARGUMENTS` as an optional list of tables/entities and row volumes (e.g. `users:50 orders:200`, or `seed the catalog`). If empty, seed every table the schema defines, defaulting to ~20 rows per top-level table and a plausible fan-out for dependents (e.g. 1–5 child rows per parent). Restate in one sentence which tables you'll seed and at what volume before writing anything.
|
|
10
|
+
|
|
11
|
+
Goal: a **re-runnable seed script** that fills a *development or test* database with data that looks real and satisfies every constraint — not a throwaway `INSERT` of `test1`/`test2` that violates a foreign key the moment someone joins.
|
|
12
|
+
|
|
13
|
+
> [!WARNING]
|
|
14
|
+
> Never point a seed script at a production database. The script must read its connection from a dev/test env var (e.g. `DATABASE_URL`) and should refuse to run if that URL looks like production (host contains `prod`, `rds.amazonaws.com` without a dev marker, etc.). State this guard in the script's header comment and in your report.
|
|
15
|
+
|
|
16
|
+
## Step 1 — Read the schema, don't guess it
|
|
17
|
+
|
|
18
|
+
Locate the source of truth for tables and columns and read it — do not invent fields:
|
|
19
|
+
|
|
20
|
+
- **Migrations**: `migrations/`, `db/migrate/`, `alembic/versions/`, `prisma/migrations/` — the latest applied state.
|
|
21
|
+
- **ORM models / schema files**: `schema.prisma`, Drizzle `schema.ts`, SQLAlchemy/Django models, ActiveRecord `schema.rb`, TypeORM entities.
|
|
22
|
+
- **Raw DDL**: `schema.sql`, `*.ddl`.
|
|
23
|
+
|
|
24
|
+
Use Glob/Grep to find them, then Read. Match the project's existing seed convention if one exists (`prisma/seed.ts`, `seeds/`, `db/seeds.rb`, a `factories/` dir) instead of inventing a new format.
|
|
25
|
+
|
|
26
|
+
## Step 2 — Extract types, constraints, and foreign keys
|
|
27
|
+
|
|
28
|
+
For each table you'll seed, record: column types, `NOT NULL`, `UNIQUE` (and composite uniques), `CHECK` constraints, enums, default values, and every **foreign key** (which column references which table's PK, and whether it's nullable). Build the FK dependency graph — you need it for insert order in Step 4.
|
|
29
|
+
|
|
30
|
+
## Step 3 — Generate plausible, constraint-satisfying values
|
|
31
|
+
|
|
32
|
+
Generate values that respect each column's type and constraints **and** look real:
|
|
33
|
+
|
|
34
|
+
- Names, emails, addresses, phone numbers, company names, dates — realistic and varied (`ava.chen@example.com`, not `user1@test.com`). Keep emails on a reserved domain like `example.com` so they can't reach real inboxes.
|
|
35
|
+
- Enums/`CHECK` columns: only emit allowed values, with a realistic distribution (most orders `completed`, a few `refunded`).
|
|
36
|
+
- `UNIQUE` columns: track generated values and guarantee no collisions (including composite uniques).
|
|
37
|
+
- Numbers, timestamps, statuses: plausible ranges and correlations (`shipped_at` after `created_at`; `total` matching summed line items if both exist).
|
|
38
|
+
- Prefer a deterministic generator (a fixed seed for the faker library) so re-runs are reproducible.
|
|
39
|
+
|
|
40
|
+
## Step 4 — Insert in foreign-key dependency order
|
|
41
|
+
|
|
42
|
+
Topologically sort the tables: insert parents before children so every FK resolves. Capture generated parent IDs (returning IDs or your ORM's create result) and reference them when building child rows — never hardcode an ID you hope exists.
|
|
43
|
+
|
|
44
|
+
> [!WARNING]
|
|
45
|
+
> Inserting in the wrong order, or referencing an ID that wasn't created, throws a foreign-key violation and aborts the whole seed. If a table has a self-referencing FK (e.g. `manager_id`), insert the rows first with nulls, then update the references in a second pass.
|
|
46
|
+
|
|
47
|
+
## Step 5 — Make it idempotent
|
|
48
|
+
|
|
49
|
+
The script must be safe to run repeatedly without duplicating rows or erroring on unique constraints. Pick the approach that fits the stack and write it explicitly:
|
|
50
|
+
|
|
51
|
+
- **Truncate-and-reseed** (simplest for dev): `TRUNCATE … RESTART IDENTITY CASCADE` (or the ORM's deleteMany in reverse FK order) at the top, then insert fresh.
|
|
52
|
+
- **Upsert**: `INSERT … ON CONFLICT DO UPDATE` / `upsert` keyed on a stable natural key, so re-runs converge instead of duplicating.
|
|
53
|
+
- **Guard**: skip seeding a table that already has rows.
|
|
54
|
+
|
|
55
|
+
Wrap the run in a single transaction where the driver allows, so a failure leaves the database untouched.
|
|
56
|
+
|
|
57
|
+
## Step 6 — Write the script
|
|
58
|
+
|
|
59
|
+
Write the seed file in the project's language/runner with: the production guard from the Scope warning, the connection read from env, generation in FK order, the idempotency mechanism, and a short usage comment. Add or note the run command (e.g. `prisma db seed`, `npm run seed`, `rails db:seed`, `python -m app.seed`) — but do not execute it; you only have Read/Grep/Glob/Write.
|
|
60
|
+
|
|
61
|
+
## Report
|
|
62
|
+
|
|
63
|
+
In your message, report: the script path written, which tables it seeds and at what row counts, the idempotency strategy chosen, the production-safety guard, and the exact command to run it. End with the single recommended first step (typically: confirm `DATABASE_URL` points at a dev database, then run the command).
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: "Set up fast pre-commit hooks that catch problems before they land — detect the repo's existing stack and hook mechanism, run lint/format/typecheck plus a secret scan on staged files only, keep the slow test suite in CI, and make the setup reproducible for the whole team."
|
|
3
|
+
allowed-tools: "Read, Write, Glob, Grep, Bash"
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
## Scope
|
|
7
|
+
|
|
8
|
+
No arguments. Your job: leave this repo with pre-commit hooks that run in **seconds**, only on **staged** content, blocking the cheap mistakes (lint errors, unformatted code, type breaks, committed secrets) before they enter history — while the full test suite stays in CI.
|
|
9
|
+
|
|
10
|
+
Match the tooling the repo already uses. Do not impose a new framework on a repo that has a working one, and do not introduce a second runner alongside an existing one.
|
|
11
|
+
|
|
12
|
+
> [!WARNING]
|
|
13
|
+
> Hooks that run the whole test suite on every commit are slow, so developers learn to type `--no-verify` and the hooks stop protecting anything. Keep the commit path under a few seconds. Slow, comprehensive checks belong in CI.
|
|
14
|
+
|
|
15
|
+
## Step 1 — Detect the stack and what already exists
|
|
16
|
+
|
|
17
|
+
Before writing anything, read the ground truth:
|
|
18
|
+
|
|
19
|
+
- **Existing hook mechanism** — `.pre-commit-config.yaml` (the pre-commit framework), `.husky/` + a `lint-staged` block in `package.json`, `lefthook.yml`, or a hand-rolled `.git/hooks/pre-commit`. Also check `git config core.hooksPath`.
|
|
20
|
+
- **Stack** — `package.json`, `pyproject.toml`/`requirements.txt`, `go.mod`, `Cargo.toml`, `Gemfile`.
|
|
21
|
+
- **Tools the repo already has** — linter (eslint, ruff, golangci-lint, clippy), formatter (prettier, ruff format/black, gofmt, rustfmt), type checker (tsc, mypy, pyright), and how the test suite is invoked.
|
|
22
|
+
|
|
23
|
+
Reuse those exact tools and their existing config. The hook should call the same `eslint`/`ruff`/`prettier` the team already runs, not a new one with different rules.
|
|
24
|
+
|
|
25
|
+
## Step 2 — Pick the mechanism (match, don't impose)
|
|
26
|
+
|
|
27
|
+
- A config already exists → **extend it**. Add missing checks to the current `.pre-commit-config.yaml` / `lint-staged` block.
|
|
28
|
+
- JS/TS repo, nothing yet → **Husky + lint-staged** (`lint-staged` already runs only on staged files — that's the whole point).
|
|
29
|
+
- Python or polyglot repo, nothing yet → **the `pre-commit` framework** (`.pre-commit-config.yaml`); it pins hook versions and handles staged-only runs.
|
|
30
|
+
- Tiny/no package manager → a **native `.git/hooks/pre-commit`** script. Note that native hooks aren't shared by git, so Step 5 must check it into the repo and add an install step.
|
|
31
|
+
|
|
32
|
+
State your choice and why in one line.
|
|
33
|
+
|
|
34
|
+
## Step 3 — Configure fast, staged-only checks
|
|
35
|
+
|
|
36
|
+
Wire these against **staged files only**, fastest-failing first:
|
|
37
|
+
|
|
38
|
+
1. **Secret scan** — block committed credentials with `gitleaks protect --staged` or pre-commit's `detect-secrets`. This is the one check worth running first; a leaked key can't be un-pushed.
|
|
39
|
+
2. **Format (auto-fix)** — run the formatter in write mode on staged files, then re-stage them (`prettier --write`, `ruff format`, `gofmt -w`). Auto-fixing beats rejecting the commit over whitespace.
|
|
40
|
+
3. **Lint** — only the staged files (`eslint`, `ruff check`, `golangci-lint run`); enable `--fix` where the linter's fixes are safe.
|
|
41
|
+
4. **Typecheck** — only if it's fast on the changed scope. `tsc` is whole-project and often too slow for the commit path; if so, leave it to CI rather than degrading the commit experience.
|
|
42
|
+
|
|
43
|
+
With `lint-staged`, the staged-file list is passed to each command automatically. With the `pre-commit` framework, set `pass_filenames: true` (the default) and scope with `files:`/`types:`.
|
|
44
|
+
|
|
45
|
+
> [!WARNING]
|
|
46
|
+
> The hook must operate on staged content only. If a tool reads the working tree instead of the index, a developer can stage a clean version, leave a broken version unstaged, and the hook passes on code that won't be what's committed. `lint-staged` and the `pre-commit` framework stash unstaged changes to avoid exactly this — a raw native hook does not, so handle it explicitly there.
|
|
47
|
+
|
|
48
|
+
## Step 4 — Keep the slow stuff in CI
|
|
49
|
+
|
|
50
|
+
Do **not** put the full test suite, full-repo typecheck, or a full build in the commit hook. Confirm those run in CI (check `.github/workflows/`); if a needed check is missing there, name the exact job that should run it (lint, typecheck, full tests on push/PR) and flag that it belongs in CI, not the commit path. A `pre-push` hook is the acceptable home for a fast smoke subset — never a substitute for CI.
|
|
51
|
+
|
|
52
|
+
## Step 5 — Make it reproducible for the team
|
|
53
|
+
|
|
54
|
+
A hook that only works on your machine is worthless. Ensure:
|
|
55
|
+
|
|
56
|
+
- The config file is **committed** (`.pre-commit-config.yaml`, the `lint-staged` block, `.husky/` scripts, or the checked-in native hook + an installer like `git config core.hooksPath .githooks`).
|
|
57
|
+
- There is **one install command** a teammate runs after cloning — `npx husky` (wired via the `prepare` script in `package.json` so `npm install` does it), or `pre-commit install`.
|
|
58
|
+
- Hook tool versions are **pinned** (pre-commit `rev:` tags; devDependencies for JS) so everyone runs identical checks.
|
|
59
|
+
|
|
60
|
+
Verify it actually fires: stage a deliberately broken file and confirm the commit is rejected, then fix and confirm it passes.
|
|
61
|
+
|
|
62
|
+
## Step 6 — Document the escape hatch
|
|
63
|
+
|
|
64
|
+
Note in the config or a short README line that `git commit --no-verify` skips hooks for genuine emergencies (hotfix, mid-rebase WIP). Don't hide it — but pair it with the reminder that CI still enforces the same checks, so bypassing locally only defers the failure.
|
|
65
|
+
|
|
66
|
+
## Report
|
|
67
|
+
|
|
68
|
+
End with:
|
|
69
|
+
|
|
70
|
+
- **Files written/changed** — config path(s) and any `package.json` script additions.
|
|
71
|
+
- **One-time install command** teammates run after cloning (exact command).
|
|
72
|
+
- **What runs on commit** vs. **what's left to CI**, and the bypass flag for emergencies.
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: "Explore the codebase and write a decision-oriented design doc / RFC for a feature or system change."
|
|
3
|
+
argument-hint: "<feature or system to design>"
|
|
4
|
+
allowed-tools: "Read, Grep, Glob"
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
## Scope
|
|
8
|
+
|
|
9
|
+
Treat `$ARGUMENTS` as the thing being designed — a feature, a subsystem, or a structural change (`move sessions from cookies to Redis`, `add multi-tenant billing`, `replace the polling sync with webhooks`). Restate it in one sentence to confirm scope before designing.
|
|
10
|
+
|
|
11
|
+
If `$ARGUMENTS` is empty, ask one focused question: *"What feature or system change should I design?"* Do not invent a problem to solve.
|
|
12
|
+
|
|
13
|
+
> [!WARNING]
|
|
14
|
+
> Read-only mode. Do not modify the repository, run migrations, install packages, or scaffold code. The written design doc is your only output. Designing without reading the current code produces a doc that won't survive contact with the repo — it proposes structure that already exists differently, or ignores constraints the code already enforces.
|
|
15
|
+
|
|
16
|
+
> [!NOTE]
|
|
17
|
+
> A design doc without honest alternatives and trade-offs is just a plan in disguise. If you cannot name an approach you rejected and *why*, you haven't done the design work yet — go back to Step 2.
|
|
18
|
+
|
|
19
|
+
## Step 1 — Frame the problem
|
|
20
|
+
|
|
21
|
+
Before any solution, pin down what you're solving and why now.
|
|
22
|
+
|
|
23
|
+
- What is broken, missing, or about to break? Why is this worth doing *now* rather than later?
|
|
24
|
+
- Who is affected — end users, a specific team, on-call, future maintainers? What do they feel today?
|
|
25
|
+
- What does "done" look like as observable behavior, and what is explicitly **not** in scope for this change?
|
|
26
|
+
|
|
27
|
+
## Step 2 — Ground the design in the real code
|
|
28
|
+
|
|
29
|
+
A design that ignores the existing structure invents a system that doesn't match the one you're changing. Use `Read`, `Grep`, and `Glob` (no shell) to map reality first:
|
|
30
|
+
|
|
31
|
+
- **Orient:** `Read` `README.md`, `package.json`, and `CLAUDE.md` for stack, conventions, and the patterns the team already commits to. `Glob` (e.g. `src/**/*.ts`, `**/migrations/**`, `**/*.config.*`) to see how the tree and its boundaries are laid out.
|
|
32
|
+
- **Find the blast radius:** `Grep` for terms from `$ARGUMENTS` to locate every module, route, model, and config the change touches. Trace the data flow and the layers it crosses — a design that only names the happy-path file underestimates the work.
|
|
33
|
+
- **Find the pattern to extend or break from:** `Read` the closest existing subsystem end to end. Decide deliberately whether your design *follows* that pattern (cite it) or *departs* from it (justify the departure in Trade-offs). Note real constraints you discover: a schema you must migrate, an interface other code depends on, a queue/cache/auth boundary you can't move freely.
|
|
34
|
+
|
|
35
|
+
## Step 3 — Write the design doc
|
|
36
|
+
|
|
37
|
+
Output the doc in this structure. Keep it skimmable and decision-oriented — cite real file paths and symbols from Step 2, not placeholders. Cut anything that isn't a decision or a constraint.
|
|
38
|
+
|
|
39
|
+
```markdown
|
|
40
|
+
## Design — <one-line summary of the change>
|
|
41
|
+
|
|
42
|
+
### Context & problem
|
|
43
|
+
<Why this, why now, who's affected. The state today, with real references
|
|
44
|
+
(`path/to/module.ts`, the current flow). 2-4 tight paragraphs, no preamble.>
|
|
45
|
+
|
|
46
|
+
### Goals
|
|
47
|
+
- <observable outcome this change must achieve>
|
|
48
|
+
|
|
49
|
+
### Non-goals
|
|
50
|
+
- <explicitly out of scope — the boundaries that keep this shippable>
|
|
51
|
+
|
|
52
|
+
### Proposed design
|
|
53
|
+
<The approach. Data model / flow changes, key interfaces and signatures,
|
|
54
|
+
and exactly how it fits (or deliberately departs from) existing patterns
|
|
55
|
+
in `path/to/...`. Diagrams-in-prose are fine; be concrete about what code
|
|
56
|
+
lives where.>
|
|
57
|
+
|
|
58
|
+
### Alternatives considered
|
|
59
|
+
- **<Alternative A>** — <how it would work> — **Rejected because** <reason>.
|
|
60
|
+
- **<Alternative B>** — <how it would work> — **Rejected because** <reason>.
|
|
61
|
+
|
|
62
|
+
### Trade-offs & risks
|
|
63
|
+
- <what this design costs: complexity, perf, coupling, ops burden>
|
|
64
|
+
- <what could break, and the failure mode if it does>
|
|
65
|
+
|
|
66
|
+
### Rollout & migration
|
|
67
|
+
- <how it ships: flag, phased rollout, backfill/migration order, rollback path>
|
|
68
|
+
|
|
69
|
+
### Observability
|
|
70
|
+
- <metrics, logs, alerts that prove it works in prod and catch regressions>
|
|
71
|
+
|
|
72
|
+
### Open questions
|
|
73
|
+
- <each unresolved decision that needs an owner / a call>
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
## Report
|
|
77
|
+
|
|
78
|
+
Deliver the design doc as your message — it is the whole deliverable. Verify it has real alternatives with reasons, honest trade-offs, and a rollout plan that names a rollback path; if any of those is hand-waved, it isn't done. End with the **Open questions** — the specific decisions that need a human call before implementation can start. No files were changed; this is a doc to align on, not the change itself.
|