diff --git a/.claude/skills/upstream-check/SKILL.md b/.claude/skills/upstream-check/SKILL.md new file mode 100644 index 0000000..2f1abda --- /dev/null +++ b/.claude/skills/upstream-check/SKILL.md @@ -0,0 +1,52 @@ +--- +name: upstream-check +description: Compare Matt Pocock's skills repo against the commit this plugin's ported skills were taken from, and list what is new, changed, or gone. Explicit invocation only. +disable-model-invocation: true +--- + +# Upstream check + +The plugin's `grill`, `build`, `spec`, `routine`, `wayfinder`, `handoff`, +`research`, `wait-what`, `teach`, `writing-for-agents`, `domain-modeling`, +`diagnosing-bugs`, and `resolving-merge-conflicts` skills were rewritten +from `github.com/mattpocock/skills`. This skill says what has changed there +since, so you can decide what to fold in. + +`UPSTREAM.md` next to this file records the upstream commit the port was +taken from, and the map from each upstream skill to the plugin skill it +became. Read it first. + +## Steps + +1. Get the current upstream commit and the list of skill folders: + + ```sh + gh api repos/mattpocock/skills/commits/main --jq .sha + gh api 'repos/mattpocock/skills/git/trees/main?recursive=1' --jq '.tree[] | select(.path | endswith("/SKILL.md")) | .path' + ``` + +2. List the skill folders at the recorded commit the same way, with the + recorded SHA in place of `main`. Compare the two lists. A folder only in + the new list is a new skill. A folder only in the old list was removed. + +3. For every upstream skill that `UPSTREAM.md` maps to a plugin skill, and + for every skill the user asked about, diff its folder between the two + commits: + + ```sh + gh api repos/mattpocock/skills/compare/... --jq '.files[] | select(.filename | startswith("skills/")) | .filename' + ``` + + Fetch the changed `SKILL.md` at both commits with + `gh api repos/mattpocock/skills/contents/?ref=` and read the + difference. + +4. Report, in this order: new skills with a one-line gist each, changed + skills with what changed and whether the plugin's version already covers + it, and removed skills. Say when nothing changed. + +5. Offer to update the recorded SHA and date in `UPSTREAM.md`. Do it only + on a yes. + +Nothing here touches the plugin's skills. Folding a change in is its own +task. diff --git a/.claude/skills/upstream-check/UPSTREAM.md b/.claude/skills/upstream-check/UPSTREAM.md new file mode 100644 index 0000000..b210af9 --- /dev/null +++ b/.claude/skills/upstream-check/UPSTREAM.md @@ -0,0 +1,27 @@ +# Upstream record + +The port was taken from `github.com/mattpocock/skills` at commit +`3cca18b368ae95cdbdebbff572ccafa662551015`, dated 2026-09-04, and checked +on 2026-09-14. + +| Upstream skill | Plugin skill | +|---|---| +| `productivity/grilling`, `productivity/grill-me`, `engineering/grill-with-docs` | `grill` | +| `engineering/implement`, `engineering/tdd`, `in-progress/implement-spec` | `build` | +| `engineering/to-spec`, `engineering/to-tickets` | `spec` | +| `in-progress/loop-me` | `routine` | +| `engineering/wayfinder` | `wayfinder` | +| `productivity/handoff`, `in-progress/claude-handoff` | `handoff` | +| `engineering/research` | `research` | +| `productivity/wait-what` | `wait-what` | +| `productivity/teach` | `teach` | +| `productivity/writing-for-agents` | `writing-for-agents` | +| `engineering/domain-modeling` | `domain-modeling` | +| `engineering/diagnosing-bugs` | `diagnosing-bugs` | +| `engineering/resolving-merge-conflicts` | `resolving-merge-conflicts` | + +Not ported: `code-review` (its check of the diff against the ask lives in +`build`), `codebase-design`, `prototype`, `triage`, `wizard`, `retro`, +`to-questionnaire`, `ask-matt`, `improve-codebase-architecture`, +`setup-matt-pocock-skills`, and everything under `misc/` and the +`writing-*` folders under `in-progress/`. diff --git a/README.md b/README.md index 629ae06..82e9b08 100644 --- a/README.md +++ b/README.md @@ -40,7 +40,7 @@ Paste this loader rather than the body of `scripts/cloud-bootstrap.sh`, so the l ```bash #!/bin/bash -# rev: 35 +# rev: 36 curl -fsSL https://raw.githubusercontent.com/gborges0727/claude-plugins/main/scripts/cloud-bootstrap.sh | bash || true exit 0 ``` @@ -71,18 +71,36 @@ Every PR bumps the `rev`, in the snippet above and in `scripts/cloud-bootstrap.s | `codex-delegate` | Skill | Handing a subtask to the Codex CLI on one of four rungs (Luna, Sol, Astra at medium, or the user-summoned Astra at xhigh), so ChatGPT-plan quota pays for it instead of Claude tokens. Each lane is a `codex exec` command run in the background, so a fan-out starts every lane at once. Needs the `codex` binary on PATH and signed in | | `model-routing-review` | Skill | Explicit invocation only. Re-derives the delegation ladder from today's catalog, prices, and scores, rewrites [docs/model-routing.md](docs/model-routing.md), and lists every file the ladder change touches | | `pair-debate` | Skill | Explicit invocation only. Puts Fable 5.1 and GPT-6 Astra, both at xhigh, in a room to work one hard problem as peers. `scripts/debate.py` runs the conversation in the background: blind drafts, an argued definition of done the user approves, then alternating turns in one shared worktree until both agree. The session reports events and reruns the agreed check at the end | +| `grill` | Skill | Explicit invocation only. Interviews the user in rounds, every askable question at once with a recommended answer, until no decision is open. Runs `domain-modeling` alongside when the repo has a `CONTEXT.md` | +| `build` | Skill | Explicit invocation only. Builds what the conversation (or a named spec or issue) asked for, one failing test then the smallest passing code, then a review by `opus-medium` against the ask and the repo's own review instructions. Commits only when told. A spec with tickets fans out one worktree per ticket | +| `spec` | Skill | Explicit invocation only. Writes the conversation up as a spec under `docs/specs/`, and splits it into end-to-end tickets only when the work will not fit one session | +| `routine` | Skill | Explicit invocation only. Grills the user into a spec for one recurring routine (trigger, check-in, brief), under `docs/routines/` | +| `wayfinder` | Skill | Explicit invocation only. For work too big for one session: a map file of open questions under `docs/specs//`, answered one session at a time | +| `handoff` | Skill | Explicit invocation only. Writes the conversation up for a fresh session under `docs/handoff/`, or into Bear, and starts the session when asked | +| `research` | Skill | Sends a background agent (`opus-medium`, or Codex Sol when Codex is on) to answer a question from primary sources into a cited file under `docs/research/` | +| `wait-what` | Skill | Explicit invocation only. Says the last message again in plain English, context first | +| `teach` | Skill | Explicit invocation only. Teaches a topic over many sessions from files in the current directory. Lessons publish as Claude artifacts | +| `writing-for-agents` | Skill | How to structure a skill, a `CLAUDE.md`, or any document an agent reads: what stays in the main file, what goes behind a pointer, and how each step says when it is done | +| `domain-modeling` | Skill | Keeps `CONTEXT.md` and `docs/adr/` sharp during a design discussion | +| `diagnosing-bugs` | Skill | A method for hard bugs: build a fast check that fails on this bug first, then shrink, list suspects, probe, fix with a regression test, clean up | +| `resolving-merge-conflicts` | Skill | Resolves an in-progress merge or rebase by reading why each side changed and keeping both intents | +| `reference/output-locations.md` | Reference | The one rule for where the document-writing skills put their files, read from the repo's `.claude/gborges-standard.json` with `docs/` as the default | | `add-to-git` | Command | Explicit invocation only, never model-triggered | +| `setup-repo` | Command | Writes `.claude/gborges-standard.json` at a repo's root, the per-repo `docs` folder and `tracker` choice the document-writing skills read. Wraps `scripts/setup-repo.sh` | | `setup` | Command | Writes `~/.claude/gborges-standard.json`, the per-machine switches for Fable access and Codex delegation, the `attribution` key in `~/.claude/settings.json` that stops Claude Code asking for AI attribution, and the Codex CLI's own model, subagent, and status line config under `~/.codex`. Wraps `scripts/setup.sh`, which does the same with no model turn | | `sonnet-medium` | Agent | Sonnet 5 at medium effort. Edits and runs with a command check in the brief, parallel copies of one such task, and fetching a named doc page | | `opus-medium` | Agent | Opus 5 at medium effort. The default, and the floor for anything that reads code to reach a conclusion | | `opus-xhigh` | Agent | Opus 5 at xhigh effort. One escalation step for a task that failed below it, and the stand-in for Fable on an account without it | | `fable-xhigh` | Agent | Fable 5.1 at xhigh effort. Runs only when the user's message names Fable. See [docs/subagent-routing.md](docs/subagent-routing.md) for the routing rule and the cost reasoning | | `frontend-design` | Dependency | From `claude-plugins-official` | -| `mattpocock-skills` | Dependency | From `claude-plugins-official` | | `context7` | Dependency | From `claude-plugins-official` | Dependencies resolve to the same identifiers a user-scope install uses, so a machine that already has them does not get a second copy. +### Skills adapted from Matt Pocock + +Thirteen of the skills (`grill`, `build`, `spec`, `routine`, `wayfinder`, `handoff`, `research`, `wait-what`, `teach`, `writing-for-agents`, `domain-modeling`, `diagnosing-bugs`, and `resolving-merge-conflicts`) started as skills in [mattpocock/skills](https://github.com/mattpocock/skills), MIT licensed, copyright Matt Pocock. They were rewritten here in plain English, merged where two did one job, and cut loose from any issue tracker. The commit they were taken from is recorded in `.claude/skills/upstream-check/UPSTREAM.md`, and the `upstream-check` skill in this repo lists what has changed there since. + ## Codex The same plugin folder installs into the Codex CLI. Codex reads its own @@ -152,7 +170,7 @@ own, and a file at it would fire twice. | Component | Codex | Notes | |---|---|---| -| The five skills | Yes | `SKILL.md` loads unchanged on both hosts | +| The skills | Yes | `SKILL.md` loads unchanged on both hosts | | `plain-english.md` | Yes, through a hook | Codex has no output styles. `codex-session-style.py` returns the style body as `SessionStart` context | | `strip-attribution.py` | Yes | Codex passes the same `tool_name` and `tool_input` fields and accepts the same `updatedInput` reply | | `flag-server-attribution.py` | Yes | Same `PostToolUse` contract | @@ -161,7 +179,7 @@ own, and a file at it would fire twice. | `route-spawns.py` | Yes, on a different event | Codex spawns subagents through a tool no `Agent` matcher catches, and fires `SubagentStart`. `--subagent-start` answers that event with the same rules block as context | | `add-to-git` | No | A Claude command. Codex loads skills, not commands | | The four agents | No | Claude Code agents. `codex-session-style.py` drops the style's Subagents section so Codex never gets sent to a name it cannot resolve | -| The three dependencies | No | `frontend-design`, `mattpocock-skills`, and `context7` live in a Claude marketplace that Codex cannot install from | +| The two dependencies | No | `frontend-design` and `context7` live in a Claude marketplace that Codex cannot install from | The style arrives as about 6,300 characters of session context, which is the one real cost of the Codex path. `WRITING_VOICE_STYLE=0` turns it off, and diff --git a/docs/codex-parity.md b/docs/codex-parity.md index 69f8ef6..69fb3a4 100644 --- a/docs/codex-parity.md +++ b/docs/codex-parity.md @@ -9,7 +9,7 @@ below ran on the home desktop that day. | Piece | The one copy | How Codex reads it | |---|---|---| -| Writing rules, hooks, five skills | this repo's `plugins/gborges-standard` | the `gborges` marketplace, installed with `codex plugin add` | +| Writing rules, hooks, skills | this repo's `plugins/gborges-standard` | the `gborges` marketplace, installed with `codex plugin add` | | Machine notes (SSH, colima, GitHub, Taildrop, 1Password) | `~/.claude/CLAUDE.md` | `~/.codex/AGENTS.md` is a symlink to it | | Model routing and status line | `scripts/setup.sh --codex-config on` | it writes `~/.codex/config.toml` and `~/.codex/agents` | | Per-machine switches (Fable, Codex delegation) | `~/.claude/gborges-standard.json` | the Codex hooks read the same file | @@ -124,24 +124,11 @@ ln -s ~/.claude/plugins/marketplaces/swiftui-agent-skill/swiftui-pro swiftui-pro Those three point at git checkouts that Claude Code updates in place, so the links never break. -The mattpocock plugin is different. Claude Code fetches it into a -versioned cache folder, and its manifest loads 25 of the 35 skill folders -it ships. This loop links the same 25: - -```sh -m=~/.claude/plugins/cache/claude-plugins-official/mattpocock-skills/1.2.3 -cd ~/.agents/skills -for s in $(python3 -c "import json;print(' '.join(json.load(open('$m/.claude-plugin/plugin.json'))['skills']))"); do - ln -s "$m/$s" "$(basename $s)" -done -``` - -Those 25 links break when Claude Code moves the plugin past 1.2.3, because -the cache path carries the version. Re-run the loop with the new version -when `ls -la ~/.agents/skills | grep mattpocock` shows dangling links. - -`use-railway` was already in `~/.agents/skills`, so the folder ends at 29 -skills. +The skills that used to come from the mattpocock plugin now ship inside +`gborges-standard`, so the Codex plugin install above covers them. A +machine that still has links named after those skills in `~/.agents/skills` +should delete them, or Codex lists two of each. `use-railway` was already +in `~/.agents/skills`, so the folder ends at four skills. ## Write the model setup and status line diff --git a/plugins/gborges-standard/.claude-plugin/plugin.json b/plugins/gborges-standard/.claude-plugin/plugin.json index 1e9a39e..8f56ab1 100644 --- a/plugins/gborges-standard/.claude-plugin/plugin.json +++ b/plugins/gborges-standard/.claude-plugin/plugin.json @@ -1,8 +1,8 @@ { "name": "gborges-standard", "displayName": "Gabe's standard set", - "description": "Standard Claude Code setup used across all of Gabe Borges' repos. Applies the Plain English output style everywhere, strips AI-attribution footers from GitHub writes, appends the writing rules to subagent prompts, keeps Codex calls off GPT-6 Astra unless the user named it, re-injects the writing rules reminder with every user message, and bundles the writing-voice, read-aloud-prep, bear-notes, codex-delegate, model-routing-review, and pair-debate skills, four routed subagents (sonnet-medium, opus-medium, opus-xhigh, and the user-summoned fable-xhigh), the add-to-git and setup commands, and the frontend-design, mattpocock-skills, and context7 plugins.", - "version": "1.29.0", + "description": "Standard Claude Code setup used across all of Gabe Borges' repos. Applies the Plain English output style everywhere, strips AI-attribution footers from GitHub writes, appends the writing rules to subagent prompts, keeps Codex calls off GPT-6 Astra unless the user named it, re-injects the writing rules reminder with every user message, and bundles the writing-voice, read-aloud-prep, bear-notes, codex-delegate, model-routing-review, and pair-debate skills, the grill, build, spec, routine, wayfinder, handoff, research, wait-what, teach, writing-for-agents, domain-modeling, diagnosing-bugs, and resolving-merge-conflicts skills rewritten in plain English from Matt Pocock's set, four routed subagents (sonnet-medium, opus-medium, opus-xhigh, and the user-summoned fable-xhigh), the add-to-git, setup, and setup-repo commands, and the frontend-design and context7 plugins.", + "version": "1.30.0", "author": { "name": "Gabe Borges", "email": "gbborges@proton.me" @@ -13,7 +13,6 @@ "hooks": "./hooks/claude-hooks.json", "dependencies": [ { "name": "frontend-design", "marketplace": "claude-plugins-official" }, - { "name": "mattpocock-skills", "marketplace": "claude-plugins-official" }, { "name": "context7", "marketplace": "claude-plugins-official" } ] } diff --git a/plugins/gborges-standard/.codex-plugin/plugin.json b/plugins/gborges-standard/.codex-plugin/plugin.json index 9085826..99e5269 100644 --- a/plugins/gborges-standard/.codex-plugin/plugin.json +++ b/plugins/gborges-standard/.codex-plugin/plugin.json @@ -1,7 +1,7 @@ { "name": "gborges-standard", - "version": "1.29.0", - "description": "Standard Codex setup used across all of Gabe Borges' repos. Injects the Plain English rules at session start and into every subagent, strips AI-attribution footers from GitHub writes, re-injects the writing rules reminder with every user message, and ships the writing-voice, read-aloud-prep, bear-notes, codex-delegate, model-routing-review, and pair-debate skills.", + "version": "1.30.0", + "description": "Standard Codex setup used across all of Gabe Borges' repos. Injects the Plain English rules at session start and into every subagent, strips AI-attribution footers from GitHub writes, re-injects the writing rules reminder with every user message, and ships the writing-voice, read-aloud-prep, bear-notes, codex-delegate, model-routing-review, and pair-debate skills, plus grill, build, spec, routine, wayfinder, handoff, research, wait-what, teach, writing-for-agents, domain-modeling, diagnosing-bugs, and resolving-merge-conflicts.", "author": { "name": "Gabe Borges", "email": "gbborges@proton.me", @@ -16,7 +16,7 @@ "interface": { "displayName": "Gabe's standard set", "shortDescription": "Plain English writing rules, hooks, and skills", - "longDescription": "Carries Gabe Borges' working conventions into Codex: the Plain English writing rules at session start and on every subagent spawn, AI-attribution stripping on GitHub writes, a per-turn reminder of the rules, and the writing-voice, read-aloud-prep, bear-notes, codex-delegate, model-routing-review, and pair-debate skills.", + "longDescription": "Carries Gabe Borges' working conventions into Codex: the Plain English writing rules at session start and on every subagent spawn, AI-attribution stripping on GitHub writes, a per-turn reminder of the rules, and the writing-voice, read-aloud-prep, bear-notes, codex-delegate, model-routing-review, and pair-debate skills, plus the thirteen skills rewritten from Matt Pocock's set (grill, build, spec, and the rest).", "developerName": "Gabe Borges", "category": "Productivity", "capabilities": ["Read", "Write"], diff --git a/plugins/gborges-standard/commands/setup-repo.md b/plugins/gborges-standard/commands/setup-repo.md new file mode 100644 index 0000000..6c72c1c --- /dev/null +++ b/plugins/gborges-standard/commands/setup-repo.md @@ -0,0 +1,32 @@ +--- +description: Ask where this repo keeps agent-written documents and whether specs become GitHub issues, then write .claude/gborges-standard.json at the repo root. +disable-model-invocation: true +--- + +# Setup repo + +## Step 1: Ask + +Call AskUserQuestion once with both questions in the one call. + +Ask which folder specs, research notes, handoffs, wayfinder maps, and +routine specs go under. Offer "docs" (the default) and let the user type +another folder name. Each skill writes under its own subfolder there, for +example `docs/specs/` and `docs/handoff/`. + +Ask whether specs and tickets should also become GitHub issues. Offer "Files +only" (the skills write files under the docs folder and touch GitHub only +when asked) and "GitHub issues" (the `spec` and `wayfinder` skills also open +one issue per spec, ticket, map, or question). Files only is the default. + +## Step 2: Write the file + +Turn the answers into a folder name and `files` or `github`, then run: + +```bash +bash "${CLAUDE_PLUGIN_ROOT}/scripts/setup-repo.sh" --docs --tracker +``` + +## Step 3: Report + +Print the `Wrote ...` line the script printed. Say nothing else. diff --git a/plugins/gborges-standard/output-styles/plain-english.md b/plugins/gborges-standard/output-styles/plain-english.md index 231bd23..fedd5b7 100644 --- a/plugins/gborges-standard/output-styles/plain-english.md +++ b/plugins/gborges-standard/output-styles/plain-english.md @@ -98,7 +98,8 @@ Spend words on clarity, save them on scope. would have flagged, then trail the rest in a plain mention. "The company page is still open. I also filed five smaller questions." - Never close a claim with a motto that restates it ("that distinction - matters", "green is the gate, not a suggestion"). Never introduce a + matters", "green is the gate, not a suggestion", "the file is the + signal"). Never introduce a restatement ("in other words", "put differently"). Say the claim once. - Repeated rhythm is not content. Fragments echoing one shape ("No gimmicks. No hacks. No filler.") and ladders that escalate ("Five became fifty. diff --git a/plugins/gborges-standard/reference/output-locations.md b/plugins/gborges-standard/reference/output-locations.md new file mode 100644 index 0000000..749160a --- /dev/null +++ b/plugins/gborges-standard/reference/output-locations.md @@ -0,0 +1,50 @@ +# Where skills write their output + +The skills in this plugin that produce a document (`spec`, `wayfinder`, +`research`, `handoff`, `routine`) all decide where it goes the same way. +This file is that rule. Each skill points here instead of restating it. + +## The repo file + +A repo can carry `.claude/gborges-standard.json` at its root, committed like +any other file. The `/setup-repo` command writes it. Two keys matter here: + +| Key | Values | Default | What it controls | +|---|---|---|---| +| `docs` | a folder name | `docs` | the folder every document goes under | +| `tracker` | `files` or `github` | `files` | whether specs and tickets also become GitHub issues | + +A missing file, a missing key, or a value that is not a string means the +default. The file with the same name under `~/.claude/` holds machine +settings (`fable`, `codex`) and never these keys. + +## Defaults + +With `docs` set to `docs`, the skills write here. `` is a short +kebab-case name for the piece of work, chosen from the ask. + +| Document | Path | +|---|---| +| a spec | `docs/specs/.md` | +| the tickets for a spec | `docs/specs//tickets/NN-.md`, numbered from `01` | +| a wayfinder map | `docs/specs//map.md`, with one file per open question at `docs/specs//questions/NN-.md` | +| a research note | `docs/research/.md` | +| a handoff | `docs/handoff/.md` | +| a routine spec | `docs/routines/.md` | + +Create a folder the first time something goes in it. + +## When the user names a place + +Anything the user says in the invocation wins over the file and the +defaults. "Put it in Bear" means the `bear-notes` skill. A path means that +path. "File it" or "open an issue" means a GitHub issue through `gh`, whatever +`tracker` says. + +## GitHub issues + +When `tracker` is `github`, `spec` and `wayfinder` also open one issue per +spec, ticket, map, or question, in dependency order so a ticket can name the +issue that blocks it. The file under `docs/` is still written and is the +copy the next session reads. When `tracker` is `files`, nothing touches +GitHub unless the user asks. diff --git a/plugins/gborges-standard/scripts/setup-repo.sh b/plugins/gborges-standard/scripts/setup-repo.sh new file mode 100755 index 0000000..55f7e17 --- /dev/null +++ b/plugins/gborges-standard/scripts/setup-repo.sh @@ -0,0 +1,138 @@ +#!/bin/bash +# Writes the per-repo config file the document-writing skills read, +# .claude/gborges-standard.json at the repo root. Two keys live there. +# 'docs' names the folder that specs, tickets, maps, research notes, +# handoffs, and routine specs go under. 'tracker' says whether specs and +# tickets also become GitHub issues ('github') or stay as files ('files'). +# reference/output-locations.md in the plugin lists what each skill writes +# and where, and what happens when the file is missing. +# +# Run it with both flags and it writes the file without asking anything. +# Leave a flag out and it asks, or falls back to the default when nothing +# is there to answer. + +set -u + +usage() { + cat <<'EOF' +Write .claude/gborges-standard.json at the repo root, the per-repo config the +document-writing skills read. + +Usage: setup-repo.sh [--docs FOLDER] [--tracker files|github] + + --docs FOLDER Folder that specs, research notes, handoffs, maps, and + routine specs go under. Default docs. + --tracker files|github Whether specs and tickets also become GitHub issues. + Default files. + -h, --help Print this text. + +The file goes at the top of the git checkout that contains the working +directory, or in the working directory when it is not inside a git checkout. +Keys already in the file other than 'docs' and 'tracker' are kept. +EOF +} + +docs="" +tracker="" + +while [ $# -gt 0 ]; do + case "$1" in + --docs) + if [ $# -lt 2 ]; then + printf 'setup-repo.sh: --docs needs a value\n' >&2 + usage >&2 + exit 2 + fi + docs="$2" + shift 2 + ;; + --tracker) + if [ $# -lt 2 ]; then + printf 'setup-repo.sh: --tracker needs a value\n' >&2 + usage >&2 + exit 2 + fi + case "$2" in + files|github) tracker="$2" ;; + *) + printf 'setup-repo.sh: --tracker takes files or github, got %s\n' "$2" >&2 + exit 2 + ;; + esac + shift 2 + ;; + -h|--help) + usage + exit 0 + ;; + *) + printf 'setup-repo.sh: unknown argument %s\n' "$1" >&2 + usage >&2 + exit 2 + ;; + esac +done + +# Ask for a value the flags did not supply. With no terminal on stdin there is +# nobody to ask, so take the default and print which one was taken. +ask() { + local name="$1" default="$2" question="$3" answer="" + if [ -t 0 ]; then + printf '%s [%s]: ' "$question" "$default" >&2 + read -r answer || answer="" + if [ -z "$answer" ]; then + printf '%s' "$default" + else + printf '%s' "$answer" + fi + else + printf 'setup-repo.sh: no terminal to ask on, using %s=%s\n' "$name" "$default" >&2 + printf '%s' "$default" + fi +} + +if [ -z "$docs" ]; then + docs=$(ask docs docs 'Folder for specs, research notes, handoffs, and routine specs') || exit 2 +fi +if [ -z "$tracker" ]; then + tracker=$(ask tracker files 'Also open GitHub issues for specs and tickets? (files/github)') || exit 2 + case "$tracker" in + files|github) ;; + *) + printf 'setup-repo.sh: tracker takes files or github, got %s\n' "$tracker" >&2 + exit 2 + ;; + esac +fi + +root=$(git rev-parse --show-toplevel 2>/dev/null) || root="$PWD" +config_dir="${root}/.claude" +config_file="${config_dir}/gborges-standard.json" + +mkdir -p "$config_dir" || exit 1 + +# Every other key in the file stays as it was. A file that is missing or is +# not an object starts over as an empty object. +DOCS="$docs" TRACKER="$tracker" CONFIG_FILE="$config_file" python3 - <<'PY' || exit 1 +import json +import os + +path = os.environ["CONFIG_FILE"] +data = {} +try: + with open(path, encoding="utf-8") as handle: + loaded = json.load(handle) + if isinstance(loaded, dict): + data = loaded +except (OSError, ValueError): + pass + +data["docs"] = os.environ["DOCS"] +data["tracker"] = os.environ["TRACKER"] + +with open(path, "w", encoding="utf-8") as handle: + json.dump(data, handle, indent=2) + handle.write("\n") +PY + +printf 'Wrote %s: docs %s, tracker %s\n' "$config_file" "$docs" "$tracker" diff --git a/plugins/gborges-standard/skills/build/SKILL.md b/plugins/gborges-standard/skills/build/SKILL.md new file mode 100644 index 0000000..39173d8 --- /dev/null +++ b/plugins/gborges-standard/skills/build/SKILL.md @@ -0,0 +1,98 @@ +--- +name: build +description: Build the work under discussion test-first, one failing test at a time, then review the diff against the ask. Explicit invocation only. +disable-model-invocation: true +--- + +# Build + +Build what was asked, test-first, and review the result before handing it +back. + +## What to build + +The ask is the conversation so far, unless the invocation names something +else. "/build let's do it" after a discussion means build what was +discussed. A path means that spec file. An issue number or URL means that +issue, with its comments. A spec whose folder has a `tickets/` subfolder +turns on the [many tickets](#many-tickets) mode below. + +Read `CONTEXT.md` when the repo has one, so names in tests and code match +the project's words. Read the decision records under `docs/adr/` for the +area you are changing and keep to them. + +## Where the tests go + +Before the first test, decide which public functions, endpoints, or +commands the tests will call. Prefer ones that exist. Prefer the fewest that +cover the behaviour, and the ones furthest from the implementation, so a +test still passes when the code underneath is rewritten. Say the list in one +line and start. The user can redirect you afterwards if the list was wrong. + +A test calls the public interface and checks what comes back or what the +next public call can see. It never reads a private field, calls a private +method, or queries the database to check what a function did. The rules and +examples are in [tests.md](tests.md). Mock only at the edges of the system, +as in [mocking.md](mocking.md). + +## The loop + +1. Write one test that fails because the behaviour is missing. +2. Write the smallest code that makes it pass. Add nothing for a test you + have not written yet. +3. Run the type checker and that one test file. +4. Repeat with the next behaviour. + +Write tests and code in turns, never all the tests first. A test written +before the code that passes it teaches you the next test. Twenty tests +written up front test a shape you imagined. + +Refactoring happens after the loop, in the review step, never inside a +cycle. + +When every behaviour has its test, run the full suite once. + +## Review + +Dispatch a review to `gborges-standard:opus-medium`. The brief carries: + +- the diff to review, as `git diff ...HEAD` plus any uncommitted + changes, and the commit list +- the ask, quoted or by path +- the repo's own review instructions, when it has any: a `CODE_REVIEW.md`, + a `CONTRIBUTING.md`, a review section in `CLAUDE.md`, or a pull request + template. The repo's instructions win wherever they and this skill + differ. +- this skill's own check, on top of the repo's: list what the ask wanted + that the diff does not do, what the diff does that the ask did not want, + and what looks done but wrong. Quote the ask for each finding. + +Fix what the review finds. Run the full suite again when anything changed. + +## Finish + +Leave the changes in the working tree and describe them in the reply. Commit +only when the invocation said to ("build and commit"). Never push. + +## Many tickets + +When the spec has tickets, each ticket is a thin slice through the whole app +that works end to end, and each names the tickets that must finish before +it can start. + +1. Read the spec and every ticket. Note which tickets have no unfinished + blockers. +2. Create a branch for the whole spec. +3. For each ticket that can start, spawn a `gborges-standard:opus-medium` + subagent in its own git worktree on its own branch. The brief carries + the ticket file's path, the spec's path, this skill's loop and test + rules, and the command that runs that ticket's tests. Start every + startable ticket at once. +4. When a subagent finishes, merge its branch into the spec branch. Then + start any ticket that merge unblocked. +5. A ticket whose check failed escalates once, to + `gborges-standard:opus-xhigh`, with the same brief plus the exact + failure output. Reread the brief for a mistake first. After a second + failure, stop and report the ticket to the user. +6. When every ticket is merged, run the review step on the whole spec + branch, fix the findings, and delete the worktrees. diff --git a/plugins/gborges-standard/skills/build/agents/openai.yaml b/plugins/gborges-standard/skills/build/agents/openai.yaml new file mode 100644 index 0000000..5b1f887 --- /dev/null +++ b/plugins/gborges-standard/skills/build/agents/openai.yaml @@ -0,0 +1,2 @@ +policy: + allow_implicit_invocation: false diff --git a/plugins/gborges-standard/skills/build/mocking.md b/plugins/gborges-standard/skills/build/mocking.md new file mode 100644 index 0000000..e149ffc --- /dev/null +++ b/plugins/gborges-standard/skills/build/mocking.md @@ -0,0 +1,49 @@ +# When to mock + +Mock only where the system meets something you do not control: + +- a third-party API (payments, email) +- the database, when no local test database is practical +- the clock and random numbers +- the filesystem, when a real temp directory is not practical + +Never mock your own modules, classes, or functions. A test that mocks an +internal collaborator checks how the code is wired, not what it does. + +## Making the edges easy to mock + +**Pass external dependencies in.** A function that builds its own client +cannot be tested without the real service. + +```typescript +// Easy to mock +function processPayment(order, paymentClient) { + return paymentClient.charge(order.total); +} + +// Hard to mock +function processPayment(order) { + const client = new StripeClient(process.env.STRIPE_KEY); + return client.charge(order.total); +} +``` + +**One function per external operation.** A single generic fetcher forces +every mock to branch on its arguments. + +```typescript +// GOOD: each function is mocked on its own +const api = { + getUser: (id) => fetch(`/users/${id}`), + getOrders: (userId) => fetch(`/users/${userId}/orders`), + createOrder: (data) => fetch('/orders', { method: 'POST', body: data }), +}; + +// BAD: one mock has to know every endpoint +const api = { + fetch: (endpoint, options) => fetch(endpoint, options), +}; +``` + +With one function per operation, each mock returns one shape, a test shows +which endpoints it touches, and each function gets its own types. diff --git a/plugins/gborges-standard/skills/build/tests.md b/plugins/gborges-standard/skills/build/tests.md new file mode 100644 index 0000000..535dd71 --- /dev/null +++ b/plugins/gborges-standard/skills/build/tests.md @@ -0,0 +1,78 @@ +# Good and bad tests + +## A good test + +A good test calls the public interface and checks the result a caller would +see. It reads like a statement of what the code can do, and it still passes +when the code underneath is rewritten. + +```typescript +// GOOD: checks what a caller sees +test("user can checkout with valid cart", async () => { + const cart = createCart(); + cart.add(product); + const result = await checkout(cart, paymentMethod); + expect(result.status).toBe("confirmed"); +}); +``` + +- The name says what the code does for its caller. +- Only public functions are called. +- One logical check per test. +- The expected value is a known literal, not a recomputation. + +## A test tied to the implementation + +```typescript +// BAD: checks how checkout works inside +test("checkout calls paymentService.process", async () => { + const mockPayment = jest.mock(paymentService); + await checkout(cart, payment); + expect(mockPayment.process).toHaveBeenCalledWith(cart.total); +}); +``` + +The signs are a mocked internal collaborator, a private method under test, a +check on how many times something was called, a name that describes the +steps instead of the outcome, and a test that breaks on a refactor that +changed no behaviour. + +## A test that bypasses the interface + +```typescript +// BAD: reads the database to check what createUser did +test("createUser saves to database", async () => { + await createUser({ name: "Alice" }); + const row = await db.query("SELECT * FROM users WHERE name = ?", ["Alice"]); + expect(row).toBeDefined(); +}); + +// GOOD: checks through the next public call +test("createUser makes user retrievable", async () => { + const user = await createUser({ name: "Alice" }); + const retrieved = await getUser(user.id); + expect(retrieved.name).toBe("Alice"); +}); +``` + +## A test that passes by construction + +When the expected value is computed the same way the code computes it, the +test can never disagree with the code. + +```typescript +// BAD: the expected value repeats the implementation +test("calculateTotal sums line items", () => { + const items = [{ price: 10 }, { price: 5 }]; + const expected = items.reduce((sum, i) => sum + i.price, 0); + expect(calculateTotal(items)).toBe(expected); +}); + +// GOOD: the expected value is a known literal +test("calculateTotal sums line items", () => { + expect(calculateTotal([{ price: 10 }, { price: 5 }])).toBe(15); +}); +``` + +The expected value comes from somewhere the code cannot influence: a +worked example, the spec, a number you checked by hand. diff --git a/plugins/gborges-standard/skills/diagnosing-bugs/SKILL.md b/plugins/gborges-standard/skills/diagnosing-bugs/SKILL.md new file mode 100644 index 0000000..5b8ec6b --- /dev/null +++ b/plugins/gborges-standard/skills/diagnosing-bugs/SKILL.md @@ -0,0 +1,166 @@ +--- +name: diagnosing-bugs +description: Find the cause of a hard bug or a slow path by first building a fast check that fails on this bug, then testing hypotheses against it. Use when the user says diagnose or debug, or reports something broken, throwing, failing, or slow. +--- + +# Diagnosing bugs + +A method for a hard bug. Skip a phase only when you can say why. + +Read `CONTEXT.md` when the repo has one, so you know the names of the parts +you are looking at, and read the decision records under `docs/adr/` for the +area. + +## Redact first + +This method has you show commands, outputs, and captured files. Replace +every secret with `` before showing anything. Build the check +around environment variables so the credential stays in the environment +and out of what you show. A captured request carries auth headers, so +quote only the lines that matter. When the redacted output is not enough +to diagnose the bug, say so and ask the user. + +## Phase 1: Build the check + +This phase is the method. Everything after it is routine. A check is one +command that fails on this bug and passes once it is fixed. With one, +bisection, hypotheses, and logging all have something to run against. +Without one, reading code produces theories and no answers. + +Spend more effort here than feels reasonable. Try each of these, roughly in +order, until one fails on the bug: + +1. A failing test, at whatever level reaches the bug: unit, integration, + end to end. +2. A curl or HTTP script against a running dev server. +3. A CLI run with a fixture input, diffing stdout against a known-good + output. +4. A headless browser script (Playwright, Puppeteer) that drives the UI and + checks the DOM, console, or network. +5. A replay. Save a real request, payload, or event log to disk and push it + through the code on its own. +6. A throwaway harness. Start the smallest part of the system that reaches + the bug (one service, dependencies mocked) and call the one function. +7. A random-input loop, when the output is "sometimes wrong". Run 1,000 + random inputs and look for the failure. +8. A bisection script, when the bug appeared between two known states + (commit, dataset, version). Script "start at state X, check, repeat" so + `git bisect run` can drive it. +9. A differential run. Push one input through the old and new version, or + two configs, and diff the outputs. +10. A script that drives a human, as the last resort. When someone must + click, copy `scripts/hitl-loop.template.sh` from this skill's folder, + edit its steps, and run it. The user follows the prompts and the script + prints what they saw for you to read. + +### Make it fast and sharp + +Once you have any check, improve it. Make it faster by caching setup, +skipping unrelated startup, and narrowing the test. Make it check the +exact symptom, not "did not crash". Make it give the same answer every run: pin the clock, +seed random numbers, isolate the filesystem, freeze the network. A flaky +30-second check is barely better than none. A 2-second check that always +agrees with itself is what you want. + +### A bug that only sometimes happens + +Aim for a higher failure rate, not a clean reproduction. Loop the trigger +100 times, run in parallel, add load, narrow the timing window, inject +sleeps. A bug that fails one run in two can be diagnosed. One in a hundred +cannot, so keep raising the rate. + +### When no check can be built + +Stop and say so. List what you tried. Ask the user for access to the +environment where it happens, or a redacted capture (a HAR file, a log +dump, a core dump, a screen recording with timestamps), or permission to +add temporary logging in production. Do not move on to hypotheses without +a check. + +### Done when + +You can name one command that you have already run at least once (show the +command and its redacted output), and it: + +- [ ] runs the code path the bug is in and checks the user's exact symptom, + so it fails on this bug and passes once fixed +- [ ] gives the same answer every run (for a flaky bug, fails at a pinned, + high rate) +- [ ] finishes in seconds +- [ ] runs without a human, except through the script in item 10 + +When you catch yourself reading code to form a theory before this command +exists, stop. That is the mistake this method prevents. + +## Phase 2: Reproduce and shrink + +Run the check and watch it fail. Confirm three things. It fails the way the +user described, not a nearby failure. It fails on repeated runs, or at a +high enough rate. You have captured the exact symptom (the error text, the +wrong output, the timing) so you can later confirm the fix addressed it. + +Then shrink the scenario to the smallest one that still fails. Remove +inputs, callers, config, data, and steps one at a time, rerunning the check +after each cut, and keep only what the failure needs. A small scenario +leaves fewer parts to suspect in Phase 3 and becomes the regression test in +Phase 5. Done when removing any one remaining part makes the check pass. + +## Phase 3: List the suspects + +Write three to five ranked hypotheses before testing any. One hypothesis +anchors you on the first plausible idea. + +Each one must make a prediction: "If X is the cause, then changing Y makes +the bug go away" or "makes it worse". A hypothesis with no prediction is a +hunch. Sharpen it or drop it. + +Show the ranked list to the user before testing. They often know something +that reorders it at once ("we deployed a change to number 3 yesterday"). +Do not wait for them. Continue with your ranking if they are away. + +## Phase 4: Probe + +Each probe tests one prediction from Phase 3. Change one thing at a time. + +Use a debugger or a REPL when the environment has one. One breakpoint +beats ten log lines. Otherwise add a log at the points that tell two +hypotheses apart. Never log everything and grep. + +Tag every debug log with one unique prefix, such as `[DEBUG-a4f2]`, so +cleanup is one grep. Untagged logs get left behind. + +For a performance regression, logs are the wrong tool. Measure a baseline +first (a timing harness, `performance.now()`, a profiler, a query plan), +then bisect. Measure before you change anything. + +## Phase 5: Fix, with a regression test first + +Write the regression test before the fix, when there is a place to put it +that reaches the real bug. The test must reproduce the pattern the bug +occurs in at its real call site. When the only place a test can go is too +shallow (one caller when the bug needs several, a unit test that cannot +rebuild the chain that triggers it), a test there gives false confidence. + +When no such place exists, that is a finding. Write it down. The code's +shape is stopping the bug from being pinned, and the cleanup phase should +note it. + +When such a place exists: + +1. Turn the shrunk scenario into a failing test there. +2. Watch it fail. +3. Apply the fix. +4. Watch it pass. +5. Rerun the Phase 1 check against the original, unshrunk scenario. + +## Phase 6: Clean up + +Before saying it is done: + +- [ ] the Phase 1 check passes on the original scenario +- [ ] the regression test passes, or the note says why there is none +- [ ] every `[DEBUG-...]` line is gone (grep the prefix) +- [ ] every throwaway harness is deleted or moved somewhere marked as + debug +- [ ] the hypothesis that turned out right is stated in the commit or PR + message, so the next person learns it diff --git a/plugins/gborges-standard/skills/diagnosing-bugs/scripts/hitl-loop.template.sh b/plugins/gborges-standard/skills/diagnosing-bugs/scripts/hitl-loop.template.sh new file mode 100755 index 0000000..2431984 --- /dev/null +++ b/plugins/gborges-standard/skills/diagnosing-bugs/scripts/hitl-loop.template.sh @@ -0,0 +1,44 @@ +#!/usr/bin/env bash +# Human-in-the-loop reproduction loop. +# Copy this file, edit the steps below, and run it. +# The agent runs the script; the user follows prompts in their terminal. +# +# Usage: +# bash hitl-loop.template.sh +# +# Two helpers: +# step "" → show instruction, wait for Enter +# capture VAR "" → show question, read response into VAR +# +# At the end, captured values are printed as KEY=VALUE for the agent to parse. +# +# `capture` prints its value back to the terminal, where the agent reads it, +# so capture observations, and leave signing in to the user as a `step`. + +set -euo pipefail + +step() { + printf '\n>>> %s\n' "$1" + read -r -p " [Enter when done] " _ +} + +capture() { + local var="$1" question="$2" answer + printf '\n>>> %s\n' "$question" + read -r -p " > " answer + printf -v "$var" '%s' "$answer" +} + +# --- edit below --------------------------------------------------------- + +step "Open the app at http://localhost:3000 and sign in." + +capture ERRORED "Click the 'Export' button. Did it throw an error? (y/n)" + +capture ERROR_MSG "Paste the error message (or 'none'):" + +# --- edit above --------------------------------------------------------- + +printf '\n--- Captured ---\n' +printf 'ERRORED=%s\n' "$ERRORED" +printf 'ERROR_MSG=%s\n' "$ERROR_MSG" diff --git a/plugins/gborges-standard/skills/domain-modeling/ADR-FORMAT.md b/plugins/gborges-standard/skills/domain-modeling/ADR-FORMAT.md new file mode 100644 index 0000000..c62d067 --- /dev/null +++ b/plugins/gborges-standard/skills/domain-modeling/ADR-FORMAT.md @@ -0,0 +1,61 @@ +# Decision record format + +A decision record (ADR) lives in `docs/adr/` and is numbered in order: +`0001-slug.md`, `0002-slug.md`. Create the folder when the first record is +written. To number a new one, find the highest number in the folder and add +one. + +## Template + +```md +# {Short title of the decision} + +{One to three sentences: the situation, what was decided, and why.} +``` + +A record can be one paragraph. Its value is that the decision and its reason +are written down, not that every section is filled. + +## Optional sections + +Add one only when it says something the paragraph does not. + +- **Status** in frontmatter (`proposed`, `accepted`, `deprecated`, + `superseded by ADR-NNNN`), for a decision that gets revisited. +- **Considered options**, when the rejected alternatives are worth + remembering. +- **Consequences**, when the decision has downstream effects a reader would + not expect. + +## When to write one + +All three must hold. + +1. **Hard to reverse.** Changing your mind later costs real work. +2. **Surprising without context.** A future reader would look at the code + and wonder why it was done this way. +3. **A real trade-off.** There were alternatives and one was chosen for + specific reasons. + +An easy-to-reverse decision needs no record, since you would reverse it. +An unsurprising one raises no question. A decision with no alternative has +nothing to record beyond "we did the obvious thing". + +## What qualifies + +- **How the system is put together.** "One repo for every package." + "Writes are event-sourced and reads come from a Postgres projection." +- **How parts talk to each other.** "Ordering and Billing exchange events, + never synchronous HTTP calls." +- **Technology choices that would take a quarter to swap.** The database, + the message bus, the auth provider, the deployment target. Not every + library. +- **Ownership and scope.** "Customer data belongs to the Customer area. + Other areas hold only the id." A decision about what a part will not do + is as useful as one about what it will. +- **A deliberate departure from the obvious.** "Hand-written SQL instead of + an ORM, because X." The record stops the next engineer from "fixing" it. +- **A constraint the code does not show.** "No AWS, for compliance." "Under + 200 ms per response, per the partner contract." +- **A rejected alternative when the reason is not obvious.** Record why REST + won over GraphQL, or someone will propose GraphQL again in six months. diff --git a/plugins/gborges-standard/skills/domain-modeling/CONTEXT-FORMAT.md b/plugins/gborges-standard/skills/domain-modeling/CONTEXT-FORMAT.md new file mode 100644 index 0000000..cbdba2b --- /dev/null +++ b/plugins/gborges-standard/skills/domain-modeling/CONTEXT-FORMAT.md @@ -0,0 +1,64 @@ +# CONTEXT.md format + +## Structure + +```md +# {Context name} + +{One or two sentences on what this area of the system is and why it exists.} + +## Language + +**Order**: +{A one or two sentence definition.} +_Avoid_: Purchase, transaction + +**Invoice**: +A request for payment sent to a customer after delivery. +_Avoid_: Bill, payment request + +**Customer**: +A person or organization that places orders. +_Avoid_: Client, buyer, account +``` + +## Rules + +- **Pick one word.** When several words exist for one concept, choose the + best and list the others under `_Avoid_`. +- **Keep definitions short.** One or two sentences. Say what the thing is, + not what it does. +- **Only this project's terms belong.** A general programming concept + (timeout, error type, retry) stays out even when the project uses it + everywhere. Before adding a term, ask whether it is unique to this + project. +- **Group under subheadings** when the terms fall into natural clusters. A + flat list is fine when they do not. + +## One glossary or several + +Most repos keep one `CONTEXT.md` at the root. + +A repo whose parts speak different languages (an ordering system and a +billing system that mean different things by "account") keeps a +`CONTEXT-MAP.md` at the root and one `CONTEXT.md` per part, next to that +part's code. The map lists the parts, where each glossary lives, and how the +parts talk to each other: + +```md +# Context map + +## Contexts + +- [Ordering](./src/ordering/CONTEXT.md): receives and tracks customer orders +- [Billing](./src/billing/CONTEXT.md): generates invoices and processes payments + +## Relationships + +- **Ordering → Billing**: Ordering emits `OrderPlaced` events and Billing reads them to raise invoices +- **Ordering ↔ Billing**: both use the shared `CustomerId` and `Money` types +``` + +Read the map when it exists to find the glossary for the current topic. With +only a root `CONTEXT.md`, that is the glossary. With neither, create a root +`CONTEXT.md` when the first term settles. diff --git a/plugins/gborges-standard/skills/domain-modeling/SKILL.md b/plugins/gborges-standard/skills/domain-modeling/SKILL.md new file mode 100644 index 0000000..ac1d5fc --- /dev/null +++ b/plugins/gborges-standard/skills/domain-modeling/SKILL.md @@ -0,0 +1,65 @@ +--- +name: domain-modeling +description: Keep the project's glossary (CONTEXT.md) and decision records (docs/adr/) sharp while a design is discussed. Use when a term is fuzzy or conflicts with the glossary, when writing or editing CONTEXT.md or CONTEXT-MAP.md, or when a decision deserves an ADR. +--- + +# Domain modeling + +Sharpen the words the project uses while the design is being discussed, and +write them down the moment they settle. Reading `CONTEXT.md` for the right +word is something every skill does on its own. This skill is for changing +the glossary, or recording a decision. + +## Files + +Most repos have one glossary at the root and one folder of decision +records: + +``` +/ +├── CONTEXT.md +├── docs/ +│ └── adr/ +│ ├── 0001-event-sourced-orders.md +│ └── 0002-postgres-for-write-model.md +└── src/ +``` + +A repo with several distinct areas of language has a `CONTEXT-MAP.md` at +the root that lists them, and one `CONTEXT.md` per area next to that area's +code. The map's format is in [CONTEXT-FORMAT.md](CONTEXT-FORMAT.md). Read +the map first when it exists, then the glossary for the area the current +topic belongs to. Ask when the area is unclear. + +Create a file only when there is something to write in it. The first +settled term creates `CONTEXT.md`. The first decision record creates +`docs/adr/`. + +## During the session + +**Challenge a term against the glossary.** When the user uses a word in a +way that conflicts with its entry, say so at once: "The glossary defines +cancellation as X, and you seem to mean Y. Which is it?" + +**Sharpen a fuzzy term.** When a word could mean two things, propose the +precise one: "By account, do you mean the Customer or the User? Those are +different things here." + +**Test the model with a concrete case.** When two concepts are being +related, invent a specific scenario that sits on the line between them and +ask the user which side it falls on. + +**Check the code.** When the user states how something works, read the code +that does it. When the code disagrees, say so: "The code cancels whole +Orders, and you said partial cancellation is possible. Which is right?" + +**Write the glossary entry at once.** When a term settles, update +`CONTEXT.md` right then, in the format in +[CONTEXT-FORMAT.md](CONTEXT-FORMAT.md). Do not batch entries for later. +The glossary holds definitions only. No implementation detail, no spec +text, no decision belongs in it. + +**Offer a decision record only when all three hold.** The decision is hard +to reverse, a future reader would wonder why it was made, and it came from +a real choice between alternatives. When any one is missing, skip the +record. The format and the tests are in [ADR-FORMAT.md](ADR-FORMAT.md). diff --git a/plugins/gborges-standard/skills/grill/SKILL.md b/plugins/gborges-standard/skills/grill/SKILL.md new file mode 100644 index 0000000..e9421c0 --- /dev/null +++ b/plugins/gborges-standard/skills/grill/SKILL.md @@ -0,0 +1,55 @@ +--- +name: grill +description: Interview the user in rounds until a plan, design, or idea has no open decision left. Explicit invocation only. +disable-model-invocation: true +--- + +# Grill + +Interview the user until the two of you agree on every decision the plan +needs. Every decision leads to further decisions that depend on it, so the +work is a tree. You walk it in rounds. + +## Rounds + +A round holds every question you can ask now without guessing at an answer +you have not heard yet. Ask all of them in one message, numbered, each with +your recommended answer. Then stop and wait for the user. + +Format each question like this: + +``` +❓ **Q1** - ****: + +➡️ +``` + +The user's answers settle some decisions and open the ones that depended on +them. Work out the new set of askable questions and send the next round. A +question whose answer depends on another question still open in this round +waits for a later round. + +## Facts are your job + +The user decides. You find facts. When a question needs a fact from the +filesystem, a tool, or the web, get it yourself before asking, or send a +subagent for it. A doc page or a single file lookup goes to +`gborges-standard:sonnet-medium`. Anything that reads code to reach a +conclusion goes to `gborges-standard:opus-medium`. A broad search goes to +`Explore`. Do not stop the round for it. Ask every question that does not +depend on the fact now, and ask the ones that do once the fact is back. + +## Glossary and decisions + +When the repo has a `CONTEXT.md` at its root, also run the +`domain-modeling` skill through the session. It challenges a term the user +uses against the glossary, writes the glossary entry the moment a term +settles, and offers a decision record when a decision is hard to reverse. +Skip this when the repo has no `CONTEXT.md` and the user has not asked for +one. + +## Done + +The session ends when no question remains, with every branch of the tree +visited and nothing assumed in silence. Then write the whole understanding +out in one message for the user to confirm. Do not act on it until they do. diff --git a/plugins/gborges-standard/skills/grill/agents/openai.yaml b/plugins/gborges-standard/skills/grill/agents/openai.yaml new file mode 100644 index 0000000..5b1f887 --- /dev/null +++ b/plugins/gborges-standard/skills/grill/agents/openai.yaml @@ -0,0 +1,2 @@ +policy: + allow_implicit_invocation: false diff --git a/plugins/gborges-standard/skills/handoff/SKILL.md b/plugins/gborges-standard/skills/handoff/SKILL.md new file mode 100644 index 0000000..a01febe --- /dev/null +++ b/plugins/gborges-standard/skills/handoff/SKILL.md @@ -0,0 +1,54 @@ +--- +name: handoff +description: Write the conversation up so a fresh session can continue the work, and start that session when asked. Explicit invocation only. +argument-hint: "What the next session is for, and where to put the handoff" +disable-model-invocation: true +--- + +# Handoff + +Write a handoff document that lets a fresh session, with none of this +conversation, pick up the work. + +## Where it goes + +The handoff path in the plugin's +[output-locations.md](../../reference/output-locations.md), which is +`docs/handoff/.md` unless the repo's `.claude/gborges-standard.json` +names another docs folder. "In Bear" means the `bear-notes` skill. A path +in the argument means that path. Never the OS temp directory. + +## What it says + +Open with today's date and the one-line goal of the next session. When the +argument says what the next session is for, write the handoff for that. + +Then, in whatever order reads best: + +- where the work stands now, and what is left +- the decisions made, each with its reason +- what was tried and did not work, so it is not tried again +- the commands that check the work, quoted exact +- the skills the next session should use, by name +- open questions only the user can answer + +Point at what already exists instead of restating it: a spec, a plan, a +decision record, an issue, a commit, a diff. Give the path or URL and one +line on what it holds. + +Replace every secret (an API key, a password, a token, a personal detail) +with ``. + +Run the `writing-voice` passes on the file before you finish. + +## Starting the next session + +When the argument asks for it ("and start it", "kick it off"), launch a +background session with the handoff as its prompt after writing the file: + +```sh +claude --bg --name "" "$(cat )" +``` + +It starts in the current directory and returns at once. The user manages +it with `claude agents`. Without that ask, print the path and stop. diff --git a/plugins/gborges-standard/skills/handoff/agents/openai.yaml b/plugins/gborges-standard/skills/handoff/agents/openai.yaml new file mode 100644 index 0000000..5b1f887 --- /dev/null +++ b/plugins/gborges-standard/skills/handoff/agents/openai.yaml @@ -0,0 +1,2 @@ +policy: + allow_implicit_invocation: false diff --git a/plugins/gborges-standard/skills/model-routing-review/agents/openai.yaml b/plugins/gborges-standard/skills/model-routing-review/agents/openai.yaml new file mode 100644 index 0000000..5b1f887 --- /dev/null +++ b/plugins/gborges-standard/skills/model-routing-review/agents/openai.yaml @@ -0,0 +1,2 @@ +policy: + allow_implicit_invocation: false diff --git a/plugins/gborges-standard/skills/pair-debate/agents/openai.yaml b/plugins/gborges-standard/skills/pair-debate/agents/openai.yaml new file mode 100644 index 0000000..5b1f887 --- /dev/null +++ b/plugins/gborges-standard/skills/pair-debate/agents/openai.yaml @@ -0,0 +1,2 @@ +policy: + allow_implicit_invocation: false diff --git a/plugins/gborges-standard/skills/research/SKILL.md b/plugins/gborges-standard/skills/research/SKILL.md new file mode 100644 index 0000000..adb3880 --- /dev/null +++ b/plugins/gborges-standard/skills/research/SKILL.md @@ -0,0 +1,40 @@ +--- +name: research +description: Send a background agent to answer a question from primary sources (official docs, source code, specs) and write the findings to a cited Markdown file in the repo. Use when the user wants a topic researched, docs or API facts checked, or reading delegated so the session can keep working. +--- + +# Research + +Send a background agent to do the reading, so this session keeps working +while it reads. + +## Who does it + +The agent reads to reach a conclusion, so it runs on +`gborges-standard:opus-medium`. When `~/.claude/gborges-standard.json` +says `"codex": true`, send it through the `codex-delegate` skill on the +`sol-xhigh` rung instead, since the brief stands alone and the output file +checks it. Either way, run it in the background. + +## The brief + +The brief carries the question, exactly as the user put it, and these +rules: + +1. Answer from primary sources, meaning the official docs, the source + code, the spec, or the first-party API. A blog post about the docs is + not a source. Follow every claim back to the page or file that owns it. +2. Write one Markdown file. Open with the question and a short answer. + Then the findings, each with a link or path to its source and the date + the source was read. Say plainly when a claim could not be verified. +3. Save it at the research path in the plugin's + `reference/output-locations.md`, which is `docs/research/.md` + unless the repo's `.claude/gborges-standard.json` names another docs + folder. When the repo already keeps such notes somewhere else, match + that. +4. Report the file's path and the short answer. + +## When it returns + +Read the file. Relay the short answer to the user with the path. Do not +restate the whole file. diff --git a/plugins/gborges-standard/skills/resolving-merge-conflicts/SKILL.md b/plugins/gborges-standard/skills/resolving-merge-conflicts/SKILL.md new file mode 100644 index 0000000..8f309c7 --- /dev/null +++ b/plugins/gborges-standard/skills/resolving-merge-conflicts/SKILL.md @@ -0,0 +1,26 @@ +--- +name: resolving-merge-conflicts +description: Resolve an in-progress git merge or rebase conflict by reading why each side changed, keeping both intents, and finishing the merge. Use when git reports conflicts or a merge or rebase is stopped. +--- + +# Resolving merge conflicts + +1. **See where the merge stands.** Run `git status` to list the conflicted + files, and `git log` on both sides to see what each branch changed. + +2. **Find out why each side changed.** For each conflict, read the commit + messages, the pull requests, and the issues behind both sides, until you + can say what each change was for. + +3. **Resolve each hunk.** Keep both intents where they fit together. Where + they cannot, keep the one that matches the merge's stated goal and say + what was given up. Add no behaviour that neither side had. Always + resolve. Never run `--abort`. + +4. **Run the project's checks.** Find them in the repo (usually the type + checker, then the tests, then the formatter) and run them. Fix anything + the merge broke. + +5. **Finish.** Stage everything and commit the merge. When rebasing, run + `git rebase --continue` and repeat from step 1 until every commit is + replayed. diff --git a/plugins/gborges-standard/skills/routine/SKILL.md b/plugins/gborges-standard/skills/routine/SKILL.md new file mode 100644 index 0000000..adee2fa --- /dev/null +++ b/plugins/gborges-standard/skills/routine/SKILL.md @@ -0,0 +1,54 @@ +--- +name: routine +description: Grill the user into a spec for one recurring routine (a daily check, a weekly report, a step on every new issue) that an agent could build unasked. Explicit invocation only. +argument-hint: "A routine to design, or nothing to go find one" +disable-model-invocation: true +--- + +# Routine + +Run a `grill` session whose only output is a spec for a recurring routine. +Ask in rounds, with a recommended answer on each question, and create, +edit, or delete spec files as the answers settle things. + +## What a routine is + +A routine is something the user does again and again: every morning, every +week, every time an email or an issue arrives. Looking at a week as a set of +routines shows how predictable much of it is, and a predictable routine can +be handed to an agent or a script. Use that view to find routines worth +specifying, and propose ones the user has not noticed. + +A routine spec lives at the routine path in the plugin's +[output-locations.md](../../reference/output-locations.md) (default +`docs/routines/.md`). The spec is the source of truth for that +routine. + +## Words the spec may use + +Reach for these only when the routine calls for them. A routine needs no +AI, no human check-in, and no schedule unless the grilling shows it does. + +- **Trigger.** What starts each run: an event (a new email, a new issue) or + a schedule (every morning at 7). An event usually costs less than a + schedule that polls. +- **Check-in.** A point where the run stops and asks the user to verify or + decide. Some routines have none and run on their own. +- **Push the check-in late.** Do as much as possible before asking the + user, so they are asked once, near the end, with everything prepared. +- **Brief.** What a check-in shows: a short, decision-ready summary of what + was produced and why, with a link to the thing itself. Never the raw + output. The user must be able to decide in seconds. + +## Done + +A routine spec is done when an agent could build it without asking a single +question. Keep grilling until then. + +## Notes about the user's world + +Keep `docs/routines/NOTES.md` (or the configured folder) with what you +learn about the user's tools, the channels they process, and their own +words for both. When it is empty or thin, interview them about their world +before specifying anything. When a fuzzy term comes up, settle it and +record it here. diff --git a/plugins/gborges-standard/skills/routine/agents/openai.yaml b/plugins/gborges-standard/skills/routine/agents/openai.yaml new file mode 100644 index 0000000..5b1f887 --- /dev/null +++ b/plugins/gborges-standard/skills/routine/agents/openai.yaml @@ -0,0 +1,2 @@ +policy: + allow_implicit_invocation: false diff --git a/plugins/gborges-standard/skills/spec/SKILL.md b/plugins/gborges-standard/skills/spec/SKILL.md new file mode 100644 index 0000000..85a906f --- /dev/null +++ b/plugins/gborges-standard/skills/spec/SKILL.md @@ -0,0 +1,110 @@ +--- +name: spec +description: Write the conversation up as a spec file, and split it into tickets only when the work is too big for one session. Explicit invocation only. +disable-model-invocation: true +--- + +# Spec + +Turn what has been discussed into a spec. Do not interview the user. Write +down what you already know, and mark the gaps as open questions inside the +spec. + +## Step 1: Gather + +Work from the conversation. When the invocation names a path, an issue +number, or a URL, read that too, comments included. When you have not yet +read the code the spec touches, read it now. Use the words in `CONTEXT.md` +when the repo has one, and keep to the decision records under `docs/adr/` +for the area. + +## Step 2: Write the spec + +Write it to the spec path in the plugin's +[output-locations.md](../../reference/output-locations.md), which reads +the repo's `.claude/gborges-standard.json` and falls back to +`docs/specs/.md`. A path or place the user named wins. + +Use this template: + +```markdown +# + +## Problem + +What the user is running into, from their point of view. + +## Solution + +What changes for the user once this is built. + +## Decisions + +Each decision made in the discussion, one bullet each. Modules to build or +change, what each exposes, schema changes, API contracts, and the reason +when the choice was not obvious. Name files and functions freely. + +## What the tests call + +The public functions, endpoints, or commands the tests will go through, +and any existing test in the repo to copy the shape of. + +## Out of scope + +What this spec does not cover, so nobody builds it by accident. + +## Open questions + +Anything the discussion left unsettled, one bullet each. Delete the section +when there are none. +``` + +Write the file, then run the `writing-voice` passes on it. + +## Step 3: Offer tickets + +Most specs are one session of work and need no tickets. Offer to split only +when the spec would not fit one session, or when the user asked for tickets. +Say why in one line and wait for a yes. + +## Step 4: Split into tickets + +Each ticket is one thin slice through every part of the app that works end +to end: schema, API, UI, and tests for that slice, all in one ticket. Never a +ticket per layer. A finished ticket can be demonstrated on its own, and one +ticket fits one fresh session. + +Each ticket names the tickets that must finish before it can start. A ticket +with none can start at once. + +One mechanical change that touches the whole codebase at once (renaming a +column, changing a shared type) does not fit a slice. Split it into three +kinds of ticket instead. First add the new form beside the old, so nothing +breaks. Then move the callers over in batches, one ticket per package or +folder, each blocked by the add. Last, delete the old form, blocked by every +batch. + +Before writing the files, show the list: title, blocked by, and the +end-to-end behaviour each ticket delivers. Ask whether the size is right and +whether the blockers are the real ones. Adjust until the user approves. + +Then write one file per ticket at the tickets path from +`output-locations.md` (default `docs/specs/<slug>/tickets/NN-<slug>.md`), +numbered from `01` with blockers first: + +```markdown +# <NN>: <Ticket title> + +**What to build:** the end-to-end behaviour this ticket makes work, from +the user's side. + +**Blocked by:** the numbers and titles of the tickets that must finish +first, or "None". + +- [ ] Acceptance criterion 1 +- [ ] Acceptance criterion 2 +``` + +When the repo's `tracker` is `github`, also open one issue per ticket in +the same order, with the blocking issue linked in each body, and one issue +for the spec that links them all. diff --git a/plugins/gborges-standard/skills/spec/agents/openai.yaml b/plugins/gborges-standard/skills/spec/agents/openai.yaml new file mode 100644 index 0000000..5b1f887 --- /dev/null +++ b/plugins/gborges-standard/skills/spec/agents/openai.yaml @@ -0,0 +1,2 @@ +policy: + allow_implicit_invocation: false diff --git a/plugins/gborges-standard/skills/teach/GLOSSARY-FORMAT.md b/plugins/gborges-standard/skills/teach/GLOSSARY-FORMAT.md new file mode 100644 index 0000000..69e48dc --- /dev/null +++ b/plugins/gborges-standard/skills/teach/GLOSSARY-FORMAT.md @@ -0,0 +1,47 @@ +# GLOSSARY.md format + +`GLOSSARY.md` holds the words this classroom uses, one definition each. +Every lesson, exercise, and learning record uses these words and no +synonyms. Writing a definition is itself learning: a term the user can +compress into one tight sentence is a term they understand. + +## Template + +```md +# {Topic} glossary + +{One or two sentences on what this glossary covers.} + +## Terms + +**Hypertrophy**: +Muscle growth driven by mechanical tension and metabolic stress over repeated training sessions. +_Avoid_: Bulking, getting big + +**Progressive overload**: +Increasing the demand on a muscle over time, through load, volume, or intensity. +_Avoid_: Pushing harder, levelling up + +**RPE (Rate of Perceived Exertion)**: +A 1 to 10 self-rating of how hard a set felt, where 10 is failure and 8 means two reps left. +_Avoid_: Effort score, intensity rating +``` + +## Rules + +- **Add a term only once the user understands it.** The glossary records + what is known. It is not a list to read in order to learn. Wait until the + user can use the term correctly. +- **Pick one word.** When several exist for one concept, choose the best + and list the others under `_Avoid_`. +- **Keep definitions short.** One or two sentences. What the thing is, not + how to do it. +- **Use glossary terms inside definitions.** Once a term is in, use it + everywhere, including in other entries. +- **Group under subheadings** when clusters form (`## Anatomy`, + `## Programming`). A flat list is fine otherwise. +- **Settle loose usage.** When the wider field uses a word loosely, say + which meaning holds here: "In this classroom, set always means a working + set. Warm-ups are counted separately." +- **Revise in place.** A definition from week one may be wrong by week six. + Update it. Leave no stale entry. diff --git a/plugins/gborges-standard/skills/teach/LEARNING-RECORD-FORMAT.md b/plugins/gborges-standard/skills/teach/LEARNING-RECORD-FORMAT.md new file mode 100644 index 0000000..b484e5e --- /dev/null +++ b/plugins/gborges-standard/skills/teach/LEARNING-RECORD-FORMAT.md @@ -0,0 +1,59 @@ +# Learning record format + +Learning records live in `learning-records/`, numbered in order: +`0001-slug.md`, `0002-slug.md`. Create the folder with the first record. +To number a new one, find the highest number in the folder and add one. + +A record captures something the user has shown they understand, a piece of +prior knowledge they told you about, or a misconception that got corrected. +Together the records say what to teach next. + +## Template + +```md +# {Short title of what was learned or established} + +{One to three sentences: what was learned, and why it changes what to teach +next.} +``` + +A record can be one paragraph. Its value is that the fact is written down, +not that every section is filled. + +## Optional sections + +Add one only when it says something the paragraph does not. + +- **Status** in frontmatter (`active`, `superseded by LR-NNNN`), when a + later record replaces this one. +- **Evidence**: how the user showed it. A question answered, an exercise + completed, prior experience described. Useful when the claim might be + doubted later. +- **Implications**: what this opens up or rules out for later sessions. + +## When to write one + +Any one of these: + +1. **The user showed they understand something.** Evidence they can use + the concept correctly, not that they saw it once. This raises the floor + for what to teach next. +2. **The user told you what they already know.** Record it, with how deep + they said it goes, so no session re-teaches it. +3. **A misconception was corrected.** The user believed something wrong + and now sees why. These predict where they will stumble next in related + topics. +4. **The mission moved because of what they learned.** Record the change + and update `MISSION.md`. + +## What does not get a record + +- Material that was covered. Covering is not learning. Wait for evidence. +- A term already defined in `GLOSSARY.md`. +- A log of what happened in the session. Records hold insights, not + activity. + +## When a later record contradicts an earlier one + +Mark the old record `Status: superseded by LR-NNNN` instead of deleting +it. How the user's understanding changed is useful later. diff --git a/plugins/gborges-standard/skills/teach/MISSION-FORMAT.md b/plugins/gborges-standard/skills/teach/MISSION-FORMAT.md new file mode 100644 index 0000000..8220e29 --- /dev/null +++ b/plugins/gborges-standard/skills/teach/MISSION-FORMAT.md @@ -0,0 +1,35 @@ +# MISSION.md format + +`MISSION.md` sits at the top of the classroom directory and says why the +user wants to learn this. Every choice of what to teach next, which source +to use, and which exercise to set traces back to it. + +## Template + +```md +# Mission: {Topic} + +## Why +{One to three sentences. The concrete goal in the user's life or work. What +changes once they have this skill.} + +## Success looks like +- {A specific thing the user will be able to do} +- {Another} + +## Constraints +- {Time, budget, other commitments, how they like to learn} + +## Out of scope +- {Nearby topics the user does not want to chase right now} +``` + +## Rules + +- **One mission per directory.** Two unrelated topics are two directories. +- **Concrete beats abstract.** "Run a half marathon by October" beats "get + fitter". "Ship a Rust CLI to my team" beats "learn Rust". +- **Push back on vagueness.** When the user cannot say why, interview them + before writing anything. A bad mission steers every later session wrong. +- **Update it when the goal moves.** Confirm with the user, change the + file, and write a learning record about the change. diff --git a/plugins/gborges-standard/skills/teach/RESOURCES-FORMAT.md b/plugins/gborges-standard/skills/teach/RESOURCES-FORMAT.md new file mode 100644 index 0000000..d727201 --- /dev/null +++ b/plugins/gborges-standard/skills/teach/RESOURCES-FORMAT.md @@ -0,0 +1,40 @@ +# RESOURCES.md format + +`RESOURCES.md` lists the trusted sources for this topic. Knowledge in a +lesson comes from here, never from memory alone. The communities listed here +are where the user tests what they learned on other people. + +## Template + +```md +# {Topic} resources + +## Knowledge + +- [Book: _The Science and Practice of Strength Training_ by Zatsiorsky & Kraemer](https://example.com) + The standard text on programming and adaptation. Use for: periodisation, recovery, intensity zones. +- [Article: "How Much Should I Train?" by Greg Nuckols (Stronger By Science)](https://example.com) + Evidence review of weekly volume. Use for: sets per muscle group per week. + +## Communities + +- [r/weightroom](https://reddit.com/r/weightroom) + Well-moderated, bro-science removed. Use for: programme critique, plateau troubleshooting. +- Local: Tuesday strength class at {gym name} + Use for: live coaching on lifts. + +## Gaps + +- {An area the mission needs that no good source covers yet} +``` + +## Rules + +- **Trusted sources only.** Primary sources, recognised experts, + peer-reviewed work, and communities with active moderation. Marketing + dressed as education stays out. +- **One line per entry.** What it covers and when to reach for it. A bare + link is useless in three months. +- **Two groups.** Knowledge and communities. A source can sit in one only. +- **List the gaps.** When the mission needs something no source covers, + write it under Gaps so the next session goes looking. diff --git a/plugins/gborges-standard/skills/teach/SKILL.md b/plugins/gborges-standard/skills/teach/SKILL.md new file mode 100644 index 0000000..06d9c71 --- /dev/null +++ b/plugins/gborges-standard/skills/teach/SKILL.md @@ -0,0 +1,117 @@ +--- +name: teach +description: Teach the user a topic over many sessions, keeping the lessons, references, and what they have learned as files in the current directory. Explicit invocation only. +argument-hint: "What would you like to learn about?" +disable-model-invocation: true +--- + +# Teach + +The user wants to learn something, over more than one session. The current +directory is the classroom, and its files hold where the learning stands. + +## The files + +- `MISSION.md` says why the user wants to learn this. Every lesson traces + back to it. Format in [MISSION-FORMAT.md](MISSION-FORMAT.md). +- `RESOURCES.md` lists the trusted sources you teach from and the + communities where the user can test what they learned. Format in + [RESOURCES-FORMAT.md](RESOURCES-FORMAT.md). +- `GLOSSARY.md` holds the terms the user now understands, one definition + each. Format in [GLOSSARY-FORMAT.md](GLOSSARY-FORMAT.md). +- `learning-records/NNNN-<slug>.md` record what the user has shown they + understand, one insight per file. They tell you what to teach next. + Format in [LEARNING-RECORD-FORMAT.md](LEARNING-RECORD-FORMAT.md). +- `lessons/NNNN-<slug>.html` are the lessons, one self-contained HTML file + each, numbered in order. +- `reference/*.html` are the cheat sheets: syntax, algorithms, sequences, + anything the user will come back to look up. +- `assets/*` are pieces shared across lessons: the stylesheet, quiz + widgets, diagrams, simulators. +- `NOTES.md` is your own scratch file for how the user likes to be taught. + +Create a file or folder the first time you have something to put in it. + +## How people learn + +The user needs three things. Knowledge comes from trusted sources. +Skills come from practising with feedback, in lessons you design from that +knowledge. Judgement comes from other people who do the thing, so the +final step of learning is a community. + +Until `RESOURCES.md` has good sources in it, finding them is the first +job. Never teach from memory alone. + +Some topics are mostly knowledge (theoretical physics). Some are mostly +skill (yoga). Weight the lessons to match. + +Being able to recall something right after reading it feels like mastery +and is not. Long-term retention is the goal, and it comes from effort: +recalling from memory instead of rereading, spacing practice over days, +and mixing related skills in one practice session. + +## The mission + +When `MISSION.md` is missing or vague, your first job is to ask why the +user wants this, until the answer is concrete. "Run a half marathon by +October" and "ship a Rust CLI to my team" are missions. "Understand X" is +not. Without a mission, lessons come out abstract and you cannot tell what +to teach next. + +Missions change as the user learns. Confirm with the user, then update the +file and write a learning record. + +## What to teach next + +When the user names the thing, teach that. Otherwise read the learning +records and the mission, and pick the most useful thing that sits just past +what they can already do. Each lesson should feel like a stretch and not a +wall. + +## A lesson + +A lesson is one HTML file that teaches one small thing tied to the mission +and gives the user one win they can build on. Keep it short. Working memory +is small. + +Publish it as a Claude artifact by default, so the user can open it on any +device, and save the same file under `lessons/`. Send it somewhere else +only when the user asks. + +Each lesson: + +- links its stylesheet and any widgets from `assets/`, so every lesson + looks like part of one course. Read `assets/` before writing, reuse what + is there, and put anything a second lesson could reuse there too. +- teaches the knowledge first, then has the user practise the skill with + immediate feedback: a quiz, a small in-browser task, or a checklist of + real-world steps. +- cites its sources inline, so every claim can be checked. +- links to related lessons and reference pages by anchor. +- names the one best source to read or watch next. +- reminds the user they can ask you follow-up questions. + +For a quiz, give every answer the same number of words, so the formatting +gives nothing away. Keep the layout clean and readable, so a lesson reads +well when the user returns to it. + +## Reference pages + +While writing lessons, also write the reference pages the user will look +things up in later: syntax and snippets for a language, steps and +flowcharts for a process, poses and sequences for yoga, routines for +fitness, a glossary for anything with its own words. Lessons are read once. +Reference pages are read many times, so make them dense and quick to scan. + +## The community + +When the user asks something that needs judgement from experience, answer +as best you can and then point them at people. Find a well-moderated +forum, a subreddit, a local class, or an interest group where they can try +what they learned on others. When the user says they do not want a +community, drop it. + +## NOTES.md + +When the user says how they like to be taught, or asks you to keep +something in mind, write it here and read it before designing a lesson. diff --git a/plugins/gborges-standard/skills/teach/agents/openai.yaml b/plugins/gborges-standard/skills/teach/agents/openai.yaml new file mode 100644 index 0000000..5b1f887 --- /dev/null +++ b/plugins/gborges-standard/skills/teach/agents/openai.yaml @@ -0,0 +1,2 @@ +policy: + allow_implicit_invocation: false diff --git a/plugins/gborges-standard/skills/wait-what/SKILL.md b/plugins/gborges-standard/skills/wait-what/SKILL.md new file mode 100644 index 0000000..f45c9e7 --- /dev/null +++ b/plugins/gborges-standard/skills/wait-what/SKILL.md @@ -0,0 +1,23 @@ +--- +name: wait-what +description: Break the last message down in plain English, context first. Explicit invocation only. +disable-model-invocation: true +--- + +# Wait, what? + +The last message did not land. Say it again as if to someone who has just +walked in. + +1. Give the context first, in two or three sentences. What is the problem, + what were we doing, and what had we already settled. +2. Then the point, one idea per sentence, short sentences, everyday words. + Say what each thing does before you name it. Do the arithmetic and give + the number. Name the file, the function, the command, so the reader can + check it. +3. When the repo has a `CONTEXT.md` (or a `CONTEXT-MAP.md` pointing at + several), use its terms and no others for the things it names. + +Leave out the path you took to get here. Leave out anything the reader did +not ask about. When the message was a recommendation, end on what you +recommend and the one reason that decides it. diff --git a/plugins/gborges-standard/skills/wait-what/agents/openai.yaml b/plugins/gborges-standard/skills/wait-what/agents/openai.yaml new file mode 100644 index 0000000..5b1f887 --- /dev/null +++ b/plugins/gborges-standard/skills/wait-what/agents/openai.yaml @@ -0,0 +1,2 @@ +policy: + allow_implicit_invocation: false diff --git a/plugins/gborges-standard/skills/wayfinder/SKILL.md b/plugins/gborges-standard/skills/wayfinder/SKILL.md new file mode 100644 index 0000000..f96f5b0 --- /dev/null +++ b/plugins/gborges-standard/skills/wayfinder/SKILL.md @@ -0,0 +1,146 @@ +--- +name: wayfinder +description: Plan work too big for one session as a map of open questions, then answer them one session at a time until the way to build it is clear. Explicit invocation only. +disable-model-invocation: true +--- + +# Wayfinder + +An idea has arrived that is too big for one session, and the way from here +to done is not visible yet. This skill writes a map of the questions that +stand in the way, then answers them one session at a time. When every +question is answered, the map is done and someone can go and build the +thing. + +The output is decisions, never the build itself. When you feel the pull to +start building, that is the end of the map. Stop and hand off. The map's +Notes can say otherwise for one effort, and only then does building happen +inside the map. + +## The map + +The map is one file, at the map path in the plugin's +[output-locations.md](../../reference/output-locations.md) (default +`docs/specs/<slug>/map.md`). Its body: + +```markdown +## Destination + +What done looks like: the spec, decision, or change this map leads to. One +or two lines. Every session reads this before picking a question. + +## Notes + +The area of the codebase, the skills every session should use, and any +standing preference for this effort. + +## Decisions so far + +- [<question title>](questions/NN-<slug>.md): one-line gist of the answer + +## Not yet specified + +Questions you can tell are coming but cannot phrase precisely yet, because +they depend on questions still open. Written loosely. + +## Out of scope + +Work ruled outside the destination, one line each, with the reason. +``` + +The map is an index. A decision lives in its question file, and the map +gives the gist and the link. + +## Questions + +Each open question is one file at `docs/specs/<slug>/questions/NN-<slug>.md` +(or the repo's configured folder), numbered from `01`: + +```markdown +# <NN>: <Question title> + +**Type:** research | prototype | grilling | task + +**Blocked by:** the numbers of the questions that must be answered first, +or "None". + +**Status:** open | answered + +## Question + +The decision or investigation this file resolves. Sized to one session. +``` + +A question is answered by appending an `## Answer` section, setting the +status, and adding one line to the map's Decisions so far. + +The four types: + +- **research**, done without the user. Read docs, a third-party API, or + local material to find a fact the decision needs. Answered by the + `research` skill. +- **prototype**, done with the user. Build something rough enough to react + to when the question is how it should look or behave. +- **grilling**, done with the user. A conversation. The default type. Run + the `grill` skill, which brings in `domain-modeling` when the repo has a + glossary. +- **task**, with or without the user. Work that must happen before a + decision can be made: sign up for a service, provision access, move data + so its shape can be seen. The answer records what was done and any fact + later questions need. + +A question done with the user is answered only in that conversation. Never +answer the user's side yourself. + +## Ticket or not yet + +Write a question file when you can state the question precisely now, even +when you cannot act on it yet. Write a line under Not yet specified when +you cannot phrase it that sharply. Do not pre-split a vague area into +question-sized pieces. One vague line may become several questions later, +or none. + +Work that lies past the destination goes under Out of scope, never under +Not yet specified. When an existing question is found to lie past the +destination, mark it answered with a one-line note, move its gist to Out of +scope, and leave it out of Decisions so far. + +## Charting the map + +The user invokes with a loose idea. + +1. Name the destination. Run the `grill` skill to pin down what this map + leads to. Settle this first, since it fixes the scope. +2. Find the open questions. Grill again, wide rather than deep, across the + whole effort. When this turns up nothing that will not fit one session, + there is no need for a map. Say so and ask how the user wants to + proceed. +3. Write the map with Destination and Notes filled, Decisions so far empty, + and the vague areas under Not yet specified. +4. Write the question files you can state now, then fill in each one's + blockers. +5. For each research question, start a background `research` run at once. +6. Stop. Charting is one session's work. + +## Working the map + +The user invokes with the map's path, and optionally a question. + +1. Read the map. Read a question file only when you need it. +2. Take the question the user named, or the lowest-numbered open question + whose blockers are all answered. +3. Answer it. Read related question files as needed. Use the skills the + map's Notes name. When in doubt, run `grill`. +4. Record the answer in the question file, set its status, and add the line + to the map. +5. Write any new question the answer made precise, and remove its line from + Not yet specified. Move anything the answer put outside the destination + to Out of scope. Update or delete any question the answer invalidated. + +Answer one question per session, except research questions, which can run +several at once. + +When the repo's `tracker` is `github`, the map and each question also +exist as issues, the questions as sub-issues of the map, with GitHub's +blocking relation between them. Answering a question closes its issue with +the answer as a comment. diff --git a/plugins/gborges-standard/skills/wayfinder/agents/openai.yaml b/plugins/gborges-standard/skills/wayfinder/agents/openai.yaml new file mode 100644 index 0000000..5b1f887 --- /dev/null +++ b/plugins/gborges-standard/skills/wayfinder/agents/openai.yaml @@ -0,0 +1,2 @@ +policy: + allow_implicit_invocation: false diff --git a/plugins/gborges-standard/skills/writing-for-agents/SKILL-MECHANICS.md b/plugins/gborges-standard/skills/writing-for-agents/SKILL-MECHANICS.md new file mode 100644 index 0000000..143fe4d --- /dev/null +++ b/plugins/gborges-standard/skills/writing-for-agents/SKILL-MECHANICS.md @@ -0,0 +1,49 @@ +# Skill mechanics + +What changes when the document is a skill: the frontmatter, who may invoke +it, and a skill that only lists other skills. Everything else is in +[SKILL.md](SKILL.md). + +## Who may invoke it + +Two choices, trading the two costs in `SKILL.md`. + +A **model-invoked** skill keeps its `description` in the agent's context +on every turn, so the agent can fire it on its own and other skills can +call it. The user can still type its name. The description is the skill's +pointer, always loaded, and its wording follows the pointer rules in +`SKILL.md`: trigger words first, one trigger per case. A model-invoked +skill that is all reference is also the one way to share reference between +skills, since another skill can call it. To make one, leave +`disable-model-invocation` out of the frontmatter. + +A **user-invoked** skill hides its description from the agent. Only a +person typing its name can run it, and no other skill can. It costs no +context, and it costs the human remembering it exists. To make one, set +`disable-model-invocation: true`. The description then reads as one line +for the human, with no trigger list. For Codex, which ignores that field, +add `agents/openai.yaml` next to `SKILL.md` with +`allow_implicit_invocation: false`. + +Pick model invocation only when the agent must reach the skill on its own, +or another skill must call it. A skill that only ever runs by hand is +user-invoked and costs nothing per turn. + +Reference that two user-invoked skills both need cannot live in either, +since neither can call the other. Put it in a plain file outside the skill +system and point at it from both. This plugin keeps such files under +`reference/`. + +## Splitting by invocation + +Split off a model-invoked skill when a distinct trigger word you use in +your own prompts should fire it on its own, or when another skill must +call it. The new description costs context on every turn, so the +independent reach has to be worth that. + +## A skill that lists other skills + +When user-invoked skills grow past what one person can remember, one more +user-invoked skill can list them and say when to reach for each. It can +only point. It cannot fire them, since user-invoked skills have no +description for it to reach. diff --git a/plugins/gborges-standard/skills/writing-for-agents/SKILL.md b/plugins/gborges-standard/skills/writing-for-agents/SKILL.md new file mode 100644 index 0000000..57b5b4d --- /dev/null +++ b/plugins/gborges-standard/skills/writing-for-agents/SKILL.md @@ -0,0 +1,153 @@ +--- +name: writing-for-agents +description: How to structure a document an agent reads (a skill, a CLAUDE.md or AGENTS.md, a doc reached by a pointer) so the agent follows it the same way every run. Use when creating or editing a skill, a CLAUDE.md, or an AGENTS.md. +--- + +# Writing for agents + +The rules for any document an agent reads: a skill, a `CLAUDE.md` or +`AGENTS.md`, a doc another document points at. The packaging differs. The +writing does not. The aim is a document the agent runs the same way every +time. + +For the parts specific to a skill (frontmatter, who may invoke it, a skill +that only lists other skills), read +[SKILL-MECHANICS.md](SKILL-MECHANICS.md). For the prose itself, the Plain +English style in the system prompt applies, and the draft goes through the +`writing-voice` passes before it ships. + +## Pointers + +A pointer is a line the agent always has in context that names some +material it does not, and says when to go and read it. A skill's +`description` is one. A line in `CLAUDE.md` naming a doc is another. The +pointer's wording decides whether the agent reaches the material, and how +reliably. When material the agent must read sits behind a weakly worded +pointer, the fix is the wording. Move the material into the main document +only when better wording does not work. + +A pointer says what the material is, and lists the cases that should send +the agent to it. Every word of a pointer costs on every turn, so cut +harder than in the body: + +- Put the word that triggers the pointer first. +- One trigger per case. Two synonyms for one case are the same case twice. +- Leave out what the body already says. + +## Two costs + +Every document and pointer spends one of two budgets. Material the agent +always has loaded (a `CLAUDE.md` line, a skill description) costs tokens +and attention on every turn whether it is used or not. Material the human +has to remember exists, and reach for by name, costs the human. The second +cost is not one to drive to zero. It is the price of the human choosing. +Spend it where the human's judgement matters and remove it where it does +not. + +Material behind a pointer costs only the pointer's line per turn. Material +with no pointer costs only the human's memory. + +## What goes where + +A document has two kinds of content. Steps are the ordered actions the +agent performs. Reference is the definitions, rules, and facts it looks up +as needed. A document can be all steps (a recipe), all reference (a +review's rules, this file), or both. + +Place each piece by how soon the agent needs it: + +1. A step in the main file, in order. This is what the agent does. +2. Reference in the main file, read when needed. A flat set of rules is + fine here. +3. Reference in a second file, reached by a pointer and loaded only when + the pointer fires. It can be a sibling file in the skill folder or a + doc anywhere. + +Push too little into a second file and the main file bloats. Push too much +and the agent misses material it needed. The test is which cases need it: +what every run needs stays in the main file, and what only some runs need +goes behind a pointer. When reference that only some runs need sits among +the steps, it buries them, and the agent attends to them by chance. + +Keep one concept's definition, rules, and exceptions under one heading. +Reading one part then brings the rest. Scattered pieces of one meaning +read like notes, not documentation. That is different from duplication, +which repeats one meaning in two places. + +A document that is simply too long fails even when every line is live. +Attention thins across it, and every line is one more to keep current. +Move reference behind pointers, and split by case or by sequence so each +run loads only what it uses. + +## Steps end on a check + +Every step ends on the condition that says it is done. Two properties make +the condition work. + +The agent must be able to tell done from not done. A vague condition +("understanding reached") invites the agent to stop early, pulled by the +steps it can see ahead. Sharpen the condition first, since that is cheap +and local. Only when it cannot be sharpened, and you have seen the agent +rush, hide the later steps by splitting the sequence across a real context +break (a handoff or a subagent). A skill called inline leaves the later +steps in context and hides nothing. + +The condition must demand enough. "Every modified model accounted for" +forces thorough work where "produce a change list" does not. The demand +sets how much digging the agent does inside the step without a step of its +own for it. A body of rules gets the same treatment: "every rule applied" +demands as much of a checklist as "every step done" demands of a recipe. + +The best conditions are both checkable and exhaustive. + +## When to split + +Splitting one document into two spends one of the two costs, so split only +when the cut pays for itself. + +Split a run of steps when the steps after one tempt the agent to rush it. +Keeping them out of view makes the agent do more on the step in front of +it. Merging two sequences does the reverse. It shows each step the steps +after it and invites rushing. + +For splitting a skill by who may invoke it, see +[SKILL-MECHANICS.md](SKILL-MECHANICS.md). + +## Say what to do + +State the behaviour you want. "Write one-line comments" gives the agent +something to do. "Do not write long comments" names the thing to avoid and +leaves the target unsaid. Write the positive form whenever it is as short. +Keep a ban only when it names a specific trap the agent falls into, and +put the positive target beside it, so the agent has both the thing to +avoid and the thing to do. + +State a mechanism in plain words. "The test calls the public function and +checks what comes back" tells the agent what to do. A borrowed word for +the same idea ("test at the seam") saves a few tokens and asks the agent +to guess what the word covers here. Use a short term only when the +document defines it in plain words at first use and reuses it enough to +pay for the definition. + +## Pruning + +- **One home per meaning.** Changing a behaviour should be an edit in one + place. The same meaning in two places costs tokens, drifts apart, and + makes the meaning look more important than it is. +- **Do not copy what the environment already says.** A `package.json` + script, a config file, the folder layout, and `--help` output are all + readable. A document that restates them is a cache that goes stale. + Write down what the agent cannot find by looking: the unwritten + convention, the reason behind a choice, the trap no config mentions. +- **Check every line still matters.** A line stops mattering when it never + bears on the task (exposition, or a case that belongs behind a pointer) + or when the behaviour or world it describes has changed. Without this + check, stale lines pile up, because adding feels safe and removing feels + risky. +- **Cut lines the agent obeys anyway.** An instruction the model already + follows by default costs tokens and changes nothing. The test is whether + the line changes behaviour against the default, and two people who + disagree settle it by running the document, not by arguing. When a line + fails, delete the whole line. A word too weak to change behaviour ("be + thorough" to an agent already thorough) fails the same test, and the fix + is a stronger word or a sharper condition. diff --git a/plugins/gborges-standard/skills/writing-voice/RULES.md b/plugins/gborges-standard/skills/writing-voice/RULES.md index 6d3ed1a..0758744 100644 --- a/plugins/gborges-standard/skills/writing-voice/RULES.md +++ b/plugins/gborges-standard/skills/writing-voice/RULES.md @@ -123,7 +123,8 @@ restatement marker ("in other words", "put differently", "in one sentence") announces that the next sentence repeats the last one, so cut both the marker and the repeat. An aphoristic closer restates the claim as a motto after it has already been made: "that distinction matters", "that -is the boundary", "green is the gate, not a suggestion". Delete it. The +is the boundary", "green is the gate, not a suggestion", "the file is the +signal". Delete it. The claim before it already did the work. The question form is the same failure as "The upshot:", with a question mark doing the deferring. Ask a question only when the reader has to answer it. Number a list only in the list itself. Attaching a reason does diff --git a/plugins/gborges-standard/skills/writing-voice/scripts/check.py b/plugins/gborges-standard/skills/writing-voice/scripts/check.py index 4f060a6..c85a881 100755 --- a/plugins/gborges-standard/skills/writing-voice/scripts/check.py +++ b/plugins/gborges-standard/skills/writing-voice/scripts/check.py @@ -102,6 +102,7 @@ (7, r"that(?:'s| is) the (?:boundary|actual constraint|real constraint|whole point|gate|line)\.", "that is the boundary"), (7, r"\b(?:is|as) (?:the|a) (?:gate|hard stop|hard boundary|hard constraint|hard gate), not a\b", "X is the gate, not a Y"), (7, r"\bnot a suggestion\.", "not a suggestion"), + (7, r"\bis the signal\b", "X is the signal"), # Rule 11 continued, validation and candor framing. (11, r"\bfair (?:hit|point|push|pushback|enough)\b", "fair hit"), (11, r"\bgood catch\b", "good catch"), diff --git a/scripts/cloud-bootstrap.sh b/scripts/cloud-bootstrap.sh index 8bfdb85..8b3941b 100755 --- a/scripts/cloud-bootstrap.sh +++ b/scripts/cloud-bootstrap.sh @@ -11,7 +11,7 @@ # missing or the two numbers differ. After the merge, paste the new rev into # each environment's Setup script field when the change cannot wait out the # snapshot expiry. -# rev: 35 +# rev: 36 set -u