jackherd 1.7.0 → 1.8.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 (157) hide show
  1. package/CHANGELOG.md +43 -0
  2. package/INSTALL.md +4 -2
  3. package/{NOTICE → NOTICE.txt} +1 -1
  4. package/README.md +94 -1033
  5. package/assets/icons/bark-dark.svg +1 -1
  6. package/assets/icons/bark.svg +1 -1
  7. package/assets/icons/bowl-dark.svg +1 -1
  8. package/assets/icons/bowl.svg +1 -1
  9. package/assets/icons/decide-dark.svg +1 -1
  10. package/assets/icons/decide.svg +1 -1
  11. package/assets/icons/ear-dark.svg +1 -1
  12. package/assets/icons/ear.svg +1 -1
  13. package/assets/icons/lanes-dark.svg +1 -1
  14. package/assets/icons/lanes.svg +1 -1
  15. package/assets/icons/plan-dark.svg +1 -1
  16. package/assets/icons/plan.svg +1 -1
  17. package/dist/{chunk-OBUWTQ6Z.js → chunk-M6DDZEW4.js} +24 -10
  18. package/dist/config.d.ts +11 -5
  19. package/dist/config.js +53 -21
  20. package/dist/plan-format.d.ts +73 -7
  21. package/dist/plan-format.js +180 -100
  22. package/docs/workflow-dark.svg +1 -1
  23. package/docs/workflow.svg +1 -1
  24. package/herdr-plugin/herdr-plugin.toml +1 -1
  25. package/install.sh +1 -1
  26. package/kit/hooks/hooks/approval-gate/herdr-panes.ts +2 -12
  27. package/kit/hooks/hooks/approval-gate/index.ts +10 -8
  28. package/kit/hooks/hooks/approval-gate/run.ts +21 -12
  29. package/kit/hooks/hooks/ask-hint/decision.ts +3 -3
  30. package/kit/hooks/hooks/git-guard/decision.ts +189 -15
  31. package/kit/hooks/hooks/git-guard/index.ts +13 -3
  32. package/kit/hooks/hooks/git-guard/push-target.ts +85 -0
  33. package/kit/hooks/hooks/git-guard/repository.ts +63 -0
  34. package/kit/hooks/hooks/merge-guard/check-free.ts +1 -13
  35. package/kit/hooks/hooks/merge-guard/decision.ts +342 -102
  36. package/kit/hooks/hooks/merge-guard/index.ts +6 -4
  37. package/kit/hooks/hooks/merge-guard/plain-path.ts +15 -0
  38. package/kit/hooks/hooks/merge-guard/plan-approval-record.ts +23 -0
  39. package/kit/hooks/hooks/merge-guard/plan-approval-settings.ts +142 -0
  40. package/kit/hooks/hooks/merge-guard/plan-approval-sources.ts +196 -0
  41. package/kit/hooks/hooks/merge-guard/plan-approval.ts +168 -0
  42. package/kit/hooks/hooks/merge-guard/plan-growth.ts +40 -0
  43. package/kit/hooks/hooks/merge-guard/plan-pointers.ts +194 -0
  44. package/kit/hooks/hooks/merge-guard/pull-request.ts +67 -21
  45. package/kit/hooks/hooks/merge-guard/run.ts +118 -29
  46. package/kit/hooks/hooks/merge-guard/settings.ts +8 -8
  47. package/kit/hooks/hooks/merge-guard/worker.ts +3 -3
  48. package/kit/hooks/hooks/shared/base-branch.ts +23 -0
  49. package/kit/hooks/hooks/shared/board-lanes.ts +93 -0
  50. package/kit/hooks/hooks/shared/command-text-runners.ts +71 -0
  51. package/kit/hooks/hooks/shared/command.ts +14 -2
  52. package/kit/hooks/hooks/shared/commit.ts +2 -0
  53. package/kit/hooks/hooks/shared/dialog-answers.ts +4 -1
  54. package/kit/hooks/hooks/shared/json.ts +10 -0
  55. package/kit/hooks/hooks/shared/lane.ts +57 -0
  56. package/kit/hooks/hooks/shared/once.ts +8 -0
  57. package/kit/hooks/hooks/shared/own-pane.ts +31 -0
  58. package/kit/hooks/hooks/shared/pane-agent.ts +24 -0
  59. package/kit/hooks/hooks/shared/plan-open-phases.ts +114 -0
  60. package/kit/hooks/hooks/shared/program-position.ts +148 -5
  61. package/kit/hooks/hooks/shared/shell-aliases.ts +258 -0
  62. package/kit/hooks/hooks/shared/shell-snapshots.ts +68 -0
  63. package/kit/hooks/hooks/shared/shell.ts +167 -20
  64. package/kit/hooks/hooks.json +11 -2
  65. package/kit/hooks/statusline.sh +29 -16
  66. package/kit/review/skills/diagnose/SKILL.md +1 -1
  67. package/kit/review/skills/review/SKILL.md +2 -0
  68. package/kit/rules/rules/kit/testing.md +2 -5
  69. package/kit/rules/rules/kit/writing-for-agents.md +3 -1
  70. package/kit/workflow/agents/spec-review.md +1 -1
  71. package/kit/workflow/skills/follow/SKILL.md +3 -3
  72. package/kit/workflow/skills/grill/SKILL.md +1 -1
  73. package/kit/workflow/skills/implement/SKILL.md +10 -11
  74. package/kit/workflow/skills/orchestrate/SKILL.md +155 -117
  75. package/kit/workflow/skills/orchestrate/references/coordinator-handover.md +17 -0
  76. package/kit/workflow/skills/orchestrate/references/herd.md +27 -0
  77. package/kit/workflow/skills/orchestrate/references/plan-commit.md +22 -0
  78. package/kit/workflow/skills/orchestrate/references/sensitive-input.md +23 -0
  79. package/kit/workflow/skills/plan/SKILL.md +5 -2
  80. package/kit/workflow/skills/plan/references/template-English.md +6 -0
  81. package/kit/workflow/skills/plan/references/template-German.md +6 -0
  82. package/package.json +3 -2
  83. package/recipes/README.md +21 -0
  84. package/recipes/github-issues.md +23 -0
  85. package/recipes/github-projects.md +22 -0
  86. package/recipes/jira-cli.md +26 -0
  87. package/recipes/jira-rest.md +24 -0
  88. package/recipes/linear.md +25 -0
  89. package/recipes/sentry.md +18 -0
  90. package/recipes/sources/jira.ts +51 -0
  91. package/recipes/sources/linear.ts +19 -0
  92. package/recipes/sources/sentry.ts +12 -0
  93. package/recipes/sources.md +80 -0
  94. package/recipes/status-page.md +19 -0
  95. package/schemas/jackherd.schema.json +42 -18
  96. package/src/check/command.ts +2 -2
  97. package/src/check/parallel-report.ts +19 -9
  98. package/src/cli.ts +1 -0
  99. package/src/config/context-shares.ts +130 -0
  100. package/src/config/legacy-names.ts +8 -0
  101. package/src/config/read-config.ts +12 -4
  102. package/src/config/schema.ts +75 -20
  103. package/src/context/auto-compact.ts +68 -0
  104. package/src/context/command.ts +68 -17
  105. package/src/context/coordinator-name.ts +27 -9
  106. package/src/context/index.ts +0 -1
  107. package/src/context/level.ts +18 -11
  108. package/src/context/report.ts +6 -3
  109. package/src/plan-format/dependencies.ts +144 -32
  110. package/src/plan-format/index.ts +6 -0
  111. package/src/plan-format/lanes.ts +6 -0
  112. package/src/plan-format/progress.ts +19 -8
  113. package/src/plans/command.ts +14 -5
  114. package/src/plans/current-phase.ts +63 -7
  115. package/src/plans/current-task.ts +51 -0
  116. package/src/plans/ensure-panes.ts +25 -8
  117. package/src/plans/frame.ts +99 -0
  118. package/src/plans/git.ts +8 -0
  119. package/src/plans/herd-memory.ts +107 -0
  120. package/src/plans/lane-plans.ts +5 -3
  121. package/src/plans/lane-summary.ts +222 -0
  122. package/src/plans/layout-phase.ts +148 -0
  123. package/src/plans/layout-summary.ts +140 -0
  124. package/src/plans/layout.ts +164 -420
  125. package/src/plans/live.ts +11 -6
  126. package/src/plans/plan-panes.ts +15 -0
  127. package/src/plans/planned.ts +9 -42
  128. package/src/plans/selection.ts +2 -6
  129. package/src/plans/settings.ts +2 -7
  130. package/src/plans/snapshot.ts +38 -39
  131. package/src/plans/symbols.ts +2 -2
  132. package/src/plans/texts.ts +32 -31
  133. package/src/plans/view.ts +0 -1
  134. package/src/plans/worked-phases.ts +2 -2
  135. package/src/settled/board-row.ts +133 -0
  136. package/src/settled/check.ts +129 -0
  137. package/src/settled/command.ts +65 -0
  138. package/src/settled/evidence-version.ts +14 -0
  139. package/src/settled/options.ts +69 -0
  140. package/src/settled/send.ts +27 -0
  141. package/src/settled/settle.ts +87 -0
  142. package/src/settled/topics.ts +56 -0
  143. package/src/sync/command.ts +16 -1
  144. package/src/sync/render.ts +13 -4
  145. package/src/sync/sync.ts +25 -5
  146. package/src/terminal/herdr.ts +46 -10
  147. package/src/terminal/none.ts +1 -0
  148. package/src/terminal/types.ts +15 -2
  149. package/src/update/command.ts +4 -2
  150. package/src/update/update.ts +33 -9
  151. package/src/vocabulary/index.ts +47 -11
  152. package/src/watch/appointments.ts +205 -0
  153. package/src/watch/command.ts +62 -19
  154. package/src/watch/lane-report.ts +11 -3
  155. package/src/watch/texts.ts +35 -59
  156. package/src/watch/watch-events.ts +21 -28
  157. package/kit/hooks/hooks/approval-gate/lane.ts +0 -14
package/README.md CHANGED
@@ -1,909 +1,163 @@
1
1
  <p>
2
2
  <picture>
3
- <source media="(prefers-color-scheme: dark)" srcset="https://unpkg.com/jackherd@1.7.0/assets/branding/jackherd-logo-dark.svg">
4
- <img alt="jackherd: a black and white Shih Tzu in a turquoise bandana, three lanes running from him to three agent nodes" src="https://unpkg.com/jackherd@1.7.0/assets/branding/jackherd-logo.svg" width="340">
3
+ <source media="(prefers-color-scheme: dark)" srcset="https://unpkg.com/jackherd/assets/branding/jackherd-logo-dark.svg">
4
+ <img alt="jackherd: a black and white Shih Tzu in a turquoise bandana, three lanes running from him to three agent nodes" src="https://unpkg.com/jackherd/assets/branding/jackherd-logo.svg" width="340">
5
5
  </picture>
6
6
  <br>
7
7
  </p>
8
8
 
9
- [![npm](https://img.shields.io/npm/v/jackherd)](https://www.npmjs.com/package/jackherd) [![license: MIT](https://img.shields.io/npm/l/jackherd)](https://www.npmjs.com/package/jackherd) [![2 dependencies](https://img.shields.io/badge/dependencies-2-brightgreen)](https://www.npmjs.com/package/jackherd?activeTab=dependencies)
9
+ [![npm](https://img.shields.io/npm/v/jackherd)](https://www.npmjs.com/package/jackherd) [![license: MIT](https://img.shields.io/npm/l/jackherd)](https://www.npmjs.com/package/jackherd) [![2 dependencies](https://img.shields.io/badge/dependencies-2-brightgreen)](https://www.npmjs.com/package/jackherd?activeTab=dependencies)<br><img width="1" height="8" alt="" src="https://unpkg.com/jackherd/assets/icons/space.svg">
10
10
 
11
- **Plan. Delegate. Ship.** Your agents do the work. Jack keeps them together.
11
+ **Let AI do the work. Stay in control.** Build several features in parallel with Claude Code. Each runs in its own branch; you only answer the questions that need a human. Jack, the coordinator (the Shih Tzu in the logo, who herds anyway), handles the rest. In a project install, hooks hold merges and releases that touch risky paths until you approve, and refuse a push to your base branch.
12
12
 
13
- Large tasks outgrow one chat, the context fills up, parallel sessions edit the same files, and
14
- you lose track of who waits on you. Jack:<br><img width="1" height="8" alt="" src="https://unpkg.com/jackherd@1.7.0/assets/icons/space.svg">
13
+ Install with one command, run `/jh-start` in a repository, and it offers your first plan: [Quickstart](#quickstart).<br><img width="1" height="8" alt="" src="https://unpkg.com/jackherd/assets/icons/space.svg">
15
14
 
16
- <p><picture><source media="(prefers-color-scheme: dark)" srcset="https://unpkg.com/jackherd@1.7.0/assets/icons/plan-dark.svg"><img align="left" width="28" height="48" alt="" src="https://unpkg.com/jackherd@1.7.0/assets/icons/plan.svg"></picture><b>Keeps the plan in your repository.</b><br>Your agent writes it as Markdown, phases with a model each, and ticks it off as it builds.<br clear="left"></p>
17
-
18
- <p><picture><source media="(prefers-color-scheme: dark)" srcset="https://unpkg.com/jackherd@1.7.0/assets/icons/lanes-dark.svg"><img align="left" width="28" height="48" alt="" src="https://unpkg.com/jackherd@1.7.0/assets/icons/lanes.svg"></picture><b>Herds the lanes.</b><br>Each lane works in its own git worktree, so sessions never share a checkout.<br clear="left"></p>
19
-
20
- <p><picture><source media="(prefers-color-scheme: dark)" srcset="https://unpkg.com/jackherd@1.7.0/assets/icons/decide-dark.svg"><img align="left" width="28" height="72" alt="" src="https://unpkg.com/jackherd@1.7.0/assets/icons/decide.svg"></picture><b>Brings you only the decisions.</b><br>A coordinating session answers what the plan settles, vets review feedback on its pull requests, asks you the rest as a choice, and asks you before every commit, push and merge.<br clear="left"></p>
21
-
22
- <p><picture><source media="(prefers-color-scheme: dark)" srcset="https://unpkg.com/jackherd@1.7.0/assets/icons/bark-dark.svg"><img align="left" width="28" height="72" alt="" src="https://unpkg.com/jackherd@1.7.0/assets/icons/bark.svg"></picture><b>Barks when something moves.</b><br>One live view of every lane: state, progress, pull request, checks, with the preview link and new feedback as badges.<br clear="left"></p>
15
+ <p>
16
+ <picture>
17
+ <source media="(prefers-color-scheme: dark)" srcset="https://unpkg.com/jackherd/docs/workflow-dark.svg">
18
+ <img src="https://unpkg.com/jackherd/docs/workflow.svg" width="720" alt="jackherd in herdr: a lane hits a question, Jack asks you as a choice, you pick, and the lane works on while the herd view counts its progress.">
19
+ </picture>
20
+ <br>
21
+ <sub>Parallel mode in herdr (optional). Planning and building work without it.</sub><br><img width="1" height="8" alt="" src="https://unpkg.com/jackherd/assets/icons/space.svg">
22
+ </p>
23
23
 
24
- <p><picture><source media="(prefers-color-scheme: dark)" srcset="https://unpkg.com/jackherd@1.7.0/assets/icons/ear-dark.svg"><img align="left" width="28" height="72" alt="" src="https://unpkg.com/jackherd@1.7.0/assets/icons/ear.svg"></picture><b>Keeps an ear on what lives outside GitHub.</b><br><code>/jh-follow PROJ-123</code>, and Jack listens to the ticket, the issue or the status page; when someone answers there, he barks.<br clear="left"></p>
24
+ <p><picture><source media="(prefers-color-scheme: dark)" srcset="https://unpkg.com/jackherd/assets/icons/plan-dark.svg"><img align="left" width="28" height="48" alt="" src="https://unpkg.com/jackherd/assets/icons/plan.svg"></picture><b>Plans live in your repository.</b> Markdown phases, a model each, ticked off as they are built.<br clear="left"></p>
25
25
 
26
- <p><picture><source media="(prefers-color-scheme: dark)" srcset="https://unpkg.com/jackherd@1.7.0/assets/icons/bowl-dark.svg"><img align="left" width="28" height="72" alt="" src="https://unpkg.com/jackherd@1.7.0/assets/icons/bowl.svg"></picture><b>Watches the context.</b><br>It measures each agent session's fill and, before it is full, offers a fresh chat that takes over the state.<br clear="left"></p>
26
+ <p><picture><source media="(prefers-color-scheme: dark)" srcset="https://unpkg.com/jackherd/assets/icons/lanes-dark.svg"><img align="left" width="28" height="48" alt="" src="https://unpkg.com/jackherd/assets/icons/lanes.svg"></picture><b>Lanes stay apart.</b> A lane is one phase of a plan in its own git worktree and session, side by side in herdr, the terminal that holds the parallel sessions.<br clear="left"></p>
27
27
 
28
- Claude Code writes the code. jackherd is the skills, hooks and CLI around it that plan the work, coordinate the sessions and carry decisions, reviews and context from one chat to the next. It brings no platform of its own: plans, boards and their decisions are Markdown in your repository and go through pull requests like code, so your team reviews them where it reviews code. With a project install, `jh init` gives everyone who clones the repository the same skills and hooks. [herdr](https://herdr.dev) is the terminal that holds the parallel sessions: without it, planning, building and reviews work as before; parallel lanes need it. Built for Claude Code today; other coding agents are planned.
28
+ <p><picture><source media="(prefers-color-scheme: dark)" srcset="https://unpkg.com/jackherd/assets/icons/decide-dark.svg"><img align="left" width="28" height="48" alt="" src="https://unpkg.com/jackherd/assets/icons/decide.svg"></picture><b>You decide. Jack takes care of the lanes.</b> He answers what the plan settles and asks you the rest.<br clear="left"></p>
29
29
 
30
- You stay in the loop: what the plan settles, the coordinator answers itself and tells you in one line; every other decision waits for you, and so do commits, pushes and merges: one approval per pull request, and the coordinator merges only where you allowed it, by picking self-merge for that pull request or in your project's rules ([what waits for you](#user-content-what-happens-on-its-own-and-what-waits-for-you)).
30
+ <p><picture><source media="(prefers-color-scheme: dark)" srcset="https://unpkg.com/jackherd/assets/icons/bark-dark.svg"><img align="left" width="28" height="48" alt="" src="https://unpkg.com/jackherd/assets/icons/bark.svg"></picture><b>Jack barks when something moves.</b> One live view of the herd, all your lanes: state, pull request, checks, feedback.<br clear="left"></p>
31
31
 
32
- <picture>
33
- <source media="(prefers-color-scheme: dark)" srcset="https://unpkg.com/jackherd@1.7.0/docs/workflow-dark.svg">
34
- <img src="https://unpkg.com/jackherd@1.7.0/docs/workflow.svg" width="720" alt="jackherd in herdr, in five steps: a lane works, hits an open question, the coordinator asks you as a choice, you pick, and the lane works on while the herd counts its progress.">
35
- </picture>
32
+ <p><picture><source media="(prefers-color-scheme: dark)" srcset="https://unpkg.com/jackherd/assets/icons/ear-dark.svg"><img align="left" width="28" height="48" alt="" src="https://unpkg.com/jackherd/assets/icons/ear.svg"></picture><b>Jack listens beyond GitHub.</b> <code>/jh-follow</code> Jira, Linear, Sentry or a status page. Reports need herdr.<br clear="left"></p>
36
33
 
37
- <sub>Schematic of one herdr window; the herd pane is drawn by jackherd itself from example data.</sub>
34
+ <p><picture><source media="(prefers-color-scheme: dark)" srcset="https://unpkg.com/jackherd/assets/icons/bowl-dark.svg"><img align="left" width="28" height="48" alt="" src="https://unpkg.com/jackherd/assets/icons/bowl.svg"></picture><b>Long sessions get a fresh start.</b> At a phase boundary, a lane whose context is filling up moves to a new chat.<br clear="left"></p>
38
35
 
39
36
  ## Quickstart
40
37
 
41
- 1. **Install** (Linux and macOS):
38
+ Planning and building work without herdr; parallel lanes need it.
39
+
40
+ 1. **Install** (Linux and macOS; needs Claude Code and git). It asks a few setup questions and leaves your repositories alone. → [INSTALL.md](https://unpkg.com/jackherd/INSTALL.md)
42
41
 
43
42
  ```bash
44
43
  curl -fsSL https://unpkg.com/jackherd/install.sh | sh
45
44
  ```
46
45
 
47
- It installs jackherd and asks a few setup questions, including whether to use herdr (and offers to install it if it is missing); your repositories stay untouched. → [INSTALL.md: what the installer changes, project or global, updating](https://unpkg.com/jackherd/INSTALL.md)
48
-
49
46
  Already in a coding agent? Paste this instead:
50
47
 
51
48
  ```text
52
49
  Install jackherd by following https://unpkg.com/jackherd/INSTALL.md. Ask me the setup questions it lists; herdr is optional.
53
50
  ```
54
- 2. **Start Claude Code in a repository and run:**
51
+ 2. **Start Claude Code in a repository.** It checks the setup and offers your first plan.
55
52
 
56
53
  ```text
57
54
  /jh-start
58
55
  ```
59
-
60
- It checks the setup, says what is missing (herdr is optional and comes with its install command) and offers to start your first plan.
61
- 3. **Describe a task or point to a spec file:**
56
+ 3. **Plan.** Phases, tasks and a model for each, written to `docs/plans/`.
62
57
 
63
58
  ```text
64
59
  /jh-plan Add product search
65
- /jh-plan docs/spec.md
66
- ```
67
-
68
- It writes the plan to `docs/plans/`: phases with tasks and a recommended model each. At the end it proposes lanes for a board, `docs/plans/board-<name>/board.md`, and asks where they go; it starts nothing. A **board** is the Markdown table of lanes.
69
- 4. **Build it.** With herdr, in a Claude Code session started inside herdr, start the board:
70
-
71
- ```text
72
- /jh-orchestrate docs/plans/board-<name>/board.md
73
60
  ```
74
-
75
- It starts every lane whose prerequisites are done; the others wait. A **lane** is one or more phases of a plan in their own git worktree with their own agent session; the session in which you ran `/jh-orchestrate` is the **coordinator**. It answers what the plan settles, brings you the open questions and asks once per pull request before commit and push.
76
-
77
- Without herdr, build a phase in your session:
61
+ 4. **Build,** one phase at a time, ticked off in the plan. No herdr needed.
78
62
 
79
63
  ```text
80
64
  /jh-implement docs/plans/<plan>.md phase 1
81
65
  ```
66
+ 5. **Or let Jack run the herd.** Inside [herdr](https://herdr.dev), one lane per phase, with the live herd view beside him (`jh herd` anywhere else).
82
67
 
83
- It builds one phase; ask for several phases and the independent ones run in parallel as subagents in the same checkout.
84
- 5. **Follow progress.** `/jh-orchestrate` opens the herd, a live view of the board's lanes, in a herdr pane beside the coordinator and one beside each lane. Anywhere else, once a board exists:
85
-
86
- ```bash
87
- jh herd
68
+ ```text
69
+ /jh-orchestrate docs/plans/board-<name>/board.md
88
70
  ```
89
71
 
90
- With herdr, the herd also shows each session's context fill. Once a lane or the coordinator reaches its threshold, jackherd offers you a fresh chat at a quiet moment and hands its state over in a file ([Hand-over to a fresh chat](#user-content-hand-over-to-a-fresh-chat)).
72
+ By hand (Bun 1.3+): `bun add -g jackherd` or `npm install -g jackherd`, then `jh init --global` for yourself, or `jh init` in a repository to give your team the same skills and guard hooks. → [INSTALL.md](https://unpkg.com/jackherd/INSTALL.md)
91
73
 
92
- → [Orchestration](#user-content-orchestration-with-herdr), [Herd](#user-content-herd-and-context)
74
+ ## Human on the loop
93
75
 
94
- Installing by hand (the CLI runs on Bun 1.3+), with Bun:
95
-
96
- ```bash
97
- bun add -g jackherd
98
- ```
99
-
100
- or with npm:
101
-
102
- ```bash
103
- npm install -g jackherd
104
- ```
105
-
106
- then:
107
-
108
- ```bash
109
- jh init --global
110
- ```
111
-
112
- ## With and without herdr
113
-
114
- Planning, building and reviewing work without herdr. herdr adds parallel lanes and the agents' state in the herd.
115
-
116
- | | Without herdr | With herdr |
117
- | --- | --- | --- |
118
- | `/jh-grill`, `/jh-plan`, `/jh-implement`, `/jh-pre-commit` and the review skills | yes | yes |
119
- | Independent phases in parallel | as subagents in one checkout | as lanes, one worktree and session each |
120
- | `/jh-orchestrate` (the coordinator) | – | yes |
121
- | `jh herd` | board and plan progress | plus the lanes' state, pull requests and checks, context fill |
122
- | `/jh-follow` (tickets, issues, status pages) | set up a source and dry-run it | the watcher reports changes to the coordinator; tickets show as badges in the herd |
123
-
124
- Pull requests and checks in the herd need `gh`.
125
-
126
- ## Global or per project
127
-
128
- The global install prefixes every skill with `jh-`, leaves repositories alone and keeps its config in `~/.jackherd/`; it brings no hooks, so the commit, push and merge approvals rest on the skills alone.
129
-
130
- To share the kit with your team instead, run `jh init` in the repository. Skills, rules and the kit's hooks (the merge guard among them) then land in `.claude/`, the same for everyone who clones it, and `jh sync` updates them without overwriting your edits (a changed file gets the new version beside it as `<file>.kit-new`); the project's config lives in `.jackherd/`. In a project install the skills drop the prefix by default: `/plan` instead of `/jh-plan`. → [Installation](https://unpkg.com/jackherd/INSTALL.md)
131
-
132
- ## Documentation
133
-
134
- Each section opens on a click; a link into one opens it.
135
-
136
- <details>
137
- <summary><b>Installation and updates</b></summary>
138
-
139
- ### Installation and updates
140
-
141
- The installer, the setup questions, project or global install, updating and removing are in [INSTALL.md](https://unpkg.com/jackherd/INSTALL.md), with the steps for a coding agent that installs jackherd for you.
142
-
143
- </details>
76
+ You decide every open question, commit and push, once per pull request. Jack answers on his own what the plan already settles, never money, auth, secrets, a migration or a release. Once you've approved a plan and a pull request, he merges it on green checks and tells you in one line. The guards catch mistakes, not an agent set on getting around them; only branch protection stops that. A pull request that touches a workflow, a migration, a lockfile or `package.json` still asks, and so does every release. Prefer to see every merge? Approve per pull request instead.
144
77
 
145
78
  <details>
146
- <summary><b>Planning: plans, models and boards</b></summary>
147
-
148
- ### Planning: plans, models and boards
149
-
150
- Skills on this page are written as the quickstart installs them, with the `jh-` prefix of the global install (`/jh-plan`, `/jh-implement`, `/jh-orchestrate`); agents carry it too (`jh-spec-review`). A project install drops it by default (`skillPrefix` in `.jackherd/config.json` sets one).
151
-
152
- - [Terms](#user-content-terms)
153
- - [The skills and what they hand on](#user-content-the-skills-and-what-they-hand-on)
154
- - [What a plan contains](#user-content-what-a-plan-contains)
155
- - [Model recommendations](#user-content-model-recommendations)
156
- - [Plan format](#user-content-plan-format)
157
- - [Boards](#user-content-boards)
158
-
159
- #### Terms
160
-
161
- - **Plan:** a Markdown file with phases (`### Phase 1: …`) and checkbox tasks. `/jh-plan` writes it, `/jh-implement` builds one phase and ticks its tasks.
162
- - **Board:** a Markdown file with a lanes table that says who works on which phases of which plan, and what each lane starts after. `/jh-plan` proposes its rows; by default it is `docs/plans/board-<name>/board.md`.
163
- - **Lane:** one agent session in its own git worktree, working on the phases its row names.
164
- - **Coordinator:** one more session, in the main checkout, running `/jh-orchestrate`: it starts the lanes, answers or relays their questions, asks you for approvals and keeps the merge order.
165
-
166
- #### The skills and what they hand on
79
+ <summary><b>What Jack does on his own, and what waits for you</b></summary>
167
80
 
168
81
  <picture>
169
- <source media="(prefers-color-scheme: dark)" srcset="https://unpkg.com/jackherd@1.7.0/docs/skills-dark.svg">
170
- <img src="https://unpkg.com/jackherd@1.7.0/docs/skills.svg" alt="/jh-grill hands decision rows to /jh-plan; /jh-plan feeds /jh-orchestrate or a single /jh-implement; /jh-implement ends in /jh-pre-commit, which runs /jh-review, /jh-spec-review and the reviewers routed by path; /jh-diagnose hands a red command to /jh-implement.">
82
+ <source media="(prefers-color-scheme: dark)" srcset="https://unpkg.com/jackherd/docs/lanes-dark.svg">
83
+ <img src="https://unpkg.com/jackherd/docs/lanes.svg" alt="Jack prompts two lanes and asks you for decisions and approvals; the lanes send questions back as text; jh watch reads lane state, pull requests and checks and wakes Jack.">
171
84
  </picture>
172
85
 
173
- - **`/jh-start`** is the first command after the install. It checks the config, herdr and `gh` with read-only commands, explains plan → board → `/jh-orchestrate` or `/jh-implement` in a few lines and offers to begin with `/jh-plan`. It changes no file.
174
- - **`/jh-grill`** questions a loose idea in rounds until every decision is settled, before there is a plan. It writes no document; it hands over decision rows that go into the plan's decisions table unchanged, and names what it left open.
175
- - **`/jh-plan`** writes the plan file from one template ([what it contains](#user-content-what-a-plan-contains)). Its dependency table and its model per phase are what `/jh-orchestrate` reads to tell which phase is ready to start, which lanes would collide, and which model a lane runs on; `/jh-implement` reads the same table to start phases in parallel without `/jh-orchestrate`. At the end `/jh-plan` proposes the board's rows and asks whether they go into the newest board, a new one it names with you, or nowhere yet. If the decisions table would hold its own guesses, `/jh-plan` stops and recommends `/jh-grill` instead.
176
- - **`/jh-orchestrate`** runs the lanes of a board in herdr from the main checkout ([Orchestration](#user-content-orchestration-with-herdr)). Each lane builds its phases with `/jh-implement`.
177
- - **`/jh-implement`** builds one phase: explore, build, test, look at it in the running app when a user sees the change, tick the plan's tasks as they finish. It hands a phase recommended for another model to a subagent on that model, starts independent phases as parallel subagents in one checkout, and ends in `/jh-pre-commit`.
178
- - **`/jh-pre-commit`** runs `jh check` and quotes its numbers, then `/jh-review` for the judgement calls and the `spec-review` agent against the plan, plus the reviewers `check.reviewerRoutes` routes the changed paths to (`/jh-uncle-bob`, `/jh-security-audit`, `/jh-devops`) and, for files a package publishes, `public-content`. It proposes the commit message and asks before committing.
179
- - **`/jh-follow`** is beside the chain too: for a ticket, an issue or a status page outside GitHub it sets up a watcher source from a [recipe](#user-content-recipes), dry-runs it and writes it to `watch.sources` after you agreed ([Following outside GitHub](#user-content-following-outside-github)).
180
- - **`/jh-diagnose`** is beside the chain: for a bug, a flaky failure or a regression it first builds a command that goes red on it, then hands the fix to `/jh-implement`.
181
-
182
- #### What a plan contains
183
-
184
- `/jh-plan` writes every plan from one template. Each part is there because a skill reads it:
185
-
186
- - **Header (date, status, scope):** what the plan touches and how far it is; the board page of the herd (key `s`) shows each plan with its title and status.
187
- - **Decisions:** each decision with its rationale, including who decided and when; `/jh-plan` does not invent them, and `/jh-orchestrate` writes a decision of yours into the plan the moment you make it.
188
- - **Model recommendations per phase:** the model for each phase with the reason ([below](#user-content-model-recommendations)).
189
- - **Dependencies and parallelization:** per phase what it depends on, the paths it writes and its serial markers (a migration, say). `jh check --parallel <plan>` lists the phases that can start together: every dependency ticked off, no shared path (a directory overlaps everything below it), no shared serial marker; a `User: …` entry counts as an open gate. `/jh-implement` starts them at the same time, the watcher proposes them to the coordinator (`=== parallel <lane> P<a> ∥ P<b>`), and `/jh-orchestrate` checks each lane's `Writes` set against the running lanes before starting it. The table cannot see two phases that change the same content or the same release file (changelog, version bump); that stays a judgement. `jh check` warns about a phase of a changed plan that has no row.
190
- - **Phases with checkboxes:** the tasks `/jh-implement` ticks as it finishes them, which is what the herd counts.
191
- - **Review gate per phase:** pre-scan, tests, the reviewers to run, documentation. A phase is finished only when its gate is ticked, and a gate item naming a skill is ticked only after the skill ran. A phase that needs none says why (`**No review gate:** <reason>`); `jh check` refuses a new phase with neither.
192
- - **Tests:** which test files are needed and why, or the reason none are.
193
- - **Documentation and architecture docs:** what gets documented after the last phase, ending with the architecture documentation derived from the finished code.
194
-
195
- The section headings stay verbatim (in the plan language the config sets), because the plan parser, `/jh-implement` and `spec-review` find them by name.
196
-
197
- #### Model recommendations
198
-
199
- Each phase gets one row, judged by **error cost × complexity**. The project's implementation model (`models.implement`) is the default of every row. A phase moves up to the planning model (`models.plan`) when its rationale names a concrete error cost: a breakable invariant, possible data loss, security or tenant isolation, a migration without a way back, distributed state across systems. "Complex" or "many files" alone keeps the default. Review gates with `/jh-security-audit` always get the strongest model.
200
-
201
- The recommendation is applied, not just noted:
202
-
203
- - `/jh-implement` compares the phase's model with the one it runs on and, when they differ in either direction, hands the phase to a subagent on the recommended model.
204
- - `/jh-orchestrate` starts each lane's agent with the full model ID from the board row and checks the banner before any work happens; an alias would resolve through `ANTHROPIC_DEFAULT_*_MODEL` and can land on another model.
205
- - In a project install the hook `model-gate` checks it once more when `/jh-implement` hands a phase to a subagent: it reads the phase (`Phase P<n>`) and the plan path from the prompt and compares the subagent's model (from the call, else from the agent's definition) with that phase's row. A weaker model is stopped with the one to use, a stronger one passes with a note, and whatever the hook cannot tell (no phase, no row, no model) passes silently. When the subagent ends, `model-log` records the model it actually ran on in `.jackherd/models/`, and the herd shows it at the phase as `Opus ✓`, or `Sonnet ≠ Opus` in amber when it differs.
206
-
207
- The defaults a fresh `init` writes are `claude-opus-5-5` for `plan` and `orchestrate`, and `claude-sonnet-5-5` for `implement`, `review` and `explore`. Change them under `models` in the config.
208
-
209
- #### Plan format
210
-
211
- A shortened example, with the parts that matter for the lanes; Current State, Target State, Tests, Risks and the architecture section are left out here:
212
-
213
- ```markdown
214
- <!-- docs/plans/2026-10-12-search.md -->
215
- # Product search
216
-
217
- **Date:** 2026-10-12
218
- **Status:** In Progress
219
- **Scope:** `services/search/`, `apps/shop/src/search/`
220
-
221
- ## Problem / Context
222
-
223
- Customers scroll the whole catalog to find a product. Support gets the same question every day.
224
-
225
- ## Decisions
226
-
227
- | Decision | Rationale |
228
- | -------- | --------- |
229
- | Index in Postgres full-text search, no external engine | User 2026-10-11: one database to run; 4 000 products fit |
230
- | The index rebuilds on product events, not on a timer | User 2026-10-11: a changed price must be searchable at once |
231
-
232
- ## Implementation
233
-
234
- ### Model recommendations per phase
235
-
236
- | Phase | Model | Rationale |
237
- | ----- | ----- | --------- |
238
- | 1 — Index | claude-opus-5-5 | Migration on a hot table; a wrong index locks writes |
239
- | 2 — Search box | claude-sonnet-5-5 | UI after an existing pattern |
240
- | 3 — Docs | claude-sonnet-5-5 | Text from finished code |
241
-
242
- ### Dependencies and parallelization
243
-
244
- | Phase | Depends on | Writes | Serial marker |
245
- | ----- | ---------- | ------ | ------------- |
246
- | 1 — Index | — | `services/search/`, `db/migrations/` | Migration |
247
- | 2 — Search box | 1 | `apps/shop/src/search/` | — |
248
- | 3 — Docs | 1, 2 | `docs/architecture/` | — |
249
-
250
- ### Phase 1: Index
251
-
252
- - [x] Migration: `tsvector` column and GIN index
253
- - [x] Rebuild the row on `product.updated`
254
- - [ ] Backfill the existing products
255
-
256
- **Review gate Phase 1:**
257
-
258
- - [ ] Tests written
259
- - [ ] `/jh-uncle-bob` — Clean Architecture review
260
-
261
- ### Phase 2: Search box
262
-
263
- - [ ] Search box with results as you type
264
- - [ ] Empty state when nothing matches
265
-
266
- **Review gate Phase 2:**
267
-
268
- - [ ] Tests written
269
- - [ ] `/jh-uncle-bob` — Clean Architecture review
270
-
271
- ### Phase 3: Docs
272
-
273
- - [ ] Search flow in the architecture docs
274
- ```
275
-
276
- **Deferred tasks.** A checkbox moved on purpose to a later phase stays unticked and ends in the target, `- [ ] Update the documentation → P3`: it counts in phase 3, not in its own. The arrow counts only at the end of the line; `jh check` refuses an open box in a changed plan that carries it anywhere else.
277
-
278
- #### Boards
279
-
280
- A board gathers the lanes one coordinator runs; it mixes plans and has no end date of its own. `/jh-plan` proposes the rows from the dependency and model tables:
281
-
282
- - **Own lane:** a phase that shares no written path and no serial marker with a phase of another lane.
283
- - **Continuation (`↳`):** a phase that shares a written path or a serial marker with an earlier phase, or depends on it, runs in the same worktree after it.
284
- - **Starts after:** from the phase's dependencies (`P1 merged`, or an external gate such as `User: mockups`); a phase without one starts at once.
285
- - **Model:** the full model ID from the model table, never an alias.
286
- - **Plan:** a Markdown link to the plan, relative to the board's directory. The herd finds a lane's plan only through that link.
287
-
288
- ```markdown
289
- <!-- docs/plans/board-01/board.md -->
290
- # Board 01
291
-
292
- ## Lanes
293
-
294
- | Lane | Port | Plan | Phases | Starts after | Model |
295
- |------|------|------|--------|--------------|-------|
296
- | `search-index` | 3100 | [Product search](../2026-10-12-search.md) | P1 Index | now | claude-opus-5-5 |
297
- | `search-ui` | 3101 | [Product search](../2026-10-12-search.md) | P2 Search box | P1 merged | claude-sonnet-5-5 |
298
- | ↳ `search-ui` | – | [Product search](../2026-10-12-search.md) | P3 Docs | P2 merged | claude-sonnet-5-5 |
299
- ```
300
-
301
- The columns are read by position. The table heading is `## Lanes` (`## Spuren` with `planLanguage: de`). Nothing is written without your answer: `/jh-plan` writes the rows into the newest board or a new `docs/plans/board-<name>/board.md`, and then names the next command, `/jh-orchestrate <board path>`. Plan and board go into one commit under one approval: yours through `/jh-pre-commit`, or the coordinator's documentation PR when `/jh-orchestrate` finds them missing on the base branch ([Orchestration](#user-content-starting)). No merge is needed before `/jh-orchestrate`.
302
-
303
- Where boards live and what the project calls them is the `board` block of the config:
304
-
305
- ```json
306
- { "board": { "pattern": "board-*", "file": "board.md", "term": "board" } }
307
- ```
308
-
309
- - `pattern`: the directories under `planDirectory` that hold a board; `jh herd` and `jh watch` take the newest one unless given a path.
310
- - `file`: the board document in each of them.
311
- - `term`: the word the herd, its HTML page and `watch` use for it (`board page opened: …`).
312
-
313
- Up to 0.12 the key was `sprintPattern` with a fixed `sprint.md`. 0.13 refuses that key and names its replacement; a project that keeps its sprint documents sets `"board": { "pattern": "sprint-*", "file": "sprint.md", "term": "sprint" }` and renames nothing. `watch --sprint` is now `watch --board`.
86
+ | On his own | After your approval | Never |
87
+ | --- | --- | --- |
88
+ | Starting a ready phase in a lane | Commits, push and pull request, once per pull request | Writing lane code himself |
89
+ | Answering what the plan or a rule settles, on record in the board | Every other decision a lane raises | Merging a pull request on a path that always asks, without asking you |
90
+ | Merging a pull request of an approved plan on green checks | A plan approval, once per plan | Storing a permission on your behalf |
91
+ | Rebasing the lanes after a merge | Migrations and writes against a shared environment | |
92
+ | Handing a session over to a fresh chat before its context fills up | Closing a lane | |
314
93
 
315
94
  </details>
316
95
 
317
96
  <details>
318
- <summary><b>Orchestration with herdr</b></summary>
319
-
320
- ### Orchestration with herdr
321
-
322
- For work that splits into independent parts, one agent session per part runs in its own git worktree, and Jack sits between them, keeping every lane on its path. One more session coordinates them. [herdr](https://herdr.dev) is the terminal multiplexer that holds the panes and reports each agent's state; `/jh-orchestrate` (`/orchestrate` in a project install) is the skill the coordinating session runs.
323
-
324
- - [What runs where](#user-content-what-runs-where)
325
- - [Starting](#user-content-starting)
326
- - [What the coordinator does](#user-content-what-the-coordinator-does)
327
- - [What happens on its own, and what waits for you](#user-content-what-happens-on-its-own-and-what-waits-for-you)
328
- - [The watcher](#user-content-the-watcher)
329
- - [Hand-over to a fresh chat](#user-content-hand-over-to-a-fresh-chat)
330
- - [Lane setup from the config](#user-content-lane-setup-from-the-config)
331
-
332
- #### What runs where
97
+ <summary><b>Guarded commands and merges</b></summary>
333
98
 
334
- <picture>
335
- <source media="(prefers-color-scheme: dark)" srcset="https://unpkg.com/jackherd@1.7.0/docs/lanes-dark.svg">
336
- <img src="https://unpkg.com/jackherd@1.7.0/docs/lanes.svg" alt="The coordinator prompts two lanes and asks you for decisions and approvals; the lanes send questions back as text; jh watch reads lane state, PRs and checks and wakes the coordinator.">
337
- </picture>
338
-
339
- `/jh-orchestrate` needs herdr: it checks that `herdr` is on the path and `HERDR_ENV=1` is set, and stops otherwise. Without herdr, `/jh-implement` still runs independent phases of one plan as parallel subagents in a single checkout, kept apart by their disjoint `Writes` sets.
340
-
341
- #### Starting
342
-
343
- 1. Set `"terminal": { "adapter": "herdr" }` in `.jackherd/config.json` (or `~/.jackherd/config.json`), so the herd and `jh context` read agent states and context fill from herdr.
344
- 2. Open herdr, run Claude Code in the main checkout and type `/jh-orchestrate docs/plans/<board>/board.md`. That session is the coordinator.
345
- 3. **Plan and board on the base branch.** Before the first lane starts, the coordinator checks that the board and every plan it links are on `origin/<baseBranch>`. A fresh plan is not, and a lane cut from the base branch would not find it. No merge is needed first: the coordinator asks once, then commits the missing files on a branch `docs/plan-<topic>` from a temporary worktree, pushes it and opens a documentation PR. The lanes of that plan start from that branch until the files are on the base branch. Say no and no lane of these plans starts.
346
-
347
- It names itself in herdr (`orchestrator-<repository>` by default, since herdr names are unique across all repositories), opens the herd beside its pane, tells you which lanes start now and which wait, and starts the watcher.
348
-
349
- #### What the coordinator does
350
-
351
- Lanes ask Jack, not you: the coordinator checks their questions against the plan and the code, answers what is settled, and brings you only what needs your decision.
352
-
353
- 1. **Reads the board.** The `## Lanes` table names each lane, its port, the plan and phases it works on, what it starts after, and the model. It is the only place that says which lane runs which phase. The coordinator adds a row itself when a lane starts without one.
354
- 2. **Starts ready lanes.** A phase is ready when its starts-after entry and the plan's dependencies are met and its `Writes` set and serial markers collide with no running lane; shared paths run one after the other, migrations strictly in order. For each lane it creates a worktree from the base branch, copies the untracked files every worktree needs, installs dependencies, starts the agent on the board's model, checks the banner shows that model, opens a plan pane beside it, and sends the lane prompt: plan, phases, the paths it may write, its port.
355
- 3. **Runs dev servers.** A lane whose phase changes something a user sees gets its dev server in a pane of its own when the lane starts, never in the lane's shell.
356
- 4. **Vets questions.** A lane never opens a dialog; it ends its turn with a text block. The coordinator checks the premises against the code and the plan and brings the open decisions to you, each as a clickable choice.
357
- - The block holds the question, the options, the lane's recommendation and the evidence at `file:line`.
358
- - A question in a lane's report is closed in the same turn, in the lane or with you.
359
- - A draft you review (a README, a page) goes in rounds: you pick the remarks, the lane changes it, you see it again, and the push waits for your approval.
360
- - Your decision goes into the plan or the board in the same turn, with the date, so it is visible in the herd and survives the chat. The date and time come from `date`, never from the chat or a guess.
361
- 5. **Reviews results.** When a lane is `done` or `idle`, it checks the diff against the lane's `Writes` set and that checks are quoted with numbers, not claimed. Then it starts the lane's next ready phase or names the reason to wait.
362
- 6. **Asks once per PR.** A lane works uncommitted until its PR is ready. The coordinator then asks you once, with the diff summary, the checks with numbers, open points and the commit message; for a visible change it first walks the changed path in the browser. That approval covers the commits, the push and the PR. The question has three options: you merge, you approve and let the coordinator merge once the checks are green ([self-merge](#user-content-guarded-commands-and-self-merge)), or you change something first.
363
- 7. **Keeps the merge order.** Merges follow the board's order, the documentation PR of a fresh plan first. After each merge:
364
- - the remaining lanes fetch and rebase on the base branch and re-run their checks; a lock-file conflict is never resolved by hand.
365
- - the coordinator removes the copies of the plan files in the main checkout that equal the merged ones, so the next update of the checkout does not stop on them.
366
- - each lane gets its next phase or a named reason to wait.
367
- 8. **Closes lanes.** After a lane's last merge and your agreement, it ends the agent, proves the worktree holds nothing unmerged, removes the worktree and the branch, and notes the end in the board.
368
-
369
- #### What happens on its own, and what waits for you
370
-
371
- | On its own | After your approval | Never |
372
- | --- | --- | --- |
373
- | Starting a ready phase in a new or running lane (one line to you afterwards) | Commits, push and PR: one approval per PR, the documentation PR for a fresh plan included | Implementing lane work itself |
374
- | Answering a question the plan, the board or a rule settles (reported in one line) | Every open decision a lane raises | Merging, unless a project supplement names PRs it may merge or you picked self-merge for the PR |
375
- | Rebasing lanes after a merge, with their checks re-run | Database migrations and writes against a shared environment | Confirming a chained git command or storing a permission |
376
- | Writing your decisions into the plan or the board | Ending an agent, closing a lane, a hand-over to a fresh chat | Steering herdr from outside herdr |
99
+ In a project install, the `merge-guard` hook asks you before `gh pr merge`, `gh workflow run`, `gh run rerun` and a `gh api` write that merges, writes a file, dispatches or reruns a workflow, or moves a ref, also behind a chain, a wrapper or `sh -c`. The `git-guard` hook refuses a git command that hides a commit, push, rebase, reset, checkout or merge from your permission rules (`git -C … push`, `sh -c "…"`), and every force push, skipped hook and hard reset.
377
100
 
378
- Who merges follows the project's own rules: by default you, unless the project supplement (`.claude/project/orchestrate.md`) names PRs the coordinator may merge itself and under which conditions, or you picked self-merge for that PR.
379
-
380
- #### Guarded commands and self-merge
381
-
382
- A merge or a release is the step nobody takes back.
383
-
384
- ##### What is guarded
385
-
386
- In a project install the `merge-guard` hook asks you before `gh pr merge` and `gh workflow run` (the usual production release) run. It recognises them when the command spells them out: behind options before the verb (`gh -R x pr merge`), a chain (`a && gh pr merge 12`), a wrapper, known or not (any command whose words contain the guarded words in this order, such as `mise exec -- gh pr merge 12`), `sh -c` and `eval`. It shows the dialog even in auto mode. A quoted text that names the words, such as a commit message, passes, and so does everything else; the hook prints nothing unless it asks.
387
-
388
- ```json
389
- {
390
- "guard": {
391
- "commands": ["gh pr merge", "gh workflow run"],
392
- "selfMerge": { "offer": true, "approvalMinutes": 180 }
393
- }
394
- }
395
- ```
396
-
397
- - `guard.commands` lists the guarded commands as words, a program and one to three plain words after it: letters, digits and `-`, no `:` and no option. These two are the default; `git push` is not on the list. An empty list switches the guard off.
398
- - `guard.selfMerge.offer` (default `true`) lets `/jh-orchestrate` offer "approve, and merge it yourself after green checks" in its PR question. With `false` the question has two options and the hook honours no such answer.
399
- - `guard.selfMerge.approvalMinutes` (default 180) is how long that answer lets `gh pr merge <number>` pass without a second dialog.
400
-
401
- ##### Self-merge
402
-
403
- You pick the second option of the PR question. The coordinator commits, pushes and opens the PR, waits for the checks in the background, merges with `gh pr merge <number> --squash --delete-branch` when they are green and tells you in one line; on a red check it does not merge and reports. The project supplement can block the option per approval tier.
404
-
405
- The hook does not take the agent's word for your answer. It reads the session's transcript, which Claude Code writes, and looks for a dialog result where you picked that exact option for a question naming one branch. Then it asks GitHub which branch pull request `<number>` carries and lets the merge pass if the two match, the PR is open and not from a fork, and the command is exactly the plain form: `gh pr merge <number>` with `--squash`, `--merge`, `--rebase` and `--delete-branch` (or `-s`, `-m`, `-r`, `-d`) as the only flags, run on its own in the directory you work in (no `-R`, no `--admin`, no chain, no wrapper). `gh workflow run` and every other guarded command ask every time. The approval covers the branch, not the commits you saw, for the configured minutes.
406
-
407
- ##### Merging without checks
408
-
409
- A pull request that only changes documents rarely needs its checks. List the paths in `merge.withoutChecks` (default empty, so every merge waits for the checks as before):
101
+ A merge passes without a question only on an answer from your own dialog: self-merge for that pull request, or the approval of its plan, with green checks and no file on a path that always asks (workflows, migrations, lockfiles, `package.json`, env files, agent instructions, and your own list). `merge.withoutChecks` lets a pull request that only touches such paths as docs merge without waiting for checks.
410
102
 
411
103
  ```json
412
104
  {
105
+ "guard": { "planApproval": { "exceptPaths": ["src/billing/**", "src/auth/**"] } },
413
106
  "merge": { "withoutChecks": ["docs/**/*.md"] }
414
107
  }
415
108
  ```
416
109
 
417
- A pattern is made of path words with `*` and `**`: no `..`, not absolute, no braces or negation, and at least one whole path segment without a wildcard, so `docs/**/*.md` is fine and `**`, `**/*.md` and `**/*.*` are refused. Keep the folder in the pattern: a `.md` file elsewhere can be an agent instruction (`CLAUDE.md`, a skill) that ships with a package and deserves its checks.
418
-
419
- With the list set, `/jh-orchestrate` lists a pull request's files with `gh pr diff <number> --name-only` before it waits for checks and merges at once when every file matches.
420
-
421
- The merge guard checks the same itself: for a plain `gh pr merge <number>` it asks GitHub for the pull request and then for its files in that repository (`gh api …/pulls/<number>/files`, which also names the old path of a renamed file). It does not ask you when the pull request is open, from this repository, GitHub lists as many files as the pull request changes (fewer than 3000) and every one matches a pattern. This works without a self-merge answer, also when `guard.selfMerge.offer` is `false`. No file, one file outside, a count that differs, a failed `gh` call or a pattern list it cannot read means it asks as before.
422
-
423
- ##### Limits
424
-
425
- The hook reads the command as written, so it does not see what a command runs behind its back: a script file or a project script (`bash ./release.sh`, `./release.sh`, `bun run release`, `make release`) that calls `gh workflow run`, a shell alias or function, `gh alias` and git aliases, text that is encoded or assembled at run time, and a wrapper that spells the words differently. Add the script runners you use to `guard.commands`, or protect the repository itself.
426
-
427
- The agent runs with your account, so it can also edit a transcript, drop the hook from `.claude/settings.json` or merge through `gh api`. The guard stops a merge or a release by mistake, by a misread instruction or a mistyped command; it does not stop an agent that sets out to get around it. For that, use branch protection and required reviews on the repository.
428
-
429
- - The merge-without-checks look is a snapshot: a push between its look and the merge is not seen.
430
- - A global install ships no hook, so there the skill text alone asks you.
431
- - If Bun cannot start (a process out of file descriptors, say), the hook cannot run and every Bash call is blocked, on purpose: a guard that cannot run does not wave commands through.
432
-
433
- ##### Dialogs inside a lane
434
-
435
- A permission dialog inside a lane the coordinator confirms itself only for the commit and push of the lane's own branch under its PR approval, and for the fetch, rebase and lock-file steps after a merge, always with the one-time option. Every other dialog goes to you.
436
-
437
- #### Approvals of a lane
438
-
439
- In a project install the `approval-gate` hook takes over the permission dialogs of a lane. A call that would ask for your approval inside a lane (an `ask` rule in the permissions, such as a database tool or a project CLI) is held: the hook writes it to `.jackherd/approvals/<id>.json` in the main checkout (ignored by git), the watcher reports `=== approval <lane> <id> <tool> · <call>`, and the coordinator shows you the whole call from that file and asks you in its own pane, with the mark `` `approval <id> <hash>` `` in the question (the hash of the call you were shown, so your answer approves that content and nothing the file says later) and the options *Allow this call* and *Deny this call* (in German *Aufruf erlauben* and *Aufruf ablehnen*). The hook reads your answer only from the coordinator's transcript, never from a file of the lane, and then lets the call run or denies it.
440
-
441
- - It waits up to `guard.approvals.waitMinutes` (1 to 9, default 8). Without an answer by then it denies the call with a note to retry it once approved; the retry of the same call passes at once when the answer is there.
442
- - An approval holds once, for exactly that call (lane, directory, tool and input), and for 30 minutes.
443
- - Outside a lane (no herdr, or not in a worktree under `worktreeDirectory`), for a call whose input exceeds 64 000 characters, and on any failure the hook decides nothing, and the dialog stays.
444
- - The lane's pane shows the dialog meanwhile; a click there decides instead of your answer to the coordinator. The hook keeps waiting after such a click and spends a later answer to the same request, which then decides nothing; a retry of the call meanwhile may then need a second question.
445
- - The hook runs from the lane's worktree (`.claude/hooks/approval-gate/`), so an edit to it there takes effect at the lane's next call.
446
- - The coordinator is any agent in the main checkout; its herdr name differs per repository. The limit is the merge guard's: an agent that runs with your account could write a transcript line itself.
447
-
448
- #### The watcher
449
-
450
- Jack does not sit and stare at every pane. He waits, and barks when something moves: a lane is blocked or done, a check turns red, a pull request gets feedback.
451
-
452
- **You do not start the watcher.** The coordinator starts `jh watch --stream` once under Claude Code's Monitor tool, which turns every line it prints into a message to the coordinator, and starts it again when the monitor ends after 30 minutes.
453
-
454
- Why it exists: the coordinator is a chat, and it only acts when something arrives in it. Without the watcher it would have to read every lane's pane in turn, or you would have to tell it that a lane is waiting. `jh watch` does the waiting instead and prints a report when there is something to do:
455
-
456
- - a lane's agent turned `blocked`, `done` or `idle`, or left herdr's list; a blocked lane adds the duty to vet its question. Under the lane's line stands its report file (`.claude/handover/<lane>-report.md` in its worktree: `report <path>` and its first 15 lines, a shorter head of 8 lines when it has one, plus every `## Questions` section from the first line the head leaves out, wherever it stands, under a `questions: <n>` line, within 20 lines and 1500 characters) when the lane wrote one since the last shown, else its screen without Claude Code's input box and status bar; a blocked lane always shows its whole screen, since the dialog is the news
457
- - a lane's pull request was merged (with a duty line per lane: rebase, next phase, or a reason), or its checks turned red or green; a red one names its failed checks and tests: `=== checks failed #<n> (<lane>): test (2 tests: search > finds a word, …)`. For that the watcher reads `gh run view <id> --log-failed` once per head commit (30 s, the last 2 MB, after the run has completed; a run still in progress is asked again at the next poll) and picks the lines `watch.checks.failedTestPattern` matches (default: Bun's `(fail) <name>`); without a match only the check names. The line is reported once per head commit and names tests only when the log was readable at that poll; the herd fills them in later
458
- - one of your own open pull requests on a branch no lane has, such as a documentation or `chore` PR the coordinator opened itself, was merged or its checks turned red or green: `=== checks failed #<n> (branch <name>)`; a merge asks every lane to rebase. The watcher finds them with one search for the pull requests of the account `gh` runs as (`--author @me`); the first sighting is a baseline
459
- - a deploy run ended, with its target, when the config sets `watch.deploy`; the run of a merged pull request without a lane names its number, `(push, #<n>)`
460
- - new feedback on a lane's open pull request: a review, an inline comment, a pull request comment, or one edited since (`watch.feedback`)
461
- - a deploy preview for a lane's new head commit, with its link (`watch.previews`)
462
- - a new or changed entry of an external service, such as a ticket comment, read by a command the config names (`watch.sources`)
463
- - `gh` became unreachable
464
- - npm has a newer jackherd than the one running: `=== update available <old> → <new>`, one line, once per version. The watcher asks npm once a day on the timestamp the `SessionStart` hook uses (`~/.jackherd/update-check.json`), so the two never ask twice a day or report twice; with nothing newer it prints nothing. The coordinator asks you and runs `jh update` only after your yes
465
- - a lane's context fill rose to `warn` or `alert` (`=== lane <name> alert 47 %`, with a duty line at `alert`: request the lane's hand-over at its next phase boundary), against the lanes' thresholds in `plans.context`
466
- - an idle lane's last answer came from a model its board row's model column does not name (`=== model <name> <id> · board Opus 5.5`, with a duty line: switch it with `/exit` and `claude --resume <session> --model <id>`, not `/model`). Every model the column names fits, written as a name (`Opus 5.5`) or a full id, matched by family and version, notes in brackets ignored; once per deviation, again only when the model changes
467
- - a lane's call waits for your approval (`=== approval <lane> <id> <tool> · <call>`, with a duty line naming the question's mark and options), see [Approvals of a lane](#user-content-approvals-of-a-lane)
468
- - the coordinator's own context fill rose to `warn` or `alert`, so it hands itself over in time
469
- - `lane added` / `lane removed` when a worktree with an agent or a board row appears or goes; a lane's helper agent, named `<lane>-<purpose>`, is watched with it
470
- - the heartbeat, a status line every 15 minutes (`--heartbeat`)
471
-
472
- Without `--board` the watcher chooses the board by the lanes it finds: the board whose table names the most found lanes (a worktree with a herdr agent of its name), then the most recently changed (last commit on the base branch, else the file's change time). The first line ends with the board it took (`armed: … board docs/plans/board-x/board.md`); when several boards were in question, the next line names the reason and the others, and `--board <path>` overrides. `jh herd` chooses the same way and shows that line with its errors.
473
-
474
- After a herdr server restart, panes come back as shells in their directories and the herd panes are gone. `jh herd --ensure` brings them back beside the coordinator and each lane. Nobody has to run it by hand: inside herdr the watcher runs it at its start, for every `lane added` and whenever a lane's agent appears, so a new lane gets its view too (a lane without its agent waits, so its shell stays free for the agent), and the kit's herdr plugin runs it after a server start (herdr's `[[startup]]` hook), once herdr answers for every pane (up to 10 s; the terminals take about a second) and with up to four tries 2 s apart. A pane herdr lists an agent for is never taken as a free shell, also in the seconds after a restart before the agent's process is back. The watcher prints a line per view it started; a failure prints `herd --ensure failed, still watching: <reason>` once and does not stop the watch. It finds the coordinator by its herdr name, else by the calling pane, else as the only agent in the main checkout.
475
-
476
- If the coordinator's herdr name is gone (a resumed session keeps the pane, not the name), the watcher reads the fill over its own pane (`HERDR_PANE_ID`) and the status line says `pane without a name, herdr agent rename "$HERDR_PANE_ID" <name>`. Without such an agent it says `no agent named <name>` instead of `null`. The watcher never renames an agent; `jh context --coordinator` reports the same on stderr.
477
-
478
- Lanes are found, not listed: without lane names the watcher watches every directory under the worktree directory whose name a herdr agent carries or a board row lists. Every report ends with `status:`, one line per lane (status, `context warn` or `context alert` once its fill passed a threshold, branch, PR with checks and the feedback reported since its last push, open phase with its starts-after entry) and one for the coordinator, which ends with `· board: <n> rows only in the checkout` while the board file of the main checkout has rows the base branch does not, a reminder to get them merged. While you have open pull requests without a lane, one more line follows, `other PRs: #<n> passed · #<m> failed`, newest first, at most five and `+<k>` for the rest. When neither a lane line nor that line changed since the last report it is the single line `status: unchanged · <coordinator line>`, and a heartbeat report always shows the whole block. When the installed kit changes version, the line after `=== kit updated <old> → <new>` names the changed sections of the orchestrate skill and the diff file (`orchestrate skill: § 4 changed · diff <path>`), so the coordinator reads the change, not the whole skill; `diff unavailable` sends it back to the whole file. Without `--stream` the watcher ends after one report, and its last line says so.
479
-
480
- `jh watch --heartbeat 0.05 search` in an example repository, without herdr agents and without a remote, so `gh` fails (the coordinator's name `coordinator` has no herdr agent either):
481
-
482
- ```text
483
- armed: search=no agent board docs/plans/board-search/board.md
484
- === gh unavailable: gh pr list: no git remotes found
485
- status:
486
- search no agent · no branch · no PR · open phase 1 Index (starts after: now)
487
- coordinator coordinator no value (no agent named coordinator)
488
- gh unavailable since 11:24:31
489
- ```
490
-
491
- As a stream (`--stream`) it keeps running, and the heartbeat is one line. A working lane shows its plan progress, `<lane> working: P12 7/12` (the lane's current phase and its ticked checkboxes, counted as the herd counts them), but only when the count moved since the last heartbeat; a lane that stays on one number says nothing, and you look for yourself. The plans are read only when a lane works:
492
-
493
- ```text
494
- armed: search=no agent board docs/plans/board-search/board.md
495
- === heartbeat 11:22:09 · search no agent
496
- === heartbeat 11:22:19 · search no agent
497
- stopped by SIGTERM
498
- ```
499
-
500
- **Monitor fallback.** Claude Code does not offer the Monitor tool on Amazon Bedrock, Google Cloud's Agent Platform (Vertex) or Microsoft Foundry (`CLAUDE_CODE_USE_BEDROCK`, `CLAUDE_CODE_USE_VERTEX`, `CLAUDE_CODE_USE_FOUNDRY`), nor with `DISABLE_TELEMETRY` or `CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC` set. There the coordinator runs the watcher without `--stream` as a background Bash call and starts it again after every report; the last line of each report says so when the environment shows one of those settings.
501
-
502
- **Start guard.** In a project install, the kit's `watch-guard` hook refuses a stream start in a plain Bash call, a one-shot start outside a background task and one sent off with `&`, where the reports would go unseen; a command that only names the watcher, such as a search or a commit message, passes. A global install ships no hook; there the skill text and the last line of a single report carry the rule.
503
-
504
- **State.** The words of the status line follow `responseLanguage`. Lock and state live in `$TMPDIR/jackherd-watch-<uid>/<repository>-<hash>/`: a new start replaces the watcher of the same repository, and coordinators in two repositories run side by side. The state is saved after every GitHub read and survives a restart, so a lane that stopped while no watcher ran fires at once.
505
-
506
- **Deploy reports** need the workflow and the environments that count as targets; without `watch.deploy` no deploy run is read:
507
-
508
- ```json
509
- { "watch": { "deploy": { "workflow": "deploy.yml", "environments": ["stage", "production"] } } }
510
- ```
511
-
512
- **Feedback reports** are on unless the config sets `"watch": { "feedback": false }`. With every GitHub read the watcher asks GitHub's REST API for the pull request comments and inline comments of the repository updated since its last read, and for the reviews of each open lane pull request, then reports what it has not seen yet, one event per pull request, author and form, with one line per entry (its file and line for an inline comment, the first line of text that is no Markdown heading):
513
-
514
- ```text
515
- === feedback #<n> (search) review-bot[bot] inline · 2 new
516
- src/index.ts:41 The cache key ignores the locale
517
- src/index.ts:88 Missing test for an empty query
518
- === feedback #<n> (search) review-bot[bot] comment edited · 1 new
519
- No issues found.
520
- ```
521
-
522
- The form is `review <state>` (`approved`, `changes requested`, `commented`), `inline`, `comment`, or either of the last two with `edited`; a review without text that only carries inline comments is left out, its comments report it. The account `gh` runs as is never reported, so the lanes' own answers do not wake the coordinator; neither are the logins in `watch.feedback.ignore`, for bots that comment on every push such as a status bot (case is ignored); a bot that links deploy previews belongs in `watch.previews.authors` instead, below:
523
-
524
- ```json
525
- { "watch": { "feedback": { "ignore": ["status-bot[bot]"] } } }
526
- ```
527
-
528
- A bot's comment that only says it found nothing is no feedback: a comment of a login ending `[bot]` whose text, whitespace collapsed, equals one of `watch.feedback.noFindingTemplates`, with no inline comment of the same author within five minutes. The default is the clean-run comment of the CI code review (`## Code review` / `No issues found. Checked for bugs and CLAUDE.md compliance.`); `[]` counts every bot comment. Its inline comments, the real findings, always count.
529
-
530
- A pull request seen for the first time, and every pull request on a first start without saved state, is a baseline: what it already carries is not reported. Seen entries and their last change are part of the saved state, so a restart reports nothing twice and an edited comment is reported again. A REST read that fails reports nothing and the next one reaches back over the gap. The cost is two calls per GitHub read plus one per open lane pull request, against GitHub's 5000 REST calls per hour.
531
-
532
- **Preview reports** are off unless the config sets `watch.previews`. Once a lane pushes a new head commit and its deploy preview is updated after it, the watcher reports the link, once per head commit:
533
-
534
- ```text
535
- === preview #<n> (search) https://<host>/ · 3f2c1a9
536
- ```
537
-
538
- Two sources, either or both. `deployments: true` reads GitHub's deployments of each head commit that has no preview reported yet: its newest deployment outside production and outside `watch.deploy.environments` counts, and once its latest status is `success` it names its `environment_url`. `authors` lists the logins whose pull request comments link a preview; their comments, reviews and inline comments are never feedback, also with `watch.feedback` off, so list a bot that only posts previews, not one that also reviews. The URL is the first group of `urlPattern` (or its whole match), else the first `https://` link in the comment; nothing else of the comment is passed on, and only an `https:` URL of at most 200 characters counts:
539
-
540
- ```json
541
- { "watch": { "previews": { "deployments": true, "authors": ["preview-bot[bot]"], "urlPattern": "Preview: (\\S+)" } } }
542
- ```
543
-
544
- An edit without a new head commit reports nothing, so a bot that updates one comment per push wakes the coordinator once per push. A pull request seen for the first time is a baseline, as with feedback, and the reported head is part of the saved state. Where a bot first edits its comment to say the build is running and the link is already in it, that edit is the one reported, before the preview is ready. An edit read before GitHub's list shows the new head counts for the old head, and the new head is reported only at the next edit. A head costs one deployment list and at most one status call per GitHub read, and after a report no deployment is read for that pull request until its next push; the comments come from the feedback read, so they cost no call of their own while feedback is on.
545
-
546
- **Source reports**: a ticket, an issue or a status page outside GitHub, set up with `/jh-follow` and reported as `=== source <name> …`. → [Following outside GitHub](#user-content-following-outside-github)
110
+ A global install ships no hooks, and the skills ask on their own.
547
111
 
548
- #### Hand-over to a fresh chat
549
-
550
- A long chat answers worse and gets cut off. Jack hands over to a fresh one before that, with a note of the state. Both sides hand over at a quiet moment, never in the middle of a task:
112
+ </details>
551
113
 
552
- - **A lane**, at a phase boundary: `/jh-implement` reads its own fill with `jh context`. At `warn` or `alert`, and with a next phase to go, it asks whether that phase runs in a new chat. The watcher reads the lanes' fills too, once a minute, and reports a rise; at `alert` the coordinator asks the lane for its hand-over at the end of its current phase, so a long phase cannot run past the threshold unnoticed, and the lane then writes it without asking. On yes it writes `.claude/handover/<lane>.md` (state, open points, next step, traps, links; ignored by git). The coordinator asks you, ends the agent, starts a new one with the same model in the same worktree, and gives it the hand-over file as its first prompt.
553
- - **The coordinator**, when the watcher reports `coordinator warn`: it records the state of every lane in the board's history, writes `.claude/handover/coordinator.md` and asks you. On yes it runs a script that waits until the coordinator is idle, sends `/clear`, and types `/jh-orchestrate <board>` with a pointer to the hand-over only once herdr shows a new session id for the pane. If any step fails, the script stops, logs the reason and the two inputs to type by hand to `.claude/handover/coordinator.log`, and shows a herdr notice.
114
+ ## In detail
554
115
 
555
- Decisions never live only in a hand-over note: they are in the plan or the board first.
116
+ <details>
117
+ <summary><b>Plans, boards and the skills</b></summary>
556
118
 
557
- #### Lane setup from the config
119
+ A **plan** is Markdown with phases, checkbox tasks, a model and a review gate per phase. A **board** lists the lanes: which phases each one works on, on which model, after what. A **lane** is one agent session in its own git worktree, and **Jack** is the session in the main checkout that runs `/jh-orchestrate`.
558
120
 
559
- - `commands.install` (default `bun install`, or what the lock file implies) installs a new lane's dependencies.
560
- - `commands.dev` (default `bun run dev`) runs as `PORT=<port> <dev>` in the lane's dev-server pane.
561
- - `lanes.copy` lists the untracked files every worktree needs (env files, say); with `lanes.copy` empty, the files `.worktreeinclude` lists are copied. Each file keeps its mode.
562
- - `lanes.lockFile` (default `bun.lock`) is the file a rebase takes from the base branch and regenerates.
563
- - `worktreeDirectory` (default `.claude/worktrees`) is where lane worktrees go: relative, below the repository root, without `..`; `jh sync` refuses anything else.
121
+ <picture>
122
+ <source media="(prefers-color-scheme: dark)" srcset="https://unpkg.com/jackherd/docs/skills-dark.svg">
123
+ <img src="https://unpkg.com/jackherd/docs/skills.svg" alt="/jh-grill hands decisions to /jh-plan; /jh-plan feeds /jh-orchestrate or a single /jh-implement; /jh-implement ends in /jh-pre-commit, which runs the reviewers; /jh-diagnose hands a red command to /jh-implement.">
124
+ </picture>
564
125
 
565
- In a repository that ignores `docs/`, plan and board exist only in the main checkout and the herd reads them there; list the plan in `lanes.copy` so the lane has it to work from and to tick off its own progress.
126
+ | Skill | Does |
127
+ | --- | --- |
128
+ | `/jh-start` | checks the setup and changes nothing |
129
+ | `/jh-grill` | questions a loose idea until every decision is settled |
130
+ | `/jh-plan` | writes the plan and proposes the board's rows |
131
+ | `/jh-implement` | builds and tests one phase and ticks it off; independent phases run as parallel subagents |
132
+ | `/jh-orchestrate` | runs a board's lanes in herdr, as Jack |
133
+ | `/jh-pre-commit` | runs `jh check` and the reviewers, proposes the commit message and asks before committing |
134
+ | `/jh-follow` | follows a ticket or a status page outside GitHub |
135
+ | `/jh-diagnose` | turns a bug into a failing command, then hands the fix on |
566
136
 
567
- The herdr plugin `zerodice0.worktree-bootstrap`, when enabled and configured in the main checkout, can copy files and run setup commands for new worktrees; the coordinator checks its result with the install command and a frozen lock file (`bun install --frozen-lockfile`, `pnpm install --frozen-lockfile`, `yarn install --immutable`, `npm ci`, derived from `commands.install`; an install command of your own runs as written) and does the step itself when it is missing.
137
+ The global install names the skills with a `jh-` prefix; a project install uses none, unless the project already has skills of the same names.
568
138
 
569
139
  </details>
570
140
 
571
141
  <details>
572
- <summary><b>Following outside GitHub</b></summary>
573
-
574
- ### Following outside GitHub
575
-
576
- Not everything that moves lives on GitHub. A product owner answers in the ticket, a vendor's status page turns red. Point Jack at it, and he keeps an ear on it:
577
-
578
- ```text
579
- /jh-follow PROJ-123
580
- ```
581
-
582
- `/jh-follow` asks what to follow, checks the tool and its login, shows you a dry run, and writes the source only after your yes.
583
-
584
- From then on [the watcher](#user-content-the-watcher) reads it every few minutes and barks at the coordinator when something changes; a ticket also shows as a badge beside its lane's plan. That needs herdr. Without it you can still set a source up and dry-run it.
585
-
586
- - [How a source works](#user-content-how-a-source-works)
587
- - [Recipes](#user-content-recipes)
588
-
589
- #### How a source works
590
-
591
- Source reports are off unless the config lists `watch.sources`. The watcher cannot use the MCP servers of a Claude Code session: their connections and logins (often OAuth in a browser) belong to the session. A source is a command instead, which prints JSON on stdout. It runs without a shell, in the main checkout, with your environment, so its token stays where it is. `/jh-follow` fills one of the recipes below for you.
592
-
593
- ```json
594
- {
595
- "watch": {
596
- "sources": [
597
- {
598
- "name": "tickets",
599
- "every": 5,
600
- "command": ["tickets-cli", "search", "--query", "key in (T-123)", "--json"],
601
- "items": ".issues",
602
- "map": {
603
- "id": ".key",
604
- "updatedAt": ".fields.updated",
605
- "title": ".fields.summary",
606
- "body": ".fields.comment.comments[-1].body",
607
- "author": ".fields.comment.comments[-1].author.name"
608
- },
609
- "ignoreAuthors": ["me"],
610
- "lane": "search"
611
- }
612
- ]
613
- }
614
- }
615
- ```
616
-
617
- ```text
618
- === source tickets (search) T-123 Decide the layout · updated
619
- Yes, take variant B.
620
- https://<host>/browse/T-123
621
- ```
622
-
623
- `items` is the path of the list in the output (without it the output is the list), `map` picks each entry's fields: `id` is required, `updatedAt` too unless a content field (`title`, `author`, `body`, `url`) or `reportOn` can tell a change, and `title`, `url`, `body`, `author` and `status` (a ticket's status, below) are optional; any further field you name (`priority`, `assignee`; a letter, then letters, digits and `_`) is read for `reportOn`. A path is `.` for the whole value or a chain of `.key`, `[n]` and `[-n]` (counted from the end), nothing more; for anything else, put a script in between.
624
-
625
- What counts as news: an unknown `id` reports `new`; `updated` only when what you read changed, a short hash over `title`, `author`, `body` and `url`. A change of the time alone (a label, a sprint, a status) wakes nobody; where none of the four is mapped, `updatedAt` is the content, and then it is required. An entry whose `author` is in `ignoreAuthors` (case ignored) is recorded but wakes nobody, so your own comment does not. The first successful run of a source is a baseline, and the saved state holds per source only each id with its hash (with `reportOn` also the values of those fields, which may name people, such as an assignee), the 1000 seen most recently, so a restart repeats nothing and an entry that falls out of a time window and comes back is not new. Everything one GitHub read finds goes into one report, at most `reportLimit` entries per source (`5`), the rest as one line `=== source tickets +3 more`. The body shows its first line with text that is no Markdown heading (a bot's `## Code review` is skipped; a body of headings only shows the first), at most 160 characters, and the URL only as an `https:` URL without user name or password. `every` is in minutes, at least 1, `5` by default; the sources run with the GitHub read, so at most once a minute, and after a restart each runs at once.
626
-
627
- **Report a status change.** `reportOn` names the mapped fields whose change reports, in place of the content hash: with `"reportOn": ["status"]` a ticket that moves from one column to the next wakes the coordinator, and a new comment alone does not. The report names each changed field with its old and new value, cut to 60 characters, `(empty)` for none, in one line; the body follows only when `body` is one of the changed fields. An unknown `id` still reports `new`, and `ignoreAuthors`, `reportLimit` and the one report per read stay as they are. `status` is just a field name: any mapped field but `id` may be listed, including `title` or `body`.
628
-
629
- ```json
630
- "map": { "id": ".key", "updatedAt": ".fields.updated", "title": ".fields.summary", "status": ".fields.status.name", "assignee": ".fields.assignee.displayName" },
631
- "reportOn": ["status", "assignee"]
632
- ```
633
-
634
- ```text
635
- === source tickets (search) T-123 Decide the layout · updated: status In progress → Ready for Dev, assignee (empty) → alex
636
- https://<host>/browse/T-123
637
- ```
638
-
639
- At most 10 fields. For this the saved state keeps the value of each `reportOn` field on one line, as the report shows it (up to 200 characters, a longer one as its first 180, `…` and a hash of the whole, so any change shows). A field added to `reportOn` later, or a source that gains `reportOn`, starts from what it reads then and reports nothing for that first read; a state from an earlier version is read as it is, and dropping `reportOn` reports nothing either.
640
-
641
- A run that ends with an exit code other than 0, runs past 30 seconds (then its whole process group is killed, a `curl` behind a script included), prints more than 1 MB, cannot be read, prints no JSON or not the configured shape reports once per streak, with the reason only and never stderr, which may carry a token. While it fails, the status block ends with `source tickets failing since 10:15:00`; the watcher and the other sources go on:
642
-
643
- ```text
644
- === source tickets failed: exit 1
645
- ```
646
-
647
- **Show a ticket beside its plan.** `"ticket": true` marks a source's entries as tickets. The herd then shows each one as a badge in the 📎 line of its lane's plan, right after `📄 Plan`: `📡 T-123 · In Progress`, linked to the ticket. A ticket belongs to the source's `lane`, and also to any lane whose board row, name or plan names its id as a whole word (`T-12` is not found in `T-123`); so one source listing all your open tickets works without `lane`. `map.status` is the status the badge shows; without it the badge shows only the key. A ticket whose last reported change is less than a day old is amber, and a folded lane then shows `📡 <n>` in amber before its bar, linked to the first new ticket. A lane shows at most three tickets, new ones first, then `+<n>`; a link chip of the plan with the same URL gives way to the badge. For this the saved state keeps, of a marked source only, each entry of its last successful run with its URL, its status (stored like a `reportOn` value) and the time its last change was reported. The badges come from the watcher's state like the pull request badges: without a running watcher or with a state older than three minutes there are none, and the header says `watcher off` or `state 4 min old`, also without a pull request. The [GitHub Projects recipe](#user-content-recipes) is a ticket source with a status.
648
-
649
- **How you hear about it.** The report goes to the coordinator, as a `=== source …` event of its watcher. It passes you one line with the entry, its gist and the link. A decision inside it, such as a product owner's answer, it takes over only after you confirmed it, since the text comes from outside. You hear of it only while the coordinator's session and its watcher run; what happened in between is reported at the next start. There is no desktop notification of its own: you get the one herdr shows for any lane that needs you. What a source reports is a change in the mapped content fields (`title`, `author`, `body`, `url`), or with `reportOn` a change of the fields it names, such as a status.
650
-
651
- **Try a source before the watcher runs it.** `jh watch source <name> --dry-run` runs a configured source once; a draft not yet in the config comes as a file (`jh watch source draft.json --dry-run`, one source object) or on stdin (`-`). It lists the entries (`<id> · <author> · <title>`), says that the watcher's first run of the source is a baseline and reports nothing, and shows one entry as a later report would print it; it neither reads nor changes the watcher's state. A field of `map` that is empty in every entry is named with its path (`map.updatedAt: in 23 of 23 entries empty (.fields.updated)`), read from the raw output, so a run that failed on a missing `id` or `updatedAt` names it too; the dry run then ends with exit code 1, as for a failed run, and `/jh-follow` does not write the source. Without entries it says `0 entries: map not checked`. `--raw` adds the command's output, for finding the paths of `items` and `map`:
652
-
653
- ```text
654
- dry run source tickets: ["tickets-cli","search","--query","key in (T-123)","--json"]
655
- 2 entries
656
- T-123 · alex · Decide the layout
657
- T-124 · me · Pick a colour (ignoreAuthors)
658
- first run of this source: baseline, nothing reported; later a new or changed entry reports like this:
659
- === source tickets (search) T-123 Decide the layout · new
660
- Yes, take variant B.
661
- watcher state not read or changed; the watcher takes a new source over at its next start
662
- ```
663
-
664
- The watcher reads `watch.sources` when it starts: a new or changed source counts from its next start.
665
-
666
- Three limits stand: the watcher cannot make a command read only, so name only commands that read. A source in `.jackherd/config.json` runs for everyone who starts the watcher in the repository, at the same level of trust as `check.checks` and hooks. The watcher reads the config of the main checkout, never the one of a lane's worktree; a dry run started inside a worktree reads that worktree's config first. And `jh watch source … --dry-run` runs whatever command its draft names, so it never belongs in a permission allowlist.
667
-
668
- #### Recipes
669
-
670
- Each recipe is one `watch.sources` entry; put in your key, project or query, then try it with `--dry-run` (and `--raw` where the shape differs from what is shown). A token never goes into an argument: an argument shows in the process list. Where a tool keeps its own login, it is used; where a token sits in an environment variable, a small TypeScript script run by Bun (which runs wherever `jh` does) puts it from `process.env` straight into the request header. A script fails with an exit code, never with an empty answer the watcher would take for a source with nothing new, and it prints neither the token nor the body of a failed answer. The closing `export {}` makes the file a module, so the type check accepts top-level `await` and the names do not clash between scripts. A recipe that needs no script (`gh …`, `curl` on a public page, `curl --netrc`) stays a direct command. The watcher starts a source without a shell and reads `bun` as the first word to mean the Bun that runs the watcher itself, so the entry needs no absolute path, which would differ from machine to machine, even when Bun is not on the `PATH` (installed in `~/.bun/bin` only, say).
671
-
672
- **Jira with the Atlassian CLI.** Login: `acli jira auth login`, kept by `acli`. `acli jira workitem search` cannot return `updated` or `comment`, so a script searches for the keys and reads each one with `view`, a few at a time to stay well under the 30 seconds a source may run. `plainText` joins the text and mention nodes of the comment's document tree into plain text; it is a pure function, so you can test it on its own. The script, for example `scripts/sources/jira.ts`, with your project in the query and `<host>` your `<site>.atlassian.net`:
673
-
674
- ```ts
675
- // scripts/sources/jira.ts
676
- const jql = 'project = PROJ AND assignee = currentUser() AND statusCategory != Done'
677
- const host = '<host>'
678
- const parallel = 6
679
-
680
- type DocumentNode = { type?: string; text?: string; attrs?: { text?: string }; content?: DocumentNode[] }
681
- type IssueComment = { author?: { displayName?: string }; body?: DocumentNode }
682
- type Issue = { key: string; fields: { updated: string; summary: string; comment?: { comments: IssueComment[] } } }
683
-
684
- const plainText = (node: DocumentNode): string =>
685
- node.type === 'text' || node.type === 'mention'
686
- ? (node.text ?? node.attrs?.text ?? '')
687
- : (node.content ?? []).map(plainText).join('')
688
-
689
- const acli = async (args: string[]): Promise<unknown> => {
690
- const child = Bun.spawn(['acli', ...args], { stdout: 'pipe', stderr: 'ignore', signal: AbortSignal.timeout(20_000) })
691
- const output = await new Response(child.stdout).text()
692
- if ((await child.exited) !== 0) process.exit(1)
693
- try {
694
- return JSON.parse(output)
695
- } catch {
696
- return process.exit(1)
697
- }
698
- }
699
-
700
- const entryOf = ({ key, fields }: Issue) => {
701
- const latest = fields.comment?.comments.at(-1)
702
- return {
703
- key,
704
- url: `https://${host}/browse/${key}`,
705
- updated: fields.updated,
706
- summary: fields.summary,
707
- author: latest?.author?.displayName ?? null,
708
- body: latest?.body ? plainText(latest.body) : null,
709
- }
710
- }
711
-
712
- const keys = ((await acli(['jira', 'workitem', 'search', '--jql', jql, '--limit', '100', '--json'])) as { key: string }[]).map(
713
- ({ key }) => key,
714
- )
715
- const entries = []
716
- for (let start = 0; start < keys.length; start += parallel) {
717
- const batch = keys.slice(start, start + parallel)
718
- const issues = await Promise.all(
719
- batch.map((key) => acli(['jira', 'workitem', 'view', key, '--fields', 'summary,updated,comment', '--json'])),
720
- )
721
- entries.push(...(issues as Issue[]).map(entryOf))
722
- }
723
- console.log(JSON.stringify(entries))
724
-
725
- export {}
726
- ```
727
-
728
- A failed `acli` call ends the script with exit code 1, which the watcher reports as a failing source; nothing is printed that it could take for an empty list.
729
-
730
- ```json
731
- {
732
- "name": "tickets-decisions",
733
- "command": ["bun", "scripts/sources/jira.ts"],
734
- "map": {
735
- "id": ".key",
736
- "updatedAt": ".updated",
737
- "title": ".summary",
738
- "url": ".url",
739
- "body": ".body",
740
- "author": ".author"
741
- }
742
- }
743
- ```
744
-
745
- **Jira over REST with `curl`.** One call returns everything, where the CLI needs one call per item, but it needs an API token. Token: an API token of your Atlassian account in `~/.netrc` (`machine <site>.atlassian.net login <email> password <api-token>`, mode 600), which `curl --netrc` reads without a shell; `<host>` is the same `<site>.atlassian.net`. Version 3 of the API returns a comment body as a document tree, so the path reaches into its first paragraph:
746
-
747
- ```json
748
- {
749
- "name": "tickets-decisions",
750
- "command": ["curl", "-fsS", "--max-time", "20", "--netrc", "-G", "https://<host>/rest/api/3/search/jql", "--data-urlencode", "jql=key in (PROJ-123)", "--data-urlencode", "fields=summary,updated,comment"],
751
- "items": ".issues",
752
- "map": {
753
- "id": ".key",
754
- "updatedAt": ".fields.updated",
755
- "title": ".fields.summary",
756
- "body": ".fields.comment.comments[-1].body.content[0].content[0].text",
757
- "author": ".fields.comment.comments[-1].author.displayName"
758
- }
759
- }
760
- ```
761
-
762
- **Linear over GraphQL.** Token: a personal API key in `LINEAR_API_KEY`, sent as `Authorization: <key>` without `Bearer`. The script, for example `scripts/sources/linear.ts`, with `<host>` the API host `api.linear.app`. Linear answers a bad key or query with HTTP 200 and an `errors` list, so the script checks for it as well:
763
-
764
- ```ts
765
- // scripts/sources/linear.ts
766
- const token = process.env.LINEAR_API_KEY
767
- if (!token) process.exit(1)
768
-
769
- const query = `{ issues(filter: { team: { key: { eq: "ENG" } }, number: { eq: 123 } }) {
770
- nodes { identifier title url updatedAt comments { nodes { body user { name } } } } } }`
771
-
772
- const response = await fetch('https://<host>/graphql', {
773
- method: 'POST',
774
- headers: { Authorization: token, 'Content-Type': 'application/json' },
775
- body: JSON.stringify({ query }),
776
- signal: AbortSignal.timeout(20_000),
777
- })
778
- if (!response.ok) process.exit(1)
779
- const answer = (await response.json()) as { errors?: unknown }
780
- if (answer.errors) process.exit(1)
781
- console.log(JSON.stringify(answer))
782
-
783
- export {}
784
- ```
785
-
786
- ```json
787
- {
788
- "name": "linear-eng-123",
789
- "command": ["bun", "scripts/sources/linear.ts"],
790
- "items": ".data.issues.nodes",
791
- "map": {
792
- "id": ".identifier",
793
- "updatedAt": ".updatedAt",
794
- "title": ".title",
795
- "url": ".url",
796
- "body": ".comments.nodes[-1].body",
797
- "author": ".comments.nodes[-1].user.name"
798
- }
799
- }
800
- ```
801
-
802
- Check with `--raw` which end of `comments.nodes` holds the newest comment; `[0]` instead of `[-1]` if it is the first.
803
-
804
- **GitHub issues.** Login: `gh auth login`, kept by `gh`:
805
-
806
- ```json
807
- {
808
- "name": "decisions",
809
- "command": ["gh", "issue", "list", "--repo", "<owner>/<repo>", "--label", "decision", "--json", "number,title,url,updatedAt,comments"],
810
- "map": {
811
- "id": ".number",
812
- "updatedAt": ".updatedAt",
813
- "title": ".title",
814
- "url": ".url",
815
- "body": ".comments[-1].body",
816
- "author": ".comments[-1].author.login"
817
- },
818
- "ignoreAuthors": ["<your-login>"]
819
- }
820
- ```
821
-
822
- **GitHub Projects.** Reads the cards of a project board as tickets, without a script. Login: `gh` needs the `project` scope, `gh auth refresh -s project`. `<n>` is the project's number, `<owner>` its organization or `@me`; without `--limit` `gh` returns only 30 cards. Each card is `items[]` with its `id`, `title`, `content` (the issue or pull request, `content.url`; a draft card has none, so its badge has no link) and one key per project field, the name with a lower-case first letter, so the `Status` field is `.status` and holds the option's name. A card carries no time of its own, so the entry maps no `updatedAt` and a change of the title or URL is the change. The cards belong to a lane through `lane`, since their ids (`PVTI_…`) are not what a plan names:
823
-
824
- ```json
825
- {
826
- "name": "board",
827
- "command": ["gh", "project", "item-list", "<n>", "--owner", "<owner>", "--format", "json", "--limit", "200"],
828
- "items": ".items",
829
- "ticket": true,
830
- "lane": "<lane>",
831
- "map": { "id": ".id", "title": ".title", "url": ".content.url", "status": ".status" }
832
- }
833
- ```
834
-
835
- jackherd only reads the board; the status moves by the project's own workflows (Settings → Workflows: "Item closed" and "Pull request merged" set Done, "Auto-add" brings new issues in), and a pull request moves its issue's card only when its description links it with `Closes #<n>`; otherwise the status stays where it was. Add `"reportOn": ["status"]` to be told of each move as well.
836
-
837
- **Sentry.** Token: an auth token with `event:read` in `SENTRY_AUTH_TOKEN`; `<host>` is `sentry.io` or your own. The script, for example `scripts/sources/sentry.ts`:
838
-
839
- ```ts
840
- // scripts/sources/sentry.ts
841
- const token = process.env.SENTRY_AUTH_TOKEN
842
- if (!token) process.exit(1)
843
-
844
- const response = await fetch(
845
- 'https://<host>/api/0/projects/<org>/<project>/issues/?query=is:unresolved&statsPeriod=24h',
846
- { headers: { Authorization: `Bearer ${token}` }, signal: AbortSignal.timeout(20_000) },
847
- )
848
- if (!response.ok) process.exit(1)
849
- console.log(JSON.stringify(await response.json()))
850
-
851
- export {}
852
- ```
853
-
854
- ```json
855
- {
856
- "name": "errors",
857
- "every": 10,
858
- "command": ["bun", "scripts/sources/sentry.ts"],
859
- "map": { "id": ".id", "updatedAt": ".lastSeen", "title": ".title", "url": ".permalink", "body": ".culprit" }
860
- }
861
- ```
862
-
863
- `lastSeen` moves with every event, but title, culprit and link do not, so only a new issue wakes the coordinator, not each occurrence.
864
-
865
- **A status page.** A page on the common status page service answers its public API without a token; `<host>` is the page's own:
866
-
867
- ```json
868
- {
869
- "name": "provider-status",
870
- "every": 5,
871
- "command": ["curl", "-fsS", "--max-time", "20", "https://<host>/api/v2/incidents/unresolved.json"],
872
- "items": ".incidents",
873
- "map": { "id": ".id", "updatedAt": ".updated_at", "title": ".name", "url": ".shortlink", "body": ".incident_updates[0].body" }
874
- }
875
- ```
142
+ <summary><b>Which model runs each phase</b></summary>
876
143
 
877
- The newest update stands first in `incident_updates`, so each new update of an open incident reports `updated` with its text.
144
+ Each phase in the plan names its model: the planning model (Opus by default) where a mistake is expensive, such as data, security or a migration, and the cheaper implementation model (Sonnet by default) for everything else. `/jh-implement` and Jack start each phase on its model, and in a project install a hook stops a phase handed to a weaker one. Set the models per role under `models` in the config.
878
145
 
879
146
  </details>
880
147
 
881
148
  <details>
882
- <summary><b>Herd and context</b></summary>
883
-
884
- ### Herd and context
149
+ <summary><b>The watcher and tickets outside GitHub</b></summary>
885
150
 
886
- `jh herd` shows the whole herd at a glance: every lane of a board with its plan progress, and with herdr also its agent's state, pull request, checks and context fill. It only reads and starts nothing; `/jh-orchestrate` runs the lanes and opens the herd beside the coordinator and one per lane (`--lane <name>`). Keep it open in a terminal or a herdr pane beside your sessions; `jh plans` still works as an alias.
151
+ Jack doesn't watch the terminals himself. `jh watch`, which he starts himself, wakes him when a lane is blocked or done, a check turns red, a pull request gets feedback, a lane's context fills up or a lane waits for an approval; deploys and previews too, when configured.
887
152
 
888
- - [What it shows](#user-content-what-it-shows)
889
- - [Attachments](#user-content-attachments)
890
- - [Where the numbers come from](#user-content-where-the-numbers-come-from)
891
- - [Keys](#user-content-keys)
892
- - [Context fill](#user-content-context-fill)
153
+ `/jh-follow PROJ-123` adds a ticket, an issue or a status page: it checks the tool and its login, shows you a dry run and writes the source to `watch.sources` after your yes. The [recipes](https://unpkg.com/jackherd/recipes/README.md) cover Jira, Linear, GitHub issues and Projects, Sentry and status pages. The reports need herdr; a source can be set up and dry-run without it.
893
154
 
894
- #### What it shows
895
-
896
- One block per lane of the chosen board, lanes that wait on you first:
155
+ </details>
897
156
 
898
- - the agent's state, a family of circles in one column: `● working`, `◆ blocked` in amber (waits for you), `◉ done` (the agent finished its answer, not the lane), `○ idle`, or `·` no agent
899
- - which lanes are listed at all. A lane with an agent or an open pull request is drawn in full. A lane with a worktree but no agent is listed under `· no agent`. A lane without a worktree is listed under `· waiting`, with what it starts after, when a phase it names is still open in its plan or, if it names no plan, when its "starts after" cell says something other than "now" (`sofort`); otherwise it is finished and not shown. A lane whose name cell carries the cleaned-up mark (`abgeräumt`) is finished whatever its plan says. The board needs no extra column for this
900
- - how full the agent's context is (`🥣`), and a bar with the lane's ticked tasks (`◐`)
901
- - the branch, commits ahead and uncommitted files
902
- - the lane's open pull request with its checks
903
- - per plan its attachments as chips (`📎`), the lane's phases with their counts, and the next open task of the current phase (`▶`). A finished phase is a closed row of its own with a green `✓` and a green `n/n`; a phase whose only open box is the documentation box of its review gate reads `docs open` instead of n-1/n.
904
- - The current phase is the one whose checkboxes moved last: on every re-read the herd compares each lane's ticks with the read before, and when exactly one phase changed, that phase becomes current and stays so until another single phase moves. On the first read, after a read where several phases changed at once (a merge, say), or when the lane does not show the moved phase, the newest board row decides, as follows. The memory lives only in the running `jh herd`, so a restart starts again from the board. The newest board row names the current phase this way: an open phase whose ticks in the worktree differ from the base branch, else one it ticked to the end there, shown as `ticked, not merged` until its ticks reach the base branch (in the sidebar title and the watcher's status line too), else its first open phase. A phase the newest row repeats from an earlier row counts as named. Ticks in older phases, say from tidying the plan, decide only when the newest row names no phase or only merged ones: then the same order runs over all rows, and without a touched phase the open phase of its newest row counts
157
+ <details>
158
+ <summary><b>The herd view</b></summary>
905
159
 
906
- Four lanes of an example board, three with herdr agents and one without, rendered by the package's own layout from example data at 80 columns. `search-ui` waits on you; its P2 is finished, its P3 lacks only the documentation box, and its plan carries a ticket, a mockup and a Figma link. Two lanes have a pull request:
160
+ `jh herd` shows every lane of a board: agent state, plan progress, pull request, checks and context fill. It only reads.
907
161
 
908
162
  ```text
909
163
  🐾 Board 01 11:38
@@ -916,7 +170,7 @@ Four lanes of an example board, three with herdr agents and one without, rendere
916
170
  │
917
171
  ├─ ✓ P2 Search box 4/4
918
172
  ├─ ▸ P3 Docs docs open
919
- │ └─ ▶ Dokumentation aktualisiert (README)
173
+ │ └─ ▶ Docs updated (README)
920
174
  └─ ▸ P4 Review 0/2
921
175
  ────────────────────────────────────────────────────────────────────────────────
922
176
  ▸ ◉ checkout-fee done ↗ #42 💬 2 🥣 48 % │ ██████████ ◐ 6/6
@@ -934,237 +188,44 @@ Four lanes of an example board, three with herdr agents and one without, rendere
934
188
  ↑↓ select →← open a show ticked s html q quit ? help
935
189
  ```
936
190
 
937
- Right after `init`, on the example plan and board, without herdr and without a remote, both lanes have no agent yet and land in one group; `search-index` has 2 of its 5 boxes ticked (tasks and review gate), `search-ui` none. This is `herd --once`:
938
-
939
- ```text
940
- 🐾 Board 01 11:38:08
941
- ───────────────────────────────────────────────────────────────────────────────
942
- ───────────────────────────────────────────────────────────────────────────────
943
- · no agent (2) search-index 2/5 · search-ui 0/5
944
- ───────────────────────────────────────────────────────────────────────────────
945
- ▸ planned (3 phases in 1 plan)
946
- gh pr list: no git remotes found
947
- origin/main not found, reading plans from the checkout
948
- ───────────────────────────────────────────────────────────────────────────────
949
- ↑↓ select →← open a show ticked s html q quit ? help
950
- ```
951
-
952
- #### Attachments
953
-
954
- Under each plan's title, a `📎` line carries the plan's links as chips; a click opens one in the browser:
955
-
956
- - `📄 Plan` opens the board's HTML page at that plan: its status, its chips and its phases with their checkboxes. The herd writes the page when it is missing or the plans changed.
957
- - Links of a known kind become chips of their own: `✦` a Claude artifact (a mockup, say), `◈` Figma, `⑂` a GitHub pull request or issue. The chip's text is the link text, else the bold label before the link (`**Mockup:** <url>`), else the kind's name. A link on a line that says it is private carries that mark.
958
-
959
- Nothing is fetched to find them; the kinds are URL patterns, and `plans.linkKinds` in the config replaces them with your own (`pattern`, `label`, `icon`).
960
-
961
- #### Preview and feedback
962
-
963
- A pull request row reads `↗ #<n> ✓ checks` (red: the failed checks, `✗ test, lint`, at most three and `+n`; under a lane's pull request up to three failed tests and `+n`), then the watcher's badges, then the title, cut with `…` beside the badges and wrapped under itself without them; the number is a grey badge linked to the pull request. While the watcher runs (`jh watch`), `🌐 preview` opens the deploy preview the watcher reported for the pull request's current head (`watch.previews`), `💬 <n> new` in amber opens the pull request and counts the feedback since its last push that `watch.feedback` does not ignore, and `✋ changes requested` in dark red opens it while a reviewer's latest decisive review requests changes, which a push does not end; an ignored author counts there, since their review blocks the merge all the same. Only when number, checks and badges leave no room for a title do the badges move to a line below, starting in the column of the number. A folded lane shows each pull request as a badge, `↗ #42 💬 2` (amber while there is feedback, the count only above zero; `↗ #42 💬 2 ✋` in dark red while changes are requested), in one column right before the bar, the same for every lane. The words follow `responseLanguage` (`💬 3 neu`, `✋ Änderungen verlangt` in German). Everything comes from the watcher's saved state; the herd makes no GitHub call for it. Without a running watcher, with a state older than three minutes (three missed GitHub reads), after a push until the watcher reports the new head, or for a preview recorded before 0.26.0 there is no such badge; while a lane has an open pull request, the header then says why beside the clock: `watcher off` or `state 4 min old`.
964
-
965
- #### Where the numbers come from
966
-
967
- - **Plans and board** are read at `origin/<baseBranch>`, and a lane's own phases from its worktree once it has changes there. A phase no row of the lane names comes from the worktree too when it is new there or ticked further than on the base branch; one with fewer ticks there stays at the base, since the lane holds an old copy. Lanes and `↳` steps that only the checkout's board has are added to what the base branch says, so a row written a minute ago shows at once, without a mark: that they still wait for a merge is the coordinator's to track, and its watcher reminds it (`board: 2 rows only in the checkout` after its coordinator line). Rows both boards have, and a board missing on the base branch (read whole from the checkout, with its notice), are unchanged. The herd fetches the base branch in the background every 5 minutes (`plans.fetchIntervalMinutes`, `0` turns it off) and redraws when a plan file changes or a fetch lands.
968
- - **Pull requests and checks** come from `gh`. Without `gh` the herd runs and says why the PRs are missing.
969
- - **Agent state and context fill** come from herdr, with `"terminal": { "adapter": "herdr" }` in the config. Without it, every lane shows no agent.
970
- - **A running lane without a board row**, neither at the base branch nor in the checkout, shows as `no row in lanes`, without plan and phases.
971
-
972
- While it runs inside herdr, the herd also sets each lane's sidebar title (`P4 · 7/12`) and clears it on exit.
973
-
974
- #### Keys
975
-
976
- | Key | Does |
977
- | --- | --- |
978
- | `↑` `↓` | select a lane, phase or task |
979
- | `→` `←` | open or close the selected row |
980
- | `a` | list the ticked checkboxes one by one, or sum them up again |
981
- | `s` | write the board as one HTML page with every lane and plan, which also shows which lane waits for which, and open it |
982
- | click, `enter`, `space` | on a lane or phase: open or close it; on a task: show its whole text; on a chip, a badge or a `↗` pull request: open it in the browser |
983
- | `?` | the legend |
984
- | `q` | quit |
985
-
986
- Options: `--once` prints one frame and exits (for reading the herd from a script or a session), `--ensure` brings back the missing herd panes beside the coordinator and each lane of the board (an empty shell in that tab takes the herd, else the agent's pane is split) and prints what it did; the watcher does the same at its start, for every new lane and when a lane's agent appears (a lane without its agent waits), so a view is never started by hand, `--lane <name>` shows one lane with its current phase open, and a board path picks another board. Without one, the board is chosen as the watcher chooses it: the one that names the most lanes with a worktree and a herdr agent, then the most recently changed; the reason shows with the errors when several boards were in question.
987
-
988
- #### Context fill
989
-
990
- How full each session's context is, as a number.
991
-
992
- `jh context [<name>]` prints how full an agent's context is, as one line of JSON, or `null` without a value:
993
-
994
- ```text
995
- {"tokens":356307,"window":1000000,"share":0.356307,"model":"claude-opus-5-5","level":"warn"}
996
- ```
997
-
998
- The value is read from Claude Code's session log of the agent herdr reports, only from the last answer of the main chain; sub-agent answers do not count. When that log has no answer yet, the newest log of the agent's working directory is used. Session logs do not name the context window, so set `plans.context.windows` per model ID; otherwise 200 000 tokens are assumed (`plans.context.defaultWindow`). A fill above the assumed window moves to the next larger known window, so a session on a larger window is not shown as 100 %.
999
-
1000
- | Who | `warn` from | `alert` from | Config |
1001
- | --- | --- | --- | --- |
1002
- | Lanes | 60 % | 80 % | `plans.context.warnAt`, `alertAt` |
1003
- | Coordinator (`--coordinator`) | 50 % | 70 % | `plans.context.coordinator.warnAt`, `alertAt` |
1004
-
1005
- At `warn` a lane offers a fresh chat at its next phase boundary, and the watcher wakes the coordinator to hand itself over ([Hand-over](#user-content-hand-over-to-a-fresh-chat)).
1006
-
1007
191
  </details>
1008
192
 
1009
193
  <details>
1010
- <summary><b>Reference</b></summary>
1011
-
1012
- ### Reference
1013
-
1014
- - [Commands](#user-content-commands)
1015
- - [Configuration](#user-content-configuration)
1016
- - [What is included](#user-content-what-is-included)
1017
- - [Status line](#user-content-status-line)
1018
- - [A package you publish from the repository](#user-content-a-package-you-publish-from-the-repository)
194
+ <summary><b>Commands and configuration</b></summary>
1019
195
 
1020
- #### Commands
1021
-
1022
- | Command | What it does | Options |
1023
- | ------- | ------------ | ------- |
1024
- | `jh --version` | The installed version, e.g. `1.6.0`; `-v` is the same | none |
1025
- | `jh init` | Creates `.jackherd/config.json` and installs the kit | `--global`: into `~/.claude` and `~/.jackherd`; `--yes`/`--no`: answer all its questions (globally the hook and the status line, in a project the prefix and the status line) and take the default [setup](https://unpkg.com/jackherd/INSTALL.md); a status line of your own is replaced only by a yes in the dialog |
1026
- | `jh sync` | Updates the kit files; edited files get a `.kit-new` beside them | `--global`; `--accept <path>` takes the kit's version; `--global --if-changed` syncs only after a package update and names a newer npm release once a day |
1027
- | `jh update` | Installs the newest release with the package manager that holds the kit globally, then syncs `~/.claude` and the project with the new code | none; does nothing when the installed version is the latest |
1028
- | `jh config` | The values the current repository decides (`test`, `typecheck`, `lint`, `format`, `install`, `dev`, `lockFile`, `baseBranch`), as set or detected | `<name>`: one bare value |
1029
- | `jh check` | Types, lint and tests of the packages you touched, plus the project's own checks; in changed plans, an open checkbox whose `→ P<n>` is not at the end of the line fails, and so does a phase the change adds with tasks but without a review gate (or a `**No review gate:** <reason>` line); in a changed board, a lane row the change adds or edits whose phases cell names no `P<n>` warns; so does one whose phases cell is longer than 250 characters or records a side order of the user (`also (User …`), which belongs as a task in the plan's phase; a phase of a changed plan without a row in its dependency table warns; in a changed plan, a phase whose tasks are all ticked on `origin/<baseBranch>`, that the change does not tick further and that still has open boxes without `→ P<n>` warns | `--skip-slow`: lint and fast checks only; `--parallel <plan>`: only the groups of phases that can run side by side |
1030
- | `jh herd` | Live view of the board's lanes and plans ([Herd](#user-content-herd-and-context)) | `--once`: one frame and exit; `--lane <name>`: one lane; `--ensure`: bring back the missing herd panes beside the coordinator and each lane, and exit; a board path (default: the newest board) |
1031
- | `jh context` | How full an agent's context is, as one line of JSON, or `null` | `<name>`: the agent (default: this worktree's); `--coordinator`: the coordinator's thresholds |
1032
- | `jh watch` | Waits for the lanes and reports what the coordinator has to do ([The watcher](#user-content-the-watcher)) | `--stream`, `--heartbeat <minutes>`, `--board <path>`, `--coordinator <name>`, lane names (default: found) |
1033
-
1034
- Without a global install, run any of them as `bunx jackherd <command>`.
1035
-
1036
- #### Configuration
1037
-
1038
- `.jackherd/config.json` in a project, `~/.jackherd/config.json` for a global install; a project config always wins. Every command validates it against one schema, and a typo fails every command alike. A project `init` points `$schema` at `schemas/jackherd.schema.json` of the installed package, so an editor completes the keys.
1039
-
1040
- | Key | Default | What it decides |
1041
- | --- | --- | --- |
1042
- | `responseLanguage` | `en` | the language the skills speak with you, and the watcher's status words |
1043
- | `responseAddress` | `informal` | `informal` or `formal`: how replies address you where the language has both (German: `du` or `Sie`). `jh sync` writes it once into the kit rule `rules/kit/code-style.md`, which every session loads; it follows `responseLanguage`; the status words of the watcher and the herd use no pronoun and stay as they are |
1044
- | `planLanguage` | `en` | the language of plans and boards, including their headings |
1045
- | `planDirectory` | `docs/plans` | where plans and boards live |
1046
- | `board.pattern`, `board.file`, `board.term` | `board-*`, `board.md`, `board` | [Boards](#user-content-boards) |
1047
- | `worktreeDirectory` | `.claude/worktrees` | where lane worktrees go; relative, below the repository root |
1048
- | `baseBranch` | detected, else `main` | the branch lanes start from and plans are read at |
1049
- | `skillPrefix` | `''` (global: `jh-`) | put before every skill and agent name |
1050
- | `commands.test`, `typecheck`, `lint`, `format`, `install`, `dev` | detected from the lock file | what the skills and `check` run; `dev` runs as `PORT=<port> <dev>` |
1051
- | `lanes.copy` | `[]` (then `.worktreeinclude`) | untracked files copied into every new worktree |
1052
- | `lanes.lockFile` | detected, else `bun.lock` | the lock file a rebase takes from the base branch |
1053
- | `models.plan`, `implement`, `review`, `explore`, `orchestrate` | written by `init` | full model IDs per role; aliases are refused |
1054
- | `terminal.adapter` | `none` | `herdr` for agent states and context fill |
1055
- | `plans.fetchIntervalMinutes` | `5` | background fetch of the base branch while the herd runs |
1056
- | `plans.linkKinds` | built in | links in a plan the board page shows as chips |
1057
- | `plans.context` | lanes 60 % / 80 %, coordinator 50 % / 70 %, window 200 000 | [Context fill](#user-content-context-fill) |
1058
- | `watch.deploy` | none | the deploy workflow and environments the watcher reports |
1059
- | `watch.feedback` | `{ "ignore": [] }` | `false` turns feedback reports off; `ignore` lists logins whose feedback does not count; `noFindingTemplates`: bot comments that report no finding (default: the CI code review's clean run) |
1060
- | `watch.checks` | `{}` | `failedTestPattern`: the regular expression for a failed test in a red run's log, first group the name (default: Bun's `(fail) <name>`); no nested quantifiers |
1061
- | `watch.previews` | none | `deployments` (`false`), `authors` (`[]`) and `urlPattern`: where the watcher reads a lane's deploy preview |
1062
- | `watch.sources` | `[]` | commands whose JSON output the watcher reports as `source`: `name`, `command`, `every` (`5`), `items`, `map`, `ignoreAuthors` (`[]`), `reportLimit` (`5`), `lane`, `reportOn`, `ticket`; [recipes](#user-content-recipes) |
1063
- | `check.lintFilesCommand` | detected | lint over the changed files |
1064
- | `check.checks` | `[]` | project checks (`name`, `command`, optional `globs`) |
1065
- | `check.reviewerRoutes` | `[]` | which reviewer `/jh-pre-commit` runs for which paths (`reviewer`, `globs`, `why`) |
1066
- | `check.generatorOwned` | `[]` | files a generator owns (`glob`, `generator`); `check` names the generator when one of them changed |
1067
- | `check.buildArtifacts` | build output globs | staged files `check` refuses |
1068
- | `check.publicContent` | none | [published files](#user-content-a-package-you-publish-from-the-repository) |
1069
- | `check.looseTests` | `false` | also run tests outside any package |
196
+ | Command | Does |
197
+ | --- | --- |
198
+ | `jh init` | creates `.jackherd/config.json` and installs the kit (`--global` for `~/.claude`) |
199
+ | `jh sync` | updates the kit; a file you edited gets the new version beside it as `.kit-new` |
200
+ | `jh update` | installs the newest release and syncs |
201
+ | `jh check` | types, lint and tests of what you touched, plus the plan and board checks (`--skip-slow`, `--parallel <plan>`) |
202
+ | `jh herd` | the live view (`--once`, `--lane <name>`) |
203
+ | `jh watch` | the watcher Jack runs |
204
+ | `jh context` | how full an agent's context is, as JSON |
205
+ | `jh config` | the commands and the base branch the repository uses |
1070
206
 
1071
- What usually stays to configure is what no file in the repository says, for example:
207
+ Without a global install, run them as `bunx jackherd <command>`. The config is `.jackherd/config.json` in a project and `~/.jackherd/config.json` globally, checked against [one schema](https://unpkg.com/jackherd/schemas/jackherd.schema.json) your editor completes. What usually needs setting:
1072
208
 
1073
209
  ```json
1074
210
  {
1075
211
  "responseLanguage": "de",
1076
- "planLanguage": "de",
1077
- "skillPrefix": "jh-",
1078
- "lanes": { "copy": ["apps/web/.env"] },
1079
212
  "terminal": { "adapter": "herdr" },
1080
- "check": {
1081
- "reviewerRoutes": [{ "reviewer": "jh-uncle-bob", "globs": ["apps/**/*.ts", "apps/**/*.tsx"], "why": "Code" }]
1082
- }
213
+ "lanes": { "copy": ["apps/web/.env"] },
214
+ "check": { "reviewerRoutes": [{ "reviewer": "jh-uncle-bob", "globs": ["src/**/*.ts"], "why": "Code" }] }
1083
215
  }
1084
216
  ```
1085
217
 
1086
- #### What is included
1087
-
1088
- - **Workflow skills:** `start`, `grill`, `plan`, `implement`, `orchestrate`, `follow` ([Planning](#user-content-the-skills-and-what-they-hand-on)).
1089
- - **Review skills:** `pre-commit`, `review`, `uncle-bob`, `security-audit`, `devops`, `diagnose`.
1090
- - **Agents:** `plan`, `implement`, `explore`, `spec-review`, `uncle-bob`, `security-audit`, `devops`, `public-content`, each on the model you set per role.
1091
- - **Rules** in `.claude/rules/kit/`: `code-style`, `testing`, `pull-requests`, `writing-for-agents`, `arc42`, `architecture-files`.
1092
- - **Hooks** (project install only):
1093
- - `git-guard` refuses a git command that hides `commit`, `push`, `rebase`, `reset`, `checkout` or `merge` from your permission rules (`git -C … push`, `sh -c "…"`).
1094
- - `watch-guard` refuses a `jh watch` start whose reports would go unseen.
1095
- - `merge-guard` asks you before `gh pr merge` and `gh workflow run` run, when they are spelled out ([Guarded commands](#user-content-guarded-commands-and-self-merge)).
1096
- - `ask-hint` (event `Stop`) tells you when a turn ends in a question asked as running text and used no `AskUserQuestion` dialog. It only hints, never blocks, and stays silent in a lane session, which asks as a text block. Switch it off with `"hints": { "askUserQuestion": false }` in `.jackherd/config.json`.
1097
- - `format-on-edit` runs the project's own Biome on edited TS, JS and JSON files, when the project has it installed.
1098
- - **herdr plugin:** a herd pane and an action that opens it ([Installation](https://unpkg.com/jackherd/INSTALL.md)).
1099
-
1100
- Each kit skill reads `.claude/project/<skill>.md` first when it exists; that file extends the skill for the project and wins where the two disagree. A project rule beside `.claude/rules/kit/` extends the kit rule of the same name.
1101
-
1102
- #### Status line
1103
-
1104
- Model, context fill and session cost. `init` asks whether to enter `.claude/statusline.sh` in `.claude/settings.json` (`init --global`: `~/.jackherd/statusline.sh` in `~/.claude/settings.json`). A status line of your own is named and kept; only a yes to the question that names it replaces it, `--yes` does not. Its colours follow `plans.context` of the config it finds above the working directory (else `~/.jackherd/config.json`): the lane thresholds inside a worktree, the coordinator's elsewhere, yellow from 60 % and red with a `/compact` hint from 85 % without a config.
1105
-
1106
- #### A package you publish from the repository
1107
-
1108
- When the repository publishes a package, `check.publicContent` names its files, and a change to them gets two extra looks before the commit:
1109
-
1110
- ```json
1111
- { "check": { "publicContent": { "globs": ["packages/my-kit/**"], "exclude": ["**/*.test.ts"] } } }
1112
- ```
1113
-
1114
- - **`jh check`** reads the added lines against names it gathers on every run and never stores: the repositories beside this one (their names, their organisations from the remotes, the ticket keys in their branch names), this project's app folders, package scopes and the domains in its `.env*.example` files. A name the published files already use counts as their own word. A hit names file, line and kind (`names a neighbour repository`), never the name. More work directories and terms can come from `~/.jackherd/public-content.json` (`{ "workDirectories": ["~/work"], "terms": ["…"] }`), which lives outside every repository.
1115
- - **The `public-content` agent**, which `/jh-pre-commit` starts for such a change, reads only the diff, as a stranger reads the package, and reports what gives away a project, a client, a team or a person. A global install names it `jh-public-content` in `check.publicContent.reviewer`.
1116
-
1117
218
  </details>
1118
219
 
1119
220
  <details>
1120
221
  <summary><b>Troubleshooting</b></summary>
1121
222
 
1122
- ### Troubleshooting
1123
-
1124
- **`/jh-orchestrate` stops right away.** It runs only inside herdr: `command -v herdr` must find the binary and `HERDR_ENV` must be `1`. Start Claude Code in a herdr pane. For a single session, use `/jh-implement` instead; it still runs independent phases in parallel as subagents.
1125
-
1126
- **The herd shows every lane as `no agent`, and no context fill.** Set `"terminal": { "adapter": "herdr" }` in the config. A lane is matched to its herdr agent by name: the worktree directory, the board's lane name and the herdr agent must carry the same name.
1127
-
1128
- **A lane shows `no row in lanes`.** Neither the board at `origin/<baseBranch>` nor the checkout's board has a row for it. Add the row (the coordinator does this itself when it starts a lane). A row that only the checkout has is shown like any other, not as `no row in lanes`.
1129
-
1130
- **A lane cannot find its plan.** The plan is not on the base branch yet, and the lane's worktree was cut from there. A lane `/jh-orchestrate` starts gets it: before the first lane, the coordinator checks board and plans against `origin/<baseBranch>` and, after your yes, starts the lanes from a documentation branch that carries them. A worktree made by hand needs the plan merged first.
1131
-
1132
- **A lane shows no plan.** The herd finds a lane's plan only through the Markdown link in the row's plan column. Link the plan once it exists, in the row of every lane that implements one of its phases.
1133
-
1134
- **The herd says `origin/main not found, reading plans from the checkout`.** The remote has no such branch yet, or the base branch is another one. Check `jh config baseBranch`, and set `baseBranch` if detection could not decide (it falls back to `main` when `origin` has both `main` and `master`, or neither).
1135
-
1136
- **Pull requests and checks are missing.** They come from `gh`. The herd and the watcher run without it and print why (`gh pr list: no git remotes found`, say); install and authenticate `gh` for them.
1137
-
1138
- **A context fill of 100 %, or one that looks too high.** Session logs do not name the context window. Set `plans.context.windows` for your model IDs; otherwise 200 000 tokens are assumed.
1139
-
1140
- **`jh context` prints `null`.** No terminal adapter, no agent of that name in herdr, or no session log with an answer yet. `null` is no value, never `ok`.
1141
-
1142
- **The watcher's reports go unseen.** It must run under Claude Code's Monitor tool as `--stream`, or, where the session has no Monitor tool (Bedrock, Vertex, Foundry, or telemetry turned off), without `--stream` as a background call that the coordinator starts again after every report. The first line, `armed: …`, has to arrive as a Monitor event; a stream started in a plain Bash call never ends and is refused by the `watch-guard` hook in a project install.
1143
-
1144
- **`(watcher <pid> did not stop)`.** The old watcher survived 5 s and two run. Check with `ps -o command= -p <pid>` that it is the watcher, stop it, and start the stream again.
1145
-
1146
- **`=== kit updated <old> → <new>`, and the watcher ended.** The kit was updated under it. The coordinator reads its skill again and starts the stream again; nothing is lost, the state is on disk.
1147
-
1148
- **A `<file>.kit-new` appeared after `sync`.** You edited that kit file and the kit changed it too. Merge the two, or take the kit's version with `jh sync --accept <path>`; delete the `.kit-new` after merging by hand.
1149
-
1150
- **`sync --global` refuses to run.** A global install needs a `skillPrefix` (lower-case letters and digits ending in a dash, such as `jh-`), because a personal skill would otherwise shadow a project skill of the same name.
1151
-
1152
- **A project `init` or `sync` refuses the directory.** It refuses the home directory, which holds the global install. Run it in the project, or use `--global`.
1153
-
1154
- **The config is refused.** Every command validates the whole config and names the key. A model alias (`opus`) is refused in favour of a full ID (`claude-opus-5-5`); `sprintPattern` is refused since 0.13 in favour of [`board`](#user-content-boards).
1155
-
1156
- **`init --global` could not add the hook.** A `settings.json` it cannot read or merge into, or a symlink, stays as it is. The entry to add by hand is in [Installation](https://unpkg.com/jackherd/INSTALL.md).
1157
-
1158
- **`update` changes nothing.** The installed version is the latest, or the kit is a project dependency or a source checkout, which `update` leaves alone. Update the dependency and run `jh sync`.
1159
-
1160
- **The coordinator's hand-over did not continue.** The script aborts rather than type into the old chat. `.claude/handover/coordinator.log` names the reason and the two inputs to type by hand: `/clear` in the pane, then the line it logged.
1161
-
1162
- **A lane has no plan to tick in a repository that ignores `docs/`.** Plan and board then exist only in the main checkout. List the plan in `lanes.copy`, so each new worktree gets a copy to work from and tick.
1163
-
1164
- **`jh check` reports `Nothing changed` after a rebase.** The tree has nothing uncommitted. Run the type check and tests from each package root and the linter on `git diff --name-only origin/<baseBranch>...HEAD` instead; never `git reset --soft` to make the runner see the diff.
223
+ - **`/jh-orchestrate` stops right away.** It needs herdr: start Claude Code in a herdr pane, or use `/jh-implement`.
224
+ - **Every lane shows `no agent`.** Set `"terminal": { "adapter": "herdr" }`; worktree, board lane and herdr agent need the same name.
225
+ - **Pull requests and checks are missing.** Install `gh` and log in.
1165
226
 
1166
227
  </details>
1167
228
 
1168
229
  ## License
1169
230
 
1170
- The code is MIT. See [LICENSE](https://unpkg.com/jackherd@1.0.0/LICENSE); [NOTICE](https://unpkg.com/jackherd@1.0.0/NOTICE) lists the third-party material the kit files adapt. All rights to the artwork in [`assets/branding`](https://unpkg.com/jackherd@1.7.0/assets/branding/README.md) are reserved.
231
+ MIT, see [LICENSE](https://unpkg.com/jackherd/LICENSE). Four kit files adapt [mattpocock/skills](https://github.com/mattpocock/skills) (MIT, Copyright (c) Matt Pocock), see [NOTICE](https://unpkg.com/jackherd/NOTICE.txt). The artwork in [`assets/branding`](https://unpkg.com/jackherd/assets/branding/README.md) is all rights reserved.