@marcomigozzi/trailhead 0.1.0

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.
Files changed (37) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +269 -0
  3. package/bin/trailhead.js +108 -0
  4. package/package.json +36 -0
  5. package/plugins/trailhead/.claude-plugin/plugin.json +25 -0
  6. package/plugins/trailhead/commands/adopt.md +6 -0
  7. package/plugins/trailhead/commands/bug.md +6 -0
  8. package/plugins/trailhead/commands/config.md +6 -0
  9. package/plugins/trailhead/commands/grill.md +6 -0
  10. package/plugins/trailhead/commands/idea.md +6 -0
  11. package/plugins/trailhead/commands/inbox.md +6 -0
  12. package/plugins/trailhead/commands/map.md +6 -0
  13. package/plugins/trailhead/commands/new.md +6 -0
  14. package/plugins/trailhead/commands/note.md +6 -0
  15. package/plugins/trailhead/commands/pause.md +6 -0
  16. package/plugins/trailhead/commands/resume.md +6 -0
  17. package/plugins/trailhead/commands/seed.md +6 -0
  18. package/plugins/trailhead/commands/split.md +6 -0
  19. package/plugins/trailhead/commands/ticket.md +6 -0
  20. package/plugins/trailhead/commands/todo.md +6 -0
  21. package/plugins/trailhead/commands/work.md +6 -0
  22. package/plugins/trailhead/hooks/hooks.json +26 -0
  23. package/plugins/trailhead/hooks/trailhead-commit-guard.js +72 -0
  24. package/plugins/trailhead/hooks/trailhead-issue-injection-scanner.js +66 -0
  25. package/plugins/trailhead/skills/trailhead/SKILL.md +429 -0
  26. package/plugins/trailhead/skills/trailhead/references/configuration.md +42 -0
  27. package/plugins/trailhead/skills/trailhead/references/techniques/acceptance-testing.md +9 -0
  28. package/plugins/trailhead/skills/trailhead/references/techniques/code-review.md +10 -0
  29. package/plugins/trailhead/skills/trailhead/references/techniques/codebase-map.md +10 -0
  30. package/plugins/trailhead/skills/trailhead/references/techniques/debug.md +13 -0
  31. package/plugins/trailhead/skills/trailhead/references/techniques/domain-vocabulary.md +10 -0
  32. package/plugins/trailhead/skills/trailhead/references/techniques/grilling.md +14 -0
  33. package/plugins/trailhead/skills/trailhead/references/techniques/plan-review.md +15 -0
  34. package/plugins/trailhead/skills/trailhead/references/techniques/prototype.md +16 -0
  35. package/plugins/trailhead/skills/trailhead/references/techniques/research.md +11 -0
  36. package/plugins/trailhead/skills/trailhead/references/techniques/tdd.md +11 -0
  37. package/plugins/trailhead/templates/trailhead-label-guard.yml +55 -0
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Marco Migozzi
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,269 @@
1
+ # trailhead
2
+
3
+ **Start and drive large projects as a map of tickets on GitHub Issues — resolving one at a time until the way to the destination is clear.**
4
+
5
+ `trailhead` is an orchestrator skill for [Claude Code](https://docs.claude.com/en/docs/claude-code). It gives a big, foggy idea a place to begin — a *trailhead* — and a disciplined way to walk it to a working result, with the whole plan living on your issue tracker instead of in scattered local files.
6
+
7
+ ---
8
+
9
+ ## Why trailhead exists
10
+
11
+ Two approaches to agent-driven project work each nail one half of the problem:
12
+
13
+ - **[Wayfinder](https://github.com/mattpocock/skills)** (by Matt Pocock) is brilliant at *starting*. It turns a loose idea into a **shared map of decision tickets on your issue tracker**, named through a proper grilling conversation, with a visible frontier and a "fog of war" for what isn't sharp enough to plan yet. You resolve one decision per session and the map grows outward. What it deliberately doesn't do is *build* — it plans, then hands off.
14
+
15
+ - **GSD** (the [open-gsd](https://github.com/open-gsd/gsd-core) workflow system) is brilliant at *doing*. Its `discuss → plan → execute → verify` cycle, atomic commits, and zero-friction capture make the actual construction reliable and reviewable. But its state lives in a local `.planning/` tree, separate from where the team can see it.
16
+
17
+ Working across both, one pattern kept recurring: **use Wayfinder's map and onboarding to decide the shape of the work, then use GSD's engine to build each piece — without ever leaving the issue tracker.** `trailhead` is that pattern made into a single skill.
18
+
19
+ > **trailhead is self-contained.** Wayfinder and GSD are *inspiration*, not dependencies. Every technique those systems package as a separate skill — grilling, TDD, systematic debugging, codebase mapping, code review — is **built in here as an inline protocol**. trailhead's core invokes no other skill; its subagents (research, codebase-map, review) use Claude Code's built-in `Agent` tool. The only thing to install is trailhead itself. (One opt-in exception: `claude.ai/design` mockup mode uses Claude Code's built-in Claude Design when enabled, and silently falls back to local-disk mockups when it isn't.)
20
+
21
+ The design choices that fall out of it:
22
+
23
+ - **Everything lives on GitHub Issues.** The map is the parent issue; each ticket is a child issue; discussion, plan, and verification are *comments*. There is no `.planning/` directory — the repo holds code only, and the plan is always visible to anyone with the repo.
24
+ - **Execution happens inside the map.** Unlike pure Wayfinder ("plan, don't do"), build tickets graduate from the fog and are executed as children of the map. The destination is the **working artifact** (a deployed app), not a spec document — though a project can override that and stop at the spec.
25
+ - **Lean by default.** The heavy machinery (codebase mapping, systematic debugging) runs once where it earns its keep; the per-ticket cycle stays light.
26
+
27
+ ---
28
+
29
+ ## How it works
30
+
31
+ A **map** is one GitHub issue labelled `trailhead:map`. It's an index, not a store: a Destination, standing Notes, the `Decisions so far`, the `Not yet specified` fog, and what's `Out of scope`.
32
+
33
+ Each **ticket** is a child issue with a type label and a one-question body. The **frontier** is the set of open, unassigned tickets whose blockers are all closed — what's takeable right now. You claim a ticket by assigning it to yourself, resolve it with the engine for its type, then record the answer as a comment, close it, and gist it back onto the map.
34
+
35
+ As tickets resolve, the fog clears: questions that were too vague to phrase become sharp enough to ticket, one at a time, until nothing is left to decide or build and the destination is reached.
36
+
37
+ ---
38
+
39
+ ## Workflow
40
+
41
+ **The lifecycle.** Every project walks the same loop:
42
+
43
+ 1. **Start** — `/trailhead:new "<idea>"` (greenfield) or `/trailhead:adopt` (existing code: map the codebase once, then go lean). This *charts the map*: name the destination, then map the frontier breadth-first into the first tickets and the fog.
44
+ 2. **Work the frontier, one ticket per session** — `/trailhead:work` takes the next takeable ticket (or one you name) and runs the engine for its type: `research` gathers a fact · `decision` diverges options then grills to choose · `prototype` makes a rough artifact to react to · `build` runs discuss → plan → execute → verify · `bug` runs repro → diagnose → fix → verify · `task` is manual plumbing that unblocks a decision.
45
+ 3. **Close & unblock** — resolve the ticket (comment + close), gist it into `Decisions so far`, and drop `trailhead:blocked` from any dependent whose last blocker just closed — graduating it onto the frontier.
46
+ 4. **Repeat** until the frontier is empty and the fog has cleared — the destination (a working artifact) is reached.
47
+
48
+ Along the way: **capture** ideas mid-work without derailing, **split** a ticket that grew too big, **pause/resume** across sessions, and let several people work unblocked tickets in parallel.
49
+
50
+ ### A worked example — "add social login"
51
+
52
+ Destination: *users sign in with Google and GitHub, alongside email/password.* Charting fans it into six tickets — one of each type — wired by their dependencies:
53
+
54
+ ```
55
+ ① research "OAuth providers + library: Supabase Auth native or Auth.js?" ← frontier (AFK)
56
+ ② decision "approach: Supabase Auth native vs Auth.js custom" ← blocked by ①
57
+ ③ task "register OAuth apps on Google + GitHub, get client id/secret" ← frontier (HITL)
58
+ ④ prototype "how the login screen with social buttons looks" ← frontier (HITL)
59
+ ⑤ build "implement Google + GitHub login" ← blocked by ②③④
60
+ ⑥ bug (appears after ⑤ ships)
61
+ ```
62
+
63
+ Initial frontier = ①③④ — three people can start in parallel. Then:
64
+
65
+ - **① research** → a subagent reads the docs, returns a decision-ready finding, closes → unblocks ②.
66
+ - **② decision** → diverge the options, grill to choose native vs custom, record the why.
67
+ - **③ task** → the agent hands you a checklist (it needs *your* Google/GitHub accounts); you register the apps, it records where the credentials live.
68
+ - **④ prototype** → a rough login screen (on the configured claude.ai/design project), approved before UI code.
69
+ - With ②③④ closed, **⑤ build** graduates → discuss → plan → execute (atomic commits, TDD at the auth seams) → verify (tests + code review + acceptance: the agent drives the browser through the real login flow).
70
+ - After it ships you spot a wrong redirect in prod → `/trailhead:bug --of ⑤ "GitHub redirect goes to localhost"` → a new ticket carrying `Regression of: ⑤`, worked repro → diagnose → fix → verify. ⑤ stays closed.
71
+
72
+ ```
73
+ ① research ─┐
74
+ ③ task ─────┼─► ② decision ─┐
75
+ ④ prototype ┘ ├─► ⑤ build ──(ship)──► ⑥ bug (Regression of: ⑤)
76
+ ③ task ──────────────────────┘
77
+ ```
78
+
79
+ The preparatory types (`research`/`decision`/`prototype`/`task`) unblock the constructive ones (`build`/`bug`); parallelism is real but across sessions on the frontier, not inside one.
80
+
81
+ ### …then a suggestion arrives — inbox → fog → graduation
82
+
83
+ The app is live, and a user *not on the team* files a plain issue: *"can we add Apple sign-in too?"* — no `trailhead:*` label, so it isn't a ticket yet. Here's how an outside voice becomes a first-class ticket on the map, without losing the reporter's authorship:
84
+
85
+ 1. **Inbox** — `/trailhead:inbox` lists it under *New — to triage*. It's in scope but not sharp: web only or native? does it need the paid Apple Developer account? You can't phrase one answerable Question yet — so you don't force a ticket.
86
+ 2. **Fog** — label it `trailhead:fog` and **keep the issue open as a clarification space**. A comment posts the sharpening questions (async grilling); the reporter and anyone interested refine it in the thread. It's off the frontier but tracked — `gh issue list --label trailhead:fog` is the durable list, and the reporter follows their own issue.
87
+ 3. **Graduation** — the discussion converges. Next time you run `/trailhead:inbox`, its *Parked fog* section surfaces the thread as recently-active — that's how you notice the fog cleared. Now the Question is phrasable, so you **adopt it in place**: swap `trailhead:fog` for `trailhead:ticket` + a type label, add the `Parent:` line and the `## Question`. Same issue number, same reporter credited — it lands on the frontier, ready for `/trailhead:work`.
88
+
89
+ ```
90
+ suggestion issue ──inbox──► 🌫️ fog (kept open, discussed) ──sharpens──► 🎫 ticket on the frontier
91
+ (same #, reporter still credited)
92
+ ```
93
+
94
+ No separate backlog, no lost credit: the map absorbs external suggestions the same way it grows its own — through fog that graduates when it's sharp.
95
+
96
+ ---
97
+
98
+ ## Commands
99
+
100
+ `trailhead` is user-invoked (it won't fire on its own). The **first word** is the verb; the rest is text or a ticket number. With no verb, `/trailhead` does **smart entry**: it inspects the repo and proposes the right next move.
101
+
102
+ Every verb is also a namespaced command — `/trailhead:new`, `/trailhead:work`, `/trailhead:bug`, … — so typing `/trailhead` lists them all in the command picker. `/trailhead <verb>` and `/trailhead:<verb>` are equivalent.
103
+
104
+ ### Flow
105
+
106
+ | Command | What it does |
107
+ |---|---|
108
+ | `/trailhead` | smart entry — detect state and propose |
109
+ | `/trailhead:new [idea]` | chart a new map from a loose idea |
110
+ | `/trailhead:adopt` | adopt an existing project (map the codebase once, then go lean) |
111
+ | `/trailhead:work [ticket]` | work the next frontier ticket, or the one you name |
112
+ | `/trailhead:inbox [issue]` | triage issues opened by others and integrate the good ones into the map |
113
+ | `/trailhead:resume [ticket]` | resume a paused ticket from its `PAUSED` checkpoint |
114
+ | `/trailhead:pause [note]` | checkpoint the ticket in play so anyone can resume it |
115
+ | `/trailhead:ticket <type> <title>` | open a ticket on the fly (diverges briefly first — a micro-charting act) |
116
+ | `/trailhead:split [ticket]` | split an oversized ticket into children, supersede the original |
117
+ | `/trailhead:grill [topic]` | run a standalone grilling session on a decision/topic |
118
+ | `/trailhead:map` | show the low-res map (destination, decisions, frontier, fog) |
119
+
120
+ ### Capture — zero friction, one confirmation line, resolves nothing
121
+
122
+ | Command | Lands as | Meaning |
123
+ |---|---|---|
124
+ | `/trailhead:todo <text>` | a frontier ticket | *I'm doing it* — defined work, now |
125
+ | `/trailhead:seed <text>` | a blocked ticket (trigger noted) | *I'll do it when X happens* |
126
+ | `/trailhead:idea <text>` | the fog (`Not yet specified`) | *maybe I'll do it* — graduates to a ticket if it sharpens |
127
+ | `/trailhead:note <text>` | verbatim text | *remember this* — not necessarily work |
128
+ | `/trailhead:bug [--of <ticket>] <text>` | a `bug` ticket | a defect; `--of` records it as a `Regression of:` a closed ticket |
129
+
130
+ The four fog/ticket captures form a spectrum of commitment and timing: **note < idea < seed < todo**. Discriminator — not even work → `note`; maybe, once it's clear → `idea`; yes, when X happens → `seed`; yes, now → `todo`.
131
+
132
+ ---
133
+
134
+ ## Ticket types and their engines
135
+
136
+ Each ticket carries a type label; each type has its own way of being resolved.
137
+
138
+ Each type has its own inline engine — no external skill is invoked.
139
+
140
+ | Type | Produces | Mode | Engine |
141
+ |---|---|---|---|
142
+ | `decision` | a choice | HITL | diverge the options if unclear, then grill to converge on one |
143
+ | `research` | a fact | AFK | a subagent on a throwaway branch (the only type run in parallel) |
144
+ | `prototype` | an approved direction | HITL | a rough throwaway artifact to react to; UI screens go through this (disk, or a configured claude.ai/design project) before UI code |
145
+ | `build` | working code | HITL/AFK | `discuss → plan → execute → verify` — atomic commits, TDD at seams, code review + acceptance testing (browser-drive or guided UAT) |
146
+ | `bug` | corrected code | HITL/AFK | `repro → diagnose → fix → verify`; a defect in closed work is a *new* ticket (`Regression of:`), not a reopen |
147
+ | `task` | an external state change | HITL/AFK | manual work that unblocks a decision (provision access, move data, sign up) |
148
+
149
+ Two rules of thumb: build tickets **never auto-grill** — on blocking ambiguity the skill stops and asks; and brainstorming (divergence) lives in charting, in `ticket`'s micro-charting, and in a `decision`'s option phase — never in the grilling itself, which only converges.
150
+
151
+ ---
152
+
153
+ ## Labels
154
+
155
+ Everything the map needs is expressed as GitHub labels, so state is queryable in the tracker UI:
156
+
157
+ - **Structural:** `trailhead:map`, `trailhead:ticket`
158
+ - **Type (one per ticket):** `trailhead:decision` · `research` · `prototype` · `build` · `bug` · `task`
159
+ - **State:** `trailhead:blocked` (has an open blocker) · `seed` (parked on a trigger) · `out-of-scope` (closed, beyond the destination) · `superseded` (closed, split into children)
160
+
161
+ The **frontier** is then a single query — open, unassigned, not `trailhead:blocked` — no body-parsing needed.
162
+
163
+ ## Working as a team
164
+
165
+ Many people (and their agent sessions) share one map and work it concurrently:
166
+
167
+ - **Claim = assign to yourself.** The claimer owns the ticket end to end and closes it — there is no approver and no one to wait on. On a claim collision the session stops and asks you, rather than resolving it silently.
168
+ - **Split** an oversized ticket into children and supersede the original (`/trailhead:split`).
169
+ - **Pause/resume** via a `PAUSED` checkpoint comment, so any session can pick a ticket back up.
170
+ - **State lives on the tickets** (claim, labels, comments — all conflict-free); the map body is a re-readable index, not the source of truth. The durable record of a resolution is the ticket's own comment.
171
+ - **Labels are protected from outsiders.** `trailhead:*` labels drive the map, so trailhead trusts a labelled issue only when a write-access collaborator applied the label and (for a ticket) it has a valid `Parent:` — anything else is quarantined as `trailhead:unverified`, off the frontier. GitHub already blocks non-write users from labelling; for defence against triage users and automations, trailhead can **install the label guard GitHub Action for you** (from `templates/trailhead-label-guard.yml`) into your repo's `.github/workflows/` — it strips unauthorized `trailhead:*` labels at the source. (Installing a workflow needs a token with the `workflow` scope; trailhead runs `gh auth refresh -s workflow` if the push is rejected.)
172
+ - **Issues opened by others are triaged, not trusted blindly.** `/trailhead:inbox` lists inbound issues (bug reports, requests, questions that aren't yet trailhead tickets) and integrates the worthwhile ones **in place** — reframing them as tickets, keeping the reporter's authorship, and applying the labels on adoption. The rest routes to fog, out-of-scope, duplicate, or needs-info.
173
+
174
+ ## Keeping tickets honest
175
+
176
+ A ticket is one answerable Question, sized to one session — so you never grow it in flight. When new scope surfaces while you're working (testing especially sparks ideas), you decide per idea: small and part of the same Question → just do it; balloons the ticket past one session → **split** it; separate work or a follow-up → **capture** it (`idea`/`todo`/`ticket`/`bug`) and keep going. Extras never become silent lines inside the ticket you're on.
177
+
178
+ Tickets that spin off from other work carry a lineage pointer in their body, so the map stays traceable:
179
+
180
+ | Pointer | Meaning |
181
+ |---|---|
182
+ | `Split from:` | a child of a ticket that was split |
183
+ | `Regression of:` | a bug in the work of a closed ticket |
184
+ | `Surfaced from:` | an idea/ticket that came up while working another ticket |
185
+
186
+ ---
187
+
188
+ ## Configuration
189
+
190
+ Two optional layers — **project overrides global**, key by key (built-in defaults otherwise):
191
+
192
+ - **Global** — `~/.claude/trailhead/config.json`: your standing defaults across every project.
193
+ - **Project** — a `## Config` block in the map issue: overrides for this map, right on the tracker.
194
+
195
+ `/trailhead:config` runs a **guided, menu-driven setup** — pick the scope, then walk each setting (🧠 models · 🎨 design · 🧪 TDD · 🌐 acceptance testing) as an icon-labelled menu; no hand-editing JSON. `config get` prints the effective merged config; `config set <key> <value>` writes one key.
196
+
197
+ | Key | Values (default **bold**) | Effect |
198
+ |---|---|---|
199
+ | `models.{plan,execute,research,review,debug}` | a model id (**inherit session**) | which model runs each activity |
200
+ | `design` | **`disk`** \| a claude.ai/design project URL | where UI mockups go |
201
+ | `tdd` | **`seams`** \| `on` \| `off` | how the `build` engine tests |
202
+ | `acceptance.browser` | **`auto`** \| `on` \| `off` | drive the browser in Verify, or hand a UAT checklist |
203
+ | `testing.webapp` / `testing.url` | bool / URL | is it browser-drivable, and where |
204
+ | `plan_review` | **`off`** \| `on` \| CLI list | send `build` PLANs to external AI CLIs (Gemini, Codex, …) for a second opinion and converge on their concerns |
205
+
206
+ **Models** apply to every subagent trailhead spawns (research, codebase-map, review); when `models.plan` differs from `models.execute`, the build's Plan step runs as a planner subagent on the plan model, then execution continues on the execute model. Example `~/.claude/trailhead/config.json`:
207
+
208
+ ```json
209
+ { "models": { "plan": "opus", "execute": "sonnet" }, "tdd": "seams", "acceptance": { "browser": "auto" } }
210
+ ```
211
+
212
+ ---
213
+
214
+ ## Requirements
215
+
216
+ Just an **authenticated `gh` CLI** — the tracker is GitHub Issues. Nothing else.
217
+
218
+ `trailhead` is **self-contained**: every technique it uses (grilling, TDD, systematic debugging, codebase mapping, code review) is built in as an inline protocol, and its subagents (research, codebase-map, review) use Claude Code's built-in `Agent` tool. No companion plugins, no preflight, no version drift.
219
+
220
+ ## Hooks
221
+
222
+ Beyond the skill's instructions, trailhead ships two of its own hooks (in `hooks/`, self-contained via `${CLAUDE_PLUGIN_ROOT}`) that *enforce* the parts of the discipline the model shouldn't be trusted to remember:
223
+
224
+ - **Commit guard** (`PreToolUse` on `git commit`) — hard-blocks a commit whose subject isn't [Conventional Commits](https://www.conventionalcommits.org), and hard-blocks any `Co-Authored-By` trailer. This mirrors GSD's commit validation.
225
+ - **Issue injection scanner** (`PostToolUse` on `gh` reads) — trailhead reads issue/PR/comment text written by anyone with repo access, i.e. untrusted input. When that text contains prompt-injection phrases, the hook injects an advisory reminding the agent to treat it as data, never as commands. Advisory only — it never blocks.
226
+
227
+ Both are crash-safe (any error → allow) and active whenever the plugin is installed. The commit guard therefore applies in **every** repo, not only trailhead projects — intentional, since conventional commits and no `Co-Authored-By` are trailhead's standing rules. To opt out, disable the plugin's hooks in your Claude Code settings.
228
+
229
+ ---
230
+
231
+ ## Install
232
+
233
+ **Prerequisites:** an authenticated [`gh` CLI](https://cli.github.com) (the tracker is GitHub Issues) and a GitHub repo to work in. The npm path also needs Node 18+.
234
+
235
+ ### As a Claude Code plugin (native, managed by `/plugin`)
236
+ ```
237
+ /plugin marketplace add ToRvaLDz/trailhead
238
+ /plugin install trailhead@trailhead
239
+ ```
240
+ Update with `/plugin update trailhead`; remove with `/plugin uninstall trailhead`.
241
+
242
+ ### Or via npm (installs into your agent's config dir)
243
+ ```
244
+ npx @marcomigozzi/trailhead # install into ~/.claude (or $CLAUDE_CONFIG_DIR)
245
+ npx @marcomigozzi/trailhead --symlink # dev install (symlink to the checkout, edits go live)
246
+ npx @marcomigozzi/trailhead --uninstall # remove everything it added
247
+ npx @marcomigozzi/trailhead --dir=<path> # target a specific config dir
248
+ ```
249
+ It copies the skill (+ its `references/`), the `/trailhead:*` commands, the two hooks (into `hooks/`, registered in `settings.json`), and the label-guard template — idempotently. Re-run `npx @marcomigozzi/trailhead` to update. The installer uses a per-agent adapter (Claude Code today) so it can grow to target other AI CLIs later.
250
+
251
+ ### After installing
252
+ Restart or reload your agent so the commands register, then run **`/trailhead`** to start (smart entry) — or `/trailhead:new "<idea>"` to chart a map. Note: once installed, the **commit guard hook runs on every `git commit`** (enforcing Conventional Commits and blocking `Co-Authored-By`); disable the plugin's hooks in settings if you don't want that. Either way trailhead is self-contained — no other skill or plugin is required.
253
+
254
+ ---
255
+
256
+ ## Acknowledgements
257
+
258
+ `trailhead` stands on the shoulders of two bodies of work. It is an original, self-contained methodology **inspired by** them — it does not copy or redistribute their code, and it invokes none of their skills; it reimplements the ideas as its own inline protocols:
259
+
260
+ - **[Matt Pocock](https://www.aihero.dev)** — for **Wayfinder**, whose map / frontier / fog-of-war model is the backbone of trailhead's planning half, and whose `grilling`, `domain-modeling`, `prototype`, `research`, and `tdd` skills shaped trailhead's inline techniques. See [`mattpocock/skills`](https://github.com/mattpocock/skills).
261
+ - **The [open-gsd](https://github.com/open-gsd/gsd-core) team** — for **GSD**, whose `discuss → plan → execute → verify` cycle, capture model, codebase mapping, and systematic debugging are the engine of trailhead's building half.
262
+
263
+ Thank you. If you like the ideas here, go star and use the originals — they go far deeper than trailhead's compact protocols.
264
+
265
+ ---
266
+
267
+ ## License
268
+
269
+ MIT © Marco Migozzi ([@ToRvaLDz](https://github.com/ToRvaLDz))
@@ -0,0 +1,108 @@
1
+ #!/usr/bin/env node
2
+ 'use strict';
3
+ // trailhead installer — places the skill, commands, hooks, and templates into
4
+ // an agent's config dir, and registers the hooks in settings.json.
5
+ // No dependencies. Idempotent. Usage:
6
+ // npx @marcomigozzi/trailhead install for Claude Code (~/.claude or $CLAUDE_CONFIG_DIR)
7
+ // npx @marcomigozzi/trailhead --symlink dev install (symlink to the package, live edits)
8
+ // npx @marcomigozzi/trailhead --uninstall
9
+ // npx @marcomigozzi/trailhead --dir=/path/to/configdir
10
+ //
11
+ // Multi-CLI: only the Claude adapter exists today. Add adapters below (config dir,
12
+ // commands/hooks layout) to target Codex, Gemini, etc. — see ADAPTERS.
13
+
14
+ const fs = require('fs');
15
+ const os = require('os');
16
+ const path = require('path');
17
+
18
+ const PKG = path.resolve(__dirname, '..');
19
+ const SRC = path.join(PKG, 'plugins', 'trailhead');
20
+ const args = process.argv.slice(2);
21
+ const has = (f) => args.includes(f);
22
+ const useSymlink = has('--symlink');
23
+ const uninstall = has('--uninstall');
24
+ const dirArg = (args.find((a) => a.startsWith('--dir=')) || '').split('=')[1];
25
+
26
+ // --- ADAPTERS: one config layout per target agent. Extend this map for multi-CLI. ---
27
+ const ADAPTERS = {
28
+ claude: {
29
+ label: 'Claude Code',
30
+ configDir: process.env.CLAUDE_CONFIG_DIR || path.join(os.homedir(), '.claude'),
31
+ },
32
+ // codex: { label: 'Codex', configDir: process.env.CODEX_HOME || path.join(os.homedir(), '.codex') },
33
+ // gemini: { label: 'Gemini', configDir: process.env.GEMINI_CONFIG_DIR || path.join(os.homedir(), '.gemini') },
34
+ };
35
+ const adapter = ADAPTERS.claude; // only Claude for now
36
+ const configDir = dirArg || adapter.configDir;
37
+
38
+ const P = {
39
+ skill: path.join(configDir, 'skills', 'trailhead'),
40
+ commands: path.join(configDir, 'commands', 'trailhead'),
41
+ hooksDir: path.join(configDir, 'hooks'),
42
+ templates: path.join(configDir, 'trailhead', 'templates'),
43
+ settings: path.join(configDir, 'settings.json'),
44
+ };
45
+ const HOOK_FILES = ['trailhead-commit-guard.js', 'trailhead-issue-injection-scanner.js'];
46
+ const hookCmd = (name) => `node "${path.join(P.hooksDir, name)}"`;
47
+
48
+ const rmrf = (p) => fs.rmSync(p, { recursive: true, force: true });
49
+ const ensure = (p) => fs.mkdirSync(p, { recursive: true });
50
+ function place(src, dest) {
51
+ rmrf(dest);
52
+ ensure(path.dirname(dest));
53
+ if (useSymlink) fs.symlinkSync(src, dest, 'dir');
54
+ else fs.cpSync(src, dest, { recursive: true });
55
+ }
56
+ const readJSON = (p) => { try { return JSON.parse(fs.readFileSync(p, 'utf8')); } catch { return {}; } };
57
+ const writeJSON = (p, o) => { ensure(path.dirname(p)); fs.writeFileSync(p, JSON.stringify(o, null, 2) + '\n'); };
58
+
59
+ function addHook(s, event, matcher, command) {
60
+ s.hooks = s.hooks || {};
61
+ s.hooks[event] = s.hooks[event] || [];
62
+ const present = s.hooks[event].some((g) => (g.hooks || []).some((h) => (h.command || '').includes(command)));
63
+ if (present) return false;
64
+ let group = s.hooks[event].find((g) => g.matcher === matcher);
65
+ if (!group) { group = { matcher, hooks: [] }; s.hooks[event].push(group); }
66
+ group.hooks.push({ type: 'command', command });
67
+ return true;
68
+ }
69
+ function stripHook(s, event, substr) {
70
+ if (!s.hooks || !s.hooks[event]) return;
71
+ for (const g of s.hooks[event]) g.hooks = (g.hooks || []).filter((h) => !(h.command || '').includes(substr));
72
+ s.hooks[event] = s.hooks[event].filter((g) => (g.hooks || []).length);
73
+ }
74
+
75
+ if (uninstall) {
76
+ [P.skill, P.commands, path.join(configDir, 'trailhead')].forEach(rmrf);
77
+ HOOK_FILES.forEach((f) => rmrf(path.join(P.hooksDir, f)));
78
+ const s = readJSON(P.settings);
79
+ stripHook(s, 'PreToolUse', 'trailhead-commit-guard.js');
80
+ stripHook(s, 'PostToolUse', 'trailhead-issue-injection-scanner.js');
81
+ writeJSON(P.settings, s);
82
+ console.log(`trailhead uninstalled from ${configDir}`);
83
+ process.exit(0);
84
+ }
85
+
86
+ // install (Claude adapter)
87
+ place(path.join(SRC, 'skills', 'trailhead'), P.skill);
88
+ place(path.join(SRC, 'commands'), P.commands);
89
+ place(path.join(SRC, 'templates'), P.templates);
90
+ ensure(P.hooksDir);
91
+ for (const f of HOOK_FILES) {
92
+ fs.copyFileSync(path.join(SRC, 'hooks', f), path.join(P.hooksDir, f));
93
+ fs.chmodSync(path.join(P.hooksDir, f), 0o755);
94
+ }
95
+ const s = readJSON(P.settings);
96
+ const added = [
97
+ addHook(s, 'PreToolUse', 'Bash', hookCmd('trailhead-commit-guard.js')),
98
+ addHook(s, 'PostToolUse', 'Bash', hookCmd('trailhead-issue-injection-scanner.js')),
99
+ ].some(Boolean);
100
+ writeJSON(P.settings, s);
101
+
102
+ console.log(`✓ trailhead installed for ${adapter.label} → ${configDir}`);
103
+ console.log(` skill → ${P.skill}${useSymlink ? ' (symlink)' : ''}`);
104
+ console.log(` commands → ${P.commands} (/trailhead:*)`);
105
+ console.log(` hooks → ${P.hooksDir}/ (${added ? 'registered' : 'already present'} in settings.json)`);
106
+ console.log(` templates → ${P.templates}`);
107
+ console.log('\nRestart or reload your agent to pick up the commands, then run /trailhead to start.');
108
+ console.log('Note: the commit guard now runs on every git commit (conventional + no Co-Authored-By).');
package/package.json ADDED
@@ -0,0 +1,36 @@
1
+ {
2
+ "name": "@marcomigozzi/trailhead",
3
+ "version": "0.1.0",
4
+ "description": "Start and drive large projects as a map of tickets on GitHub Issues. Wayfinder's map-based onboarding + a discuss→plan→execute→verify engine, all on the Issues. Installs the trailhead skill, commands, and hooks into your agent's config dir.",
5
+ "bin": {
6
+ "trailhead": "bin/trailhead.js"
7
+ },
8
+ "publishConfig": {
9
+ "access": "public"
10
+ },
11
+ "files": [
12
+ "bin/",
13
+ "plugins/",
14
+ "README.md",
15
+ "LICENSE"
16
+ ],
17
+ "engines": {
18
+ "node": ">=18"
19
+ },
20
+ "keywords": [
21
+ "trailhead",
22
+ "claude-code",
23
+ "wayfinder",
24
+ "gsd",
25
+ "github-issues",
26
+ "planning",
27
+ "agent-skill"
28
+ ],
29
+ "author": "Marco Migozzi (https://github.com/ToRvaLDz)",
30
+ "license": "MIT",
31
+ "repository": {
32
+ "type": "git",
33
+ "url": "git+https://github.com/ToRvaLDz/trailhead.git"
34
+ },
35
+ "homepage": "https://github.com/ToRvaLDz/trailhead"
36
+ }
@@ -0,0 +1,25 @@
1
+ {
2
+ "name": "trailhead",
3
+ "version": "0.1.0",
4
+ "description": "Start and drive large projects as a map of tickets on GitHub Issues, resolving one at a time. Map-based onboarding + a discuss→plan→execute→verify engine with atomic commits, all on the Issues. Self-contained: inspired by Wayfinder and GSD, but depends on nothing beyond an authenticated gh CLI.",
5
+ "author": {
6
+ "name": "Marco Migozzi",
7
+ "url": "https://github.com/ToRvaLDz"
8
+ },
9
+ "homepage": "https://github.com/ToRvaLDz/trailhead",
10
+ "repository": "https://github.com/ToRvaLDz/trailhead",
11
+ "license": "MIT",
12
+ "keywords": [
13
+ "wayfinder",
14
+ "gsd",
15
+ "github-issues",
16
+ "planning",
17
+ "orchestrator",
18
+ "tdd",
19
+ "capture"
20
+ ],
21
+ "skills": [
22
+ "./skills/trailhead"
23
+ ],
24
+ "hooks": "./hooks/hooks.json"
25
+ }
@@ -0,0 +1,6 @@
1
+ ---
2
+ description: Adopt an existing project — map the codebase once, then go lean
3
+ argument-hint: ""
4
+ ---
5
+
6
+ Invoke the `trailhead` skill for the **adopt** action: call the Skill tool with skill name `trailhead` and arguments `adopt $ARGUMENTS`, then carry out the skill's instructions for that verb. Do not re-implement the behaviour here — the skill is the single source of truth.
@@ -0,0 +1,6 @@
1
+ ---
2
+ description: Capture a bug ticket (use --of <ticket> for a regression)
3
+ argument-hint: "[--of <ticket>] <text>"
4
+ ---
5
+
6
+ Invoke the `trailhead` skill for the **bug** action: call the Skill tool with skill name `trailhead` and arguments `bug $ARGUMENTS`, then carry out the skill's instructions for that verb. Do not re-implement the behaviour here — the skill is the single source of truth.
@@ -0,0 +1,6 @@
1
+ ---
2
+ description: Guided menu setup, or get/set trailhead config
3
+ argument-hint: "[get|set …]"
4
+ ---
5
+
6
+ Invoke the `trailhead` skill for the **config** action: call the Skill tool with skill name `trailhead` and arguments `config $ARGUMENTS`, then carry out the skill's instructions for that verb. Do not re-implement the behaviour here — the skill is the single source of truth.
@@ -0,0 +1,6 @@
1
+ ---
2
+ description: Run a standalone grilling session on a decision or topic
3
+ argument-hint: "[topic|ticket]"
4
+ ---
5
+
6
+ Invoke the `trailhead` skill for the **grill** action: call the Skill tool with skill name `trailhead` and arguments `grill $ARGUMENTS`, then carry out the skill's instructions for that verb. Do not re-implement the behaviour here — the skill is the single source of truth.
@@ -0,0 +1,6 @@
1
+ ---
2
+ description: Capture an idea into the fog (Not yet specified)
3
+ argument-hint: "<text>"
4
+ ---
5
+
6
+ Invoke the `trailhead` skill for the **idea** action: call the Skill tool with skill name `trailhead` and arguments `idea $ARGUMENTS`, then carry out the skill's instructions for that verb. Do not re-implement the behaviour here — the skill is the single source of truth.
@@ -0,0 +1,6 @@
1
+ ---
2
+ description: Triage issues opened by others and integrate the worthwhile ones into the map
3
+ argument-hint: "[issue]"
4
+ ---
5
+
6
+ Invoke the `trailhead` skill for the **inbox** action: call the Skill tool with skill name `trailhead` and arguments `inbox $ARGUMENTS`, then carry out the skill's instructions for that verb. Do not re-implement the behaviour here — the skill is the single source of truth.
@@ -0,0 +1,6 @@
1
+ ---
2
+ description: Show the low-res map (destination, decisions, frontier, fog)
3
+ argument-hint: ""
4
+ ---
5
+
6
+ Invoke the `trailhead` skill for the **map** action: call the Skill tool with skill name `trailhead` and arguments `map $ARGUMENTS`, then carry out the skill's instructions for that verb. Do not re-implement the behaviour here — the skill is the single source of truth.
@@ -0,0 +1,6 @@
1
+ ---
2
+ description: Chart a new trailhead map from a loose idea
3
+ argument-hint: "[idea]"
4
+ ---
5
+
6
+ Invoke the `trailhead` skill for the **new** action: call the Skill tool with skill name `trailhead` and arguments `new $ARGUMENTS`, then carry out the skill's instructions for that verb. Do not re-implement the behaviour here — the skill is the single source of truth.
@@ -0,0 +1,6 @@
1
+ ---
2
+ description: Capture a verbatim note
3
+ argument-hint: "<text>"
4
+ ---
5
+
6
+ Invoke the `trailhead` skill for the **note** action: call the Skill tool with skill name `trailhead` and arguments `note $ARGUMENTS`, then carry out the skill's instructions for that verb. Do not re-implement the behaviour here — the skill is the single source of truth.
@@ -0,0 +1,6 @@
1
+ ---
2
+ description: Checkpoint the ticket in play so it can be resumed later
3
+ argument-hint: "[note]"
4
+ ---
5
+
6
+ Invoke the `trailhead` skill for the **pause** action: call the Skill tool with skill name `trailhead` and arguments `pause $ARGUMENTS`, then carry out the skill's instructions for that verb. Do not re-implement the behaviour here — the skill is the single source of truth.
@@ -0,0 +1,6 @@
1
+ ---
2
+ description: Resume a paused ticket from its checkpoint
3
+ argument-hint: "[ticket]"
4
+ ---
5
+
6
+ Invoke the `trailhead` skill for the **resume** action: call the Skill tool with skill name `trailhead` and arguments `resume $ARGUMENTS`, then carry out the skill's instructions for that verb. Do not re-implement the behaviour here — the skill is the single source of truth.
@@ -0,0 +1,6 @@
1
+ ---
2
+ description: Capture a seed — work gated on a future trigger
3
+ argument-hint: "<text>"
4
+ ---
5
+
6
+ Invoke the `trailhead` skill for the **seed** action: call the Skill tool with skill name `trailhead` and arguments `seed $ARGUMENTS`, then carry out the skill's instructions for that verb. Do not re-implement the behaviour here — the skill is the single source of truth.
@@ -0,0 +1,6 @@
1
+ ---
2
+ description: Split an oversized ticket into children and supersede it
3
+ argument-hint: "[ticket]"
4
+ ---
5
+
6
+ Invoke the `trailhead` skill for the **split** action: call the Skill tool with skill name `trailhead` and arguments `split $ARGUMENTS`, then carry out the skill's instructions for that verb. Do not re-implement the behaviour here — the skill is the single source of truth.
@@ -0,0 +1,6 @@
1
+ ---
2
+ description: Open a ticket of any type on the fly (diverges briefly first)
3
+ argument-hint: "<type> <title>"
4
+ ---
5
+
6
+ Invoke the `trailhead` skill for the **ticket** action: call the Skill tool with skill name `trailhead` and arguments `ticket $ARGUMENTS`, then carry out the skill's instructions for that verb. Do not re-implement the behaviour here — the skill is the single source of truth.
@@ -0,0 +1,6 @@
1
+ ---
2
+ description: Capture a small build ticket — defined work you will do now
3
+ argument-hint: "<text>"
4
+ ---
5
+
6
+ Invoke the `trailhead` skill for the **todo** action: call the Skill tool with skill name `trailhead` and arguments `todo $ARGUMENTS`, then carry out the skill's instructions for that verb. Do not re-implement the behaviour here — the skill is the single source of truth.
@@ -0,0 +1,6 @@
1
+ ---
2
+ description: Work the next frontier ticket, or the one you name
3
+ argument-hint: "[ticket]"
4
+ ---
5
+
6
+ Invoke the `trailhead` skill for the **work** action: call the Skill tool with skill name `trailhead` and arguments `work $ARGUMENTS`, then carry out the skill's instructions for that verb. Do not re-implement the behaviour here — the skill is the single source of truth.
@@ -0,0 +1,26 @@
1
+ {
2
+ "hooks": {
3
+ "PreToolUse": [
4
+ {
5
+ "matcher": "Bash",
6
+ "hooks": [
7
+ {
8
+ "type": "command",
9
+ "command": "node \"${CLAUDE_PLUGIN_ROOT}/hooks/trailhead-commit-guard.js\""
10
+ }
11
+ ]
12
+ }
13
+ ],
14
+ "PostToolUse": [
15
+ {
16
+ "matcher": "Bash",
17
+ "hooks": [
18
+ {
19
+ "type": "command",
20
+ "command": "node \"${CLAUDE_PLUGIN_ROOT}/hooks/trailhead-issue-injection-scanner.js\""
21
+ }
22
+ ]
23
+ }
24
+ ]
25
+ }
26
+ }