jackherd 1.24.0 → 1.25.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 +5 -0
- package/README.md +21 -21
- package/dist/{chunk-JDO6O3UO.js → chunk-O365KHHP.js} +139 -3
- package/dist/config.d.ts +4 -0
- package/dist/config.js +4 -1
- package/dist/plan-format.d.ts +121 -3
- package/dist/plan-format.js +176 -35
- package/docs/plan-contract.md +52 -5
- package/herdr-plugin/herdr-plugin.toml +1 -1
- package/install.sh +1 -1
- package/kit/hooks/hooks/approval-gate/index.ts +2 -2
- package/kit/hooks/hooks/merge-guard/lane-plan.ts +1 -1
- package/kit/hooks/hooks/shared/board-lanes.ts +33 -12
- package/kit/hooks/hooks/shared/boards.ts +22 -6
- package/kit/workflow/skills/orchestrate/references/herd.md +3 -3
- package/kit/workflow/skills/plan/SKILL.md +3 -1
- package/kit/workflow/skills/plan/references/template-English.md +2 -0
- package/kit/workflow/skills/plan/references/template-German.md +2 -0
- package/package.json +1 -1
- package/schemas/jackherd.schema.json +7 -0
- package/src/brief/command.ts +1 -1
- package/src/brief/lane-facts.ts +2 -2
- package/src/check/command.ts +2 -1
- package/src/check/plan-structure-check.ts +68 -14
- package/src/check/settings.ts +6 -1
- package/src/check/skill-budget-defaults.ts +6 -3
- package/src/config/schema.ts +3 -0
- package/src/lane/close-board.ts +4 -4
- package/src/lane/close-command.ts +2 -2
- package/src/lane/dev-command.ts +1 -1
- package/src/lane/port-command.ts +1 -1
- package/src/plan-contract/command.ts +2 -1
- package/src/plan-contract/contract.ts +30 -9
- package/src/plan-format/bare-codes.ts +4 -4
- package/src/plan-format/dependencies.ts +5 -5
- package/src/plan-format/lanes.ts +3 -4
- package/src/plan-format/markdown.ts +125 -10
- package/src/plan-format/open-status.ts +15 -0
- package/src/plan-format/phases.ts +45 -4
- package/src/plan-format/plan-head.ts +31 -9
- package/src/plan-format/plan-structure.ts +43 -7
- package/src/plan-format/progress.ts +3 -0
- package/src/plan-format/sprint-document.ts +4 -4
- package/src/plan-format/unrecognized-sections.ts +21 -0
- package/src/plan-format/waiting.ts +13 -13
- package/src/plans/board-close.ts +25 -16
- package/src/plans/board-pick.ts +1 -1
- package/src/plans/ensure-panes.ts +2 -2
- package/src/plans/layout-summary.ts +8 -1
- package/src/plans/layout.ts +14 -5
- package/src/plans/live.ts +1 -1
- package/src/plans/settings.ts +3 -0
- package/src/plans/snapshot.ts +9 -5
- package/src/plans/sprint-export.ts +1 -1
- package/src/plans/texts.ts +3 -0
- package/src/plans/view.ts +1 -0
- package/src/resume/coordinator-read.ts +2 -2
- package/src/settled/command.ts +1 -1
- package/src/settled/settle.ts +5 -5
- package/src/sync/render.ts +20 -5
- package/src/sync/sync-plan.ts +6 -2
- package/src/sync/sync.ts +3 -2
- package/src/vocabulary/index.ts +192 -4
- package/src/watch/appointments.ts +13 -20
- package/src/watch/command.ts +5 -5
- package/src/watch/lane-progress.ts +1 -1
- package/src/watch/merge-overlap-read.ts +2 -2
- package/src/watch/plan-missing-read.ts +3 -1
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.25.0
|
|
6
|
+
|
|
7
|
+
- **Every plan phase says in one sentence what it is about.** The plan template puts a line directly under each `### Phase` heading, `**What it is about:** <sentence>` (German: `**Worum es geht:** <Satz>`): what is different afterwards and for whom, or "nothing visible". `jh check` fails a phase the change adds that has tasks and no such line; phases a plan already had are not read, so older plans stay green. `jh plan check` prints a sixth line, `purpose: present` or `purpose: missing <file>:<line> no purpose line in P2, P3`; like the head it is a note and never changes the exit code. In an opened lane the herd shows the sentence dimmed under the head of each phase it draws; the folded row and the sidebar title stay as they are. The plan skill (§ structure), the herd reference and `docs/plan-contract.md` describe it. Run `jh sync` to get the template and the skill text.
|
|
8
|
+
- **A board and a plan are read in every language, and the language jh writes the board in is its own setting.** A German board next to an English plan (or the other way round) used to leave "Your turn" empty and `jh plan check` red, because every heading, column name and status word was compared with the one language in `planLanguage`. jackherd now reads both languages whatever the config says, and a document in a third language carries a marker, `<!-- jh: <key> -->`, which Markdown does not show: on the line under a heading (`lanes`, `history`, `for-you`, …), in a header cell (`depends-on`, `status`, …), on a head line, in a status cell (`status-open`, `status-done`, …) and on a line that opens with bold words (`purpose`, `no-review-gate`). New `boardLanguage` in `.jackherd/config.json` is the language jh writes the board in; it defaults to `planLanguage`, so a config without it runs unchanged. A plan template renders in the language of its file name (the English file is no longer filled with German headings under a German `planLanguage`). When no section is recognised, `jh plan check` names the forms it looked for, `jh check` warns on a changed file jh reads nothing of, and the herd shows `board: no section recognised` instead of "nothing waits for you". `docs/plan-contract.md` lists the keys. Run `jh sync` to get the template and the skill text.
|
|
9
|
+
|
|
5
10
|
## 1.24.0
|
|
6
11
|
|
|
7
12
|
- **A shared backend for all lanes: `lanes.backend` and `jh backend`.** With herdr, the services every lane's app talks to run in one pane (its own tab) of the main checkout, which the watcher starts at its start and once a minute, and starts again after it ended, at most three times in ten minutes. `lanes.backend` takes a `command` that contains `{services}`, a list of `services` (`name`, `port`, both unique) and an optional pane `label`; the command line starts with `env -u PORT`. A service port that something listens on prevents a start and is never freed. The pane keeps running when the watcher ends, even after `kit updated`; what was last started is kept in `backend.json` in the watcher's directory, so a restart of the watcher leaves a pane with that command alone. `jh backend` shows pane, command, handed-over services and starts; `jh backend restart` starts again after "gave up"; `jh backend release <service> --lane <lane>` stops the pane, starts it without that service and names the port for `jh lane dev <lane> --filter <service> --port <port>`; `jh backend return <service>` takes it back, and the watcher does it once the lane's worktree is gone. Only the process in the pane with the backend's label, in the main checkout, is ever stopped (SIGTERM to its foreground process group). A service name or label must be one shell word that does not start with a dash, and `jh lane dev --filter` now refuses a leading dash too. The orchestrate skill (§ 3, § 7, the `backend:` lines in its events reference) tells the coordinator when to hand a service over. A jackherd older than this version refuses a config that has `lanes.backend`: run `jh update` on every machine first, then `jh sync` for the skill text.
|
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.25.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) [](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.
|
|
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.25.0/assets/icons/space.svg">
|
|
7
7
|
|
|
8
8
|
# Jack herds your coding agents.
|
|
9
9
|
|
|
@@ -15,29 +15,29 @@ 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.25.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.25.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.25.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.25.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.25.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.25.0/assets/icons/bark.svg" width="24" height="34" align="middle" alt=""> <b>Jack barks.</b><br>A lane gets stuck, a check goes red, a decision needs you: Jack steps in and brings you only what's yours to decide. 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.25.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.25.0/assets/icons/ear.svg" width="24" height="34" align="middle" alt=""> <b>Jack listens.</b><br>Review comments on a lane's pull request go back to that lane, which fixes them with a test. <code>/jh-follow</code> brings in anything a CLI or an API can read as JSON: tickets, error logs, status pages.</p>
|
|
35
35
|
|
|
36
|
-
<p><img src="https://unpkg.com/jackherd@1.
|
|
36
|
+
<p><img src="https://unpkg.com/jackherd@1.25.0/assets/icons/model.svg" width="24" height="34" align="middle" alt=""> <b>The right model for each phase.</b><br>Every phase names its model: the stronger one where a mistake is expensive (data, security, a migration), the cheaper one for the rest. Lanes start on it, and a hook refuses to start a phase's subagent on a weaker one.</p>
|
|
37
37
|
|
|
38
|
-
<p><img src="https://unpkg.com/jackherd@1.
|
|
38
|
+
<p><img src="https://unpkg.com/jackherd@1.25.0/assets/icons/bowl.svg" width="24" height="34" align="middle" alt=""> <b>A crash or a full context doesn't cost you the work.</b><br>Plan, board and a hand-over file hold the state, not the chat: the next session picks up where the last one stopped.</p>
|
|
39
39
|
|
|
40
|
-
<p><img src="https://unpkg.com/jackherd@1.
|
|
40
|
+
<p><img src="https://unpkg.com/jackherd@1.25.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.25.0/assets/icons/space.svg"></p>
|
|
41
41
|
|
|
42
42
|
## Quickstart
|
|
43
43
|
|
|
@@ -68,22 +68,22 @@ Then `/jh-start` in a repository, and Jack offers your first plan. → [Quicksta
|
|
|
68
68
|
/jh-orchestrate docs/plans/board-<name>/board.md
|
|
69
69
|
```
|
|
70
70
|
|
|
71
|
-
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.<br><img width="1" height="8" alt="" src="https://unpkg.com/jackherd@1.
|
|
71
|
+
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.<br><img width="1" height="8" alt="" src="https://unpkg.com/jackherd@1.25.0/assets/icons/space.svg">
|
|
72
72
|
|
|
73
73
|
## Follow anything you can query
|
|
74
74
|
|
|
75
75
|
Tickets, error logs, status pages: if a command or an API call can read it as JSON with your login, Jack can watch it. Recipes cover Jira, Linear, GitHub issues and Projects, Sentry, Google Calendar and status pages; for anything else, `/jh-follow` writes the command with you. One yes, and every change shows up like this:
|
|
76
76
|
|
|
77
|
-
<img src="https://unpkg.com/jackherd@1.
|
|
77
|
+
<img src="https://unpkg.com/jackherd@1.25.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.">
|
|
78
78
|
|
|
79
|
-
The herd sums up your sources in one `Sources` line: new entries per source, or its last run, red while it fails; opened, a click opens an entry. One that needs you lands in Your turn under the source's label, [ask] has Jack look at it, and a ticket shows on the lane that works it.<br><img width="1" height="8" alt="" src="https://unpkg.com/jackherd@1.
|
|
79
|
+
The herd sums up your sources in one `Sources` line: new entries per source, or its last run, red while it fails; opened, a click opens an entry. One that needs you lands in Your turn under the source's label, [ask] has Jack look at it, and a ticket shows on the lane that works it.<br><img width="1" height="8" alt="" src="https://unpkg.com/jackherd@1.25.0/assets/icons/space.svg">
|
|
80
80
|
|
|
81
81
|
## In detail
|
|
82
82
|
|
|
83
83
|
<details>
|
|
84
84
|
<summary><b>What Jack does on his own, and what waits for you</b></summary>
|
|
85
85
|
|
|
86
|
-
<img src="https://unpkg.com/jackherd@1.
|
|
86
|
+
<img src="https://unpkg.com/jackherd@1.25.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.">
|
|
87
87
|
|
|
88
88
|
| On his own | After your approval | Never |
|
|
89
89
|
| --- | --- | --- |
|
|
@@ -116,9 +116,9 @@ The guards catch mistakes, not intent; for that, use branch protection. The hook
|
|
|
116
116
|
<details>
|
|
117
117
|
<summary><b>Plans, boards and the skills, yours included</b></summary>
|
|
118
118
|
|
|
119
|
-
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.
|
|
119
|
+
A **plan** is Markdown with phases, checkbox tasks, a model and a review gate per phase, and under each phase heading one sentence on what is different afterwards and for whom. 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.
|
|
120
120
|
|
|
121
|
-
<img src="https://unpkg.com/jackherd@1.
|
|
121
|
+
<img src="https://unpkg.com/jackherd@1.25.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.">
|
|
122
122
|
|
|
123
123
|
| Skill | Does |
|
|
124
124
|
| --- | --- |
|
|
@@ -185,7 +185,7 @@ How the herd reads the board:
|
|
|
185
185
|
- **Sources** shows, per source that is no ticket, its entries and how many are new, or when it last ran: amber with a new entry that is not in Your turn yet, red while it fails. Opened, its entries stand newest first by the time they show, and a click on an entry opens it; `↑ in your turn` marks one a row of Your turn asks about.
|
|
186
186
|
- **Lanes** opens at the start. A lane that works or is blocked opens by itself only when it first appears; after that, opening and folding are yours. A lane symbol says the state (`●` working, `◆` blocked, `◉` the agent finished its answer, `○` idle, `(·) no agent` a worktree nobody works in); only blocked wears its word. A folded lane line opens with its status and the age of its last activity: `needs you 3 min` and the first open question of its report (`+2 questions` counts the others), `blocked 3 min` and what it waits on (a row of Your turn, or the lane's starts-after cell), or `running 3 min` and its step. The plan's short title and the phase as `P2/5` with its name follow, and the plan is the first part to give way in a narrow pane. A resting lane without a known reason keeps `idle · no reason`. `:3100 ↗` on the branch line of an open lane opens its dev server, while one listens on the port its board row names.
|
|
187
187
|
- **Waiting** lists the lanes not started yet: no worktree yet, something left to do, and what each starts after; it starts folded. Finished lanes (cleaned up, or every phase they name ticked) are not shown.
|
|
188
|
-
- **An open lane** shows, top down: its branch, the model and how long it idles; its link chips on one line, `+n` for the rest; its pull requests; the phases it works on, each with its bar and its tasks as a checklist in plan order (ticked above, the running one marked, the ones to come below; above seven tasks a window around the running one, with `… n more` above and below to open the whole list); `▸ next` with the phases that wait, as ranges; `▸ done` with the finished ones. A lane shows only the phases its board rows name; a lane without a worktree reads its plan from the main checkout. A folded lane shows a reported task below its line while it has the mouse; behind each pull request badge it shows the checks, also while the lane rests: `✓ checks`, `checks running`, `✗ test +1` with the first red check, `draft` for a draft.
|
|
188
|
+
- **An open lane** shows, top down: its branch, the model and how long it idles; its link chips on one line, `+n` for the rest; its pull requests; the phases it works on, each with its bar, its purpose sentence dimmed under the head, and its tasks as a checklist in plan order (ticked above, the running one marked, the ones to come below; above seven tasks a window around the running one, with `… n more` above and below to open the whole list); `▸ next` with the phases that wait, as ranges; `▸ done` with the finished ones. A lane shows only the phases its board rows name; a lane without a worktree reads its plan from the main checkout. A folded lane shows a reported task below its line while it has the mouse; behind each pull request badge it shows the checks, also while the lane rests: `✓ checks`, `checks running`, `✗ test +1` with the first red check, `draft` for a draft.
|
|
189
189
|
- **A phase runs** when an agent reports working on it, one of its boxes was ticked in the last 20 minutes, or some of its tasks are ticked. A phase whose run was merged and that the lane's newest board row no longer names waits behind the others in `▸ next`, its open boxes left open on purpose. `n min ago` counts since the last tick or report; `· gate` means only the review gate is open, `docs open` only the documentation box.
|
|
190
190
|
- **A running task** carries `●` when an agent reported it, from its task list or `jh task`; its text stands in teal and a light band runs over it (`plans.shimmer: false`, `NO_COLOR` and `--once` keep it still). `◌` is a guess: in dim, a task guessed from the words of the lane's log; shimmering like a reported one, the first open task of a phase the lane runs when no agent reported one. `now: …` is a reported task no plan task names, `Subagent: …` a running subagent on no phase; a running review sits on the gate box that names it, in the phase it names or else the lane's current one. With several agents in a lane, each task carries its agent's name, dim, at the right edge.
|
|
191
191
|
- **What a working lane does right now:** under its lane line, open or folded, a dim `↳` line shows the last action from the agent's session log, a tool with its target (`↳ Read plans/layout.ts`) or the start of its answer, and each running subagent adds one, indented and led by its task (where `Subagent: …` says what it works on, `↳` says what it does); the lines go when the lane rests.
|
|
@@ -223,7 +223,7 @@ jackherd sets the model, not the effort and not the advisor: those come from Cla
|
|
|
223
223
|
| `jh init` | creates `.jackherd/config.json` and installs the kit (`--global` for `~/.claude`) |
|
|
224
224
|
| `jh sync` | updates the kit; a file you edited gets the new version beside it as `.kit-new` |
|
|
225
225
|
| `jh update` | installs the newest release and syncs |
|
|
226
|
-
| `jh check` | types, lint and tests of what you touched, plus the plan and board checks (`--skip-slow`, `--parallel <plan>`) |
|
|
226
|
+
| `jh check` | types, lint and tests of what you touched, plus the plan and board checks, among them the purpose line every new plan phase with tasks needs (`--skip-slow`, `--parallel <plan>`); `jh plan check <plan>` reports the plan contract, the purpose line as a note; boards and plans are read in English and German whatever `planLanguage` says, a third language by `<!-- jh: <key> -->` markers, and `boardLanguage` (default `planLanguage`) is the language jh writes the board in (`docs/plan-contract.md`) |
|
|
227
227
|
| `jh herd` | the live view (`--once`, `--lane <name>`) |
|
|
228
228
|
| `jh watch` | the watcher Jack runs |
|
|
229
229
|
| `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. With `--coordinator` it lists what a coordinator takes up after `/clear` (lanes, PRs, approvals, what waits on you, appointments, standing approvals), from files and herdr; `--clear-line` prints the first input of that chat. |
|
|
@@ -1,5 +1,9 @@
|
|
|
1
1
|
// src/vocabulary/index.ts
|
|
2
2
|
var PLAN_LANGUAGE = { de: "de", en: "en" };
|
|
3
|
+
var languagesFrom = (language) => [
|
|
4
|
+
language,
|
|
5
|
+
...Object.values(PLAN_LANGUAGE).filter((other) => other !== language)
|
|
6
|
+
];
|
|
3
7
|
var LANGUAGE_NAME = {
|
|
4
8
|
[PLAN_LANGUAGE.en]: "English",
|
|
5
9
|
[PLAN_LANGUAGE.de]: "German"
|
|
@@ -87,6 +91,73 @@ var WAITING_VOCABULARY = {
|
|
|
87
91
|
open: ["open"]
|
|
88
92
|
}
|
|
89
93
|
};
|
|
94
|
+
var SECTION_KEY = {
|
|
95
|
+
waiting: "waiting",
|
|
96
|
+
appointments: "appointments",
|
|
97
|
+
lanes: "lanes",
|
|
98
|
+
goal: "goal",
|
|
99
|
+
history: "history",
|
|
100
|
+
plans: "plans",
|
|
101
|
+
decisions: "decisions",
|
|
102
|
+
implementation: "implementation",
|
|
103
|
+
model: "model",
|
|
104
|
+
dependencies: "dependencies",
|
|
105
|
+
documentation: "documentation",
|
|
106
|
+
forYou: "for-you",
|
|
107
|
+
next: "next"
|
|
108
|
+
};
|
|
109
|
+
var isSectionKey = (value) => Object.values(SECTION_KEY).includes(value);
|
|
110
|
+
var HEAD_KEY = {
|
|
111
|
+
changes: "head-changes",
|
|
112
|
+
status: "head-status",
|
|
113
|
+
decisions: "head-decisions",
|
|
114
|
+
tryNow: "head-try-now",
|
|
115
|
+
next: "head-next"
|
|
116
|
+
};
|
|
117
|
+
var LINE_KEY = { noReviewGate: "no-review-gate", purpose: "purpose" };
|
|
118
|
+
var COLUMN_KEY = {
|
|
119
|
+
since: "since",
|
|
120
|
+
item: "item",
|
|
121
|
+
origin: "origin",
|
|
122
|
+
unblocks: "unblocks",
|
|
123
|
+
status: "status",
|
|
124
|
+
when: "when",
|
|
125
|
+
action: "action",
|
|
126
|
+
dependsOn: "depends-on",
|
|
127
|
+
writes: "writes",
|
|
128
|
+
serialMarker: "serial-marker"
|
|
129
|
+
};
|
|
130
|
+
var columnWords = (key, language) => {
|
|
131
|
+
const waiting = WAITING_VOCABULARY[language];
|
|
132
|
+
const appointments = APPOINTMENT_VOCABULARY[language];
|
|
133
|
+
const { columns } = PLAN_VOCABULARY[language];
|
|
134
|
+
const words = {
|
|
135
|
+
[COLUMN_KEY.since]: [waiting.columns.since],
|
|
136
|
+
[COLUMN_KEY.item]: [waiting.columns.item],
|
|
137
|
+
[COLUMN_KEY.origin]: [waiting.columns.origin],
|
|
138
|
+
[COLUMN_KEY.unblocks]: [waiting.columns.unblocks],
|
|
139
|
+
[COLUMN_KEY.status]: [
|
|
140
|
+
waiting.columns.status,
|
|
141
|
+
...waiting.statusAlso,
|
|
142
|
+
appointments.columns.status,
|
|
143
|
+
...appointments.statusAlso
|
|
144
|
+
],
|
|
145
|
+
[COLUMN_KEY.when]: [appointments.columns.when],
|
|
146
|
+
[COLUMN_KEY.action]: [appointments.columns.action],
|
|
147
|
+
[COLUMN_KEY.dependsOn]: [columns.dependsOn],
|
|
148
|
+
[COLUMN_KEY.writes]: [columns.writes],
|
|
149
|
+
[COLUMN_KEY.serialMarker]: [columns.serialMarker]
|
|
150
|
+
};
|
|
151
|
+
return words[key];
|
|
152
|
+
};
|
|
153
|
+
var STATUS_KEY = {
|
|
154
|
+
open: "status-open",
|
|
155
|
+
done: "status-done",
|
|
156
|
+
abandoned: "status-abandoned",
|
|
157
|
+
inProgress: "status-in-progress",
|
|
158
|
+
draft: "status-draft"
|
|
159
|
+
};
|
|
160
|
+
var isStatusKey = (value) => Object.values(STATUS_KEY).includes(value);
|
|
90
161
|
var isPlanLanguage = (value) => Object.values(PLAN_LANGUAGE).includes(value);
|
|
91
162
|
var PLAN_STATUS = {
|
|
92
163
|
done: "done",
|
|
@@ -138,6 +209,7 @@ var PLAN_VOCABULARY = {
|
|
|
138
209
|
columns: { dependsOn: "H\xE4ngt ab von", writes: "Schreibt", serialMarker: "Seriell-Marker" },
|
|
139
210
|
phase: "Phase",
|
|
140
211
|
noReviewGate: "Kein Review-Gate",
|
|
212
|
+
phasePurpose: "Worum es geht",
|
|
141
213
|
laneClosed: "abger\xE4umt",
|
|
142
214
|
partialPhase: "(Teil)",
|
|
143
215
|
openSubagents: "offene Sub-Agenten",
|
|
@@ -181,6 +253,7 @@ var PLAN_VOCABULARY = {
|
|
|
181
253
|
columns: { dependsOn: "Depends on", writes: "Writes", serialMarker: "Serial marker" },
|
|
182
254
|
phase: "Phase",
|
|
183
255
|
noReviewGate: "No review gate",
|
|
256
|
+
phasePurpose: "What it is about",
|
|
184
257
|
laneClosed: "cleaned up",
|
|
185
258
|
partialPhase: "(part)",
|
|
186
259
|
openSubagents: "open subagents",
|
|
@@ -202,15 +275,63 @@ var PLAN_VOCABULARY = {
|
|
|
202
275
|
}
|
|
203
276
|
};
|
|
204
277
|
var STATUS_PRECEDENCE = [PLAN_STATUS.done, PLAN_STATUS.abandoned, PLAN_STATUS.inProgress, PLAN_STATUS.draft];
|
|
205
|
-
var
|
|
278
|
+
var STATUS_OF_KEY = {
|
|
279
|
+
[STATUS_KEY.done]: PLAN_STATUS.done,
|
|
280
|
+
[STATUS_KEY.abandoned]: PLAN_STATUS.abandoned,
|
|
281
|
+
[STATUS_KEY.inProgress]: PLAN_STATUS.inProgress,
|
|
282
|
+
[STATUS_KEY.draft]: PLAN_STATUS.draft
|
|
283
|
+
};
|
|
284
|
+
var MARKER_IN_TEXT = /<!--\s*jh:\s*([a-z-]+)\s*-->/;
|
|
285
|
+
var statusOfMarker = (statusText) => {
|
|
286
|
+
const key = MARKER_IN_TEXT.exec(statusText)?.[1];
|
|
287
|
+
return key !== void 0 && isStatusKey(key) ? STATUS_OF_KEY[key] : void 0;
|
|
288
|
+
};
|
|
289
|
+
var statusOfWords = (language, statusText) => {
|
|
206
290
|
const { statusWords } = PLAN_VOCABULARY[language];
|
|
207
291
|
const lowerStatus = statusText.toLowerCase();
|
|
208
292
|
const matches = ({ leading, anywhere }) => leading.some((word) => lowerStatus.startsWith(word)) || anywhere.some((fragment) => lowerStatus.includes(fragment));
|
|
209
|
-
|
|
293
|
+
return STATUS_PRECEDENCE.find((status) => matches(statusWords[status]));
|
|
294
|
+
};
|
|
295
|
+
var classifyStatus = (language, statusText) => {
|
|
296
|
+
const marked = statusOfMarker(statusText);
|
|
297
|
+
if (marked) return marked;
|
|
298
|
+
const text = statusText.replace(MARKER_IN_TEXT, "");
|
|
299
|
+
const matched = languagesFrom(language).map((candidate) => statusOfWords(candidate, text)).find((status) => status !== void 0);
|
|
210
300
|
if (matched) return matched;
|
|
211
|
-
return
|
|
301
|
+
return text.trim() ? PLAN_STATUS.other : PLAN_STATUS.none;
|
|
212
302
|
};
|
|
213
303
|
var phaseHeading = (language, id, title) => `### ${PLAN_VOCABULARY[language].phase} ${id}: ${title}`;
|
|
304
|
+
var headingOfSection = (key, language) => {
|
|
305
|
+
const { sections } = PLAN_VOCABULARY[language];
|
|
306
|
+
const headings = {
|
|
307
|
+
[SECTION_KEY.waiting]: WAITING_VOCABULARY[language].heading,
|
|
308
|
+
[SECTION_KEY.appointments]: APPOINTMENT_VOCABULARY[language].heading,
|
|
309
|
+
[SECTION_KEY.lanes]: sections.lanes,
|
|
310
|
+
[SECTION_KEY.goal]: sections.goal,
|
|
311
|
+
[SECTION_KEY.history]: sections.history,
|
|
312
|
+
[SECTION_KEY.plans]: sections.plans,
|
|
313
|
+
[SECTION_KEY.decisions]: sections.decisions,
|
|
314
|
+
[SECTION_KEY.implementation]: sections.implementation,
|
|
315
|
+
[SECTION_KEY.model]: sections.modelRecommendations,
|
|
316
|
+
[SECTION_KEY.dependencies]: sections.dependencies,
|
|
317
|
+
[SECTION_KEY.documentation]: sections.documentation,
|
|
318
|
+
[SECTION_KEY.forYou]: sections.forYou,
|
|
319
|
+
[SECTION_KEY.next]: NEXT_VOCABULARY[language].heading
|
|
320
|
+
};
|
|
321
|
+
return headings[key];
|
|
322
|
+
};
|
|
323
|
+
var sectionOfHeading = (headingText) => {
|
|
324
|
+
for (const language of Object.values(PLAN_LANGUAGE)) {
|
|
325
|
+
const key = Object.values(SECTION_KEY).find((candidate) => headingOfSection(candidate, language) === headingText);
|
|
326
|
+
if (key !== void 0) return { key, language };
|
|
327
|
+
}
|
|
328
|
+
return null;
|
|
329
|
+
};
|
|
330
|
+
var CELL_KEY = { laneClosed: "lane-closed", partialPhase: "partial-phase" };
|
|
331
|
+
var LANE_CLOSED_WORDS = Object.values(PLAN_VOCABULARY).map(({ laneClosed }) => laneClosed);
|
|
332
|
+
var PARTIAL_PHASE_WORDS = Object.values(PLAN_VOCABULARY).map(
|
|
333
|
+
({ partialPhase }) => partialPhase
|
|
334
|
+
);
|
|
214
335
|
var sectionHeading = (language, section) => `## ${PLAN_VOCABULARY[language].sections[section]}`;
|
|
215
336
|
|
|
216
337
|
// src/plan-format/terminal-text.ts
|
|
@@ -232,6 +353,7 @@ var terminalSafe = (text) => text.replace(UNSAFE_TERMINAL_TEXT, "");
|
|
|
232
353
|
|
|
233
354
|
export {
|
|
234
355
|
PLAN_LANGUAGE,
|
|
356
|
+
languagesFrom,
|
|
235
357
|
LANGUAGE_NAME,
|
|
236
358
|
RESPONSE_ADDRESS,
|
|
237
359
|
ADDRESS_FORM,
|
|
@@ -244,13 +366,27 @@ export {
|
|
|
244
366
|
APPOINTMENT_VOCABULARY,
|
|
245
367
|
NEXT_VOCABULARY,
|
|
246
368
|
WAITING_VOCABULARY,
|
|
369
|
+
SECTION_KEY,
|
|
370
|
+
isSectionKey,
|
|
371
|
+
HEAD_KEY,
|
|
372
|
+
LINE_KEY,
|
|
373
|
+
COLUMN_KEY,
|
|
374
|
+
columnWords,
|
|
375
|
+
STATUS_KEY,
|
|
376
|
+
isStatusKey,
|
|
247
377
|
isPlanLanguage,
|
|
248
378
|
PLAN_STATUS,
|
|
249
379
|
REPORT_FIELDS,
|
|
250
380
|
HEAD_LINE,
|
|
251
381
|
PLAN_VOCABULARY,
|
|
382
|
+
MARKER_IN_TEXT,
|
|
252
383
|
classifyStatus,
|
|
253
384
|
phaseHeading,
|
|
385
|
+
headingOfSection,
|
|
386
|
+
sectionOfHeading,
|
|
387
|
+
CELL_KEY,
|
|
388
|
+
LANE_CLOSED_WORDS,
|
|
389
|
+
PARTIAL_PHASE_WORDS,
|
|
254
390
|
sectionHeading,
|
|
255
391
|
terminalSafe
|
|
256
392
|
};
|
package/dist/config.d.ts
CHANGED
|
@@ -95,6 +95,10 @@ declare const jackherdConfigSchema: z.ZodObject<{
|
|
|
95
95
|
de: "de";
|
|
96
96
|
en: "en";
|
|
97
97
|
}>>;
|
|
98
|
+
boardLanguage: z.ZodOptional<z.ZodEnum<{
|
|
99
|
+
de: "de";
|
|
100
|
+
en: "en";
|
|
101
|
+
}>>;
|
|
98
102
|
planDirectory: z.ZodDefault<z.ZodString>;
|
|
99
103
|
board: z.ZodPrefault<z.ZodObject<{
|
|
100
104
|
pattern: z.ZodDefault<z.ZodString>;
|
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-O365KHHP.js";
|
|
5
5
|
|
|
6
6
|
// src/config/json-schema.ts
|
|
7
7
|
import { z as z4 } from "zod";
|
|
@@ -720,7 +720,10 @@ var jackherdConfigSchema = z3.strictObject({
|
|
|
720
720
|
responseLanguage: language.default(PLAN_LANGUAGE.en),
|
|
721
721
|
/** Whether replies address the user with the familiar or the polite form where the language has both (German: du or Sie). */
|
|
722
722
|
responseAddress: z3.enum(Object.values(RESPONSE_ADDRESS)).default(RESPONSE_ADDRESS.informal),
|
|
723
|
+
/** The language new plans and the plan templates are written in. Boards and plans are read in every known language whatever this says. */
|
|
723
724
|
planLanguage: language.default(PLAN_LANGUAGE.en),
|
|
725
|
+
/** The language jh writes the board in (history lines, lane rows, waiting rows); `planLanguage` when left out. */
|
|
726
|
+
boardLanguage: language.optional(),
|
|
724
727
|
planDirectory: z3.string().min(1).default("docs/plans"),
|
|
725
728
|
/** Where the boards live and what the project calls them. */
|
|
726
729
|
board: boardSchema.prefault({}),
|
package/dist/plan-format.d.ts
CHANGED
|
@@ -3,6 +3,8 @@ declare const PLAN_LANGUAGE: {
|
|
|
3
3
|
readonly en: "en";
|
|
4
4
|
};
|
|
5
5
|
type PlanLanguage = (typeof PLAN_LANGUAGE)[keyof typeof PLAN_LANGUAGE];
|
|
6
|
+
/** The languages to try when reading: `language` first, then every other known one. */
|
|
7
|
+
declare const languagesFrom: (language: PlanLanguage) => PlanLanguage[];
|
|
6
8
|
/** The language's name as a reply or a question says it. */
|
|
7
9
|
declare const LANGUAGE_NAME: Record<PlanLanguage, string>;
|
|
8
10
|
declare const RESPONSE_ADDRESS: {
|
|
@@ -106,6 +108,73 @@ declare const WAITING_VOCABULARY: Record<PlanLanguage, {
|
|
|
106
108
|
statusAlso: readonly string[];
|
|
107
109
|
open: readonly string[];
|
|
108
110
|
}>;
|
|
111
|
+
/**
|
|
112
|
+
* The closed set of keys a section carries in a marker, `<!-- jh: <key> -->` on the line directly under its heading.
|
|
113
|
+
* The marker names the section whatever language the heading is written in.
|
|
114
|
+
*/
|
|
115
|
+
declare const SECTION_KEY: {
|
|
116
|
+
readonly waiting: "waiting";
|
|
117
|
+
readonly appointments: "appointments";
|
|
118
|
+
readonly lanes: "lanes";
|
|
119
|
+
readonly goal: "goal";
|
|
120
|
+
readonly history: "history";
|
|
121
|
+
readonly plans: "plans";
|
|
122
|
+
readonly decisions: "decisions";
|
|
123
|
+
readonly implementation: "implementation";
|
|
124
|
+
readonly model: "model";
|
|
125
|
+
readonly dependencies: "dependencies";
|
|
126
|
+
readonly documentation: "documentation";
|
|
127
|
+
readonly forYou: "for-you";
|
|
128
|
+
readonly next: "next";
|
|
129
|
+
};
|
|
130
|
+
type SectionKey = (typeof SECTION_KEY)[keyof typeof SECTION_KEY];
|
|
131
|
+
declare const isSectionKey: (value: string) => value is SectionKey;
|
|
132
|
+
/**
|
|
133
|
+
* The marker key of each line of the head section (`- **Stand:** <!-- jh: head-status --> …`). The marker names the
|
|
134
|
+
* line whatever language its label is written in.
|
|
135
|
+
*/
|
|
136
|
+
declare const HEAD_KEY: Record<HeadLine, string>;
|
|
137
|
+
/**
|
|
138
|
+
* The closed set of keys a line that opens with bold words carries in a marker
|
|
139
|
+
* (`**Kein Review-Gate** <!-- jh: no-review-gate --> reason`). The marker names the line whatever language it is in.
|
|
140
|
+
*/
|
|
141
|
+
declare const LINE_KEY: {
|
|
142
|
+
readonly noReviewGate: "no-review-gate";
|
|
143
|
+
readonly purpose: "purpose";
|
|
144
|
+
};
|
|
145
|
+
/**
|
|
146
|
+
* The closed set of keys a table column carries in a marker in its header cell (`Hängt ab von <!-- jh: depends-on -->`).
|
|
147
|
+
* The marker names the column whatever language the header is written in.
|
|
148
|
+
*/
|
|
149
|
+
declare const COLUMN_KEY: {
|
|
150
|
+
readonly since: "since";
|
|
151
|
+
readonly item: "item";
|
|
152
|
+
readonly origin: "origin";
|
|
153
|
+
readonly unblocks: "unblocks";
|
|
154
|
+
readonly status: "status";
|
|
155
|
+
readonly when: "when";
|
|
156
|
+
readonly action: "action";
|
|
157
|
+
readonly dependsOn: "depends-on";
|
|
158
|
+
readonly writes: "writes";
|
|
159
|
+
readonly serialMarker: "serial-marker";
|
|
160
|
+
};
|
|
161
|
+
type ColumnKey = (typeof COLUMN_KEY)[keyof typeof COLUMN_KEY];
|
|
162
|
+
/** The header names the column `key` goes by in `language`: its name first, then the names that count as the same column. */
|
|
163
|
+
declare const columnWords: (key: ColumnKey, language: PlanLanguage) => readonly string[];
|
|
164
|
+
/**
|
|
165
|
+
* The closed set of keys a status carries in a marker in its cell (`erledigt <!-- jh: status-done -->`): `open` for a
|
|
166
|
+
* board row that still waits, the others for the status of a plan. The marker names the status whatever language the
|
|
167
|
+
* cell is written in.
|
|
168
|
+
*/
|
|
169
|
+
declare const STATUS_KEY: {
|
|
170
|
+
readonly open: "status-open";
|
|
171
|
+
readonly done: "status-done";
|
|
172
|
+
readonly abandoned: "status-abandoned";
|
|
173
|
+
readonly inProgress: "status-in-progress";
|
|
174
|
+
readonly draft: "status-draft";
|
|
175
|
+
};
|
|
176
|
+
type StatusKey = (typeof STATUS_KEY)[keyof typeof STATUS_KEY];
|
|
177
|
+
declare const isStatusKey: (value: string) => value is StatusKey;
|
|
109
178
|
declare const isPlanLanguage: (value: string) => value is PlanLanguage;
|
|
110
179
|
declare const PLAN_STATUS: {
|
|
111
180
|
readonly done: "done";
|
|
@@ -173,6 +242,8 @@ type PlanVocabulary = {
|
|
|
173
242
|
phase: string;
|
|
174
243
|
/** Opens the line by which a phase says it has no review gate on purpose: `**Kein Review-Gate:** <reason>`. */
|
|
175
244
|
noReviewGate: string;
|
|
245
|
+
/** Opens the line directly under a phase heading that says in one sentence what is different afterwards, for whom: `**Worum es geht:** <sentence>`. */
|
|
246
|
+
phasePurpose: string;
|
|
176
247
|
/** Marks a lane as closed when its name cell contains it. */
|
|
177
248
|
laneClosed: string;
|
|
178
249
|
/** Stands in a board row's phases cell when that pull request builds only part of the phase. */
|
|
@@ -196,10 +267,35 @@ type PlanVocabulary = {
|
|
|
196
267
|
statusWords: Record<PlanStatus, StatusWord>;
|
|
197
268
|
};
|
|
198
269
|
declare const PLAN_VOCABULARY: Record<PlanLanguage, PlanVocabulary>;
|
|
199
|
-
/**
|
|
270
|
+
/** A `<!-- jh: <key> -->` marker anywhere in a text; the key is group 1. */
|
|
271
|
+
declare const MARKER_IN_TEXT: RegExp;
|
|
272
|
+
/**
|
|
273
|
+
* Classifies a free-text `Status:` line. A status marker (`<!-- jh: status-done -->`) decides, any language; else the
|
|
274
|
+
* first matching status in precedence order of the words of `language`, then of any other known language.
|
|
275
|
+
*/
|
|
200
276
|
declare const classifyStatus: (language: PlanLanguage, statusText: string) => PlanStatus;
|
|
201
277
|
/** `### Phase 3: Title` as the plan spells it in `language`. */
|
|
202
278
|
declare const phaseHeading: (language: PlanLanguage, id: string, title: string) => string;
|
|
279
|
+
/** The heading text of the section `key` names, as `language` writes it. */
|
|
280
|
+
declare const headingOfSection: (key: SectionKey, language: PlanLanguage) => string;
|
|
281
|
+
/** The key and language of the section a heading text names, read the way a caller spells it; `null` when no language does. */
|
|
282
|
+
declare const sectionOfHeading: (headingText: string) => {
|
|
283
|
+
key: SectionKey;
|
|
284
|
+
language: PlanLanguage;
|
|
285
|
+
} | null;
|
|
286
|
+
/**
|
|
287
|
+
* The closed set of keys a lanes-table cell carries in a marker: `lane-closed` in the name cell of a lane that was
|
|
288
|
+
* cleaned up, `partial-phase` in the phases cell of a row that builds only part of a phase.
|
|
289
|
+
*/
|
|
290
|
+
declare const CELL_KEY: {
|
|
291
|
+
readonly laneClosed: "lane-closed";
|
|
292
|
+
readonly partialPhase: "partial-phase";
|
|
293
|
+
};
|
|
294
|
+
type CellKey = (typeof CELL_KEY)[keyof typeof CELL_KEY];
|
|
295
|
+
/** The words of every known language that mark a lane as cleaned up. */
|
|
296
|
+
declare const LANE_CLOSED_WORDS: readonly string[];
|
|
297
|
+
/** The words of every known language that mark a row as building only part of a phase. */
|
|
298
|
+
declare const PARTIAL_PHASE_WORDS: readonly string[];
|
|
203
299
|
declare const sectionHeading: (language: PlanLanguage, section: keyof PlanVocabulary["sections"]) => string;
|
|
204
300
|
|
|
205
301
|
/**
|
|
@@ -260,11 +356,13 @@ type PhaseWorkFields = {
|
|
|
260
356
|
/**
|
|
261
357
|
* `work` is set by the phase source and says nothing for a phase the worktree left as the base has it; a merged phase
|
|
262
358
|
* is touched as well. `deferred` holds the checkboxes moved to another phase; they are not among `items`, so they
|
|
263
|
-
* count neither as open nor as done.
|
|
359
|
+
* count neither as open nor as done. `purpose` is the sentence of the purpose line directly under a `### Phase`
|
|
360
|
+
* heading (`phasePurposeOf`); absent when the phase has none.
|
|
264
361
|
*/
|
|
265
362
|
type PlanPhase = {
|
|
266
363
|
id: string;
|
|
267
364
|
title: string;
|
|
365
|
+
purpose?: string;
|
|
268
366
|
items: ChecklistItem[];
|
|
269
367
|
deferred?: DeferredItem[];
|
|
270
368
|
} & Partial<PhaseWorkFields>;
|
|
@@ -283,6 +381,15 @@ declare const endsPhase: (line: string) => boolean;
|
|
|
283
381
|
declare const isCheckboxLine: (line: string) => boolean;
|
|
284
382
|
declare const isOpenCheckboxLine: (line: string) => boolean;
|
|
285
383
|
declare const phaseHeadingPattern: (language: PlanLanguage) => RegExp;
|
|
384
|
+
/** The bold opening of a line (`**Label:**` or `**Label**:`): the label and the text after it. */
|
|
385
|
+
declare const BOLD_OPENING: RegExp;
|
|
386
|
+
/**
|
|
387
|
+
* The sentence of a purpose line, `**Worum es geht:** <sentence>`, the colon inside or outside the bold; null for any
|
|
388
|
+
* other line and for a purpose line without text after the colon.
|
|
389
|
+
*/
|
|
390
|
+
declare const phasePurposeOf: (line: string, language: PlanLanguage) => string | null;
|
|
391
|
+
/** The first non-empty line under a heading decides whether a phase has its purpose line; once `decided`, later lines never do. */
|
|
392
|
+
declare const decidesPurpose: (decided: boolean, line: string) => boolean;
|
|
286
393
|
/**
|
|
287
394
|
* The phase an open checkbox line is moved to with `→ P<n>` at its end; null for any other line, a ticked box,
|
|
288
395
|
* a ✅/⬜ table row, and an arrow to the box's own phase `phaseId`. The one rule the parser and the tally share.
|
|
@@ -556,6 +663,8 @@ type PhaseWithOpenBoxes = PhaseWithoutGate & {
|
|
|
556
663
|
};
|
|
557
664
|
/** A `PhaseWithOpenBoxes` whose tasks are all ticked on the base branch. */
|
|
558
665
|
type MergedPhaseWithOpenBoxes = PhaseWithOpenBoxes;
|
|
666
|
+
/** A `### Phase` heading (1-based line) whose section has tasks and no purpose line directly under the heading. */
|
|
667
|
+
type PhaseWithoutPurpose = PhaseHeading;
|
|
559
668
|
/** The ids of the `### Phase` headings of a plan. */
|
|
560
669
|
declare const phaseIdsOfPlan: (markdown: string, language: PlanLanguage) => Set<string>;
|
|
561
670
|
/**
|
|
@@ -565,6 +674,13 @@ declare const phaseIdsOfPlan: (markdown: string, language: PlanLanguage) => Set<
|
|
|
565
674
|
* plan does not turn red when it is touched.
|
|
566
675
|
*/
|
|
567
676
|
declare const newPhasesWithoutReviewGate: (markdown: string, language: PlanLanguage, previousIds: ReadonlySet<string>) => PhaseWithoutGate[];
|
|
677
|
+
/**
|
|
678
|
+
* The phases a plan gained against `previousIds` that have checkboxes and no purpose line as the first non-empty line
|
|
679
|
+
* under the heading (`phasePurposeOf`). Phases the plan already had are not read, like in `newPhasesWithoutReviewGate`.
|
|
680
|
+
*/
|
|
681
|
+
declare const newPhasesWithoutPurpose: (markdown: string, language: PlanLanguage, previousIds: ReadonlySet<string>) => PhaseWithoutPurpose[];
|
|
682
|
+
/** Every phase of the plan with tasks and without a purpose line: for a plan read whole, where no phase is older than the change. */
|
|
683
|
+
declare const phasesWithoutPurpose: (markdown: string, language: PlanLanguage) => PhaseWithoutPurpose[];
|
|
568
684
|
/** Whether the document has a lanes table (`## Spuren`): a board, not a plan. */
|
|
569
685
|
declare const hasLanesTable: (markdown: string, language: PlanLanguage) => boolean;
|
|
570
686
|
/**
|
|
@@ -593,6 +709,8 @@ declare const boardRowsWithoutPhase: (markdown: string, language: PlanLanguage,
|
|
|
593
709
|
type PhaseProgress = {
|
|
594
710
|
id: string;
|
|
595
711
|
title: string | null;
|
|
712
|
+
/** The phase's purpose sentence; absent when the plan has none or lacks the phase. */
|
|
713
|
+
purpose?: string;
|
|
596
714
|
done: number;
|
|
597
715
|
total: number;
|
|
598
716
|
items: ChecklistItem[];
|
|
@@ -709,4 +827,4 @@ declare const readSprintDocument: (markdown: string, language: PlanLanguage) =>
|
|
|
709
827
|
*/
|
|
710
828
|
declare const readPhaseDependencies: (planMarkdown: string, language: PlanLanguage) => Record<string, string>;
|
|
711
829
|
|
|
712
|
-
export { ADDRESS_FORM, APPOINTMENT_VOCABULARY, APPROVAL_TEXTS, CARD_FIELDS, type ChecklistItem, type ColumnModel, type DeferredItem, type DriftFinding, HEAD_LINE, HEAD_STATUS, type HeadLine, type HeadPhase, type HeadStatus, type HeadVerdict, 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 PlanHead, type PlanHeader, type PlanLanguage, type PlanPhase, type PlanStatus, type PlanStep, type PlanVocabulary, type PlansByHref, type ProseReason, RECOMMENDED_MARK, REPORT_FIELDS, 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, checkPlanHead, 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, parsePlanHead, 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 };
|
|
830
|
+
export { ADDRESS_FORM, APPOINTMENT_VOCABULARY, APPROVAL_TEXTS, BOLD_OPENING, CARD_FIELDS, CELL_KEY, COLUMN_KEY, type CellKey, type ChecklistItem, type ColumnKey, type ColumnModel, type DeferredItem, type DriftFinding, HEAD_KEY, HEAD_LINE, HEAD_STATUS, type HeadLine, type HeadPhase, type HeadStatus, type HeadVerdict, LANE_CLOSED_WORDS, LANE_PROMPT_OPENING, LANGUAGE_NAME, LINE_KEY, type LaneProgress, type LongPhaseTitle, MARKER_IN_TEXT, type MergedPhaseWithOpenBoxes, type MisplacedDeferral, type ModelRow, NEXT_VOCABULARY, NO_DEPENDENCY_TABLE, PARTIAL_PHASE_WORDS, 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 PhaseWithoutPurpose, type PhaseWithoutRow, type PhaseWork, type PhaseWorkFields, type PlanHead, type PlanHeader, type PlanLanguage, type PlanPhase, type PlanStatus, type PlanStep, type PlanVocabulary, type PlansByHref, type ProseReason, RECOMMENDED_MARK, REPORT_FIELDS, RESPONSE_ADDRESS, type ResponseAddress, type RowWithProse, type RowWithoutPhase, SECTION_KEY, SELF_MERGE_LABEL, SHORT_NAME_LIMIT, SHORT_NAME_PROBLEM, STANDING_APPROVAL_TEXTS, STATUS_KEY, type SectionKey, type ShortNameFinding, type ShortNameProblem, type SprintDocument, type SprintLane, type SprintPlanRow, type SprintPlanTable, type StatusKey, type StatusWord, type StepProgress, WAITING_VOCABULARY, activeItem, boardRowsWithProse, boardRowsWithoutPhase, checkPlanHead, classifyStatus, columnModels, columnWords, countDecisions, criticalPath, currentPhase, currentPhaseOf, decidesPurpose, deferralTarget, differsFromColumn, endsPhase, findDrift, hasLanesTable, headingOfSection, isCheckboxLine, isDocumentationGateItem, isOpenCheckboxLine, isPhaseFinished, isPhaseOpen, isPlanLanguage, isReviewGateLine, isSectionKey, isStatusKey, isTickedUnmerged, laneNameOf, laneProgress, languagesFrom, longPhaseTitles, mergedPhasesWithOpenBoxes, misplacedDeferrals, newPhasesWithoutPurpose, newPhasesWithoutReviewGate, onlyDocumentationOpen, openBoxesOfPhase, parallelGroups, parallelGroupsOfPlan, parseDependencyTable, parseModelTable, parsePlanHead, parsePlanPhases, parseSprintLanes, phaseCount, phaseHeading, phaseHeadingPattern, phaseHeadings, phaseIdOf, phaseIdsOfCell, phaseIdsOfPlan, phaseLabel, phaseLabels, phaseProgress, phasePurposeOf, phaseWaves, phaseWorkFields, phasesText, phasesWithoutDependencyRow, phasesWithoutPurpose, readPhaseDependencies, readPlanHeader, readSprintDocument, resolvePlanPath, sectionHeading, sectionOfHeading, shortNameFindings, shortTitle, sprintTitleOf, sumPhases, tallyPhases, wavesOfPlan, withoutRepeats };
|