mirror of
https://github.com/mattpocock/skills.git
synced 2026-09-12 18:38:06 +07:00
refactor: flatten skills/ and adopt Agent Plugins 1.0
The Agent Plugins standard reads only the immediate children of skills/ and cannot be pointed elsewhere, so a bucketed tree is invisible to every conformant client. Rather than generate a flat copy of the repo into a package directory — a builder, a validator, an allowlist, a drift CI job, and every skill committed twice — remove the buckets. - skills/ is flat and holds exactly the promoted 25 - in-progress/ -> drafts/, misc/ -> extras/, deprecated/ deleted - new root plugin.json (Agent Plugins 1.0) - .claude-plugin/plugin.json loses its 25-entry skills array - docs/ keeps its category folders; it is now the only place a skill's category lives Reasoning in .agents/adr/0003. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5
parent
84fdeffd12
commit
a16a2674bc
@@ -0,0 +1,16 @@
|
||||
# Drafts
|
||||
|
||||
Beta. These skills are public on purpose — try them and tell me what breaks. They live outside `skills/`, so they're excluded from the plugin and the top-level README until they graduate, they get no docs pages, and they can change or disappear without warning.
|
||||
|
||||
The plugin won't give you these. Install one directly:
|
||||
|
||||
```bash
|
||||
npx skills@latest add mattpocock/skills --skill=<name>
|
||||
```
|
||||
|
||||
- **[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.
|
||||
- **[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.
|
||||
- **[claude-handoff](./claude-handoff/SKILL.md)** — Hand the current conversation off to a fresh background agent that picks up the work immediately, seeded with a handoff summary via `claude --bg`. User-invoked.
|
||||
- **[setup-ts-deep-modules](./setup-ts-deep-modules/SKILL.md)** — Wire dependency-cruiser into a TypeScript repo so each package is a deep module — implementation hidden in subfolders, reachable only through its entry-point files, tests exercising it through those. User-invoked.
|
||||
@@ -0,0 +1,18 @@
|
||||
---
|
||||
name: claude-handoff
|
||||
description: Hand the current conversation off to a fresh background agent that picks up the work immediately.
|
||||
argument-hint: "What will the next session be used for?"
|
||||
disable-model-invocation: true
|
||||
---
|
||||
|
||||
Write a handoff summary of the current conversation so a fresh agent can continue the work. Instead of saving it, launch a background agent seeded with the summary as its prompt: `claude --bg --name "<descriptive name>" "<handoff summary>"`. It starts in the current working directory and returns immediately; the user manages it with `claude agents`.
|
||||
|
||||
Always pass `-n`/`--name` with a descriptive name (e.g. `--name "Fix login bug"`) — it sets the display name shown in the job list, session picker, and terminal title.
|
||||
|
||||
Include a "suggested skills" section in the summary, which suggests skills that the agent should invoke.
|
||||
|
||||
Do not duplicate content already captured in other artifacts (specs, plans, ADRs, issues, commits, diffs). Reference them by path or URL instead.
|
||||
|
||||
Redact any sensitive information, such as API keys, passwords, or personally identifiable information — the summary becomes the agent's prompt.
|
||||
|
||||
If the user passed arguments, treat them as a description of what the next session will focus on and tailor the summary accordingly.
|
||||
@@ -0,0 +1,5 @@
|
||||
interface:
|
||||
display_name: "Claude Handoff"
|
||||
short_description: "Hand off to a background agent"
|
||||
policy:
|
||||
allow_implicit_invocation: false
|
||||
@@ -0,0 +1,32 @@
|
||||
---
|
||||
name: loop-me
|
||||
description: Grill me about specs for the workflows I want to build, within this workspace.
|
||||
disable-model-invocation: true
|
||||
argument-hint: "A workflow to design, or nothing to go find one"
|
||||
---
|
||||
|
||||
Run a stateful `/grilling` session whose only output is **workflow** specs. Use the grilling discipline — relentless, a round of questions at a time, a recommended answer attached to each — aimed at the vocabulary and goal below. Create, edit, and delete specs as the grilling resolves things.
|
||||
|
||||
## The loop lens
|
||||
|
||||
A **loop** is a recurring pattern in the user's life: their career, their week, their morning, a single repeated activity. Picturing a life as loops within loops reveals how predictable its activities really are — which is what makes them worth **delegating**. Use the lens to find loops worth specifying, and propose ones the user hasn't noticed.
|
||||
|
||||
A **workflow** is the spec of one loop, made real. You run a workflow on a loop — the loop is its running instantiation. Workflows live in `workflows/*.md` and are the source of truth.
|
||||
|
||||
## Vocabulary
|
||||
|
||||
A shared language, reached for only when a workflow calls for it — never a checklist. **Mandate nothing structural**: a workflow needs no AI, no checkpoint, and no schedule unless the grilling shows it does.
|
||||
|
||||
- **Trigger** — what fires each run: an **event** (a new email, a new issue) or a **schedule** (every morning). Event-triggering is usually the more efficient.
|
||||
- **Checkpoint** — a human-in-the-loop point where the user is asked to verify or decide. Some workflows have none and run autonomously; some use no AI at all.
|
||||
- **Push right** — defer the checkpoint as far as it will go. Do maximal work before involving the human, so they are asked once, late, with everything prepared.
|
||||
- **Brief** — what a checkpoint presents: a tight, decision-ready summary — what was produced, why, and a link down to the asset itself — never the raw output. The user reads a brief, not a draft. Speed of review is imperative.
|
||||
|
||||
## Definition of done
|
||||
|
||||
A workflow spec is done when an implementer agent could build it without asking a single question. Grill until then; nothing is done while a question remains.
|
||||
|
||||
## The workspace
|
||||
|
||||
- `workflows/*.md` — one spec per workflow.
|
||||
- `NOTES.md` — raw notes on the user's world: the tools they use, the channels they process, and their own terminology for both. When it is empty or thin, interview them about their world before specifying anything. Sharpen fuzzy terms into canonical ones as they surface, and record them here.
|
||||
@@ -0,0 +1,5 @@
|
||||
interface:
|
||||
display_name: "Loop Me"
|
||||
short_description: "Spec the workflows you want to build"
|
||||
policy:
|
||||
allow_implicit_invocation: false
|
||||
@@ -0,0 +1,102 @@
|
||||
---
|
||||
name: setup-ts-deep-modules
|
||||
description: Wire dependency-cruiser into a TypeScript repo so each package is a deep module — implementation hidden in subfolders, reachable only through its entry-point files. User-invoked.
|
||||
disable-model-invocation: true
|
||||
---
|
||||
|
||||
# Setup TS Deep Modules
|
||||
|
||||
Make every package in this repo a **deep module**: a lot of behaviour behind a small interface. A package's public surface is its **entry points** — the files at the package root — and everything in its subfolders is hidden. This skill installs [dependency-cruiser](https://github.com/sverweij/dependency-cruiser) and the rules that make the entry points the only way in, then proves the rules bite.
|
||||
|
||||
For the vocabulary (deep module, interface, seam, depth), run the `/codebase-design` skill — use its language throughout.
|
||||
|
||||
## The shape this enforces
|
||||
|
||||
```
|
||||
src/packages/
|
||||
<name>/
|
||||
index.ts ← an entry point (public). Import this from outside.
|
||||
client.ts ← another entry point. Packages may expose SEVERAL.
|
||||
lib/ ← implementation: hidden from outside, free to import each other.
|
||||
tests/ ← co-located tests + fixtures (a subfolder, so private).
|
||||
```
|
||||
|
||||
The public surface is the package's **root files** — not one designated `index.ts`. By convention implementation lives in `lib/` and tests in `tests/`, giving every package the same two-folder shape. The rule itself is general, though: *anything* in *any* subfolder is private, so you never extend the config to add a folder.
|
||||
|
||||
Four rules, all `error`:
|
||||
|
||||
1. **Entry-point boundary** — code outside a package (app code or another package) may import only that package's entry points (its root files), never anything in its subfolders.
|
||||
2. **Intra-package freedom** — a package's own files import each other freely.
|
||||
3. **Tests through the entry points** — files under `<pkg>/tests/` may import any package's entry points and their own `tests/` fixtures, but never any package's subfolder internals (not even their own). Integration tests across packages are fine; deep imports are not.
|
||||
4. **No cycles** — no dependency cycles.
|
||||
|
||||
**Entry points, not a barrel.** Because the public surface is *every* root file, a package can expose several small entry points (`index.ts`, `client.ts`, `server.ts`) instead of funnelling everything through one giant `index.ts`. Barrel files that re-export a whole subtree are discouraged — keep entry points small and hide implementation in subfolders.
|
||||
|
||||
Layering (which packages may depend on which) is a *different* concern and is left as a commented stub in the config for this repo to fill in.
|
||||
|
||||
## Steps
|
||||
|
||||
### 1. Detect the environment
|
||||
|
||||
- **Package manager** — `pnpm-lock.yaml` → pnpm, `yarn.lock` → yarn, `bun.lockb` → bun, else npm. Use it for every command below (`pnpm`/`yarn`/`npm run`/`bunx`).
|
||||
- **Packages root** — if `src/` exists use `src/packages`, else `packages`. Confirm the choice with the user if the repo already has a different obvious convention.
|
||||
- **Existing config** — check for a `.dependency-cruiser.*` file. If one exists, do **not** overwrite it: merge the four rules and the options in, and tell the user what you added.
|
||||
|
||||
**Done when:** package manager, packages root, and existing-config status are all known.
|
||||
|
||||
### 2. Install dependency-cruiser
|
||||
|
||||
Install `dependency-cruiser` as a devDependency with the detected package manager.
|
||||
|
||||
**Done when:** `dependency-cruiser` is in `devDependencies`.
|
||||
|
||||
### 3. Write the config
|
||||
|
||||
Copy [`dependency-cruiser.config.cjs`](./dependency-cruiser.config.cjs) to the repo root as `.dependency-cruiser.cjs`. Set `PACKAGES_ROOT` to the root detected in step 1. The rules are path-depth based and extension-agnostic, so nothing else needs adapting.
|
||||
|
||||
**Done when:** `.dependency-cruiser.cjs` exists with the correct `PACKAGES_ROOT`, and the four forbidden rules are present.
|
||||
|
||||
### 4. Wire it into the checks
|
||||
|
||||
- Add a `lint:boundaries` script: `depcruise <packages-root>` (or `depcruise src`).
|
||||
- Fold it into the repo's umbrella check command — the one that already runs typecheck (e.g. a `check` / `ci` / `validate` script). Do **not** touch `tsconfig` or add path aliases.
|
||||
- If there is no umbrella script, add `lint:boundaries` and tell the user to include it in CI.
|
||||
|
||||
**Done when:** `lint:boundaries` exists and runs as part of the same command as typecheck.
|
||||
|
||||
### 5. Scaffold the example package
|
||||
|
||||
Create a committed `<packages-root>/example/` as a copy-me template:
|
||||
|
||||
- `index.ts` — an entry point. Export one function that delegates to an internal file (so the package is visibly *deep*, not a pass-through).
|
||||
- `lib/impl.ts` — an internal file in a **subfolder**, imported by `index.ts`, not reachable from outside.
|
||||
- `tests/example.test.ts` — imports **only** `../index` (an entry point), and asserts against the public function.
|
||||
|
||||
Tell the user this is a starter template to copy or delete.
|
||||
|
||||
**Done when:** the example package exists, exposes its behaviour through a root entry point, and hides `impl` in a subfolder.
|
||||
|
||||
### 6. Prove the rules bite
|
||||
|
||||
This is the completion criterion for the whole skill — a config that doesn't fail on a violation is worthless.
|
||||
|
||||
1. Run `lint:boundaries`. It must **pass** on the clean example.
|
||||
2. Temporarily add a deep import to `tests/example.test.ts` (e.g. `import { thing } from "../lib/impl"`). Run `lint:boundaries` again — it must **fail** with `tests-through-entrypoints`.
|
||||
3. Revert the deep import. Run once more — it must **pass**.
|
||||
|
||||
**Done when:** you have observed a pass, then a fail on the deep import, then a pass again. If step 2 does not fail, the rules are not wired correctly — fix before finishing.
|
||||
|
||||
### 7. Document the convention
|
||||
|
||||
Write a `README.md` **in the packages folder** (`<packages-root>/README.md`) — next to the packages it governs — covering: the `src/packages/<name>/` layout (entry points at the root, `lib/` for implementation, `tests/` for tests), "import only through a package's entry points (its root files)", and how to run `lint:boundaries`. **Discourage barrel files** explicitly — expose several small entry points instead of re-exporting a whole subtree through one index. Keep it to the copy-me snippet plus the four rules in one paragraph each.
|
||||
|
||||
Then add a **context pointer** to it from the repo's agent-instructions file — `CLAUDE.md` if present, else `AGENTS.md` (create `AGENTS.md` if neither exists). One line is enough, e.g. `Packages are deep modules — see [src/packages/README.md](./src/packages/README.md) before adding or importing one.` This is what makes an agent discover the boundary rule instead of tripping over it.
|
||||
|
||||
**Done when:** `<packages-root>/README.md` exists and discourages barrels, and the repo's `CLAUDE.md`/`AGENTS.md` links to it.
|
||||
|
||||
## Notes
|
||||
|
||||
- The config's `$1` back-references (dependency-cruiser's group matching) are what let a package reach its own internals while outsiders can't — don't flatten them into separate per-package rules.
|
||||
- Public vs private is decided by **depth**: a package's root files are entry points; anything in a subfolder is private. The conventional subfolders are `lib/` (implementation) and `tests/`, but the rule doesn't hardcode them — any subfolder is private, so a new folder never needs a config change. Adding an entry point is just adding a root file — no barrel.
|
||||
- Packages are **flat**: one tier of immediate children under the root. A package's internals may nest as deep as you like; a package may not contain another package.
|
||||
- Use `.cjs` (not `.js`) so the config's `module.exports` works even in `"type": "module"` repos.
|
||||
@@ -0,0 +1,5 @@
|
||||
interface:
|
||||
display_name: "Setup TS Deep Modules"
|
||||
short_description: "Enforce deep TypeScript modules"
|
||||
policy:
|
||||
allow_implicit_invocation: false
|
||||
@@ -0,0 +1,95 @@
|
||||
// @ts-check
|
||||
// Deep-module enforcement for dependency-cruiser.
|
||||
//
|
||||
// Each package under the packages root is a DEEP MODULE: a lot of behaviour
|
||||
// behind a small interface. A package's PUBLIC SURFACE is its ENTRY POINTS —
|
||||
// the files at the package root. Implementation lives in SUBFOLDERS and is
|
||||
// private — by convention `lib/` for implementation and `tests/` for tests,
|
||||
// though any subfolder is private. A package may expose several small entry
|
||||
// points (index.ts, client.ts, server.ts, …) — prefer that over one giant
|
||||
// barrel index.
|
||||
//
|
||||
// The only thing you should ever need to edit here is PACKAGES_ROOT.
|
||||
|
||||
/** Where packages live. One immediate child dir per package (flat, no nesting). */
|
||||
const PACKAGES_ROOT = "src/packages";
|
||||
|
||||
// --- derived patterns (no need to edit) -------------------------------------
|
||||
const R = PACKAGES_ROOT;
|
||||
/**
|
||||
* A package's private internals: anything nested inside a package subfolder.
|
||||
* The package's root files are its entry points and are NOT matched here —
|
||||
* they stay importable from outside.
|
||||
*/
|
||||
const PACKAGE_INTERNALS = `^${R}/[^/]+/[^/]+/`;
|
||||
|
||||
/** @type {import('dependency-cruiser').IConfiguration} */
|
||||
module.exports = {
|
||||
forbidden: [
|
||||
{
|
||||
name: "entrypoint-boundary-from-app",
|
||||
comment:
|
||||
"App/root code may import a package's entry points (its root files), but nothing inside its subfolders.",
|
||||
severity: "error",
|
||||
from: { pathNot: `^${R}/` }, // importer is NOT inside any package
|
||||
to: { path: PACKAGE_INTERNALS },
|
||||
},
|
||||
{
|
||||
name: "entrypoint-boundary-across-packages",
|
||||
comment:
|
||||
"A package's own files import each other freely, but may reach OTHER packages only through their entry points — never their internals.",
|
||||
severity: "error",
|
||||
// importer is inside a package ($1), but is not a test file
|
||||
from: { path: `^${R}/([^/]+)/`, pathNot: `^${R}/[^/]+/tests/` },
|
||||
to: {
|
||||
path: PACKAGE_INTERNALS,
|
||||
pathNot: `^${R}/$1/`, // same package → intra-package freedom
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "tests-through-entrypoints",
|
||||
comment:
|
||||
"A package's tests exercise it through its entry points like everyone else: they may import any package's entry points and their own tests/ fixtures, but never any package's internals — not even their own.",
|
||||
severity: "error",
|
||||
from: { path: `^${R}/([^/]+)/tests/` }, // a test file, in package $1
|
||||
to: {
|
||||
path: PACKAGE_INTERNALS,
|
||||
pathNot: `^${R}/$1/tests/`, // own tests/ fixtures → allowed
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "tests-folder-is-private",
|
||||
comment:
|
||||
"A package's tests/ folder is reachable only from tests — nothing else may import fixtures.",
|
||||
severity: "error",
|
||||
from: { pathNot: `^${R}/[^/]+/tests/` }, // importer is not itself a test
|
||||
to: { path: `^${R}/[^/]+/tests/` },
|
||||
},
|
||||
{
|
||||
name: "no-circular",
|
||||
comment: "No dependency cycles. Scope to `^${R}/` if you want to allow cycles outside packages.",
|
||||
severity: "error",
|
||||
from: {},
|
||||
to: { circular: true },
|
||||
},
|
||||
|
||||
// --- Layering (optional, off by default) ----------------------------------
|
||||
// Interface-hiding controls HOW you import (through the entry points).
|
||||
// Layering controls WHICH packages may depend on which. Add your own rules
|
||||
// here, e.g.:
|
||||
//
|
||||
// {
|
||||
// name: "ui-may-not-depend-on-billing",
|
||||
// severity: "error",
|
||||
// from: { path: `^${R}/ui/` },
|
||||
// to: { path: `^${R}/billing/` },
|
||||
// },
|
||||
],
|
||||
options: {
|
||||
doNotFollow: { path: "node_modules" },
|
||||
tsConfig: { fileName: "tsconfig.json" },
|
||||
enhancedResolveOptions: {
|
||||
extensions: [".ts", ".tsx", ".js", ".jsx", ".json"],
|
||||
},
|
||||
},
|
||||
};
|
||||
@@ -0,0 +1,67 @@
|
||||
---
|
||||
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
|
||||
---
|
||||
|
||||
<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.
|
||||
|
||||
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:
|
||||
|
||||
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.
|
||||
|
||||
</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.
|
||||
|
||||
A beat is sized by what it needs:
|
||||
|
||||
- A single sentence if that's all the move is ("And then nothing happened for three weeks.").
|
||||
- A short paragraph if the move needs setup.
|
||||
- Multiple paragraphs if the beat is a self-contained vignette, argument, or example.
|
||||
|
||||
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
|
||||
|
||||
Pull material from the raw pile to populate each beat. You can paraphrase, split, recombine, or quote. The pile is a quarry.
|
||||
|
||||
## Ending the journey
|
||||
|
||||
The article ends when the journey is complete — not when the pile is empty. Most piles will have leftover fragments that don't make it in. That is fine; that is the point of having more raw material than you need.
|
||||
|
||||
## Writing rhythm
|
||||
|
||||
- Append one beat at a time. Never write ahead.
|
||||
- Re-read the article file from disk before every write. Preserve user edits absolutely.
|
||||
- If the user edits a previous beat substantially, let it change what comes next.
|
||||
- If the user says "rewrite that beat" or "go back and try a different beat 3", do it — edit in place, leave the rest alone.
|
||||
|
||||
</supporting-info>
|
||||
@@ -0,0 +1,5 @@
|
||||
interface:
|
||||
display_name: "Writing Beats"
|
||||
short_description: "Assemble raw material into beats"
|
||||
policy:
|
||||
allow_implicit_invocation: false
|
||||
@@ -0,0 +1,79 @@
|
||||
---
|
||||
name: writing-fragments
|
||||
description: Writing, explore — mine raw fragments, no structure yet.
|
||||
disable-model-invocation: true
|
||||
---
|
||||
|
||||
<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.
|
||||
|
||||
As fragments emerge from either side of the conversation, append them to a single markdown file.
|
||||
|
||||
If the user did not pass a path, ask once where to save the document, then remember it for the rest of the session.
|
||||
|
||||
Capture fragments from the very first thing the user says, including the initial prompt.
|
||||
|
||||
On first write, put a single H1 at the top with a working title (it can change later) and nothing else — no metadata, no TOC, no date.
|
||||
|
||||
</what-to-do>
|
||||
|
||||
<supporting-info>
|
||||
|
||||
## What is a fragment
|
||||
|
||||
A fragment is any piece of text that might survive into the final article. It must be _readable by the author_ — the author can tell what it means — but it does not need to define its terms or be comprehensible to a cold reader. The bar is "is this a piece of good writing?", not "is this a self-contained argument?"
|
||||
|
||||
Fragments are deliberately heterogeneous. Examples of what could be a fragment:
|
||||
|
||||
- A sharp sentence you'd want to deploy somewhere but don't yet know where.
|
||||
- A claim with a one-line justification.
|
||||
- A vignette: a thing that happened, a code snippet, a scenario, an analogy.
|
||||
- A half-thought: "something about how X feels like Y, work this out later."
|
||||
- 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.
|
||||
|
||||
## File format
|
||||
|
||||
```markdown
|
||||
# Working title
|
||||
|
||||
A first fragment lives here.
|
||||
|
||||
It can be multiple paragraphs. It can include lists, code, quotes — whatever
|
||||
shape the fragment naturally takes.
|
||||
|
||||
---
|
||||
|
||||
A second fragment.
|
||||
|
||||
---
|
||||
|
||||
> A quoted line that the user wants to keep around.
|
||||
|
||||
A reaction to it.
|
||||
|
||||
---
|
||||
|
||||
- A cluster of related observations
|
||||
- That hang together by feel
|
||||
- And want to be near each other
|
||||
```
|
||||
|
||||
Fragments are separated by a horizontal rule (`\n---\n`). No headings inside the body. No tags. No order beyond the order they were added.
|
||||
|
||||
## Writing rhythm
|
||||
|
||||
Append silently. Don't ask permission for each fragment. Mention what you added in passing ("adding that"), but don't interrupt the conversation with save dialogs.
|
||||
|
||||
Before every write: re-read the file from disk. The user may have edited, reordered, or deleted fragments between turns — preserve their changes. Never overwrite the file; only append (or, if the user asks, edit a specific fragment in place).
|
||||
|
||||
The user can say "cut the last one", "rewrite that one sharper", "merge those two" at any time. Treat those as first-class instructions.
|
||||
|
||||
</supporting-info>
|
||||
@@ -0,0 +1,5 @@
|
||||
interface:
|
||||
display_name: "Writing Fragments"
|
||||
short_description: "Mine raw writing fragments"
|
||||
policy:
|
||||
allow_implicit_invocation: false
|
||||
@@ -0,0 +1,79 @@
|
||||
---
|
||||
name: writing-shape
|
||||
description: Writing, exploit — shape raw material into an article, paragraph by paragraph.
|
||||
disable-model-invocation: true
|
||||
---
|
||||
|
||||
<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.
|
||||
|
||||
If the user did not say where to save the article, ask once and remember the path.
|
||||
|
||||
</what-to-do>
|
||||
|
||||
<supporting-info>
|
||||
|
||||
## 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.
|
||||
|
||||
## Conversational feel
|
||||
|
||||
This is a grilling session inverted. In ideation, the question was "what are you actually noticing?" Here it's "what is this article actually arguing, and in what order does the reader need to hear it?" Push back. Refuse to let weak transitions slide. If a paragraph doesn't earn its place, cut it.
|
||||
|
||||
Specific moves to keep using:
|
||||
|
||||
- "What does this paragraph do for the reader that the previous one didn't?"
|
||||
- "If I cut this, what breaks?"
|
||||
- "Is this prose, or should it be a list? Why prose?"
|
||||
- "This sentence is doing two jobs — split it or pick one."
|
||||
- "The opening promised X. We've drifted to Y. Either re-thread it or change the opening."
|
||||
|
||||
## Pulling from the pile
|
||||
|
||||
Treat the raw material as a quarry, not a script. Pull a fragment, rework it to fit the surrounding paragraph, and place it. A fragment may be split across multiple paragraphs, merged with another, or paraphrased. The pile's job is to be mined; the article's job is to read as one voice.
|
||||
|
||||
If the pile lacks something the article needs, name the gap explicitly: "We need an example here and the pile doesn't have one — give me one now or we cut this section."
|
||||
|
||||
## Format arguments to actually have
|
||||
|
||||
When choosing how to render a block, 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.
|
||||
- **Table vs. repeated structure.** If the same shape repeats 3+ times with the same fields, a table. Otherwise prose with bold leads.
|
||||
- **Quote vs. paraphrase.** Quote when the original wording is the point. Paraphrase when only the idea matters.
|
||||
- **Code block vs. inline code.** Multi-line, runnable, or illustrative → block. Single token or identifier → inline.
|
||||
|
||||
## Writing rhythm
|
||||
|
||||
Append to the article file as each block is agreed. Re-read the file from disk before every write — the user may have edited between turns. Never overwrite blindly. If the user wants a paragraph rewritten, edit that specific paragraph in place; leave the rest alone.
|
||||
|
||||
## Out of scope
|
||||
|
||||
- Mining for new fragments that aren't in the pile (handle gaps as in "Pulling from the pile").
|
||||
- Editing the raw material file.
|
||||
- Publishing, formatting for a specific platform, or adding frontmatter the user didn't ask for.
|
||||
|
||||
</supporting-info>
|
||||
@@ -0,0 +1,5 @@
|
||||
interface:
|
||||
display_name: "Writing Shape"
|
||||
short_description: "Shape raw material into an article"
|
||||
policy:
|
||||
allow_implicit_invocation: false
|
||||
Reference in New Issue
Block a user