jackherd 1.15.2 → 1.17.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 +34 -0
- package/INSTALL.md +2 -1
- package/README.md +26 -22
- package/dist/{chunk-4LUFR7RC.js → chunk-N5FAOQZL.js} +7 -0
- package/dist/config.d.ts +33 -1
- package/dist/config.js +57 -5
- package/dist/plan-format.d.ts +18 -6
- package/dist/plan-format.js +8 -1
- package/docs/plan-contract.md +1 -0
- package/herdr-plugin/herdr-plugin.toml +4 -1
- package/herdr-plugin/restore-plans.ts +60 -26
- package/herdr-plugin/resume.ts +18 -0
- package/install.sh +1 -1
- package/kit/hooks/ask-rules.json +11 -0
- package/kit/hooks/hooks/approval-gate/checked-marks.ts +59 -0
- package/kit/hooks/hooks/approval-gate/index.ts +36 -7
- package/kit/hooks/hooks/approval-gate/run.ts +72 -16
- package/kit/hooks/hooks/approval-gate/settings.ts +27 -1
- package/kit/hooks/hooks/approval-gate/standing-history.ts +90 -0
- package/kit/hooks/hooks/approval-gate/standing-sql.ts +58 -0
- package/kit/hooks/hooks/approval-gate/transcripts.ts +11 -5
- package/kit/hooks/hooks/lane-start-guard/decision.ts +74 -0
- package/kit/hooks/hooks/lane-start-guard/index.ts +36 -0
- package/kit/hooks/hooks/merge-guard/always-ask.ts +2 -2
- package/kit/hooks/hooks/merge-guard/approval.ts +25 -13
- package/kit/hooks/hooks/merge-guard/change-id.ts +34 -0
- package/kit/hooks/hooks/merge-guard/decision.ts +438 -35
- package/kit/hooks/hooks/merge-guard/pane-input.ts +61 -0
- package/kit/hooks/hooks/merge-guard/pane-process-info.ts +38 -0
- package/kit/hooks/hooks/merge-guard/run.ts +110 -28
- package/kit/hooks/hooks/merge-guard/standing-approval.ts +15 -78
- package/kit/hooks/hooks/plain-question/form.ts +89 -0
- package/kit/hooks/hooks/plain-question/index.ts +44 -4
- package/kit/hooks/hooks/plain-question/recommendation.ts +73 -0
- package/kit/hooks/hooks/plain-question/run.ts +14 -7
- package/kit/hooks/hooks/shared/standing-mark.ts +104 -0
- package/kit/hooks/hooks.json +11 -2
- package/kit/review/agents/skill-review.md +3 -2
- package/kit/review/skills/pre-commit/SKILL.md +1 -1
- package/kit/rules/rules/kit/code-style.md +6 -6
- package/kit/rules/rules/kit/writing-for-agents.md +3 -0
- package/kit/workflow/skills/follow/SKILL.md +18 -11
- package/kit/workflow/skills/implement/SKILL.md +1 -1
- package/kit/workflow/skills/orchestrate/SKILL.md +124 -300
- package/kit/workflow/skills/orchestrate/references/appointments.md +21 -0
- package/kit/workflow/skills/orchestrate/references/close-lane.md +8 -7
- package/kit/workflow/skills/orchestrate/references/coordinator-handover.md +8 -8
- package/kit/workflow/skills/orchestrate/references/events.md +46 -1
- package/kit/workflow/skills/orchestrate/references/herd-ask.md +10 -0
- package/kit/workflow/skills/orchestrate/references/lane-handover.md +9 -4
- package/kit/workflow/skills/orchestrate/references/lane-session.md +5 -4
- package/kit/workflow/skills/orchestrate/references/lane-setup.md +2 -1
- package/kit/workflow/skills/orchestrate/references/plan-commit.md +9 -2
- package/kit/workflow/skills/orchestrate/references/self-merge.md +26 -0
- package/kit/workflow/skills/orchestrate/references/stage-approval.md +50 -0
- package/kit/workflow/skills/orchestrate/references/standing-approval.md +38 -0
- package/kit/workflow/skills/orchestrate/references/start-lane.md +67 -0
- package/kit/workflow/skills/orchestrate/references/waiting.md +16 -0
- package/kit/workflow/skills/orchestrate/references/watch-without-monitor.md +13 -0
- package/kit/workflow/skills/plan/SKILL.md +3 -2
- package/package.json +3 -1
- package/recipes/README.md +4 -2
- package/recipes/google-calendar.md +30 -0
- package/recipes/jira-cli.md +1 -1
- package/recipes/sources/google-calendar-today.ts +38 -0
- package/recipes/sources/google-calendar.ts +368 -0
- package/recipes/sources/jira.ts +10 -2
- package/recipes/sources/linear.ts +9 -3
- package/recipes/sources/sentry.ts +8 -2
- package/recipes/sources.md +17 -3
- package/schemas/jackherd.schema.json +67 -0
- package/schemas/sources.schema.json +9 -0
- package/src/approval/checked.ts +60 -0
- package/src/approval/command.ts +16 -0
- package/src/check/command.ts +51 -6
- package/src/check/lint-pointers.ts +79 -0
- package/src/check/lint-shaped.ts +196 -0
- package/src/check/plan-structure-check.ts +116 -4
- package/src/check/report.ts +9 -3
- package/src/check/schema.ts +37 -0
- package/src/check/settings.ts +4 -0
- package/src/check/skill-budget-defaults.ts +81 -23
- package/src/check/skill-budgets.ts +58 -10
- package/src/cli.ts +2 -0
- package/src/config/personal-sources.ts +46 -10
- package/src/config/repository-identity.ts +40 -0
- package/src/config/schema.ts +53 -1
- package/src/context/command.ts +33 -2
- package/src/context/options.ts +33 -8
- package/src/context/usage-logs.ts +53 -0
- package/src/context/usage.ts +110 -0
- package/src/lane/base-command.ts +35 -0
- package/src/lane/base.ts +43 -0
- package/src/lane/close-check.ts +49 -15
- package/src/lane/close-command.ts +71 -11
- package/src/lane/close-options.ts +29 -8
- package/src/lane/close-survey.ts +13 -1
- package/src/lane/close.ts +51 -12
- package/src/lane/command.ts +21 -3
- package/src/lane/send-command.ts +32 -0
- package/src/lane/send-herdr.ts +77 -0
- package/src/lane/send-options.ts +23 -0
- package/src/lane/send.ts +90 -0
- package/src/lane/skill-hint.ts +27 -0
- package/src/lane/space-command.ts +30 -0
- package/src/lane/space.ts +49 -0
- package/src/lane/start-lane-hint.ts +18 -0
- package/src/plan-contract/base-check-git.ts +33 -0
- package/src/plan-contract/base-check.ts +17 -0
- package/src/plan-contract/command.ts +25 -4
- package/src/plan-format/plan-structure.ts +17 -5
- package/src/plans/ask-coordinator.ts +23 -13
- package/src/plans/board-rows.ts +9 -4
- package/src/plans/click-hint.ts +4 -0
- package/src/plans/command.ts +6 -5
- package/src/plans/ensure-panes.ts +14 -3
- package/src/plans/git.ts +14 -0
- package/src/plans/github.ts +19 -10
- package/src/plans/layout.ts +191 -25
- package/src/plans/live.ts +57 -14
- package/src/plans/plan-panes.ts +21 -4
- package/src/plans/row-actions.ts +17 -10
- package/src/plans/selection.ts +29 -0
- package/src/plans/settings.ts +4 -0
- package/src/plans/snapshot.ts +8 -3
- package/src/plans/source-rows.ts +35 -2
- package/src/resume/command.ts +213 -0
- package/src/resume/decision.ts +102 -0
- package/src/resume/keeper.ts +65 -0
- package/src/resume/location.ts +22 -0
- package/src/resume/log.ts +27 -0
- package/src/resume/ports.ts +102 -0
- package/src/resume/record.ts +116 -0
- package/src/resume/targets.ts +84 -0
- package/src/resume/texts.ts +27 -0
- package/src/secrets/command.ts +15 -5
- package/src/sync/herdr-plugin.ts +2 -11
- package/src/sync/plan-ask-rules.ts +11 -0
- package/src/sync/render.ts +3 -1
- package/src/vocabulary/index.ts +13 -0
- package/src/waiting/command.ts +13 -4
- package/src/watch/command.ts +177 -78
- package/src/watch/config-reload.ts +119 -0
- package/src/watch/lane-model.ts +11 -1
- package/src/watch/lane-progress.ts +70 -8
- package/src/watch/lane-report.ts +68 -12
- package/src/watch/main-checkout.ts +5 -1
- package/src/watch/merge-duties.ts +33 -0
- package/src/watch/merge-overlap-read.ts +69 -0
- package/src/watch/plan-views.ts +16 -4
- package/src/watch/reference-paths.ts +46 -5
- package/src/watch/report-questions.ts +5 -2
- package/src/watch/single-report.ts +3 -2
- package/src/watch/source-dry-run-command.ts +22 -4
- package/src/watch/source-dry-run.ts +1 -1
- package/src/watch/source-reason.ts +58 -0
- package/src/watch/source-reload.ts +21 -17
- package/src/watch/source-run.ts +24 -7
- package/src/watch/sources.ts +9 -2
- package/src/watch/texts.ts +15 -4
- package/src/watch/waiting-line.ts +4 -3
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,40 @@
|
|
|
2
2
|
|
|
3
3
|
What changed for people who use jackherd, newest first.
|
|
4
4
|
|
|
5
|
+
## 1.17.0
|
|
6
|
+
|
|
7
|
+
- **Jack resumes after a crash.** After a machine or herdr crash, herdr restored the agents but nobody restarted the watcher, and every lane whose command was cut off stood still. The watcher now keeps a record in `~/.jackherd/resume/`, and a second `[[startup]]` entry of the herdr plugin runs `jh resume`: it wakes the coordinator with a fixed `/orchestrate` line and every lane that was working with a fixed resume text, at most once per crash. Nothing resumes after a clean stop, for a record older than 12 hours, while a watcher runs, or when a target is ambiguous; `~/.jackherd/logs/resume.log` says why.
|
|
8
|
+
- **The herd finds its coordinator after a herdr restart.** `herd --ensure` ended with 0 when it found no coordinator, and with several unnamed agents in the main checkout it found none. It now exits with 3 in that case, the startup hook keeps waiting for exactly those repositories, and the coordinator is the one agent whose tab holds a pane labelled `herd`.
|
|
9
|
+
- **A failed source says why.** A source's command may print one stdout line `jackherd-reason: <reason>`; the watcher shows the last such line as `exit 1: <reason>`, with the values of the source's `secrets`, URL credentials and URL query values masked, control characters dropped and the text cut to 160 characters. stderr is still never read. The recipe scripts print such a line for a missing variable, an HTTP status or a failed tool call.
|
|
10
|
+
- **A personal source can name its repositories.** `"repos": ["owner/name"]` (one to twenty) in `~/.jackherd/sources.json` runs the source only where the main checkout's `origin` names one of them; elsewhere the watcher notes `<name>: not for this repo`, or `<name>: no origin`. `jh watch source <name> --dry-run` of a left-out source prints the note and exits 2, and `jh secret list` still names its secrets. The repository's own config refuses `repos`.
|
|
11
|
+
- **`deploy`, `feedback`, `previews` and `checks` change without a restart.** The watcher reads them again with the sources. A change that applies prints `config reloaded: <parts>`; an invalid or deleted config keeps every value and says `config not reloaded: <file>: <reason>` once. A part switched on starts from a baseline, so only what happens after the reload is reported.
|
|
12
|
+
- **Writing SQL against stage under a standing approval.** With `guard.approvals.standing.tools` naming an SQL tool as `stage` (production, any other target and any name saying `prod` are refused), a single insert, update or delete passes without a dialog while you approved the environment (at most 24 hours) and Jack marked that exact call checked with `jh approval checked <id> <hash>`. Each pass leaves a line in the board's history with the lane, the kind of statement and its first table, never a value. A select, a schema change or several statements still ask you. `jh approval show` prints a held call for Jack's reading.
|
|
13
|
+
- **A question without its recommendation is sent back.** The first option of a dialog must end in `(recommended)` (`(Empfohlen)` in German); a single choice marks exactly one option, a multi-select marks its first and may mark more. A question that offers a label a guard reads word for word, such as the self-merge or standing approval, needs no mark and must not carry one.
|
|
14
|
+
- **The coordinator's rules are checked in code.** A dialog needs the paw in its header chip, at most 15 words in the question and 5 in a label; `lane-start-guard` refuses `herdr agent start <lane>` from the main checkout while no board row names the lane; `jh lane send` sends the prompt once and tells a stalled lane from a working one; `jh lane base <plan>` prints the base ref; `jh plan check` prints a `base:` line and exits 3 for a plan missing on the base branch; after `merged #n` the watcher names the checks each rebasing lane runs.
|
|
15
|
+
- **The merge guard closes four more ways around it.** A merge through `gh api graphql` or `curl` against the GitHub API asks; a self-merge approval is bound to the change you saw (the patch id), so a push after the answer asks again, and a pull request that changes the guard itself always asks; `herdr agent send-keys` and `prompt` into the coordinator ask. `herdr pane send-text` and `send-keys` always ask, and `herdr pane run` passes only into a plain pane that is not the herd, with a single-line text. The README says what the guards are: they catch mistakes, not intent.
|
|
16
|
+
- **`jh check` fails on open boxes of the phase a lane builds.** The phase comes from the lane's board row; `check.openBoxes` is `fail` (default), `warn` or `off`. A row marked `(Teil)` is left alone. After `merged #<n>` the watcher says once when a phase merged with open boxes.
|
|
17
|
+
- **`jh context --usage` counts a lane's tokens.** `jh context --usage [--since 24h|7d|<date>] [<name>] [--coordinator]` prints the input, cache and output tokens of a lane's sessions and subagents as one line of JSON.
|
|
18
|
+
- **A half-closed lane closes.** `jh lane close --branch <name>` finishes a lane whose worktree is already gone, and a detached worktree closes when the base branch holds its head. `jh lane space [--min-gb <n>]` refuses a new lane below `lanes.minFreeGb` free disk space (5 GB by default).
|
|
19
|
+
- **Board lines are written at once and reach `main` bundled.** History lines, waiting rows and appointments go into the main checkout's board immediately, where the herd reads them, and travel to the base with the next documentation pull request instead of one of their own. The herd also drops a closed lane without waiting for that merge.
|
|
20
|
+
- **A shorter orchestrate skill.** Rules that only some wake-ups need moved into references, and the watcher appends `· read <skill>/references/<file>` to the event that needs one. Word budgets are targets with ceilings that may only fall, and `jh check` prints `over target` hints. Run `jh sync` to get the new texts.
|
|
21
|
+
- **Fewer false alarms from the watcher.** "Keine Rückfragen." no longer counts as a question, the model alarm measures against the row of the phase the lane works on, and a phase whose box stayed open after its merge is not reported as ready.
|
|
22
|
+
- **The herd draws more calmly.** A source's entries block has a head with a count that folds, a resting lane's badge keeps its padding until the name would be cut, and exactly one line per working lane shimmers: its activity line, else its running task, else its first subagent line.
|
|
23
|
+
- **`jh check` warns on rules that belong in your linter.** A rule block you added or changed in `**/rules/**/*.md`, `CLAUDE.md` or `AGENTS.md` that carries a banned import path, a literal comparison, a comment form or a banned call, and names no lint rule, shows as `Lint pointers`. Name the rule with `` `lint:<rule id>` ``, or write `` `lint:none` `` and a reason where it stays prose. `check.lintPointers.level` is `warn` (default), `fail` or `off`; `check.lintPointers.exclude` lists globs never read. Files `jh sync` installed are not read.
|
|
24
|
+
- **The kit asks for the linter first.** `/jh-plan` asks of every rule it writes whether your linter can check it, `skill-review` has a seventh lens for rules that could be lint rules, and the kit's code style rule shrinks by about eighty words. Run `jh sync` to get the new texts.
|
|
25
|
+
- **macOS and Linux.** The system requirements are written down in INSTALL.md, and the orchestrate reference names the macOS form of the working-directory check.
|
|
26
|
+
|
|
27
|
+
## 1.16.0
|
|
28
|
+
|
|
29
|
+
- **Sources of your own.** A source that belongs to you alone, such as your calendar, goes into `~/.jackherd/sources.json` instead of the repository's config: same shape as `watch.sources`. Its command runs in `~/.jackherd/`, not in the repository, in every repository where you start the watcher, and for nobody else. The watcher, the herd and `jh watch source <name> --dry-run` take it up after the repository's sources. If the repository has a source of the same name, the repository's wins and yours is skipped with a note. A broken personal file never stops the watcher. The file has a JSON schema, `schemas/sources.schema.json`.
|
|
30
|
+
- **Secrets in the keychain: `jh secret`.** A source lists the variables it needs in its `"secrets"` field. `jh secret set <NAME>` asks for the value hidden, from a terminal only, and keeps it in the system keychain; `jh secret list` shows which names your sources need and whether each is set, never a value; `jh secret delete <NAME>` removes one. A repository source, which anyone with a commit can change, gets only the watcher's environment, never a keychain value. A value reaches the environment of its source's command and nowhere else: no argument, output, state or log. A missing one fails the run with `secret missing: <NAME>` before the command starts.
|
|
31
|
+
- **Sources change without a restart.** The watcher reads the repository's config and your personal file again when one of them changed; a new source runs at once, a removed one stops. A changed file that does not read keeps the sources as they were and says so once. The herd takes the sources from the running watcher, so a new one shows there too without restarting either.
|
|
32
|
+
- **Entries in a block of their own.** `"show": "entries"` opens a source's entries in a block under "Your turn", headed by its label, in its colour and linked where an entry has an address. Together such blocks show at most five rows, at least one per block, and `+<n>` opens the rest of a block. The source's status stays in the `Sources` line.
|
|
33
|
+
- **`/jh-follow` asks where a source belongs.** It asks whether the source is yours or the repository's and writes exactly that file. For a personal source with a secret, you set the value with `jh secret set` in a terminal of your own; the skill then checks it with `jh secret list` and dry-runs the source by name.
|
|
34
|
+
- **A recipe for today's appointments.** The Google Calendar recipe shows the day's appointments of one or more calendars from their secret iCal addresses, kept in the keychain, recurring events included, each linked to the calendar's day view. No Google login and no API project. A failed request prints nothing of the address.
|
|
35
|
+
- **`[done]` hides its row at once.** A click on `[done]` in "Your turn" removes the row right away instead of at the next read; if the write fails, the row stays and the footer says why.
|
|
36
|
+
- **A cut activity line opens on a click.** The `↳` line of a working lane or subagent that did not fit unfolds when you click it.
|
|
37
|
+
- **The merge guard says why it asks.** An approved merge passes without a question only as a plain `gh pr merge <n>` standing alone. When it asks anyway, the question now says why: run a chained or piped merge alone, and a merge with other flags is let through only in its plain form.
|
|
38
|
+
|
|
5
39
|
## 1.15.2
|
|
6
40
|
|
|
7
41
|
- **The installer no longer crashes on macOS.** `install.sh` redirected `jh init --global` from `/dev/tty`, which the macOS kqueue that Bun reads input through rejects (`EINVAL: invalid argument, kqueue`) at the first question. It now names the terminal's own device (such as `/dev/ttys003`) first, through `tty` on its input or, since macOS prints `/dev/tty` for `tty </dev/tty`, through `ps`, and reads from that.
|
package/INSTALL.md
CHANGED
|
@@ -19,6 +19,7 @@ If the user wants the kit in the repository, shared with the team, run `bunx jac
|
|
|
19
19
|
|
|
20
20
|
## Requirements
|
|
21
21
|
|
|
22
|
+
- Linux or macOS; on Windows, through WSL. `git`, `sh`, `ps` and `tty` must be there: both systems bring them.
|
|
22
23
|
- [Bun](https://bun.sh) 1.3 or later, and Claude Code.
|
|
23
24
|
- 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
|
|
|
@@ -54,7 +55,7 @@ When `init` creates a new config, it asks:
|
|
|
54
55
|
| Use it when | the team shares the kit through the repository | the repository must stay untouched, not even untracked files |
|
|
55
56
|
| Lives in | the project's `.claude/` | `~/.claude/` |
|
|
56
57
|
| Skill names | `/plan`, `/implement`, `/orchestrate`, … | `/jh-plan`, `/jh-implement`, `/jh-orchestrate`, … |
|
|
57
|
-
| Hooks | `git-guard`, `watch-guard`, `merge-guard`, `approval-gate`, `model-gate`, `model-log`, `ask-hint`, `open-question`, `plain-question`, `sent-file`, `format-on-edit` | none; `init --global` offers only a `SessionStart` update notice |
|
|
58
|
+
| Hooks | `git-guard`, `watch-guard`, `lane-start-guard`, `merge-guard`, `approval-gate`, `model-gate`, `model-log`, `ask-hint`, `open-question`, `plain-question`, `sent-file`, `format-on-edit` | none; `init --global` offers only a `SessionStart` update notice |
|
|
58
59
|
| Config | `.jackherd/config.json` | `~/.jackherd/config.json`, used where a repository has none |
|
|
59
60
|
| Update | `jh sync` | `jh update` |
|
|
60
61
|
|
package/README.md
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
<p>
|
|
2
|
-
<img alt="jackherd: a black and white Shih Tzu in a turquoise bandana, three lanes running from him to three agent nodes" src="https://unpkg.com/jackherd@1.
|
|
2
|
+
<img alt="jackherd: a black and white Shih Tzu in a turquoise bandana, three lanes running from him to three agent nodes" src="https://unpkg.com/jackherd@1.17.0/assets/branding/jackherd-logo-outline.svg" width="340">
|
|
3
3
|
<br>
|
|
4
4
|
</p>
|
|
5
5
|
|
|
6
|
-
[](https://www.npmjs.com/package/jackherd) [](https://www.npmjs.com/package/jackherd) [](https://www.npmjs.com/package/jackherd?activeTab=dependencies)<br><img width="1" height="8" alt="" src="https://unpkg.com/jackherd@1.
|
|
6
|
+
[](https://www.npmjs.com/package/jackherd) [](https://www.npmjs.com/package/jackherd) [](https://www.npmjs.com/package/jackherd?activeTab=dependencies) [](https://unpkg.com/jackherd/INSTALL.md) [](https://unpkg.com/jackherd/INSTALL.md)<br><img width="1" height="8" alt="" src="https://unpkg.com/jackherd@1.17.0/assets/icons/space.svg">
|
|
7
7
|
|
|
8
8
|
# Jack herds your coding agents.
|
|
9
9
|
|
|
@@ -15,31 +15,31 @@ For developers who'd rather decide than supervise.
|
|
|
15
15
|
curl -fsSL https://unpkg.com/jackherd/install.sh | sh
|
|
16
16
|
```
|
|
17
17
|
|
|
18
|
-
Then `/jh-start` in a repository, and Jack offers your first plan. → [Quickstart](#quickstart)<br><img width="1" height="8" alt="" src="https://unpkg.com/jackherd@1.
|
|
18
|
+
Then `/jh-start` in a repository, and Jack offers your first plan. → [Quickstart](#quickstart)<br><img width="1" height="8" alt="" src="https://unpkg.com/jackherd@1.17.0/assets/icons/space.svg">
|
|
19
19
|
|
|
20
20
|
<p>
|
|
21
|
-
<img src="https://unpkg.com/jackherd@1.
|
|
21
|
+
<img src="https://unpkg.com/jackherd@1.17.0/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.">
|
|
22
22
|
<br>
|
|
23
|
-
<sub>Jack and his lanes in <a href="https://herdr.dev/">herdr</a>, a terminal for coding agents (recommended). Without it, one Claude Code session builds with subagents.</sub><br><img width="1" height="8" alt="" src="https://unpkg.com/jackherd@1.
|
|
23
|
+
<sub>Jack and his lanes in <a href="https://herdr.dev/">herdr</a>, a terminal for coding agents (recommended). Without it, one Claude Code session builds with subagents.</sub><br><img width="1" height="8" alt="" src="https://unpkg.com/jackherd@1.17.0/assets/icons/space.svg">
|
|
24
24
|
</p>
|
|
25
25
|
|
|
26
|
-
<p><img src="https://unpkg.com/jackherd@1.
|
|
26
|
+
<p><img src="https://unpkg.com/jackherd@1.17.0/assets/icons/lanes.svg" width="24" height="34" align="middle" alt=""> <b>Parallel where it pays.</b><br>Phases that don't collide run side by side, each lane on its own branch and worktree. Jack keeps the merge order and has the others rebase.</p>
|
|
27
27
|
|
|
28
|
-
<p><img src="https://unpkg.com/jackherd@1.
|
|
28
|
+
<p><img src="https://unpkg.com/jackherd@1.17.0/assets/icons/decide.svg" width="24" height="34" align="middle" alt=""> <b>You make the calls.</b><br>Commits, pushes and merges wait for your approval. Jack merges on green checks only where you said so: one pull request, or a plan's for up to 24 hours. The guards catch mistakes, not intent; a hard boundary takes branch protection.</p>
|
|
29
29
|
|
|
30
|
-
<p><img src="https://unpkg.com/jackherd@1.
|
|
30
|
+
<p><img src="https://unpkg.com/jackherd@1.17.0/assets/icons/bark.svg" width="24" height="34" align="middle" alt=""> <b>Jack barks.</b><br>A blocked lane, a red check, a review comment: Jack wakes, and the herd view shows where every lane stands.</p>
|
|
31
31
|
|
|
32
|
-
<p><img src="https://unpkg.com/jackherd@1.
|
|
32
|
+
<p><img src="https://unpkg.com/jackherd@1.17.0/assets/icons/skills.svg" width="24" height="34" align="middle" alt=""> <b>Works with your skills.</b><br>Keep your own skills: a lane builds with whichever one you pick. Plans written elsewhere run too, once Jack fills in what they're missing.</p>
|
|
33
33
|
|
|
34
|
-
<p><img src="https://unpkg.com/jackherd@1.
|
|
34
|
+
<p><img src="https://unpkg.com/jackherd@1.17.0/assets/icons/ear.svg" width="24" height="34" align="middle" alt=""> <b>Jack listens.</b><br>Review comments go to the lane that fixes them, with a test. <code>/jh-follow</code> brings anything with a CLI or an API into the herd: tickets, error logs, status pages, deploys, and whatever else you point it at.</p>
|
|
35
35
|
|
|
36
|
-
<p><img src="https://unpkg.com/jackherd@1.
|
|
36
|
+
<p><img src="https://unpkg.com/jackherd@1.17.0/assets/icons/bowl.svg" width="24" height="34" align="middle" alt=""> <b>Picks up where you left off.</b><br>Plan and board hold the state, not the chat.</p>
|
|
37
37
|
|
|
38
|
-
<p><img src="https://unpkg.com/jackherd@1.
|
|
38
|
+
<p><img src="https://unpkg.com/jackherd@1.17.0/assets/icons/plan.svg" width="24" height="34" align="middle" alt=""> <b>Lives in your repository.</b><br>Plans, board and every decision are Markdown, reviewed like code. <code>jh init</code> gives your team the same setup.<br><img width="1" height="8" alt="" src="https://unpkg.com/jackherd@1.17.0/assets/icons/space.svg"></p>
|
|
39
39
|
|
|
40
40
|
## Quickstart
|
|
41
41
|
|
|
42
|
-
1. **Install** (
|
|
42
|
+
1. **Install** (needs Claude Code and git). → [INSTALL.md](https://unpkg.com/jackherd/INSTALL.md)
|
|
43
43
|
|
|
44
44
|
```bash
|
|
45
45
|
curl -fsSL https://unpkg.com/jackherd/install.sh | sh
|
|
@@ -71,22 +71,22 @@ Then `/jh-start` in a repository, and Jack offers your first plan. → [Quicksta
|
|
|
71
71
|
/jh-orchestrate docs/plans/board-<name>/board.md
|
|
72
72
|
```
|
|
73
73
|
|
|
74
|
-
By hand (Bun 1.3+): `bun add -g jackherd` or `npm install -g jackherd`, then `jh init --global` for yourself, or `jh init` in a repository to give your team the same skills and guard hooks. → [INSTALL.md](https://unpkg.com/jackherd/INSTALL.md)<br><img width="1" height="8" alt="" src="https://unpkg.com/jackherd@1.
|
|
74
|
+
Linux and macOS are supported (Windows through WSL); the requirements are in [INSTALL.md](https://unpkg.com/jackherd/INSTALL.md). By hand (Bun 1.3+): `bun add -g jackherd` or `npm install -g jackherd`, then `jh init --global` for yourself, or `jh init` in a repository to give your team the same skills and guard hooks. → [INSTALL.md](https://unpkg.com/jackherd/INSTALL.md)<br><img width="1" height="8" alt="" src="https://unpkg.com/jackherd@1.17.0/assets/icons/space.svg">
|
|
75
75
|
|
|
76
76
|
## Follow anything outside GitHub
|
|
77
77
|
|
|
78
78
|
Whatever you check by hand, Jack can watch: tickets, error logs, status pages, deploys. One `/jh-follow`, one yes, and every change comes to you like this:
|
|
79
79
|
|
|
80
|
-
<img src="https://unpkg.com/jackherd@1.
|
|
80
|
+
<img src="https://unpkg.com/jackherd@1.17.0/docs/follow-dark.svg" width="720" alt="A source (ticket, error log or status page) has a new or changed entry; jh watch reports it to Jack; Jack puts a row into Your turn, prefixed with the source's label; its [ask] button makes Jack ask you, and then a lane works it.">
|
|
81
81
|
|
|
82
|
-
The herd sums up your sources in one `Sources` line: per source its new entries, or when it last ran, red while it fails; opened, a click on an entry opens it. A ticket shows on the lane that works it, and a row in Your turn that came from a source starts with that source's label.<br><img width="1" height="8" alt="" src="https://unpkg.com/jackherd@1.
|
|
82
|
+
The herd sums up your sources in one `Sources` line: per source its new entries, or when it last ran, red while it fails; opened, a click on an entry opens it. A ticket shows on the lane that works it, and a row in Your turn that came from a source starts with that source's label.<br><img width="1" height="8" alt="" src="https://unpkg.com/jackherd@1.17.0/assets/icons/space.svg">
|
|
83
83
|
|
|
84
84
|
## In detail
|
|
85
85
|
|
|
86
86
|
<details>
|
|
87
87
|
<summary><b>What Jack does on his own, and what waits for you</b></summary>
|
|
88
88
|
|
|
89
|
-
<img src="https://unpkg.com/jackherd@1.
|
|
89
|
+
<img src="https://unpkg.com/jackherd@1.17.0/docs/lanes-dark.svg" alt="Jack prompts two lanes and asks you for decisions and approvals; the lanes send questions back as text; jh watch reads lane state, pull requests and checks and wakes Jack.">
|
|
90
90
|
|
|
91
91
|
| On his own | After your approval | Never |
|
|
92
92
|
| --- | --- | --- |
|
|
@@ -108,7 +108,7 @@ A project install adds two hooks to Claude Code's [permission rules](https://cod
|
|
|
108
108
|
|
|
109
109
|
`jh sync` also adds ask rules for check reruns and `gh api` writes.
|
|
110
110
|
|
|
111
|
-
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.
|
|
111
|
+
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 write against stage works the same way: with `guard.approvals.standing.tools` naming an SQL tool as `stage` (never production), you approve the environment for up to 24 hours, Jack reads each statement and marks it checked with `jh approval checked`, and a single insert, update or delete then runs without a dialog, leaving a line in the board's history that names the table but no value. A select, a schema change or several statements still ask you. 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.
|
|
112
112
|
|
|
113
113
|
`"merge": { "openAsDraft": true }` has lanes open every pull request as a draft, so pushes while it is worked on start no workflow that skips drafts; Jack runs `gh pr ready` before the merge. The guard never merges a draft, and a check in `guard.requiredChecks` counts only with a run that succeeded, not with the skipped run a draft leaves behind; without that list, at least one run must have succeeded.
|
|
114
114
|
|
|
@@ -121,7 +121,7 @@ The guards catch mistakes, not intent; for that, use branch protection. The hook
|
|
|
121
121
|
|
|
122
122
|
A **plan** is Markdown with phases, checkbox tasks, a model and a review gate per phase. A **board** lists the lanes of one or more plans: which phases each lane builds, on which model, after what. A **lane** is one agent session in its own git worktree (a second checkout of your repository), and **Jack** is the session in the main checkout that runs `/jh-orchestrate` on a board.
|
|
123
123
|
|
|
124
|
-
<img src="https://unpkg.com/jackherd@1.
|
|
124
|
+
<img src="https://unpkg.com/jackherd@1.17.0/docs/skills-dark.svg" alt="Your own plan or an issue is adopted (Jack adds what is missing, you see the diff) and goes to /jh-orchestrate; /jh-grill hands decisions to /jh-plan; /jh-plan feeds /jh-orchestrate or a single /jh-implement, which your own skill can replace; /jh-implement ends in /jh-pre-commit, which runs the reviewers; /jh-diagnose hands a red command to /jh-implement.">
|
|
125
125
|
|
|
126
126
|
| Skill | Does |
|
|
127
127
|
| --- | --- |
|
|
@@ -203,7 +203,9 @@ How the herd reads the board:
|
|
|
203
203
|
|
|
204
204
|
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.
|
|
205
205
|
|
|
206
|
-
`/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. Recipes for Jira, Linear, GitHub issues and Projects, Sentry
|
|
206
|
+
`/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. Recipes for Jira, Linear, GitHub issues and Projects, Sentry, status pages and today's Google Calendar appointments come with it. The reports need herdr; a source can be set up and dry-run without it.
|
|
207
|
+
|
|
208
|
+
A source that is yours alone, such as your calendar, goes into `~/.jackherd/sources.json` instead: it runs for nobody else, in every repository where you start the watcher, or only in the ones its `repos` list (`owner/name`) names. Its secrets live in the system keychain: `jh secret set <NAME>` asks for the value hidden, and only the command of the source that lists it receives it. The watcher takes up a changed source, and a changed `deploy`, `feedback`, `previews` or `checks`, without a restart. A source that fails can say why: a line `jackherd-reason: <reason>` on its stdout shows as `exit 1: <reason>`, with your secrets masked. Any source can show its entries in a block of their own under Your turn with `"show": "entries"`.
|
|
207
209
|
|
|
208
210
|
</details>
|
|
209
211
|
|
|
@@ -225,11 +227,13 @@ Each phase in the plan names its model: the planning model (Opus by default) whe
|
|
|
225
227
|
| `jh check` | types, lint and tests of what you touched, plus the plan and board checks (`--skip-slow`, `--parallel <plan>`) |
|
|
226
228
|
| `jh herd` | the live view (`--once`, `--lane <name>`) |
|
|
227
229
|
| `jh watch` | the watcher Jack runs |
|
|
230
|
+
| `jh resume` | after herdr restarts, wakes the coordinator and the lanes that worked when the machine crashed; runs once per crash and only when the watcher's record shows no `stoppedAt`. Stopping the watcher before quitting herdr prevents it. |
|
|
231
|
+
| `jh secret` | keeps a personal source's secrets in the system keychain (`set <NAME>`, `list`, `delete <NAME>`); `list` shows which are set, never a value |
|
|
228
232
|
| `jh lane prompt <lane>` | writes a lane's first prompt from its board row and its plan (`--out`, `--handover`); your additions below the marker line at its end stay |
|
|
229
233
|
| `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 |
|
|
230
234
|
| `jh preview <file>` | renders a Markdown file through the GitHub API into a preview in GitHub and npm style |
|
|
231
235
|
| `jh waiting` | the files and Artifacts sent to you that you have not answered yet (`close <id>…` or `close --all` once you have) |
|
|
232
|
-
| `jh context` | how full an agent's context is, as JSON |
|
|
236
|
+
| `jh context` | how full an agent's context is, as JSON; `--usage [--since 24h\|7d\|<date>]` the tokens a lane used instead (Jack's with `--coordinator`) |
|
|
233
237
|
| `jh config` | the commands and the base branch the repository uses; a missing value goes to stderr with the line to set, and `jh config <key>` for an unset value exits 3 |
|
|
234
238
|
|
|
235
239
|
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:
|
|
@@ -246,7 +250,7 @@ Without a global install, run them as `bunx jackherd <command>`. The config is `
|
|
|
246
250
|
}
|
|
247
251
|
```
|
|
248
252
|
|
|
249
|
-
`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.
|
|
253
|
+
`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. It also reads the rule blocks you added or changed in `**/rules/**/*.md`, `CLAUDE.md` and `AGENTS.md`, and warns (`Lint pointers`) where a block carries a rule your linter could check (a banned import path, a literal comparison, a comment form, a banned call) and names no lint rule. Name it with `` `lint:<rule id>` ``, or write `` `lint:none` `` and a reason where the rule stays prose. `check.lintPointers.level` is `warn` (default), `fail` or `off`; `check.lintPointers.exclude` lists globs that are never read. `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.
|
|
250
254
|
|
|
251
255
|
</details>
|
|
252
256
|
|
|
@@ -23,6 +23,10 @@ var STANDING_APPROVAL_TEXTS = {
|
|
|
23
23
|
[PLAN_LANGUAGE.en]: { approve: "Approve standing", until: "until" },
|
|
24
24
|
[PLAN_LANGUAGE.de]: { approve: "Stehend freigeben", until: "bis" }
|
|
25
25
|
};
|
|
26
|
+
var RECOMMENDED_MARK = {
|
|
27
|
+
[PLAN_LANGUAGE.en]: "(recommended)",
|
|
28
|
+
[PLAN_LANGUAGE.de]: "(Empfohlen)"
|
|
29
|
+
};
|
|
26
30
|
var LANE_PROMPT_OPENING = {
|
|
27
31
|
[PLAN_LANGUAGE.en]: "You are the lane",
|
|
28
32
|
[PLAN_LANGUAGE.de]: "Du bist die Spur"
|
|
@@ -96,6 +100,7 @@ var PLAN_VOCABULARY = {
|
|
|
96
100
|
phase: "Phase",
|
|
97
101
|
noReviewGate: "Kein Review-Gate",
|
|
98
102
|
laneClosed: "abger\xE4umt",
|
|
103
|
+
partialPhase: "(Teil)",
|
|
99
104
|
openSubagents: "offene Sub-Agenten",
|
|
100
105
|
sideOrderMarker: "dazu (User",
|
|
101
106
|
startsNow: ["sofort"],
|
|
@@ -130,6 +135,7 @@ var PLAN_VOCABULARY = {
|
|
|
130
135
|
phase: "Phase",
|
|
131
136
|
noReviewGate: "No review gate",
|
|
132
137
|
laneClosed: "cleaned up",
|
|
138
|
+
partialPhase: "(part)",
|
|
133
139
|
openSubagents: "open subagents",
|
|
134
140
|
sideOrderMarker: "also (User",
|
|
135
141
|
startsNow: ["now", "immediately"],
|
|
@@ -184,6 +190,7 @@ export {
|
|
|
184
190
|
ADDRESS_FORM,
|
|
185
191
|
SELF_MERGE_LABEL,
|
|
186
192
|
STANDING_APPROVAL_TEXTS,
|
|
193
|
+
RECOMMENDED_MARK,
|
|
187
194
|
LANE_PROMPT_OPENING,
|
|
188
195
|
APPROVAL_TEXTS,
|
|
189
196
|
APPOINTMENT_VOCABULARY,
|
package/dist/config.d.ts
CHANGED
|
@@ -8,6 +8,10 @@ declare const TERMINAL_ADAPTER: {
|
|
|
8
8
|
readonly herdr: "herdr";
|
|
9
9
|
};
|
|
10
10
|
type TerminalAdapter = (typeof TERMINAL_ADAPTER)[keyof typeof TERMINAL_ADAPTER];
|
|
11
|
+
/** The environments a standing approval may cover; production is never one. */
|
|
12
|
+
declare const STANDING_ENVIRONMENT: {
|
|
13
|
+
readonly stage: "stage";
|
|
14
|
+
};
|
|
11
15
|
declare const MODEL_ROLE: {
|
|
12
16
|
readonly plan: "plan";
|
|
13
17
|
readonly implement: "implement";
|
|
@@ -75,6 +79,7 @@ declare const sourceSchema: z.ZodObject<{
|
|
|
75
79
|
reportOn: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
76
80
|
secrets: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
77
81
|
show: z.ZodOptional<z.ZodLiteral<"entries">>;
|
|
82
|
+
repos: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
78
83
|
}, z.core.$strict>;
|
|
79
84
|
declare const jackherdConfigSchema: z.ZodObject<{
|
|
80
85
|
$schema: z.ZodOptional<z.ZodString>;
|
|
@@ -114,6 +119,7 @@ declare const jackherdConfigSchema: z.ZodObject<{
|
|
|
114
119
|
lanes: z.ZodPrefault<z.ZodObject<{
|
|
115
120
|
copy: z.ZodDefault<z.ZodArray<z.ZodString>>;
|
|
116
121
|
lockFile: z.ZodOptional<z.ZodString>;
|
|
122
|
+
minFreeGb: z.ZodOptional<z.ZodNumber>;
|
|
117
123
|
}, z.core.$strict>>;
|
|
118
124
|
models: z.ZodDefault<z.ZodRecord<z.ZodEnum<{
|
|
119
125
|
review: "review";
|
|
@@ -254,6 +260,7 @@ declare const jackherdConfigSchema: z.ZodObject<{
|
|
|
254
260
|
reportOn: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
255
261
|
secrets: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
256
262
|
show: z.ZodOptional<z.ZodLiteral<"entries">>;
|
|
263
|
+
repos: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
257
264
|
}, z.core.$strict>>>;
|
|
258
265
|
}, z.core.$strict>>;
|
|
259
266
|
guard: z.ZodPrefault<z.ZodPipe<z.ZodObject<{
|
|
@@ -264,6 +271,11 @@ declare const jackherdConfigSchema: z.ZodObject<{
|
|
|
264
271
|
}, z.core.$strict>>;
|
|
265
272
|
approvals: z.ZodPrefault<z.ZodObject<{
|
|
266
273
|
waitMinutes: z.ZodDefault<z.ZodNumber>;
|
|
274
|
+
standing: z.ZodOptional<z.ZodObject<{
|
|
275
|
+
tools: z.ZodRecord<z.ZodString, z.ZodEnum<{
|
|
276
|
+
stage: "stage";
|
|
277
|
+
}>>;
|
|
278
|
+
}, z.core.$strict>>;
|
|
267
279
|
}, z.core.$strict>>;
|
|
268
280
|
alwaysAsk: z.ZodDefault<z.ZodArray<z.ZodString>>;
|
|
269
281
|
requiredChecks: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
@@ -281,6 +293,9 @@ declare const jackherdConfigSchema: z.ZodObject<{
|
|
|
281
293
|
};
|
|
282
294
|
approvals: {
|
|
283
295
|
waitMinutes: number;
|
|
296
|
+
standing?: {
|
|
297
|
+
tools: Record<string, "stage">;
|
|
298
|
+
} | undefined;
|
|
284
299
|
};
|
|
285
300
|
}, {
|
|
286
301
|
commands: string[];
|
|
@@ -290,6 +305,9 @@ declare const jackherdConfigSchema: z.ZodObject<{
|
|
|
290
305
|
};
|
|
291
306
|
approvals: {
|
|
292
307
|
waitMinutes: number;
|
|
308
|
+
standing?: {
|
|
309
|
+
tools: Record<string, "stage">;
|
|
310
|
+
} | undefined;
|
|
293
311
|
};
|
|
294
312
|
alwaysAsk: string[];
|
|
295
313
|
requiredChecks?: string[] | undefined;
|
|
@@ -333,6 +351,19 @@ declare const jackherdConfigSchema: z.ZodObject<{
|
|
|
333
351
|
reviewer: z.ZodDefault<z.ZodString>;
|
|
334
352
|
}, z.core.$strict>>;
|
|
335
353
|
skillBudgets: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodNumber>>;
|
|
354
|
+
openBoxes: z.ZodDefault<z.ZodEnum<{
|
|
355
|
+
fail: "fail";
|
|
356
|
+
warn: "warn";
|
|
357
|
+
off: "off";
|
|
358
|
+
}>>;
|
|
359
|
+
lintPointers: z.ZodOptional<z.ZodObject<{
|
|
360
|
+
level: z.ZodDefault<z.ZodEnum<{
|
|
361
|
+
fail: "fail";
|
|
362
|
+
warn: "warn";
|
|
363
|
+
off: "off";
|
|
364
|
+
}>>;
|
|
365
|
+
exclude: z.ZodDefault<z.ZodArray<z.ZodString>>;
|
|
366
|
+
}, z.core.$strict>>;
|
|
336
367
|
looseTests: z.ZodDefault<z.ZodBoolean>;
|
|
337
368
|
roots: z.ZodOptional<z.ZodArray<z.ZodPipe<z.ZodString, z.ZodTransform<string, string>>>>;
|
|
338
369
|
sourceFiles: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
@@ -378,6 +409,7 @@ declare const personalSourcesSchema: z.ZodObject<{
|
|
|
378
409
|
reportOn: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
379
410
|
secrets: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
380
411
|
show: z.ZodOptional<z.ZodLiteral<"entries">>;
|
|
412
|
+
repos: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
381
413
|
}, z.core.$strict>>>;
|
|
382
414
|
}, z.core.$strict>;
|
|
383
415
|
type PersonalSourcesFile = z.infer<typeof personalSourcesSchema>;
|
|
@@ -386,4 +418,4 @@ declare const parsePersonalSourcesFile: (raw: unknown, label: string) => Persona
|
|
|
386
418
|
/** One `watch.sources` entry outside the config, such as a draft to try; invalid throws with `label` and the issues. */
|
|
387
419
|
declare const parseSourceDraft: (raw: unknown, label: string) => z.infer<typeof sourceSchema>;
|
|
388
420
|
|
|
389
|
-
export { COMMAND_KEYS, type CommandKey, DEFAULT_CONTEXT_WINDOW, DEFAULT_MODELS, DEFAULT_SKILL_PREFIX, type JackherdConfig, KIT_CORE, MODEL_ROLE, type ModelRole, type PersonalSourcesFile, SKILL_PREFIX, TERMINAL_ADAPTER, type TerminalAdapter, isModelRole, isTerminalAdapter, jackherdConfigSchema, parseConfigFile, parseJackherdConfig, parsePersonalSourcesFile, parseSourceDraft, personalSourcesSchema, renderConfigJsonSchema, renderSourcesJsonSchema };
|
|
421
|
+
export { COMMAND_KEYS, type CommandKey, DEFAULT_CONTEXT_WINDOW, DEFAULT_MODELS, DEFAULT_SKILL_PREFIX, type JackherdConfig, KIT_CORE, MODEL_ROLE, type ModelRole, type PersonalSourcesFile, SKILL_PREFIX, STANDING_ENVIRONMENT, TERMINAL_ADAPTER, type TerminalAdapter, isModelRole, isTerminalAdapter, jackherdConfigSchema, parseConfigFile, parseJackherdConfig, parsePersonalSourcesFile, parseSourceDraft, personalSourcesSchema, renderConfigJsonSchema, renderSourcesJsonSchema };
|
package/dist/config.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import {
|
|
2
2
|
PLAN_LANGUAGE,
|
|
3
3
|
RESPONSE_ADDRESS
|
|
4
|
-
} from "./chunk-
|
|
4
|
+
} from "./chunk-N5FAOQZL.js";
|
|
5
5
|
|
|
6
6
|
// src/config/json-schema.ts
|
|
7
7
|
import { z as z3 } from "zod";
|
|
@@ -73,6 +73,15 @@ var publicContent = z.strictObject({
|
|
|
73
73
|
reviewer: nonEmpty.default("public-content")
|
|
74
74
|
});
|
|
75
75
|
var skillBudgets = z.record(glob, z.number().int().positive());
|
|
76
|
+
var OPEN_BOXES_LEVEL = { fail: "fail", warn: "warn", off: "off" };
|
|
77
|
+
var LINT_POINTERS_LEVEL = { warn: "warn", fail: "fail", off: "off" };
|
|
78
|
+
var lintPointers = z.strictObject({
|
|
79
|
+
/** `warn` reports a block and lets the commit through; `fail` stops it; `off` skips the check. */
|
|
80
|
+
level: z.enum([LINT_POINTERS_LEVEL.warn, LINT_POINTERS_LEVEL.fail, LINT_POINTERS_LEVEL.off]).default(LINT_POINTERS_LEVEL.warn),
|
|
81
|
+
/** Rule files left unread, as globs from the project root; the kit copies in the lock file are always left out. */
|
|
82
|
+
exclude: z.array(glob).default([])
|
|
83
|
+
});
|
|
84
|
+
var DEFAULT_LINT_POINTERS = { level: LINT_POINTERS_LEVEL.warn, exclude: [] };
|
|
76
85
|
var checkConfigSchema = z.strictObject({
|
|
77
86
|
/**
|
|
78
87
|
* Run once over the changed files, which are appended as arguments. Separate from `commands.lint`,
|
|
@@ -90,6 +99,17 @@ var checkConfigSchema = z.strictObject({
|
|
|
90
99
|
* path and sets one for the project's own skills; a file with none is measured against the kit's default.
|
|
91
100
|
*/
|
|
92
101
|
skillBudgets: skillBudgets.optional(),
|
|
102
|
+
/**
|
|
103
|
+
* What a lane's check does when the phase its board row names still has open boxes without `→ P<n>`: `fail` (the
|
|
104
|
+
* default) stops the commit, `warn` reports, `off` skips. A row carrying the `partialPhase` word (`(Teil)` in German) builds part of the phase and is
|
|
105
|
+
* left alone; a branch whose directory is no lane's is not checked.
|
|
106
|
+
*/
|
|
107
|
+
openBoxes: z.enum([OPEN_BOXES_LEVEL.fail, OPEN_BOXES_LEVEL.warn, OPEN_BOXES_LEVEL.off]).default(OPEN_BOXES_LEVEL.fail),
|
|
108
|
+
/**
|
|
109
|
+
* Whether a new or changed block of a rule file (or `CLAUDE.md`, `AGENTS.md`) that a linter could check, such
|
|
110
|
+
* as a forbidden import path or call, must name its lint rule with `` `lint:<rule id>` `` (or `` `lint:none` `` and a reason).
|
|
111
|
+
*/
|
|
112
|
+
lintPointers: lintPointers.optional(),
|
|
93
113
|
/** Also run tests in directories that hold test files but belong to no package. */
|
|
94
114
|
looseTests: z.boolean().default(false),
|
|
95
115
|
/**
|
|
@@ -275,6 +295,9 @@ import { isAbsolute } from "path";
|
|
|
275
295
|
var segmentsOf = (path) => path.split(/[\\/]/).filter((segment) => segment !== "" && segment !== ".");
|
|
276
296
|
var isRepositoryDirectory = (value) => !isAbsolute(value) && segmentsOf(value).length > 0 && !segmentsOf(value).includes("..");
|
|
277
297
|
|
|
298
|
+
// src/config/repository-identity.ts
|
|
299
|
+
var REPOSITORY_NAME = /^[\w.-]+\/[\w.-]+$/;
|
|
300
|
+
|
|
278
301
|
// src/config/tones.ts
|
|
279
302
|
var TONE = {
|
|
280
303
|
amber: "amber",
|
|
@@ -303,6 +326,7 @@ var DEFAULT_TONES = {
|
|
|
303
326
|
|
|
304
327
|
// src/config/schema.ts
|
|
305
328
|
var TERMINAL_ADAPTER = { none: "none", herdr: "herdr" };
|
|
329
|
+
var STANDING_ENVIRONMENT = { stage: "stage" };
|
|
306
330
|
var MODEL_ROLE = {
|
|
307
331
|
plan: "plan",
|
|
308
332
|
implement: "implement",
|
|
@@ -482,7 +506,12 @@ var sourceSchema = z2.strictObject({
|
|
|
482
506
|
*/
|
|
483
507
|
secrets: z2.array(z2.string().regex(SECRET_NAME, "capital letters, digits and `_`, starting with a letter")).max(10).refine((names) => new Set(names).size === names.length, "secret names must be unique").optional(),
|
|
484
508
|
/** `entries` shows the source's entries open, in a herd block of its own; not for a ticket source, whose entries are badges. */
|
|
485
|
-
show: z2.literal(SOURCE_SHOW.entries).optional()
|
|
509
|
+
show: z2.literal(SOURCE_SHOW.entries).optional(),
|
|
510
|
+
/**
|
|
511
|
+
* `owner/name` of the repositories a personal source runs in (matched without regard to case); without it every
|
|
512
|
+
* repository. Only in the personal sources file: the repository's own config is for that repository already.
|
|
513
|
+
*/
|
|
514
|
+
repos: z2.array(z2.string().regex(REPOSITORY_NAME, "owner/name, such as acme/tool")).min(1).max(20).refine((repos) => new Set(repos.map((repo) => repo.toLowerCase())).size === repos.length, "repos must be unique").optional()
|
|
486
515
|
}).refine(canChange, {
|
|
487
516
|
message: "map needs updatedAt, a content field (title, author, body, url) or reportOn",
|
|
488
517
|
path: ["map"]
|
|
@@ -500,12 +529,26 @@ var sourceSchema = z2.strictObject({
|
|
|
500
529
|
path: ["reportOn"]
|
|
501
530
|
});
|
|
502
531
|
var hasUniqueNames = (sources) => new Set(sources.map(({ name }) => name)).size === sources.length;
|
|
532
|
+
var repositorySources = z2.array(sourceSchema).refine(hasUniqueNames, "source names must be unique").superRefine((sources, context) => {
|
|
533
|
+
sources.forEach((source, index) => {
|
|
534
|
+
if (source.repos === void 0) return;
|
|
535
|
+
context.addIssue({
|
|
536
|
+
code: "custom",
|
|
537
|
+
path: [index, "repos"],
|
|
538
|
+
message: "repos belongs in the personal sources file (~/.jackherd/sources.json): this config is for one repository"
|
|
539
|
+
});
|
|
540
|
+
});
|
|
541
|
+
});
|
|
503
542
|
var coordinatorSchema = z2.strictObject({
|
|
504
543
|
/** Tokens in the coordinator's context from which the watcher reports `coordinator warn`. */
|
|
505
544
|
warnTokens: tokenCount.default(35e4),
|
|
506
545
|
/** Tokens in the coordinator's context from which the watcher reports `coordinator alert`. */
|
|
507
546
|
alertTokens: tokenCount.default(5e5)
|
|
508
547
|
}).refine(warnBelowAlert, WARN_BELOW_ALERT);
|
|
548
|
+
var standingTool = z2.string().regex(
|
|
549
|
+
/^mcp__[A-Za-z0-9_-]+__execute_sql$/,
|
|
550
|
+
"a full MCP tool name ending in execute_sql, such as mcp__<server>__execute_sql"
|
|
551
|
+
).refine((name) => !/prod/i.test(name), "a tool whose name says prod never runs under a standing approval");
|
|
509
552
|
var guardedCommand = z2.string().regex(/^[a-z][a-z0-9-]*( [a-z][a-z0-9-]*){1,3}$/, "a program and one to three plain words, such as gh pr merge");
|
|
510
553
|
var checkFreePattern = z2.string().regex(/^[A-Za-z0-9_.*/-]+$/, "path words with * and ** only, such as docs/**/*.md").refine(
|
|
511
554
|
(pattern) => !pattern.startsWith("/") && !pattern.split("/").includes(".."),
|
|
@@ -551,7 +594,13 @@ var guardFields = z2.strictObject({
|
|
|
551
594
|
}).prefault({}),
|
|
552
595
|
approvals: z2.strictObject({
|
|
553
596
|
/** 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. */
|
|
554
|
-
waitMinutes: z2.number().int().min(1).max(9).default(8)
|
|
597
|
+
waitMinutes: z2.number().int().min(1).max(9).default(8),
|
|
598
|
+
/**
|
|
599
|
+
* The calls a standing approval of the user may let through once the coordinator marked them checked: each tool
|
|
600
|
+
* by its full name and the environment it writes to, only `stage`. A tool whose name says `prod` is refused, as the
|
|
601
|
+
* hook refuses it under a broken config; which project a server reaches is the project's start script's to say.
|
|
602
|
+
*/
|
|
603
|
+
standing: z2.strictObject({ tools: z2.record(standingTool, z2.enum(Object.values(STANDING_ENVIRONMENT))) }).optional()
|
|
555
604
|
}).prefault({}),
|
|
556
605
|
/**
|
|
557
606
|
* Paths that always ask, on top of the ones jackherd brings (`.github/**`, `**\/migrations/**`, `**\/.env*`,
|
|
@@ -638,7 +687,9 @@ var jackherdConfigSchema = z2.strictObject({
|
|
|
638
687
|
/** Untracked files copied into every new worktree; empty means the files `.worktreeinclude` lists. */
|
|
639
688
|
copy: z2.array(laneCopyPath).default([]),
|
|
640
689
|
/** The dependency lock file a rebase takes from the base branch and regenerates, never merges by hand; detected from the repository when left out. */
|
|
641
|
-
lockFile: lockFilePath.optional()
|
|
690
|
+
lockFile: lockFilePath.optional(),
|
|
691
|
+
/** Free gigabytes the disk must hold before `jh lane space` lets a new lane install its dependencies; 5 when left out. */
|
|
692
|
+
minFreeGb: z2.number().positive().optional()
|
|
642
693
|
}).prefault({}),
|
|
643
694
|
models: z2.record(z2.enum(Object.values(MODEL_ROLE)), modelId).default(DEFAULT_MODELS),
|
|
644
695
|
terminal: z2.strictObject({ adapter: z2.enum(Object.values(TERMINAL_ADAPTER)).default(TERMINAL_ADAPTER.none) }).prefault({}),
|
|
@@ -680,7 +731,7 @@ var jackherdConfigSchema = z2.strictObject({
|
|
|
680
731
|
parallelTasks: parallelTasksSchema.prefault({}),
|
|
681
732
|
taskSignalMinutes: z2.number().int().positive().default(DEFAULT_TASK_SIGNAL_MINUTES),
|
|
682
733
|
previews: previewsSchema.optional(),
|
|
683
|
-
sources:
|
|
734
|
+
sources: repositorySources.default([])
|
|
684
735
|
}).prefault({}),
|
|
685
736
|
/** Read by the merge guard hook, which asks before the listed commands run, and by `/orchestrate`. */
|
|
686
737
|
guard: guardSchema.prefault({}),
|
|
@@ -719,6 +770,7 @@ export {
|
|
|
719
770
|
KIT_CORE,
|
|
720
771
|
MODEL_ROLE,
|
|
721
772
|
SKILL_PREFIX,
|
|
773
|
+
STANDING_ENVIRONMENT,
|
|
722
774
|
TERMINAL_ADAPTER,
|
|
723
775
|
isModelRole,
|
|
724
776
|
isTerminalAdapter,
|
package/dist/plan-format.d.ts
CHANGED
|
@@ -32,6 +32,11 @@ declare const STANDING_APPROVAL_TEXTS: Record<PlanLanguage, {
|
|
|
32
32
|
approve: string;
|
|
33
33
|
until: string;
|
|
34
34
|
}>;
|
|
35
|
+
/**
|
|
36
|
+
* The mark at the end of the first option's label that says it is the recommendation. The plain question hook sends
|
|
37
|
+
* a dialog back whose first option lacks it, so it is fixed per language and not a config value.
|
|
38
|
+
*/
|
|
39
|
+
declare const RECOMMENDED_MARK: Record<PlanLanguage, string>;
|
|
35
40
|
/**
|
|
36
41
|
* The words a lane prompt opens with, followed by the lane's name. The ask hint hook and the project's lane exception
|
|
37
42
|
* recognise a lane by them, so they are fixed per language and not a config value.
|
|
@@ -133,6 +138,8 @@ type PlanVocabulary = {
|
|
|
133
138
|
noReviewGate: string;
|
|
134
139
|
/** Marks a lane as closed when its name cell contains it. */
|
|
135
140
|
laneClosed: string;
|
|
141
|
+
/** Stands in a board row's phases cell when that pull request builds only part of the phase. */
|
|
142
|
+
partialPhase: string;
|
|
136
143
|
/** Opens the line a lane's hand-over file must hold as `<word>: 0`, so the coordinator hands over only after a wave. */
|
|
137
144
|
openSubagents: string;
|
|
138
145
|
/** Words a starts-after cell begins with when the lane may start at once. */
|
|
@@ -460,13 +467,12 @@ declare const tallyPhases: (markdown: string, language: PlanLanguage) => PhaseTa
|
|
|
460
467
|
type PhaseWithoutGate = PhaseHeading;
|
|
461
468
|
/** A row of the lanes table (1-based line) whose phases cell names no phase id. */
|
|
462
469
|
type RowWithoutPhase = OpenLaneRow;
|
|
463
|
-
/**
|
|
464
|
-
|
|
465
|
-
* has open checkboxes not moved on with `→ P<n>` (`openLines`, 1-based).
|
|
466
|
-
*/
|
|
467
|
-
type MergedPhaseWithOpenBoxes = PhaseWithoutGate & {
|
|
470
|
+
/** A `### Phase` heading (1-based line) whose own section has open checkboxes not moved on with `→ P<n>` (`openLines`, 1-based). */
|
|
471
|
+
type PhaseWithOpenBoxes = PhaseWithoutGate & {
|
|
468
472
|
openLines: number[];
|
|
469
473
|
};
|
|
474
|
+
/** A `PhaseWithOpenBoxes` whose tasks are all ticked on the base branch. */
|
|
475
|
+
type MergedPhaseWithOpenBoxes = PhaseWithOpenBoxes;
|
|
470
476
|
/** The ids of the `### Phase` headings of a plan. */
|
|
471
477
|
declare const phaseIdsOfPlan: (markdown: string, language: PlanLanguage) => Set<string>;
|
|
472
478
|
/**
|
|
@@ -485,6 +491,12 @@ declare const hasLanesTable: (markdown: string, language: PlanLanguage) => boole
|
|
|
485
491
|
* with an open task there, is still being worked on.
|
|
486
492
|
*/
|
|
487
493
|
declare const mergedPhasesWithOpenBoxes: (markdown: string, base: string, language: PlanLanguage) => MergedPhaseWithOpenBoxes[];
|
|
494
|
+
/**
|
|
495
|
+
* The phase `id` of `markdown` when its own section still has open checkboxes, gate boxes included, that no `→ P<n>`
|
|
496
|
+
* at their end moves on; null when it has none or the plan lacks the phase. Unlike `mergedPhasesWithOpenBoxes` it
|
|
497
|
+
* does not ask whether the phase counts as merged: the caller knows which phase a branch builds.
|
|
498
|
+
*/
|
|
499
|
+
declare const openBoxesOfPhase: (markdown: string, language: PlanLanguage, id: string) => PhaseWithOpenBoxes | null;
|
|
488
500
|
/**
|
|
489
501
|
* The changed rows of open lanes whose phases cell names no phase id (`P10c`), so neither the view nor the watcher
|
|
490
502
|
* can tie that step to a phase.
|
|
@@ -614,4 +626,4 @@ declare const readSprintDocument: (markdown: string, language: PlanLanguage) =>
|
|
|
614
626
|
*/
|
|
615
627
|
declare const readPhaseDependencies: (planMarkdown: string, language: PlanLanguage) => Record<string, string>;
|
|
616
628
|
|
|
617
|
-
export { ADDRESS_FORM, APPOINTMENT_VOCABULARY, APPROVAL_TEXTS, type ChecklistItem, type ColumnModel, type DeferredItem, type DriftFinding, LANE_PROMPT_OPENING, LANGUAGE_NAME, type LaneProgress, type LongPhaseTitle, type MergedPhaseWithOpenBoxes, type MisplacedDeferral, type ModelRow, NEXT_VOCABULARY, NO_DEPENDENCY_TABLE, PHASE_CELL_LIMIT, PHASE_ID_PREFIX, PHASE_TITLE_LIMIT, PHASE_WORK, PLAN_LANGUAGE, PLAN_STATUS, PLAN_VOCABULARY, PROSE_REASON, type PhaseDependency, type PhaseHeading, type PhaseProgress, type PhaseSchedule, type PhaseTally, type PhaseWithoutGate, type PhaseWithoutRow, type PhaseWork, type PhaseWorkFields, type PlanHeader, type PlanLanguage, type PlanPhase, type PlanStatus, type PlanStep, type PlanVocabulary, type PlansByHref, type ProseReason, RESPONSE_ADDRESS, type ResponseAddress, type RowWithProse, type RowWithoutPhase, SELF_MERGE_LABEL, SHORT_NAME_LIMIT, SHORT_NAME_PROBLEM, STANDING_APPROVAL_TEXTS, type ShortNameFinding, type ShortNameProblem, type SprintDocument, type SprintLane, type SprintPlanRow, type SprintPlanTable, type StatusWord, type StepProgress, WAITING_VOCABULARY, activeItem, boardRowsWithProse, boardRowsWithoutPhase, classifyStatus, columnModels, countDecisions, criticalPath, currentPhase, currentPhaseOf, deferralTarget, differsFromColumn, endsPhase, findDrift, hasLanesTable, isCheckboxLine, isDocumentationGateItem, isOpenCheckboxLine, isPhaseFinished, isPhaseOpen, isPlanLanguage, isReviewGateLine, isTickedUnmerged, laneNameOf, laneProgress, longPhaseTitles, mergedPhasesWithOpenBoxes, misplacedDeferrals, newPhasesWithoutReviewGate, onlyDocumentationOpen, parallelGroups, parallelGroupsOfPlan, parseDependencyTable, parseModelTable, parsePlanPhases, parseSprintLanes, phaseCount, phaseHeading, phaseHeadingPattern, phaseHeadings, phaseIdOf, phaseIdsOfCell, phaseIdsOfPlan, phaseLabel, phaseLabels, phaseProgress, phaseWaves, phaseWorkFields, phasesText, phasesWithoutDependencyRow, readPhaseDependencies, readPlanHeader, readSprintDocument, resolvePlanPath, sectionHeading, shortNameFindings, shortTitle, sprintTitleOf, sumPhases, tallyPhases, wavesOfPlan, withoutRepeats };
|
|
629
|
+
export { ADDRESS_FORM, APPOINTMENT_VOCABULARY, APPROVAL_TEXTS, type ChecklistItem, type ColumnModel, type DeferredItem, type DriftFinding, LANE_PROMPT_OPENING, LANGUAGE_NAME, type LaneProgress, type LongPhaseTitle, type MergedPhaseWithOpenBoxes, type MisplacedDeferral, type ModelRow, NEXT_VOCABULARY, NO_DEPENDENCY_TABLE, PHASE_CELL_LIMIT, PHASE_ID_PREFIX, PHASE_TITLE_LIMIT, PHASE_WORK, PLAN_LANGUAGE, PLAN_STATUS, PLAN_VOCABULARY, PROSE_REASON, type PhaseDependency, type PhaseHeading, type PhaseProgress, type PhaseSchedule, type PhaseTally, type PhaseWithOpenBoxes, type PhaseWithoutGate, type PhaseWithoutRow, type PhaseWork, type PhaseWorkFields, type PlanHeader, type PlanLanguage, type PlanPhase, type PlanStatus, type PlanStep, type PlanVocabulary, type PlansByHref, type ProseReason, RECOMMENDED_MARK, RESPONSE_ADDRESS, type ResponseAddress, type RowWithProse, type RowWithoutPhase, SELF_MERGE_LABEL, SHORT_NAME_LIMIT, SHORT_NAME_PROBLEM, STANDING_APPROVAL_TEXTS, type ShortNameFinding, type ShortNameProblem, type SprintDocument, type SprintLane, type SprintPlanRow, type SprintPlanTable, type StatusWord, type StepProgress, WAITING_VOCABULARY, activeItem, boardRowsWithProse, boardRowsWithoutPhase, classifyStatus, columnModels, countDecisions, criticalPath, currentPhase, currentPhaseOf, deferralTarget, differsFromColumn, endsPhase, findDrift, hasLanesTable, isCheckboxLine, isDocumentationGateItem, isOpenCheckboxLine, isPhaseFinished, isPhaseOpen, isPlanLanguage, isReviewGateLine, isTickedUnmerged, laneNameOf, laneProgress, longPhaseTitles, mergedPhasesWithOpenBoxes, misplacedDeferrals, newPhasesWithoutReviewGate, onlyDocumentationOpen, openBoxesOfPhase, parallelGroups, parallelGroupsOfPlan, parseDependencyTable, parseModelTable, parsePlanPhases, parseSprintLanes, phaseCount, phaseHeading, phaseHeadingPattern, phaseHeadings, phaseIdOf, phaseIdsOfCell, phaseIdsOfPlan, phaseLabel, phaseLabels, phaseProgress, phaseWaves, phaseWorkFields, phasesText, phasesWithoutDependencyRow, readPhaseDependencies, readPlanHeader, readSprintDocument, resolvePlanPath, sectionHeading, shortNameFindings, shortTitle, sprintTitleOf, sumPhases, tallyPhases, wavesOfPlan, withoutRepeats };
|
package/dist/plan-format.js
CHANGED
|
@@ -8,6 +8,7 @@ import {
|
|
|
8
8
|
PLAN_LANGUAGE,
|
|
9
9
|
PLAN_STATUS,
|
|
10
10
|
PLAN_VOCABULARY,
|
|
11
|
+
RECOMMENDED_MARK,
|
|
11
12
|
RESPONSE_ADDRESS,
|
|
12
13
|
SELF_MERGE_LABEL,
|
|
13
14
|
STANDING_APPROVAL_TEXTS,
|
|
@@ -17,7 +18,7 @@ import {
|
|
|
17
18
|
phaseHeading,
|
|
18
19
|
sectionHeading,
|
|
19
20
|
terminalSafe
|
|
20
|
-
} from "./chunk-
|
|
21
|
+
} from "./chunk-N5FAOQZL.js";
|
|
21
22
|
|
|
22
23
|
// src/plan-format/lanes.ts
|
|
23
24
|
import { posix } from "path";
|
|
@@ -830,6 +831,10 @@ var mergedPhasesWithOpenBoxes = (markdown, base, language) => {
|
|
|
830
831
|
};
|
|
831
832
|
return phaseSections(markdown, language).filter(({ id, openLines }) => openLines.length > 0 && isMerged(id)).map(({ line, id, title, openLines }) => ({ line, id, title, openLines }));
|
|
832
833
|
};
|
|
834
|
+
var openBoxesOfPhase = (markdown, language, id) => {
|
|
835
|
+
const section = phaseSections(markdown, language).find((candidate) => candidate.id === id);
|
|
836
|
+
return section && section.openLines.length > 0 ? { line: section.line, id: section.id, title: section.title, openLines: section.openLines } : null;
|
|
837
|
+
};
|
|
833
838
|
var boardRowsWithoutPhase = (markdown, language, previousLines) => changedOpenLaneRows(markdown, language, previousLines).filter(
|
|
834
839
|
({ phases }) => phaseIdsOfCell(phases, language).length === 0
|
|
835
840
|
);
|
|
@@ -920,6 +925,7 @@ export {
|
|
|
920
925
|
PLAN_STATUS,
|
|
921
926
|
PLAN_VOCABULARY,
|
|
922
927
|
PROSE_REASON,
|
|
928
|
+
RECOMMENDED_MARK,
|
|
923
929
|
RESPONSE_ADDRESS,
|
|
924
930
|
SELF_MERGE_LABEL,
|
|
925
931
|
SHORT_NAME_LIMIT,
|
|
@@ -955,6 +961,7 @@ export {
|
|
|
955
961
|
misplacedDeferrals,
|
|
956
962
|
newPhasesWithoutReviewGate,
|
|
957
963
|
onlyDocumentationOpen,
|
|
964
|
+
openBoxesOfPhase,
|
|
958
965
|
parallelGroups,
|
|
959
966
|
parallelGroupsOfPlan,
|
|
960
967
|
parseDependencyTable,
|
package/docs/plan-contract.md
CHANGED
|
@@ -82,3 +82,4 @@ dependencies: missing no dependency table: phases run one after another
|
|
|
82
82
|
| 0 | the contract is met (a missing dependency table is a note, not a gap) |
|
|
83
83
|
| 1 | at least one part is missing or malformed |
|
|
84
84
|
| 2 | wrong command line: no subcommand, unknown subcommand, no file, unreadable file, unreadable config |
|
|
85
|
+
| 3 | the contract is met, but the plan or a board that links it is not on the base branch (a `base:` line names each path); the coordinator commits it first (`/orchestrate` § 2 step 0), after which the check counts as done |
|