@linchpinagency/skills 0.1.8 → 0.1.10

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -12,7 +12,7 @@ GitHub Copilot, and other compatible coding agents.
12
12
  ![Zero dependencies](https://img.shields.io/badge/Dependencies-0-brightgreen)
13
13
 
14
14
  <!-- x-release-please-start-version -->
15
- ### Latest release: 0.1.8
15
+ ### Latest release: 0.1.10
16
16
  <!-- x-release-please-end -->
17
17
 
18
18
  | Release | Skill standard | Install |
@@ -259,8 +259,8 @@ A project that wants skills in more than one agent's directory should run
259
259
  | `engagement-types` | Project mgmt | Tell support, site maintenance, projects, product/plugin work, and pre-sales apart — each lives somewhere different in ClickUp and is planned and closed differently. |
260
260
  | `support-triage` | Project mgmt | Run a client support request end to end — clarify the real need, reproduce, judge urgency and scope, fix in the right layer, verify, and close the loop with the requester. |
261
261
  | `dependency-updates` | Workflow | Handle the dependency work Renovate can't automerge — majors, breaking changes, failing or conflicted bot PRs, security advisories, `@wordpress/*` package sets. |
262
- | `commit-and-release` | Workflow | Write commits, branches, and PR titles that satisfy the repo's own commitlint rules, and stay out of release-please's way (it owns versions and `CHANGELOG.md`). |
263
- | `task-tracking` | Workflow | Tie every unit of work to a ClickUp task (or explicit `NO-TASK`) with minimal friction via the ClickUp MCP — resolve/search a task, create one on request ("create an issue" means ClickUp, not GitHub), update it when the work lands, and carry the task key in the conventional-commit scope. |
262
+ | `commit-and-release` | Workflow | Write commit messages and PR titles that satisfy the repo's own commitlint rules, and stay out of release-please's way (it owns versions and `CHANGELOG.md`). Branch naming lives in `task-tracking`. |
263
+ | `task-tracking` | Workflow | Tie every unit of work to a ClickUp task (or explicit `NO-TASK`) with minimal friction via the ClickUp MCP — resolve/search a task, create one on request ("create an issue" means ClickUp, not GitHub), split work that spans sessions or PRs into parent + subtasks, name the branch, update the task when the work lands, and carry the task key in the commit scope. |
264
264
  | `write-a-linchpin-skill` | Meta | The house standard for authoring skills in this library — placement test, tier model, required frontmatter, the section skeleton, and the four house rules. Enforced by `scripts/validate-skills.mjs`. |
265
265
 
266
266
  _(More WordPress, React, Cloudflare Workers, marketing, and design skills to come.)_
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@linchpinagency/skills",
3
- "version": "0.1.8",
3
+ "version": "0.1.10",
4
4
  "description": "Linchpin's library of reusable AI agent skills for WordPress projects.",
5
5
  "type": "module",
6
6
  "bin": {
@@ -13,7 +13,10 @@ parallel runs.
13
13
 
14
14
  Skills that need a browser ([`web-qa`](../web-qa/SKILL.md),
15
15
  [`wp-audit`](../wp-audit/SKILL.md), [`design-previews`](../design-previews/SKILL.md))
16
- reference this rather than each choosing their own way in.
16
+ reference this rather than each choosing their own way in. **The one exception is a local
17
+ Studio site**, whose MCP has its own `take_screenshot` and `inspect_design` against PHP-WASM
18
+ — that isn't a browser and doesn't come through this ladder; see
19
+ [`wp-studio-cli`](../wp-studio-cli/SKILL.md).
17
20
 
18
21
  ## When to use
19
22
 
@@ -81,6 +84,9 @@ session and are not part of this ladder.
81
84
  - **Never store credentials, cookies, or auth state in the repo.** Use the browser's existing
82
85
  session or a gitignored auth file.
83
86
  - **Never navigate anywhere the task didn't call for.** Stay on the target application.
87
+ - **Never reach for `claude-in-chrome` tools.** They fight the Chrome DevTools MCP for the
88
+ same browser session; the ladder above is the whole set. This is stated here as well as in
89
+ the ladder because it's the one rule another skill is most likely to trip over.
84
90
  - If the browser tooling fails twice in a row, stop and report it rather than cycling through
85
91
  variations of the same call.
86
92
 
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  name: commit-and-release
3
- description: Write commits, branches, and PR titles that satisfy a Linchpin repo's own commitlint rules, and stay out of the way of release-please, which owns versions and CHANGELOG.md. Use when composing a commit message, when commitlint or a husky hook rejects one, when opening a PR, when asked how a change gets released or deployed, or before touching a version number anywhere. Not for running lint or tests — use `quality-gates`.
4
- version: 1.0.0
3
+ description: Write commit messages and PR titles that satisfy a Linchpin repo's own commitlint rules, and stay out of the way of release-please, which owns versions and CHANGELOG.md. Use when composing a commit message, when commitlint or a husky hook rejects one, when opening a PR, when asked how a change gets released or deployed, or before touching a version number anywhere. Not for running lint or tests — use `quality-gates`. Not for naming the branch or resolving the task key in the scope — use `task-tracking`.
4
+ version: 1.1.0
5
5
  ---
6
6
 
7
7
  # Commit and release
@@ -17,20 +17,21 @@ Two pieces of automation own this pipeline, and both bite when you guess:
17
17
 
18
18
  ## When to use
19
19
 
20
- - Composing a commit message, branch name, or PR title.
20
+ - Composing a commit message or PR title.
21
21
  - A commit was rejected by commitlint or a husky hook.
22
22
  - The user asks how a change reaches staging or production.
23
23
  - Anything is about to touch a version number or `CHANGELOG.md`.
24
24
  - Reviewing or merging a release PR.
25
25
 
26
26
  **Not this skill:** running lint/tests before the commit — [`quality-gates`](../quality-gates/SKILL.md).
27
- Finding or creating the task whose key goes in the scope — [`task-tracking`](../task-tracking/SKILL.md).
27
+ Naming the branch, and finding or creating the task whose key goes in the scope —
28
+ [`task-tracking`](../task-tracking/SKILL.md).
28
29
 
29
30
  ## Owns
30
31
 
31
- Canonical for: commit message grammar, branch and PR-title rules, and the boundary between
32
- what you write and what release-please generates. Defers task resolution to
33
- [`task-tracking`](../task-tracking/SKILL.md) and verification to [`quality-gates`](../quality-gates/SKILL.md).
32
+ Canonical for: commit message grammar, PR-title rules, and the boundary between what you
33
+ write and what release-please generates. Defers task resolution **and branch naming** to
34
+ [`task-tracking`](../task-tracking/SKILL.md), verification to [`quality-gates`](../quality-gates/SKILL.md).
34
35
 
35
36
  ## Preflight — the rules are per-repo
36
37
 
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  name: design-previews
3
- description: Generate three genuinely different visual directions as self-contained HTML previews, screenshot them at desktop and mobile with the Chrome DevTools MCP (or Playwright), and get the client's pick before any theme or block work starts. Use when kicking off a design, when someone asks for design options or mockups, when a brief is vague and needs something to react to, or before building a homepage or landing page. Not for auditing an existing design — use `wp-audit`.
3
+ description: Generate three genuinely different visual directions as self-contained HTML previews, screenshot them at desktop and mobile, and get the client's pick before any theme or block work starts. Use when kicking off a design, when someone asks for design options or mockups, when a brief is vague and needs something to react to, or before building a homepage or landing page. Not for auditing an existing design — use `wp-audit`. Not for choosing a browser tool — use `browser-automation`.
4
4
  version: 1.1.0
5
5
  ---
6
6
 
@@ -27,7 +27,8 @@ and [`wp-block-conventions`](../wp-block-conventions/SKILL.md).
27
27
  ## Owns
28
28
 
29
29
  Canonical for: the brief template, how directions are differentiated, preview output rules,
30
- the review loop, and the handoff. Implementation belongs to the skills above.
30
+ the review loop, and the handoff. Implementation belongs to the skills above; getting a
31
+ browser to screenshot with belongs to [`browser-automation`](../browser-automation/SKILL.md).
31
32
 
32
33
  ## Preflight — confirm the brief first
33
34
 
@@ -68,10 +69,10 @@ three palettes of one layout still isn't.
68
69
  gitignored; the repo is `wp-content` and previews must never ship). One file per
69
70
  direction, generated in parallel when the harness supports it.
70
71
  → `design-1.html`, `design-2.html`, `design-3.html` exist and open standalone.
71
- 3. **Screenshot each at desktop and mobile.** Prefer the **Chrome DevTools MCP** —
72
- `new_page`, `navigate_page` to the `file://` path, `resize_page` or `emulate`, then
73
- `take_screenshot`. Use **Playwright** when you need scripted runs or Chrome isn't
74
- available. → Six images: three directions × two widths.
72
+ 3. **Screenshot each at desktop and mobile.** Get a browser via
73
+ [`browser-automation`](../browser-automation/SKILL.md), which owns the tool choice and its
74
+ order navigate to the `file://` path, set each viewport, capture. Don't pick a browser
75
+ tool here. → Six images: three directions × two widths.
75
76
  4. **Present for a decision** — each direction as name, file path, screenshot, and one line
76
77
  on what makes it different. Offer exactly three responses: pick one, pick one with
77
78
  modifications, or regenerate all three.
@@ -1,59 +1,77 @@
1
1
  ---
2
2
  name: task-tracking
3
- description: Associate every unit of work with a task in Linchpin's task platform (currently ClickUp, via the ClickUp MCP) with the least friction, update it when the work lands, and leave a handoff when stopping mid-flight. Use whenever starting work, creating a TODO, preparing to commit, finishing a change, or pausing work someone else may pick up — and whenever anyone says "create an issue", "create a task", "file a ticket", or "log a bug", all of which mean a ClickUp task unless they name GitHub. Resolve a task from an ID/custom-ID/URL or by searching; if none exists, confirm NO-TASK and keep working. The conventional-commit scope carries the task key (e.g. LINCHPIN-5113) or NO-TASK.
4
- version: 1.3.0
3
+ description: Associate every unit of work with a task in Linchpin's task platform (currently ClickUp, via the ClickUp MCP) with the least friction, update it when the work lands, and leave a handoff when stopping mid-flight. Use whenever starting work, creating a TODO, scoping something that will span multiple sessions or PRs, preparing to commit, finishing a change, or pausing work someone else may pick up — and whenever anyone says "create an issue", "create a task", "file a ticket", or "log a bug", all of which mean a ClickUp task unless they name GitHub. If no task exists, confirm NO-TASK and keep working; the commit scope carries the task key (e.g. LINCHPIN-5113) or NO-TASK.
4
+ version: 1.4.0
5
5
  ---
6
6
 
7
7
  # Task tracking (ClickUp)
8
8
 
9
- **Goal: get work into the task system with the least friction, while never blocking the
10
- user from working.** Every unit of work is either tied to a ClickUp task (its commits carry
11
- the task key) or explicitly marked **NO-TASK**. The user always chooses; you make the
12
- right thing the easy thing.
9
+ **Goal: get work into the task system with the least friction, while never blocking the user
10
+ from working.** Every unit of work is either tied to a ClickUp task (its commits carry the
11
+ task key) or explicitly marked **NO-TASK**. The user always chooses; you make the right thing
12
+ the easy thing.
13
13
 
14
- The platform is currently **ClickUp**, driven through the **ClickUp MCP**. Keep ClickUp
15
- specifics in the tool calls; the workflow below is what matters and would survive a
16
- platform change.
14
+ The platform is currently **ClickUp**, driven through the **ClickUp MCP** keep the ClickUp
15
+ specifics in the tool calls, so the workflow below survives a platform change.
17
16
 
18
17
  ## When to use
19
18
 
20
19
  - Starting any unit of work, before cutting a branch.
21
20
  - **Anyone asking for an issue, task, ticket, bug, or backlog item to be created** — in any
22
21
  wording. See *"Create an issue" means ClickUp* below.
22
+ - **Scoping work that won't fit one sitting** — multiple PRs, multiple sessions, or a long
23
+ list of distinct action items. See step 2.
23
24
  - Opening a local TODO that should exist in the task system too.
24
25
  - Preparing to commit and needing the scope key.
25
26
  - Finishing work — the task needs its status and a pointer to the PR.
26
27
 
27
- **Not this skill:** the commit message grammar and release rules
28
+ **Not this skill:** the commit message grammar and release behavior
28
29
  [`commit-and-release`](../commit-and-release/SKILL.md). Running checks before you commit —
29
- [`quality-gates`](../quality-gates/SKILL.md).
30
+ [`quality-gates`](../quality-gates/SKILL.md). Which space and folder a task belongs in —
31
+ [`engagement-types`](../engagement-types/SKILL.md).
30
32
 
31
33
  ## Owns
32
34
 
33
- Canonical for: resolving, creating, and updating the task; the **scope key** that goes in
34
- commits; branch naming; and the PR ↔ task link. Everything about the commit message *other
35
- than the scope* belongs to [`commit-and-release`](../commit-and-release/SKILL.md).
35
+ Canonical for: resolving, creating, and updating the task; how work is split across tasks
36
+ and subtasks; the **scope key** that goes in commits; branch naming; and the PR ↔ task link.
37
+ Everything about the commit message *other than the scope* belongs to
38
+ [`commit-and-release`](../commit-and-release/SKILL.md).
39
+
40
+ ## Preflight
41
+
42
+ Establish three things before the first ClickUp call. None of them blocks the work:
43
+
44
+ | Check | How | When it fails |
45
+ | --- | --- | --- |
46
+ | **Is the ClickUp MCP connected?** | Are `clickup_*` tools present in this session? | **Say so once, out loud** — "no ClickUp MCP in this session, so this is NO-TASK" — then work as NO-TASK |
47
+ | **Is the routing pinned?** | `.clickup.json` at the repo root, else a ClickUp section in `CLAUDE.md`/`AGENTS.md` | Fall back to the hierarchy lookup in step 4, then offer to write the file |
48
+ | **Which Space?** | Infer from the git remote or repo name (repo `linchpin.com` → Space **Linchpin**) | Widen to a scoped Space picker — never dump the whole hierarchy |
49
+
50
+ **A missing MCP is a reportable condition, not a silent skip.** A user can act on "I couldn't
51
+ reach ClickUp"; they can't act on commits that quietly say `NO-TASK`. Same for a pinned list
52
+ id that no longer resolves — say it and re-look-up rather than falling through to the default
53
+ list. Orientation beyond ClickUp is [`project-context`](../project-context/SKILL.md), which
54
+ defers back here for the Space.
36
55
 
37
56
  ## "Create an issue" means ClickUp
38
57
 
39
58
  **"Issue", "task", "ticket", "bug", "backlog item" — all of them mean a ClickUp task here.**
40
- Asked to create one, run the creation flow in step 3. Being in a GitHub repo, reviewing a
41
- PR, or reading `gh` output does not make "create an issue" mean a GitHub issue. Which space,
59
+ Asked to create one, run the creation flow in step 4. Being in a GitHub repo, reviewing a PR,
60
+ or reading `gh` output does not make "create an issue" mean a GitHub issue. Which space,
42
61
  folder, and (for multi-site clients) which site it lands in is
43
62
  [`engagement-types`](../engagement-types/SKILL.md)'s call.
44
63
 
45
- **A GitHub issue only when GitHub is named** — "open a *GitHub* issue", "file it in the
46
- repo's issues", "`gh issue create`". Open it with `gh issue create`; if it's work Linchpin
47
- will do, create the ClickUp task too and cross-link them (issue body →
48
- `app.clickup.com/t/<KEY>`; `clickup_create_comment` → issue URL). ClickUp stays the system
49
- of record. For the genuinely ambiguous — a public repo where issues *are* the tracker — ask
50
- once with `AskUserQuestion`, recommending ClickUp.
64
+ **A GitHub issue only when GitHub is named** — "open a *GitHub* issue", "`gh issue create`".
65
+ Use `gh issue create`; if it's work Linchpin will do, create the ClickUp task too and
66
+ cross-link them (issue body → `app.clickup.com/t/<KEY>`; `clickup_create_comment` → issue
67
+ URL). ClickUp stays the system of record. For the genuinely ambiguous — a public repo where
68
+ issues *are* the tracker — ask once with `AskUserQuestion`, recommending ClickUp.
51
69
 
52
70
  ## Vocabulary
53
71
 
54
72
  - **Task key / issue key** — ClickUp's *custom ID*, e.g. `LINCHPIN-5113`. Space-scoped, so
55
- prefixes differ per space (`LINCHPIN-…`, etc.). This is what goes in commits. It is **not**
56
- the internal id (`86badg2te`) though `clickup_get_task` accepts either.
73
+ prefixes differ per space. This is what goes in commits **not** the internal id
74
+ (`86badg2te`), though `clickup_get_task` accepts either.
57
75
  - **NO-TASK** — the sentinel used in the commit scope when work has no associated task.
58
76
 
59
77
  ## Workflow
@@ -66,187 +84,153 @@ Try these in order; stop at the first that succeeds:
66
84
  `app.clickup.com/t/...` URL) → `clickup_get_task` to confirm it exists. Capture its
67
85
  `custom_id`, `name`, and `status`. Done.
68
86
  2. **No reference given → search before asking.** Derive keywords from the work and
69
- `clickup_search` (or `clickup_filter_tasks`), narrowed to the relevant Space/List so you
70
- don't trawl all 36 spaces — infer the Space from the repo (e.g. repo `linchpin.com` →
71
- Space **Linchpin**). Prefer open/active statuses. If there are plausible matches, present
72
- the top few and let the user pick or reject.
73
- 3. **Still nothing ask, don't assume.** Use `AskUserQuestion`:
74
- - **Work as NO-TASK** (recommended for quick/throwaway work) — proceed now, commits use
75
- `NO-TASK`.
76
- - **Create a task now** — run the creation flow below, then use the new key.
77
- - The user can also paste a key they had in mind.
87
+ `clickup_search` (or `clickup_filter_tasks`), narrowed to the Space you established in
88
+ Preflight so you don't trawl the whole workspace. Prefer open/active statuses. If there
89
+ are plausible matches, present the top few and let the user pick or reject.
90
+ 3. **Still nothing ask, don't assume.** `AskUserQuestion` with three ways out: **work as
91
+ NO-TASK** (recommended for quick/throwaway work), **create a task now** (the flow in step
92
+ 4), or paste a key they had in mind.
78
93
 
79
94
  **Never block work.** NO-TASK is a first-class, always-available choice.
80
95
 
81
- Record the resolved key (or `NO-TASK`) for this unit of work and reuse it for every commit
82
- in it. When you open a local TODO with `TaskCreate`, put the ClickUp key (or `NO-TASK`) in
83
- the task text so the local list and ClickUp stay aligned. Then cut the working branch for
84
- this change — see **Branch & PR**.
96
+ Record the resolved key (or `NO-TASK`) and reuse it for every commit in this unit of work.
97
+ When you open a local TODO with `TaskCreate`, put the key in the task text so the local list
98
+ and ClickUp stay aligned.
85
99
 
86
- ### 2. Before committing, if NO-TASK offer to create one
100
+ ### 2. Right-size the tracking (before cutting the branch)
87
101
 
88
- When the work is complete and you're about to commit a NO-TASK change, ask once (via
89
- `AskUserQuestion`): **"Create a ClickUp task for this before committing?"**
102
+ One task is the default and usually right. But when scoping reveals work that will
90
103
 
91
- - **No** commit with `NO-TASK`.
92
- - **Yes** run the creation flow, then commit with the new key.
104
+ - **span more than one session**, or plausibly **exhaust this context window**, or
105
+ - need **more than one PR**, or
106
+ - carry **a large number of distinct action items**,
93
107
 
94
- Don't ask repeatedly and don't nag one prompt at commit time.
108
+ then one task can't hold the state, and a plan that lives only in the chat dies with the
109
+ session. Confirm the shape with the user — once, with `AskUserQuestion`:
95
110
 
96
- ### 3. Creation flow (least friction)
111
+ | Shape | Use when | How |
112
+ | --- | --- | --- |
113
+ | **One task** | Multi-step, but one sitting and one PR | The default; nothing extra to do |
114
+ | **Parent + a subtask per step** | Steps land in separate commits or PRs, or someone else may pick one up | Create the parent, then each subtask via `clickup_create_task`'s `parent` |
115
+ | **Keep the plan local** | Exploratory work whose shape will change | `TaskCreate` only — revisit if it grows |
116
+
117
+ - **Every item carries its own "done when"** in its description. A subtask named "Fix the
118
+ thing" with no acceptance line is a reminder, not a handoff.
119
+ - **The scope key is the parent's**, unless a subtask genuinely owns its own PR — then that
120
+ subtask's key scopes those commits and the parent tracks the whole.
121
+ - **Splitting late is fine.** If one task turns out bigger than it looked, come back here.
122
+
123
+ ### 3. Before committing, if NO-TASK — offer to create one
124
+
125
+ When the work is complete and you're about to commit a NO-TASK change, ask once (via
126
+ `AskUserQuestion`): **"Create a ClickUp task for this before committing?"** No → commit with
127
+ `NO-TASK`. Yes → run the creation flow, then commit with the new key. One prompt at commit
128
+ time; don't nag.
97
129
 
98
- Reached two ways: from step 2 (a NO-TASK change about to be committed), or directly, when
99
- someone just says *"create an issue/task for X"* — that's a standalone request and doesn't
100
- need a commit or a branch behind it.
130
+ ### 4. Creation flow (least friction)
131
+
132
+ Reached three ways: from step 2 (splitting a larger endeavor), from step 3 (a NO-TASK change
133
+ about to be committed), or directly, when someone just says *"create an issue/task for X"* —
134
+ a standalone request needing no commit or branch behind it.
101
135
 
102
136
  `clickup_create_task` requires a `list_id` and `name`. Resolve the list with the cheapest
103
137
  path that works:
104
138
 
105
- 1. **Use a known default if the project has one.** If the project pins a default list
106
- (see *Reducing friction*), confirm it in one line ("Create in *Linchpin linchpin.com
107
- Development*?") rather than making the user navigate.
108
- 2. **Otherwise present a picker** built from `clickup_get_workspace_hierarchy`:
109
- - Call it with `max_depth: 2` **scoped to the likely Space** (pass `space_ids`) so you
110
- return Folders + Lists for one space, not the whole workspace.
111
- - Offer the candidate Lists via `AskUserQuestion`. Example (Linchpin space):
112
- `linchpin.com Development`, `linchpin.com Deploy`, `Internal Projects …`.
113
- - Only widen to a Space picker first if you can't infer the Space.
114
- 3. **Name + details:** default the task `name` to the commit subject / work summary; confirm
115
- or let the user tweak. Optionally set `assignees: ["me"]` (resolve via
116
- `clickup_resolve_assignees`), a `priority`, and a short `markdown_description`.
117
- 4. `clickup_create_task` → read back the new task's **`custom_id`** and use it as the commit
118
- scope.
119
-
120
- ### 4. Update the task when the work lands
139
+ 1. **Use the routing you found in Preflight.** `.clickup.json` pins the Space, a default
140
+ list, and often a source-directory list map confirm the destination in one line
141
+ ("Create in *Mantle › Modules › Security*?") rather than making the user navigate.
142
+ 2. **Otherwise build a scoped picker** from `clickup_get_workspace_hierarchy` — see
143
+ [`references/clickup-json.md`](references/clickup-json.md) for both paths.
144
+ 3. **Name + details:** default `name` to the commit subject / work summary and confirm.
145
+ Optionally `assignees: ["me"]` (via `clickup_resolve_assignees`), a `priority`, and a
146
+ `markdown_description` which is where the "done when" line goes.
147
+ 4. `clickup_create_task`, then **read the key back with `clickup_get_task`.** The create
148
+ response carries `custom_id` inconsistently often `null`, because the key is assigned
149
+ after the task exists. `null` means "not yet", never "this space has no keys", so don't
150
+ fall back to the internal id as your commit scope.
151
+
152
+ ### 5. Update the task when the work lands
121
153
 
122
154
  A task that never moves is worse than no task — it makes the board lie. Once the change is
123
155
  committed and the PR is open, close the loop in ClickUp:
124
156
 
125
- 1. **Comment with the pointer.** `clickup_create_comment` on the task with the PR URL and a
126
- one-line summary of what changed. This is the step that makes the task useful to whoever
127
- picks it up next — do it even if you can't move the status.
128
- 2. **Move the status** with `clickup_update_task` — but **read the valid statuses first**.
129
- They're per-Space/List (`in progress`, `review`, `qa`, `complete`, all vary); take them
130
- from the task's own status metadata or `clickup_get_list`. If none obviously matches the
131
- state ("PR open, awaiting review"), ask rather than guess.
132
- 3. **Don't close what you can't verify.** Work in an open PR is *in review*, not done. Only
133
- move a task to a terminal status when the user says it's shipped, or when a deploy you
134
- can observe has completed.
135
- 4. **Time tracking is opt-in.** Only use `clickup_add_time_entry` (or the start/stop tools)
136
- when the user asked for it.
157
+ 1. **Comment with the pointer.** `clickup_create_comment` with the PR URL and a one-line
158
+ summary. This is what makes the task useful to whoever picks it up next — do it even if
159
+ you can't move the status.
160
+ 2. **Move the status, but read the valid ones first** (`expand_statuses: true`, or
161
+ `clickup_get_list`). They're per-Space/List and there can be dozens. If none obviously
162
+ matches the state ("PR open, awaiting review"), ask rather than guess.
163
+ 3. **Don't close what you can't verify.** An open PR is *in review*, not done terminal
164
+ statuses need the user's word or a deploy you observed.
165
+ 4. **Subtasks close as they land; the parent closes last.** Moving a parent while its
166
+ subtasks are open is the same lie as never moving anything.
167
+ 5. **Time tracking is opt-in** `clickup_add_time_entry` only when asked.
137
168
 
138
169
  For `NO-TASK` work there's nothing to update — skip this step silently.
139
170
 
140
- ### 5. Session handoff (when you stop mid-flight)
141
-
142
- When work pauses unfinished — end of day, a context switch, or handing off — the task is
143
- where the state belongs. Not a local file, not the chat: the task is what a teammate opens.
144
-
145
- Post one `clickup_create_comment` with these five things, and nothing else:
146
-
147
- ```markdown
148
- **Handoff — <date>**
149
- - **Done:** <what actually works now, and where it's committed or pushed>
150
- - **In progress:** <what's half-built, and which files>
151
- - **Open decisions:** <what needs an answer before continuing, and the options>
152
- - **Where:** branch `<branch>`, PR <link or "none yet">, environment <local|staging|prod>
153
- - **Next step:** <the single next action, concrete enough to start from cold>
154
- ```
171
+ ### 6. Session handoff (when you stop mid-flight)
155
172
 
156
- Rules that keep this useful:
173
+ When work pauses unfinished, the state belongs on the task — not in a local file and not in
174
+ the chat. Post one `clickup_create_comment` covering **done / in progress / open decisions /
175
+ where (branch, PR, environment) / next step**, written for someone who wasn't here, and
176
+ commit or stash first so it points at something real.
157
177
 
158
- - **Write it for someone who wasn't here.** No "as discussed", no pronouns pointing at chat
159
- history.
160
- - **Be honest about what's unfinished.** A handoff that overstates progress costs more than
161
- no handoff.
162
- - **One handoff comment per pause**, not a running log — supersede the previous one by
163
- posting a fresh comment rather than editing history.
164
- - Uncommitted work is not a handoff. Commit or stash it first and say which
165
- ([`commit-and-release`](../commit-and-release/SKILL.md)).
178
+ Template and the rules that make it actionable: [`references/handoff.md`](references/handoff.md).
166
179
 
167
- ## Conventional commits
180
+ ## Scope, branch & PR
168
181
 
169
- **Scope = the task key or `NO-TASK`.** That is this skill's half of the message:
182
+ **Scope = the task key or `NO-TASK`.** That is this skill's half of the commit message:
170
183
 
171
184
  ```
172
185
  feat(LINCHPIN-5113): Add cloudflare email sending on launch
173
- chore(NO-TASK): Tidy editorconfig and ignore rules
174
186
  ```
175
187
 
176
- Everything else about the message — allowed types (they differ per repo), sentence case,
177
- punctuation limits, breaking changes lives in
178
- [`commit-and-release`](../commit-and-release/SKILL.md). Two notes that are about the *key*
179
- rather than the grammar:
180
-
181
- - A trailing PR number `… (#758)` is appended by the PR/release flow; you don't add it
182
- by hand.
183
- - Reserve `chore(main): …` for release-please release commits — don't reuse `main` as a
184
- scope for normal work.
185
-
186
- ## Branch & PR
187
-
188
- Work happens on a dedicated branch opened as a PR against the base branch (usually `main`)
189
- never commit straight to `main`.
190
-
191
- - **Branch naming** (cut from an up-to-date `main`):
192
- - With a task: **`issue/<ISSUE-KEY>`** — the ClickUp custom ID, e.g. `issue/LINCHPIN-5113`.
193
- - NO-TASK: **`no-task/<short-kebab-slug>`** describing the change (a bare `issue/no-task`
194
- would collide across changes).
195
- - Started NO-TASK, then created a task before pushing? Rename the branch to match:
196
- `git branch -m issue/<ISSUE-KEY>`.
197
- - **Pull request** (head = your branch, base = `main`):
198
- - Title follows the commit convention: `type(<ISSUE-KEY | NO-TASK>): subject`.
199
- - Body **links the ClickUp task** — paste `https://app.clickup.com/t/<ISSUE-KEY>` and the
200
- key so GitHub ↔ ClickUp stay connected. For NO-TASK, note there's no task.
201
- - Keep every commit on the branch using the same scope.
188
+ Everything else — allowed types, sentence case, punctuation limits, breaking changes, release
189
+ commits is [`commit-and-release`](../commit-and-release/SKILL.md). One note about the *key*
190
+ rather than the grammar: a trailing PR number (`… (#758)`) is appended by the PR/release flow,
191
+ not by hand.
192
+
193
+ Work happens on a dedicated branch opened as a PR against the base branch (usually `main`)
194
+ never commit straight to `main`.
195
+
196
+ - **Branch naming**, cut from an up-to-date `main`: **`issue/<custom_id>`** — the ClickUp
197
+ custom ID verbatim (`issue/LINCHPIN-5113`), never the internal id and never a slug. Without
198
+ a task, **`no-task/<short-kebab-slug>`**, since a bare `issue/no-task` would collide. Created
199
+ a task after starting NO-TASK? `git branch -m issue/<custom_id>`.
200
+ - **The PR body links the ClickUp task** paste `https://app.clickup.com/t/<custom_id>` and
201
+ the key so GitHub ClickUp stay connected; for NO-TASK, note there's no task. The title
202
+ follows the commit convention, whose grammar is `commit-and-release`'s.
203
+ - Keep every commit on the branch using the same scope.
202
204
 
203
205
  ## Reducing friction
204
206
 
205
- - **Pin a default list per project.** Record the project's usual Space/List (id + path) in
206
- the project's `CLAUDE.md` or a small `.clickup.json`, so creation becomes a one-line
207
- confirm instead of navigation — e.g. `<Space> <Project> Development` with its
208
- `list_id`. The id belongs in that project's repo, not in this shared library.
207
+ - **Pin the routing per project in `.clickup.json`** the Space, a default list, and (where
208
+ the board mirrors the code) a directory list map, so creation is a one-line confirm
209
+ instead of navigation. Schema, worked example, and packaging notes:
210
+ [`references/clickup-json.md`](references/clickup-json.md). The ids live in that project's
211
+ repo, never in this shared library.
212
+ - **Write the file when you had to look it up.** Resolving a list the slow way is the moment
213
+ to offer to pin it — otherwise the next agent pays the same cost.
209
214
  - **Remember the last-used list** within a session and reuse it.
210
- - **Infer the Space from the repo** to scope every search and hierarchy call.
211
- - **Batch the questions**: when you must ask, resolve task-vs-NO-TASK and (if creating) the
212
- list in as few `AskUserQuestion` prompts as possible.
213
-
214
- ## Gotchas
215
-
216
- - **"Issue" is not a GitHub word here.** Route it to ClickUp unless GitHub was named.
217
- - **Search before creating** — avoid duplicate tasks; an open task often already exists.
218
- - **Don't dump the hierarchy.** 36 spaces is overwhelming; always scope `space_ids` and go
219
- only as deep as you need (`max_depth`).
220
- - **Use `custom_id` in commits**, never the internal id (`86badg2te`).
221
- - **Custom-ID prefixes are space-specific** — read the prefix off the task; don't assume
222
- `LINCHPIN-` for non-Linchpin work.
223
- - **NO-TASK is not failure.** Offer task creation, accept "no", and move on.
224
-
225
- ## Quick reference
226
-
227
- | Step | Tool |
228
- | --- | --- |
229
- | Confirm a given key/URL | `clickup_get_task` (accepts `LINCHPIN-5113` or `86badg2te`) |
230
- | Find an existing task | `clickup_search` / `clickup_filter_tasks` (scope to the Space) |
231
- | Build a Space/List picker | `clickup_get_workspace_hierarchy` (`space_ids`, `max_depth: 2`) |
232
- | Resolve "me"/assignees | `clickup_resolve_assignees` |
233
- | Create the task | `clickup_create_task` (`list_id` + `name` required) |
234
- | Branch the work | `git switch -c issue/LINCHPIN-#### main` (or `no-task/<slug>`) |
235
- | Commit | `type(LINCHPIN-#### | NO-TASK): subject` |
236
- | Open the PR | `gh pr create` — title `type(KEY): …`, body links `app.clickup.com/t/<KEY>` |
237
- | Comment on the task | `clickup_create_comment` (PR URL + one-line summary) |
238
- | Move the status | `clickup_update_task` (valid statuses come from the List) |
239
- | Hand off mid-flight | `clickup_create_comment` with the five-line handoff block |
215
+ - **Batch the questions** task-vs-NO-TASK, the split shape, and the list in as few
216
+ `AskUserQuestion` prompts as possible.
217
+
218
+ Call signatures for every step: [`references/clickup-mcp-tools.md`](references/clickup-mcp-tools.md).
240
219
 
241
220
  ## Guardrails
242
221
 
243
222
  - **Never open a GitHub issue in place of a ClickUp task.** "Create an issue" means ClickUp;
244
223
  `gh issue create` needs the user to have said *GitHub*.
245
- - **Never invent a task key.** If you can't resolve one, `NO-TASK` is the correct answer.
246
- - **Never block the user** waiting for a task decision NO-TASK is always available.
247
- - **Never mark a task complete** on your own judgment. An open PR is at most "in review";
248
- terminal statuses need the user's word or an observed deploy.
249
- - **Never guess a status name** they're Space-specific. Read them, then set them.
224
+ - **Never invent a task key**, and never fall back to the internal id (`86badg2te`) because
225
+ the create call returned `custom_id: null` read it back. Unresolvable means `NO-TASK`.
226
+ - **Never assume the custom-ID prefix** it's Space-specific; read it off the task rather
227
+ than reaching for `LINCHPIN-` on non-Linchpin work.
228
+ - **Never claim a task exists that you didn't create.** A missing MCP or a failed call gets
229
+ said out loud, not treated as though the board was updated.
230
+ - **Never block the user** waiting for a task decision. NO-TASK is always available and is
231
+ not a failure state; offer creation, accept "no", move on.
232
+ - **Never mark a task complete** on your own judgment, and **never guess a status name** —
233
+ they're Space-specific. An open PR is at most "in review".
250
234
  - **Never create a duplicate task** — search first; an open one usually exists.
251
235
  - **Never dump the full workspace hierarchy** into a prompt; scope `space_ids` and
252
236
  `max_depth`.
@@ -254,12 +238,16 @@ Work happens on a dedicated branch opened as a PR against the base branch (usual
254
238
 
255
239
  ## Done
256
240
 
241
+ - [ ] Preflight ran: MCP availability, routing, and Space established — and any failure was
242
+ **said out loud** rather than silently downgraded.
257
243
  - [ ] Any "create an issue/task/ticket" request produced a **ClickUp** task — or a GitHub
258
244
  issue only because the user named GitHub, in which case the two are cross-linked.
259
- - [ ] The unit of work has a resolved task key or an explicit, user-accepted `NO-TASK`.
260
- - [ ] The branch name matches the key (`issue/<KEY>` or `no-task/<slug>`).
261
- - [ ] Every commit on the branch carries the same scope.
262
- - [ ] The PR body links `app.clickup.com/t/<KEY>` (or notes there is no task).
245
+ - [ ] The unit of work has a resolved task key or an explicit, user-accepted `NO-TASK`, read
246
+ back from `custom_id` rather than taken from an internal id.
247
+ - [ ] Work spanning sessions, contexts, or PRs was split deliberately with the user, and
248
+ every task or subtask carries a "done when".
249
+ - [ ] The branch matches the key (`issue/<custom_id>` or `no-task/<slug>`), every commit on it
250
+ carries the same scope, and the PR body links `app.clickup.com/t/<custom_id>`.
263
251
  - [ ] For task-backed work: a comment with the PR link exists, and the status reflects
264
252
  reality without over-claiming completion.
265
253
  - [ ] If work is pausing unfinished, a handoff comment exists that a teammate could act on
@@ -0,0 +1,139 @@
1
+ # `.clickup.json` — per-project ClickUp routing
2
+
3
+ A small file at a project's repo root that pins **where this project's tasks live**, so
4
+ creating one is a single confirmation instead of a workspace-hierarchy crawl across dozens of
5
+ spaces.
6
+
7
+ The **convention** is portable and lives here. The **IDs** are project-specific and live in
8
+ that project's repo — never in this library.
9
+
10
+ ## Why it exists
11
+
12
+ Without it, every "create a task for X" starts the same way: call
13
+ `clickup_get_workspace_hierarchy`, guess the Space from the repo name, page through folders,
14
+ and rediscover the same handful of list IDs that were found last week. That's slow, it burns
15
+ context, and it produces inconsistent placement when the guess is wrong.
16
+
17
+ With it, the flow is: read the file, confirm the list in one line, create.
18
+
19
+ ## Where to look
20
+
21
+ Check in this order, and stop at the first hit:
22
+
23
+ 1. `.clickup.json` at the repo root
24
+ 2. A ClickUp section in the project's `CLAUDE.md` / `AGENTS.md`
25
+ 3. Nothing pinned → fall back to the hierarchy lookup in the main skill, and **offer to write
26
+ `.clickup.json`** once the list has been resolved, so the next agent doesn't repeat the work
27
+
28
+ ## When nothing is pinned — the picker
29
+
30
+ Falling back to a lookup is fine; doing it unscoped is not. `clickup_get_workspace_hierarchy`
31
+ will happily return dozens of spaces and bury the prompt.
32
+
33
+ 1. Call it with `max_depth: 2` and **`space_ids` set to the likely Space**, so you get Folders
34
+ + Lists for one space rather than the whole workspace.
35
+ 2. Offer the candidate Lists via `AskUserQuestion` — e.g. `linchpin.com › Development`,
36
+ `linchpin.com › Deploy`, `Internal Projects › …`.
37
+ 3. Only widen to a Space picker first when the Space genuinely can't be inferred from the git
38
+ remote or repo name.
39
+
40
+ Then write the file, so the next agent skips all of this.
41
+
42
+ ## Schema
43
+
44
+ Every field is optional except `space` and `defaultList` — a two-key file is already useful.
45
+
46
+ | Key | Type | Purpose |
47
+ | --- | --- | --- |
48
+ | `space` | object | `id`, `name`, and `customIdPrefix` (e.g. `MANTLE` — the prefix on custom IDs, used to sanity-check a key before putting it in a commit scope) |
49
+ | `defaultList` | object | `id`, `name`, `path`, and a `use` string saying what belongs there. The fallback for anything that doesn't route elsewhere |
50
+ | `lists` | object | Human list name → list id. Flat map; use `›` in the name for nesting when two lists share a name |
51
+ | `folders` | object | Human folder name → folder id. Only needed when a tool call wants a folder rather than a list |
52
+ | `moduleRouting` | object | Source directory → list **name** (a key in `lists`). For repos whose board mirrors their code structure |
53
+ | `unmapped` | object | Deliberate code↔board mismatches, recorded so they read as intentional rather than as failed lookups |
54
+
55
+ Two rules that matter more than the shape:
56
+
57
+ - **IDs are the contract; names are for humans.** ClickUp list names get renamed freely and
58
+ the id survives it. Route on the id, show the name.
59
+ - **`moduleRouting` points at names, not ids** — so a renamed list is a one-line fix in
60
+ `lists` rather than a find-and-replace through the routing map.
61
+
62
+ ## Example
63
+
64
+ Trimmed from a real plugin repo whose ClickUp board has one list per code module:
65
+
66
+ ```json
67
+ {
68
+ "$comment": "ClickUp routing for this repo. IDs are workspace-stable; names are for humans.",
69
+ "space": {
70
+ "id": "90140515528",
71
+ "name": "Mantle",
72
+ "customIdPrefix": "MANTLE"
73
+ },
74
+ "defaultList": {
75
+ "id": "901401607739",
76
+ "name": "Product Roadmap",
77
+ "path": "Mantle › Product Roadmap",
78
+ "use": "Cross-cutting work, new modules, and anything that doesn't map to a single existing module."
79
+ },
80
+ "lists": {
81
+ "Product Roadmap": "901401607739",
82
+ "Housekeeping": "901414301271",
83
+ "Optimizations": "901413938412",
84
+ "Security": "901413938417",
85
+ "Declutter": "901413954051"
86
+ },
87
+ "folders": {
88
+ "Modules": "90147467026"
89
+ },
90
+ "moduleRouting": {
91
+ "$comment": "includes/Modules/<Dir> → list name. Fall back to defaultList.",
92
+ "Optimizations": "Optimizations",
93
+ "Security": "Security"
94
+ },
95
+ "unmapped": {
96
+ "modulesWithoutList": [
97
+ "Maintenance — no dedicated list; use Product Roadmap"
98
+ ],
99
+ "listsWithoutModule": [
100
+ "Declutter — intended home for admin-menu tidying; no module exists yet"
101
+ ]
102
+ }
103
+ }
104
+ ```
105
+
106
+ `$comment` keys are ignored by every JSON parser and are the only way to annotate JSON —
107
+ use them, since this file is read by people as often as by agents.
108
+
109
+ ## `unmapped` is the part people skip
110
+
111
+ A board and a codebase drift apart. A list gets created for work that was never built; a
112
+ module ships without anyone adding a list for it. An agent that finds no route for
113
+ `includes/Modules/Maintenance` can't tell "nobody added it" from "I looked in the wrong
114
+ place", so it either asks a pointless question or files the task somewhere wrong.
115
+
116
+ Recording the mismatch converts a lookup failure into a documented decision. Keep it honest
117
+ and prune it when the gap closes.
118
+
119
+ ## Packaging
120
+
121
+ Projects that build a distributable — WordPress plugins and themes especially — should
122
+ exclude the file from the build:
123
+
124
+ - `.distignore` for `wp dist-archive` / plugin zips
125
+ - `.npmignore` or a `files` allowlist for npm packages
126
+
127
+ It's dev-time metadata with no runtime meaning. It contains no secrets — workspace, folder,
128
+ and list IDs are not credentials, and the ClickUp API still requires a token — so it can be
129
+ committed to a private repo without concern. Treat it the same as any other project config
130
+ in a public repo: harmless, but pointless to publish.
131
+
132
+ ## Keeping it current
133
+
134
+ - A renamed list keeps its id — nothing to do.
135
+ - A **new** list, or one that gets retired, needs the map updated. Cheapest moment is when
136
+ you notice the drift while creating a task; fix it in the same PR.
137
+ - If a lookup by the pinned id fails, the list was deleted or moved out of the space. Re-run
138
+ the hierarchy lookup, correct the file, and say so — don't silently fall back to the
139
+ default list, or tasks quietly pile up in the wrong place.
@@ -0,0 +1,37 @@
1
+ # ClickUp MCP — call signatures
2
+
3
+ The tool for each step of the workflow in [`../SKILL.md`](../SKILL.md). Names are the ClickUp
4
+ MCP's; if these tools aren't in the session, the workflow's Preflight says what to do instead.
5
+
6
+ | Step | Tool |
7
+ | --- | --- |
8
+ | Confirm a given key/URL | `clickup_get_task` (accepts `LINCHPIN-5113` or `86badg2te`) |
9
+ | Find an existing task | `clickup_search` / `clickup_filter_tasks` (scope to the Space) |
10
+ | Build a Space/List picker | `clickup_get_workspace_hierarchy` (`space_ids`, `max_depth: 2`) |
11
+ | Resolve "me"/assignees | `clickup_resolve_assignees` |
12
+ | Create the task | `clickup_create_task` (`list_id` + `name` required) |
13
+ | Create a subtask | `clickup_create_task` with `parent: <parent task id>` |
14
+ | Read the key back | `clickup_get_task` → `custom_id` |
15
+ | List valid statuses | `clickup_get_task` with `expand_statuses: true`, or `clickup_get_list` |
16
+ | Branch the work | `git switch -c issue/LINCHPIN-#### main` (or `no-task/<slug>`) |
17
+ | Commit | `type(LINCHPIN-#### \| NO-TASK): subject` |
18
+ | Open the PR | `gh pr create` — title `type(KEY): …`, body links `app.clickup.com/t/<KEY>` |
19
+ | Comment on the task | `clickup_create_comment` (PR URL + one-line summary) |
20
+ | Move the status | `clickup_update_task` (valid statuses come from the List) |
21
+ | Hand off mid-flight | `clickup_create_comment` with the five-line handoff block |
22
+
23
+ ## Two that bite
24
+
25
+ **`custom_id` is assigned after creation.** `clickup_create_task` returns `custom_id: null`
26
+ much of the time — the key exists moments later. Always `clickup_get_task` before using a key
27
+ as a commit scope, and never read `null` as "this space doesn't use custom IDs".
28
+
29
+ **Statuses are per-Space/List and there are often dozens** — a single Linchpin list carries
30
+ open, unstarted, custom, done, and closed variants (`needs estimate`, `needs peer/code
31
+ review`, `in qa/testing`, `blocked by client`, `resolved`, `complete`, …). Read them; never
32
+ guess a name, and never assume `complete` is the only terminal one.
33
+
34
+ ## Escaping note for editors of this file
35
+
36
+ The pipe in the commit row is escaped (`\|`). GFM splits table cells on pipes **even inside
37
+ code spans**, so an unescaped one silently truncates the cell.
@@ -0,0 +1,40 @@
1
+ # Session handoff
2
+
3
+ What to post on a task when work pauses unfinished — end of day, a context switch, running
4
+ low on context, or handing to someone else. Step 6 of [`../SKILL.md`](../SKILL.md).
5
+
6
+ The task is where the state belongs. Not a local file, not the chat: the task is what a
7
+ teammate opens.
8
+
9
+ ## The comment
10
+
11
+ One `clickup_create_comment`, these five things, nothing else:
12
+
13
+ ```markdown
14
+ **Handoff — <date>**
15
+ - **Done:** <what actually works now, and where it's committed or pushed>
16
+ - **In progress:** <what's half-built, and which files>
17
+ - **Open decisions:** <what needs an answer before continuing, and the options>
18
+ - **Where:** branch `<branch>`, PR <link or "none yet">, environment <local|staging|prod>
19
+ - **Next step:** <the single next action, concrete enough to start from cold>
20
+ ```
21
+
22
+ ## Rules that keep it useful
23
+
24
+ - **Write it for someone who wasn't here.** No "as discussed", no pronouns pointing at chat
25
+ history, no "the file we changed".
26
+ - **Be honest about what's unfinished.** A handoff that overstates progress costs more than
27
+ no handoff — the next person trusts it and builds on sand.
28
+ - **One handoff per pause, not a running log.** Supersede the previous one by posting a fresh
29
+ comment rather than editing history, so the newest comment is always current.
30
+ - **Uncommitted work is not a handoff.** Commit or stash first and say which, per
31
+ [`commit-and-release`](../../commit-and-release/SKILL.md). A handoff pointing at a dirty
32
+ working tree on someone else's machine is not actionable.
33
+ - **On a parent + subtasks split, the handoff goes on the parent** and names the subtask in
34
+ flight — so one comment still answers "where is this?" without opening every child.
35
+
36
+ ## Why the "Next step" line carries the most weight
37
+
38
+ Everything above it is context; that line is the only part that gets someone moving. "Finish
39
+ the migration" fails the test. "Run `npm run validate` — it fails on `wp-audit`'s missing
40
+ README row; add the row and re-run" passes: the next person starts without rereading anything.
@@ -35,8 +35,8 @@ the code being changed.
35
35
  | Target | How | Caveat |
36
36
  | --- | --- | --- |
37
37
  | Local Studio site | [`wp-studio-cli`](../wp-studio-cli/SKILL.md) → `need_for_speed`, `take_screenshot`, `inspect_design` | **Synthetic.** PHP-WASM + SQLite, no CDN, no production cache — good for diagnosis and before/after, not a production claim |
38
- | Production / staging | Real-browser tooling against the live URL; server state via [`wp-pressable`](../wp-pressable/SKILL.md) read-only commands | The only numbers you can quote to a client as real |
39
- | Legacy local (wp-env / LocalWP) | Browser tooling against the local URL | Studio MCP tools won't apply |
38
+ | Production / staging | A real browser via [`browser-automation`](../browser-automation/SKILL.md) against the live URL; server state via [`wp-pressable`](../wp-pressable/SKILL.md) read-only commands | The only numbers you can quote to a client as real |
39
+ | Legacy local (wp-env / LocalWP) | [`browser-automation`](../browser-automation/SKILL.md) against the local URL | Studio MCP tools won't apply |
40
40
 
41
41
  Resolve two things before measuring: **which environment**, and **which URL path** (default
42
42
  `/` if unspecified). State both in the report — an audit without them isn't reproducible.
@@ -51,68 +51,14 @@ Target shape (the repo root doubles as `wp-content`):
51
51
  .gitignore # allowlist pattern — see below
52
52
  ```
53
53
 
54
- ### composer.json baseline
55
-
56
- ```json
57
- {
58
- "name": "linchpin/<project>",
59
- "repositories": [
60
- { "type": "composer", "url": "https://wpackagist.org" },
61
- { "type": "composer", "url": "https://packagist.linchpin.com" }
62
- ],
63
- "require": {
64
- "php": ">=8.2"
65
- },
66
- "require-dev": {
67
- "composer/installers": "^2"
68
- },
69
- "extra": {
70
- "installer-paths": {
71
- "plugins/{$name}/": ["type:wordpress-plugin"],
72
- "themes/{$name}/": ["type:wordpress-theme"]
73
- }
74
- }
75
- }
76
- ```
54
+ ### composer.json and .gitignore baselines
77
55
 
78
- - wordpress.org plugins/themes `wpackagist-plugin/<slug>` / `wpackagist-theme/<slug>`.
79
- - Premium or shared-private packages → `linchpin/<slug>` from packagist.linchpin.com.
80
- - A plugin built **only for this client** is committed to `plugins/` instead (and
81
- allowlisted in `.gitignore`).
82
- - Which plugins a project needs is project-specific — don't copy another site's list.
83
- - Mature projects add the PHP QA stack to `require-dev` (`wp-coding-standards/wpcs`,
84
- `phpstan/phpstan` + `szepeviktor/phpstan-wordpress`, `php-parallel-lint/php-parallel-lint`,
85
- `friendsofphp/php-cs-fixer`) — mirror `linchpin/linchpin.com` when setting that up.
86
-
87
- ### .gitignore baseline (allowlist pattern)
88
-
89
- Ignore everything Composer or the runtime writes; explicitly re-include what's ours:
90
-
91
- ```gitignore
92
- # Plugins/themes are Composer-installed — commit only project code
93
- /plugins/*
94
- !plugins/<project>-functionality/
95
- !plugins/<project>-functionality/**
96
- /themes/*
97
- !themes/<project>/
98
- !themes/<project>/**
99
- /vendor
100
- node_modules
101
-
102
- # WordPress runtime
103
- debug.log
104
- /uploads/
105
- /upgrade/
106
-
107
- # WordPress Studio runtime (SQLite) — never commit these
108
- db.php
109
- /database
110
- /mu-plugins/sqlite-database-integration
111
- mu-plugins/99-studio-loader.php
112
- ```
56
+ Both templates, the package-name conventions for each dependency source, and the allowlist
57
+ trap: [`references/scaffold-baselines.md`](references/scaffold-baselines.md).
113
58
 
114
- Adding a new **committed** plugin later requires a new `!plugins/<name>/` pair
115
- otherwise the `/plugins/*` rule silently keeps it out of git.
59
+ Two things worth knowing before you open it: **which plugins a project needs is
60
+ project-specific** don't copy another site's list and the `.gitignore` is an *allowlist*,
61
+ so every committed plugin needs its own re-include pair or git silently ignores it.
116
62
 
117
63
  ### The project theme
118
64
 
@@ -203,19 +149,6 @@ correct. From here, day-to-day operation is `wp-studio-cli`.
203
149
  - **Media isn't in git** (`/uploads/` is ignored). Getting real content/uploads locally
204
150
  is per-project — check that project's docs.
205
151
 
206
- ## Quick reference
207
-
208
- | Task | Command |
209
- | --- | --- |
210
- | Start the project theme | see [`wp-theme-baseline`](../wp-theme-baseline/SKILL.md) — child / fresh / fork |
211
- | Install plugins | `composer install` (repo root) |
212
- | Add a wordpress.org plugin | `composer require wpackagist-plugin/<slug>` |
213
- | Add a premium/shared plugin | `composer require linchpin/<slug>` (packagist.linchpin.com) |
214
- | Build the theme (if it has a build) | `npm run build` in `themes/<project>` (`npm start` to watch) |
215
- | Symlink repo into Studio | `mv <site>/wp-content <site>/wp-content-studio-default && ln -s <repo> <site>/wp-content` |
216
- | Start the site | `studio start --skip-browser --path ~/Studio/<project>` |
217
- | Activate the theme | `studio wp theme activate <project> --path ~/Studio/<project>` |
218
- | Site URL + credentials | `studio site status --path ~/Studio/<project> --format json` |
219
152
 
220
153
  ## Guardrails
221
154
 
@@ -231,7 +164,8 @@ correct. From here, day-to-day operation is `wp-studio-cli`.
231
164
  - **Never commit Composer-installed plugins/themes** — `installer-paths` writes into
232
165
  gitignored directories by design.
233
166
  - Don't hand-edit versions or `CHANGELOG.md` in a scaffolded repo — release-please owns
234
- them ([`commit-and-release`](../commit-and-release/SKILL.md)).
167
+ them ([`commit-and-release`](../commit-and-release/SKILL.md)), and tie the setup work to a
168
+ task before committing ([`task-tracking`](../task-tracking/SKILL.md)).
235
169
 
236
170
  ## Done
237
171
 
@@ -241,14 +175,3 @@ correct. From here, day-to-day operation is `wp-studio-cli`.
241
175
  and the SQLite runtime pieces intact.
242
176
  - [ ] The site loads, the project theme is active, and the admin URL/credentials are known.
243
177
  - [ ] `.gitignore` allowlists only project code; `git status` is clean of vendored plugins.
244
-
245
- ## Related skills
246
-
247
- - [`wp-theme-baseline`](../wp-theme-baseline/SKILL.md) — choosing and standing up the project
248
- theme that lands in `themes/<project>/`.
249
- - [`wp-studio-cli`](../wp-studio-cli/SKILL.md) — operating the running Studio site (WP-CLI
250
- passthrough, `eval`, the PHP-WASM `ABSPATH` rule).
251
- - [`wp-pressable`](../wp-pressable/SKILL.md) — the hosted environments this baseline deploys
252
- to, and the release-please → `linchpin/actions` pipeline.
253
- - [`task-tracking`](../task-tracking/SKILL.md) — tie the setup work to a ClickUp task before
254
- committing.
@@ -0,0 +1,83 @@
1
+ # Scaffold baselines — `composer.json` and `.gitignore`
2
+
3
+ The two file templates a new Linchpin WordPress project repo starts from. Copy, substitute
4
+ `<project>`, then follow Part 1 of [`../SKILL.md`](../SKILL.md).
5
+
6
+ Both encode the same premise: **the repo is `wp-content`**, plugins and themes arrive via
7
+ Composer, and only project-specific code is committed.
8
+
9
+ ## `composer.json`
10
+
11
+ ```json
12
+ {
13
+ "name": "linchpin/<project>",
14
+ "repositories": [
15
+ { "type": "composer", "url": "https://wpackagist.org" },
16
+ { "type": "composer", "url": "https://packagist.linchpin.com" }
17
+ ],
18
+ "require": {
19
+ "php": ">=8.2"
20
+ },
21
+ "require-dev": {
22
+ "composer/installers": "^2"
23
+ },
24
+ "extra": {
25
+ "installer-paths": {
26
+ "plugins/{$name}/": ["type:wordpress-plugin"],
27
+ "themes/{$name}/": ["type:wordpress-theme"]
28
+ }
29
+ }
30
+ }
31
+ ```
32
+
33
+ Where dependencies come from:
34
+
35
+ | Source | Package name | Command |
36
+ | --- | --- | --- |
37
+ | wordpress.org plugin | `wpackagist-plugin/<slug>` | `composer require wpackagist-plugin/<slug>` |
38
+ | wordpress.org theme | `wpackagist-theme/<slug>` | `composer require wpackagist-theme/<slug>` |
39
+ | Premium / shared-private | `linchpin/<slug>` | `composer require linchpin/<slug>` |
40
+ | Built only for this client | *not a dependency* | Commit it to `plugins/` and allowlist it below |
41
+
42
+ - **Which plugins a project needs is project-specific** — don't copy another site's list.
43
+ - `installer-paths` writes into `plugins/` and `themes/`, which are gitignored. That's by
44
+ design; CI runs `composer install` at deploy time.
45
+ - Mature projects add the PHP QA stack to `require-dev`: `wp-coding-standards/wpcs`,
46
+ `phpstan/phpstan` + `szepeviktor/phpstan-wordpress`, `php-parallel-lint/php-parallel-lint`,
47
+ `friendsofphp/php-cs-fixer`. Mirror an existing repo that already has it configured rather
48
+ than assembling the versions from scratch.
49
+
50
+ ## `.gitignore` (allowlist pattern)
51
+
52
+ Ignore everything Composer or the runtime writes; explicitly re-include what's ours:
53
+
54
+ ```gitignore
55
+ # Plugins/themes are Composer-installed — commit only project code
56
+ /plugins/*
57
+ !plugins/<project>-functionality/
58
+ !plugins/<project>-functionality/**
59
+ /themes/*
60
+ !themes/<project>/
61
+ !themes/<project>/**
62
+ /vendor
63
+ node_modules
64
+
65
+ # WordPress runtime
66
+ debug.log
67
+ /uploads/
68
+ /upgrade/
69
+
70
+ # WordPress Studio runtime (SQLite) — never commit these
71
+ db.php
72
+ /database
73
+ /mu-plugins/sqlite-database-integration
74
+ mu-plugins/99-studio-loader.php
75
+ ```
76
+
77
+ **The allowlist is the part that bites.** Adding a new *committed* plugin later requires its
78
+ own `!plugins/<name>/` **and** `!plugins/<name>/**` pair — without both, the `/plugins/*` rule
79
+ silently keeps it out of git and the omission usually surfaces at deploy.
80
+
81
+ The Studio block matters just as much in the other direction: those four paths are the local
82
+ SQLite database layer. Committing them ships a `db.php` drop-in that shadows a real host's
83
+ MySQL configuration.
@@ -27,6 +27,11 @@ the fallback and the scripting route.
27
27
  [`wp-audit`](../wp-audit/SKILL.md). A legacy project on wp-env or LocalWP — use that
28
28
  project's tooling ([`quality-gates`](../quality-gates/SKILL.md) detects which).
29
29
 
30
+ `take_screenshot` and `inspect_design` below are **Studio's own**, rendering PHP-WASM rather
31
+ than driving a browser — enough for markup and layout checks. When you need a *real* browser
32
+ against the Studio URL (actual session state, extensions, console and network behaviour), get
33
+ one via [`browser-automation`](../browser-automation/SKILL.md).
34
+
30
35
  ## Preflight — pick the interface
31
36
 
32
37
  1. **Try MCP first.** A cheap call like `site_list` confirms it's connected.
@@ -61,6 +61,13 @@ hand-roll this parse and drift"). If you can't name one, drop to B.
61
61
  Most skills here are **A or B**. `wordpress-blocks` is the reference B (recipes and grammar
62
62
  live in `references/`); this library's own `scripts/validate-skills.mjs` is the C pattern.
63
63
 
64
+ **What the validator checks:** a body over **200 lines with no `references/` at all** warns —
65
+ that combination is Tier B content held in Tier A. Length alone is never the fault;
66
+ `wordpress-blocks` runs 877 lines across its package and passes, because the
67
+ reference-shaped parts were promoted. **Don't compress prose to clear this warning** —
68
+ promote the templates, command matrices, and schemas. That's the only fix that improves the
69
+ skill rather than just the number.
70
+
64
71
  ## Step 3 — Write `SKILL.md`
65
72
 
66
73
  ### Frontmatter (all required)