Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .claude-plugin/marketplace.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
},
"metadata": {
"description": "Personal Claude Code plugins, skills, and subagents",
"version": "0.25.0",
"version": "0.26.4",
"homepage": "https://cc.fx.gd"
},
"plugins": [
Expand Down
5 changes: 5 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -24,3 +24,8 @@ node_modules/
# `duvet report --ci` verifies against.
.duvet/reports/
.duvet/requirements/

# Transient agent scratch: reviewer wait logs and per-run worktrees. Both are
# produced by a single fx-dev run and are meaningless outside it.
.claude/team/waits/
.claude/worktrees/
2 changes: 1 addition & 1 deletion plugins/fx-dev/.claude-plugin/plugin.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
{
"name": "fx-dev",
"version": "5.0.0",
"version": "5.1.4",
"description": "Complete development workflow including SDLC, pull requests, git utilities, and GitHub integration"
}
13 changes: 7 additions & 6 deletions plugins/fx-dev/skills/coder/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -38,12 +38,13 @@ Pay attention to the user's framing. "Just fix the login bug real quick" is a bu
3. Plan logical PR structure if needed
4. Implement with tests
5. Run the local Codex review (`fx-dev:codex-review`) and converge it **before** opening the PR — it is the only local reviewer, and it is mandatory here exactly as in `fx-dev:dev` Step 4.5
6. Create PR
7. Settle the automated reviewers with `fx-dev:copilot-review` and, where its GitHub App is installed, `fx-dev:coderabbit-review`
8. Address feedback
9. Launch a sub-agent with the pr-check-monitor skill for failing checks
10. Continue until ready for user review
11. Update issue to Done
6. Load `fx-dev:github` and follow its **PR conventions block** — conventional-commit title, no `#<number>` or wave/phase wording in the title, and a body that is **never hard-wrapped** (one long line per paragraph; only the commit message wraps, at ~72 columns). Verify both before and after creating.
7. Create PR
8. Settle the automated reviewers with `fx-dev:copilot-review` and, where its GitHub App is installed, `fx-dev:coderabbit-review`
9. Address feedback
10. Launch a sub-agent with the pr-check-monitor skill for failing checks
11. Continue until ready for user review
12. Update issue to Done

**When invoked from SDLC:** Stop after step 4 (implement with tests + commit). Do NOT create PRs or launch reviewers — the SDLC owns steps 5 onward.

Expand Down
51 changes: 51 additions & 0 deletions plugins/fx-dev/skills/github/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -192,6 +192,57 @@ Standardizes every skill on two canonical instruction files, with a pointer for

This applies however the body is authored — heredoc, `--body-file`, or `gh api -F body=@file`. Tables, lists, and fenced code blocks keep their own line structure; the rule is about prose paragraphs.

### PR conventions block (paste verbatim into any agent prompt that may open or edit a PR)

A convention that lives only in this skill does not survive delegation: an agent spawned with an ad-hoc prompt never loads it. Whenever you delegate PR creation — a `/team` coder opening its own PR, a fix agent editing a body, any sub-agent running `gh pr create` — paste this block into that agent's prompt verbatim. Quote it by name ("the github skill's PR conventions block") when referring to it from another skill.

~~~markdown
### PR conventions (mandatory)
- TITLE: a conventional-commit subject. The **mechanical floor** is `^(feat|fix|docs|refactor|chore|test|perf|build|ci|style|revert)(\(.+\))?!?: .+` — that regex checks the type prefix and nothing else, so passing it is necessary, not sufficient.
- TITLE: three more rules the floor regex cannot express. Two are checkable against the title (`grep -Eq '^[a-z]+(\(.+\))?!?: [a-z]'` must match — lowercase after the colon; `grep -Eq '\.$'` must NOT match — no trailing period). The third, **imperative mood** ("add", not "adds" or "added"), has no mechanical check: read the title and confirm it by eye.
- TITLE: no `#<number>` unless it references a real existing PR/issue, and no wave/phase/step/batch or change-doc number. Squash-merge bakes the title into the default branch, where `#N` auto-links permanently.
- BODY: **never hard-wrapped.** GitHub reflows markdown to the reader's viewport, so write each paragraph as ONE long line and let it soft-wrap. Lists, tables and fenced code blocks keep their own line structure. This applies however the body is authored — heredoc, `--body-file`, or `gh api -F body=@file`.
- COMMIT MESSAGE: the opposite — wrap the body at ~72 columns, because git renders it as plain text. The rule follows the renderer, not the content.
- Verify before AND after creating: the title against every rule above, and the body with the command below. It judges only prose — fenced code, headings, blockquotes, tables, list items and their continuation lines are all skipped — and **exits 1 printing `HARD-WRAPPED`** when prose clusters in the 60-100 column band. Read its output; do not assume it passed. Fix with `gh pr edit <N> --body-file <file>` and re-run.

```bash
gh pr view <N> --json body -q .body | awk '
# Fenced code, CommonMark rules: a fence opens on ``` or ~~~ and closes only
# on the SAME character, at least as long as the fence that opened it — so a
# ```` block may legally contain a ``` line without closing.
{
t = $0; sub(/^[[:space:]]+/, "", t)
if (t ~ /^```/ || t ~ /^~~~/) {
ch = substr(t, 1, 1); len = 0
while (substr(t, len + 1, 1) == ch) len++
if (!fence) { fence = ch; flen = len; next }
else if (ch == fence && len >= flen) { fence = ""; flen = 0; next }
}
}
fence { next }
/^[[:space:]]*$/ { next } # blank: does not end a list
/^( |\t)/ { next } # indented code block
/^[[:space:]]*#/ { list = 0; next } # heading
/^[[:space:]]*>/ { list = 0; next } # blockquote
/^[[:space:]]*\|/ { list = 0; next } # table row
/^[[:space:]]*([-*+]|[0-9]+[.)])[[:space:]]/ { list = 1; next } # list marker line
/^[[:space:]]/ { if (list) next } # continuation, ONLY inside a list
{ list = 0; n++; if (length($0) >= 60 && length($0) <= 100) w++ }
END {
if (n == 0) { print "no prose lines to check"; exit 0 }
printf "prose lines: %d; in the 60-100 col hard-wrap band: %d\n", n, w
if (w * 2 > n) { print "HARD-WRAPPED - rewrite each paragraph as ONE long line"; exit 1 }
print "OK - prose is not hard-wrapped"
}'
```
~~~

**That command is the canonical "Mechanical body check"**, and `fx-dev:team` merge gate 5c refers to it by that name. Run it yourself after creating or editing any PR body, not only when delegating.

**It is a heuristic, not a Markdown parser, and that is deliberate.** It follows CommonMark on the two things that actually bite — a fence closes only on the same character at a length at least its opening, and an indented line is a continuation only inside a list — and it guesses at the rest. Treat a `HARD-WRAPPED` verdict as a prompt to *read* the body, not as proof. If you have read it and the prose genuinely is one line per paragraph, say so in your report and move on: **do not rewrite correct prose to satisfy the checker**, and do not extend the awk to chase a further Markdown construct. The supply of constructs does not run out, and the rule the gate enforces is the one in prose above it.

Do not substitute `awk '{print length}' | sort -rn | head -3`. The three longest lines in a body are usually a table row or a code line, both exempt, so it reports a healthy number for a body whose prose is entirely hard-wrapped.

**Use Conventional Formats:**
- **Commit messages**: Follow conventional commit format (`feat:`, `fix:`, `refactor:`, `docs:`, etc.)
- **PR titles**: MUST use conventional commit format — `type(scope): description` (e.g., `feat: add user authentication`, `fix(api): handle null token`). **BLOCKING**: on squash-merge the PR title becomes the commit subject, so a plain prose title (no `type:` prefix) permanently pollutes a conventional-commit history. **Canonical check** — every PR title, no matter who creates it (pr-preparer, the `/dev` workflow, or a `/team` coordinator running `gh pr create` directly), MUST match this regex; verify before creating AND before merging:
Expand Down
35 changes: 35 additions & 0 deletions plugins/fx-dev/skills/pr-preparer/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,41 @@ Then, your primary responsibilities:

**Never hard-wrap the description.** GitHub reflows markdown to the reader's viewport, so hard-wrapping prose at 80 columns (or any column) only renders ragged and re-wraps badly on narrow screens. Write each paragraph as ONE long line and let it soft-wrap. Commit messages are the opposite — those stay wrapped at ~72 columns, because git renders them as plain text. See the `fx-dev:github` skill's "Never hard-wrap anything GitHub renders as markdown".

**Verify it, do not merely intend it.** After creating or editing the PR, run the canonical body check from the `fx-dev:github` skill's "Mechanical body check" — restated here in full so it reaches you even when that skill is not loaded — and read its output:

```bash
gh pr view <N> --json body -q .body | awk '
# Fenced code, CommonMark rules: a fence opens on ``` or ~~~ and closes only
# on the SAME character, at least as long as the fence that opened it — so a
# ```` block may legally contain a ``` line without closing.
{
t = $0; sub(/^[[:space:]]+/, "", t)
if (t ~ /^```/ || t ~ /^~~~/) {
ch = substr(t, 1, 1); len = 0
while (substr(t, len + 1, 1) == ch) len++
if (!fence) { fence = ch; flen = len; next }
else if (ch == fence && len >= flen) { fence = ""; flen = 0; next }
}
}
fence { next }
/^[[:space:]]*$/ { next } # blank: does not end a list
/^( |\t)/ { next } # indented code block
/^[[:space:]]*#/ { list = 0; next } # heading
/^[[:space:]]*>/ { list = 0; next } # blockquote
/^[[:space:]]*\|/ { list = 0; next } # table row
/^[[:space:]]*([-*+]|[0-9]+[.)])[[:space:]]/ { list = 1; next } # list marker line
/^[[:space:]]/ { if (list) next } # continuation, ONLY inside a list
{ list = 0; n++; if (length($0) >= 60 && length($0) <= 100) w++ }
END {
if (n == 0) { print "no prose lines to check"; exit 0 }
printf "prose lines: %d; in the 60-100 col hard-wrap band: %d\n", n, w
if (w * 2 > n) { print "HARD-WRAPPED - rewrite each paragraph as ONE long line"; exit 1 }
print "OK - prose is not hard-wrapped"
}'
```

It exempts fenced code, headings, blockquotes, tables, and list items with their continuation lines, and judges only prose. It **exits 1 and prints `HARD-WRAPPED`** when prose clusters in the 60-100 column band. If it does, rewrite each paragraph as one long line and `gh pr edit <N> --body-file <file>`, then run it again. It is a heuristic, not a Markdown parser: if you have read the body and its prose genuinely is one line per paragraph, say so and move on rather than rewriting correct prose to satisfy the checker.

5. **Check Compliance**: Verify adherence to:
- Project-specific guidelines from AGENTS.md files
- Global coding standards and architectural decisions
Expand Down
8 changes: 7 additions & 1 deletion plugins/fx-dev/skills/team/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -222,7 +222,11 @@ Two constraints worth knowing rather than rediscovering:

When you spawn the coder for the FINAL piece of a change, your prompt MUST include: "This is the final implementing PR for <change>. In the same commit, flip `**Status:** draft` → `**Status:** complete` in `docs/changes/<NNNN>-*.md` AND flip `status: draft` → `status: complete` for that change's entry in `docs/index.yml`. Sync `docs/index.md` if present." For every NON-final coder on the same change, your prompt MUST include: "Leave the change-doc `**Status:**` field and `docs/index.yml` entry untouched — the final PR flips them." This split prevents rebase-conflict storms across multi-PR changes and ensures the final PR carries the Status flip atomically.

**PR creation** → Either do it yourself via `gh pr create` or spawn a focused PR preparer agent. Load `fx-dev:github` skill first. **⛔ If you create the PR yourself, the `--title` MUST be a conventional-commit subject — `type(scope): description` — matching the canonical regex `^(feat|fix|docs|refactor|chore|test|perf|build|ci|style|revert)(\(.+\))?!?: .+` (see the github skill's "Use Conventional Formats"). Do NOT write a prose title; running `gh pr create` directly does NOT exempt you from the conventional-commit rule. Verify the title against the regex before AND after creation.** (Prose titles the coordinator wrote directly — bypassing pr-preparer — are exactly how non-conventional titles have slipped onto `main`.)
**⛔ Every spawn prompt that may open or edit a PR MUST carry the PR conventions block verbatim (BLOCKING).** Load `fx-dev:github` BEFORE you author your first spawn prompt, and paste its **"PR conventions block"** into the prompt of every agent that might run `gh pr create` or `gh pr edit` — coder, fix agent, PR preparer, anything. A convention that lives only in a skill nobody loads does not survive delegation: a spawned agent inherits your prompt, not your skills.

This is not hypothetical. In an observed run, all three coders received the TITLE rule — because this skill restates it inline below and gates it at merge — and none received the BODY rule, which lives only in `fx-dev:github`. All three PRs shipped hard-wrapped bodies that render ragged on GitHub, while PRs prepared through `fx-dev:pr-preparer` in the same repo did not. Restating a rule here is what makes it propagate; anything you do not restate or gate, you will not get.

**PR creation** → Either do it yourself via `gh pr create`, spawn a focused PR preparer agent, or let a coder open its own PR. Load `fx-dev:github` skill first, and pass its PR conventions block into the prompt whenever you delegate. **⛔ Whoever creates the PR — you or an agent you spawned — the `--title` MUST be a conventional-commit subject — `type(scope): description` — matching the canonical regex `^(feat|fix|docs|refactor|chore|test|perf|build|ci|style|revert)(\(.+\))?!?: .+` (see the github skill's "Use Conventional Formats"). Do NOT write a prose title; running `gh pr create` directly does NOT exempt you from the conventional-commit rule. Verify the title against the regex before AND after creation.** (Prose titles the coordinator wrote directly — bypassing pr-preparer — are exactly how non-conventional titles have slipped onto `main`.)

**Review and CI steps** (Copilot review, CodeRabbit review, CI monitoring, feedback resolution) → **Handle these DIRECTLY as the coordinator.** These are lightweight skill/command invocations that must not be delegated. **Pass the STEP 0 Scope Brief into every reviewer invocation that accepts one, and apply it when triaging every reviewer that does not** (Copilot and the CodeRabbit GitHub App accept nothing). A finding covered by the brief's out-of-scope list is recorded as deferred with the covering exclusion — never silently fixed, never silently dropped, and never a reason to widen a teammate's PR. Use each reviewer's waiter or read-only inspection first, classify and deduplicate findings under `fx-dev:dev` Step 2.5, then invoke feedback resolvers only for the classified disposition. Never let a resolver implement unclassified feedback or modify task trackers for deferred feedback.

Expand Down Expand Up @@ -300,6 +304,7 @@ duvet# A pull request MUST NOT be merged while any review thread on it from a co
| 4 | **Spec task marked complete** | Check via project-management skill | YES |
| 5 | **PR description is clear** | Read PR body | YES |
| 5b | **PR title is clean AND conventional** | Title (a) is a conventional-commit subject — run the canonical check from the `fx-dev:github` skill's "Use Conventional Formats" (a plain prose title with no `type:` prefix FAILS) — AND (b) has NO stray `#<number>` (only a real PR/issue ref) and NO wave/phase/step/change-doc number. Fix with `gh pr edit <N> --title "type(scope): …"` before merge — squash bakes the title into `main` | YES |
| 5c | **PR body is NOT hard-wrapped** | Run the canonical **"Mechanical body check"** from the `fx-dev:github` skill — it exempts lists, tables and code blocks, judges only prose, and exits 1 printing `HARD-WRAPPED` when prose clusters in the 60-100 column band. Do NOT substitute a `sort -rn \| head` on line lengths: the longest lines are usually exempt ones, so it passes a body whose prose is entirely wrapped. Fix with `gh pr edit <N> --body-file <file>` and re-run before merging | YES |
| 6 | **Browser verification completed** | Spawn a verify agent if needed (see below) | YES |

### ⛔ Reviewer Gates (Gates 2 + 2b) — CRITICAL
Expand Down Expand Up @@ -419,6 +424,7 @@ When all tasks are complete and all PRs merged:
- **NEVER write code yourself** — all implementation goes through coder agents
- **NEVER create branches or commits** — coder agents handle this
- **NEVER delegate the full SDLC to a single agent** — agents cannot spawn sub-agents, so they will inline everything and skip later steps
- **ALWAYS paste the `fx-dev:github` PR conventions block into every spawn prompt whose agent may open or edit a PR** — load that skill before authoring your first prompt. A spawned agent inherits your prompt, not your skills; a rule you do not restate is a rule that does not reach it.
- **NEVER skip PR inspection** — every PR gets reviewed before marking ready
- **NEVER merge without completing the MERGE GATE CHECKLIST** — every gate must pass, every time, for every PR
- **NEVER merge without Copilot review** — always invoke `fx-dev:copilot-review` yourself. No exceptions.
Expand Down
Loading