jackherd 1.9.0 → 1.10.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (204) hide show
  1. package/CHANGELOG.md +30 -0
  2. package/INSTALL.md +1 -1
  3. package/README.md +50 -32
  4. package/dist/{chunk-MCP4XJQH.js → chunk-5MQBCUQ5.js} +9 -4
  5. package/dist/config.d.ts +39 -4
  6. package/dist/config.js +67 -26
  7. package/dist/plan-format.d.ts +45 -6
  8. package/dist/plan-format.js +46 -6
  9. package/docs/workflow-dark.svg +67 -63
  10. package/docs/workflow.svg +67 -63
  11. package/herdr-plugin/herdr-plugin.toml +1 -1
  12. package/install.sh +1 -1
  13. package/kit/hooks/ask-rules.json +15 -0
  14. package/kit/hooks/hooks/ask-hint/decision.ts +82 -14
  15. package/kit/hooks/hooks/ask-hint/index.ts +15 -19
  16. package/kit/hooks/hooks/ask-hint/settings.ts +3 -18
  17. package/kit/hooks/hooks/git-guard/decision.ts +50 -102
  18. package/kit/hooks/hooks/merge-guard/always-ask.ts +63 -0
  19. package/kit/hooks/hooks/merge-guard/{plan-approval-settings.ts → board-settings.ts} +21 -67
  20. package/kit/hooks/hooks/merge-guard/check-free.ts +20 -0
  21. package/kit/hooks/hooks/merge-guard/decision.ts +127 -187
  22. package/kit/hooks/hooks/merge-guard/index.ts +6 -3
  23. package/kit/hooks/hooks/merge-guard/lane-plan.ts +79 -0
  24. package/kit/hooks/hooks/merge-guard/run.ts +59 -54
  25. package/kit/hooks/hooks/merge-guard/settings.ts +13 -1
  26. package/kit/hooks/hooks/merge-guard/standing-approval.ts +142 -0
  27. package/kit/hooks/hooks/open-question/index.ts +26 -0
  28. package/kit/hooks/hooks/open-question/record.ts +135 -0
  29. package/kit/hooks/hooks/open-question/run.ts +52 -0
  30. package/kit/hooks/hooks/open-question/store.ts +39 -0
  31. package/kit/hooks/hooks/sent-file/store.ts +3 -12
  32. package/kit/hooks/hooks/shared/config.ts +7 -0
  33. package/kit/hooks/hooks/shared/dialog-answers.ts +9 -9
  34. package/kit/hooks/hooks/shared/files.ts +7 -0
  35. package/kit/hooks/hooks/shared/lane.ts +1 -1
  36. package/kit/hooks/hooks/shared/newest-verdict.ts +10 -0
  37. package/kit/hooks/hooks/shared/private-directory.ts +56 -9
  38. package/kit/hooks/hooks/shared/program-position.ts +2 -75
  39. package/kit/hooks/hooks/shared/shell-aliases.ts +1 -71
  40. package/kit/hooks/hooks/{ask-hint → shared}/transcript.ts +37 -6
  41. package/kit/hooks/hooks/{merge-guard → shared}/xargs-program.ts +10 -8
  42. package/kit/hooks/hooks/task-hint/decision.ts +49 -0
  43. package/kit/hooks/hooks/task-hint/index.ts +79 -0
  44. package/kit/hooks/hooks/task-hint/run.ts +62 -0
  45. package/kit/hooks/hooks/task-hint/store.ts +51 -0
  46. package/kit/hooks/hooks.json +13 -5
  47. package/kit/review/agents/skill-review.md +34 -0
  48. package/kit/review/skills/pre-commit/SKILL.md +1 -1
  49. package/kit/workflow/skills/follow/SKILL.md +1 -8
  50. package/kit/workflow/skills/implement/SKILL.md +8 -6
  51. package/kit/workflow/skills/orchestrate/SKILL.md +210 -433
  52. package/kit/workflow/skills/orchestrate/handover/decision.ts +43 -8
  53. package/kit/workflow/skills/orchestrate/handover/index.ts +32 -4
  54. package/kit/workflow/skills/orchestrate/handover/standing-approval.ts +84 -0
  55. package/kit/workflow/skills/orchestrate/references/acceptance.md +15 -0
  56. package/kit/workflow/skills/orchestrate/references/close-lane.md +17 -0
  57. package/kit/workflow/skills/orchestrate/references/coordinator-handover.md +43 -17
  58. package/kit/workflow/skills/orchestrate/references/draft-review.md +19 -0
  59. package/kit/workflow/skills/orchestrate/references/events.md +86 -0
  60. package/kit/workflow/skills/orchestrate/references/herd.md +6 -6
  61. package/kit/workflow/skills/orchestrate/references/lane-handover.md +18 -0
  62. package/kit/workflow/skills/orchestrate/references/lane-session.md +25 -0
  63. package/kit/workflow/skills/orchestrate/references/lane-setup.md +37 -0
  64. package/kit/workflow/skills/orchestrate/references/plan-commit.md +22 -10
  65. package/kit/workflow/skills/orchestrate/references/sensitive-input.md +5 -7
  66. package/kit/workflow/skills/plan/SKILL.md +6 -5
  67. package/kit/workflow/skills/plan/references/template-English.md +4 -4
  68. package/kit/workflow/skills/plan/references/template-German.md +4 -4
  69. package/package.json +3 -1
  70. package/recipes/README.md +2 -1
  71. package/recipes/sources.md +1 -1
  72. package/schemas/jackherd.schema.json +59 -5
  73. package/src/check/agent-text-reviewers.ts +23 -0
  74. package/src/check/command.ts +2 -0
  75. package/src/check/plan-structure-check.ts +49 -2
  76. package/src/check/plan.ts +8 -1
  77. package/src/check/schema.ts +9 -0
  78. package/src/check/skill-budget-defaults.ts +55 -0
  79. package/src/check/skill-budgets.ts +185 -0
  80. package/src/cli.ts +1 -0
  81. package/src/config/auto-window.ts +8 -0
  82. package/src/config/link-kinds.ts +7 -1
  83. package/src/config/schema.ts +58 -24
  84. package/src/lane/close-board.ts +106 -0
  85. package/src/lane/close-check.ts +183 -0
  86. package/src/lane/close-command-line.ts +11 -0
  87. package/src/lane/close-command.ts +217 -0
  88. package/src/lane/close-options.ts +43 -0
  89. package/src/lane/close-survey.ts +83 -0
  90. package/src/lane/close.ts +141 -0
  91. package/src/lane/command.ts +13 -0
  92. package/src/lane-prompt/build.ts +74 -0
  93. package/src/lane-prompt/command.ts +87 -0
  94. package/src/lane-prompt/input.ts +116 -0
  95. package/src/lane-prompt/options.ts +68 -0
  96. package/src/lane-prompt/texts.ts +101 -0
  97. package/src/plan-format/clock.ts +8 -0
  98. package/src/plan-format/dependencies.ts +7 -4
  99. package/src/plan-format/index.ts +1 -0
  100. package/src/plan-format/short-names.ts +71 -0
  101. package/src/plan-format/waiting.ts +7 -2
  102. package/src/plans/appointment-rows.ts +59 -0
  103. package/src/plans/ask-coordinator.ts +77 -0
  104. package/src/plans/ask-target.ts +25 -0
  105. package/src/plans/child-stop.ts +51 -0
  106. package/src/plans/closable-records.ts +34 -0
  107. package/src/plans/command.ts +3 -2
  108. package/src/plans/fetch.ts +26 -3
  109. package/src/plans/git.ts +19 -5
  110. package/src/plans/herd-claim.ts +173 -0
  111. package/src/plans/herd-memory.ts +36 -25
  112. package/src/plans/lane-plans.ts +44 -13
  113. package/src/plans/lane-summary.ts +253 -45
  114. package/src/plans/layout-phase.ts +29 -7
  115. package/src/plans/layout-summary.ts +89 -18
  116. package/src/plans/layout.ts +262 -68
  117. package/src/plans/link-chips.ts +1 -0
  118. package/src/plans/live.ts +107 -20
  119. package/src/plans/phase-ids.ts +8 -0
  120. package/src/plans/phase-source.ts +15 -0
  121. package/src/plans/plan-cache.ts +28 -0
  122. package/src/plans/plan-heads.ts +54 -0
  123. package/src/plans/pull-requests.ts +17 -2
  124. package/src/plans/question-records.ts +48 -0
  125. package/src/plans/questions-read.ts +13 -0
  126. package/src/plans/read-cache.ts +8 -0
  127. package/src/plans/record-id.ts +4 -0
  128. package/src/plans/restart-decision.ts +13 -4
  129. package/src/plans/selection.ts +98 -18
  130. package/src/plans/sent-files-read.ts +6 -15
  131. package/src/plans/sent-files.ts +7 -14
  132. package/src/plans/settings.ts +2 -1
  133. package/src/plans/signalled-phase.ts +42 -0
  134. package/src/plans/signals-read.ts +12 -4
  135. package/src/plans/snapshot.ts +85 -62
  136. package/src/plans/sprint-export.ts +11 -11
  137. package/src/plans/sprint-page.ts +5 -5
  138. package/src/plans/subagent-phases.ts +95 -0
  139. package/src/plans/subagent-tasks.ts +51 -0
  140. package/src/plans/subagents-read.ts +66 -17
  141. package/src/plans/subagents.ts +64 -6
  142. package/src/plans/supervisor.ts +14 -4
  143. package/src/plans/symbols.ts +3 -0
  144. package/src/plans/task-name.ts +34 -0
  145. package/src/plans/texts.ts +23 -6
  146. package/src/plans/view.ts +17 -9
  147. package/src/plans/waiting-click.ts +192 -37
  148. package/src/plans/waiting-records.ts +39 -0
  149. package/src/plans/waiting-window.ts +8 -0
  150. package/src/preview/command.ts +25 -3
  151. package/src/shared/private-location.ts +89 -0
  152. package/src/shared/stat-cache.ts +48 -0
  153. package/src/sync/kit-source.ts +29 -10
  154. package/src/sync/kit.ts +2 -0
  155. package/src/sync/manifest.ts +2 -0
  156. package/src/sync/plan-ask-rules.ts +94 -0
  157. package/src/sync/plan-hooks.ts +2 -0
  158. package/src/sync/render.ts +3 -3
  159. package/src/sync/settings-hooks.ts +4 -1
  160. package/src/sync/sync-plan.ts +1 -0
  161. package/src/sync/sync.ts +24 -3
  162. package/src/task-signals/claude-read.ts +27 -10
  163. package/src/terminal/text.ts +8 -3
  164. package/src/version/skill-diff.ts +46 -0
  165. package/src/vocabulary/index.ts +16 -6
  166. package/src/waiting/command.ts +22 -7
  167. package/src/watch/appointments.ts +1 -2
  168. package/src/watch/asking-lanes.ts +40 -0
  169. package/src/watch/base-tip.ts +11 -0
  170. package/src/watch/command.ts +330 -105
  171. package/src/watch/heartbeat-progress.ts +6 -18
  172. package/src/watch/kit-update.ts +28 -9
  173. package/src/watch/lane-name.ts +6 -0
  174. package/src/watch/lane-progress.ts +40 -8
  175. package/src/watch/lane-report-file.ts +20 -5
  176. package/src/watch/lane-report.ts +32 -4
  177. package/src/watch/lane-sources.ts +31 -3
  178. package/src/watch/main-checkout.ts +34 -0
  179. package/src/watch/plan-badges-read.ts +13 -7
  180. package/src/watch/pull-requests.ts +6 -2
  181. package/src/watch/reference-paths.ts +87 -0
  182. package/src/watch/running-subagents.ts +43 -0
  183. package/src/watch/skill-change.ts +124 -32
  184. package/src/watch/source-change.ts +48 -0
  185. package/src/watch/sources.ts +6 -3
  186. package/src/watch/stream-output.ts +4 -3
  187. package/src/watch/task-parallel-auto.ts +208 -0
  188. package/src/watch/task-parallel-data-paths.ts +46 -0
  189. package/src/watch/task-parallel-input.ts +5 -15
  190. package/src/watch/task-parallel-read.ts +3 -1
  191. package/src/watch/task-parallel.ts +36 -7
  192. package/src/watch/task-signal-missing.ts +69 -0
  193. package/src/watch/task-signal-read.ts +29 -0
  194. package/src/watch/texts.ts +10 -0
  195. package/src/watch/update-available.ts +6 -3
  196. package/src/watch/watch-events.ts +23 -0
  197. package/src/watch/watcher-lock.ts +25 -29
  198. package/kit/hooks/hooks/merge-guard/plan-approval-record.ts +0 -23
  199. package/kit/hooks/hooks/merge-guard/plan-approval-sources.ts +0 -196
  200. package/kit/hooks/hooks/merge-guard/plan-approval.ts +0 -168
  201. package/kit/hooks/hooks/merge-guard/plan-growth.ts +0 -40
  202. package/kit/hooks/hooks/merge-guard/plan-pointers.ts +0 -194
  203. package/kit/hooks/hooks/shared/command-text-runners.ts +0 -71
  204. package/kit/hooks/hooks/shared/plan-open-phases.ts +0 -114
package/CHANGELOG.md CHANGED
@@ -2,6 +2,36 @@
2
2
 
3
3
  What changed for people who use jackherd, newest first.
4
4
 
5
+ ## 1.10.0
6
+
7
+ - **`jh sync` writes the ask rules the merge guard left behind.** A project sync adds `permissions.ask` rules for `gh run rerun` and the `gh api` writes that merge, write a file, dispatch or rerun a workflow, or move a ref; it records them in the lock, removes one only when the kit drops it and only if a kit shipped it, and leaves your own rules alone. Claude Code applies a matching ask rule even when a hook answers `allow`.
8
+ - **A standing approval replaces the plan approval.** The coordinator asks once per plan with an end time, at most 24 hours ahead; until then the merge guard lets the plan's lane pull requests merge on green checks, except workflows, migrations, env files and the guards' own files (hooks, settings, the jackherd config, git hooks). It lives in that chat only: after a hand-over the new chat asks again. The plan approval, its hook and its pointers under `.jackherd/approvals/plans/` are gone.
9
+ - **`guard.planApproval` is now `guard.alwaysAsk` and `guard.requiredChecks`.** The old key is still read until 2.0.0, so your config keeps working.
10
+ - **A lane that works without reporting its task is noticed.** A new `PostToolUse` hook `task-hint` reminds a lane once per task to run `jh task start` (off with `hints.taskSignal: false`); the watcher reports `lane <name> no task signal (P<n>)` once per lane and phase after `watch.taskSignalMinutes` (default 10) of work. The herd takes the current phase from the lane's newest reported task, else from its branch name, and a phase with a signal never reads `next`.
11
+ - **The herd and the watcher read ticked boxes from open pull requests.** A phase whose pull request ticks it further shows that count, not the one from the base branch; `jh lane prompt` reads the plan from the lane's worktree and leaves out a phase finished in either copy. Pull request heads are fetched into `refs/jackherd/pr/<n>`, apart from your branches.
12
+ - **`jh lane close <lane> [--dry-run]` closes a finished lane in one run.** It first checks that nothing is lost and lists every reason it refuses: the board does not name the lane, its branch has no merged pull request whose merge commit holds the same tree, the worktree holds anything beyond `node_modules`, the `.worktreeinclude` files and known build output, the hand-over directory is not empty, an agent still works in it, or the command runs inside it. Then it exits the agent, checks again, removes the worktree (never forced) and the herdr workspace, deletes the branch at the checked head and marks the board rows with a history row. `--dry-run` only checks. `jh sync` adds ask rules, so Claude Code asks before every call, `--dry-run` included. The watcher's `plan finished` line names the dry run, and the orchestrate skill shrinks its closing steps to the command.
13
+ - **The herd shows a lane's subagents on their task.** A lane's own review ran as a subagent the herd never listed. Now the subagents of the lane's worktree count in the heartbeat, a review sits on the gate item that names its type (or shows `▶ Subagent: <description>`), and a subagent whose description starts with an open task's short name sits on that task. `task-hint` also reminds of the task signal when a subagent starts. An opened task shows its short name once.
14
+ - **The watcher reminds you when the main checkout is out of date.** While it lags behind `origin/<base>` or has another branch or a detached head checked out, every report and heartbeat carries a line (`main checkout behind origin/<base> by <n> (git merge --ff-only origin/<base>)`, `main checkout on <branch>, not <base>`). A watcher run from source ends with `kit updated … (source changed)` when its sources change, so the herd restarts on the new code.
15
+ - **A question from a working lane is reported at once.** A lane waiting on background subagents ends no turn, so its question waited for them. The watcher now reports a changed questions section of a working lane as `report … questions: <n>`, once per change and not again after a restart. The `open-question` hook treats "noch nicht" and "not yet" as putting a question off.
16
+ - **A source keeps its interval over a restart of the watcher.** Each `watch.sources` entry remembers when it last ran, so a restart no longer runs every source at once.
17
+
18
+ - **No mandatory rule is lost when a skill gets shorter.** The kit's own test holds the 67 rules the coordinator and the lanes must follow and finds each in its skill or in a reference the skill links, so a cut that drops one turns red.
19
+ - **`jh check` measures what an agent loads, in words.** Every installed kit skill, reference, agent and always-loaded kit rule, each project supplement (`.claude/project/<skill>.md`, 800 words) and each skill of your own (2000 words) is counted against a word budget: a warning above it, an error above it plus 10 %. Set your own under `check.skillBudgets` in `.jackherd/config.json`, by path or glob; a key joining two paths with ` + ` budgets what loads together. The kit's test reads the same budgets, which now sit at the measured size plus at most 10 %, and every budget names a file the kit ships.
20
+ - **A rare watcher line names the file that handles it.** A rare event ends in `· read <skill directory>/references/<file>`, so the coordinator reads that procedure when it is needed instead of carrying it in every session; a kit update reports changed, new and deleted references like the skill. The heartbeat names the tick count of every working lane and its running subagents (`P12 7/12, 3 subagents`).
21
+ - **The herd shows what waits for you and the board's appointments as blocks of their own.** `Waiting for you` lists the board's open questions, the files sent to you and the dialog questions you left open (a new stop hook `open-question` records them); a click sends the coordinator a fixed line with the question's id. `Appointments` lists the open rows of the board's appointments section. Both fold together. A lane row shows the task's short name, a running reviewer makes its phase active, finished phases read `done P1–P2` beside `next P4–P5`, and the clock is `HH:MM`. One herd runs per terminal: a new one stops the older on the same tty (Linux). The herd reads session logs and plans through a cache and redraws only a changed frame.
22
+ - **`jh lane prompt <lane>` writes a lane's first prompt** from its board row and the plan's dependency table, in the plan's language (`--board`, `--out`, `--handover` for a restart). It tells the lane its phases, its write set, that it may tick its own tasks, and that code and tests go through Write and Edit, never a here-document in Bash.
23
+ - **The orchestrate skill is about a third shorter.** The coordinator loads about 7400 words instead of 11400; rare steps (setting up and closing a lane, the rare events, a draft for review, an acceptance, a lane's hand-over) moved to references under `references/` that name their section and trigger. Sections and step numbers stay.
24
+ - **Every file jackherd keeps outside the repository lies in a private directory it checks first.** The board page, `jh preview`'s page, the watcher's lock and state, skill diffs and the herd's lane memory go below `$XDG_RUNTIME_DIR` (when it is the user's own and closed to others) or `<tmp>/jackherd-<uid>`, written through a temporary file. The herd reads the watcher's files only from a private directory and only regular files.
25
+ - **The implement, plan and follow skills are shorter, and every task has a short name.** A plan task starts with `- [ ] **Name**: …`, at most 40 characters; the plan templates use it, and the herd shows it. `/follow` reads the rules every recipe follows from the recipe index.
26
+ - **`skill-review` reviews changed agent texts.** `jh check` routes a change to Markdown under `skills/`, `agents/`, `rules/`, `.claude/project/` and to `CLAUDE.md`/`AGENTS.md` to the new kit agent, which judges it against `writing-for-agents.md`: no-op, the same rule twice, the wrong rung, a late trigger, a negation, an open step. `jh check` warns about a new plan task without a short name or with one over 40 characters, and about a phase title over 50.
27
+ - **The coordinator may start parallel tasks without asking, inside a window you set.** `watch.parallelTasks.auto: { "since": "<date>", "until": "<date>" }`, at most seven days, off by default. Inside it, a `parallel tasks` proposal ends in `· auto until <day>` when every limit holds: running work in the lane, at most two extra subagents, cost under `watch.parallelTasks.maxTokens`, no path that always asks (nor a migration or env file below a written directory), read from the plan at the base branch. Otherwise it ends in `· asks: <limit>` and the coordinator asks as before. The end of the window is reported once.
28
+ - **The merge guard and the git guard read only the parsed script.** They count a command at a program position (past options, chains, subshells, substitutions, `env`/`sudo`/`xargs`, `sh -c`, `eval`, aliases and functions), and no longer search interpreter code, here-document data or quoted text for command words, which caused most of their false alarms. A call that only reads passes (`--help`, `gh … view`, `gh … list`), and a script the parser cannot read asks only when `gh` stands where a command starts. Deliberately accepted: `subprocess.run(['gh', 'pr', 'merge', …])` in an interpreter no longer asks.
29
+ - **The merge guard guards only `gh pr merge` and `gh workflow run` by default.** `gh run rerun` and the `gh api` writes left `guard.commands`; `jh sync` now writes them as ask rules (first entry above).
30
+ - **`merge.withoutChecks` no longer merges a file on a path that always asks** (workflows, migrations, lockfiles, env files, agent instructions, `guard.alwaysAsk`), besides `package.json` and changelogs.
31
+ - **A coordinator hand-over carries your standing approvals.** The hand-over script takes `--board <board> --approval "<line> <DD.MM.> <HH:MM>"` and quotes that board row word for word into the new chat's first input, so a commit you already approved is not refused there. It refuses a row without your dated mark, an end the row does not name, and an approval that has ended.
32
+ - **npm finds jackherd under `plan-driven` and `multi-agent` too**, beside `orchestration` and the other keywords.
33
+ - **The README says what jackherd adds to Claude Code** after a check against the code and the Claude Code docs, recommends herdr and names who it is for. `INSTALL.md` lists all eleven project hooks, and `/orchestrate` sends migrations and writes against a shared environment to you every time.
34
+
5
35
  ## 1.9.0
6
36
 
7
37
  - **An open lane in the herd takes fewer lines, and a red pull request says what failed where.** A failed test hangs under its check as `└ ✗ <package> · <test>`, the package read from the log (the monorepo task header, else the test file), the test path dim with `›`; a cut name unfolds on a click and folds on the next. The pull request title stays on one line, a lane's only link chip moves onto its session line, and the finished phases are ranges (`✓ P1–P7, P10–P15`). The label column (SESSION, PR, NOW, NEXT, LATER, DONE, LINKS) is gone: the tree lines and each row's mark (`⎇`, `↗`, `▶`/`▷`, `✓`) say what it is, and every row under a lane gains the 9 columns the labels took. Each running phase is a branch of its own, and the other open phases share one line, `next P18–P25` (ranges, the other plans by name after them). A reported task in other words still finds its plan task, and a lane that reports a task shows no guessed `▷`. The progress column is calmer: no `◐`, the `🥣` cell on a folded lane only from `warn`, and a hidden cell keeps its width, so no bar moves; the checkbox count stands on every lane line. A folded lane's title line in a narrow pane starts in the column of the lane's name, where an open lane's branches start too. A click on a for-you row now also lands right while `now:` lines stand above it.
package/INSTALL.md CHANGED
@@ -54,7 +54,7 @@ When `init` creates a new config, it asks:
54
54
  | Use it when | the team shares the kit through the repository | the repository must stay untouched, not even untracked files |
55
55
  | Lives in | the project's `.claude/` | `~/.claude/` |
56
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 |
57
+ | Hooks | `git-guard`, `watch-guard`, `merge-guard`, `approval-gate`, `model-gate`, `model-log`, `ask-hint`, `open-question`, `sent-file`, `format-on-edit` | none; `init --global` offers only a `SessionStart` update notice |
58
58
  | Config | `.jackherd/config.json` | `~/.jackherd/config.json`, used where a repository has none |
59
59
  | Update | `jh sync` | `jh update` |
60
60
 
package/README.md CHANGED
@@ -7,27 +7,33 @@
7
7
 
8
8
  # Your agents write code. Jack keeps the work moving.
9
9
 
10
- **Let AI do the work. Stay in control.** jackherd is a coordination layer for your coding agents, built around a plan in your repository. Claude Code alone builds it one phase at a time; with [herdr](https://herdr.dev), a terminal for coding agents, phases run in parallel, each in its own git worktree. Jack, the coordinator, keeps these lanes moving: he answers what the plan already settles and brings you only the decisions that need you.
10
+ **Let AI do the work. Stay in control.** jackherd orchestrates your coding agents, alone or as a team. You plan once, in your repository; Jack, the coordinator, runs it in parallel lanes.
11
+
12
+ He settles the small questions himself and asks only what needs you. What's running, blocked or waiting for you sits on one screen.
13
+
14
+ For developers who'd rather make the calls than babysit their agents.
15
+
16
+ **We recommend [herdr](https://herdr.dev), a terminal for coding agents:** Jack, the lanes and the watcher run there; without it, one Claude Code session plans and builds, with independent phases as parallel subagents.
11
17
 
12
18
  Install with one command, run `/jh-start` in a repository, and it offers your first plan: [Quickstart](#quickstart).<br><img width="1" height="8" alt="" src="https://unpkg.com/jackherd/assets/icons/space.svg?v=2">
13
19
 
14
20
  <p>
15
21
  <img src="https://unpkg.com/jackherd/docs/workflow-dark.svg" width="720" alt="jackherd in herdr: a lane hits a question, Jack asks you as a choice, you pick, and the lane works on while the herd view counts its progress.">
16
22
  <br>
17
- <sub>Parallel lanes in herdr. Without herdr, you plan and build one phase at a time.</sub><br><img width="1" height="8" alt="" src="https://unpkg.com/jackherd/assets/icons/space.svg?v=2">
23
+ <sub>Jack and parallel lanes in herdr (recommended).</sub><br><img width="1" height="8" alt="" src="https://unpkg.com/jackherd/assets/icons/space.svg?v=2">
18
24
  </p>
19
25
 
20
- <p><img src="https://unpkg.com/jackherd/assets/icons/decide.svg?v=2" width="24" height="34" align="middle" alt=""> <b>You decide. Jack takes care of the lanes.</b><br>Jack answers what the plan already settles, records his decisions on the board, and asks you about the rest. One approval per pull request covers its commits and pushes. Jack merges automatically only when you've allowed it and checks pass.</p>
26
+ <p><img src="https://unpkg.com/jackherd/assets/icons/decide.svg?v=2" width="24" height="34" align="middle" alt=""> <b>You make the calls that matter.</b><br>Jack answers what the plan or a rule already settles and records each answer on the board, where you can check it. The rest reaches you as a short choice. Approve a plan once, and its pull requests merge on green checks, except on paths you marked to always ask.</p>
21
27
 
22
- <p><img src="https://unpkg.com/jackherd/assets/icons/lanes.svg?v=2" width="24" height="34" align="middle" alt=""> <b>Lanes stay apart.</b><br>One phase per lane, on its own branch and worktree. Jack keeps the board's merge order and has the other lanes rebase after each merge.</p>
28
+ <p><img src="https://unpkg.com/jackherd/assets/icons/bark.svg?v=2" width="24" height="34" align="middle" alt=""> <b>Jack barks when something moves.</b><br>One view of the whole herd: each lane's progress, pull request and checks, and on top whatever waits for your answer. In herdr it also shows what each agent is doing and every new review comment. You stop hopping between terminals and holding the state in your head.</p>
23
29
 
24
- <p><img src="https://unpkg.com/jackherd/assets/icons/bark.svg?v=2" width="24" height="34" align="middle" alt=""> <b>Jack barks when something moves.</b><br>One live view of the herd, all your lanes: state, plan progress, pull request, checks, feedback, context fill.</p>
30
+ <p><img src="https://unpkg.com/jackherd/assets/icons/ear.svg?v=2" width="24" height="34" align="middle" alt=""> <b>Jack listens, on GitHub and beyond.</b><br>A review comment, from a teammate or a bot, goes to its lane, which fixes it with a test and answers in the thread; a judgement call comes to you. <code>/jh-follow</code> follows any tool with a CLI or an API, with recipes for Jira, Linear, Sentry and status pages as a head start, and Jack reports changes. Runs in herdr.</p>
25
31
 
26
- <p><img src="https://unpkg.com/jackherd/assets/icons/ear.svg?v=2" width="24" height="34" align="middle" alt=""> <b>Jack listens beyond GitHub.</b><br><code>/jh-follow</code> Jira, Linear, Sentry or a status page, and Jack reports changes while he runs in herdr.</p>
32
+ <p><img src="https://unpkg.com/jackherd/assets/icons/lanes.svg?v=2" width="24" height="34" align="middle" alt=""> <b>Parallel where it pays.</b><br>The plan says what each phase writes and waits for, and jackherd works out what can run side by side: independent phases as subagents in one session, bigger ones in a lane of their own in herdr, with its own branch and worktree. Where more could run in parallel at little cost, Jack suggests it. He keeps the merge order and has the others rebase after each merge.</p>
27
33
 
28
- <p><img src="https://unpkg.com/jackherd/assets/icons/plan.svg?v=2" width="24" height="34" align="middle" alt=""> <b>One workflow for the whole team.</b><br>After <code>jh init</code>, plans, skills, rules and guard hooks are files in your repository. Commit them, and everyone who opens it in Claude Code works with the same set; changes to it go through your usual pull request review.</p>
34
+ <p><img src="https://unpkg.com/jackherd/assets/icons/plan.svg?v=2" width="24" height="34" align="middle" alt=""> <b>One workflow, solo or team.</b><br>Plans, boards and Jack's decisions live in your repository, so everyone sees what was planned, what was settled and what comes next, and workflow changes go through your usual review. <code>jh init</code> adds the skills, rules and guard hooks through Claude Code's own project settings.</p>
29
35
 
30
- <p><img src="https://unpkg.com/jackherd/assets/icons/bowl.svg?v=2" width="24" height="34" align="middle" alt=""> <b>Long chats get a fresh start.</b><br>A lane whose context fills up continues in a new chat at the end of its phase, from the ticked-off plan and a hand-over note.</p>
36
+ <p><img src="https://unpkg.com/jackherd/assets/icons/bowl.svg?v=2" width="24" height="34" align="middle" alt=""> <b>Pick up where you left off.</b><br>The ticked-off plan and the board carry the state, not the chat. A lane whose context runs high continues in a fresh chat at the end of its phase; Jack hands himself over the same way.</p>
31
37
 
32
38
  ## Quickstart
33
39
 
@@ -40,7 +46,7 @@ Install with one command, run `/jh-start` in a repository, and it offers your fi
40
46
  Already in a coding agent? Paste this instead:
41
47
 
42
48
  ```text
43
- Install jackherd by following https://unpkg.com/jackherd/INSTALL.md. Ask me the setup questions it lists; herdr is optional.
49
+ Install jackherd by following https://unpkg.com/jackherd/INSTALL.md. Ask me the setup questions it lists; herdr is recommended, not required.
44
50
  ```
45
51
  2. **Start Claude Code in a repository.**
46
52
 
@@ -57,7 +63,7 @@ Install with one command, run `/jh-start` in a repository, and it offers your fi
57
63
  ```text
58
64
  /jh-implement docs/plans/<plan>.md phase 1
59
65
  ```
60
- 5. **Run phases in parallel with herdr (optional).** In Claude Code inside herdr, hand Jack the board:
66
+ 5. **Run phases in parallel with herdr (recommended).** Lanes, the full herd view, review-comment fixes and ticket reports need it. In Claude Code inside herdr, hand Jack the board:
61
67
 
62
68
  ```text
63
69
  /jh-orchestrate docs/plans/board-<name>/board.md
@@ -76,27 +82,25 @@ By hand (Bun 1.3+): `bun add -g jackherd` or `npm install -g jackherd`, then `jh
76
82
  | --- | --- | --- |
77
83
  | Starting a ready phase in a lane | Commits, push and pull request, once per pull request | Writing lane code himself |
78
84
  | Answering what the plan or a rule settles, on record in the board | Every other decision a lane raises | Merging a pull request on a path that always asks, without asking you |
79
- | Merging a pull request of an approved plan on green checks | A plan approval, once per plan | Storing a permission on your behalf |
80
- | Rebasing the lanes after a merge | Migrations and writes against a shared environment | |
81
- | Handing a session over to a fresh chat before its context fills up | Closing a lane | |
85
+ | Merging a plan's pull requests on green checks for a while | A standing approval, until a time you pick | Storing a permission on your behalf |
86
+ | Having the lanes rebase after a merge | Migrations and writes against a shared environment | |
87
+ | Handing a lane over to a fresh chat at the end of a phase, once its context runs high | Closing a lane | |
82
88
 
83
89
  </details>
84
90
 
85
91
  <details>
86
92
  <summary><b>Guarded commands and merges</b></summary>
87
93
 
88
- In a project install, the `merge-guard` hook asks you before `gh pr merge`, `gh workflow run`, `gh run rerun` and a `gh api` write that merges, writes a file, dispatches or reruns a workflow, or moves a ref, also behind a chain, a wrapper or `sh -c`. The `git-guard` hook refuses a git command that hides a commit, push, rebase, reset, checkout or merge from your permission rules (`git -C … push`, `sh -c "…"`), every push to the base branch, and every force push, skipped hook and hard reset.
94
+ A project install adds two hooks to Claude Code's [permission rules](https://code.claude.com/docs/en/permissions):
89
95
 
90
- A merge passes without a question only on an answer from your own dialog: self-merge for that pull request, or the approval of its plan, with green checks and no file on a path that always asks (workflows, migrations, lockfiles, `package.json`, env files, agent instructions, and your own list). `merge.withoutChecks` lets a pull request that only touches such paths as docs merge without waiting for checks.
96
+ - **`merge-guard`** asks before `gh pr merge` and `gh workflow run`, also behind options, a chain or `sh -c`. Calls that only read pass.
97
+ - **`git-guard`** refuses hidden commits and pushes (`git -C … push`), pushes to the base branch, force pushes without lease, skipped hooks and hard resets.
91
98
 
92
- ```json
93
- {
94
- "guard": { "planApproval": { "exceptPaths": ["src/billing/**", "src/auth/**"] } },
95
- "merge": { "withoutChecks": ["docs/**/*.md"] }
96
- }
97
- ```
99
+ `jh sync` also adds ask rules for check reruns and `gh api` writes.
100
+
101
+ A merge passes without a question on green checks after self-merge, or under a standing approval: one plan's lanes until a time you pick, at most 24 hours; workflows, migrations, env files and the guards' own files still ask. A pull request whose files all match `merge.withoutChecks`, such as docs only, skips question and checks, unless a file lies on a path that always asks.
98
102
 
99
- The guards catch an agent's mistakes, not an agent set on getting around them; for that, use branch protection. A global install ships no hooks, and the skills ask on their own.
103
+ The guards catch mistakes, not intent; for that, use branch protection. The hooks need Bun; a global install ships none.
100
104
 
101
105
  </details>
102
106
 
@@ -115,8 +119,8 @@ A **plan** is Markdown with phases, checkbox tasks, a model and a review gate pe
115
119
  | `/jh-implement` | builds and tests one phase and ticks it off; independent phases run as parallel subagents |
116
120
  | `/jh-orchestrate` | runs a board's lanes in herdr, as Jack |
117
121
  | `/jh-pre-commit` | runs `jh check` and the reviewers, proposes the commit message and asks before committing |
118
- | `/jh-follow` | follows a ticket or a status page outside GitHub |
119
- | `/jh-diagnose` | turns a bug into a failing command, then hands the fix on |
122
+ | `/jh-follow` | follows something outside GitHub: a ticket, error logs, a status page |
123
+ | `/jh-diagnose` | turns a bug into a failing command, fixes it with a regression test |
120
124
 
121
125
  The global install names the skills with a `jh-` prefix; a project install uses none, unless the project already has skills of the same names.
122
126
 
@@ -125,26 +129,33 @@ The global install names the skills with a `jh-` prefix; a project install uses
125
129
  <details>
126
130
  <summary><b>Which model runs each phase</b></summary>
127
131
 
128
- Each phase in the plan names its model: the planning model (Opus by default) where a mistake is expensive, such as data, security or a migration, and the cheaper implementation model (Sonnet by default) for everything else. `/jh-implement` and Jack start each phase on its model, and in a project install a hook stops a phase handed to a weaker one. Set the models per role under `models` in the config.
132
+ Each phase in the plan names its model: the planning model (Opus by default) where a mistake is expensive, such as data, security or a migration, and the cheaper implementation model (Sonnet by default) for everything else. `/jh-implement` and Jack start each phase on its model. In a project install, a hook stops a phase's subagent started on a weaker one, and the watcher tells Jack about a lane that runs on one. Set the models per role under `models` in the config.
129
133
 
130
134
  </details>
131
135
 
132
136
  <details>
133
- <summary><b>The watcher and tickets outside GitHub</b></summary>
137
+ <summary><b>The watcher and sources outside GitHub</b></summary>
134
138
 
135
- Jack doesn't watch the terminals himself. `jh watch`, which he starts, wakes him when a lane is blocked or done, a check turns red, a pull request gets feedback, a lane's context fills up or a lane waits for an approval; deploys and previews too, when configured.
139
+ Jack doesn't watch the terminals himself. `jh watch`, which he starts, wakes him when a lane is blocked or done, a check turns red, a pull request gets feedback, a lane's context fills up, a lane waits for an approval or works for ten minutes without reporting its task; deploys and previews too, when configured.
136
140
 
137
- `/jh-follow PROJ-123` adds a ticket, an issue or a status page: it checks the tool and its login, shows you a dry run and writes the source to `watch.sources` after your yes. The [recipes](https://unpkg.com/jackherd/recipes/README.md) cover Jira, Linear, GitHub issues and Projects, Sentry and status pages. The reports need herdr; a source can be set up and dry-run without it.
141
+ `/jh-follow PROJ-123` adds anything a command can print as JSON: a ticket, an issue, error logs, a status page. It checks the tool and its login, shows you a dry run and writes the source to `watch.sources` after your yes. A token stays in the environment, read by a small Bun script, never in an argument. Each source runs on its own interval, kept across a restart of the watcher. The [recipes](https://unpkg.com/jackherd/recipes/README.md) cover Jira, Linear, GitHub issues and Projects, Sentry and status pages. The reports need herdr; a source can be set up and dry-run without it.
138
142
 
139
143
  </details>
140
144
 
141
145
  <details>
142
146
  <summary><b>The herd view</b></summary>
143
147
 
144
- `jh herd` shows every lane of a board: agent state, plan progress, pull request, checks and context fill. It only reads.
148
+ `jh herd` shows every lane of a board: plan progress, pull request and checks, and with herdr agent state and context fill. It changes nothing in your repository.
149
+
150
+ On top, **Waiting for you** lists what needs your answer: a file or Artifact sent to you (`📎`) and open questions from the board or a session's dialog (`?`). **Appointments** below it shows the dated rows of the board's appointments section. A click folds either block.
145
151
 
146
152
  ```text
147
153
  🐾 Board 01 11:38
154
+ ▾ Waiting for you · 2 ──────────────────────────────────────────────────────────
155
+ ▌ 📎 search-box-mockup.html
156
+ ▌ ? Ship the fee change before Friday? [ask]
157
+ ▾ Appointments · 1 ─────────────────────────────────────────────────────────────
158
+ ▌ ◷ Mo 09:00 Release 1.4 to production
148
159
  ────────────────────────────────────────────────────────────────────────────────
149
160
  ▾ ◆ search-ui blocked ██████░░░░ 6/9
150
161
  ├─ Search box docs ⎇ feat/search-ui ↑1 ±4 🥣 62 %
@@ -164,7 +175,7 @@ Jack doesn't watch the terminals himself. `jh watch`, which he starts, wakes him
164
175
  ────────────────────────────────────────────────────────────────────────────────
165
176
  · no agent (1) docs-site 1/3
166
177
  ────────────────────────────────────────────────────────────────────────────────
167
- ↑↓ select →← open a show ticked s html q quit ? help
178
+ ↑↓ select →← open a ticked tasks s html q quit ? help
168
179
  ```
169
180
 
170
181
  </details>
@@ -180,22 +191,29 @@ Jack doesn't watch the terminals himself. `jh watch`, which he starts, wakes him
180
191
  | `jh check` | types, lint and tests of what you touched, plus the plan and board checks (`--skip-slow`, `--parallel <plan>`) |
181
192
  | `jh herd` | the live view (`--once`, `--lane <name>`) |
182
193
  | `jh watch` | the watcher Jack runs |
194
+ | `jh lane prompt <lane>` | writes a lane's first prompt from its board row and its plan (`--out`, `--handover`) |
195
+ | `jh lane close <lane>` | removes a finished lane: agent, worktree, herdr workspace and branch, and marks the board; first checks that nothing is lost, and refuses with every reason; `--dry-run` only checks. Claude Code asks before each call |
183
196
  | `jh preview <file>` | renders a Markdown file through the GitHub API into a preview in GitHub and npm style |
184
197
  | `jh waiting` | the files and Artifacts sent to you that you have not answered yet (`close <id>…` or `close --all` once you have) |
185
198
  | `jh context` | how full an agent's context is, as JSON |
186
199
  | `jh config` | the commands and the base branch the repository uses |
187
200
 
188
- Without a global install, run them as `bunx jackherd <command>`. The config is `.jackherd/config.json` in a project and `~/.jackherd/config.json` globally, checked against [one schema](https://unpkg.com/jackherd/schemas/jackherd.schema.json) your editor completes. What usually needs setting:
201
+ Without a global install, run them as `bunx jackherd <command>`. The config is `.jackherd/config.json` in a project and `~/.jackherd/config.json` globally, checked against [one schema](https://unpkg.com/jackherd/schemas/jackherd.schema.json), which your editor completes in the project config. What usually needs setting:
189
202
 
190
203
  ```json
191
204
  {
192
205
  "responseLanguage": "de",
193
206
  "terminal": { "adapter": "herdr" },
194
207
  "lanes": { "copy": ["apps/web/.env"] },
195
- "check": { "reviewerRoutes": [{ "reviewer": "jh-uncle-bob", "globs": ["src/**/*.ts"], "why": "Code" }] }
208
+ "check": {
209
+ "reviewerRoutes": [{ "reviewer": "jh-uncle-bob", "globs": ["src/**/*.ts"], "why": "Code" }],
210
+ "skillBudgets": { ".claude/skills/release/SKILL.md": 1200 }
211
+ }
196
212
  }
197
213
  ```
198
214
 
215
+ `jh check` counts the words of every skill, agent and always-loaded rule an agent reads, and warns above its budget; `check.skillBudgets` sets your own, by path or glob. `watch.parallelTasks.auto` (`since`, `until`, at most seven days) lets Jack start up to two extra subagents in a running lane without asking, under the cost limit and away from paths that always ask; he switches it on only after asking you.
216
+
199
217
  </details>
200
218
 
201
219
  <details>
@@ -19,9 +19,13 @@ var SELF_MERGE_LABEL = {
19
19
  [PLAN_LANGUAGE.en]: "Approve, and merge it yourself after green checks",
20
20
  [PLAN_LANGUAGE.de]: "Freigeben, und nach gr\xFCnen Checks selbst mergen"
21
21
  };
22
- var PLAN_APPROVAL_LABELS = {
23
- [PLAN_LANGUAGE.en]: { approve: "Approve the plan", revoke: "Revoke the plan approval" },
24
- [PLAN_LANGUAGE.de]: { approve: "Plan freigeben", revoke: "Plan-Freigabe widerrufen" }
22
+ var STANDING_APPROVAL_TEXTS = {
23
+ [PLAN_LANGUAGE.en]: { approve: "Approve standing", until: "until" },
24
+ [PLAN_LANGUAGE.de]: { approve: "Stehend freigeben", until: "bis" }
25
+ };
26
+ var LANE_PROMPT_OPENING = {
27
+ [PLAN_LANGUAGE.en]: "You are the lane",
28
+ [PLAN_LANGUAGE.de]: "Du bist die Spur"
25
29
  };
26
30
  var APPROVAL_REASONS = {
27
31
  pending: "Approval pending with the coordinator; retry the same call once it is approved.",
@@ -156,7 +160,8 @@ export {
156
160
  RESPONSE_ADDRESS,
157
161
  ADDRESS_FORM,
158
162
  SELF_MERGE_LABEL,
159
- PLAN_APPROVAL_LABELS,
163
+ STANDING_APPROVAL_TEXTS,
164
+ LANE_PROMPT_OPENING,
160
165
  APPROVAL_TEXTS,
161
166
  APPOINTMENT_VOCABULARY,
162
167
  WAITING_VOCABULARY,
package/dist/config.d.ts CHANGED
@@ -125,7 +125,12 @@ declare const jackherdConfigSchema: z.ZodObject<{
125
125
  }, z.core.$strict>>;
126
126
  parallelTasks: z.ZodPrefault<z.ZodObject<{
127
127
  maxTokens: z.ZodDefault<z.ZodNumber>;
128
+ auto: z.ZodOptional<z.ZodObject<{
129
+ since: z.ZodISODate;
130
+ until: z.ZodISODate;
131
+ }, z.core.$strict>>;
128
132
  }, z.core.$strict>>;
133
+ taskSignalMinutes: z.ZodDefault<z.ZodNumber>;
129
134
  previews: z.ZodOptional<z.ZodObject<{
130
135
  deployments: z.ZodDefault<z.ZodBoolean>;
131
136
  authors: z.ZodDefault<z.ZodArray<z.ZodString>>;
@@ -152,7 +157,7 @@ declare const jackherdConfigSchema: z.ZodObject<{
152
157
  reportOn: z.ZodOptional<z.ZodArray<z.ZodString>>;
153
158
  }, z.core.$strict>>>;
154
159
  }, z.core.$strict>>;
155
- guard: z.ZodPrefault<z.ZodObject<{
160
+ guard: z.ZodPrefault<z.ZodPipe<z.ZodObject<{
156
161
  commands: z.ZodDefault<z.ZodArray<z.ZodString>>;
157
162
  selfMerge: z.ZodPrefault<z.ZodObject<{
158
163
  offer: z.ZodDefault<z.ZodBoolean>;
@@ -161,16 +166,45 @@ declare const jackherdConfigSchema: z.ZodObject<{
161
166
  approvals: z.ZodPrefault<z.ZodObject<{
162
167
  waitMinutes: z.ZodDefault<z.ZodNumber>;
163
168
  }, z.core.$strict>>;
164
- planApproval: z.ZodPrefault<z.ZodObject<{
165
- exceptPaths: z.ZodDefault<z.ZodArray<z.ZodString>>;
169
+ alwaysAsk: z.ZodDefault<z.ZodArray<z.ZodString>>;
170
+ requiredChecks: z.ZodOptional<z.ZodArray<z.ZodString>>;
171
+ planApproval: z.ZodOptional<z.ZodObject<{
172
+ exceptPaths: z.ZodOptional<z.ZodArray<z.ZodString>>;
166
173
  requiredChecks: z.ZodOptional<z.ZodArray<z.ZodString>>;
167
174
  }, z.core.$strict>>;
168
- }, z.core.$strict>>;
175
+ }, z.core.$strict>, z.ZodTransform<{
176
+ requiredChecks?: string[] | undefined;
177
+ alwaysAsk: string[];
178
+ commands: string[];
179
+ selfMerge: {
180
+ offer: boolean;
181
+ approvalMinutes: number;
182
+ };
183
+ approvals: {
184
+ waitMinutes: number;
185
+ };
186
+ }, {
187
+ commands: string[];
188
+ selfMerge: {
189
+ offer: boolean;
190
+ approvalMinutes: number;
191
+ };
192
+ approvals: {
193
+ waitMinutes: number;
194
+ };
195
+ alwaysAsk: string[];
196
+ requiredChecks?: string[] | undefined;
197
+ planApproval?: {
198
+ exceptPaths?: string[] | undefined;
199
+ requiredChecks?: string[] | undefined;
200
+ } | undefined;
201
+ }>>>;
169
202
  merge: z.ZodPrefault<z.ZodObject<{
170
203
  withoutChecks: z.ZodDefault<z.ZodArray<z.ZodString>>;
171
204
  }, z.core.$strict>>;
172
205
  hints: z.ZodPrefault<z.ZodObject<{
173
206
  askUserQuestion: z.ZodDefault<z.ZodBoolean>;
207
+ taskSignal: z.ZodDefault<z.ZodBoolean>;
174
208
  }, z.core.$strict>>;
175
209
  check: z.ZodOptional<z.ZodObject<{
176
210
  lintFilesCommand: z.ZodDefault<z.ZodString>;
@@ -194,6 +228,7 @@ declare const jackherdConfigSchema: z.ZodObject<{
194
228
  exclude: z.ZodDefault<z.ZodArray<z.ZodString>>;
195
229
  reviewer: z.ZodDefault<z.ZodString>;
196
230
  }, z.core.$strict>>;
231
+ skillBudgets: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodNumber>>;
197
232
  looseTests: z.ZodDefault<z.ZodBoolean>;
198
233
  }, z.core.$strict>>;
199
234
  }, z.core.$strict>;
package/dist/config.js CHANGED
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  PLAN_LANGUAGE,
3
3
  RESPONSE_ADDRESS
4
- } from "./chunk-MCP4XJQH.js";
4
+ } from "./chunk-5MQBCUQ5.js";
5
5
 
6
6
  // src/config/json-schema.ts
7
7
  import { z as z3 } from "zod";
@@ -129,6 +129,7 @@ var publicContent = z.strictObject({
129
129
  /** The agent that judges such a change; a global install names it with its prefix. */
130
130
  reviewer: nonEmpty.default("public-content")
131
131
  });
132
+ var skillBudgets = z.record(glob, z.number().int().positive());
132
133
  var checkConfigSchema = z.strictObject({
133
134
  /**
134
135
  * Run once over the changed files, which are appended as arguments. Separate from `commands.lint`,
@@ -141,6 +142,11 @@ var checkConfigSchema = z.strictObject({
141
142
  buildArtifacts: z.array(glob).default([...DEFAULT_BUILD_ARTIFACT_GLOBS]),
142
143
  reviewerRoutes: z.array(reviewerRoute).default([]),
143
144
  publicContent: publicContent.optional(),
145
+ /**
146
+ * Word budgets for the files an agent loads again and again. An entry replaces the kit's budget for the same
147
+ * path and sets one for the project's own skills; a file with none is measured against the kit's default.
148
+ */
149
+ skillBudgets: skillBudgets.optional(),
144
150
  /** Also run tests in directories that hold test files but belong to no package. */
145
151
  looseTests: z.boolean().default(false)
146
152
  });
@@ -182,11 +188,23 @@ var isSourcePath = (path) => pathSteps(path) !== null;
182
188
  var CLEAN_REVIEW_TEXT = "## Code review\n\nNo issues found. Checked for bugs and CLAUDE.md compliance.";
183
189
  var BUN_FAILED_TEST_PATTERN = "^\\(fail\\) (.+?)(?: \\[[\\d.]+m?s\\])?$";
184
190
 
191
+ // src/plan-format/clock.ts
192
+ var DAY_MS = 864e5;
193
+
194
+ // src/config/auto-window.ts
195
+ var MAX_AUTO_WINDOW_DAYS = 7;
196
+ var windowDays = (since, until) => Math.round((Date.parse(`${until}T00:00:00Z`) - Date.parse(`${since}T00:00:00Z`)) / DAY_MS);
197
+
185
198
  // src/config/link-kinds.ts
199
+ var ARTIFACT_LINK_KIND = {
200
+ pattern: "^https://claude\\.ai/(code/)?artifact/",
201
+ label: "Artifact",
202
+ icon: "\u2726"
203
+ };
186
204
  var DEFAULT_LINK_KINDS = [
187
205
  { pattern: "^https://github\\.com/[^/]+/[^/]+/(pull|issues)/\\d+", label: "GitHub", icon: "\u2442" },
188
206
  { pattern: "^https://(www\\.)?figma\\.com/", label: "Figma", icon: "\u25C8" },
189
- { pattern: "^https://claude\\.ai/(code/)?artifact/", label: "Artifact", icon: "\u2726" }
207
+ ARTIFACT_LINK_KIND
190
208
  ];
191
209
  var isRegularExpression = (source) => {
192
210
  try {
@@ -320,7 +338,17 @@ var parallelTasksSchema = z2.strictObject({
320
338
  * agent times the agents to add. Above it the watcher does not propose parallel tasks. The baseline is the context
321
339
  * the first answer of a lane's session carried.
322
340
  */
323
- maxTokens: z2.number().int().positive().default(DEFAULT_PARALLEL_TASKS_MAX_TOKENS)
341
+ maxTokens: z2.number().int().positive().default(DEFAULT_PARALLEL_TASKS_MAX_TOKENS),
342
+ /**
343
+ * The days, both included, on which a proposal that keeps every limit is marked `auto` and the coordinator starts it
344
+ * without asking: subagents beside running work in a lane, at most two extra agents, no path that always asks.
345
+ * `since` is the day it was switched on, `until` at most seven days later; outside the window it is off. Unset by
346
+ * default, so every proposal asks.
347
+ */
348
+ auto: z2.strictObject({ since: z2.iso.date(), until: z2.iso.date() }).refine(({ since, until }) => until >= since, "until must not be before since").refine(
349
+ ({ since, until }) => windowDays(since, until) <= MAX_AUTO_WINDOW_DAYS,
350
+ `until must be at most ${MAX_AUTO_WINDOW_DAYS} days after since`
351
+ ).optional()
324
352
  });
325
353
  var checksSchema = z2.strictObject({
326
354
  /**
@@ -434,13 +462,13 @@ var mergeSchema = z2.strictObject({
434
462
  */
435
463
  withoutChecks: z2.array(checkFreePattern).default([])
436
464
  });
437
- var guardSchema = z2.strictObject({
465
+ var guardFields = z2.strictObject({
438
466
  /**
439
- * The commands the merge guard hook asks the user about before they run, also behind options before the verb, a chain,
440
- * a wrapper or `sh -c`. `gh api` asks only for a write to a merge, a file, a dispatch, a rerun or a ref. An empty list switches the
441
- * guard off.
467
+ * The commands the merge guard hook asks the user about before they run, where they stand at a program position, also
468
+ * behind options before the verb, a chain, a wrapper or `sh -c`. `gh api`, when listed, asks only for a write to a
469
+ * merge, a file, a dispatch, a rerun or a ref. An empty list switches the guard off.
442
470
  */
443
- commands: z2.array(guardedCommand).default(["gh pr merge", "gh workflow run", "gh run rerun", "gh api"]),
471
+ commands: z2.array(guardedCommand).default(["gh pr merge", "gh workflow run"]),
444
472
  selfMerge: z2.strictObject({
445
473
  /** Whether `/orchestrate` offers "merge it yourself after green checks" in the pull request question; off also stops the hook honouring such an answer. */
446
474
  offer: z2.boolean().default(true),
@@ -451,28 +479,39 @@ var guardSchema = z2.strictObject({
451
479
  /** How long the approval gate hook holds a lane's call for the coordinator's dialog before it denies it; below the hook's 10 minutes. */
452
480
  waitMinutes: z2.number().int().min(1).max(9).default(8)
453
481
  }).prefault({}),
454
- planApproval: z2.strictObject({
455
- /**
456
- * Paths that always ask, on top of the ones jackherd brings (`.github/**`, `**\/migrations/**`, `**\/.env*`,
457
- * `.claude/**`, `.jackherd/**`, `bunfig.toml`, `**\/CLAUDE.md`, `**\/AGENTS.md`, `.agents/**`, `.mcp.json`,
458
- * `.husky/**`, `lefthook.yml`, the lockfiles of bun, npm, pnpm and yarn, `**\/.npmrc`, `**\/CODEOWNERS`, every
459
- * package.json, and the CHANGELOG.md of every package without `"private": true`):
460
- * a pull request that changes one is never merged on a plan approval alone. They add to that list, never replace
461
- * it; an invalid entry turns plan approval off, so every merge asks.
462
- */
463
- exceptPaths: z2.array(alwaysAskPattern).default([]),
464
- /**
465
- * Checks that must be among the head commit's and green before a plan approval lets a merge pass, by their names
466
- * on GitHub. Unset, the merge needs at least one check and every one green; set, also each of these. Either way
467
- * GitHub must be able to merge it and the head must contain the base branch.
468
- */
469
- requiredChecks: z2.array(checkName).optional()
470
- }).prefault({})
482
+ /**
483
+ * Paths that always ask, on top of the ones jackherd brings (`.github/**`, `**\/migrations/**`, `**\/.env*`,
484
+ * `.claude/**`, `.jackherd/**`, `bunfig.toml`, `**\/CLAUDE.md`, `**\/AGENTS.md`, `.agents/**`, `.mcp.json`,
485
+ * `.husky/**`, `lefthook.yml`, the lockfiles of bun, npm, pnpm and yarn, `**\/.npmrc`, `**\/CODEOWNERS`): a
486
+ * `merge.withoutChecks` pattern never opens them, and the watcher never starts parallel work on them by itself. They
487
+ * add to that list, never replace it. A standing approval covers them; only workflows, migrations and env files ask.
488
+ */
489
+ alwaysAsk: z2.array(alwaysAskPattern).default([]),
490
+ /**
491
+ * Checks that must be among the head commit's and green before a standing approval lets a merge pass, by their names
492
+ * on GitHub. Unset, the merge needs at least one check and every one green; set, also each of these. Either way
493
+ * GitHub must be able to merge it.
494
+ */
495
+ requiredChecks: z2.array(checkName).optional(),
496
+ /** Read until 2.0.0 as `alwaysAsk` (`exceptPaths`) and `requiredChecks`; the plan approval it was for is gone. */
497
+ planApproval: z2.strictObject({ exceptPaths: z2.array(alwaysAskPattern).optional(), requiredChecks: z2.array(checkName).optional() }).optional().meta({ deprecated: true })
471
498
  });
499
+ var guardWithoutPlanApproval = ({ planApproval, ...guard }) => {
500
+ const requiredChecks = guard.requiredChecks ?? planApproval?.requiredChecks;
501
+ return {
502
+ ...guard,
503
+ alwaysAsk: [...guard.alwaysAsk, ...planApproval?.exceptPaths ?? []],
504
+ ...requiredChecks === void 0 ? {} : { requiredChecks }
505
+ };
506
+ };
507
+ var guardSchema = guardFields.transform(guardWithoutPlanApproval);
472
508
  var hintsSchema = z2.strictObject({
473
509
  /** Whether the `ask-hint` hook points out a last message that ends in a question while the turn used no `AskUserQuestion`; a lane session is never hinted. */
474
- askUserQuestion: z2.boolean().default(true)
510
+ askUserQuestion: z2.boolean().default(true),
511
+ /** Whether the `task-hint` hook points a lane that edits a file without an open task signal to `jh task start`, once per task. */
512
+ taskSignal: z2.boolean().default(true)
475
513
  });
514
+ var DEFAULT_TASK_SIGNAL_MINUTES = 10;
476
515
  var contextSchema = z2.strictObject({
477
516
  /** Tokens in a lane's context from which its fill shows yellow and `/implement` offers a new chat. */
478
517
  warnTokens: tokenCount.default(25e4),
@@ -548,6 +587,7 @@ var jackherdConfigSchema = z2.strictObject({
548
587
  * Read by `jh watch`; without `deploy` it reports no deploy runs. `feedback` is on unless set to `false`:
549
588
  * it reports new feedback on the lanes' pull requests except from the account the watcher runs as and `ignore`.
550
589
  * `parallelTasks.maxTokens` caps the cost of the extra agents in a proposal of parallel tasks at the heartbeat.
590
+ * `taskSignalMinutes` is how long a lane works without a task signal before it reports `no task signal`, once per phase.
551
591
  * `checks` reads the failed tests of a red lane pull request (`failedTestPattern`), also with `feedback` off.
552
592
  * Without `previews` it reads no deploy preview; with it, one `preview` per new head commit. Each of `sources` is a
553
593
  * command whose JSON output reports `source` for a new or changed entry.
@@ -557,6 +597,7 @@ var jackherdConfigSchema = z2.strictObject({
557
597
  feedback: z2.union([z2.literal(false), feedbackSchema]).prefault({}),
558
598
  checks: checksSchema.prefault({}),
559
599
  parallelTasks: parallelTasksSchema.prefault({}),
600
+ taskSignalMinutes: z2.number().int().positive().default(DEFAULT_TASK_SIGNAL_MINUTES),
560
601
  previews: previewsSchema.optional(),
561
602
  sources: z2.array(sourceSchema).refine(hasUniqueNames, "source names must be unique").default([])
562
603
  }).prefault({}),