Skills

A bundle of files telling an agent when to do what. The doc_* tools work without them; skills add the conventions — how to recover from a conflict, what shape a new ADR takes, which library to look at.

What follows is exactly the files served by https://www.cushion.pics/api/skills. Installing them is step 2 of the install page.

/cushion-captureFold the decisions, rules and traps settled during this session back into the Cushion docs. Only when the user asks with /cushion-capture.

Writing what was settled back into the docs

Docs go stale not because nobody reads them but because nobody writes back to them. This skill is that step.

1. Work out what actually got settled

Take only what reached a conclusion in this conversation:

  • Decisions and their rationale (what, why, and what was rejected)
  • Rules and traps you could not learn by reading the code (order dependencies, paths that fail silently)
  • Contracts that changed (API shape, schema, tool arguments)

Leave out: anything still under discussion, anything the code already tells you (function signatures, file structure), and context that only mattered this session (throwaway debug logging, one-off commands). If there is nothing to record, say so and stop. Padding it out is how the next person learns not to trust the docs.

2. Find where it belongs

doc_outline                       # what documents exist
doc_search(query:"relevant terms")  # is there already a section about this
  • Editing an existing section is the default. A new section splits one topic across two places
  • Decisions go in the decision log, rules in the relevant spec section, procedures in the runbook
  • If you are not sure which document, ask

3. Show it, get approval, then write

Do not skip this order. An agent quietly editing a spec is itself a new source of drift.

Show a summary like this first:

cushion/PLAN.md ## Decision log
  + D-017. The service ships the skills — rationale: …

cushion/SPEC.md ## 9. Screens
  ~ added the three-step onboarding table

Only once approved:

doc_get(library:"…", path:"…", heading:"…")   # take the sha
doc_put(library:"…", path:"…", heading:"…", content:"…",
        base_sha:"<the sha you got>", note:"what and why")
  • Use heading to change only that section. Do not rewrite the whole document
  • A mismatched base_sha means someone edited in the meantime — do not merge, reapply on top of the current body
  • Put the why in note. That is the line the next person reads

4. When you write a decision

A decision with no rationale gets reversed by the next person. Cover at least these three:

  • Decision: what we are doing
  • Rationale: why. What was rejected, and for what reason
  • Revisit when: what would make us look at this again

5. Report

One line per document and section you changed. That is all. Do not print the body back out.

/cushion-compactStrip out the parts of a Cushion document that no longer do any work. Only when the user asks with /cushion-compact [document]. Given a name it does that document, otherwise it looks for candidates across all of them.

Cutting dead sentences out of a document

Documents only ever grow. In-progress narration of finished work, the backstory of a settled question, the same thing said in two places — all of it is weight an agent carries every time.

This is deletion, not summarising. Sentences that stay keep their exact wording. Rewriting them is not compaction, it is a rewrite, and the rationale quietly shifts underneath.

1. Set the scope

If a document name came in as an argument, look only at that one — .md is optional and case does not matter:

/cushion-compact SPEC     →  SPEC.md

With no argument, list them with doc_outline and choose candidates. If there are more than ten documents, ask before reading them all — judging requires reading the bodies, and that is the cost. If the name matches several, let the user pick.

2. What can go, and what cannot

This list is the whole point of the skill. Without it an agent deletes the sentences it did not understand.

Delete

  • In-progress narration of finished work — an item marked [x] that still says "next we should …"
  • Items struck through with ~~strikethrough~~ to mark them resolved — keep the one-line conclusion, drop the backstory
  • The same content in two places → make one of them a one-line reference
  • Anything the code, the tools or the screen already answers — file structure, function signatures, numbers that the UI displays

Do not delete

  • The rationale for a decision, and why alternatives were rejected. It is the only thing stopping the next person proposing the same idea again
  • Traps and order dependencies. "A has to come before B" is short and therefore easy to delete, and it is exactly what you cannot learn by reading the code
  • Revisit-when conditions
  • Sentences that look stale but explain why something was removed. Delete those and the decision comes back

3. Show it, get approval, then delete

For every line you would cut, label which of the categories above it falls under. If you cannot label it, it is not something to cut.

SPEC.md ## 9. Screens
  − 12 lines  in-progress narration of finished work
  − 5 lines   numbers the UI displays (the code answers this)
  = 6,796 chars → 5,100 chars (−25%)

Only once approved:

doc_get(library:"…", path:"…", heading:"…")     # take the sha
doc_put(library:"…", path:"…", heading:"…", content:"…",
        base_sha:"<the sha you got>", note:"what was cut and why")

content must include the ## line — leave it out and the section title goes with it.

4. When to stop

  • One section at a time. Cutting a whole document at once makes the approval a formality
  • If you would cut more than 30% of a section, stop and ask. That is a restructure, not compaction, and restructuring is a person's call
  • If there is nothing to cut, say so and stop. Padding it out costs you rationale

What makes this skill acceptable at all is that it is reversible — every deletion lands in document_versions and can be restored from the history screen. Do not run it alongside work that touches that path.

5. Report

One line per section: path ## section −N chars (−N%). Do not print the body back out.

/cushion-divideSplit a section that has grown heavy, or move it into its own document, so the unit an agent reads gets smaller. Only when the user asks with /cushion-divide [document]. Given a name it does that document, otherwise it looks for candidates across all of them.

Splitting when the unit you read gets too heavy

Document size is not read cost. doc_get(heading:) works per section, so a document can grow and you still only get the section you asked for. The outline, which does load every session, is tiny next to the bodies.

The cost appears when the addressable unit is coarser than what you need — when reading one item means loading the enormous section that item sits in. A decision log is the classic case. So what you measure is not the document, it is the section.

1. Set the scope

If a document name came in as an argument, look only at that one (.md optional, case insensitive):

/cushion-divide PLAN     →  PLAN.md

Otherwise list them with doc_outline and pick candidates. You only learn a section's size by reading the body — the outline carries no lengths. If there are more than ten documents, ask before reading them all.

2. When to split

Propose a split only if at least one of these three holds. Otherwise leaving it alone is better.

  1. The section is 5× the median section size in that library or more
  2. One section mixes different readers or different tasks — reading one means loading the rest
  3. The section is edited oftenbase_sha conflicts are per document, so splitting it out reduces contention

3. Two different moves — splitting a section is the default

WhenOutline costRead benefit
Split a section (same document)A big section is really one topicA few heading linesLarge
Split out a document (new file)The topic itself is differentPath + every headingNone

Splitting a document does not reduce read cost. The section count is unchanged; only the outline gets longer. Create a new document only when the topic genuinely differs — what you gain there is not read cost but findability and write contention.

4. Survey the references first

Identifiers like section numbers (§6) and decision numbers (D-011) get cited from outside the document — code comments and repo files such as AGENTS.md point at them as (SPEC §6) or (D-011). Nothing fails when they break; the next person just hunts for a section that is not there.

  • Do not renumber. A number travels with its content even when it moves
  • Use doc_search to see whether other documents point at that section — fix those too
  • References from outside the docs (code, repo files) are beyond this skill's reach. Report them as a list and let a person decide

5. Show it, get approval, then move

Show the range being moved (or split) and the resulting shape first. Proceed only once approved. Either way content must include the ## line — leave it out and the section title disappears.

Splitting a section — one replacement does it. Put several ## headings in the replacement body and it splits:

doc_get(library:"…", path:"…", heading:"the big section")   # take the body and sha
doc_put(library:"…", path:"…", heading:"the big section",
        content:"## Topic A\n…\n\n## Topic B\n…",
        base_sha:"<the sha you got>", note:"…")

Splitting out a document — two calls, and the order matters:

doc_get(library:"…", path:"<original>", heading:"…")        # take the body and sha
doc_put(library:"…", path:"<new path>", content:"…")        # ① create the new document first
doc_put(library:"…", path:"<original>", heading:"…",
        content:"## …\none-line summary + the new path",
        base_sha:"<the sha you got>", note:"…")              # ② then shrink the original
  • Do not reverse ① and ②. There are no transactions, so a call can fail halfway; in this order a failure leaves the content in two places (visible, and fixable). Reversed, it leaves the original gone and the new document missing
  • If ① comes back saying the document already exists, stop. The path collided, and overwriting destroys somebody's document. Propose another path
  • Leave a one-line summary plus the new path in the original. An empty shell heading eats outline space and tells you nothing

6. Report

One line each for the new document path, how much the original shrank, and the list of outside references that need fixing. Do not print the body back out.

/cushion-listShow the Cushion libraries (document bundles) you can reach. Only when the user asks with /cushion-list.

Document libraries you can reach

doc_outline(depth:"libraries")

Leave depth off and you drag in the ## headings of every document — they asked for a list, not an outline.

The response comes back one per line, like this:

cushion — Cushion docs (neruu00/cushion) · 2 docs
design-system — Design system (acme/*) · 0 docs

Pass it through almost as is. Add a leading - and leave the rest alone.

  • acme/* means that whole organisation uses this library. Do not expand it
  • If you get No libraries you can reach, relay that and add one line saying library_create makes one
  • No tools at all means MCP is not connected. Check with claude mcp list first, say which it is (not registered, or a 401), and point them at /settings/tokens for the connect command

Do not add anything beyond the list.

/cushion-newCreate a new document in Cushion following the existing conventions (ADR, runbook, meeting notes, and so on). Only when the user asks with /cushion-new [kind] [title].

Creating a document the way this library does it

doc_put will happily create anything anywhere. That is exactly why you end up reinventing "what is the next ADR number" and "where do meeting notes go" every single time. This skill removes that.

1. Read the conventions first

doc_outline(library:"<slug>")

Follow what is already there. Do not invent a new scheme. The usual shapes:

adr/0007-token-rotation.md      number + kebab-case
runbook/deploy.md               procedures
meetings/2026-08-14.md          dates
glossary.md                     a single page
  • If documents with the same prefix already exist, copy their shape exactly
  • For numbered kinds (adr/ and friends) use the highest existing number + 1. Count it off the list yourself
  • If there is no convention at all (the first document), propose one of the shapes above and get the user to confirm it

2. Use a template if there is one

If the library has templates/<kind>.md, that is your skeleton:

doc_get(library:"<slug>", path:"templates/adr.md")

Otherwise build the smallest skeleton that works — do not scatter empty sections around. A heading with nothing under it only teaches the next person that this field goes unused.

For an ADR this is plenty:

# 0007. Title

## Context
What is the problem.

## Decision
What we decided to do.

## Rationale
Why. What was rejected, and for what reason.

## Revisit when
What would make us look at this again.

3. Create it

doc_put(library:"<slug>", path:"adr/0007-….md", content:"…", note:"…")
  • A new document has no base_sha. If you are passing one, you are overwriting something that already exists
  • If the response says the document already exists, stop rather than overwrite. It means the path collided, and overwriting destroys somebody's document. Tell the user and propose another path
  • Fill in only what you actually know. Leave the rest empty and report what you left empty

4. Report

One line for the path you created, one line for what you could not fill in. Do not print the body back out.

/cushion-useDecide which Cushion library this project uses and record it in AGENTS.md. Only when the user asks with /cushion-use [slug].

Deciding this project's document library

If a slug came in as an argument, use it. Otherwise work it out in this order.

1. Pick one

doc_outline(depth:"libraries")

What is in the parentheses is the GitHub repos that use that library:

cushion — Cushion docs (neruu00/cushion) · 2 docs
platform — Platform docs (acme/*) · 14 docs
  1. Get org/repo from git remote get-url origin (https://github.com/acme/web.gitacme/web)
  2. Compare it against the parenthesised lists. An exact match (acme/web) or an organisation wildcard (acme/*) is your library
  3. If several match, prefer the exact one. Still more than one? Go to 4
  4. If nothing matches, or more than one still does, ask. Show the list and let them choose — pinning the wrong library means every later session reads the wrong docs

No git remote, or not GitHub? Go straight to 4.

If there is no library to use at all

If you get No libraries you can reach, or nothing in the list matches, propose creating one (do not just create it — a slug cannot be changed later):

library_create(slug:"<proposed>", name:"<proposed>", github_repos:["<org/repo from git remote>"])
  • Take the slug candidate from the git repo name. Keep only lowercase letters, digits and hyphens
  • Putting the current repo in github_repos means this whole step resolves itself next time
  • If a whole organisation will share one library, suggest a single acme/* line
  • Whoever creates it becomes the first member. Teammates get invited from the library screen on the web

Do not pick a library because the slug looks similar. That is a guess; the comparison above is evidence.

2. Record it in AGENTS.md

Put it in a file, not in session memory. It survives into the next session, and being committed it points the whole team at the same library. Create AGENTS.md if it does not exist.

If a line mentioning Cushion is already there, change only the slug. Otherwise add it at the top of the document, right below the title:

## Docs

This project's docs (specs, ADRs, runbooks, meeting notes) live in the `<slug>` library on
Cushion, not in this repo. Read and write them with the `doc_*` tools on the `cushion` MCP
server. The `cushion` skill explains how.

If CLAUDE.md imports AGENTS.md, leave CLAUDE.md alone.

3. Report

One line on what you picked and why (argument / acme/web matched / acme/* matched / the user chose), and one line on what you changed in AGENTS.md. That is all.

If the chosen library's GitHub list does not include this repo, add one line: adding it in the library settings on the web makes this step resolve itself next time.

/cushionFor reading or writing this project's docs (specs, ADRs, runbooks, meeting notes, glossaries). The docs live in Cushion, not in the repo, and you read and write them a piece at a time with the doc_* tools on the `cushion` MCP server. Check this first when you cannot find a document locally.

Cushion — how to use the doc library

The source of truth lives in Cushion. There being no SPEC.md in the local repo is the design, not an omission. There is no git to fall back on, so the rules below are safety equipment rather than convenience.

Reading

doc_outline                                    # what exists, not the body
doc_get(library:"…", path:"SPEC.md", heading:"6. Auth")
  • Never read a whole document. Read the outline, then take only the ## section you need. That is the entire reason this tool exists
  • If you do not know which document holds it, use doc_search(query:"…") — you get back the matching sections only
  • Reading the same document again? Pass the previous sha as if_none_match. If nothing changed you get the single word unchanged instead of a body
  • Do not throw away the sha:… on the first line of a doc_get response. You have to hand it back when you edit

Writing

doc_put(library:"…", path:"SPEC.md", heading:"6. Auth",
        content:"## 6. Auth\n…", base_sha:"<sha from doc_get>", note:"what changed and why")
  • base_sha is required (only a brand-new document is exempt). Pass back the sha you got when reading
  • To change one section, pass heading. Then you never resend the whole document, and content holds that entire section including its ## line
  • note is the commit message slot. One line on what changed and why
  • For a new document just give a new path. It has to end in .md

When there is a conflict

If base_sha no longer matches, the write is rejected and you get back the current sha and body.

Do not merge. There are no branches, so there is nothing to merge against. Reapply your change on top of the body you were handed, and send it again with the sha you were handed as base_sha. If it looks like you would be erasing someone else's edit, stop and tell the person you are working with.

When you see [stale]

A trailing [stale] repo: path… line means someone edited since you last looked. Call doc_changes_since and you get a summary of what changed, and your cursor moves forward. No such line means you are up to date — do not re-read to check.

Easy mistakes

  • Do not create a document just because there is nothing locally. Call doc_outline first
  • Do not doc_get a large document whole. Use heading
  • Do not re-read after editing to confirm. The doc_put response gives you the new sha
  • To remove a document use doc_delete — the previous body stays in the history, so do not make a backup copy first

Where to put a new document

The path is the taxonomy. Look at the conventions already in use with doc_outline and follow them (adr/0001-*.md, runbook/*.md, meetings/2026-08-13.md, and so on). If there is no library to put it in at all, library_create(slug:"…", name:"…").

If you are not connected

No doc_* tools means MCP is not connected. Issue a token at Cushion's /settings/tokens and the screen hands you the whole claude mcp add command.

The manifest version is 60e61fae. It is a hash of the file contents, so you can use it to tell whether an installed copy has gone stale.