jackherd 1.6.0 → 1.6.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -2,6 +2,11 @@
2
2
 
3
3
  What changed for people who use jackherd, newest first.
4
4
 
5
+ ## 1.6.1
6
+
7
+ - **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.
8
+ - **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.
9
+
5
10
  ## 1.6.0
6
11
 
7
12
  - **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/README.md CHANGED
@@ -15,7 +15,7 @@ you lose track of who waits on you. Jack:
15
15
 
16
16
  - 📄 **Keeps the plan in your repository.** Your agent writes it as Markdown, phases with a model each, and ticks it off as it builds.
17
17
  - ⚡ **Herds the lanes.** Each lane works in its own git worktree, so sessions never share a checkout.
18
- - ❓ **Brings you only the decisions.** A coordinating session answers what the plan settles, vets review feedback on its pull requests, asks you the rest as a choice, and asks you before every commit and push.
18
+ - ❓ **Brings you only the decisions.** A coordinating session answers what the plan settles, vets review feedback on its pull requests, asks you the rest as a choice, and asks you before every commit, push and merge.
19
19
  - 💬 **Barks when something moves.** One live view of every lane: state, progress, pull request, checks, with the preview link and new feedback as badges; `/follow` brings in tickets, issues and status pages outside GitHub.
20
20
  - 🧠 **Watches the context.** It measures each agent session's fill and, before it is full, offers a fresh chat that takes over the state.
21
21
 
@@ -497,10 +497,17 @@ Lanes ask Jack, not you: the coordinator checks their questions against the plan
497
497
  1. **Reads the board.** The `## Lanes` table names each lane, its port, the plan and phases it works on, what it starts after, and the model. It is the only place that says which lane runs which phase. The coordinator adds a row itself when a lane starts without one.
498
498
  2. **Starts ready lanes.** A phase is ready when its starts-after entry and the plan's dependencies are met and its `Writes` set and serial markers collide with no running lane; shared paths run one after the other, migrations strictly in order. For each lane it creates a worktree from the base branch, copies the untracked files every worktree needs, installs dependencies, starts the agent on the board's model, checks the banner shows that model, opens a plan pane beside it, and sends the lane prompt: plan, phases, the paths it may write, its port.
499
499
  3. **Runs dev servers.** A lane whose phase changes something a user sees gets its dev server in a pane of its own when the lane starts, never in the lane's shell.
500
- 4. **Vets questions.** A lane never opens a dialog. It ends its turn with a text block: the question, the options, its recommendation, the evidence at `file:line`. The coordinator checks the premises against the code and the plan, answers what the plan or a rule already settles, and brings only the open decisions to you, each as a clickable choice. A question in a lane's report is closed in the same turn, in the lane or with you. A draft you review (a README, a page) goes in rounds: you pick the remarks, the lane changes it, you see it again, and the push waits for your approval. Your decision goes into the plan or the board in the same turn, with the date, so it is visible in the plan view and survives the chat.
500
+ 4. **Vets questions.** A lane never opens a dialog; it ends its turn with a text block. The coordinator checks the premises against the code and the plan and brings the open decisions to you, each as a clickable choice.
501
+ - The block holds the question, the options, the lane's recommendation and the evidence at `file:line`.
502
+ - A question in a lane's report is closed in the same turn, in the lane or with you.
503
+ - A draft you review (a README, a page) goes in rounds: you pick the remarks, the lane changes it, you see it again, and the push waits for your approval.
504
+ - Your decision goes into the plan or the board in the same turn, with the date, so it is visible in the plan view and survives the chat. The date and time come from `date`, never from the chat or a guess.
501
505
  5. **Reviews results.** When a lane is `done` or `idle`, it checks the diff against the lane's `Writes` set and that checks are quoted with numbers, not claimed. Then it starts the lane's next ready phase or names the reason to wait.
502
506
  6. **Asks once per PR.** A lane works uncommitted until its PR is ready. The coordinator then asks you once, with the diff summary, the checks with numbers, open points and the commit message; for a visible change it first walks the changed path in the browser. That approval covers the commits, the push and the PR. The question has three options: you merge, you approve and let the coordinator merge once the checks are green ([self-merge](#user-content-guarded-commands-and-self-merge)), or you change something first.
503
- 7. **Keeps the merge order.** Merge order is the board's, and the documentation PR of a fresh plan merges first. Afterwards the lanes rebase as after any merge, and the coordinator removes the copies of the plan files in the main checkout that equal the merged ones, so the next update of the checkout does not stop on them. After each merge the remaining lanes fetch and rebase on the base branch and re-run their checks; a lock-file conflict is never resolved by hand. Each lane gets its next phase or a named reason to wait.
507
+ 7. **Keeps the merge order.** Merges follow the board's order, the documentation PR of a fresh plan first. After each merge:
508
+ - the remaining lanes fetch and rebase on the base branch and re-run their checks; a lock-file conflict is never resolved by hand.
509
+ - the coordinator removes the copies of the plan files in the main checkout that equal the merged ones, so the next update of the checkout does not stop on them.
510
+ - each lane gets its next phase or a named reason to wait.
504
511
  8. **Closes lanes.** After a lane's last merge and your agreement, it ends the agent, proves the worktree holds nothing unmerged, removes the worktree and the branch, and notes the end in the board.
505
512
 
506
513
  #### What happens on its own, and what waits for you
@@ -516,7 +523,11 @@ Who merges follows the project's own rules: by default you, unless the project s
516
523
 
517
524
  #### Guarded commands and self-merge
518
525
 
519
- A merge or a release is the step nobody takes back. In a project install the `merge-guard` hook asks you before `gh pr merge` and `gh workflow run` (the usual production release) run: it recognises them when the command spells them out: behind options before the verb (`gh -R x pr merge`), a chain (`a && gh pr merge 12`), a wrapper (also one it does not know, such as `mise exec -- gh pr merge 12`, as any command whose words contain the guarded words in this order), `sh -c` and `eval`, and shows the dialog even in auto mode. A quoted text that names the words, such as a commit message, passes, and so does everything else; the hook prints nothing unless it asks.
526
+ A merge or a release is the step nobody takes back.
527
+
528
+ ##### What is guarded
529
+
530
+ In a project install the `merge-guard` hook asks you before `gh pr merge` and `gh workflow run` (the usual production release) run. It recognises them when the command spells them out: behind options before the verb (`gh -R x pr merge`), a chain (`a && gh pr merge 12`), a wrapper, known or not (any command whose words contain the guarded words in this order, such as `mise exec -- gh pr merge 12`), `sh -c` and `eval`. It shows the dialog even in auto mode. A quoted text that names the words, such as a commit message, passes, and so does everything else; the hook prints nothing unless it asks.
520
531
 
521
532
  ```json
522
533
  {
@@ -531,7 +542,15 @@ A merge or a release is the step nobody takes back. In a project install the `me
531
542
  - `guard.selfMerge.offer` (default `true`) lets `/orchestrate` offer "approve, and merge it yourself after green checks" in its PR question. With `false` the question has two options and the hook honours no such answer.
532
543
  - `guard.selfMerge.approvalMinutes` (default 180) is how long that answer lets `gh pr merge <number>` pass without a second dialog.
533
544
 
534
- **Merging without checks.** A pull request that only changes documents rarely needs its checks. List the paths in `merge.withoutChecks` (default empty, so every merge waits for the checks as before):
545
+ ##### Self-merge
546
+
547
+ You pick the second option of the PR question. The coordinator commits, pushes and opens the PR, waits for the checks in the background, merges with `gh pr merge <number> --squash --delete-branch` when they are green and tells you in one line; on a red check it does not merge and reports. The project supplement can block the option per approval tier.
548
+
549
+ The hook does not take the agent's word for your answer. It reads the session's transcript, which Claude Code writes, and looks for a dialog result where you picked that exact option for a question naming one branch. Then it asks GitHub which branch pull request `<number>` carries and lets the merge pass if the two match, the PR is open and not from a fork, and the command is exactly the plain form: `gh pr merge <number>` with `--squash`, `--merge`, `--rebase` and `--delete-branch` as the only flags, run on its own in the directory you work in (no `-R`, no `--admin`, no chain, no wrapper). `gh workflow run` and every other guarded command ask every time. The approval covers the branch, not the commits you saw, for the configured minutes.
550
+
551
+ ##### Merging without checks
552
+
553
+ A pull request that only changes documents rarely needs its checks. List the paths in `merge.withoutChecks` (default empty, so every merge waits for the checks as before):
535
554
 
536
555
  ```json
537
556
  {
@@ -539,13 +558,23 @@ A merge or a release is the step nobody takes back. In a project install the `me
539
558
  }
540
559
  ```
541
560
 
542
- A pattern is made of path words with `*` and `**`: no `..`, not absolute, no braces or negation, and at least one whole path segment without a wildcard, so `docs/**/*.md` is fine and `**`, `**/*.md` and `**/*.*` are refused. Keep the folder in the pattern: a `.md` file elsewhere can be an agent instruction (`CLAUDE.md`, a skill) that ships with a package and deserves its checks. With the list set, `/orchestrate` lists a pull request's files with `gh pr diff <number> --name-only` before it waits for checks and merges at once when every file matches. The merge guard checks the same itself: for a plain `gh pr merge <number>` it asks GitHub for the pull request and then for its files in that repository (`gh api …/pulls/<number>/files`, which also names the old path of a renamed file) and does not ask you when the pull request is open, from this repository, GitHub lists as many files as the pull request changes (fewer than 3000) and every one matches a pattern. This works without a self-merge answer, also when `guard.selfMerge.offer` is `false`. No file, one file outside, a count that differs, a failed `gh` call or a pattern list it cannot read means it asks as before. Limit: a push between its look and the merge is not seen.
561
+ A pattern is made of path words with `*` and `**`: no `..`, not absolute, no braces or negation, and at least one whole path segment without a wildcard, so `docs/**/*.md` is fine and `**`, `**/*.md` and `**/*.*` are refused. Keep the folder in the pattern: a `.md` file elsewhere can be an agent instruction (`CLAUDE.md`, a skill) that ships with a package and deserves its checks.
562
+
563
+ With the list set, `/orchestrate` lists a pull request's files with `gh pr diff <number> --name-only` before it waits for checks and merges at once when every file matches.
564
+
565
+ The merge guard checks the same itself: for a plain `gh pr merge <number>` it asks GitHub for the pull request and then for its files in that repository (`gh api …/pulls/<number>/files`, which also names the old path of a renamed file). It does not ask you when the pull request is open, from this repository, GitHub lists as many files as the pull request changes (fewer than 3000) and every one matches a pattern. This works without a self-merge answer, also when `guard.selfMerge.offer` is `false`. No file, one file outside, a count that differs, a failed `gh` call or a pattern list it cannot read means it asks as before.
566
+
567
+ ##### Limits
568
+
569
+ The hook reads the command as written, so it does not see what a command runs behind its back: a script file or a project script (`bash ./release.sh`, `./release.sh`, `bun run release`, `make release`) that calls `gh workflow run`, a shell alias or function, `gh alias` and git aliases, text that is encoded or assembled at run time, and a wrapper that spells the words differently. Add the script runners you use to `guard.commands`, or protect the repository itself.
543
570
 
544
- **What self-merge does.** You pick the second option of the PR question. The coordinator commits, pushes and opens the PR, waits for the checks in the background, merges with `gh pr merge <number> --squash --delete-branch` when they are green and tells you in one line; on a red check it does not merge and reports. The project supplement can block the option per approval tier.
571
+ The agent runs with your account, so it can also edit a transcript, drop the hook from `.claude/settings.json` or merge through `gh api`. The guard stops a merge or a release by mistake, by a misread instruction or a mistyped command; it does not stop an agent that sets out to get around it. For that, use branch protection and required reviews on the repository.
545
572
 
546
- **What the hook checks.** It does not take the agent's word for your answer. It reads the session's transcript, which Claude Code writes, and looks for a dialog result where you picked that exact option for a question naming one branch. Then it asks GitHub which branch pull request `<number>` carries and lets the merge pass if the two match, the PR is open and not from a fork, and the command is exactly the plain form: `gh pr merge <number>` with `--squash`, `--merge`, `--rebase` and `--delete-branch` as the only flags, run on its own in the directory you work in (no `-R`, no `--admin`, no chain, no wrapper). `gh workflow run` and every other guarded command ask every time.
573
+ - The merge-without-checks look is a snapshot: a push between its look and the merge is not seen.
574
+ - A global install ships no hook, so there the skill text alone asks you.
575
+ - If Bun cannot start (a process out of file descriptors, say), the hook cannot run and every Bash call is blocked, on purpose: a guard that cannot run does not wave commands through.
547
576
 
548
- **Its limit.** The hook reads the command as written, so it does not see what a command runs behind its back: a script file or a project script (`bash ./release.sh`, `./release.sh`, `bun run release`, `make release`) that calls `gh workflow run`, a shell alias or function, `gh alias` and git aliases, text that is encoded or assembled at run time, and a wrapper that spells the words differently. Add the script runners you use to `guard.commands`, or protect the repository itself. The agent runs with your account, so it can also edit a transcript, drop the hook from `.claude/settings.json` or merge through `gh api`. The guard stops a merge or a release by mistake, by a misread instruction or a mistyped command; it does not stop an agent that sets out to get around it. For that, use branch protection and required reviews on the repository. The approval covers the branch, not the commits you saw, for the configured minutes. A global install ships no hook, so there the skill text alone asks you. If Bun cannot start (a process out of file descriptors, say), the hook cannot run and every Bash call is blocked, on purpose: a guard that cannot run does not wave commands through.
577
+ ##### Dialogs inside a lane
549
578
 
550
579
  A permission dialog inside a lane the coordinator confirms itself only for the commit and push of the lane's own branch under its PR approval, and for the fetch, rebase and lock-file steps after a merge, always with the one-time option. Every other dialog goes to you.
551
580
 
@@ -1,6 +1,6 @@
1
1
  id = "crossdelta.jackherd"
2
2
  name = "jackherd"
3
- version = "1.6.0"
3
+ version = "1.6.1"
4
4
  min_herdr_version = "0.8.2"
5
5
  description = "Show the plans of a board's lanes in a herdr pane"
6
6
  platforms = ["linux", "macos"]
package/install.sh CHANGED
@@ -9,7 +9,7 @@ set -eu
9
9
 
10
10
  # The default is the version this file ships with. The release bump changes it together with
11
11
  # package.json; install.test.ts fails while the two differ. Overridable, but only by a plain release.
12
- JACKHERD_VERSION="${JACKHERD_VERSION:-1.6.0}"
12
+ JACKHERD_VERSION="${JACKHERD_VERSION:-1.6.1}"
13
13
 
14
14
  JACKHERD_PACKAGE='jackherd'
15
15
  # Test seam: the terminal the wizard asks its questions on.
@@ -388,7 +388,8 @@ Dim text in the input line is Claude Code's suggestion, not typed input; check w
388
388
  **A decision of the user goes into the file at once.** Whatever the user decides about a plan or the
389
389
  board (a phase dropped or postponed, the order changed, an approval with its scope, the answer to an
390
390
  open question) you write into the plan or the board in the same turn, with the date and
391
- "User". The plans view reads only these files, so a decision that lives in your memory or hand-over
391
+ "User". **A date or time in a record is read, never estimated:** run `date` in the same turn and write
392
+ what it prints, never a time taken from the conversation or guessed; an agent has no clock. The plans view reads only these files, so a decision that lives in your memory or hand-over
392
393
  note is invisible to the user. Where it goes:
393
394
 
394
395
  - **A lane runs for the plan:** the plan file in that lane's worktree. The view reads it there live;
@@ -523,8 +524,8 @@ included, goes to the user.
523
524
  When the user picked `"{{kit.guard.selfMergeLabel}}"` for a PR:
524
525
 
525
526
  1. Commit, push and open the PR as in step 4. Enter in the board's `{{kit.heading.history}}` that the
526
- user approved this PR with self-merge (time, lane, PR number), so the approval has a record outside
527
- the chat.
527
+ user approved this PR with self-merge (time, lane, PR number; the time as § 4 reads it), so the
528
+ approval has a record outside the chat.
528
529
  2. **Merging without checks.** The paths a PR may carry to be merged without waiting for checks are
529
530
  `{{kit.merge.withoutChecks}}` (`merge.withoutChecks` in the config; `none` means every merge waits).
530
531
  Unless `none`, list the PR's files with `gh pr diff <n> --name-only` and compare each one with the
@@ -589,7 +590,7 @@ After its last PR is merged and the user agreed:
589
590
  first; check the session has ended.
590
591
  4. Delete the local branch; the remote one is usually gone with the merge (`git ls-remote --heads`).
591
592
  5. Record the lane's end in the board's `{{kit.heading.history}}`, and mark every row of the lane by appending
592
- `, {{kit.word.laneClosed}} <date>` to its name cell, in the same documentation PR as the history entry. The plans view lists a lane without a worktree as waiting until its
593
+ `, {{kit.word.laneClosed}} <date>` to its name cell (the date as § 4 reads it), in the same documentation PR as the history entry. The plans view lists a lane without a worktree as waiting until its
593
594
  name cell carries that word and its plan has open phases.
594
595
 
595
596
  Close only lanes you started or the user named.
@@ -621,7 +622,7 @@ fill of each lane shows in the plans view and comes from the session log
621
622
  no value: tell the user in one line and never read it as `ok`. On `coordinator warn` or above, hand yourself over to a fresh chat, at a
622
623
  quiet moment (no merge or rebase half done):
623
624
  1. **Prepare.** Record your hand-over where the project supplement says, by default in the
624
- board's `{{kit.heading.history}}`: the state of every lane, what waits on the user, the next
625
+ board's `{{kit.heading.history}}` (dated as § 4 reads it): the state of every lane, what waits on the user, the next
625
626
  step, the traps. It holds the hand-over only: a decision of the user that is not yet in a plan or
626
627
  the board belongs there first (§ 4), not into the note. Write the prompt for the new chat to `.claude/handover/coordinator.md`: what to
627
628
  read first, then the steps in order. It is read, not typed, so it may be long.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "jackherd",
3
- "version": "1.6.0",
3
+ "version": "1.6.1",
4
4
  "keywords": [
5
5
  "claude-code",
6
6
  "coding-agents",