@marcomigozzi/trailhead 0.1.0 → 0.3.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.
- package/README.md +175 -98
- package/bin/trailhead.js +14 -3
- package/package.json +2 -4
- package/plugins/trailhead/.claude-plugin/plugin.json +2 -4
- package/plugins/trailhead/commands/adopt.md +2 -2
- package/plugins/trailhead/commands/bug.md +1 -1
- package/plugins/trailhead/commands/config.md +1 -1
- package/plugins/trailhead/commands/dashboard.md +6 -0
- package/plugins/trailhead/commands/grill.md +1 -1
- package/plugins/trailhead/commands/idea.md +1 -1
- package/plugins/trailhead/commands/inbox.md +1 -1
- package/plugins/trailhead/commands/map.md +1 -1
- package/plugins/trailhead/commands/new.md +1 -1
- package/plugins/trailhead/commands/note.md +1 -1
- package/plugins/trailhead/commands/pause.md +1 -1
- package/plugins/trailhead/commands/quick.md +6 -0
- package/plugins/trailhead/commands/resume.md +1 -1
- package/plugins/trailhead/commands/seed.md +2 -2
- package/plugins/trailhead/commands/split.md +1 -1
- package/plugins/trailhead/commands/ticket.md +1 -1
- package/plugins/trailhead/commands/todo.md +2 -2
- package/plugins/trailhead/commands/update.md +5 -0
- package/plugins/trailhead/commands/whiteboard.md +6 -0
- package/plugins/trailhead/commands/work.md +1 -1
- package/plugins/trailhead/hooks/hooks.json +14 -0
- package/plugins/trailhead/hooks/trailhead-check-update.js +125 -0
- package/plugins/trailhead/hooks/trailhead-commit-guard.js +3 -3
- package/plugins/trailhead/hooks/trailhead-issue-injection-scanner.js +5 -5
- package/plugins/trailhead/hooks/trailhead-secret-guard.js +131 -0
- package/plugins/trailhead/hooks/trailhead-secret-guard.test.js +71 -0
- package/plugins/trailhead/skills/trailhead/SKILL.md +243 -203
- package/plugins/trailhead/skills/trailhead/references/capture.md +24 -0
- package/plugins/trailhead/skills/trailhead/references/configuration.md +44 -16
- package/plugins/trailhead/skills/trailhead/references/inbox.md +29 -0
- package/plugins/trailhead/skills/trailhead/references/teamwork.md +87 -0
- package/plugins/trailhead/skills/trailhead/references/techniques/acceptance-testing.md +6 -2
- package/plugins/trailhead/skills/trailhead/references/techniques/code-review.md +10 -6
- package/plugins/trailhead/skills/trailhead/references/techniques/codebase-map.md +9 -7
- package/plugins/trailhead/skills/trailhead/references/techniques/debug.md +13 -9
- package/plugins/trailhead/skills/trailhead/references/techniques/domain-vocabulary.md +6 -5
- package/plugins/trailhead/skills/trailhead/references/techniques/grilling.md +8 -8
- package/plugins/trailhead/skills/trailhead/references/techniques/plan-review.md +14 -5
- package/plugins/trailhead/skills/trailhead/references/techniques/prototype.md +10 -8
- package/plugins/trailhead/skills/trailhead/references/techniques/research.md +7 -7
- package/plugins/trailhead/skills/trailhead/references/techniques/tdd.md +6 -6
- package/plugins/trailhead/skills/trailhead/references/updating.md +42 -0
- package/plugins/trailhead/templates/statusline-trailhead.sh +344 -0
- package/plugins/trailhead/templates/trailhead-label-guard.yml +2 -2
package/README.md
CHANGED
|
@@ -1,55 +1,104 @@
|
|
|
1
1
|
# trailhead
|
|
2
2
|
|
|
3
|
-
**Start and drive large projects as a map of tickets on GitHub Issues
|
|
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
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
|
|
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
6
|
|
|
7
7
|
---
|
|
8
8
|
|
|
9
|
-
## Why trailhead exists
|
|
9
|
+
## 💡 Why trailhead exists
|
|
10
10
|
|
|
11
11
|
Two approaches to agent-driven project work each nail one half of the problem:
|
|
12
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
|
|
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
14
|
|
|
15
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
16
|
|
|
17
|
-
Working across both, one pattern kept recurring: **use Wayfinder
|
|
17
|
+
Working across both, one pattern kept recurring: **use a Wayfinder-style map and onboarding to decide the shape of the work, then a GSD-style engine to build each piece, without ever leaving the issue tracker.** `trailhead` is that pattern made into a single skill, its own self-contained take on both ideas rather than a wrapper around either.
|
|
18
18
|
|
|
19
|
-
> **trailhead is self-contained.** Wayfinder and GSD are *inspiration*, not dependencies. Every technique those systems package as a separate skill
|
|
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. There is one opt-in exception: when enabled, `claude.ai/design` mockup mode pushes to a design-system project via DesignSync (the `claude_design` MCP plus the `/design-sync` skill). If that isn't available, it silently falls back to local-disk mockups.
|
|
20
20
|
|
|
21
21
|
The design choices that fall out of it:
|
|
22
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
|
|
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
|
|
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
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
26
|
|
|
27
|
+
- **Built for teams, because the plan is shared.** The tracker is the single source of truth, so the map is a **shared workspace**, not one person's local file. Many people (and their agents) work it at once, and the mechanics keep them from colliding:
|
|
28
|
+
- **Claiming.** You take a ticket by assigning it to yourself; the assignee *is* the lock. A collision stops you and asks, rather than clobbering someone else's work. Whoever claims a ticket owns it end to end and closes it, no separate approver.
|
|
29
|
+
- **A shared frontier, one ticket per session.** The **frontier** (open, unassigned, unblocked tickets) is the common queue of what's takeable now; each person picks a different one. Resolving a ticket ends the session, so work stays in reviewable, single-ticket chunks.
|
|
30
|
+
- **Isolation for concurrent sessions on one machine.** Two agents editing one checkout corrupt each other, so the `isolation:` convention gives each in-flight ticket its own **`git worktree`** (or a full **`clone`** for path-bound apps a worktree can't build); disjoint `Scope:` lines let non-overlapping work run in parallel and serialise only where it truly overlaps.
|
|
31
|
+
- **Pause, resume, split.** Any ticket can be **paused** with a checkpoint comment and **resumed** later by anyone (once released), or **split** into children when it outgrows a session, with blockers re-pointed so the frontier stays honest.
|
|
32
|
+
- **Several maps at once.** A repo can carry **more than one live map** (parallel milestones or features), each with its own scoped frontier, so two people can drive two efforts on the same repo without stepping on each other, and you can park one map to work another.
|
|
33
|
+
- **Outsiders and trust.** Issues opened by other people flow through an **inbox** that reframes the worthwhile ones into tickets *in place* (keeping the reporter's authorship), while a **trust and provenance** guard quarantines `trailhead:*`-labelled issues from untrusted sources until a maintainer adopts them, backed by a repo-side label-guard workflow.
|
|
34
|
+
|
|
35
|
+
The full protocol lives in the **Working as a team** section below.
|
|
36
|
+
|
|
27
37
|
---
|
|
28
38
|
|
|
29
|
-
##
|
|
39
|
+
## 📦 Install
|
|
40
|
+
|
|
41
|
+
> **Claude Code only, for now.** trailhead installs as a Claude Code skill + `/trailhead:*` commands + hooks. It's built to grow to other AI CLIs later (the installer already uses a per-agent adapter), but today Claude Code is the only supported host.
|
|
42
|
+
|
|
43
|
+
**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+.
|
|
44
|
+
|
|
45
|
+
### As a Claude Code plugin (native, managed by `/plugin`)
|
|
46
|
+
```
|
|
47
|
+
/plugin marketplace add ToRvaLDz/trailhead
|
|
48
|
+
/plugin install trailhead@trailhead
|
|
49
|
+
```
|
|
50
|
+
Update with `/plugin update trailhead`; remove with `/plugin uninstall trailhead`.
|
|
51
|
+
|
|
52
|
+
### Or via npm (installs into your agent's config dir)
|
|
53
|
+
```
|
|
54
|
+
npx @marcomigozzi/trailhead # install into ~/.claude (or $CLAUDE_CONFIG_DIR)
|
|
55
|
+
npx @marcomigozzi/trailhead --symlink # dev install (symlink to the checkout, edits go live)
|
|
56
|
+
npx @marcomigozzi/trailhead --uninstall # remove everything it added
|
|
57
|
+
npx @marcomigozzi/trailhead --dir=<path> # target a specific config dir
|
|
58
|
+
```
|
|
59
|
+
It copies the skill (+ its `references/`), the `/trailhead:*` commands, the hooks (into `hooks/`, registered in `settings.json`) including a SessionStart **update check**, and the label-guard + statusline templates, idempotently. Re-run `npx @marcomigozzi/trailhead` to update, or run **`/trailhead:update`** from inside the agent: it detects how trailhead was installed and installs the newer version where that is safe (a `git pull` for a dev-symlink, an `npx` re-run for npm, or `/plugin update` for the plugin). When a newer version exists, the statusline shows a `⬆ trailhead <version>` flag.
|
|
60
|
+
|
|
61
|
+
### After installing
|
|
62
|
+
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, no preflight, no version drift.
|
|
63
|
+
|
|
64
|
+
---
|
|
65
|
+
|
|
66
|
+
## 🗺️ How it works
|
|
30
67
|
|
|
31
68
|
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
69
|
|
|
33
|
-
|
|
70
|
+
**What earns a map?** A map is the unit for an effort that is **too big to hold in one session** and carries **open decisions**, not just execution. In practice that's a **milestone** (a release, a version, "v2 of the dashboard"), or a **single feature big enough that you must decide before you can build it**: where the data lives, which library, what the screen actually does. Charting a map names its destination and fans that effort out into many tickets, wired by their dependencies. As the decisions resolve, the fog clears and the build tickets graduate, until the destination is reached.
|
|
71
|
+
|
|
72
|
+
What does **not** need its own map is a single, defined piece of work with no open questions, small enough to finish in one session. That is just **one ticket**, a direct `build` (or a `todo`): a *work* you take with `/trailhead:work` and resolve in a sitting, no charting required. **Rule of thumb: decisions to make, or too big for one sitting → a map; "just do X" → a single work.** A feature that starts as a lone work but turns out to need decisions gets promoted to its own map (or split); a map whose questions all evaporate collapses back to a handful of works. And a repo can hold **several maps at once** (parallel milestones or features), each with its own frontier, so you can park one and work another.
|
|
73
|
+
|
|
74
|
+
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
75
|
|
|
35
76
|
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
77
|
|
|
78
|
+
A map is scoped to **one** effort, so knowledge that belongs to the *repo* (not to any single map) lives in **repo-scoped anchor issues**, created once and shared by every map (each map's Notes just links them, so nothing is stranded when a map finishes):
|
|
79
|
+
|
|
80
|
+
- **`trailhead:codebase`**: the distilled codebase map (architecture, stack, conventions, risks, test/build), written once at adopt and refreshed only on major drift.
|
|
81
|
+
- **`trailhead:conventions`**: the project's **way of working**, readable by everyone: a small machine-read header the engine obeys (`git: main|pr`, `release: command|auto`, `isolation: none|worktree|clone`) over human prose. `/trailhead:adopt` and `:new` ask for it up front. `isolation: worktree` gives each executing ticket its own `git worktree` + branch (concurrent sessions on one clone never share a working tree); `isolation: clone` gives it a dedicated clone instead, for path-bound apps a worktree can't build.
|
|
82
|
+
- **`trailhead:dashboard`**: the pinned index of the whole surface, a link to every open map (with GitHub's native progress bars), the whiteboard, and live counts (inbox, whiteboard frontier). Refreshed when a map is charted or exhausted, and on demand via `/trailhead:dashboard`.
|
|
83
|
+
|
|
84
|
+
These three fill GitHub's **3 pinned-issue slots**, so a repo's trailhead anchors stay one click away; **maps themselves are never pinned** (they're indexed by the dashboard instead, since the "active" map is per-checkout local state, not a repo-global fact). Project *config* (models, TDD, design…) is separate again, a plain `.trailhead/config.json` file at the repo root, never in an issue.
|
|
85
|
+
|
|
37
86
|
---
|
|
38
87
|
|
|
39
|
-
## Workflow
|
|
88
|
+
## 🔄 Workflow
|
|
40
89
|
|
|
41
90
|
**The lifecycle.** Every project walks the same loop:
|
|
42
91
|
|
|
43
|
-
1. **Start
|
|
44
|
-
2. **Work the frontier, one ticket per session
|
|
45
|
-
3. **Close & unblock
|
|
46
|
-
4. **Repeat** until the frontier is empty and the fog has cleared
|
|
92
|
+
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.
|
|
93
|
+
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.
|
|
94
|
+
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.
|
|
95
|
+
4. **Repeat** until the frontier is empty and the fog has cleared: the destination (a working artifact) is reached.
|
|
47
96
|
|
|
48
97
|
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
98
|
|
|
50
|
-
### A worked example
|
|
99
|
+
### A worked example: "add social login"
|
|
51
100
|
|
|
52
|
-
Destination: *users sign in with Google and GitHub, alongside email/password.* Charting fans it into six tickets
|
|
101
|
+
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
102
|
|
|
54
103
|
```
|
|
55
104
|
① research "OAuth providers + library: Supabase Auth native or Auth.js?" ← frontier (AFK)
|
|
@@ -60,7 +109,7 @@ Destination: *users sign in with Google and GitHub, alongside email/password.* C
|
|
|
60
109
|
⑥ bug (appears after ⑤ ships)
|
|
61
110
|
```
|
|
62
111
|
|
|
63
|
-
Initial frontier =
|
|
112
|
+
Initial frontier = ①③④, three people can start in parallel. Then:
|
|
64
113
|
|
|
65
114
|
- **① research** → a subagent reads the docs, returns a decision-ready finding, closes → unblocks ②.
|
|
66
115
|
- **② decision** → diverge the options, grill to choose native vs custom, record the why.
|
|
@@ -78,102 +127,131 @@ Initial frontier = ①③④ — three people can start in parallel. Then:
|
|
|
78
127
|
|
|
79
128
|
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
129
|
|
|
81
|
-
### …then a suggestion arrives
|
|
130
|
+
### …then a suggestion arrives: inbox → fog → graduation
|
|
82
131
|
|
|
83
|
-
The app is live, and a user *not on the team* files a plain issue: *"can we add Apple sign-in too?"*
|
|
132
|
+
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
133
|
|
|
85
|
-
1. **Inbox
|
|
86
|
-
2. **Fog
|
|
87
|
-
3. **Graduation
|
|
134
|
+
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.
|
|
135
|
+
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.
|
|
136
|
+
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
137
|
|
|
89
138
|
```
|
|
90
139
|
suggestion issue ──inbox──► 🌫️ fog (kept open, discussed) ──sharpens──► 🎫 ticket on the frontier
|
|
91
140
|
(same #, reporter still credited)
|
|
92
141
|
```
|
|
93
142
|
|
|
94
|
-
No separate backlog, no lost credit: the map absorbs external suggestions the same way it grows its own
|
|
143
|
+
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
144
|
|
|
96
145
|
---
|
|
97
146
|
|
|
98
|
-
## Commands
|
|
147
|
+
## ⌨️ Commands
|
|
99
148
|
|
|
100
149
|
`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
150
|
|
|
102
|
-
Every verb is also a namespaced command
|
|
151
|
+
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
152
|
|
|
104
153
|
### Flow
|
|
105
154
|
|
|
106
155
|
| Command | What it does |
|
|
107
156
|
|---|---|
|
|
108
|
-
| `/trailhead` | smart entry
|
|
157
|
+
| `/trailhead` | smart entry: detect state and propose |
|
|
109
158
|
| `/trailhead:new [idea]` | chart a new map from a loose idea |
|
|
110
159
|
| `/trailhead:adopt` | adopt an existing project (map the codebase once, then go lean) |
|
|
111
160
|
| `/trailhead:work [ticket]` | work the next frontier ticket, or the one you name |
|
|
161
|
+
| `/trailhead:quick [ticket \| "text"]` | work one ticket whole, off the map: opens a whiteboard ticket from `"text"` (or takes `<n>`), runs the full engine, grills only if needed, never splits |
|
|
162
|
+
| `/trailhead:whiteboard` | show the whiteboard: the loose (map-less) tickets and their frontier |
|
|
112
163
|
| `/trailhead:inbox [issue]` | triage issues opened by others and integrate the good ones into the map |
|
|
113
164
|
| `/trailhead:resume [ticket]` | resume a paused ticket from its `PAUSED` checkpoint |
|
|
114
165
|
| `/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
|
|
166
|
+
| `/trailhead:ticket <type> <title>` | open a ticket on the fly (diverges briefly first: a micro-charting act) |
|
|
116
167
|
| `/trailhead:split [ticket]` | split an oversized ticket into children, supersede the original |
|
|
117
168
|
| `/trailhead:grill [topic]` | run a standalone grilling session on a decision/topic |
|
|
118
169
|
| `/trailhead:map` | show the low-res map (destination, decisions, frontier, fog) |
|
|
170
|
+
| `/trailhead:dashboard` | show the repo dashboard: the pinned index of every open map, the whiteboard, and live counts |
|
|
119
171
|
|
|
120
|
-
### Capture
|
|
172
|
+
### Capture: zero friction, one confirmation line, resolves nothing
|
|
121
173
|
|
|
122
174
|
| Command | Lands as | Meaning |
|
|
123
175
|
|---|---|---|
|
|
124
|
-
| `/trailhead:todo <text>` | a frontier ticket | *I'm doing it
|
|
176
|
+
| `/trailhead:todo <text>` | a frontier ticket | *I'm doing it*: defined work, now |
|
|
125
177
|
| `/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
|
|
127
|
-
| `/trailhead:note <text>` | verbatim text | *remember this
|
|
178
|
+
| `/trailhead:idea <text>` | the fog (`Not yet specified`) | *maybe I'll do it*: graduates to a ticket if it sharpens |
|
|
179
|
+
| `/trailhead:note <text>` | verbatim text | *remember this*: not necessarily work |
|
|
128
180
|
| `/trailhead:bug [--of <ticket>] <text>` | a `bug` ticket | a defect; `--of` records it as a `Regression of:` a closed ticket |
|
|
129
181
|
|
|
130
|
-
The four fog/ticket captures form a spectrum of commitment and timing: **note < idea < seed < todo**.
|
|
182
|
+
The four fog/ticket captures form a spectrum of commitment and timing: **note < idea < seed < todo**.
|
|
183
|
+
|
|
184
|
+
When a map is open, a capture that produces a ticket (`todo`/`bug`/`seed`/a sharp `idea`) asks whether to file it on the **active map** or the **whiteboard**, the home for loose, map-less work that doesn't belong to any map (or isn't worth charting one). With no map open it lands on the whiteboard. Work a whiteboard ticket with **`/trailhead:quick`** (which also opens one from `"text"` and works it in the same sitting), and see them all with `/trailhead:whiteboard`.
|
|
185
|
+
|
|
186
|
+
### 🧯 Don't get trapped in a map: the whiteboard
|
|
187
|
+
|
|
188
|
+
Deep in a map, something unrelated surfaces: a bug in another area, a chore, a quick idea you want to act on now. Forcing it onto the map's frontier pollutes the map; charting a whole new map for it is overkill. That is what the **whiteboard** is for, loose map-less work, and two moves keep you from getting stuck:
|
|
189
|
+
|
|
190
|
+
- **Capture it aside.** A `todo`/`bug`/`seed`/sharp `idea` fired while a map is open asks *map or whiteboard?*. Send it to the whiteboard and it stays off the map: tracked, but out of the way, so the map's frontier keeps meaning "the way to this destination".
|
|
191
|
+
- **Do it on the fly.** `/trailhead:quick "<text>"` opens a whiteboard ticket and works it end to end in the same sitting, the full discuss → plan → execute → verify engine (atomic commits, code review, the lot), except it **grills only if needed and never splits**, and skips every map book-keeping step. `/trailhead:quick <n>` does the same for a ticket that already exists.
|
|
192
|
+
|
|
193
|
+
See the whole whiteboard with `/trailhead:whiteboard`. Nothing about the map changes: you just stepped off it, did the thing, and step back on when you're ready.
|
|
194
|
+
|
|
195
|
+
The three that trip people up are **idea, seed, todo**, so here they are spelled out:
|
|
196
|
+
|
|
197
|
+
- **`idea`** = *maybe, and not yet clear.* It lands in the **fog** (`Not yet specified`), **not** as a ticket, because you can't even phrase the question sharply yet, so there is nothing to work. It **graduates** into a ticket later, when the frontier reaches it or it simply gets clearer.
|
|
198
|
+
- **`seed`** = *yes, but not yet.* It **is** a ticket, but **parked** (`trailhead:blocked`) on a **trigger** you name ("when the public API ships", "when we pass 1k users"). The question is already sharp; what's missing is a **condition**, not clarity. When the trigger fires, it graduates onto the frontier.
|
|
199
|
+
- **`todo`** = *yes, now.* Defined work you will just do: it's born a **`build` ticket on the frontier**, takeable immediately.
|
|
200
|
+
|
|
201
|
+
The two cuts that matter: **idea vs seed** is waiting on *clarity* vs waiting on a *condition* (both land "later", for different reasons); **seed vs todo** is committed *later* vs committed *now*. Below all three sits **`note`** (raw text to remember, maybe never work), and off to the side is **`bug`** (a defect, not a commitment tier). The test for idea vs ticket is always: **can you phrase the question precisely now?** Yes → a ticket (`todo` if takeable, `seed` if gated); no → the fog (`idea`).
|
|
131
202
|
|
|
132
203
|
---
|
|
133
204
|
|
|
134
|
-
## Ticket types and their engines
|
|
205
|
+
## 🎫 Ticket types and their engines
|
|
135
206
|
|
|
136
207
|
Each ticket carries a type label; each type has its own way of being resolved.
|
|
137
208
|
|
|
138
|
-
Each type has its own inline engine
|
|
209
|
+
Each type has its own inline engine: no external skill is invoked.
|
|
139
210
|
|
|
140
211
|
| Type | Produces | Mode | Engine |
|
|
141
212
|
|---|---|---|---|
|
|
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
|
|
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) |
|
|
213
|
+
| 🧭 `decision` | a choice | HITL | diverge the options if unclear, then grill to converge on one |
|
|
214
|
+
| 🔬 `research` | a fact | AFK | a subagent on a throwaway branch (the only type run in parallel) |
|
|
215
|
+
| 🎨 `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 |
|
|
216
|
+
| 🔨 `build` | working code | HITL/AFK | `discuss → plan → execute → verify`: atomic commits, TDD at seams, **a mockup first for user-facing UI** (Prototype technique, gated by `design.approval`), code review + acceptance testing (browser-drive or conversational step-by-step UAT) |
|
|
217
|
+
| 🐛 `bug` | corrected code | HITL/AFK | `repro → diagnose → fix → verify`; a defect in closed work is a *new* ticket (`Regression of:`), not a reopen |
|
|
218
|
+
| 🔧 `task` | an external state change | HITL/AFK | manual work that unblocks a decision (provision access, move data, sign up) |
|
|
148
219
|
|
|
149
|
-
Two rules of thumb: build tickets **never auto-grill
|
|
220
|
+
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
221
|
|
|
151
222
|
---
|
|
152
223
|
|
|
153
|
-
## Labels
|
|
224
|
+
## 🏷️ Labels
|
|
154
225
|
|
|
155
226
|
Everything the map needs is expressed as GitHub labels, so state is queryable in the tracker UI:
|
|
156
227
|
|
|
157
228
|
- **Structural:** `trailhead:map`, `trailhead:ticket`
|
|
158
|
-
- **
|
|
229
|
+
- **Repo-scoped anchors (one each per repo, pinned):** `trailhead:codebase` (the distilled codebase map), `trailhead:conventions` (the way of working), `trailhead:dashboard` (the pinned index of maps + whiteboard + counts)
|
|
230
|
+
- **Type (one per ticket):** 🧭 `trailhead:decision` · 🔬 `research` · 🎨 `prototype` · 🔨 `build` · 🐛 `bug` · 🔧 `task`
|
|
159
231
|
- **State:** `trailhead:blocked` (has an open blocker) · `seed` (parked on a trigger) · `out-of-scope` (closed, beyond the destination) · `superseded` (closed, split into children)
|
|
232
|
+
- **Container:** `trailhead:whiteboard` (a loose, map-less ticket, off every map's frontier, on the whiteboard's own)
|
|
160
233
|
|
|
161
|
-
The **frontier** is then a single query
|
|
234
|
+
The **frontier** is then a single query (open, unassigned, not `trailhead:blocked`), no body-parsing needed.
|
|
162
235
|
|
|
163
|
-
## Working as a team
|
|
236
|
+
## 👥 Working as a team
|
|
164
237
|
|
|
165
238
|
Many people (and their agent sessions) share one map and work it concurrently:
|
|
166
239
|
|
|
167
|
-
- **Claim = assign to yourself.** The claimer owns the ticket end to end and closes it
|
|
240
|
+
- **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
241
|
- **Split** an oversized ticket into children and supersede the original (`/trailhead:split`).
|
|
169
242
|
- **Pause/resume** via a `PAUSED` checkpoint comment, so any session can pick a ticket back up.
|
|
170
|
-
- **
|
|
171
|
-
- **
|
|
172
|
-
- **
|
|
243
|
+
- **Isolate concurrent sessions with worktrees.** Claiming keeps two sessions off the *same* ticket, but two sessions on *different* tickets in the *same* clone still share one working tree. Set `isolation: worktree` in the conventions header and each executing ticket runs in its own `git worktree` on a `trailhead/t<n>` branch, integrated back to the trunk at Resolve. It implies a branch per ticket even under `git: main` (git won't check the trunk out twice), and it is the right posture for a **monorepo** (a worktree shares the object store, far cheaper than a second clone). One caveat: a worktree isolates *source* but is a fresh path with no installed deps, so a **path-bound package** (React Native/Expo, native toolchains, anything tied to a local `node_modules`) won't build from it. For those there's **`isolation: clone`**: a dedicated per-ticket clone that installs its own deps and builds/UATs like the real app (heavier than a worktree, and the right tool for an Expo/RN submodule). Or keep `isolation: none` and serialize. Otherwise run concurrent sessions in separate clones.
|
|
244
|
+
- **A session-ticket marker for your statusline.** When it starts working a ticket, trailhead drops a one-line, gitignored `.trailhead/session-ticket` (`#<n> <title>`) at the working root and clears it at handoff. It's purely a cheap, offline hint so a statusline (or any tool) can show *which ticket this session is on* without hitting the tracker; trailhead itself still treats the Issues as the source of truth.
|
|
245
|
+
- **It nudges you when isolation was never chosen.** If a repo was charted or adopted before you set an `isolation:` mode (so it silently defaults to `none`), the first time you go to work a ticket with a real collision risk (another ticket in progress, or path-bound tooling in its `Scope:`) trailhead surfaces a one-line suggestion of the fitting mode (`worktree` or `clone`) instead of quietly editing the shared checkout. Whatever you decide, including keeping `none`, it writes the `isolation:` key into the conventions so the choice is recorded and it never asks again. A header that already set `isolation:` is left alone.
|
|
246
|
+
- **Scope tickets by path on a monorepo.** A ticket can carry a `Scope:` line naming the package/dir(s) it touches: disjoint scopes parallelize safely, and it scopes build/test and the worktree's commits to the affected package. `/trailhead:adopt` detects a monorepo and proposes both `isolation: worktree` and the `Scope:` convention. Under `isolation: none`, `Scope:` also **serializes** a shared area: at claim time a session holds off if another in-progress ticket's scope overlaps, so two tickets scoped to the same submodule/package can't run at once (how you keep two sessions off one path-bound app).
|
|
247
|
+
- **Git submodules are handled as separate repos.** Work that lands inside a submodule is committed *inside the submodule* plus a gitlink bump in the parent, and (under `worktree`) isolated at the submodule, not the superproject. A ticket spanning two submodules stays atomic via a single parent commit that bumps both gitlinks together; its `Scope:` names both. `/trailhead:adopt` detects `.gitmodules` and writes the rule into the conventions.
|
|
248
|
+
- **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.
|
|
249
|
+
- **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 carries a valid `Parent:`. Anything else is quarantined as `trailhead:unverified`, off the frontier. GitHub already blocks non-write users from labelling; 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/`, where it strips unauthorized `trailhead:*` labels at the source. Installing a workflow needs a token with the `workflow` scope, so trailhead runs `gh auth refresh -s workflow` if the push is rejected.
|
|
250
|
+
- **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
251
|
|
|
174
|
-
## Keeping tickets honest
|
|
252
|
+
## ✅ Keeping tickets honest
|
|
175
253
|
|
|
176
|
-
A ticket is one answerable Question, sized to one session
|
|
254
|
+
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
255
|
|
|
178
256
|
Tickets that spin off from other work carry a lineage pointer in their body, so the map stays traceable:
|
|
179
257
|
|
|
@@ -185,85 +263,84 @@ Tickets that spin off from other work carry a lineage pointer in their body, so
|
|
|
185
263
|
|
|
186
264
|
---
|
|
187
265
|
|
|
188
|
-
## Configuration
|
|
266
|
+
## ⚙️ Configuration
|
|
267
|
+
|
|
268
|
+
Three layers: **nearest wins**, key by key (a key unset at one layer inherits the next):
|
|
189
269
|
|
|
190
|
-
|
|
270
|
+
- **Project**: a `.trailhead/config.json` file at the repo root: overrides for this project, committed so a team shares one config. Config lives in a plain file, **never in the map issue**; it's yours to change at any time.
|
|
271
|
+
- **Global**: `~/.claude/trailhead/config.json`: your standing defaults across every project.
|
|
272
|
+
- **Defaults**: the built-in values.
|
|
191
273
|
|
|
192
|
-
- **
|
|
193
|
-
- **Project** — a `## Config` block in the map issue: overrides for this map, right on the tracker.
|
|
274
|
+
`/trailhead:config` runs a **guided, menu-driven setup**: pick the scope, then walk each setting (🌐 ticket language · 🧠 models · 🎨 design + approval · 🧪 TDD · 🖥️ acceptance testing · 🧑⚖️ plan review · 📊 statusline) as an icon-labelled menu; no hand-editing JSON. Every step is asked (none skipped), and **plan and execute models are always two separate, version-pinned choices**. `config get` prints the effective merged config; `config set <key> <value>` writes one key.
|
|
194
275
|
|
|
195
|
-
|
|
276
|
+
The **📊 statusline** step offers to install trailhead's Claude Code status bar: one line with **model · project · branch · plan usage (`5h %` · reset · `7d %`) · a context-window bar**, plus a **second line with the active ticket** (`▸ #N Title`) whenever you're working one, and a `⬆ trailhead <version>` flag when a newer trailhead is available (run `/trailhead:update`). It's a global Claude Code setting; if you already run a statusline (e.g. `ccstatusline`) the setup asks before replacing it, and the script also exposes `--ticket-only` / `--context-only` / `--usage-only` segments to slot into an existing tool.
|
|
277
|
+
|
|
278
|
+
**First-use offer (once per project).** You don't have to seek this out: the first time you set a project up (at the end of `/trailhead:new`/`:adopt`, or on a bare `/trailhead` when a map exists but no config does) trailhead offers to run the guided setup, or to continue on defaults. It asks **only once**: configuring writes `.trailhead/config.json`, and "continue on defaults" writes an empty `{}`. Once that file exists it's never offered again, and it never interrupts mid-ticket.
|
|
196
279
|
|
|
197
280
|
| Key | Values (default **bold**) | Effect |
|
|
198
281
|
|---|---|---|
|
|
199
|
-
| `
|
|
200
|
-
| `
|
|
282
|
+
| `ticket.language` | an ISO 639-1 code (**`en`**) | the language trailhead **writes** its GitHub prose & commit descriptions in, decoupled from the language it converses in |
|
|
283
|
+
| `models.{plan,execute,research,review,debug}` | a full **versioned** model id (**inherit session**) | which model runs each activity; `plan` and `execute` are always set separately |
|
|
284
|
+
| `design` | **`disk`** \| `claude.ai/design` | where UI mockups go: local throwaway HTML, or a design-system project on claude.ai/design via DesignSync |
|
|
285
|
+
| `design.approval` | **`explicit`** \| `auto` | wait for mockup approval before UI code, or proceed without blocking |
|
|
201
286
|
| `tdd` | **`seams`** \| `on` \| `off` | how the `build` engine tests |
|
|
202
|
-
| `acceptance.browser` | **`auto`** \| `on` \| `off` | drive the browser in Verify, or
|
|
287
|
+
| `acceptance.browser` | **`auto`** \| `on` \| `off` | drive the browser in Verify, or walk you through a conversational UAT (step by step in chat, not a checklist to self-serve) |
|
|
203
288
|
| `testing.webapp` / `testing.url` | bool / URL | is it browser-drivable, and where |
|
|
204
289
|
| `plan_review` | **`off`** \| `on` \| CLI list | send `build` PLANs to external AI CLIs (Gemini, Codex, …) for a second opinion and converge on their concerns |
|
|
290
|
+
| `plan_review.rounds` | integer (**`2`**) | max converge-and-re-review rounds |
|
|
205
291
|
|
|
206
|
-
**Models
|
|
292
|
+
**Models.** Each key runs its activity as a **subagent** on the model you name, so the whole per-activity split applies within a single work session, whatever model that session runs on:
|
|
207
293
|
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
```
|
|
294
|
+
- `plan` and `execute` are the build/bug engine's steps. Execute is a subagent too, and it commits to `main`.
|
|
295
|
+
- `research`, `review`, `debug`, and the codebase-map fan-out each run on their own key.
|
|
211
296
|
|
|
212
|
-
|
|
297
|
+
Choose `plan` and `execute` separately, each by **full versioned id** (never a bare `opus` or `sonnet`). A key that is unset, or equal to the session model, simply runs inline.
|
|
213
298
|
|
|
214
|
-
|
|
299
|
+
The **main session stays the orchestrator**. It holds the interactive moments (a build's Discuss, and Verify's acceptance/UAT) and it does charting, grilling, and ticket-writing on its own model. No key governs ticket quality, so run charting sessions on your strong model.
|
|
215
300
|
|
|
216
|
-
|
|
301
|
+
Example config file (the same shape works for the project `.trailhead/config.json` and the global `~/.claude/trailhead/config.json`):
|
|
217
302
|
|
|
218
|
-
|
|
303
|
+
```json
|
|
304
|
+
{ "ticket": { "language": "en" }, "models": { "plan": "claude-opus-4-8", "execute": "claude-sonnet-5" }, "tdd": "seams", "acceptance": { "browser": "auto" } }
|
|
305
|
+
```
|
|
219
306
|
|
|
220
|
-
|
|
307
|
+
**Design mockups.** `design: disk` (the default) drops a throwaway static HTML mockup next to the code and links it from the ticket. `design: claude.ai/design` instead pushes the mockup to a **design-system project** on claude.ai/design via **DesignSync** (the `/design-sync` skill plus the `claude_design` MCP), where you refine it visually.
|
|
221
308
|
|
|
222
|
-
|
|
309
|
+
On the first UI screen, trailhead asks which design system to use. You can:
|
|
223
310
|
|
|
224
|
-
- **
|
|
225
|
-
- **
|
|
311
|
+
- **pick from your 10 most-recent**,
|
|
312
|
+
- **paste a `claude.ai/design/p/<id>` URL**, or
|
|
313
|
+
- **create a new one** (it asks you for the name).
|
|
226
314
|
|
|
227
|
-
|
|
315
|
+
It caches the chosen id in `design.project`. Each screen is pushed there, and its URL is linked from the ticket. Once you approve, trailhead **re-fetches** the current design (in case you edited it live) before writing any UI code.
|
|
316
|
+
|
|
317
|
+
DesignSync drives only design-system projects, not regular ones; for a regular project you place the mockup by hand. `design.approval` decides whether the build waits for your explicit go-ahead (`explicit`), or proceeds right after surfacing the mockup (`auto`).
|
|
228
318
|
|
|
229
319
|
---
|
|
230
320
|
|
|
231
|
-
##
|
|
321
|
+
## 🪝 Hooks
|
|
232
322
|
|
|
233
|
-
|
|
323
|
+
Beyond the skill's instructions, trailhead ships three 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:
|
|
234
324
|
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
/
|
|
238
|
-
/plugin install trailhead@trailhead
|
|
239
|
-
```
|
|
240
|
-
Update with `/plugin update trailhead`; remove with `/plugin uninstall trailhead`.
|
|
325
|
+
- **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.
|
|
326
|
+
- **Secret guard** (`PreToolUse` on `gh` issue/PR writes): trailhead posts a lot to the tracker (ticket bodies, engine comments, codebase/conventions issues, resolutions). This scans what a `gh issue`/`gh pr`/`gh api` write is about to post (the inline `--body`, a heredoc, or a `--body-file`) and **hard-blocks** it if it matches a credential pattern (private keys, GitHub/AWS/OpenAI/Slack/Google/Stripe tokens, JWTs, or a hardcoded `password`/`secret`/`token=…`). The block is a **clean-and-retry cue, not a dead end**: trailhead redacts the flagged value in place (`<REDACTED>` or an env-var reference) and re-posts automatically, so the cleaned content still lands: the secret just never reaches the tracker. It stays a fail-safe *block* rather than a silent auto-rewrite on purpose: a block never leaks, whereas a redaction that quietly failed to apply would. It never scans reads, only outbound writes.
|
|
327
|
+
- **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.
|
|
241
328
|
|
|
242
|
-
|
|
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.
|
|
329
|
+
All three are crash-safe (any error → allow) and active whenever the plugin is installed. The commit and secret guards therefore apply in **every** repo, not only trailhead projects: intentional, since conventional commits, no `Co-Authored-By`, and no leaked secrets are trailhead's standing rules. To opt out, disable the plugin's hooks in your Claude Code settings.
|
|
253
330
|
|
|
254
331
|
---
|
|
255
332
|
|
|
256
|
-
## Acknowledgements
|
|
333
|
+
## 🙏 Acknowledgements
|
|
257
334
|
|
|
258
|
-
`trailhead` stands on the shoulders of two bodies of work. It is an original, self-contained methodology **inspired by** them
|
|
335
|
+
`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
336
|
|
|
260
|
-
- **[Matt Pocock](https://www.aihero.dev)
|
|
261
|
-
- **The [open-gsd](https://github.com/open-gsd/gsd-core) team
|
|
337
|
+
- **[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).
|
|
338
|
+
- **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
339
|
|
|
263
|
-
Thank you. If you like the ideas here, go star and use the originals
|
|
340
|
+
Thank you. If you like the ideas here, go star and use the originals: they go far deeper than trailhead's compact protocols.
|
|
264
341
|
|
|
265
342
|
---
|
|
266
343
|
|
|
267
|
-
## License
|
|
344
|
+
## 📄 License
|
|
268
345
|
|
|
269
346
|
MIT © Marco Migozzi ([@ToRvaLDz](https://github.com/ToRvaLDz))
|
package/bin/trailhead.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
'use strict';
|
|
3
|
-
// trailhead installer
|
|
3
|
+
// trailhead installer: places the skill, commands, hooks, and templates into
|
|
4
4
|
// an agent's config dir, and registers the hooks in settings.json.
|
|
5
5
|
// No dependencies. Idempotent. Usage:
|
|
6
6
|
// npx @marcomigozzi/trailhead install for Claude Code (~/.claude or $CLAUDE_CONFIG_DIR)
|
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
// npx @marcomigozzi/trailhead --dir=/path/to/configdir
|
|
10
10
|
//
|
|
11
11
|
// Multi-CLI: only the Claude adapter exists today. Add adapters below (config dir,
|
|
12
|
-
// commands/hooks layout) to target Codex, Gemini, etc.
|
|
12
|
+
// commands/hooks layout) to target Codex, Gemini, etc. See ADAPTERS.
|
|
13
13
|
|
|
14
14
|
const fs = require('fs');
|
|
15
15
|
const os = require('os');
|
|
@@ -42,7 +42,7 @@ const P = {
|
|
|
42
42
|
templates: path.join(configDir, 'trailhead', 'templates'),
|
|
43
43
|
settings: path.join(configDir, 'settings.json'),
|
|
44
44
|
};
|
|
45
|
-
const HOOK_FILES = ['trailhead-commit-guard.js', 'trailhead-issue-injection-scanner.js'];
|
|
45
|
+
const HOOK_FILES = ['trailhead-commit-guard.js', 'trailhead-issue-injection-scanner.js', 'trailhead-secret-guard.js', 'trailhead-check-update.js'];
|
|
46
46
|
const hookCmd = (name) => `node "${path.join(P.hooksDir, name)}"`;
|
|
47
47
|
|
|
48
48
|
const rmrf = (p) => fs.rmSync(p, { recursive: true, force: true });
|
|
@@ -77,7 +77,9 @@ if (uninstall) {
|
|
|
77
77
|
HOOK_FILES.forEach((f) => rmrf(path.join(P.hooksDir, f)));
|
|
78
78
|
const s = readJSON(P.settings);
|
|
79
79
|
stripHook(s, 'PreToolUse', 'trailhead-commit-guard.js');
|
|
80
|
+
stripHook(s, 'PreToolUse', 'trailhead-secret-guard.js');
|
|
80
81
|
stripHook(s, 'PostToolUse', 'trailhead-issue-injection-scanner.js');
|
|
82
|
+
stripHook(s, 'SessionStart', 'trailhead-check-update.js');
|
|
81
83
|
writeJSON(P.settings, s);
|
|
82
84
|
console.log(`trailhead uninstalled from ${configDir}`);
|
|
83
85
|
process.exit(0);
|
|
@@ -92,10 +94,19 @@ for (const f of HOOK_FILES) {
|
|
|
92
94
|
fs.copyFileSync(path.join(SRC, 'hooks', f), path.join(P.hooksDir, f));
|
|
93
95
|
fs.chmodSync(path.join(P.hooksDir, f), 0o755);
|
|
94
96
|
}
|
|
97
|
+
// version marker: serve al check-update hook per la versione installata (canale npm)
|
|
98
|
+
const pkgVersion = (readJSON(path.join(SRC, '.claude-plugin', 'plugin.json')).version || '').trim();
|
|
99
|
+
if (pkgVersion) {
|
|
100
|
+
ensure(path.join(configDir, 'trailhead'));
|
|
101
|
+
fs.writeFileSync(path.join(configDir, 'trailhead', 'VERSION'), pkgVersion + '\n');
|
|
102
|
+
}
|
|
103
|
+
|
|
95
104
|
const s = readJSON(P.settings);
|
|
96
105
|
const added = [
|
|
97
106
|
addHook(s, 'PreToolUse', 'Bash', hookCmd('trailhead-commit-guard.js')),
|
|
107
|
+
addHook(s, 'PreToolUse', 'Bash', hookCmd('trailhead-secret-guard.js')),
|
|
98
108
|
addHook(s, 'PostToolUse', 'Bash', hookCmd('trailhead-issue-injection-scanner.js')),
|
|
109
|
+
addHook(s, 'SessionStart', '', hookCmd('trailhead-check-update.js')),
|
|
99
110
|
].some(Boolean);
|
|
100
111
|
writeJSON(P.settings, s);
|
|
101
112
|
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@marcomigozzi/trailhead",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"description": "Start and drive large projects as a map of tickets on GitHub Issues.
|
|
3
|
+
"version": "0.3.0",
|
|
4
|
+
"description": "Start and drive large projects as a map of tickets on GitHub Issues. 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
5
|
"bin": {
|
|
6
6
|
"trailhead": "bin/trailhead.js"
|
|
7
7
|
},
|
|
@@ -20,8 +20,6 @@
|
|
|
20
20
|
"keywords": [
|
|
21
21
|
"trailhead",
|
|
22
22
|
"claude-code",
|
|
23
|
-
"wayfinder",
|
|
24
|
-
"gsd",
|
|
25
23
|
"github-issues",
|
|
26
24
|
"planning",
|
|
27
25
|
"agent-skill"
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "trailhead",
|
|
3
|
-
"version": "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:
|
|
3
|
+
"version": "0.3.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: depends on nothing beyond an authenticated gh CLI.",
|
|
5
5
|
"author": {
|
|
6
6
|
"name": "Marco Migozzi",
|
|
7
7
|
"url": "https://github.com/ToRvaLDz"
|
|
@@ -10,8 +10,6 @@
|
|
|
10
10
|
"repository": "https://github.com/ToRvaLDz/trailhead",
|
|
11
11
|
"license": "MIT",
|
|
12
12
|
"keywords": [
|
|
13
|
-
"wayfinder",
|
|
14
|
-
"gsd",
|
|
15
13
|
"github-issues",
|
|
16
14
|
"planning",
|
|
17
15
|
"orchestrator",
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
|
-
description: Adopt an existing project
|
|
2
|
+
description: "Adopt an existing project: map the codebase once, then go lean"
|
|
3
3
|
argument-hint: ""
|
|
4
4
|
---
|
|
5
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
|
|
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.
|
|
@@ -3,4 +3,4 @@ description: Capture a bug ticket (use --of <ticket> for a regression)
|
|
|
3
3
|
argument-hint: "[--of <ticket>] <text>"
|
|
4
4
|
---
|
|
5
5
|
|
|
6
|
-
Invoke the `trailhead` skill for the **bug** action: call the Skill tool with skill name `trailhead` and arguments `bug $ARGUMENTS`, then
|
|
6
|
+
Invoke the `trailhead` skill for the **bug** action: call the Skill tool with skill name `trailhead` and arguments `bug $ARGUMENTS`, then **read the skill's `references/capture.md` and follow it**: that file holds the full protocol for the capture verbs. Do not re-implement the behaviour here. The skill is the single source of truth.
|