jackherd 1.6.1 → 1.7.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +31 -0
- package/INSTALL.md +176 -0
- package/README.md +228 -312
- package/assets/branding/README.md +7 -3
- package/assets/branding/jackherd-logo-dark-1024.png +0 -0
- package/assets/branding/jackherd-logo-dark-512.png +0 -0
- package/assets/branding/jackherd-logo-dark.svg +1 -1
- package/assets/icons/bark-dark.svg +1 -0
- package/assets/icons/bark.svg +1 -0
- package/assets/icons/bowl-dark.svg +1 -0
- package/assets/icons/bowl.svg +1 -0
- package/assets/icons/decide-dark.svg +1 -0
- package/assets/icons/decide.svg +1 -0
- package/assets/icons/ear-dark.svg +1 -0
- package/assets/icons/ear.svg +1 -0
- package/assets/icons/lanes-dark.svg +1 -0
- package/assets/icons/lanes.svg +1 -0
- package/assets/icons/plan-dark.svg +1 -0
- package/assets/icons/plan.svg +1 -0
- package/assets/icons/space.svg +1 -0
- package/dist/{chunk-QJSXHHQ3.js → chunk-OBUWTQ6Z.js} +25 -0
- package/dist/config.d.ts +14 -3
- package/dist/config.js +111 -10
- package/dist/plan-format.d.ts +180 -77
- package/dist/plan-format.js +292 -93
- package/docs/lanes-dark.svg +50 -0
- package/docs/lanes.svg +50 -1
- package/docs/skills-dark.svg +65 -0
- package/docs/skills.svg +65 -1
- package/docs/workflow-dark.svg +138 -0
- package/docs/workflow.svg +135 -169
- package/herdr-plugin/herdr-plugin.toml +6 -6
- package/herdr-plugin/open-plans.ts +1 -1
- package/herdr-plugin/restore-plans.ts +7 -7
- package/install.sh +1 -1
- package/kit/hooks/hooks/approval-gate/decision.ts +61 -0
- package/kit/hooks/hooks/approval-gate/herdr-panes.ts +24 -0
- package/kit/hooks/hooks/approval-gate/index.ts +69 -0
- package/kit/hooks/hooks/approval-gate/lane.ts +14 -0
- package/kit/hooks/hooks/approval-gate/request.ts +111 -0
- package/kit/hooks/hooks/approval-gate/run.ts +94 -0
- package/kit/hooks/hooks/approval-gate/settings.ts +26 -0
- package/kit/hooks/hooks/approval-gate/store.ts +70 -0
- package/kit/hooks/hooks/approval-gate/transcripts.ts +40 -0
- package/kit/hooks/hooks/merge-guard/approval.ts +11 -74
- package/kit/hooks/hooks/merge-guard/run.ts +2 -1
- package/kit/hooks/hooks/model-gate/agent-definition.ts +13 -0
- package/kit/hooks/hooks/model-gate/index.ts +58 -0
- package/kit/hooks/hooks/model-gate/run.ts +63 -0
- package/kit/hooks/hooks/model-log/index.ts +60 -0
- package/kit/hooks/hooks/model-log/run.ts +57 -0
- package/kit/hooks/hooks/model-log/store.ts +40 -0
- package/kit/hooks/hooks/model-log/transcript.ts +37 -0
- package/kit/hooks/hooks/shared/checkout.ts +22 -0
- package/kit/hooks/hooks/shared/command.ts +6 -0
- package/kit/hooks/hooks/shared/config.ts +21 -0
- package/kit/hooks/hooks/shared/dialog-answer.ts +5 -0
- package/kit/hooks/hooks/shared/dialog-answers.ts +74 -0
- package/kit/hooks/hooks/shared/files.ts +19 -0
- package/kit/hooks/hooks/shared/fresh.ts +4 -0
- package/kit/hooks/hooks/shared/json.ts +4 -0
- package/kit/hooks/hooks/shared/model-family.ts +48 -0
- package/kit/hooks/hooks/shared/plan-model-table.ts +41 -0
- package/kit/hooks/hooks/shared/private-directory.ts +29 -0
- package/kit/hooks/hooks/shared/repository-directory.ts +14 -0
- package/kit/hooks/hooks/shared/subagent-phase.ts +99 -0
- package/kit/hooks/hooks.json +25 -0
- package/kit/workflow/skills/follow/SKILL.md +4 -3
- package/kit/workflow/skills/implement/SKILL.md +6 -6
- package/kit/workflow/skills/orchestrate/SKILL.md +109 -27
- package/kit/workflow/skills/plan/SKILL.md +5 -5
- package/kit/workflow/skills/start/SKILL.md +2 -2
- package/package.json +4 -1
- package/schemas/jackherd.schema.json +43 -5
- package/src/check/command.ts +31 -4
- package/src/check/deferral-arrows.ts +1 -1
- package/src/check/parallel-report.ts +23 -0
- package/src/check/plan-structure-check.ts +120 -53
- package/src/cli.ts +8 -2
- package/src/config/legacy-names.ts +8 -0
- package/src/config/nested-quantifier.ts +73 -0
- package/src/config/repository-directory.ts +11 -0
- package/src/config/schema.ts +63 -8
- package/src/plan-format/board-prose.ts +36 -0
- package/src/plan-format/dependencies.ts +227 -0
- package/src/plan-format/documentation-gate.ts +16 -0
- package/src/plan-format/index.ts +23 -1
- package/src/plan-format/lanes.ts +35 -1
- package/src/plan-format/model-column.ts +52 -0
- package/src/plan-format/phases.ts +16 -1
- package/src/plan-format/plan-structure.ts +10 -28
- package/src/plan-format/progress.ts +7 -29
- package/src/plans/command.ts +3 -3
- package/src/plans/current-phase.ts +71 -0
- package/src/plans/ensure-panes.ts +2 -2
- package/src/plans/herd-command.ts +4 -0
- package/src/plans/lane-discovery.ts +14 -2
- package/src/plans/lane-tickets.ts +69 -0
- package/src/plans/layout.ts +182 -88
- package/src/plans/live.ts +5 -1
- package/src/plans/phase-models-read.ts +37 -0
- package/src/plans/phase-models.ts +70 -0
- package/src/plans/plan-panes.ts +8 -7
- package/src/plans/pull-requests.ts +78 -4
- package/src/plans/selection.ts +1 -40
- package/src/plans/settings.ts +9 -3
- package/src/plans/shown-with-more.ts +5 -0
- package/src/plans/snapshot.ts +82 -8
- package/src/plans/sprint-html.ts +2 -2
- package/src/plans/supervisor.ts +5 -4
- package/src/plans/symbols.ts +9 -6
- package/src/plans/texts.ts +30 -13
- package/src/plans/view.ts +1 -0
- package/src/sync/command.ts +16 -1
- package/src/sync/herdr-plugin.ts +1 -1
- package/src/sync/next-steps.ts +1 -1
- package/src/sync/render.ts +2 -1
- package/src/sync/setup-questions.ts +2 -2
- package/src/terminal/text.ts +4 -0
- package/src/terminal/types.ts +1 -1
- package/src/update/command.ts +4 -2
- package/src/update/update.ts +33 -9
- package/src/vocabulary/index.ts +39 -0
- package/src/watch/approvals.ts +96 -0
- package/src/watch/command.ts +154 -50
- package/src/watch/failed-tests.ts +52 -0
- package/src/watch/failure-read.ts +56 -0
- package/src/watch/failures.ts +107 -0
- package/src/watch/feedback-item.ts +21 -0
- package/src/watch/feedback-read.ts +2 -1
- package/src/watch/feedback.ts +37 -28
- package/src/watch/gh-process.ts +53 -0
- package/src/watch/gh.ts +10 -20
- package/src/watch/heartbeat-progress.ts +1 -1
- package/src/watch/lane-model.ts +54 -0
- package/src/watch/lane-parallel.ts +46 -0
- package/src/watch/lane-progress.ts +28 -4
- package/src/watch/lane-report.ts +62 -9
- package/src/watch/lane-sources.ts +1 -1
- package/src/watch/no-finding.ts +47 -0
- package/src/watch/plan-badges-read.ts +19 -7
- package/src/watch/plan-badges.ts +59 -7
- package/src/watch/plan-views.ts +5 -5
- package/src/watch/previews.ts +3 -9
- package/src/watch/pull-requests.ts +12 -2
- package/src/watch/recorded-reads.ts +25 -0
- package/src/watch/source-fields.ts +4 -0
- package/src/watch/source-tickets.ts +99 -0
- package/src/watch/sources.ts +62 -15
- package/src/watch/texts.ts +21 -1
- package/src/watch/watch-defaults.ts +8 -0
- package/src/watch/watch-events.ts +64 -4
- package/src/watch/watcher-lock.ts +1 -1
- package/src/watch/workflow-runs.ts +5 -7
- package/docs/lanes.mmd +0 -9
- package/docs/skills.mmd +0 -10
package/README.md
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
<p>
|
|
2
2
|
<picture>
|
|
3
|
-
<source media="(prefers-color-scheme: dark)" srcset="https://unpkg.com/jackherd@1.
|
|
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.
|
|
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">
|
|
5
5
|
</picture>
|
|
6
6
|
<br>
|
|
7
7
|
</p>
|
|
@@ -11,25 +11,32 @@
|
|
|
11
11
|
**Plan. Delegate. Ship.** Your agents do the work. Jack keeps them together.
|
|
12
12
|
|
|
13
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
|
|
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">
|
|
15
15
|
|
|
16
|
-
-
|
|
17
|
-
- ⚡ **Herds the lanes.** Each lane works in its own git worktree, so sessions never share a checkout.
|
|
18
|
-
- ❓ **Brings you only the decisions.** 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.
|
|
19
|
-
- 💬 **Barks when something moves.** One live view of every lane: state, progress, pull request, checks, with the preview link and new feedback as badges; `/follow` brings in tickets, issues and status pages outside GitHub.
|
|
20
|
-
- 🧠 **Watches the context.** It measures each agent session's fill and, before it is full, offers a fresh chat that takes over the state.
|
|
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>
|
|
21
17
|
|
|
22
|
-
|
|
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>
|
|
23
19
|
|
|
24
|
-
|
|
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>
|
|
25
21
|
|
|
26
|
-
<
|
|
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>
|
|
27
23
|
|
|
28
|
-
<
|
|
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>
|
|
29
25
|
|
|
30
|
-
|
|
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>
|
|
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.
|
|
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)).
|
|
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>
|
|
36
|
+
|
|
37
|
+
<sub>Schematic of one herdr window; the herd pane is drawn by jackherd itself from example data.</sub>
|
|
31
38
|
|
|
32
|
-
|
|
39
|
+
## Quickstart
|
|
33
40
|
|
|
34
41
|
1. **Install** (Linux and macOS):
|
|
35
42
|
|
|
@@ -37,7 +44,13 @@ Plans and boards are Markdown files in your repository, nothing is hidden. [herd
|
|
|
37
44
|
curl -fsSL https://unpkg.com/jackherd/install.sh | sh
|
|
38
45
|
```
|
|
39
46
|
|
|
40
|
-
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. → [
|
|
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
|
+
Already in a coding agent? Paste this instead:
|
|
50
|
+
|
|
51
|
+
```text
|
|
52
|
+
Install jackherd by following https://unpkg.com/jackherd/INSTALL.md. Ask me the setup questions it lists; herdr is optional.
|
|
53
|
+
```
|
|
41
54
|
2. **Start Claude Code in a repository and run:**
|
|
42
55
|
|
|
43
56
|
```text
|
|
@@ -68,15 +81,15 @@ Plans and boards are Markdown files in your repository, nothing is hidden. [herd
|
|
|
68
81
|
```
|
|
69
82
|
|
|
70
83
|
It builds one phase; ask for several phases and the independent ones run in parallel as subagents in the same checkout.
|
|
71
|
-
5. **Follow progress.** `/jh-orchestrate` opens the
|
|
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:
|
|
72
85
|
|
|
73
86
|
```bash
|
|
74
|
-
jh
|
|
87
|
+
jh herd
|
|
75
88
|
```
|
|
76
89
|
|
|
77
|
-
With herdr, the
|
|
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)).
|
|
78
91
|
|
|
79
|
-
→ [Orchestration](#user-content-orchestration-with-herdr), [
|
|
92
|
+
→ [Orchestration](#user-content-orchestration-with-herdr), [Herd](#user-content-herd-and-context)
|
|
80
93
|
|
|
81
94
|
Installing by hand (the CLI runs on Bun 1.3+), with Bun:
|
|
82
95
|
|
|
@@ -98,20 +111,23 @@ jh init --global
|
|
|
98
111
|
|
|
99
112
|
## With and without herdr
|
|
100
113
|
|
|
101
|
-
Planning, building and reviewing work without herdr. herdr adds parallel lanes and the agents' state in the
|
|
114
|
+
Planning, building and reviewing work without herdr. herdr adds parallel lanes and the agents' state in the herd.
|
|
102
115
|
|
|
103
116
|
| | Without herdr | With herdr |
|
|
104
117
|
| --- | --- | --- |
|
|
105
|
-
| `/grill`, `/plan`, `/implement`, `/pre-commit` and the review skills | yes | yes |
|
|
118
|
+
| `/jh-grill`, `/jh-plan`, `/jh-implement`, `/jh-pre-commit` and the review skills | yes | yes |
|
|
106
119
|
| Independent phases in parallel | as subagents in one checkout | as lanes, one worktree and session each |
|
|
107
|
-
| `/orchestrate` (the coordinator) | – | yes |
|
|
108
|
-
| `jh
|
|
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 |
|
|
109
123
|
|
|
110
|
-
Pull requests and checks in the
|
|
124
|
+
Pull requests and checks in the herd need `gh`.
|
|
111
125
|
|
|
112
126
|
## Global or per project
|
|
113
127
|
|
|
114
|
-
The global install prefixes every skill with `jh-`, leaves repositories alone and keeps its config in `~/.jackherd
|
|
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)
|
|
115
131
|
|
|
116
132
|
## Documentation
|
|
117
133
|
|
|
@@ -122,174 +138,7 @@ Each section opens on a click; a link into one opens it.
|
|
|
122
138
|
|
|
123
139
|
### Installation and updates
|
|
124
140
|
|
|
125
|
-
|
|
126
|
-
- [The installer](#user-content-the-installer)
|
|
127
|
-
- [Setup questions](#user-content-setup-questions)
|
|
128
|
-
- [Project or global](#user-content-project-or-global)
|
|
129
|
-
- [Project install](#user-content-project-install)
|
|
130
|
-
- [Global install](#user-content-global-install)
|
|
131
|
-
- [Detected per repository](#user-content-detected-per-repository)
|
|
132
|
-
- [Updating](#user-content-updating)
|
|
133
|
-
- [Keeping the global kit current](#user-content-keeping-the-global-kit-current)
|
|
134
|
-
- [Removing the global install](#user-content-removing-the-global-install)
|
|
135
|
-
|
|
136
|
-
#### Requirements
|
|
137
|
-
|
|
138
|
-
- [Bun](https://bun.sh) 1.3 or later, and Claude Code.
|
|
139
|
-
- Optional: [herdr](https://herdr.dev) for parallel lanes (`/orchestrate`, the watcher, agent states and context fill in the plan view), and `gh` for pull requests and checks in the plan view and the watcher.
|
|
140
|
-
|
|
141
|
-
#### The installer
|
|
142
|
-
|
|
143
|
-
```bash
|
|
144
|
-
curl -fsSL https://unpkg.com/jackherd/install.sh | sh
|
|
145
|
-
```
|
|
146
|
-
|
|
147
|
-
To read the script before it runs, open [install.sh](https://unpkg.com/jackherd/install.sh), the file the command above runs, or download it with `curl -fsSL <that address> -o install.sh` and start it with `sh install.sh` after reading. The address without a version leads to the newest release; for a fixed one use `https://unpkg.com/jackherd@<version>/install.sh`.
|
|
148
|
-
|
|
149
|
-
For Linux and macOS, without root: the script refuses to run as root. It announces each step on one line before it runs:
|
|
150
|
-
|
|
151
|
-
1. If no Bun is found (on the `PATH` or in `~/.bun/bin`), it runs Bun's official installer from `bun.com/install`, which needs `curl`, `unzip` and `bash`; a Bun older than 1.3 stops the script with a hint to run `bun upgrade`. That installer puts Bun into `~/.bun` and names the shell file it changed; the script itself edits none.
|
|
152
|
-
2. `bun add -g --ignore-scripts jackherd@<version>`, the version the script ships with. `curl … | JACKHERD_VERSION=1.2.3 sh` picks another release.
|
|
153
|
-
3. `jh init --global` on your terminal, so the [setup questions](#user-content-setup-questions) work under `curl … | sh`. Without a terminal it runs `init --global --yes` and says so.
|
|
154
|
-
|
|
155
|
-
The same by hand: `bun add -g jackherd` or `npm install -g jackherd`, then `jh init --global`. The CLI runs on Bun in both cases.
|
|
156
|
-
|
|
157
|
-
#### Setup questions
|
|
158
|
-
|
|
159
|
-
When `init` creates a new config, it asks:
|
|
160
|
-
|
|
161
|
-
- the language the skills answer you in, and the language plans and boards are written in (English or German; it sets their headings),
|
|
162
|
-
- whether to use herdr as the terminal adapter. If herdr is missing on Linux or macOS, it first offers herdr's official installer, shows the command and runs it only after a yes.
|
|
163
|
-
|
|
164
|
-
`--yes`, `--no` or a run without a terminal ask nothing and install nothing: replies and plans in English, herdr as the adapter only if herdr is found, and one line names what was taken. An existing config is never changed by `init`; edit `responseLanguage`, `planLanguage` and `terminal.adapter` in it, then run `sync`. At the end `init` prints the next steps: the start skill, `/plan` and what herdr or `gh` still need.
|
|
165
|
-
|
|
166
|
-
#### Project or global
|
|
167
|
-
|
|
168
|
-
| | Project install | Global install |
|
|
169
|
-
| --- | --- | --- |
|
|
170
|
-
| Use it when | the team shares the kit through the repository | the repository must stay untouched, not even untracked files |
|
|
171
|
-
| Lives in | the project's `.claude/` | `~/.claude/` |
|
|
172
|
-
| Skill names | `/plan`, `/implement`, `/orchestrate`, … | `/jh-plan`, `/jh-implement`, `/jh-orchestrate`, … |
|
|
173
|
-
| Hooks | `git-guard`, `watch-guard`, `merge-guard`, `ask-hint`, `format-on-edit` | none; `init --global` offers only a `SessionStart` update notice |
|
|
174
|
-
| Config | `.jackherd/config.json` | `~/.jackherd/config.json`, used where a repository has none |
|
|
175
|
-
| Update | `jh sync` | `jh update` |
|
|
176
|
-
|
|
177
|
-
A project config always wins over the global one. Both can exist side by side: in a project with its own kit, the project's skills are the ones in use.
|
|
178
|
-
|
|
179
|
-
#### Project install
|
|
180
|
-
|
|
181
|
-
```bash
|
|
182
|
-
bunx jackherd init # writes the kit into .claude/ and creates .jackherd/config.json
|
|
183
|
-
bunx jackherd sync # later: updates the kit files
|
|
184
|
-
```
|
|
185
|
-
|
|
186
|
-
`init` writes what it [detected](#user-content-detected-per-repository) into the new config, so the project sees it. It asks whether to enter the [status line](#user-content-status-line) in `.claude/settings.json`; `--yes` or `--no` answer every question without asking.
|
|
187
|
-
|
|
188
|
-
**Name clashes.** A project that already has a skill or agent of a kit name (`implement`, `plan`, …): `init` names the clash and offers the prefix `jh-`, so the kit's copies sit beside yours and call each other, not yours. Any project can set `skillPrefix` in `.jackherd/config.json`; `sync` then renames, and removes the unprefixed kit files you never edited, with the directories they leave empty. A directory that holds a file of yours stays, and `sync` names it.
|
|
189
|
-
|
|
190
|
-
**Your edits survive.** `sync` leaves files you have edited alone. When the kit changed one of them too, its version lands beside yours as `<file>.kit-new`: merge it, or take the kit version with `sync --accept <path>`. A kit file you deleted stays deleted. Project-specific additions go into `.claude/project/<skill>.md` (read first by the skill of that name) or a rule beside `.claude/rules/kit/`, so the kit files stay updatable.
|
|
191
|
-
|
|
192
|
-
**herdr plugin.** Run inside herdr (`HERDR_ENV=1`), `init` and `sync` link the kit's herdr plugin when herdr does not list `crossdelta.jackherd` yet, and link it again when herdr's link points at another package or herdr cannot read its manifest (a deleted checkout); one line names the old and the new path. An installed package (global, `bunx`, a project's dependency) replaces only a link herdr cannot read, so a checkout that develops the kit keeps its link across `jh update`. From a lane's worktree they never link, since that path goes away with the lane; a broken link is named there instead. It adds a plans pane, an action that opens it, and the startup hook that brings back the plans views. If herdr refuses the link, the command prints herdr's message and carries on. The link belongs to herdr, not to the project; `herdr plugin unlink crossdelta.jackherd` removes it.
|
|
193
|
-
|
|
194
|
-
#### Global install
|
|
195
|
-
|
|
196
|
-
On a new machine these two commands are the whole setup:
|
|
197
|
-
|
|
198
|
-
```bash
|
|
199
|
-
bun add -g jackherd
|
|
200
|
-
jh init --global # writes the kit into ~/.claude, creates ~/.jackherd/config.json, asks about the hook
|
|
201
|
-
```
|
|
202
|
-
|
|
203
|
-
`init --global` asks once whether to add a `SessionStart` hook that [keeps the global kit current](#user-content-keeping-the-global-kit-current), and whether to enter the status line; `--yes` or `--no` answer without asking. Nothing in the config needs editing: each repository's package manager and base branch are [detected](#user-content-detected-per-repository) where the kit runs. Run `jh sync --global` again after editing the config.
|
|
204
|
-
|
|
205
|
-
**Why the prefix.** Claude Code lets a personal skill in `~/.claude/skills` win over a project skill of the same name, so a global install renames everything with `skillPrefix` (`init --global` sets `jh-`; `sync --global` refuses to run without one): `/jh-implement`, `/jh-pre-commit`, the agent `jh-spec-review`, with every reference inside the files renamed to match. Each description starts with `Global jackherd copy. In a project that has its own implement skill, use that one instead.`, so in a project with its own kit the project's skills stay the ones in use.
|
|
206
|
-
|
|
207
|
-
**What lands where:** `~/.claude/skills/jh-*/` and `~/.claude/agents/jh-*.md`, which Claude Code reads, and under `~/.jackherd/` the config, the lock, the rules in `rules/` (where Claude Code does not load them in every project; the skills read them when they need them) and the status line as `statusline.sh`. Hooks are not installed globally, and `sync --global` never reads or writes `~/.claude/settings.json`; only `init --global` does, after asking. Other files in `~/.claude` are left alone; a file of your own at a kit path becomes a conflict with a `.kit-new` beside it, as in a project.
|
|
208
|
-
|
|
209
|
-
`check`, `plans`, `context` and `watch` read `~/.jackherd/config.json` when the repository has no `.jackherd/config.json`. `check` then runs from the repository's top level. Reviewer names in `check.reviewerRoutes` carry the prefix, since `/jh-pre-commit` dispatches exactly the names it prints.
|
|
210
|
-
|
|
211
|
-
Checked with a packed tarball and a throwaway `HOME` (shortened to `<home>`), in a throwaway Yarn 4 repository with `yarn.lock`, `origin/master` and no config of its own:
|
|
212
|
-
|
|
213
|
-
```text
|
|
214
|
-
$ jh init --global --yes
|
|
215
|
-
Created <home>/.jackherd/config.json with defaults.
|
|
216
|
-
Installed into ~/.claude; the paths below are relative to <home>.
|
|
217
|
-
jh sync: 30 created
|
|
218
|
-
Added the SessionStart hook to ~/.claude/settings.json.
|
|
219
|
-
Added the status line to ~/.claude/settings.json.
|
|
220
|
-
|
|
221
|
-
$ jh check --skip-slow
|
|
222
|
-
Changed files (8, 0 staged):
|
|
223
|
-
.editorconfig
|
|
224
|
-
.gitattributes
|
|
225
|
-
.gitignore
|
|
226
|
-
.yarnrc.yml
|
|
227
|
-
README.md
|
|
228
|
-
package.json
|
|
229
|
-
src/probe.ts
|
|
230
|
-
yarn.lock
|
|
231
|
-
|
|
232
|
-
Package roots: none
|
|
233
|
-
Reviewers: none
|
|
234
|
-
|
|
235
|
-
✅ Staged build artifacts (git status --porcelain --untracked-files=all, 0ms)
|
|
236
|
-
✅ Lint & unused imports (yarn biome check --no-errors-on-unmatched (8 files), 368ms)
|
|
237
|
-
⏭️ Types & tests (--skip-slow, 0ms)
|
|
238
|
-
skipped on request
|
|
239
|
-
|
|
240
|
-
✅ 2 passed, 0 failed, 1 skipped
|
|
241
|
-
```
|
|
242
|
-
|
|
243
|
-
#### Detected per repository
|
|
244
|
-
|
|
245
|
-
What a config leaves out of `commands`, `lanes.lockFile`, `check.lintFilesCommand` and `baseBranch`, the kit reads from the repository it runs in, each time:
|
|
246
|
-
|
|
247
|
-
- **Package manager** from the lock file in the repository root: `bun.lock` → `bun test`, `bun install`, `bunx @biomejs/biome …`; `yarn.lock` → `yarn test`, `yarn tsc --noEmit`, `yarn biome …`; `pnpm-lock.yaml` → `pnpm test`, `pnpm exec …`; `package-lock.json` → `npm test`, `npx …`. The lock file becomes `lanes.lockFile`. Lock files of two managers decide nothing: the Bun defaults hold, with one line saying so.
|
|
248
|
-
- **Base branch** from the branches on `origin`: `main` or `master`, when exactly one of them exists. With both or neither, `main` holds, with one line saying so; `origin/HEAD` is not asked, since it can point at a branch long deleted.
|
|
249
|
-
|
|
250
|
-
Detection only reads the lock files and `refs/remotes/origin/*` as the last fetch left them; it never fetches. A value you set always wins, key by key. `jh config` prints what applies in the repository you are in.
|
|
251
|
-
|
|
252
|
-
The global skills cannot carry one repository's values, so `sync --global` writes them as `<test>`, `<install>`, `<lockFile>`, `<baseBranch>` and so on, and every global skill starts with a sentence that sends the model to `jh config` for them; a project install keeps the real values in its files. `init --global` writes none of these keys, so one global config serves a Yarn repository on `master` and a Bun repository on `main` alike. A global config from 0.10 or 0.11 spells out the Bun defaults and `main`, which count as set: delete `commands`, `baseBranch` and `lanes.lockFile` from it to get detection.
|
|
253
|
-
|
|
254
|
-
#### Updating
|
|
255
|
-
|
|
256
|
-
`jh update` asks npm for the newest release and, when there is one, installs exactly that version, from your home directory and without install scripts, with the package manager that installed the kit globally (bun, npm, pnpm or yarn, read from where the package lies), then syncs `~/.claude` and, inside a project, the project with the freshly installed code. It prints the old and the new version. A kit that is a project dependency or a source checkout is not a global install, and `update` says so and changes nothing; there, update the dependency and run `jh sync`.
|
|
257
|
-
|
|
258
|
-
A running `plans` view restarts itself with the new code, and a running `watch` prints `=== kit updated <old> → <new>` and ends, so the coordinator reads its skill again and starts the stream again.
|
|
259
|
-
|
|
260
|
-
#### Keeping the global kit current
|
|
261
|
-
|
|
262
|
-
`jh sync --global --if-changed` is meant to run at the start of every session. It syncs `~/.claude` only when the installed package is not the version the global lock was written by, so after `bun add -g jackherd@<version>` the next session brings the skills up to date. Once a day it asks npm for the newest release and, when there is one, prints a single line of this form:
|
|
263
|
-
|
|
264
|
-
```text
|
|
265
|
-
jh 1.2.0 is on npm, 1.1.0 is installed. Ask the user with AskUserQuestion whether to run `jh update` now (update now / not now), and run it only after a yes.
|
|
266
|
-
```
|
|
267
|
-
|
|
268
|
-
It never installs anything itself: the session asks you, and `jh update` runs only after your yes. With nothing to do it prints nothing and exits 0; without a global install it syncs nothing. The time of the last npm question is kept in `~/.jackherd/update-check.json`. If npm does not answer (3 seconds), the question is asked again after an hour, not after a day, and nothing is printed meanwhile.
|
|
269
|
-
|
|
270
|
-
The hook runs when a session starts, resumes, is cleared or is compacted (`startup|resume|clear|compact`), so a hand-over by `/clear` sees the line too; the daily timestamp makes the extra runs free. A session that runs for days is covered by [the watcher](#user-content-the-watcher), which looks at npm once a day on the same timestamp. `jh sync --global` moves a hook that still reads `startup` only, exactly as an older install wrote it, to the new matcher; a hook of your own, or one with another command or extra keys, stays as it is.
|
|
271
|
-
|
|
272
|
-
`init --global` asks once whether to add this hook to `~/.claude/settings.json`, appends it as a group of its own beside your other hooks, and does nothing when it is there already. A file it cannot read or merge into, or a symlink, stays as it is; the entry to add by hand:
|
|
273
|
-
|
|
274
|
-
```json
|
|
275
|
-
{
|
|
276
|
-
"hooks": {
|
|
277
|
-
"SessionStart": [
|
|
278
|
-
{ "matcher": "startup|resume|clear|compact", "hooks": [{ "type": "command", "command": "jh sync --global --if-changed" }] }
|
|
279
|
-
]
|
|
280
|
-
}
|
|
281
|
-
}
|
|
282
|
-
```
|
|
283
|
-
|
|
284
|
-
Claude Code adds a `SessionStart` hook's output to the session's context, so the update line reaches Claude, not only the terminal; a failing hook does not block the session.
|
|
285
|
-
|
|
286
|
-
#### Removing the global install
|
|
287
|
-
|
|
288
|
-
```bash
|
|
289
|
-
rm -rf ~/.claude/skills/jh-* ~/.claude/agents/jh-*.md ~/.jackherd
|
|
290
|
-
```
|
|
291
|
-
|
|
292
|
-
Remove `~/.jackherd/config.json` with it if you no longer need the config, and the `SessionStart` hook and status line entries from `~/.claude/settings.json` if `init --global` added them.
|
|
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.
|
|
293
142
|
|
|
294
143
|
</details>
|
|
295
144
|
|
|
@@ -298,7 +147,7 @@ Remove `~/.jackherd/config.json` with it if you no longer need the config, and t
|
|
|
298
147
|
|
|
299
148
|
### Planning: plans, models and boards
|
|
300
149
|
|
|
301
|
-
|
|
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).
|
|
302
151
|
|
|
303
152
|
- [Terms](#user-content-terms)
|
|
304
153
|
- [The skills and what they hand on](#user-content-the-skills-and-what-they-hand-on)
|
|
@@ -309,47 +158,51 @@ Names on this page are those of a project install. A global install puts `jh-` b
|
|
|
309
158
|
|
|
310
159
|
#### Terms
|
|
311
160
|
|
|
312
|
-
- **Plan:** a Markdown file with phases (`### Phase 1: …`) and checkbox tasks. `/plan` writes it, `/implement` builds one phase and ticks its tasks.
|
|
313
|
-
- **Board:** a Markdown file with a lanes table that says who works on which phases of which plan, and what each lane starts after. `/plan` proposes its rows; by default it is `docs/plans/board-<name>/board.md`.
|
|
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`.
|
|
314
163
|
- **Lane:** one agent session in its own git worktree, working on the phases its row names.
|
|
315
|
-
- **Coordinator:** one more session, in the main checkout, running `/orchestrate`: it starts the lanes, answers or relays their questions, asks you for approvals and keeps the merge order.
|
|
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.
|
|
316
165
|
|
|
317
166
|
#### The skills and what they hand on
|
|
318
167
|
|
|
319
|
-
|
|
168
|
+
<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.">
|
|
171
|
+
</picture>
|
|
320
172
|
|
|
321
|
-
- **`/start`** is the first command after the install. It checks the config, herdr and `gh` with read-only commands, explains plan → board → `/orchestrate` or `/implement` in a few lines and offers to begin with `/plan`. It changes no file.
|
|
322
|
-
- **`/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.
|
|
323
|
-
- **`/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 `/orchestrate` reads to tell which phase is ready to start, which lanes would collide, and which model a lane runs on; `/implement` reads the same table to start phases in parallel without `/orchestrate`. At the end `/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, `/plan` stops and recommends `/grill` instead.
|
|
324
|
-
- **`/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 `/implement`.
|
|
325
|
-
- **`/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 `/pre-commit`.
|
|
326
|
-
- **`/pre-commit`** runs `jh check` and quotes its numbers, then `/review` for the judgement calls and the `spec-review` agent against the plan, plus the reviewers `check.reviewerRoutes` routes the changed paths to (`/uncle-bob`, `/security-audit`, `/devops`) and, for files a package publishes, `public-content`. It proposes the commit message and asks before committing.
|
|
327
|
-
- **`/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 ([
|
|
328
|
-
- **`/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 `/implement`.
|
|
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`.
|
|
329
181
|
|
|
330
182
|
#### What a plan contains
|
|
331
183
|
|
|
332
|
-
`/plan` writes every plan from one template. Each part is there because a skill reads it:
|
|
184
|
+
`/jh-plan` writes every plan from one template. Each part is there because a skill reads it:
|
|
333
185
|
|
|
334
|
-
- **Header (date, status, scope):** what the plan touches and how far it is; the board page of
|
|
335
|
-
- **Decisions:** each decision with its rationale, including who decided and when; `/plan` does not invent them, and `/orchestrate` writes a decision of yours into the plan the moment you make it.
|
|
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.
|
|
336
188
|
- **Model recommendations per phase:** the model for each phase with the reason ([below](#user-content-model-recommendations)).
|
|
337
|
-
- **Dependencies and parallelization:** per phase what it depends on, the paths it writes and its serial markers (a migration, say).
|
|
338
|
-
- **Phases with checkboxes:** the tasks `/implement` ticks as it finishes them, which is what
|
|
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.
|
|
339
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.
|
|
340
192
|
- **Tests:** which test files are needed and why, or the reason none are.
|
|
341
193
|
- **Documentation and architecture docs:** what gets documented after the last phase, ending with the architecture documentation derived from the finished code.
|
|
342
194
|
|
|
343
|
-
The section headings stay verbatim (in the plan language the config sets), because the plan parser, `/implement` and `spec-review` find them by name.
|
|
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.
|
|
344
196
|
|
|
345
197
|
#### Model recommendations
|
|
346
198
|
|
|
347
|
-
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 `/security-audit` always get the strongest model.
|
|
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.
|
|
348
200
|
|
|
349
201
|
The recommendation is applied, not just noted:
|
|
350
202
|
|
|
351
|
-
- `/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.
|
|
352
|
-
- `/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.
|
|
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.
|
|
353
206
|
|
|
354
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.
|
|
355
208
|
|
|
@@ -403,7 +256,7 @@ Customers scroll the whole catalog to find a product. Support gets the same ques
|
|
|
403
256
|
**Review gate Phase 1:**
|
|
404
257
|
|
|
405
258
|
- [ ] Tests written
|
|
406
|
-
- [ ] `/uncle-bob` — Clean Architecture review
|
|
259
|
+
- [ ] `/jh-uncle-bob` — Clean Architecture review
|
|
407
260
|
|
|
408
261
|
### Phase 2: Search box
|
|
409
262
|
|
|
@@ -413,7 +266,7 @@ Customers scroll the whole catalog to find a product. Support gets the same ques
|
|
|
413
266
|
**Review gate Phase 2:**
|
|
414
267
|
|
|
415
268
|
- [ ] Tests written
|
|
416
|
-
- [ ] `/uncle-bob` — Clean Architecture review
|
|
269
|
+
- [ ] `/jh-uncle-bob` — Clean Architecture review
|
|
417
270
|
|
|
418
271
|
### Phase 3: Docs
|
|
419
272
|
|
|
@@ -424,13 +277,13 @@ Customers scroll the whole catalog to find a product. Support gets the same ques
|
|
|
424
277
|
|
|
425
278
|
#### Boards
|
|
426
279
|
|
|
427
|
-
A board gathers the lanes one coordinator runs; it mixes plans and has no end date of its own. `/plan` proposes the rows from the dependency and model tables:
|
|
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:
|
|
428
281
|
|
|
429
282
|
- **Own lane:** a phase that shares no written path and no serial marker with a phase of another lane.
|
|
430
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.
|
|
431
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.
|
|
432
285
|
- **Model:** the full model ID from the model table, never an alias.
|
|
433
|
-
- **Plan:** a Markdown link to the plan, relative to the board's directory. The
|
|
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.
|
|
434
287
|
|
|
435
288
|
```markdown
|
|
436
289
|
<!-- docs/plans/board-01/board.md -->
|
|
@@ -445,7 +298,7 @@ A board gathers the lanes one coordinator runs; it mixes plans and has no end da
|
|
|
445
298
|
| ↳ `search-ui` | – | [Product search](../2026-10-12-search.md) | P3 Docs | P2 merged | claude-sonnet-5-5 |
|
|
446
299
|
```
|
|
447
300
|
|
|
448
|
-
The columns are read by position. The table heading is `## Lanes` (`## Spuren` with `planLanguage: de`). Nothing is written without your answer: `/plan` writes the rows into the newest board or a new `docs/plans/board-<name>/board.md`, and then names the next command, `/orchestrate <board path>`. Plan and board go into one commit under one approval: yours through `/pre-commit`, or the coordinator's documentation PR when `/orchestrate` finds them missing on the base branch ([Orchestration](#user-content-starting)). No merge is needed before `/orchestrate`.
|
|
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`.
|
|
449
302
|
|
|
450
303
|
Where boards live and what the project calls them is the `board` block of the config:
|
|
451
304
|
|
|
@@ -453,9 +306,9 @@ Where boards live and what the project calls them is the `board` block of the co
|
|
|
453
306
|
{ "board": { "pattern": "board-*", "file": "board.md", "term": "board" } }
|
|
454
307
|
```
|
|
455
308
|
|
|
456
|
-
- `pattern`: the directories under `planDirectory` that hold a board; `
|
|
309
|
+
- `pattern`: the directories under `planDirectory` that hold a board; `jh herd` and `jh watch` take the newest one unless given a path.
|
|
457
310
|
- `file`: the board document in each of them.
|
|
458
|
-
- `term`: the word
|
|
311
|
+
- `term`: the word the herd, its HTML page and `watch` use for it (`board page opened: …`).
|
|
459
312
|
|
|
460
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`.
|
|
461
314
|
|
|
@@ -466,7 +319,7 @@ Up to 0.12 the key was `sprintPattern` with a fixed `sprint.md`. 0.13 refuses th
|
|
|
466
319
|
|
|
467
320
|
### Orchestration with herdr
|
|
468
321
|
|
|
469
|
-
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; `/orchestrate` (
|
|
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.
|
|
470
323
|
|
|
471
324
|
- [What runs where](#user-content-what-runs-where)
|
|
472
325
|
- [Starting](#user-content-starting)
|
|
@@ -478,17 +331,20 @@ For work that splits into independent parts, one agent session per part runs in
|
|
|
478
331
|
|
|
479
332
|
#### What runs where
|
|
480
333
|
|
|
481
|
-
|
|
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>
|
|
482
338
|
|
|
483
|
-
`/orchestrate` needs herdr: it checks that `herdr` is on the path and `HERDR_ENV=1` is set, and stops otherwise. Without herdr, `/implement` still runs independent phases of one plan as parallel subagents in a single checkout, kept apart by their disjoint `Writes` sets.
|
|
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.
|
|
484
340
|
|
|
485
341
|
#### Starting
|
|
486
342
|
|
|
487
|
-
1. Set `"terminal": { "adapter": "herdr" }` in `.jackherd/config.json` (or `~/.jackherd/config.json`), so the
|
|
488
|
-
2. Open herdr, run Claude Code in the main checkout and type `/orchestrate docs/plans/<board>/board.md`. That session is the coordinator.
|
|
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.
|
|
489
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.
|
|
490
346
|
|
|
491
|
-
It names itself in herdr (`orchestrator-<repository>` by default, since herdr names are unique across all repositories), opens the
|
|
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.
|
|
492
348
|
|
|
493
349
|
#### What the coordinator does
|
|
494
350
|
|
|
@@ -501,7 +357,7 @@ Lanes ask Jack, not you: the coordinator checks their questions against the plan
|
|
|
501
357
|
- The block holds the question, the options, the lane's recommendation and the evidence at `file:line`.
|
|
502
358
|
- A question in a lane's report is closed in the same turn, in the lane or with you.
|
|
503
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.
|
|
504
|
-
- Your decision goes into the plan or the board in the same turn, with the date, so it is visible in the
|
|
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.
|
|
505
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.
|
|
506
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.
|
|
507
363
|
7. **Keeps the merge order.** Merges follow the board's order, the documentation PR of a fresh plan first. After each merge:
|
|
@@ -539,14 +395,14 @@ In a project install the `merge-guard` hook asks you before `gh pr merge` and `g
|
|
|
539
395
|
```
|
|
540
396
|
|
|
541
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.
|
|
542
|
-
- `guard.selfMerge.offer` (default `true`) lets `/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.
|
|
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.
|
|
543
399
|
- `guard.selfMerge.approvalMinutes` (default 180) is how long that answer lets `gh pr merge <number>` pass without a second dialog.
|
|
544
400
|
|
|
545
401
|
##### Self-merge
|
|
546
402
|
|
|
547
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.
|
|
548
404
|
|
|
549
|
-
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` 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.
|
|
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.
|
|
550
406
|
|
|
551
407
|
##### Merging without checks
|
|
552
408
|
|
|
@@ -560,7 +416,7 @@ A pull request that only changes documents rarely needs its checks. List the pat
|
|
|
560
416
|
|
|
561
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.
|
|
562
418
|
|
|
563
|
-
With the list set, `/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.
|
|
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.
|
|
564
420
|
|
|
565
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.
|
|
566
422
|
|
|
@@ -578,6 +434,17 @@ The agent runs with your account, so it can also edit a transcript, drop the hoo
|
|
|
578
434
|
|
|
579
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.
|
|
580
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
|
+
|
|
581
448
|
#### The watcher
|
|
582
449
|
|
|
583
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.
|
|
@@ -587,7 +454,7 @@ Jack does not sit and stare at every pane. He waits, and barks when something mo
|
|
|
587
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:
|
|
588
455
|
|
|
589
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
|
|
590
|
-
- 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
|
|
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
|
|
591
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
|
|
592
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>)`
|
|
593
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`)
|
|
@@ -596,13 +463,15 @@ Why it exists: the coordinator is a chat, and it only acts when something arrive
|
|
|
596
463
|
- `gh` became unreachable
|
|
597
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
|
|
598
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)
|
|
599
468
|
- the coordinator's own context fill rose to `warn` or `alert`, so it hands itself over in time
|
|
600
|
-
- `lane added` / `lane removed` when a worktree with an agent or a board row appears or goes
|
|
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
|
|
601
470
|
- the heartbeat, a status line every 15 minutes (`--heartbeat`)
|
|
602
471
|
|
|
603
|
-
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
|
|
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.
|
|
604
473
|
|
|
605
|
-
After a herdr server restart, panes come back as shells in their directories and the
|
|
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.
|
|
606
475
|
|
|
607
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.
|
|
608
477
|
|
|
@@ -619,7 +488,7 @@ coordinator coordinator no value (no agent named coordinator)
|
|
|
619
488
|
gh unavailable since 11:24:31
|
|
620
489
|
```
|
|
621
490
|
|
|
622
|
-
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
|
|
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:
|
|
623
492
|
|
|
624
493
|
```text
|
|
625
494
|
armed: search=no agent board docs/plans/board-search/board.md
|
|
@@ -656,6 +525,8 @@ The form is `review <state>` (`approved`, `changes requested`, `commented`), `in
|
|
|
656
525
|
{ "watch": { "feedback": { "ignore": ["status-bot[bot]"] } } }
|
|
657
526
|
```
|
|
658
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
|
+
|
|
659
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.
|
|
660
531
|
|
|
661
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:
|
|
@@ -672,7 +543,52 @@ Two sources, either or both. `deployments: true` reads GitHub's deployments of e
|
|
|
672
543
|
|
|
673
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.
|
|
674
545
|
|
|
675
|
-
**Source reports
|
|
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)
|
|
547
|
+
|
|
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:
|
|
551
|
+
|
|
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.
|
|
554
|
+
|
|
555
|
+
Decisions never live only in a hand-over note: they are in the plan or the board first.
|
|
556
|
+
|
|
557
|
+
#### Lane setup from the config
|
|
558
|
+
|
|
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.
|
|
564
|
+
|
|
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.
|
|
566
|
+
|
|
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.
|
|
568
|
+
|
|
569
|
+
</details>
|
|
570
|
+
|
|
571
|
+
<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.
|
|
676
592
|
|
|
677
593
|
```json
|
|
678
594
|
{
|
|
@@ -704,9 +620,9 @@ An edit without a new head commit reports nothing, so a bot that updates one com
|
|
|
704
620
|
https://<host>/browse/T-123
|
|
705
621
|
```
|
|
706
622
|
|
|
707
|
-
`items` is the path of the list in the output (without it the output is the list), `map` picks each entry's fields: `id`
|
|
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.
|
|
708
624
|
|
|
709
|
-
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. 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.
|
|
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.
|
|
710
626
|
|
|
711
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`.
|
|
712
628
|
|
|
@@ -728,9 +644,11 @@ A run that ends with an exit code other than 0, runs past 30 seconds (then its w
|
|
|
728
644
|
=== source tickets failed: exit 1
|
|
729
645
|
```
|
|
730
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
|
+
|
|
731
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.
|
|
732
650
|
|
|
733
|
-
**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 `/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`:
|
|
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`:
|
|
734
652
|
|
|
735
653
|
```text
|
|
736
654
|
dry run source tickets: ["tickets-cli","search","--query","key in (T-123)","--json"]
|
|
@@ -747,7 +665,7 @@ The watcher reads `watch.sources` when it starts: a new or changed source counts
|
|
|
747
665
|
|
|
748
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.
|
|
749
667
|
|
|
750
|
-
|
|
668
|
+
#### Recipes
|
|
751
669
|
|
|
752
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).
|
|
753
671
|
|
|
@@ -901,6 +819,21 @@ Check with `--raw` which end of `comments.nodes` holds the newest comment; `[0]`
|
|
|
901
819
|
}
|
|
902
820
|
```
|
|
903
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
|
+
|
|
904
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`:
|
|
905
838
|
|
|
906
839
|
```ts
|
|
@@ -943,35 +876,14 @@ export {}
|
|
|
943
876
|
|
|
944
877
|
The newest update stands first in `incident_updates`, so each new update of an open incident reports `updated` with its text.
|
|
945
878
|
|
|
946
|
-
#### Hand-over to a fresh chat
|
|
947
|
-
|
|
948
|
-
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:
|
|
949
|
-
|
|
950
|
-
- **A lane**, at a phase boundary: `/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.
|
|
951
|
-
- **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 `/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.
|
|
952
|
-
|
|
953
|
-
Decisions never live only in a hand-over note: they are in the plan or the board first.
|
|
954
|
-
|
|
955
|
-
#### Lane setup from the config
|
|
956
|
-
|
|
957
|
-
- `commands.install` (default `bun install`, or what the lock file implies) installs a new lane's dependencies.
|
|
958
|
-
- `commands.dev` (default `bun run dev`) runs as `PORT=<port> <dev>` in the lane's dev-server pane.
|
|
959
|
-
- `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.
|
|
960
|
-
- `lanes.lockFile` (default `bun.lock`) is the file a rebase takes from the base branch and regenerates.
|
|
961
|
-
- `worktreeDirectory` (default `.claude/worktrees`) is where lane worktrees go.
|
|
962
|
-
|
|
963
|
-
In a repository that ignores `docs/`, plan and board exist only in the main checkout and the plan view reads them there; list the plan in `lanes.copy` so the lane has it to work from and to tick off its own progress.
|
|
964
|
-
|
|
965
|
-
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.
|
|
966
|
-
|
|
967
879
|
</details>
|
|
968
880
|
|
|
969
881
|
<details>
|
|
970
|
-
<summary><b>
|
|
882
|
+
<summary><b>Herd and context</b></summary>
|
|
971
883
|
|
|
972
|
-
###
|
|
884
|
+
### Herd and context
|
|
973
885
|
|
|
974
|
-
`jh
|
|
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.
|
|
975
887
|
|
|
976
888
|
- [What it shows](#user-content-what-it-shows)
|
|
977
889
|
- [Attachments](#user-content-attachments)
|
|
@@ -983,45 +895,49 @@ The herdr plugin `zerodice0.worktree-bootstrap`, when enabled and configured in
|
|
|
983
895
|
|
|
984
896
|
One block per lane of the chosen board, lanes that wait on you first:
|
|
985
897
|
|
|
986
|
-
- the agent's state:
|
|
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
|
|
987
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
|
|
988
|
-
- how full the agent's context is, and a bar with the lane's ticked tasks
|
|
900
|
+
- how full the agent's context is (`🥣`), and a bar with the lane's ticked tasks (`◐`)
|
|
989
901
|
- the branch, commits ahead and uncommitted files
|
|
990
902
|
- the lane's open pull request with its checks
|
|
991
|
-
- per plan its attachments as chips (`📎`), the lane's phases with their counts, and the next open task of the current phase.
|
|
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
|
|
992
905
|
|
|
993
|
-
|
|
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:
|
|
994
907
|
|
|
995
908
|
```text
|
|
996
|
-
Board 01
|
|
909
|
+
🐾 Board 01 11:38
|
|
997
910
|
────────────────────────────────────────────────────────────────────────────────
|
|
998
|
-
▾
|
|
911
|
+
▾ ◆ search-ui blocked 🥣 62 % │ ██████░░░░ ◐ 6/9
|
|
999
912
|
⎇ feat/search-ui ↑1 ±4
|
|
1000
913
|
│
|
|
1001
914
|
└─ Product search
|
|
1002
|
-
├─ 📎 📄 Plan ✦ Mockup ◈ Search box
|
|
915
|
+
├─ 📎 📄 Plan 📡 PROJ-123 · In Progress ✦ Mockup ◈ Search box
|
|
1003
916
|
│
|
|
1004
|
-
├─
|
|
1005
|
-
|
|
1006
|
-
|
|
917
|
+
├─ ✓ P2 Search box 4/4
|
|
918
|
+
├─ ▸ P3 Docs docs open
|
|
919
|
+
│ └─ ▶ Dokumentation aktualisiert (README)
|
|
920
|
+
└─ ▸ P4 Review 0/2
|
|
1007
921
|
────────────────────────────────────────────────────────────────────────────────
|
|
1008
|
-
▸
|
|
922
|
+
▸ ◉ checkout-fee done ↗ #42 💬 2 🥣 48 % │ ██████████ ◐ 6/6
|
|
1009
923
|
────────────────────────────────────────────────────────────────────────────────
|
|
1010
|
-
▾
|
|
924
|
+
▾ ● search-index working 🥣 34 % │ ████████░░ ◐ 4/5
|
|
1011
925
|
⎇ feat/search-index ↑3 ±2
|
|
1012
926
|
↗ #41 … checks 🌐 preview 💬 1 new Search index
|
|
1013
927
|
│
|
|
1014
928
|
└─ Product search
|
|
1015
929
|
└─ ▸ P1 Index 4/5
|
|
1016
|
-
└─ ▶ /uncle-bob
|
|
930
|
+
└─ ▶ /jh-uncle-bob
|
|
931
|
+
────────────────────────────────────────────────────────────────────────────────
|
|
932
|
+
· no agent (1) docs-site 1/3
|
|
1017
933
|
────────────────────────────────────────────────────────────────────────────────
|
|
1018
|
-
↑↓ select →← open a
|
|
934
|
+
↑↓ select →← open a show ticked s html q quit ? help
|
|
1019
935
|
```
|
|
1020
936
|
|
|
1021
|
-
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 `
|
|
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`:
|
|
1022
938
|
|
|
1023
939
|
```text
|
|
1024
|
-
Board 01
|
|
940
|
+
🐾 Board 01 11:38:08
|
|
1025
941
|
───────────────────────────────────────────────────────────────────────────────
|
|
1026
942
|
───────────────────────────────────────────────────────────────────────────────
|
|
1027
943
|
· no agent (2) search-index 2/5 · search-ui 0/5
|
|
@@ -1030,30 +946,30 @@ Board 01 11:38:08
|
|
|
1030
946
|
gh pr list: no git remotes found
|
|
1031
947
|
origin/main not found, reading plans from the checkout
|
|
1032
948
|
───────────────────────────────────────────────────────────────────────────────
|
|
1033
|
-
↑↓ select →← open a
|
|
949
|
+
↑↓ select →← open a show ticked s html q quit ? help
|
|
1034
950
|
```
|
|
1035
951
|
|
|
1036
952
|
#### Attachments
|
|
1037
953
|
|
|
1038
954
|
Under each plan's title, a `📎` line carries the plan's links as chips; a click opens one in the browser:
|
|
1039
955
|
|
|
1040
|
-
- `📄 Plan` opens the board's HTML page at that plan: its status, its chips and its phases with their checkboxes. The
|
|
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.
|
|
1041
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.
|
|
1042
958
|
|
|
1043
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`).
|
|
1044
960
|
|
|
1045
961
|
#### Preview and feedback
|
|
1046
962
|
|
|
1047
|
-
A pull request row reads `↗ #<n> ✓ checks`, 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
|
|
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`.
|
|
1048
964
|
|
|
1049
965
|
#### Where the numbers come from
|
|
1050
966
|
|
|
1051
|
-
- **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
|
|
1052
|
-
- **Pull requests and checks** come from `gh`. Without `gh` the
|
|
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.
|
|
1053
969
|
- **Agent state and context fill** come from herdr, with `"terminal": { "adapter": "herdr" }` in the config. Without it, every lane shows no agent.
|
|
1054
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.
|
|
1055
971
|
|
|
1056
|
-
While it runs inside herdr, the
|
|
972
|
+
While it runs inside herdr, the herd also sets each lane's sidebar title (`P4 · 7/12`) and clears it on exit.
|
|
1057
973
|
|
|
1058
974
|
#### Keys
|
|
1059
975
|
|
|
@@ -1061,14 +977,13 @@ While it runs inside herdr, the view also sets each lane's sidebar title (`P4 ·
|
|
|
1061
977
|
| --- | --- |
|
|
1062
978
|
| `↑` `↓` | select a lane, phase or task |
|
|
1063
979
|
| `→` `←` | open or close the selected row |
|
|
1064
|
-
| `a` |
|
|
1065
|
-
| `n` | show or hide a lane's notes |
|
|
980
|
+
| `a` | list the ticked checkboxes one by one, or sum them up again |
|
|
1066
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 |
|
|
1067
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 |
|
|
1068
983
|
| `?` | the legend |
|
|
1069
984
|
| `q` | quit |
|
|
1070
985
|
|
|
1071
|
-
Options: `--once` prints one frame and exits (for reading the
|
|
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.
|
|
1072
987
|
|
|
1073
988
|
#### Context fill
|
|
1074
989
|
|
|
@@ -1107,12 +1022,12 @@ At `warn` a lane offers a fresh chat at its next phase boundary, and the watcher
|
|
|
1107
1022
|
| Command | What it does | Options |
|
|
1108
1023
|
| ------- | ------------ | ------- |
|
|
1109
1024
|
| `jh --version` | The installed version, e.g. `1.6.0`; `-v` is the same | none |
|
|
1110
|
-
| `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](
|
|
1111
|
-
| `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 |
|
|
1112
|
-
| `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 |
|
|
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; refuses a project that has a config but no install of its own | `--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, when it has its own install, the project with the new code | none; does nothing when the installed version is the latest |
|
|
1113
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 |
|
|
1114
|
-
| `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; 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 |
|
|
1115
|
-
| `jh
|
|
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) |
|
|
1116
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 |
|
|
1117
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) |
|
|
1118
1033
|
|
|
@@ -1125,11 +1040,11 @@ Without a global install, run any of them as `bunx jackherd <command>`.
|
|
|
1125
1040
|
| Key | Default | What it decides |
|
|
1126
1041
|
| --- | --- | --- |
|
|
1127
1042
|
| `responseLanguage` | `en` | the language the skills speak with you, and the watcher's status words |
|
|
1128
|
-
| `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
|
|
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 |
|
|
1129
1044
|
| `planLanguage` | `en` | the language of plans and boards, including their headings |
|
|
1130
1045
|
| `planDirectory` | `docs/plans` | where plans and boards live |
|
|
1131
1046
|
| `board.pattern`, `board.file`, `board.term` | `board-*`, `board.md`, `board` | [Boards](#user-content-boards) |
|
|
1132
|
-
| `worktreeDirectory` | `.claude/worktrees` | where lane worktrees go |
|
|
1047
|
+
| `worktreeDirectory` | `.claude/worktrees` | where lane worktrees go; relative, below the repository root |
|
|
1133
1048
|
| `baseBranch` | detected, else `main` | the branch lanes start from and plans are read at |
|
|
1134
1049
|
| `skillPrefix` | `''` (global: `jh-`) | put before every skill and agent name |
|
|
1135
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>` |
|
|
@@ -1137,16 +1052,17 @@ Without a global install, run any of them as `bunx jackherd <command>`.
|
|
|
1137
1052
|
| `lanes.lockFile` | detected, else `bun.lock` | the lock file a rebase takes from the base branch |
|
|
1138
1053
|
| `models.plan`, `implement`, `review`, `explore`, `orchestrate` | written by `init` | full model IDs per role; aliases are refused |
|
|
1139
1054
|
| `terminal.adapter` | `none` | `herdr` for agent states and context fill |
|
|
1140
|
-
| `plans.fetchIntervalMinutes` | `5` | background fetch of the base branch while
|
|
1055
|
+
| `plans.fetchIntervalMinutes` | `5` | background fetch of the base branch while the herd runs |
|
|
1141
1056
|
| `plans.linkKinds` | built in | links in a plan the board page shows as chips |
|
|
1142
1057
|
| `plans.context` | lanes 60 % / 80 %, coordinator 50 % / 70 %, window 200 000 | [Context fill](#user-content-context-fill) |
|
|
1143
1058
|
| `watch.deploy` | none | the deploy workflow and environments the watcher reports |
|
|
1144
|
-
| `watch.feedback` | `{ "ignore": [] }` | `false` turns feedback reports off; `ignore` lists logins whose feedback does not count |
|
|
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 |
|
|
1145
1061
|
| `watch.previews` | none | `deployments` (`false`), `authors` (`[]`) and `urlPattern`: where the watcher reads a lane's deploy preview |
|
|
1146
|
-
| `watch.sources` | `[]` | commands whose JSON output the watcher reports as `source`: `name`, `command`, `every` (`5`), `items`, `map`, `ignoreAuthors` (`[]`), `reportLimit` (`5`), `lane`, `reportOn`; [recipes](#user-content-recipes) |
|
|
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) |
|
|
1147
1063
|
| `check.lintFilesCommand` | detected | lint over the changed files |
|
|
1148
1064
|
| `check.checks` | `[]` | project checks (`name`, `command`, optional `globs`) |
|
|
1149
|
-
| `check.reviewerRoutes` | `[]` | which reviewer `/pre-commit` runs for which paths (`reviewer`, `globs`, `why`) |
|
|
1065
|
+
| `check.reviewerRoutes` | `[]` | which reviewer `/jh-pre-commit` runs for which paths (`reviewer`, `globs`, `why`) |
|
|
1150
1066
|
| `check.generatorOwned` | `[]` | files a generator owns (`glob`, `generator`); `check` names the generator when one of them changed |
|
|
1151
1067
|
| `check.buildArtifacts` | build output globs | staged files `check` refuses |
|
|
1152
1068
|
| `check.publicContent` | none | [published files](#user-content-a-package-you-publish-from-the-repository) |
|
|
@@ -1179,7 +1095,7 @@ What usually stays to configure is what no file in the repository says, for exam
|
|
|
1179
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)).
|
|
1180
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`.
|
|
1181
1097
|
- `format-on-edit` runs the project's own Biome on edited TS, JS and JSON files, when the project has it installed.
|
|
1182
|
-
- **herdr plugin:** a
|
|
1098
|
+
- **herdr plugin:** a herd pane and an action that opens it ([Installation](https://unpkg.com/jackherd/INSTALL.md)).
|
|
1183
1099
|
|
|
1184
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.
|
|
1185
1101
|
|
|
@@ -1196,7 +1112,7 @@ When the repository publishes a package, `check.publicContent` names its files,
|
|
|
1196
1112
|
```
|
|
1197
1113
|
|
|
1198
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.
|
|
1199
|
-
- **The `public-content` agent**, which `/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`.
|
|
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`.
|
|
1200
1116
|
|
|
1201
1117
|
</details>
|
|
1202
1118
|
|
|
@@ -1205,19 +1121,19 @@ When the repository publishes a package, `check.publicContent` names its files,
|
|
|
1205
1121
|
|
|
1206
1122
|
### Troubleshooting
|
|
1207
1123
|
|
|
1208
|
-
**`/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 `/implement` instead; it still runs independent phases in parallel as subagents.
|
|
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.
|
|
1209
1125
|
|
|
1210
|
-
**The
|
|
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.
|
|
1211
1127
|
|
|
1212
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`.
|
|
1213
1129
|
|
|
1214
|
-
**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 `/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.
|
|
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.
|
|
1215
1131
|
|
|
1216
|
-
**A lane shows no plan.** The
|
|
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.
|
|
1217
1133
|
|
|
1218
|
-
**The
|
|
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).
|
|
1219
1135
|
|
|
1220
|
-
**Pull requests and checks are missing.** They come from `gh`. The
|
|
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.
|
|
1221
1137
|
|
|
1222
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.
|
|
1223
1139
|
|
|
@@ -1237,7 +1153,7 @@ When the repository publishes a package, `check.publicContent` names its files,
|
|
|
1237
1153
|
|
|
1238
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).
|
|
1239
1155
|
|
|
1240
|
-
**`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](
|
|
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).
|
|
1241
1157
|
|
|
1242
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`.
|
|
1243
1159
|
|
|
@@ -1251,4 +1167,4 @@ When the repository publishes a package, `check.publicContent` names its files,
|
|
|
1251
1167
|
|
|
1252
1168
|
## License
|
|
1253
1169
|
|
|
1254
|
-
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.
|
|
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.
|