mirror of
https://github.com/mattpocock/skills.git
synced 2026-07-29 11:02:41 +07:00
Compare commits
1
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
3252670bd6 |
@@ -1,81 +0,0 @@
|
||||
# Writing docs pages
|
||||
|
||||
Every promoted skill (`engineering/`, `productivity/`, `misc/`) has a human-facing **docs page** at `docs/<bucket>/<skill-name>.md` — the docs tree mirrors the bucket folders under `skills/`. It is published at `https://aihero.dev/skills-<skill-name>`; the URL is always `skills-<skill-name>` regardless of bucket, so the docs path is repo organisation only. The page is not the skill and not a copy of `SKILL.md`.
|
||||
|
||||
Most of these skills are **user-invoked**: the agent will never fire them for you, so *you* are the index that has to remember they exist and when to reach for them. That memory is **cognitive load**. The job of a docs page is to relieve it — to orient one reader around one skill so they can hold it in their head, know when to reach for it, and see where it sits in the system. The pages are collectively a distributed router; each is a node.
|
||||
|
||||
Act whenever a promoted skill is added, renamed, or has its behaviour changed: create or re-sync its docs page. A rename moves the file too (`docs/<bucket>/<old>.md` → `docs/<bucket>/<new>.md`), because the published URL tracks the name; a skill that moves buckets moves its docs file to the matching folder. Skills in `personal/`, `in-progress/`, and `deprecated/` get no page, mirroring the README rule.
|
||||
|
||||
Because these pages are published on `aihero.dev`, **every link is absolute** — never a repo-relative path. A link to another skill points at `https://aihero.dev/skills-<name>`; a link into the repo points at its full `https://github.com/mattpocock/skills/...` URL. A relative link that works in the repo breaks once published.
|
||||
|
||||
There is no H1 — the published page takes its title from the slug.
|
||||
|
||||
## Page structure
|
||||
|
||||
Fill the template below. The **fixed frame** (Quickstart block, source link, `## What it does`, `## When to reach for it`, `## Where it fits`) appears on every page. The **adaptable middle** — `## Prerequisites` and the free-form substance sections — carries only what this particular skill earns; delete the rest.
|
||||
|
||||
<page-template>
|
||||
|
||||
Quickstart:
|
||||
|
||||
```bash
|
||||
npx skills add mattpocock/skills --skill=<name>
|
||||
```
|
||||
|
||||
```bash
|
||||
npx skills update <name>
|
||||
```
|
||||
|
||||
[Source](https://github.com/mattpocock/skills/tree/main/skills/<bucket>/<name>)
|
||||
|
||||
## What it does
|
||||
|
||||
One or two plain-language paragraphs. Lead with the skill's one-sentence job, then state the **load-bearing constraint** — the single fact that makes this skill behave differently from the obvious default (for `to-prd`: it does not interview the user again, it synthesises what is already known). This line is the most valuable on the page; never omit it.
|
||||
|
||||
## When to reach for it
|
||||
|
||||
How and when you reach for the skill — two beats, both effectively always present:
|
||||
|
||||
- **Invocation mode.** State whether you type it or the agent fires it. A user-invoked skill: "You invoke this by typing `/<name>` — the agent won't reach for it on its own." A model-invoked skill: "Type `/<name>`, or the agent reaches for it automatically when a task fits."
|
||||
- **Trigger boundary.** The index entry: "reach for this when …". Where the skill is confusable with a sibling, add the other half — "for <X> instead, use [<sibling>](https://aihero.dev/skills-<sibling>)."
|
||||
|
||||
## Prerequisites
|
||||
|
||||
Optional — include only when the skill needs something in place to be functional; omit the heading entirely otherwise. Covers: a **workspace it writes into** (a stateful skill like `grill-with-docs` writes `CONTEXT.md` and ADRs; `teach` builds a whole directory — say what it writes and where), **prior setup** (`triage`/`to-prd`/`to-issues` need `setup-matt-pocock-skills` to have configured an issue tracker), or **repo-specific tooling**. A stateless skill that runs anywhere has no prerequisites — drop the section.
|
||||
|
||||
## <free-form middle>
|
||||
|
||||
One to three short sections, in the skill's *own vocabulary*, that make it click — choose whatever headings fit the skill: the loop it runs, the artifact it produces, the fork it makes, the one anti-pattern it kills. There is no prescribed heading; the skills are too heterogeneous for one.
|
||||
|
||||
The single non-negotiable: **surface the skill's leading word / load-bearing idea** — `tight` feedback loop, `deep module`, throwaway-code-answers-a-question, red-green. It pays off twice: the reader learns what the skill *is*, and learns the word they'll later think with to *reach for* it.
|
||||
|
||||
## It's working if
|
||||
|
||||
Optional. A short, checkable list of the observable signals that tell the reader the skill is actually doing its job — what they should see when it fires, and by absence when it hasn't. Include it when a skill has crisp tells (e.g. `to-prd` writes without re-interviewing you; a leading word reappearing in the trace); omit the heading when the signals are vague. A few bullets, no more.
|
||||
|
||||
## Where it fits
|
||||
|
||||
Always present. Situate the skill in the system in a sentence or two:
|
||||
|
||||
- **Role.** Name it: a **chain step** (`grill-with-docs → to-prd → to-issues → tdd`), a **run-once setup** (`setup-matt-pocock-skills`), **periodic maintenance** (`improve-codebase-architecture`, "every few days"), or a **reach-for-it-anytime standalone** (`diagnosing-bugs`, `prototype`, `handoff`). A standalone's map is one honest sentence — far better than omitting the section.
|
||||
- **Neighbours.** The one or two siblings that matter, each with a because-clause, linked absolutely.
|
||||
- **The map.** Point to [ask-matt](https://aihero.dev/skills-ask-matt), the router over the whole set, so this page stays a node and never has to redraw the graph.
|
||||
|
||||
</page-template>
|
||||
|
||||
## Conventions
|
||||
|
||||
- Explain the **why**, not the process. The page orients and situates the skill; it never reproduces the `SKILL.md` steps or template dumps — a human choosing a tool does not need the runbook.
|
||||
- Use the skill's **leading words** (_seam_, _deep module_, _tracer bullet_) so the page and the skill speak one language.
|
||||
- Keep the page itself low-load. It is documentation *about* low-cognitive-load skills; furniture (spare headings, restated links) is the thing it is arguing against.
|
||||
|
||||
## Done when
|
||||
|
||||
- The page exists at `docs/<bucket>/<name>.md`, and no stale page survives a rename or bucket move.
|
||||
- The Quickstart block and source link name the correct bucket and skill; the update line names the skill.
|
||||
- `## What it does` states the load-bearing constraint.
|
||||
- `## When to reach for it` states invocation mode and the trigger boundary.
|
||||
- `## Where it fits` names the role and links to `ask-matt`.
|
||||
- A prerequisite (workspace, prior setup, tooling) is stated where one exists, and the section is absent where none does.
|
||||
- The middle surfaces the leading word.
|
||||
- Every link is absolute, and every one resolves.
|
||||
@@ -1,5 +0,0 @@
|
||||
---
|
||||
"mattpocock-skills": minor
|
||||
---
|
||||
|
||||
Add a fourth **Task** ticket type to the **`decision-mapping`** skill. Some blockers are neither a decision, a prototype, nor research — just literal manual work that has to happen before the discussion can move forward (moving data, signing up for a third-party service, provisioning access). The agent automates it where it can, otherwise hands the human a precise checklist, and records any resulting facts later tickets depend on.
|
||||
@@ -1,5 +0,0 @@
|
||||
---
|
||||
"mattpocock-skills": minor
|
||||
---
|
||||
|
||||
Make the **`prototype`** skill model-invoked, so the agent can reach for it autonomously (and other skills can too). Its description is rewritten around the leading word _prototype_ — throwaway code that answers a design question — with one trigger per branch (state/logic sanity-check, or UI exploration).
|
||||
@@ -1,5 +0,0 @@
|
||||
---
|
||||
"mattpocock-skills": patch
|
||||
---
|
||||
|
||||
Give the in-progress **`review`** skill an always-on Fowler smell baseline on its Standards axis. A curated ~12 high-signal "Bad Smells in Code" (Mysterious Name, Duplicated Code, Feature Envy, Data Clumps, Primitive Obsession, Repeated Switches, Shotgun Surgery, Divergent Change, Speculative Generality, Message Chains, Middle Man, Refused Bequest) are inlined into `SKILL.md` as a fixed baseline alongside whatever the repo documents — not a new third axis. Two binding rules keep it safe: a documented repo standard overrides the baseline, and every smell is reported as a judgement call, never a hard violation.
|
||||
@@ -1,5 +0,0 @@
|
||||
---
|
||||
"mattpocock-skills": patch
|
||||
---
|
||||
|
||||
Add the **tautological test** anti-pattern to the `tdd` skill. Tests whose assertion is recomputed the way the code computes it pass by construction and give zero confidence — distinct from the implementation-coupling anti-pattern already covered. Added as a peer at the same three sites: a Philosophy principle (expected values must come from an independent source of truth), a per-cycle checklist gate, and a BAD/GOOD example pair in `tests.md`.
|
||||
@@ -1,5 +0,0 @@
|
||||
---
|
||||
"mattpocock-skills": patch
|
||||
---
|
||||
|
||||
Extend the **`triage`** skill to triage external pull requests, treating a PR as an issue with attached code that runs through the same roles and state machine. PRs flow inline alongside issues (gated by a per-repo setup toggle), discovery surfaces only external PRs, the bug-only "reproduce" step is generalized into a single "verify the claim" step, and a redundancy check resolves already-implemented requests to `wontfix` without polluting the out-of-scope knowledge base. `setup-matt-pocock-skills` gains the PRs-as-a-request-surface toggle for GitHub/GitLab.
|
||||
@@ -1,5 +1,11 @@
|
||||
# mattpocock-skills
|
||||
|
||||
## 1.0.2
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- [`e00eadb`](https://github.com/mattpocock/skills/commit/e00eadb4bb32c3d5a631ead1a5ed5d6a7c5f74e2) Thanks [@mattpocock](https://github.com/mattpocock)! - Extend the **`triage`** skill to triage external pull requests, treating a PR as an issue with attached code that runs through the same roles and state machine. PRs flow inline alongside issues (gated by a per-repo setup toggle), discovery surfaces only external PRs, the bug-only "reproduce" step is generalized into a single "verify the claim" step, and a redundancy check resolves already-implemented requests to `wontfix` without polluting the out-of-scope knowledge base. `setup-matt-pocock-skills` gains the PRs-as-a-request-surface toggle for GitHub/GitLab.
|
||||
|
||||
## 1.0.1
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -13,8 +13,4 @@ Each skill entry in the top-level `README.md` must link the skill name to its `S
|
||||
|
||||
Each bucket folder has a `README.md` that lists every skill in the bucket with a one-line description, with the skill name linked to its `SKILL.md`. Bucket `README.md`s and the top-level `README.md` group entries into **User-invoked** and **Model-invoked**.
|
||||
|
||||
Every promoted skill also has a human-facing docs page at `docs/<bucket>/<skill-name>.md` (the docs tree mirrors the bucket folders under `skills/`). The published URL is `https://aihero.dev/skills-<skill-name>` regardless of bucket — the docs path is repo organisation only. When you add, rename, or change the behaviour of a skill in `engineering/`, `productivity/`, or `misc/`, create or re-sync its docs page following [.agents/writing-docs.md](./.agents/writing-docs.md).
|
||||
|
||||
Every `SKILL.md` is either user-invoked (`disable-model-invocation: true`, reachable only by the human) or model-invoked (model- or user-reachable). See [.agents/invocation.md](./.agents/invocation.md).
|
||||
|
||||
To (re)link every skill into the local harness skill directories (`~/.claude/skills`, `~/.agents/skills`), run `scripts/link-skills.sh`. Each entry is a symlink into this repo, so a `git pull` keeps installed skills current; re-run the script after adding, removing, or renaming a skill.
|
||||
Every `SKILL.md` is either user-invoked (`disable-model-invocation: true`, reachable only by the human) or model-invoked (model- or user-reachable). For the full definitions, description conventions, and why a user-invoked skill can invoke model-invoked skills but never another user-invoked one, see [docs/invocation.md](./docs/invocation.md).
|
||||
|
||||
@@ -156,10 +156,10 @@ Skills I use daily for code work.
|
||||
- **[setup-matt-pocock-skills](./skills/engineering/setup-matt-pocock-skills/SKILL.md)** — Configure this repo for the engineering skills (issue tracker, triage labels, domain doc layout). Run once per repo before using the other engineering skills.
|
||||
- **[to-issues](./skills/engineering/to-issues/SKILL.md)** — Break any plan, spec, or PRD into independently-grabbable issues using vertical slices.
|
||||
- **[to-prd](./skills/engineering/to-prd/SKILL.md)** — Turn the current conversation into a PRD and publish it to the issue tracker. No interview — just synthesizes what you've already discussed.
|
||||
- **[prototype](./skills/engineering/prototype/SKILL.md)** — Build a throwaway prototype to flesh out a design — either a runnable terminal app for state/business-logic questions, or several radically different UI variations toggleable from one route.
|
||||
|
||||
**Model-invoked**
|
||||
|
||||
- **[prototype](./skills/engineering/prototype/SKILL.md)** — Build a throwaway prototype to answer a design question — a runnable terminal app for state/logic questions, or several radically different UI variations toggleable from one route.
|
||||
- **[diagnosing-bugs](./skills/engineering/diagnosing-bugs/SKILL.md)** — Disciplined diagnosis loop for hard bugs and performance regressions: reproduce → minimise → hypothesise → instrument → fix → regression-test.
|
||||
- **[tdd](./skills/engineering/tdd/SKILL.md)** — Test-driven development with a red-green-refactor loop. Builds features or fixes bugs one vertical slice at a time.
|
||||
- **[domain-modeling](./skills/engineering/domain-modeling/SKILL.md)** — Actively build and sharpen a project's domain model — challenge terms against the glossary, stress-test with edge-case scenarios, and update `CONTEXT.md` and ADRs inline.
|
||||
|
||||
@@ -1,59 +0,0 @@
|
||||
Quickstart:
|
||||
|
||||
```bash
|
||||
npx skills add mattpocock/skills --skill=to-prd
|
||||
```
|
||||
|
||||
```bash
|
||||
npx skills update to-prd
|
||||
```
|
||||
|
||||
[Source](https://github.com/mattpocock/skills/tree/main/skills/engineering/to-prd)
|
||||
|
||||
## What it does
|
||||
|
||||
`to-prd` turns the current conversation and your codebase understanding into a product requirements document, then publishes it to your issue tracker.
|
||||
|
||||
The load-bearing constraint: it does **not** interview you again. By the time you reach for it, the alignment work is done — `to-prd` synthesises what is already known rather than asking a fresh round of questions.
|
||||
|
||||
## When to reach for it
|
||||
|
||||
You invoke this by typing `/to-prd` — the agent won't reach for it on its own.
|
||||
|
||||
Reach for it once a change has been talked through and the domain language is settled, and you want that shared understanding written down as a spec before any code is written. If you *haven't* aligned yet, grill first — for that, use [grill-with-docs](https://aihero.dev/skills-grill-with-docs). To split the finished PRD into tickets, use [to-issues](https://aihero.dev/skills-to-issues).
|
||||
|
||||
## Prerequisites
|
||||
|
||||
`to-prd` publishes into your issue tracker, so [setup-matt-pocock-skills](https://aihero.dev/skills-setup-matt-pocock-skills) must have configured the tracker and triage labels for this repo first. It applies the `ready-for-agent` label itself — no separate triage pass needed.
|
||||
|
||||
## What the PRD includes
|
||||
|
||||
- **Problem statement** — what is broken or missing, and why it's worth solving, in the project's own vocabulary.
|
||||
- **Solution** — the shape of the fix at a high level, before any implementation detail.
|
||||
- **User stories** — an extensive, numbered list of the concrete behaviours the change must support, each one independently checkable.
|
||||
- **Implementation decisions** — the choices already settled during the conversation, so they aren't relitigated later.
|
||||
- **Testing decisions** — the seams the feature will be tested at, and what "done" looks like.
|
||||
- **Out-of-scope items** — what this change deliberately does *not* cover, to keep the ticket bounded.
|
||||
- **Further notes** — anything else worth carrying forward that doesn't fit the sections above.
|
||||
|
||||
## Deep modules
|
||||
|
||||
Before writing the PRD, `to-prd` sketches the **seams** at which the feature will be tested and looks for **deep module** opportunities — a lot of functionality hidden behind a small, stable interface. It prefers existing seams to new ones and the highest seam possible, ideally just one across the whole change.
|
||||
|
||||
That matters for agentic development: a good interface gives tests something durable to target, so the code underneath can change without the tests moving.
|
||||
|
||||
## It's working if
|
||||
|
||||
- It starts writing the PRD instead of asking you a fresh round of questions.
|
||||
- It checks the seams with you before writing, and proposes as few as possible.
|
||||
- The PRD comes back in your project's domain vocabulary, not generic boilerplate.
|
||||
|
||||
## Where it fits
|
||||
|
||||
`to-prd` is a step in the main build chain:
|
||||
|
||||
```txt
|
||||
grill-with-docs → to-prd → to-issues → tdd
|
||||
```
|
||||
|
||||
Reach for it after the plan and domain language are resolved, and before you break the work into implementation tickets. Its key neighbours are [grill-with-docs](https://aihero.dev/skills-grill-with-docs), which sharpens the context so the PRD is precise, and [to-issues](https://aihero.dev/skills-to-issues), which turns the PRD into independently-grabbable issues for [tdd](https://aihero.dev/skills-tdd) to implement. When you're unsure which skill or flow fits, [ask-matt](https://aihero.dev/skills-ask-matt) routes you.
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "mattpocock-skills",
|
||||
"version": "1.0.1",
|
||||
"version": "1.0.2",
|
||||
"private": true,
|
||||
"description": "Matt Pocock's agent skills for real engineering",
|
||||
"repository": {
|
||||
|
||||
@@ -1,10 +1,6 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
# NOTE: This is a dev-only script, intended for use by maintainers of this repo.
|
||||
# It is not a supported installer. Modifications to it — or requests for
|
||||
# modifications — will not be approved.
|
||||
#
|
||||
# Links all skills in the repository into the local skill directories used by
|
||||
# each agent harness:
|
||||
# - ~/.claude/skills — Claude Code
|
||||
|
||||
@@ -13,13 +13,12 @@ Reachable only when you type them (`disable-model-invocation: true`).
|
||||
- **[setup-matt-pocock-skills](./setup-matt-pocock-skills/SKILL.md)** — Configure this repo for the engineering skills (issue tracker, triage labels, domain doc layout). Run once per repo.
|
||||
- **[to-issues](./to-issues/SKILL.md)** — Break any plan, spec, or PRD into independently-grabbable issues using vertical slices.
|
||||
- **[to-prd](./to-prd/SKILL.md)** — Turn the current conversation into a PRD and publish it to the issue tracker.
|
||||
- **[prototype](./prototype/SKILL.md)** — Build a throwaway prototype — a runnable terminal app for state/logic questions, or several toggleable UI variations.
|
||||
|
||||
## Model-invoked
|
||||
|
||||
Model- or user-reachable (rich trigger phrasing so the model can reach for them).
|
||||
|
||||
- **[prototype](./prototype/SKILL.md)** — Build a throwaway prototype to answer a design question: a runnable terminal app for state/logic, or several toggleable UI variations.
|
||||
|
||||
- **[diagnosing-bugs](./diagnosing-bugs/SKILL.md)** — Disciplined diagnosis loop for hard bugs and performance regressions: reproduce → minimise → hypothesise → instrument → fix → regression-test.
|
||||
- **[tdd](./tdd/SKILL.md)** — Test-driven development with a red-green-refactor loop. Builds features or fixes bugs one vertical slice at a time.
|
||||
- **[domain-modeling](./domain-modeling/SKILL.md)** — Actively build and sharpen a project's domain model — challenge terms, stress-test with scenarios, update `CONTEXT.md` and ADRs inline.
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
---
|
||||
name: prototype
|
||||
description: Build a throwaway prototype to answer a design question. Use when the user wants to sanity-check whether a state model or logic feels right, or explore what a UI should look like.
|
||||
description: Build a throwaway prototype to flesh out a design — a runnable terminal app for state/business-logic questions, or several radically different UI variations toggleable from one route.
|
||||
disable-model-invocation: true
|
||||
---
|
||||
|
||||
# Prototype
|
||||
|
||||
@@ -13,8 +13,6 @@ description: Test-driven development. Use when the user wants to build features
|
||||
|
||||
**Bad tests** are coupled to implementation. They mock internal collaborators, test private methods, or verify through external means (like querying a database directly instead of using the interface). The warning sign: your test breaks when you refactor, but behavior hasn't changed. If you rename an internal function and tests fail, those tests were testing implementation, not behavior.
|
||||
|
||||
**Tautological tests** restate the implementation inside the assertion, so they pass by construction and give zero confidence. When the expected value is computed the way the code computes it — `expect(add(a, b)).toBe(a + b)`, snapshotting a figure you derived by hand the same way the code does, asserting a constant equals itself — the test can never disagree with the code: break the code wrong and the assertion breaks wrong with it. The expected value must come from an independent source of truth — a known-good literal, a worked example, the spec.
|
||||
|
||||
See [tests.md](tests.md) for examples and [mocking.md](mocking.md) for mocking guidelines.
|
||||
|
||||
## Anti-Pattern: Horizontal Slices
|
||||
@@ -105,7 +103,6 @@ After all tests pass, look for [refactor candidates](refactoring.md):
|
||||
[ ] Test describes behavior, not implementation
|
||||
[ ] Test uses public interface only
|
||||
[ ] Test would survive internal refactor
|
||||
[ ] Expected values are independent literals, not recomputed from the code
|
||||
[ ] Code is minimal for this test
|
||||
[ ] No speculative features added
|
||||
```
|
||||
|
||||
@@ -59,19 +59,3 @@ test("createUser makes user retrievable", async () => {
|
||||
expect(retrieved.name).toBe("Alice");
|
||||
});
|
||||
```
|
||||
|
||||
**Tautological tests**: Expected value restates the implementation, so the test passes by construction.
|
||||
|
||||
```typescript
|
||||
// BAD: Expected value is recomputed the way the code computes it
|
||||
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: Expected value is an independent, known literal
|
||||
test("calculateTotal sums line items", () => {
|
||||
expect(calculateTotal([{ price: 10 }, { price: 5 }])).toBe(15);
|
||||
});
|
||||
```
|
||||
|
||||
@@ -5,7 +5,6 @@ Skills that are still being developed. They're not ready to ship — expect roug
|
||||
- **[decision-mapping](./decision-mapping/SKILL.md)** — Turn a loose idea into a sequenced map of investigation tickets, then drive them to resolution one at a time. User-invoked.
|
||||
- **[loop-me](./loop-me/SKILL.md)** — Grill yourself into implementable workflow specs over multiple sessions, using the current directory as a stateful workspace. User-invoked.
|
||||
- **[review](./review/SKILL.md)** — Review changes since a fixed point along two parallel axes: **Standards** (does the diff follow the repo's coding standards?) and **Spec** (does the diff faithfully implement the originating issue/PRD?).
|
||||
- **[wizard](./wizard/SKILL.md)** — Generate an interactive bash wizard that walks a human through a manual procedure (setup, a one-off migration, a state transition) — opening URLs, capturing values, writing `.env` and GitHub Actions secrets. User-invoked.
|
||||
- **[writing-beats](./writing-beats/SKILL.md)** — Shape an article as a journey of beats, choose-your-own-adventure style. Pick a starting beat, write only that beat, then pivot to the next, until the article reaches a natural end.
|
||||
- **[writing-fragments](./writing-fragments/SKILL.md)** — Grilling session that mines you for fragments — heterogeneous nuggets of writing — and appends them to a single document as raw material for a future article.
|
||||
- **[writing-shape](./writing-shape/SKILL.md)** — Take a markdown file of raw material and shape it into an article paragraph by paragraph, arguing format choices at each step.
|
||||
|
||||
@@ -4,7 +4,7 @@ description: Turn a loose idea into a sequenced map of investigation tickets, th
|
||||
disable-model-invocation: true
|
||||
---
|
||||
|
||||
This skill is invoked when a loose idea requires more than one agent session to turn into a plan. It creates a stateful decision map in a markdown file, and drives the user through a sequence of tickets to resolve the open questions - which may require either prototyping, research or grilling. The map is domain-agnostic: it plans engineering work, course content, or anything else that fits the same shape.
|
||||
This skill is invoked when a loose idea requires more than one agent session to turn into a plan. It creates a stateful decision map in a markdown file, and drives the user through a sequence of tickets to resolve the open questions - which may require either prototyping, research or discussion.
|
||||
|
||||
## The Decision Map
|
||||
|
||||
@@ -14,16 +14,13 @@ Assets created during tickets should be linked to from the map, not duplicated w
|
||||
|
||||
### Structure
|
||||
|
||||
Entries ("tickets"), each its own section keyed by a short dash-case slug that
|
||||
reads as a mini-title (e.g. `relational-db`, `auth-strategy`, `cache-layer`) —
|
||||
terse enough to stay token-efficient, and unique within the map.
|
||||
Numbered entries ("tickets"), each its own section keyed by its number:
|
||||
|
||||
```markdown
|
||||
## relational-db: Relational Or Non-Relational Database?
|
||||
## #1: Relational Or Non-Relational Database?
|
||||
|
||||
Blocked by: <slug>, <slug>
|
||||
Status: open | in-progress | resolved
|
||||
Type: Research | Prototype | Grilling | Task
|
||||
Blocked by: #<ticket-number>, #<ticket-number>
|
||||
Type: Research | Prototype | Grilling
|
||||
|
||||
### Question
|
||||
|
||||
@@ -34,75 +31,54 @@ Type: Research | Prototype | Grilling | Task
|
||||
<answer-here>
|
||||
```
|
||||
|
||||
The slug is the canonical id, used in every `Blocked by` edge and prose
|
||||
reference; the title after the colon is optional. A ticket
|
||||
is **unblocked** when every ticket in its `Blocked by` list is `resolved`. A
|
||||
session **claims** its ticket by setting `Status: in-progress` and saving the map
|
||||
before any work, so concurrent sessions skip it.
|
||||
|
||||
Each ticket must be sized to one 100K token agent session.
|
||||
|
||||
## Ticket Types
|
||||
|
||||
There are four types of tickets:
|
||||
There are three types of tickets:
|
||||
|
||||
- **Research**: Reading documentation, third-party API's, or local resources like knowledge bases. Creates a markdown summary as an asset. Use this when knowledge outside the current working directory is required.
|
||||
- **Prototype**: Raise the fidelity of the discussion by making a cheap, rough, concrete artifact to react to — an outline, a rough take, a stub, or UI/logic code via the /prototype skill. Creates the prototype as an asset. Use this when "how should it look" or "how should it behave" is the key question.
|
||||
- **Grilling**: Conversation with the agent. Uses the /grilling and /domain-modeling skills. Asks one question at a time. The default case.
|
||||
- **Task**: Literal manual work that must be done before the discussion can move forward — nothing to decide, prototype, or research. Moving data from one place to another, signing up for a third-party service, provisioning access. The agent automates it where it can; otherwise it hands the human a precise checklist to do by hand. Resolved when the work is done; the answer records what was done and any resulting facts (credentials location, new URLs, row counts) later tickets depend on.
|
||||
- **Prototype**: Writing UI or logic code to test a hypothesis, or to explore a design space. Uses the /prototype skill. Creates a prototype as an asset. Use this when "how should it look" or "how should it behave" is the key question.
|
||||
- **Grilling**: Conversation with the agent. Uses the /grilling and /domain-modelling skills. Asks one question at a time. The default case.
|
||||
|
||||
## Fog of war
|
||||
|
||||
The map is _deliberately_ incomplete beyond the frontier. Your job is to investigate the frontier, and to resolve tickets in order to push the frontier forward. Push back the fog of war, one node at a time — until the path to the finish line is clear and no tickets remain.
|
||||
The map is _deliberately_ incomplete beyond the frontier. Your job is to investigate the frontier, and to resolve tickets in order to push the frontier forward. Push back the fog of war, one node at a time.
|
||||
|
||||
At some point, the fog of war should have been pushed back far enough that the path to the finish line is clear. At that point, no more tickets will be required and the decision map can be considered 'done'.
|
||||
|
||||
## Invocation
|
||||
|
||||
Two branches. Either way, **every session ends with a [Handoff](#handoff)** — never resolve more than one ticket per session.
|
||||
There are two ways this skill can be invoked: **bootstrap** and **resume**.
|
||||
|
||||
### Create the map
|
||||
### Bootstrap
|
||||
|
||||
User invokes with a loose idea.
|
||||
|
||||
1. Run a `/grilling` and `/domain-modeling` session to surface the open decisions. Ask one question at a time.
|
||||
1. Run a /grilling + /domain-modelling session to surface the open decisions. Ask one question at a time.
|
||||
2. Write a new decision map — mostly fog, frontier identified, trivially-decidable entries resolved inline.
|
||||
3. Handoff. Map-building is one session's work; do not also resolve tickets.
|
||||
3. Stop. Map-building is one session's work; do not also resolve tickets.
|
||||
|
||||
### Work through the map
|
||||
### Resume
|
||||
|
||||
User invokes with a path to an existing map. A ticket slug is **optional** — without one, you pick the next decision, not the user.
|
||||
User invokes with a path to an existing map and a ticket number.
|
||||
|
||||
1. Load the **whole map** as context.
|
||||
2. Choose the ticket. If the user named one, use it. Otherwise pick the first `open` ticket in document order that is [unblocked](#structure). [Claim it](#structure): set `Status: in-progress` and save before any work.
|
||||
3. Resolve it, invoking skills as needed — including any the `## Notes` block names. If in doubt, use `/grilling` and `/domain-modeling`.
|
||||
4. Record the answer in the ticket's body and set `Status: resolved`.
|
||||
5. Add newly-discovered tickets with correct `Blocked by` edges. If the decisions made invalidate other parts of the map, update or delete those nodes.
|
||||
6. Handoff.
|
||||
2. Run a session to resolve the ticket, invoking skills as needed. If in doubt, use `/grilling` and `/domain-modelling`.
|
||||
3. Record what the session resolved in the ticket's body.
|
||||
4. Add newly-discovered tickets (with correct `blocked_by` edges).
|
||||
5. Stop.
|
||||
|
||||
The user may run unblocked tickets in parallel, so expect other agents to be editing the map in their own sessions.
|
||||
If the decisions made invalidate other parts of the map, update or delete those nodes.
|
||||
|
||||
## Handoff
|
||||
## Parallelism
|
||||
|
||||
End every session by clearing the context and opening one or more fresh sessions. Close with a **Next steps** block the user can copy-paste. Two cases:
|
||||
The user may choose to run tickets in parallel, so expect other agents to make changes to the map.
|
||||
|
||||
**Open tickets remain.** List the currently-unblocked tickets, then give two copy-paste options: a bare command for one session (you pick the next ticket), and one pinned command per unblocked ticket for running them in parallel. Paste one line per fresh window — opening one, some, or all of them.
|
||||
## Skipping The Decision Map
|
||||
|
||||
> **Next steps** — 3 tickets unblocked: `auth-strategy`, `cache-layer`, `rate-limits`.
|
||||
> Clear the context, then open fresh sessions.
|
||||
>
|
||||
> **One session** — resolves the next unblocked ticket:
|
||||
> ```
|
||||
> Invoke /decision-mapping with the map at <path>.
|
||||
> ```
|
||||
>
|
||||
> **Parallel** — paste one line per window, up to all 3:
|
||||
> ```
|
||||
> Invoke /decision-mapping with the map at <path>, ticket auth-strategy.
|
||||
> Invoke /decision-mapping with the map at <path>, ticket cache-layer.
|
||||
> Invoke /decision-mapping with the map at <path>, ticket rate-limits.
|
||||
> ```
|
||||
Many times, the initial grilling will result in no fog of war. No unresolved tickets. Nothing to do, except implement.
|
||||
|
||||
**No open tickets remain.** The fog is pushed back far enough that the path to the finish line is clear — the map is done. (The initial grilling may also surface no fog at all, in which case there was never a map to build.) Recommend implementing directly, or using `/to-prd` to schedule a multi-session implementation.
|
||||
In those situations, you should offer the user the chance to skip the decision map - since the decision map is only needed if multi-session decisions need to be made.
|
||||
|
||||
## Notes
|
||||
|
||||
An optional block declaring the **domain**, any skills every session should `consult`, and freeform standing preferences the planning surfaces.
|
||||
If they skip it, you should recommend either implementing directly or using `/to-prd` to schedule a multi-session implementation.
|
||||
|
||||
@@ -35,26 +35,6 @@ Look for the originating spec, in this order:
|
||||
|
||||
Anything in the repo that documents how code should be written, such as `CODING_STANDARDS.md` or `CONTRIBUTING.md`.
|
||||
|
||||
On top of whatever the repo documents, the Standards axis always carries the **smell baseline** below — a fixed set of Fowler code smells (_Refactoring_, ch.3) that applies even when a repo documents nothing. Two rules bind it:
|
||||
|
||||
- **The repo overrides.** A documented repo standard always wins; where it endorses something the baseline would flag, suppress the smell.
|
||||
- **Always a judgement call.** Each smell is a labelled heuristic ("possible Feature Envy"), never a hard violation — and, like any standard here, skip anything tooling already enforces.
|
||||
|
||||
Each smell reads *what it is* → *how to fix*; match it against the diff:
|
||||
|
||||
- **Mysterious Name** — a function, variable, or type whose name doesn't reveal what it does or holds. → rename it; if no honest name comes, the design's murky.
|
||||
- **Duplicated Code** — the same logic shape appears in more than one hunk or file in the change. → extract the shared shape, call it from both.
|
||||
- **Feature Envy** — a method that reaches into another object's data more than its own. → move the method onto the data it envies.
|
||||
- **Data Clumps** — the same few fields or params keep travelling together (a type wanting to be born). → bundle them into one type, pass that.
|
||||
- **Primitive Obsession** — a primitive or string standing in for a domain concept that deserves its own type. → give the concept its own small type.
|
||||
- **Repeated Switches** — the same `switch`/`if`-cascade on the same type recurs across the change. → replace with polymorphism, or one map both sites share.
|
||||
- **Shotgun Surgery** — one logical change forces scattered edits across many files in the diff. → gather what changes together into one module.
|
||||
- **Divergent Change** — one file or module is edited for several unrelated reasons. → split so each module changes for one reason.
|
||||
- **Speculative Generality** — abstraction, parameters, or hooks added for needs the spec doesn't have. → delete it; inline back until a real need shows.
|
||||
- **Message Chains** — long `a.b().c().d()` navigation the caller shouldn't depend on. → hide the walk behind one method on the first object.
|
||||
- **Middle Man** — a class or function that mostly just delegates onward. → cut it, call the real target direct.
|
||||
- **Refused Bequest** — a subclass or implementer that ignores or overrides most of what it inherits. → drop the inheritance, use composition.
|
||||
|
||||
### 4. Spawn both sub-agents in parallel
|
||||
|
||||
Send a single message with two `Agent` tool calls. Use the `general-purpose` subagent for both.
|
||||
@@ -62,8 +42,8 @@ Send a single message with two `Agent` tool calls. Use the `general-purpose` sub
|
||||
**Standards sub-agent prompt** — include:
|
||||
|
||||
- The full diff command and commit list.
|
||||
- The list of standards-source files you found in step 3, **plus the smell baseline from step 3** pasted in full — the sub-agent has no other access to it.
|
||||
- The brief: "Report — per file/hunk where relevant — (a) every place the diff violates a documented standard: cite the standard (file + the rule); and (b) any baseline smell you spot: name it and quote the hunk. Distinguish hard violations from judgement calls — documented-standard breaches can be hard, but baseline smells are always judgement calls, and a documented repo standard overrides the baseline. Skip anything tooling enforces. Under 400 words."
|
||||
- The list of standards-source files you found in step 3.
|
||||
- The brief: "Report — per file/hunk where relevant — every place the diff violates a documented standard. Cite the standard (file + the rule). Distinguish hard violations from judgement calls. Skip anything tooling enforces. Under 400 words."
|
||||
|
||||
**Spec sub-agent prompt** — include:
|
||||
|
||||
|
||||
@@ -1,45 +0,0 @@
|
||||
---
|
||||
name: wizard
|
||||
description: Generate an interactive bash wizard that walks a human through a manual procedure — third-party setup, a one-off migration, an A→B state transition — opening URLs, capturing values, confirming each step, and writing .env files and GitHub Actions secrets.
|
||||
disable-model-invocation: true
|
||||
---
|
||||
|
||||
# Wizard
|
||||
|
||||
A **wizard** is a bash script that walks a human, step by step, through a manual procedure that's tedious to do by hand and tedious to re-explain to an AI every time. It opens each URL, says exactly what to click and copy, captures the values, writes them where they belong (`.env`, GitHub secrets), confirms at every stage, and shows how much is left. It might configure third-party services, run a one-off migration, or move the project from one state to another.
|
||||
|
||||
The delightful UX is already solved by [template.sh](template.sh) — progress with time-remaining, confirmation gates, cross-platform URL opening (including WSL), hidden secret entry, idempotent `.env` upserts, `gh secret`/`gh variable` writes, and a closing summary. **Your job is only to scope the procedure and author its stages.** The library above the `STAGES` marker is identical in every wizard; that consistency is the point — never hand-edit it.
|
||||
|
||||
A wizard is ephemeral by default — built for one run, saved to a scratch or `scripts/` path, deleted when the job's done. Commit it only when the user wants a repeatable setup path that should live in the repo.
|
||||
|
||||
## Process
|
||||
|
||||
### 1. Scope the procedure
|
||||
|
||||
Work out every manual step the human must take and every value that gets captured along the way. Read the repo first — don't ask cold:
|
||||
|
||||
- For setup: `.env`, `.env.example`, `.env.*`, `README`, `docker-compose*`, framework config, and `.github/workflows/*` (every `secrets.*` / `vars.*` reference is a value the wizard must produce).
|
||||
- For a migration or transition: the current state, the target state, and the irreversible actions between them.
|
||||
|
||||
Then show the user the ordered list of stages and the values each produces, and confirm — they may add, drop, or reorder.
|
||||
|
||||
**Done when:** every stage is named in order, and for each captured value you know (a) where the human gets it, (b) where it's written (`.env`, a GitHub secret, both, or nowhere — some stages are pure actions), and (c) whether it's secret (hidden entry) or public.
|
||||
|
||||
### 2. Map each stage's journey
|
||||
|
||||
For each stage, write the precise path a human follows: which URL to open, what to do there, where a value is shown, which variable it fills — e.g. "Dashboard → Developers → API keys → Reveal test key → copy". Where you don't actually know the current UI or the exact command, say so and ask the user or check the docs — never invent steps that may not exist.
|
||||
|
||||
**Done when:** every stage traces to concrete instructions a stranger could follow.
|
||||
|
||||
### 3. Author the wizard
|
||||
|
||||
Copy `template.sh` to the target path. Replace the example stage with one `stage` per step, in dependency order. Use the library helpers — `stage`, `say`/`step`, `open_url`, `ask`/`ask_secret`, `write_env`, `set_secret`/`set_var`, `pause`/`confirm` — and set `TOTAL_STAGES` and `TOTAL_MINUTES` to honest estimates (this drives the time-remaining display).
|
||||
|
||||
Hold the bar the template sets: open the URL before asking for its value, use `ask_secret` for anything secret, `write_env` every persisted value, `set_secret` only the values CI actually needs, and `confirm` before any irreversible action. Each `stage` clears the screen so only the current step is visible — keep a stage to one focused task so nothing the human needs scrolls away. Don't touch the library above the marker.
|
||||
|
||||
### 4. Verify and hand off
|
||||
|
||||
- `bash -n <script>`; run `shellcheck` if available.
|
||||
- `chmod +x <script>`.
|
||||
- Don't run it end-to-end yourself — it opens browsers and blocks on human input. Trace it statically instead: every value from step 1 is captured and lands where step 1 said, and every `set_secret` name exactly matches a `secrets.*` reference in CI.
|
||||
- Tell the user how to run it. If it's a repeatable setup path, commit it and link it from the README so the next person runs the script instead of asking an AI.
|
||||
@@ -1,211 +0,0 @@
|
||||
#!/usr/bin/env bash
|
||||
#
|
||||
# A wizard — walks a human through a manual procedure step by step.
|
||||
# Generated by the /wizard skill.
|
||||
#
|
||||
# Everything above the "STAGES" marker is the wizard library: do not hand-edit
|
||||
# it. Author the per-step stages below the marker.
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
# ──────────────────────────────────────────────────────────────────────────
|
||||
# Wizard library — delightful, consistent UX. Identical across every wizard.
|
||||
# ──────────────────────────────────────────────────────────────────────────
|
||||
|
||||
if [[ -t 1 ]] && command -v tput >/dev/null 2>&1 && [[ "$(tput colors 2>/dev/null || echo 0)" -ge 8 ]]; then
|
||||
BOLD=$(tput bold); DIM=$(tput dim); RESET=$(tput sgr0)
|
||||
BLUE=$(tput setaf 4); GREEN=$(tput setaf 2); YELLOW=$(tput setaf 3); RED=$(tput setaf 1)
|
||||
else
|
||||
BOLD=""; DIM=""; RESET=""; BLUE=""; GREEN=""; YELLOW=""; RED=""
|
||||
fi
|
||||
|
||||
# Author sets these two at the top of the stages section.
|
||||
TOTAL_STAGES=0
|
||||
TOTAL_MINUTES=0
|
||||
|
||||
_STAGE_INDEX=0
|
||||
_MINUTES_ELAPSED=0
|
||||
ENV_FILE="${ENV_FILE:-.env}"
|
||||
WRITTEN_ENV=() # KEYs written to ENV_FILE this run
|
||||
WRITTEN_SECRET=() # secret NAMEs set this run
|
||||
SKIPPED=() # things we couldn't do (e.g. gh missing)
|
||||
|
||||
# _clear — wipe the terminal so only the current step is on screen. No-op when
|
||||
# output isn't a terminal, so piped logs stay readable.
|
||||
_clear() {
|
||||
[[ -t 1 ]] || return 0
|
||||
if command -v tput >/dev/null 2>&1; then tput clear; else printf '\033[2J\033[3J\033[H'; fi
|
||||
}
|
||||
|
||||
# banner "Title" — opening frame: what this wizard does and how long it takes.
|
||||
banner() {
|
||||
_clear
|
||||
printf '\n%s%s %s%s\n' "$BOLD" "$BLUE" "$1" "$RESET"
|
||||
printf '%s %s stages · about %s minutes%s\n\n' \
|
||||
"$DIM" "$TOTAL_STAGES" "$TOTAL_MINUTES" "$RESET"
|
||||
printf '%s You drive the browser; this wizard tells you exactly what to do and\n' "$DIM"
|
||||
printf ' captures the values you copy back. Stop any time with Ctrl-C and re-run\n'
|
||||
printf ' later — it remembers values already saved.%s\n' "$RESET"
|
||||
pause "Ready to start?"
|
||||
}
|
||||
|
||||
# stage "Name" <minutes> — clear the screen, then announce a stage and show
|
||||
# progress + time remaining. Clearing keeps only the current step on screen.
|
||||
stage() {
|
||||
_clear
|
||||
_STAGE_INDEX=$((_STAGE_INDEX + 1))
|
||||
local remaining=$((TOTAL_MINUTES - _MINUTES_ELAPSED))
|
||||
(( remaining < 0 )) && remaining=0
|
||||
_MINUTES_ELAPSED=$((_MINUTES_ELAPSED + ${2:-0}))
|
||||
printf '\n%s%s▸ Stage %s/%s · %s%s %s(~%s min left)%s\n' \
|
||||
"$BOLD" "$BLUE" "$_STAGE_INDEX" "$TOTAL_STAGES" "$1" "$RESET" "$DIM" "$remaining" "$RESET"
|
||||
}
|
||||
|
||||
# say "..." — a plain instruction line.
|
||||
say() { printf ' %s\n' "$1"; }
|
||||
# step "..." — a numbered-feeling action the human takes in the browser.
|
||||
step() { printf ' %s•%s %s\n' "$BLUE" "$RESET" "$1"; }
|
||||
note() { printf ' %s%s%s\n' "$DIM" "$1" "$RESET"; }
|
||||
warn() { printf ' %s⚠ %s%s\n' "$YELLOW" "$1" "$RESET"; }
|
||||
|
||||
# open_url URL — open in the human's browser, cross-platform incl. WSL.
|
||||
open_url() {
|
||||
local url="$1"
|
||||
printf ' %s↗ opening%s %s\n' "$GREEN" "$RESET" "$url"
|
||||
{ if command -v wslview >/dev/null 2>&1; then wslview "$url"
|
||||
elif command -v explorer.exe >/dev/null 2>&1; then explorer.exe "$url"
|
||||
elif command -v xdg-open >/dev/null 2>&1; then xdg-open "$url"
|
||||
elif command -v open >/dev/null 2>&1; then open "$url"
|
||||
else warn "couldn't open a browser — visit it manually: $url"; fi
|
||||
} >/dev/null 2>&1 || warn "couldn't open a browser — visit it manually: $url"
|
||||
}
|
||||
|
||||
# pause "msg" — wait for the human to confirm they've done the manual part.
|
||||
pause() {
|
||||
printf ' %s%s%s ' "$DIM" "${1:-Press Enter to continue}" "$RESET"
|
||||
read -r _ || true
|
||||
}
|
||||
|
||||
# confirm "question" — y/N gate; returns success on yes.
|
||||
confirm() {
|
||||
local reply=""
|
||||
printf ' %s? %s [y/N] ' "$YELLOW" "$1"
|
||||
read -r reply || true
|
||||
[[ "$reply" =~ ^[Yy] ]]
|
||||
}
|
||||
|
||||
# _existing KEY — current value of KEY in ENV_FILE, if any.
|
||||
_existing() {
|
||||
[[ -f "$ENV_FILE" ]] || return 1
|
||||
local line; line=$(grep -E "^${1}=" "$ENV_FILE" | tail -n1) || return 1
|
||||
printf '%s' "${line#*=}"
|
||||
}
|
||||
|
||||
# ask KEY "Prompt" — read a value into $KEY. Offers the existing .env value as
|
||||
# a default on re-runs (Enter keeps it). Visible input (non-secret).
|
||||
ask() {
|
||||
local key="$1" prompt="$2" current input
|
||||
current=$(_existing "$key" || true)
|
||||
if [[ -n "$current" ]]; then
|
||||
printf ' %s%s%s %s[Enter keeps current]%s ' "$BOLD" "$prompt" "$RESET" "$DIM" "$RESET"
|
||||
else
|
||||
printf ' %s%s%s ' "$BOLD" "$prompt" "$RESET"
|
||||
fi
|
||||
read -r input || true
|
||||
[[ -z "$input" && -n "$current" ]] && input="$current"
|
||||
printf -v "$key" '%s' "$input"
|
||||
}
|
||||
|
||||
# ask_secret KEY "Prompt" — like ask, but input is hidden.
|
||||
ask_secret() {
|
||||
local key="$1" prompt="$2" current input
|
||||
current=$(_existing "$key" || true)
|
||||
if [[ -n "$current" ]]; then
|
||||
printf ' %s%s%s %s[Enter keeps current]%s ' "$BOLD" "$prompt" "$RESET" "$DIM" "$RESET"
|
||||
else
|
||||
printf ' %s%s%s ' "$BOLD" "$prompt" "$RESET"
|
||||
fi
|
||||
read -rs input || true
|
||||
printf '\n'
|
||||
[[ -z "$input" && -n "$current" ]] && input="$current"
|
||||
printf -v "$key" '%s' "$input"
|
||||
}
|
||||
|
||||
# write_env KEY VALUE — upsert KEY=VALUE into ENV_FILE (creates it; replaces
|
||||
# any existing line). Idempotent.
|
||||
write_env() {
|
||||
local key="$1" value="$2" tmp
|
||||
touch "$ENV_FILE"
|
||||
tmp=$(mktemp)
|
||||
grep -vE "^${key}=" "$ENV_FILE" > "$tmp" || true
|
||||
printf '%s=%s\n' "$key" "$value" >> "$tmp"
|
||||
mv "$tmp" "$ENV_FILE"
|
||||
WRITTEN_ENV+=("$key")
|
||||
printf ' %s✓ wrote%s %s → %s\n' "$GREEN" "$RESET" "$key" "$ENV_FILE"
|
||||
}
|
||||
|
||||
# set_secret NAME VALUE — set a GitHub Actions repo secret via gh. Falls back
|
||||
# to a warning (and records it) if gh is unavailable or unauthenticated.
|
||||
set_secret() {
|
||||
local name="$1" value="$2"
|
||||
if command -v gh >/dev/null 2>&1 && gh auth status >/dev/null 2>&1; then
|
||||
if printf '%s' "$value" | gh secret set "$name" >/dev/null 2>&1; then
|
||||
WRITTEN_SECRET+=("$name")
|
||||
printf ' %s✓ set%s GitHub secret %s\n' "$GREEN" "$RESET" "$name"
|
||||
return
|
||||
fi
|
||||
fi
|
||||
SKIPPED+=("GitHub secret $name (set it manually: gh secret set $name)")
|
||||
warn "skipped GitHub secret $name — gh not ready; set it later"
|
||||
}
|
||||
|
||||
# set_var NAME VALUE — set a GitHub Actions repo variable (non-secret).
|
||||
set_var() {
|
||||
local name="$1" value="$2"
|
||||
if command -v gh >/dev/null 2>&1 && gh auth status >/dev/null 2>&1; then
|
||||
if gh variable set "$name" --body "$value" >/dev/null 2>&1; then
|
||||
printf ' %s✓ set%s GitHub variable %s\n' "$GREEN" "$RESET" "$name"
|
||||
return
|
||||
fi
|
||||
fi
|
||||
SKIPPED+=("GitHub variable $name")
|
||||
warn "skipped GitHub variable $name — gh not ready; set it later"
|
||||
}
|
||||
|
||||
# finish — clear, then a closing summary of everything configured.
|
||||
finish() {
|
||||
_clear
|
||||
printf '\n%s%s ✓ Setup complete%s\n' "$BOLD" "$GREEN" "$RESET"
|
||||
(( ${#WRITTEN_ENV[@]} )) && note "wrote ${#WRITTEN_ENV[@]} value(s) to $ENV_FILE: ${WRITTEN_ENV[*]}"
|
||||
(( ${#WRITTEN_SECRET[@]} )) && note "set ${#WRITTEN_SECRET[@]} GitHub secret(s): ${WRITTEN_SECRET[*]}"
|
||||
if (( ${#SKIPPED[@]} )); then
|
||||
printf '\n'; warn "still to do by hand:"
|
||||
for s in "${SKIPPED[@]}"; do note " - $s"; done
|
||||
fi
|
||||
printf '\n'
|
||||
}
|
||||
|
||||
# ──────────────────────────────────────────────────────────────────────────
|
||||
# STAGES — author this section. One stage() per step the human takes.
|
||||
# Replace the example below. Set the two totals to match the stages you write.
|
||||
# ──────────────────────────────────────────────────────────────────────────
|
||||
|
||||
TOTAL_STAGES=1
|
||||
TOTAL_MINUTES=5
|
||||
|
||||
banner "Stripe setup"
|
||||
|
||||
# ── Example stage: replace with your real steps ───────────────────────────
|
||||
stage "Stripe — API keys" 5
|
||||
say "We'll grab your Stripe test keys and store them for local dev + CI."
|
||||
open_url "https://dashboard.stripe.com/test/apikeys"
|
||||
step "On the API keys page, copy the Publishable key (starts pk_test_)."
|
||||
ask STRIPE_PUBLISHABLE_KEY "Paste the publishable key:"
|
||||
step "Click 'Reveal test key' on the Secret key row, then copy it."
|
||||
ask_secret STRIPE_SECRET_KEY "Paste the secret key:"
|
||||
write_env STRIPE_PUBLISHABLE_KEY "$STRIPE_PUBLISHABLE_KEY"
|
||||
write_env STRIPE_SECRET_KEY "$STRIPE_SECRET_KEY"
|
||||
set_secret STRIPE_SECRET_KEY "$STRIPE_SECRET_KEY" # CI needs this one
|
||||
# ──────────────────────────────────────────────────────────────────────────
|
||||
|
||||
finish
|
||||
@@ -1,42 +1,25 @@
|
||||
---
|
||||
name: writing-beats
|
||||
description: Writing, exploit — assemble raw material into a journey of beats, grounding each term before a beat leans on it.
|
||||
disable-model-invocation: true
|
||||
description: Shape an article as a journey of beats, choose-your-own-adventure style. The user picks a starting beat from the raw material, you write only that beat, then offer options for where to pivot next, beat by beat, until the article reaches a natural end. Use when the user has raw material and wants to assemble it as a narrative rather than an argument.
|
||||
---
|
||||
|
||||
<what-to-do>
|
||||
|
||||
The user has passed (or will pass) a markdown file of raw material. This is **exploit**: the exploring is done, the pile is fixed — commit to a path through it and mine the pile to fill each beat.
|
||||
The user has passed (or will pass) a markdown file of raw material.
|
||||
|
||||
If the user did not say where to save the article, ask once and remember the path.
|
||||
|
||||
Then run a beat-by-beat journey, choose-your-own-adventure style:
|
||||
Then run a beat-by-beat journey:
|
||||
|
||||
1. **Establish the prerequisites.** Before any beats, settle with the user what the audience already knows walking in — the concepts that are **grounded** from the start. Everything else must be grounded by a beat before a later beat can use it. See [Grounding](#grounding).
|
||||
2. Write 2–3 candidate **starting beats**, drawn from the raw material. Each is a different entry point into the article. Each may only lean on grounded concepts; note what new concepts each one grounds. Show the user the beats before writing to the article file. The user picks one. Preview what beats that pick unlocks — as if the user is seeing a little way down the path.
|
||||
3. Once the user picks a starting beat, write **only that beat** to the article file. A beat may be one sentence or several paragraphs — whatever that beat naturally is. Stop there.
|
||||
4. Re-read the article file from disk. Then offer 2–3 candidate **next beats** — different directions the journey could pivot to from where the article now stands. Each must be reachable from the current grounded set; note what each one grounds.
|
||||
5. Loop steps 3–5 until the article reaches a natural end.
|
||||
1. Write 2–3 candidate **starting beats**, drawn from the raw material. Each is a different entry point into the article. Show the user the beats before writing it to the article file. The user picks one. Preview what beats that might lead to once written - as if the user is seeing a little way down the path.
|
||||
2. Once the user picks a starting beat, write **only that beat** to the article file. A beat may be one sentence or several paragraphs — whatever that beat naturally is. Stop there.
|
||||
3. Re-read the article file from disk. Then offer 2–3 candidate **next beats** — different directions the journey could pivot to from where the article now stands.
|
||||
4. Loop steps 2–4 until the article reaches a natural end.
|
||||
|
||||
</what-to-do>
|
||||
|
||||
<supporting-info>
|
||||
|
||||
## Grounding
|
||||
|
||||
Every **concept** has to be **grounded** before a beat can lean on it: the audience either walked in knowing it or met it in an earlier beat. A beat that reaches for an ungrounded concept loses the reader — that is the one move the journey can't make. The unit is the concept, not the word for it: a beat can lean on an idea the reader lacks even with no jargon in sight. Where a concept has a name — a **term** — grounding it means landing the idea and the term together.
|
||||
|
||||
A concept gets grounded one of two ways:
|
||||
|
||||
- **Prerequisite** — grounded before the first beat. The audience brings it. Fixed at the start.
|
||||
- **Introduced** — a beat establishes it, and from then on it's grounded for every later beat.
|
||||
|
||||
So each beat does two jobs: it **requires** concepts that are already grounded, and it **grounds** new ones. Keep a running list of what's grounded so far, and update it each time a beat lands.
|
||||
|
||||
This is what shapes the choose-your-own-adventure. A candidate beat is only reachable if everything it requires is already grounded; picking a beat that grounds concept X unlocks every beat that was waiting on X. When you offer next beats, they must all be reachable from the current grounded set — and say what each one grounds, so the user can see which paths it opens.
|
||||
|
||||
The big lever is what you make a prerequisite versus what you ground inside the piece. Demand too much up front and you shut out readers who don't have it; ground too much inside and the early beats drown in definitions. Settle this with the user when you establish prerequisites, and revisit it whenever a tempting beat turns out to require a concept nothing has grounded yet — the fix is either a grounding beat before it, or promoting the concept to a prerequisite.
|
||||
|
||||
## What is a beat
|
||||
|
||||
A beat is one move in the journey. It does one thing — sets a scene, lands a point, asks a question, drops an aside, twists the angle. Then it stops, leaving the reader at a place where the next beat can pivot.
|
||||
@@ -49,9 +32,11 @@ A beat is sized by what it needs:
|
||||
|
||||
If a "beat" needs five paragraphs and three subheadings, it's not a beat — it's two beats glued together. Split it.
|
||||
|
||||
## Pulling from the pile
|
||||
## Writing one beat
|
||||
|
||||
Pull material from the raw pile to populate each beat. You can paraphrase, split, recombine, or quote. The pile is a quarry.
|
||||
Once a beat is picked, write _that beat only_ to the article file. Do not write the next beat.
|
||||
|
||||
Pull material from the raw pile to populate the beat. You can paraphrase, split, recombine, or quote. The pile is a quarry.
|
||||
|
||||
## Ending the journey
|
||||
|
||||
|
||||
@@ -1,14 +1,13 @@
|
||||
---
|
||||
name: writing-fragments
|
||||
description: Writing, explore — mine raw fragments, no structure yet.
|
||||
disable-model-invocation: true
|
||||
description: Grilling session that mines the user for fragments — heterogeneous nuggets of writing (claims, vignettes, sharp sentences, half-thoughts) — and appends them to a single document as raw material for a future article. Use when the user wants to develop ideas before imposing structure, or mentions "fragments", "ideate", or "raw material" for writing.
|
||||
---
|
||||
|
||||
<what-to-do>
|
||||
|
||||
This is pure **explore**: widen the space of what could be written without committing to structure — committing is _exploit_, a separate skill's job. Run a grilling session that produces fragments, interviewing the user relentlessly about whatever they want to write about. Imposing phases, outlines, or article structure is out of scope here.
|
||||
Run a grilling session that produces fragments. Interview the user relentlessly about whatever they want to write about. Do not impose phases, outlines, or structure — that is explicitly out of scope.
|
||||
|
||||
As fragments emerge from either side of the conversation, append them to a single markdown file.
|
||||
As fragments emerge from either side of the conversation, append them to a single markdown file. The user will be editing this file during the session; always re-read it before writing so their edits are preserved.
|
||||
|
||||
If the user did not pass a path, ask once where to save the document, then remember it for the rest of the session.
|
||||
|
||||
@@ -33,9 +32,6 @@ Fragments are deliberately heterogeneous. Examples of what could be a fragment:
|
||||
- A quote, a piece of dialogue, an overheard line.
|
||||
- A list of related observations that hang together by feel.
|
||||
- A complaint, a confession, a punchline.
|
||||
- A **leading word** — a compact metaphor or coinage the whole piece can hang on (one term that names the idea, the way _tracer bullets_ or _fog of war_ names a whole pattern).
|
||||
|
||||
Of these, the leading word is the most valuable fragment to land. It is load-bearing: name the right one in explore and it shapes the structure, the transitions, and the title later — paying dividends through the entire exploit phase. When the conversation circles a recurring idea, push to coin a word for it.
|
||||
|
||||
The novelist's diary is the model: years of unstructured noticings that later get mined for raw material. Fragments are noticings.
|
||||
|
||||
|
||||
@@ -1,16 +1,15 @@
|
||||
---
|
||||
name: writing-shape
|
||||
description: Writing, exploit — shape raw material into an article, paragraph by paragraph.
|
||||
disable-model-invocation: true
|
||||
description: Take a markdown file of raw material and shape it into an article through a conversational session — drafting candidate openings, growing the piece paragraph by paragraph, arguing about format (lists, tables, callouts, quotes) at each step. Use when the user has a pile of notes, fragments, or a rough draft and wants help turning it into something publishable.
|
||||
---
|
||||
|
||||
<what-to-do>
|
||||
|
||||
The user has passed (or will pass) a markdown file of raw material. Treat it as the input pile — anything from a tidy list of fragments to a wall of unstructured prose to a transcript. The format does not matter. Read it end-to-end before doing anything else.
|
||||
|
||||
Then run a shaping session that produces a separate article document. This is **exploit**: the exploring is done, the pile is fixed — commit to a structure and mine the pile to fill it. Do not edit the raw material file — it is read-only to this skill.
|
||||
Then run a shaping session that produces a separate article document. Do not edit the raw material file — it is read-only to this skill.
|
||||
|
||||
If the user did not say where to save the article, ask once and remember the path.
|
||||
If the user did not say where to save the article, ask once and remember the path. The user will be editing the article file during the session; always re-read it before writing so their edits are preserved.
|
||||
|
||||
</what-to-do>
|
||||
|
||||
@@ -19,24 +18,10 @@ If the user did not say where to save the article, ask once and remember the pat
|
||||
## The loop
|
||||
|
||||
1. **Read the pile.** Read the input file in full. Form a sense of what's in it.
|
||||
2. **Establish the prerequisites.** Settle with the user what the reader knows walking in — the concepts that are **grounded** from the start. Everything else must be grounded by a block before a later block can lean on it. See [Grounding](#grounding).
|
||||
3. **Draft 2–3 candidate openings.** Each opening should imply a different thesis or angle for the article. Show all of them. Force the user to pick or compose a hybrid. The chosen opening defines what the rest of the article must do.
|
||||
4. **Grow paragraph by paragraph.** After the opening lands, ask "given this opening, what does the reader need to hear next?" Pull material from the pile to answer. The next block may only lean on grounded concepts, and grounds new ones as it lands. Argue about the form the next block takes — a paragraph, a list, a table, a callout, a quote, a code block. Each format choice should be deliberate and defensible.
|
||||
5. **Append to the article file as you go.** Don't batch. Write each agreed paragraph or block immediately so the user can see the article taking shape.
|
||||
6. **Loop step 4 until the article is done.** The user decides when it's done.
|
||||
|
||||
## Grounding
|
||||
|
||||
Every **concept** has to be **grounded** before a block can lean on it: the reader either walked in knowing it or met it in an earlier block. A block that reaches for an ungrounded concept loses the reader. The unit is the concept, not the word for it — a block can lean on an idea the reader lacks even with no jargon in sight. Where a concept has a name — a **term** — grounding it means landing the idea and the term together.
|
||||
|
||||
A concept gets grounded one of two ways:
|
||||
|
||||
- **Prerequisite** — grounded before the opening. The reader brings it. Fixed at the start.
|
||||
- **Introduced** — a block establishes it, and from then on it's grounded for the rest of the article.
|
||||
|
||||
Keep a running list of what's grounded. When you ask "what does the reader need to hear next?", an ungrounded concept the next move needs is itself the answer: ground it first — here or in an earlier block — or you can't make the move. This is the gap-naming of [Pulling from the pile](#pulling-from-the-pile) one level up: there the pile is missing material; here the article is missing a foundation.
|
||||
|
||||
The lever is what you make a prerequisite versus what you ground inside the article. Demand too much up front and you shut readers out; ground too much inside and the opening drowns in definitions. Settle it with the user when you establish prerequisites.
|
||||
2. **Draft 2–3 candidate openings.** Each opening should imply a different thesis or angle for the article. Show all of them. Force the user to pick or compose a hybrid. The chosen opening defines what the rest of the article must do.
|
||||
3. **Grow paragraph by paragraph.** After the opening lands, ask "given this opening, what does the reader need to hear next?" Pull material from the pile to answer. Argue about whether the next beat is a paragraph, a list, a table, a callout, a quote, a code block. Each format choice should be deliberate and defensible.
|
||||
4. **Append to the article file as you go.** Don't batch. Write each agreed paragraph or block immediately so the user can see the article taking shape.
|
||||
5. **Loop step 3 until the article is done.** The user decides when it's done.
|
||||
|
||||
## Conversational feel
|
||||
|
||||
@@ -58,7 +43,7 @@ If the pile lacks something the article needs, name the gap explicitly: "We need
|
||||
|
||||
## Format arguments to actually have
|
||||
|
||||
When choosing how to render a block, weigh these tradeoffs out loud with the user, not silently:
|
||||
When choosing how to render a beat, weigh these tradeoffs out loud with the user, not silently:
|
||||
|
||||
- **Prose vs. list.** Prose carries argument; lists carry parallel items. If items aren't truly parallel, prose is better. If they are, a list is faster to scan.
|
||||
- **Inline vs. callout.** Tips, warnings, and asides go in callouts (`> [!TIP]`, `> [!NOTE]`) — but only if they'd genuinely derail the main argument inline. Otherwise leave them inline.
|
||||
@@ -72,7 +57,7 @@ Append to the article file as each block is agreed. Re-read the file from disk b
|
||||
|
||||
## Out of scope
|
||||
|
||||
- Mining for new fragments that aren't in the pile (handle gaps as in "Pulling from the pile").
|
||||
- Mining for new fragments that aren't in the pile (the pile is the input — if it's incomplete, name the gap and either get the user to fill it or cut the section).
|
||||
- Editing the raw material file.
|
||||
- Publishing, formatting for a specific platform, or adding frontmatter the user didn't ask for.
|
||||
|
||||
|
||||
Reference in New Issue
Block a user