init: centralized agent skills for Claude Code + Codex
- skills/ shared by both tools (open Agent Skills standard) - portable cross-skill refs (root-relative, no ~/.claude hardcode) - bin/link.sh bootstrap for non-Nix machines - nix/home.nix + flake.nix for home-manager Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014SK5Lo7LQfwLRVdCv1A8uF
This commit is contained in:
@@ -0,0 +1,133 @@
|
||||
---
|
||||
name: blitz
|
||||
description: "Autonomously drive an entire tracker milestone to done — sweep every open issue (one subagent per issue, parallel where dependencies allow), fix bugs found along the way, then either deploy (safe to debug in prod) or spawn a local dev instance, and push a Home Assistant notification with the preview URL. Built for long unattended runs. Use when the user wants to blitz / sweep / complete a whole milestone, e.g. \"/blitz M0\"."
|
||||
user-invocable: true
|
||||
args:
|
||||
- name: input
|
||||
description: "A milestone identifier: its number/id (e.g. 3), or a substring of its title (e.g. M0). Omit to auto-pick the lowest-numbered open milestone with unclosed issues."
|
||||
required: false
|
||||
---
|
||||
|
||||
# Blitz — Milestone Autopilot
|
||||
|
||||
Milestone-scale sibling of `/yolo`. `yolo` ships one issue; **blitz drives a whole milestone to done, unattended** — sweeping its issues, fixing bugs it discovers, then shipping (deploy or local dev) and pinging the user's phone with a preview URL.
|
||||
|
||||
Design goal: keep working productively for long stretches while spiking an idea, so the user only steps in once there's something to preview.
|
||||
|
||||
**First:** read `linear-common/COMMON.md` (sibling skill, same skills root) for shared config, worktree, and implementation conventions. Everything there applies; this doc only adds the milestone orchestration on top.
|
||||
|
||||
This skill targets **`tracker: gitea`** (milestones live in the repo's Gitea tracker). For `tracker: linear`, treat a Linear **cycle or sub-project** as the milestone and adapt the API calls; the orchestration shape is identical.
|
||||
|
||||
---
|
||||
|
||||
## Roles
|
||||
|
||||
- **Orchestrator** = the main blitz thread (you). Owns the DAG, spawns subagents, merges branches, closes issues, runs the readiness gate, ships, notifies. Does *not* implement issues itself.
|
||||
- **Issue subagent** = one `Agent` per issue (`isolation: "worktree"`). Implements exactly one issue via the yolo flow, returns a structured result. **One subagent per issue is the default and is incentivized** — do not batch multiple issues into one agent.
|
||||
|
||||
---
|
||||
|
||||
## 1. Setup
|
||||
|
||||
1. Load project config + `source ~/.env.claude` (for `$GITEA_TOKEN`). Set `BASE=$remoteBaseUrl`, `REPO=<owner>/<repo>` (from `git remote get-url origin`).
|
||||
2. **Resolve the milestone** from `$ARGUMENTS`:
|
||||
- `GET $BASE/api/v1/repos/$REPO/milestones?state=open` → match by id or title-substring (case-insensitive). No arg → lowest-numbered open milestone that still has open issues.
|
||||
- Save `MS_ID`, `MS_TITLE`, and a slug (lowercase-hyphenated, e.g. `m0`).
|
||||
3. **Integration branch** `blitz/<slug>` off `defaultBranch`. Create + push if absent, else check it out. Everything merges here; `defaultBranch` stays untouched until Ship.
|
||||
4. kitty tab title `blitz/<slug>` (silent skip if unavailable).
|
||||
|
||||
## 2. Build the issue DAG
|
||||
|
||||
1. List the milestone's open issues: `GET $BASE/api/v1/repos/$REPO/issues?state=open&type=issues&limit=100`, filter to those whose `milestone.id == MS_ID` (or pass `&milestones=<MS_TITLE>`). `type=issues` excludes PRs.
|
||||
2. For each, `GET .../issues/$N/dependencies` → its **blocked-by** set. Build the dependency graph.
|
||||
3. Classify each issue:
|
||||
- **Epic / tracking-only**: title contains "Epic" or body is a children checklist with no own implementation scope. Do NOT assign a subagent — it closes automatically when its children (its blockers) all close.
|
||||
- **Workable**: everything else.
|
||||
4. **Ready set** = workable, open issues whose every blocker is closed.
|
||||
|
||||
## 3. Execution pass (the loop body)
|
||||
|
||||
**Blitz drives its own loop — no external `/loop` needed.** The orchestrator thread stays alive and repeats the pass below until the milestone is done. Fan-out subagents run in the background; when they finish they re-invoke you, which advances the next wave naturally. Only use `ScheduleWakeup` as a fallback heartbeat when you're blocked waiting on something the harness can't notify you about (e.g. polling a deploy's health). Wrapping blitz in `/loop` is unnecessary and not the intended usage.
|
||||
|
||||
Each pass:
|
||||
|
||||
1. Recompute the **ready set** (§2.4).
|
||||
2. **Fan out**: spawn one issue subagent per ready issue, **in parallel** (multiple `Agent` calls in a single message), `isolation: "worktree"`. Cap concurrency at a sane number (≈4–6). Each subagent prompt:
|
||||
- "Implement Gitea issue #N (`<title>`) in this repo following the `/yolo` flow and `COMMON.md`. You are on integration branch `blitz/<slug>`; create branch `<slug>/N-<issue-slug>` **off it**. Read the issue body + its linked spec/epic; that plus the repo is your full context. Implement, run `buildCommand`, commit in logical steps. **Do not merge to any shared branch and do not close the issue** — push your branch and return the result. If you discover a bug or missing work outside this issue's scope, do not fix it silently; report it in `newFindings`."
|
||||
- Force a structured return (schema): `{ issue, done, branch, summary, buildPassed, newFindings: [{title, body}] }`.
|
||||
- **Strict rule**: never spawn a subagent for a blocked issue. Dependencies are load-bearing.
|
||||
3. **Integrate serially** (orchestrator, to avoid parallel-merge conflicts): for each finished subagent whose `done && buildPassed`, merge its branch into `blitz/<slug>`, resolve conflicts, run `buildCommand`. If the merge or build breaks, fix on the integration branch (or bounce the issue back for another pass).
|
||||
4. **Close** each successfully integrated issue on Gitea (`Closes #N` in the merge commit, or PATCH `state:closed`). Epics whose blockers are now all closed: close them too.
|
||||
5. **Integration review (cadence-gated) — do NOT skip.** After each wave (or every ~3 integrated issues, whichever comes first), audit the *accumulated* diff of `blitz/<slug>` vs `defaultBranch` — not each issue in isolation. Run `/code-review` on that diff, or spawn a reviewer subagent, hunting the cross-issue drift that blind parallel work causes: inconsistent data shapes / contracts between issues, divergent naming, duplicated or conflicting logic, dead code, regressions, misbehavior. **Findings are top priority**: fix them (inline, or file + wire as blocking issues) *before* spawning the next fan-out wave. This is the load-bearing coherence check — parallel subagents can't see each other's work, so this is the only place drift gets caught.
|
||||
6. **Fold in findings**: for each `newFindings` item and any bug you find, create a new Gitea issue in this milestone (`milestone: MS_ID`), wire dependencies if it blocks/relies on others, and let the next pass pick it up. Fix trivial bugs inline instead of filing.
|
||||
7. Repeat passes until: no open workable issues, no open epics, the latest integration review is clean, and a full pass produced **no new findings**.
|
||||
|
||||
## 4. Readiness gate
|
||||
|
||||
Declare the milestone **ready** only when ALL hold:
|
||||
- Every workable issue closed; every epic closed.
|
||||
- `buildCommand` green on `blitz/<slug>`.
|
||||
- A real smoke/verify of the app passes — drive the actual feature (use `/verify` or `/run`, or the project's verify skill), not just unit tests. This catches integration breakage the per-issue subagents couldn't see.
|
||||
|
||||
If the gate fails, file/fix the gap as a finding and run another pass.
|
||||
|
||||
## 5. Ship — deploy OR local dev
|
||||
|
||||
Pick the path per the milestone's nature and config. Resolve `deployPolicy`:
|
||||
- Explicit: `.claude/linear.json` → `blitz.deploy` (a `{ "<slug>": "deploy" | "local" }` map) wins if present.
|
||||
- Heuristic (when unset), **deploy only if ALL true**:
|
||||
1. Milestone is user-facing / shippable — NOT a throwaway spike (check the milestone description for "throwaway"/"spike"/"disposable").
|
||||
2. A deploy target is wired — a Dokploy app for this repo exists, or `blit.deployTarget` / `deployUrl` is configured.
|
||||
3. Change is safe to debug in prod — static site or additive change, no destructive migration.
|
||||
- **When genuinely unsure, choose LOCAL.** Never surprise-deploy to prod.
|
||||
|
||||
### Deploy path (safe to debug in prod)
|
||||
1. Merge `blitz/<slug>` → `defaultBranch`, push.
|
||||
2. Trigger the deploy via the **Dokploy MCP** for the configured app (`application-deploy` / `application-redeploy`, or `compose-deploy` for compose apps). Poll until healthy.
|
||||
3. Preview URL = the app's Dokploy domain (`domain-byApplicationId`) or configured `deployUrl`.
|
||||
|
||||
### Local path (spike / not prod-safe)
|
||||
1. Keep `blitz/<slug>` (do not merge to `defaultBranch` unless the change is trivially safe).
|
||||
2. Launch a local dev instance in the background — use the `/run` skill, or run the app's dev server directly, **bound to `0.0.0.0`** (global rule: this VM is reached from other devices), e.g. `pnpm --dir <app> dev --host 0.0.0.0` with `run_in_background: true`.
|
||||
3. Preview URL = `http://<LAN-IP>:<port>` (LAN IP, never `localhost`): `hostname -I | awk '{print $1}'`.
|
||||
|
||||
## 6. Notify (Home Assistant MCP)
|
||||
|
||||
Push to the user's phone so they know it's ready to look at. Default target `notify.mobile_app_pixel_7_naps` (override with `blitz.notifyService` in config):
|
||||
|
||||
```
|
||||
mcp__ha-mcp__ha_call_service
|
||||
domain: "notify"
|
||||
service: "mobile_app_pixel_7_naps"
|
||||
service_data:
|
||||
title: "blitz: <MS_TITLE> ready ✅"
|
||||
message: "<n> issues shipped · <deploy|local dev> · tap to preview"
|
||||
data:
|
||||
url: "<preview-url>" # opens the companion app at the URL
|
||||
clickAction: "<preview-url>" # Android notification tap target
|
||||
```
|
||||
|
||||
Then post a one-line summary + preview URL in the chat too, and **finish the run** — the self-driven loop ends here (cancel any pending `ScheduleWakeup` heartbeat with `stop: true`).
|
||||
|
||||
## 7. Autonomy & stop conditions
|
||||
|
||||
- Fully autonomous. Never ask the user except on a genuine blocker (missing credentials, architectural contradiction, an irreversible/destructive op, or a deploy that would break prod). **Away ≠ approval** — if you must ask, wait; don't decide for them.
|
||||
- **Build it can't fix** after a few honest attempts, or a hard blocker: file a blocker issue in the milestone, send an HA notification describing the blocker (same call, title `blitz: <MS_TITLE> BLOCKED ⚠️`), and stop. Don't thrash.
|
||||
- Never auto-deploy to prod when the ship decision is uncertain — fall back to local + notify.
|
||||
- Idempotent: a re-run picks up where it left off (open issues + integration branch already reflect progress).
|
||||
|
||||
## Config (optional, `.claude/linear.json`)
|
||||
|
||||
```jsonc
|
||||
{
|
||||
"blitz": {
|
||||
"deploy": { "m1": "deploy", "m0": "local" }, // per-slug override of the ship path
|
||||
"deployTarget": "<dokploy app name or id>",
|
||||
"deployUrl": "https://mapmatch.naps.pt", // preview URL for the deploy path
|
||||
"devCommand": "pnpm --dir spike dev --host 0.0.0.0",
|
||||
"notifyService": "mobile_app_pixel_7_naps"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
All optional — sane fallbacks apply (heuristic ship decision, `/run` for local, default notify target).
|
||||
Reference in New Issue
Block a user