jackherd 1.6.0 → 1.7.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +31 -0
- package/INSTALL.md +176 -0
- package/README.md +260 -315
- 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 +113 -30
- 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/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/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/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/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,37 @@
|
|
|
2
2
|
|
|
3
3
|
What changed for people who use jackherd, newest first.
|
|
4
4
|
|
|
5
|
+
## 1.7.0
|
|
6
|
+
|
|
7
|
+
- **Tickets show beside their plan.** A `watch.sources` entry with `"ticket": true` marks its entries as tickets, and the herd shows each 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 to any lane whose name, board row or plan names its id as a whole word, so one source of all your open tickets needs no `lane`. The status comes from the new field `map.status`; without it the badge shows the key alone. A ticket whose last change was reported within a day is amber, and a folded lane then shows `📡 <n>` in amber. At most three per lane, new ones first, then `+<n>`; a link chip with the same URL gives way to the badge. The badges come from the watcher's saved state, which now keeps URL, status and change time of a marked source's entries; without a running watcher there are none, and the header says so. `/follow` asks whether a source holds tickets and finds the path of their status itself, for you to approve with the entry.
|
|
8
|
+
- **GitHub Projects as a ticket source.** A new README recipe reads a project board with `gh project item-list` (scope `project`: `gh auth refresh -s project`), without a script; each card's `Status` becomes its badge's status. jackherd only reads: the project's own workflows move the status. `/follow` offers it.
|
|
9
|
+
- **`updatedAt` is optional** where a content field (`title`, `author`, `body`, `url`) or `reportOn` tells a change, so a source whose entries carry no time, such as project cards, needs no stand-in. A source with none of these is refused when the config loads, since its entries could never change.
|
|
10
|
+
- **The herd reads `## P15 — …` sections as phases.** A plan section headed with the phase prefix, as boards name phases, was not read at all, so its lane showed `not in the plan` and an older ticked section took over as the current phase. Now `## P15 — title` is phase 15, like `## 10 — title`.
|
|
11
|
+
- **A board phase the plan lacks never borrows another phase.** A row naming `P3c` on a plan without phase 3c showed the title and checkboxes of phase 3; it now shows `not in the plan`, and phase 3 stays where it is.
|
|
12
|
+
- **The watcher reports a lane on the wrong model.** When an idle lane's last answer came from a model its board row's model column does not name, the watcher prints `=== model <lane> <id> · board <column>` once, with a duty line to switch it without a dialog (`/exit`, then `claude --resume <session> --model <id>` in the same pane). Every model the column names fits, matched by family and version; it reports again only when the model changes.
|
|
13
|
+
- **`orchestrate`: switching a lane's model, sensitive input, bundled approvals.** § 2 switches a running lane's model by resume instead of `/model`; § 4 runs a step that needs a secret in a pane of its own (herdr, else tmux, else the user's terminal, or parked while the user is away); the lane prompt (§ 1) sends such steps to the coordinator and bundles calls that need an approval each.
|
|
14
|
+
- **A lane's permission dialogs go to the coordinator.** The new kit hook `approval-gate` holds a call that would open a permission dialog in a lane, the watcher reports `=== approval <lane> <id> <tool> · <call>`, the coordinator asks the user, and the hook allows or denies the call from the user's answer in the coordinator's transcript. It waits up to `guard.approvals.waitMinutes` (default 8), then denies with a note to retry once approved; an approval holds once, for that exact call, for 30 minutes. The merge guard reads dialog answers through the same shared reader.
|
|
15
|
+
- **An approval binds the call that was shown.** The mark in the coordinator's question is now `` `approval <id> <hash>` ``, with the hash of the request it showed; the hook takes the answer only when that hash is the one of the waiting call, so a request rewritten while you looked is approved by nothing. The coordinator shows the request's tool, directory and whole input before it asks.
|
|
16
|
+
- **`worktreeDirectory` must lie below the repository root.** `jh sync` now refuses an absolute path, `.` or a path with `..` (before, the approval gate fell back to `.claude/worktrees` in silence while the watcher used the value, so lanes were not gated).
|
|
17
|
+
- **A lane's helper agent is watched with it.** An agent named `<lane>-<purpose>` without a worktree of its own counts with that lane; `orchestrate` has a lane announce such a helper first and close it itself.
|
|
18
|
+
- **The key `a` says what it does, and the notes line is gone.** The hint reads `a show ticked`; board rows without a phase no longer get a notes line, and the key `n` does nothing.
|
|
19
|
+
- **Install through your coding agent.** The installation and update instructions moved from the README into `INSTALL.md`, published with the package (`https://unpkg.com/jackherd/INSTALL.md`). It carries the steps for an agent without an interactive terminal; the quickstart has a sentence to paste into your agent instead of running the installer. herdr is set up only after a yes.
|
|
20
|
+
- **`/follow` in the README.** A feature line with `/follow PROJ-123`, a row in "With and without herdr" and a section of its own under Documentation, which now holds the source details.
|
|
21
|
+
- **A side task goes into the plan, not the board.** `jh check` warns about a board row whose phase cell carries prose (over 250 characters, or an `also (User …)` note) with "it belongs as a task in the plan"; `orchestrate` § 4 writes a side task of yours as a task of its phase, with origin and time, and the board row names only the phase.
|
|
22
|
+
- **Which phases can run side by side.** `jh check` warns about a plan phase without a row in the dependency table. `jh check --parallel <plan>` prints the groups of ready phases whose `Writes` paths do not overlap and that share no serial marker; the watcher reports `=== parallel <lane> P<a> ∥ P<b>` once per lane and set of phases, and `/implement` step 0b reads the result instead of working it out. Content collisions and shared release files (changelog, version) stay a judgement.
|
|
23
|
+
- **A clean review comment is no feedback.** A bot's pull request comment without an inline comment of its own within five minutes, whose text matches `watch.feedback.noFindingTemplates` (default: the CI code review's comment for a clean run), wakes nobody and shows no 💬 badge. Real findings count as before.
|
|
24
|
+
- **Red checks say which.** A lane's PR badge names the red checks (`✗ test, lint` instead of `✗ checks`). On a red run the watcher reads `gh run view <id> --log-failed` once per head commit, finds the failed tests with `watch.checks.failedTestPattern` (default Bun's `(fail) …`), shows up to three under the PR and reports `=== checks failed #<n> (<lane>): <checks> (<k> tests: …)`. Without a match it names the checks alone.
|
|
25
|
+
- **The plans view is now "the herd": `jh herd`.** It shows the whole herd at a glance, the state of every lane; `jh plans` stays as an alias with every option, and herd panes started by 1.6 are still found. The herdr plugin opens it with the title `herd` (pane id `plans` and action `open-plans` stay, so existing herdr configs keep working); the watcher says `herd --ensure: started the herd in <pane>`. The config keys `plans.*` are unchanged. The README writes every skill with the `jh-` prefix of the global install.
|
|
26
|
+
- **The herd shows what a lane works on.** The current phase is the one whose boxes moved last while `jh herd` runs; without movement, or when several phases moved at once, the newest board row decides as before, and ▶ marks that phase's first open task. Finished phases stand as folded rows of their own with a green ✓ instead of one summary line, and a phase with only its documentation box open shows `docs open`. New symbols, one cell each and in one colour with their word: working ● teal, blocked ◆ amber, idle ○ grey, done ◉ teal; context 🥣, progress ◐, ticket 📡, 🐾 in the header. The watcher's status line and `--once` keep the board's phase.
|
|
27
|
+
- **README: icons and diagrams in the logo's style.** The six features carry their own icons, light and dark; the three diagrams are redrawn, each light and dark on a card of its own colours, so a dark diagram stays readable on a light page. The head image walks through five numbered steps (a lane works, hits a question, the coordinator asks you, you pick, the lane works on) and holds still with reduced motion. The Mermaid sources are gone; icons and diagrams are generated by scripts in the repository.
|
|
28
|
+
- **README for a first-time reader.** The head says who does what (Claude Code writes the code, jackherd plans and coordinates, herdr holds the parallel sessions) and that open decisions, commits, pushes and merges wait for you. "Global or per project" says that a global install brings no hooks, and what a team shares with a project install. The self-merge rules name the short flags `-s`, `-m`, `-r`, `-d`.
|
|
29
|
+
- **A subagent on the wrong model is stopped.** The new kit hook `model-gate` checks a phase that `/implement` hands to a subagent: it reads `Phase P<n>` and the plan path from the prompt, takes the subagent's model from the call or the agent's definition, and compares it with the phase's row in the plan's model table. A weaker model is denied with the one to use, a stronger one passes with a note, anything it cannot tell passes. The hook `model-log` records the model a subagent actually ran on in `.jackherd/models/` (ignored by git), and the herd shows it at the phase: `Opus ✓`, or `Sonnet ≠ Opus` in amber. `implement` and `orchestrate` name the convention `Phase P<n>` in the subagent's prompt. `jh sync` brings both hooks to a project install; a global install ships none.
|
|
30
|
+
|
|
31
|
+
## 1.6.1
|
|
32
|
+
|
|
33
|
+
- **The README's guard section is split.** The head says the coordinator asks before every commit, push and merge, and "Guarded commands and self-merge" is split into What is guarded, Self-merge, Merging without checks and Limits. Points 4 and 7 of "What the coordinator does" are lists. The behaviour is unchanged.
|
|
34
|
+
- **Dates and times in a record are read from the clock.** The `orchestrate` skill now says in § 4 that a date or time written into a plan, the board or a history entry comes from `date`, run in the same turn, never from the conversation or a guess; the self-merge note, the closing note and the hand-over note in § 5, § 7 and § 8 point to it.
|
|
35
|
+
|
|
5
36
|
## 1.6.0
|
|
6
37
|
|
|
7
38
|
- **Replies address you as you like.** The new setting `responseAddress` (`informal` by default, or `formal`) says whether replies use the familiar or the polite form where the language has both: in German `du` or `Sie`. Set it in `.jackherd/config.json`; `jh sync` writes it into the kit rule `rules/kit/code-style.md`, which every session loads. It follows `responseLanguage`. English has one form, so there the setting only nudges the tone (direct or businesslike). The watcher's and the plans view's own German words use no pronoun and are unchanged.
|
package/INSTALL.md
ADDED
|
@@ -0,0 +1,176 @@
|
|
|
1
|
+
# Installing jackherd
|
|
2
|
+
|
|
3
|
+
How to install jackherd, set it up and keep it current. The [README](https://www.npmjs.com/package/jackherd) says what jackherd does.
|
|
4
|
+
|
|
5
|
+
## For a coding agent
|
|
6
|
+
|
|
7
|
+
You were asked to install jackherd for the user. Install it globally, then ask the user the setup questions below; do not answer them yourself.
|
|
8
|
+
|
|
9
|
+
1. Run the installer: `curl -fsSL https://unpkg.com/jackherd/install.sh | sh`. It installs Bun if it is missing, then jackherd. Your shell has no interactive terminal, so the installer skips its own questions and takes the defaults (replies and plans in English, herdr only if it is already installed, no other software) and says so; it also adds the update hook and the status line to `~/.claude/settings.json`, tell the user. You ask in the next step. If `jh` is not found afterwards, Bun's directory is not on your `PATH` yet: run it as `PATH="$HOME/.bun/bin:$PATH" jh …`.
|
|
10
|
+
2. Ask the user each question as a choice, with your question tool if you have one, else one short question per message:
|
|
11
|
+
- the language you answer in, `responseLanguage`: `en` or `de`; for `de` also `responseAddress`: `informal` (du) or `formal` (Sie),
|
|
12
|
+
- the language plans and boards are written in, `planLanguage`: `en` or `de`,
|
|
13
|
+
- whether to use herdr. Explain it first in one sentence: it runs parallel lanes, each in its own worktree and agent session, and shows their state in the herd; everything else works without it.
|
|
14
|
+
- Only after a yes, and only if `herdr` is missing: take the install command from [herdr.dev](https://herdr.dev), show it, and run it after a second yes.
|
|
15
|
+
3. Write the answers into `~/.jackherd/config.json` (`responseLanguage`, `responseAddress`, `planLanguage`, and `terminal.adapter`: `herdr` or `none`), then run `jh sync --global`. If the file existed before the installer ran, show its values first and change nothing without a yes.
|
|
16
|
+
4. Tell the user the next step: open a repository and run `/jh-start`. Claude Code lists skills installed during a session in that session; if `/jh-start` is not offered, start a new session.
|
|
17
|
+
|
|
18
|
+
If the user wants the kit in the repository, shared with the team, run `bunx jackherd init --yes` there, write the same answers into `.jackherd/config.json` and run `bunx jackherd sync`. The skills then have no `jh-` prefix (`/start`). See [Project or global](#project-or-global).
|
|
19
|
+
|
|
20
|
+
## Requirements
|
|
21
|
+
|
|
22
|
+
- [Bun](https://bun.sh) 1.3 or later, and Claude Code.
|
|
23
|
+
- Optional: [herdr](https://herdr.dev) for parallel lanes (`/jh-orchestrate`, the watcher, agent states and context fill in the herd), and `gh` for pull requests and checks in the herd and the watcher.
|
|
24
|
+
|
|
25
|
+
## The installer
|
|
26
|
+
|
|
27
|
+
```bash
|
|
28
|
+
curl -fsSL https://unpkg.com/jackherd/install.sh | sh
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
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`.
|
|
32
|
+
|
|
33
|
+
For Linux and macOS, without root: the script refuses to run as root. It announces each step on one line before it runs:
|
|
34
|
+
|
|
35
|
+
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.
|
|
36
|
+
2. `bun add -g --ignore-scripts jackherd@<version>`, the version the script ships with. `curl … | JACKHERD_VERSION=1.2.3 sh` picks another release.
|
|
37
|
+
3. `jh init --global` on your terminal, so the [setup questions](#setup-questions) work under `curl … | sh`. Without a terminal it runs `init --global --yes` and says so.
|
|
38
|
+
|
|
39
|
+
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.
|
|
40
|
+
|
|
41
|
+
## Setup questions
|
|
42
|
+
|
|
43
|
+
When `init` creates a new config, it asks:
|
|
44
|
+
|
|
45
|
+
- the language the skills answer you in, and the language plans and boards are written in (English or German; it sets their headings),
|
|
46
|
+
- 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.
|
|
47
|
+
|
|
48
|
+
`--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.
|
|
49
|
+
|
|
50
|
+
## Project or global
|
|
51
|
+
|
|
52
|
+
| | Project install | Global install |
|
|
53
|
+
| --- | --- | --- |
|
|
54
|
+
| Use it when | the team shares the kit through the repository | the repository must stay untouched, not even untracked files |
|
|
55
|
+
| Lives in | the project's `.claude/` | `~/.claude/` |
|
|
56
|
+
| Skill names | `/plan`, `/implement`, `/orchestrate`, … | `/jh-plan`, `/jh-implement`, `/jh-orchestrate`, … |
|
|
57
|
+
| Hooks | `git-guard`, `watch-guard`, `merge-guard`, `approval-gate`, `ask-hint`, `format-on-edit` | none; `init --global` offers only a `SessionStart` update notice |
|
|
58
|
+
| Config | `.jackherd/config.json` | `~/.jackherd/config.json`, used where a repository has none |
|
|
59
|
+
| Update | `jh sync` | `jh update` |
|
|
60
|
+
|
|
61
|
+
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.
|
|
62
|
+
|
|
63
|
+
## Project install
|
|
64
|
+
|
|
65
|
+
```bash
|
|
66
|
+
bunx jackherd init # writes the kit into .claude/ and creates .jackherd/config.json
|
|
67
|
+
bunx jackherd sync # later: updates the kit files
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
`init` writes what it [detected](#detected-per-repository) into the new config, so the project sees it. It asks whether to enter the status line (README, *Status line*) in `.claude/settings.json`; `--yes` or `--no` answer every question without asking.
|
|
71
|
+
|
|
72
|
+
**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.
|
|
73
|
+
|
|
74
|
+
**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.
|
|
75
|
+
|
|
76
|
+
**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 the herd pane, an action that opens it, and the startup hook that brings the herd panes back. 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.
|
|
77
|
+
|
|
78
|
+
## Global install
|
|
79
|
+
|
|
80
|
+
On a new machine these two commands are the whole setup:
|
|
81
|
+
|
|
82
|
+
```bash
|
|
83
|
+
bun add -g jackherd
|
|
84
|
+
jh init --global # writes the kit into ~/.claude, creates ~/.jackherd/config.json, asks about the hook
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
`init --global` asks once whether to add a `SessionStart` hook that [keeps the global kit current](#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](#detected-per-repository) where the kit runs. Run `jh sync --global` again after editing the config.
|
|
88
|
+
|
|
89
|
+
**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.
|
|
90
|
+
|
|
91
|
+
**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.
|
|
92
|
+
|
|
93
|
+
`check`, `herd`, `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.
|
|
94
|
+
|
|
95
|
+
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:
|
|
96
|
+
|
|
97
|
+
```text
|
|
98
|
+
$ jh init --global --yes
|
|
99
|
+
Created <home>/.jackherd/config.json with defaults.
|
|
100
|
+
Installed into ~/.claude; the paths below are relative to <home>.
|
|
101
|
+
jh sync: 30 created
|
|
102
|
+
Added the SessionStart hook to ~/.claude/settings.json.
|
|
103
|
+
Added the status line to ~/.claude/settings.json.
|
|
104
|
+
|
|
105
|
+
$ jh check --skip-slow
|
|
106
|
+
Changed files (8, 0 staged):
|
|
107
|
+
.editorconfig
|
|
108
|
+
.gitattributes
|
|
109
|
+
.gitignore
|
|
110
|
+
.yarnrc.yml
|
|
111
|
+
README.md
|
|
112
|
+
package.json
|
|
113
|
+
src/probe.ts
|
|
114
|
+
yarn.lock
|
|
115
|
+
|
|
116
|
+
Package roots: none
|
|
117
|
+
Reviewers: none
|
|
118
|
+
|
|
119
|
+
✅ Staged build artifacts (git status --porcelain --untracked-files=all, 0ms)
|
|
120
|
+
✅ Lint & unused imports (yarn biome check --no-errors-on-unmatched (8 files), 368ms)
|
|
121
|
+
⏭️ Types & tests (--skip-slow, 0ms)
|
|
122
|
+
skipped on request
|
|
123
|
+
|
|
124
|
+
✅ 2 passed, 0 failed, 1 skipped
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
## Detected per repository
|
|
128
|
+
|
|
129
|
+
What a config leaves out of `commands`, `lanes.lockFile`, `check.lintFilesCommand` and `baseBranch`, the kit reads from the repository it runs in, each time:
|
|
130
|
+
|
|
131
|
+
- **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.
|
|
132
|
+
- **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.
|
|
133
|
+
|
|
134
|
+
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.
|
|
135
|
+
|
|
136
|
+
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.
|
|
137
|
+
|
|
138
|
+
## Updating
|
|
139
|
+
|
|
140
|
+
`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`.
|
|
141
|
+
|
|
142
|
+
A running herd 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.
|
|
143
|
+
|
|
144
|
+
## Keeping the global kit current
|
|
145
|
+
|
|
146
|
+
`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:
|
|
147
|
+
|
|
148
|
+
```text
|
|
149
|
+
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.
|
|
150
|
+
```
|
|
151
|
+
|
|
152
|
+
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.
|
|
153
|
+
|
|
154
|
+
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 (README, *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.
|
|
155
|
+
|
|
156
|
+
`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:
|
|
157
|
+
|
|
158
|
+
```json
|
|
159
|
+
{
|
|
160
|
+
"hooks": {
|
|
161
|
+
"SessionStart": [
|
|
162
|
+
{ "matcher": "startup|resume|clear|compact", "hooks": [{ "type": "command", "command": "jh sync --global --if-changed" }] }
|
|
163
|
+
]
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
```
|
|
167
|
+
|
|
168
|
+
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.
|
|
169
|
+
|
|
170
|
+
## Removing the global install
|
|
171
|
+
|
|
172
|
+
```bash
|
|
173
|
+
rm -rf ~/.claude/skills/jh-* ~/.claude/agents/jh-*.md ~/.jackherd
|
|
174
|
+
```
|
|
175
|
+
|
|
176
|
+
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.
|