jackherd 1.9.0 → 1.11.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 +49 -0
- package/INSTALL.md +1 -1
- package/README.md +65 -32
- package/dist/{chunk-MCP4XJQH.js → chunk-RMMXD4G5.js} +18 -6
- package/dist/config.d.ts +99 -4
- package/dist/config.js +105 -29
- package/dist/plan-format.d.ts +51 -6
- package/dist/plan-format.js +49 -7
- package/docs/workflow-dark.svg +67 -63
- package/docs/workflow.svg +67 -63
- package/herdr-plugin/herdr-plugin.toml +1 -1
- package/install.sh +1 -1
- package/kit/hooks/ask-rules.json +15 -0
- package/kit/hooks/hooks/ask-hint/decision.ts +82 -14
- package/kit/hooks/hooks/ask-hint/index.ts +15 -19
- package/kit/hooks/hooks/ask-hint/settings.ts +3 -18
- package/kit/hooks/hooks/git-guard/decision.ts +50 -102
- package/kit/hooks/hooks/merge-guard/always-ask.ts +63 -0
- package/kit/hooks/hooks/merge-guard/{plan-approval-settings.ts → board-settings.ts} +21 -67
- package/kit/hooks/hooks/merge-guard/check-free.ts +20 -0
- package/kit/hooks/hooks/merge-guard/decision.ts +127 -187
- package/kit/hooks/hooks/merge-guard/index.ts +6 -3
- package/kit/hooks/hooks/merge-guard/lane-plan.ts +79 -0
- package/kit/hooks/hooks/merge-guard/run.ts +59 -54
- package/kit/hooks/hooks/merge-guard/settings.ts +13 -1
- package/kit/hooks/hooks/merge-guard/standing-approval.ts +142 -0
- package/kit/hooks/hooks/open-question/index.ts +26 -0
- package/kit/hooks/hooks/open-question/record.ts +135 -0
- package/kit/hooks/hooks/open-question/run.ts +57 -0
- package/kit/hooks/hooks/open-question/store.ts +39 -0
- package/kit/hooks/hooks/sent-file/store.ts +3 -12
- package/kit/hooks/hooks/shared/config.ts +7 -0
- package/kit/hooks/hooks/shared/dialog-answers.ts +9 -9
- package/kit/hooks/hooks/shared/files.ts +7 -0
- package/kit/hooks/hooks/shared/lane.ts +1 -1
- package/kit/hooks/hooks/shared/newest-verdict.ts +10 -0
- package/kit/hooks/hooks/shared/private-directory.ts +56 -9
- package/kit/hooks/hooks/shared/program-position.ts +2 -75
- package/kit/hooks/hooks/shared/shell-aliases.ts +1 -71
- package/kit/hooks/hooks/{ask-hint → shared}/transcript.ts +37 -6
- package/kit/hooks/hooks/{merge-guard → shared}/xargs-program.ts +10 -8
- package/kit/hooks/hooks/task-hint/decision.ts +49 -0
- package/kit/hooks/hooks/task-hint/index.ts +79 -0
- package/kit/hooks/hooks/task-hint/run.ts +62 -0
- package/kit/hooks/hooks/task-hint/store.ts +51 -0
- package/kit/hooks/hooks.json +13 -5
- package/kit/review/agents/skill-review.md +34 -0
- package/kit/review/skills/pre-commit/SKILL.md +1 -1
- package/kit/rules/rules/kit/pull-requests.md +2 -0
- package/kit/workflow/skills/follow/SKILL.md +3 -10
- package/kit/workflow/skills/implement/SKILL.md +8 -6
- package/kit/workflow/skills/orchestrate/SKILL.md +218 -433
- package/kit/workflow/skills/orchestrate/handover/decision.ts +43 -8
- package/kit/workflow/skills/orchestrate/handover/index.ts +32 -4
- package/kit/workflow/skills/orchestrate/handover/standing-approval.ts +84 -0
- package/kit/workflow/skills/orchestrate/references/acceptance.md +15 -0
- package/kit/workflow/skills/orchestrate/references/close-lane.md +17 -0
- package/kit/workflow/skills/orchestrate/references/coordinator-handover.md +43 -17
- package/kit/workflow/skills/orchestrate/references/draft-review.md +19 -0
- package/kit/workflow/skills/orchestrate/references/events.md +86 -0
- package/kit/workflow/skills/orchestrate/references/herd.md +6 -6
- package/kit/workflow/skills/orchestrate/references/lane-handover.md +18 -0
- package/kit/workflow/skills/orchestrate/references/lane-session.md +25 -0
- package/kit/workflow/skills/orchestrate/references/lane-setup.md +37 -0
- package/kit/workflow/skills/orchestrate/references/plan-commit.md +22 -10
- package/kit/workflow/skills/orchestrate/references/sensitive-input.md +5 -7
- package/kit/workflow/skills/plan/SKILL.md +6 -5
- package/kit/workflow/skills/plan/references/template-English.md +4 -4
- package/kit/workflow/skills/plan/references/template-German.md +4 -4
- package/package.json +3 -1
- package/recipes/README.md +2 -1
- package/recipes/sources.md +5 -1
- package/schemas/jackherd.schema.json +139 -5
- package/src/check/agent-text-reviewers.ts +23 -0
- package/src/check/command.ts +2 -0
- package/src/check/plan-structure-check.ts +49 -2
- package/src/check/plan.ts +8 -1
- package/src/check/schema.ts +9 -0
- package/src/check/skill-budget-defaults.ts +55 -0
- package/src/check/skill-budgets.ts +185 -0
- package/src/cli.ts +2 -0
- package/src/config/auto-window.ts +8 -0
- package/src/config/link-kinds.ts +7 -1
- package/src/config/schema.ts +82 -25
- package/src/config/tones.ts +27 -0
- package/src/lane/close-board.ts +106 -0
- package/src/lane/close-check.ts +183 -0
- package/src/lane/close-command-line.ts +11 -0
- package/src/lane/close-command.ts +217 -0
- package/src/lane/close-options.ts +43 -0
- package/src/lane/close-survey.ts +83 -0
- package/src/lane/close.ts +141 -0
- package/src/lane/command.ts +13 -0
- package/src/lane-prompt/additions.ts +14 -0
- package/src/lane-prompt/build.ts +74 -0
- package/src/lane-prompt/command.ts +93 -0
- package/src/lane-prompt/input.ts +157 -0
- package/src/lane-prompt/options.ts +70 -0
- package/src/lane-prompt/texts.ts +101 -0
- package/src/mermaid/command.ts +36 -0
- package/src/mermaid/problems.ts +48 -0
- package/src/plan-format/clock.ts +8 -0
- package/src/plan-format/dependencies.ts +9 -6
- package/src/plan-format/index.ts +1 -0
- package/src/plan-format/short-names.ts +71 -0
- package/src/plan-format/waiting.ts +22 -4
- package/src/plans/appointment-rows.ts +83 -0
- package/src/plans/ask-coordinator.ts +111 -0
- package/src/plans/ask-target.ts +36 -0
- package/src/plans/board-close.ts +85 -0
- package/src/plans/child-stop.ts +51 -0
- package/src/plans/closable-records.ts +34 -0
- package/src/plans/command.ts +3 -2
- package/src/plans/fetch.ts +26 -3
- package/src/plans/frame.ts +5 -12
- package/src/plans/git.ts +54 -5
- package/src/plans/groomed.ts +27 -0
- package/src/plans/herd-claim.ts +173 -0
- package/src/plans/herd-memory.ts +36 -25
- package/src/plans/lane-plans.ts +44 -13
- package/src/plans/lane-summary.ts +253 -45
- package/src/plans/layout-phase.ts +36 -11
- package/src/plans/layout-summary.ts +89 -18
- package/src/plans/layout.ts +502 -98
- package/src/plans/link-chips.ts +23 -4
- package/src/plans/live.ts +178 -23
- package/src/plans/next-rows.ts +44 -0
- package/src/plans/phase-ids.ts +8 -0
- package/src/plans/phase-source.ts +15 -0
- package/src/plans/plan-cache.ts +28 -0
- package/src/plans/plan-heads.ts +54 -0
- package/src/plans/planned.ts +22 -0
- package/src/plans/pull-requests.ts +17 -2
- package/src/plans/question-records.ts +48 -0
- package/src/plans/questions-read.ts +13 -0
- package/src/plans/read-cache.ts +8 -0
- package/src/plans/record-id.ts +4 -0
- package/src/plans/restart-decision.ts +13 -4
- package/src/plans/row-actions.ts +52 -0
- package/src/plans/row-focus.ts +148 -0
- package/src/plans/row-targets.ts +7 -0
- package/src/plans/selection.ts +111 -18
- package/src/plans/sent-files-read.ts +6 -15
- package/src/plans/sent-files.ts +7 -14
- package/src/plans/settings.ts +12 -1
- package/src/plans/signalled-phase.ts +42 -0
- package/src/plans/signals-read.ts +12 -4
- package/src/plans/snapshot.ts +162 -81
- package/src/plans/source-asked.ts +19 -0
- package/src/plans/source-prefix.ts +17 -0
- package/src/plans/source-rows.ts +188 -0
- package/src/plans/sprint-export.ts +11 -11
- package/src/plans/sprint-page.ts +5 -5
- package/src/plans/subagent-phases.ts +95 -0
- package/src/plans/subagent-tasks.ts +51 -0
- package/src/plans/subagents-read.ts +66 -17
- package/src/plans/subagents.ts +64 -6
- package/src/plans/supervisor.ts +14 -4
- package/src/plans/symbols.ts +3 -0
- package/src/plans/task-name.ts +34 -0
- package/src/plans/texts.ts +48 -6
- package/src/plans/tones.ts +30 -0
- package/src/plans/view.ts +25 -11
- package/src/plans/waiting-click.ts +295 -35
- package/src/plans/waiting-records.ts +39 -0
- package/src/plans/waiting-window.ts +8 -0
- package/src/preview/command.ts +25 -3
- package/src/shared/private-location.ts +89 -0
- package/src/shared/stat-cache.ts +48 -0
- package/src/sync/kit-source.ts +29 -10
- package/src/sync/kit.ts +2 -0
- package/src/sync/manifest.ts +2 -0
- package/src/sync/plan-ask-rules.ts +94 -0
- package/src/sync/plan-hooks.ts +2 -0
- package/src/sync/render.ts +3 -3
- package/src/sync/settings-hooks.ts +4 -1
- package/src/sync/sync-plan.ts +1 -0
- package/src/sync/sync.ts +24 -3
- package/src/task-signals/claude-read.ts +27 -10
- package/src/terminal/text.ts +16 -3
- package/src/terminal/types.ts +4 -0
- package/src/version/skill-diff.ts +46 -0
- package/src/vocabulary/index.ts +26 -6
- package/src/waiting/command.ts +22 -7
- package/src/watch/appointments.ts +1 -2
- package/src/watch/asking-lanes.ts +40 -0
- package/src/watch/base-tip.ts +11 -0
- package/src/watch/command.ts +330 -105
- package/src/watch/heartbeat-progress.ts +6 -18
- package/src/watch/kit-update.ts +28 -9
- package/src/watch/lane-name.ts +6 -0
- package/src/watch/lane-progress.ts +40 -8
- package/src/watch/lane-report-file.ts +20 -5
- package/src/watch/lane-report.ts +32 -4
- package/src/watch/lane-sources.ts +31 -3
- package/src/watch/main-checkout.ts +34 -0
- package/src/watch/plan-badges-read.ts +22 -9
- package/src/watch/plan-badges.ts +10 -2
- package/src/watch/pull-requests.ts +6 -2
- package/src/watch/reference-paths.ts +87 -0
- package/src/watch/report-questions.ts +4 -3
- package/src/watch/running-subagents.ts +43 -0
- package/src/watch/skill-change.ts +124 -32
- package/src/watch/source-change.ts +48 -0
- package/src/watch/source-entries.ts +55 -0
- package/src/watch/source-fields.ts +1 -0
- package/src/watch/source-statuses.ts +60 -0
- package/src/watch/source-tickets.ts +21 -1
- package/src/watch/sources.ts +47 -4
- package/src/watch/stream-output.ts +4 -3
- package/src/watch/task-parallel-auto.ts +208 -0
- package/src/watch/task-parallel-data-paths.ts +46 -0
- package/src/watch/task-parallel-input.ts +5 -15
- package/src/watch/task-parallel-read.ts +3 -1
- package/src/watch/task-parallel.ts +36 -7
- package/src/watch/task-signal-missing.ts +69 -0
- package/src/watch/task-signal-read.ts +29 -0
- package/src/watch/texts.ts +10 -0
- package/src/watch/update-available.ts +6 -3
- package/src/watch/watch-events.ts +23 -0
- package/src/watch/watcher-lock.ts +25 -29
- package/kit/hooks/hooks/merge-guard/plan-approval-record.ts +0 -23
- package/kit/hooks/hooks/merge-guard/plan-approval-sources.ts +0 -196
- package/kit/hooks/hooks/merge-guard/plan-approval.ts +0 -168
- package/kit/hooks/hooks/merge-guard/plan-growth.ts +0 -40
- package/kit/hooks/hooks/merge-guard/plan-pointers.ts +0 -194
- package/kit/hooks/hooks/shared/command-text-runners.ts +0 -71
- package/kit/hooks/hooks/shared/plan-open-phases.ts +0 -114
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-RMMXD4G5.js";
|
|
5
5
|
|
|
6
6
|
// src/config/json-schema.ts
|
|
7
7
|
import { z as z3 } from "zod";
|
|
@@ -129,6 +129,7 @@ var publicContent = z.strictObject({
|
|
|
129
129
|
/** The agent that judges such a change; a global install names it with its prefix. */
|
|
130
130
|
reviewer: nonEmpty.default("public-content")
|
|
131
131
|
});
|
|
132
|
+
var skillBudgets = z.record(glob, z.number().int().positive());
|
|
132
133
|
var checkConfigSchema = z.strictObject({
|
|
133
134
|
/**
|
|
134
135
|
* Run once over the changed files, which are appended as arguments. Separate from `commands.lint`,
|
|
@@ -141,6 +142,11 @@ var checkConfigSchema = z.strictObject({
|
|
|
141
142
|
buildArtifacts: z.array(glob).default([...DEFAULT_BUILD_ARTIFACT_GLOBS]),
|
|
142
143
|
reviewerRoutes: z.array(reviewerRoute).default([]),
|
|
143
144
|
publicContent: publicContent.optional(),
|
|
145
|
+
/**
|
|
146
|
+
* Word budgets for the files an agent loads again and again. An entry replaces the kit's budget for the same
|
|
147
|
+
* path and sets one for the project's own skills; a file with none is measured against the kit's default.
|
|
148
|
+
*/
|
|
149
|
+
skillBudgets: skillBudgets.optional(),
|
|
144
150
|
/** Also run tests in directories that hold test files but belong to no package. */
|
|
145
151
|
looseTests: z.boolean().default(false)
|
|
146
152
|
});
|
|
@@ -158,7 +164,8 @@ var SOURCE_FIELD = {
|
|
|
158
164
|
url: "url",
|
|
159
165
|
body: "body",
|
|
160
166
|
author: "author",
|
|
161
|
-
status: "status"
|
|
167
|
+
status: "status",
|
|
168
|
+
count: "count"
|
|
162
169
|
};
|
|
163
170
|
var CONTENT_FIELDS = [SOURCE_FIELD.title, SOURCE_FIELD.author, SOURCE_FIELD.body, SOURCE_FIELD.url];
|
|
164
171
|
|
|
@@ -182,11 +189,23 @@ var isSourcePath = (path) => pathSteps(path) !== null;
|
|
|
182
189
|
var CLEAN_REVIEW_TEXT = "## Code review\n\nNo issues found. Checked for bugs and CLAUDE.md compliance.";
|
|
183
190
|
var BUN_FAILED_TEST_PATTERN = "^\\(fail\\) (.+?)(?: \\[[\\d.]+m?s\\])?$";
|
|
184
191
|
|
|
192
|
+
// src/plan-format/clock.ts
|
|
193
|
+
var DAY_MS = 864e5;
|
|
194
|
+
|
|
195
|
+
// src/config/auto-window.ts
|
|
196
|
+
var MAX_AUTO_WINDOW_DAYS = 7;
|
|
197
|
+
var windowDays = (since, until) => Math.round((Date.parse(`${until}T00:00:00Z`) - Date.parse(`${since}T00:00:00Z`)) / DAY_MS);
|
|
198
|
+
|
|
185
199
|
// src/config/link-kinds.ts
|
|
200
|
+
var ARTIFACT_LINK_KIND = {
|
|
201
|
+
pattern: "^https://claude\\.ai/(code/)?artifact/",
|
|
202
|
+
label: "Artifact",
|
|
203
|
+
icon: "\u2726"
|
|
204
|
+
};
|
|
186
205
|
var DEFAULT_LINK_KINDS = [
|
|
187
206
|
{ pattern: "^https://github\\.com/[^/]+/[^/]+/(pull|issues)/\\d+", label: "GitHub", icon: "\u2442" },
|
|
188
207
|
{ pattern: "^https://(www\\.)?figma\\.com/", label: "Figma", icon: "\u25C8" },
|
|
189
|
-
|
|
208
|
+
ARTIFACT_LINK_KIND
|
|
190
209
|
];
|
|
191
210
|
var isRegularExpression = (source) => {
|
|
192
211
|
try {
|
|
@@ -256,6 +275,25 @@ import { isAbsolute } from "path";
|
|
|
256
275
|
var segmentsOf = (path) => path.split(/[\\/]/).filter((segment) => segment !== "" && segment !== ".");
|
|
257
276
|
var isRepositoryDirectory = (value) => !isAbsolute(value) && segmentsOf(value).length > 0 && !segmentsOf(value).includes("..");
|
|
258
277
|
|
|
278
|
+
// src/config/tones.ts
|
|
279
|
+
var TONE = {
|
|
280
|
+
amber: "amber",
|
|
281
|
+
teal: "teal",
|
|
282
|
+
grey: "grey",
|
|
283
|
+
red: "red",
|
|
284
|
+
green: "green",
|
|
285
|
+
blue: "blue",
|
|
286
|
+
magenta: "magenta"
|
|
287
|
+
};
|
|
288
|
+
var TONE_NAMES = Object.values(TONE);
|
|
289
|
+
var TONE_ROLE = { waiting: "waiting", new: "new", running: "running", resting: "resting" };
|
|
290
|
+
var DEFAULT_TONES = {
|
|
291
|
+
[TONE_ROLE.waiting]: TONE.amber,
|
|
292
|
+
[TONE_ROLE.new]: TONE.amber,
|
|
293
|
+
[TONE_ROLE.running]: TONE.teal,
|
|
294
|
+
[TONE_ROLE.resting]: TONE.grey
|
|
295
|
+
};
|
|
296
|
+
|
|
259
297
|
// src/config/schema.ts
|
|
260
298
|
var TERMINAL_ADAPTER = { none: "none", herdr: "herdr" };
|
|
261
299
|
var MODEL_ROLE = {
|
|
@@ -320,7 +358,17 @@ var parallelTasksSchema = z2.strictObject({
|
|
|
320
358
|
* agent times the agents to add. Above it the watcher does not propose parallel tasks. The baseline is the context
|
|
321
359
|
* the first answer of a lane's session carried.
|
|
322
360
|
*/
|
|
323
|
-
maxTokens: z2.number().int().positive().default(DEFAULT_PARALLEL_TASKS_MAX_TOKENS)
|
|
361
|
+
maxTokens: z2.number().int().positive().default(DEFAULT_PARALLEL_TASKS_MAX_TOKENS),
|
|
362
|
+
/**
|
|
363
|
+
* The days, both included, on which a proposal that keeps every limit is marked `auto` and the coordinator starts it
|
|
364
|
+
* without asking: subagents beside running work in a lane, at most two extra agents, no path that always asks.
|
|
365
|
+
* `since` is the day it was switched on, `until` at most seven days later; outside the window it is off. Unset by
|
|
366
|
+
* default, so every proposal asks.
|
|
367
|
+
*/
|
|
368
|
+
auto: z2.strictObject({ since: z2.iso.date(), until: z2.iso.date() }).refine(({ since, until }) => until >= since, "until must not be before since").refine(
|
|
369
|
+
({ since, until }) => windowDays(since, until) <= MAX_AUTO_WINDOW_DAYS,
|
|
370
|
+
`until must be at most ${MAX_AUTO_WINDOW_DAYS} days after since`
|
|
371
|
+
).optional()
|
|
324
372
|
});
|
|
325
373
|
var checksSchema = z2.strictObject({
|
|
326
374
|
/**
|
|
@@ -359,11 +407,14 @@ var sourceMapSchema = z2.object({
|
|
|
359
407
|
[SOURCE_FIELD.body]: sourcePath.optional(),
|
|
360
408
|
[SOURCE_FIELD.author]: sourcePath.optional(),
|
|
361
409
|
/** A ticket source's status, shown on its badge in the herd; also a field `reportOn` can name. */
|
|
362
|
-
[SOURCE_FIELD.status]: sourcePath.optional()
|
|
410
|
+
[SOURCE_FIELD.status]: sourcePath.optional(),
|
|
411
|
+
/** How often the entry happened (an error count), shown as `113×` beside the entry in the herd's `Sources` block. */
|
|
412
|
+
[SOURCE_FIELD.count]: sourcePath.optional()
|
|
363
413
|
}).catchall(sourcePath).refine((map) => Object.keys(map).every((field) => SOURCE_FIELD_NAME.test(field)), {
|
|
364
414
|
message: "a map field is a letter, then letters, digits and `_`"
|
|
365
415
|
});
|
|
366
416
|
var canChange = ({ map, reportOn }) => map.updatedAt !== void 0 || CONTENT_FIELDS.some((field) => map[field] !== void 0) || reportOn !== void 0;
|
|
417
|
+
var toneName = z2.enum(TONE_NAMES);
|
|
367
418
|
var sourceSchema = z2.strictObject({
|
|
368
419
|
/** Printed in every report of the source and the key of its saved state. */
|
|
369
420
|
name: z2.string().regex(/^[A-Za-z0-9][\w.-]*$/, "letters, digits, `_`, `.` and `-`"),
|
|
@@ -378,6 +429,8 @@ var sourceSchema = z2.strictObject({
|
|
|
378
429
|
ignoreAuthors: z2.array(z2.string().min(1)).default([]),
|
|
379
430
|
/** The most new or changed entries one report shows of this source; the rest is one line `+<n> more`. */
|
|
380
431
|
reportLimit: z2.number().int().min(1).default(5),
|
|
432
|
+
/** Shown before a row of "Waiting for you" that came from this source (`Error log · Checkout: …`); without it the source's name. */
|
|
433
|
+
label: z2.string().min(1).max(40).regex(/^[^\r\n]+$/, "one line").optional(),
|
|
381
434
|
/** The lane the source's reports name. */
|
|
382
435
|
lane: z2.string().min(1).optional(),
|
|
383
436
|
/**
|
|
@@ -385,6 +438,8 @@ var sourceSchema = z2.strictObject({
|
|
|
385
438
|
* the lane above or the lane whose board row or plan names its id.
|
|
386
439
|
*/
|
|
387
440
|
ticket: z2.boolean().optional(),
|
|
441
|
+
/** The tone of the source's mark in the herd's `Sources` block while its newest report is new; without it the tone of the role `new`. */
|
|
442
|
+
tone: toneName.optional(),
|
|
388
443
|
/**
|
|
389
444
|
* The mapped fields whose change reports, with the old and the new value, in place of the content of `title`,
|
|
390
445
|
* `author`, `body` and `url`; without it a change of that content reports.
|
|
@@ -434,13 +489,13 @@ var mergeSchema = z2.strictObject({
|
|
|
434
489
|
*/
|
|
435
490
|
withoutChecks: z2.array(checkFreePattern).default([])
|
|
436
491
|
});
|
|
437
|
-
var
|
|
492
|
+
var guardFields = z2.strictObject({
|
|
438
493
|
/**
|
|
439
|
-
* The commands the merge guard hook asks the user about before they run,
|
|
440
|
-
* a wrapper or `sh -c`. `gh api
|
|
441
|
-
* guard off.
|
|
494
|
+
* The commands the merge guard hook asks the user about before they run, where they stand at a program position, also
|
|
495
|
+
* behind options before the verb, a chain, a wrapper or `sh -c`. `gh api`, when listed, asks only for a write to a
|
|
496
|
+
* merge, a file, a dispatch, a rerun or a ref. An empty list switches the guard off.
|
|
442
497
|
*/
|
|
443
|
-
commands: z2.array(guardedCommand).default(["gh pr merge", "gh workflow run"
|
|
498
|
+
commands: z2.array(guardedCommand).default(["gh pr merge", "gh workflow run"]),
|
|
444
499
|
selfMerge: z2.strictObject({
|
|
445
500
|
/** Whether `/orchestrate` offers "merge it yourself after green checks" in the pull request question; off also stops the hook honouring such an answer. */
|
|
446
501
|
offer: z2.boolean().default(true),
|
|
@@ -451,27 +506,44 @@ var guardSchema = z2.strictObject({
|
|
|
451
506
|
/** How long the approval gate hook holds a lane's call for the coordinator's dialog before it denies it; below the hook's 10 minutes. */
|
|
452
507
|
waitMinutes: z2.number().int().min(1).max(9).default(8)
|
|
453
508
|
}).prefault({}),
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
}).prefault({})
|
|
509
|
+
/**
|
|
510
|
+
* Paths that always ask, on top of the ones jackherd brings (`.github/**`, `**\/migrations/**`, `**\/.env*`,
|
|
511
|
+
* `.claude/**`, `.jackherd/**`, `bunfig.toml`, `**\/CLAUDE.md`, `**\/AGENTS.md`, `.agents/**`, `.mcp.json`,
|
|
512
|
+
* `.husky/**`, `lefthook.yml`, the lockfiles of bun, npm, pnpm and yarn, `**\/.npmrc`, `**\/CODEOWNERS`): a
|
|
513
|
+
* `merge.withoutChecks` pattern never opens them, and the watcher never starts parallel work on them by itself. They
|
|
514
|
+
* add to that list, never replace it. A standing approval covers them; only workflows, migrations and env files ask.
|
|
515
|
+
*/
|
|
516
|
+
alwaysAsk: z2.array(alwaysAskPattern).default([]),
|
|
517
|
+
/**
|
|
518
|
+
* Checks that must be among the head commit's and green before a standing approval lets a merge pass, by their names
|
|
519
|
+
* on GitHub. Unset, the merge needs at least one check and every one green; set, also each of these. Either way
|
|
520
|
+
* GitHub must be able to merge it.
|
|
521
|
+
*/
|
|
522
|
+
requiredChecks: z2.array(checkName).optional(),
|
|
523
|
+
/** Read until 2.0.0 as `alwaysAsk` (`exceptPaths`) and `requiredChecks`; the plan approval it was for is gone. */
|
|
524
|
+
planApproval: z2.strictObject({ exceptPaths: z2.array(alwaysAskPattern).optional(), requiredChecks: z2.array(checkName).optional() }).optional().meta({ deprecated: true })
|
|
471
525
|
});
|
|
526
|
+
var guardWithoutPlanApproval = ({ planApproval, ...guard }) => {
|
|
527
|
+
const requiredChecks = guard.requiredChecks ?? planApproval?.requiredChecks;
|
|
528
|
+
return {
|
|
529
|
+
...guard,
|
|
530
|
+
alwaysAsk: [...guard.alwaysAsk, ...planApproval?.exceptPaths ?? []],
|
|
531
|
+
...requiredChecks === void 0 ? {} : { requiredChecks }
|
|
532
|
+
};
|
|
533
|
+
};
|
|
534
|
+
var guardSchema = guardFields.transform(guardWithoutPlanApproval);
|
|
472
535
|
var hintsSchema = z2.strictObject({
|
|
473
536
|
/** Whether the `ask-hint` hook points out a last message that ends in a question while the turn used no `AskUserQuestion`; a lane session is never hinted. */
|
|
474
|
-
askUserQuestion: z2.boolean().default(true)
|
|
537
|
+
askUserQuestion: z2.boolean().default(true),
|
|
538
|
+
/** Whether the `task-hint` hook points a lane that edits a file without an open task signal to `jh task start`, once per task. */
|
|
539
|
+
taskSignal: z2.boolean().default(true)
|
|
540
|
+
});
|
|
541
|
+
var DEFAULT_TASK_SIGNAL_MINUTES = 10;
|
|
542
|
+
var tonesSchema = z2.strictObject({
|
|
543
|
+
waiting: toneName.default(DEFAULT_TONES.waiting),
|
|
544
|
+
new: toneName.default(DEFAULT_TONES.new),
|
|
545
|
+
running: toneName.default(DEFAULT_TONES.running),
|
|
546
|
+
resting: toneName.default(DEFAULT_TONES.resting)
|
|
475
547
|
});
|
|
476
548
|
var contextSchema = z2.strictObject({
|
|
477
549
|
/** Tokens in a lane's context from which its fill shows yellow and `/implement` offers a new chat. */
|
|
@@ -542,12 +614,15 @@ var jackherdConfigSchema = z2.strictObject({
|
|
|
542
614
|
icon: z2.string().min(1)
|
|
543
615
|
})
|
|
544
616
|
).default(DEFAULT_LINK_KINDS.map((kind) => ({ ...kind }))),
|
|
545
|
-
context: contextSchema.prefault({})
|
|
617
|
+
context: contextSchema.prefault({}),
|
|
618
|
+
/** The colour of each role in the herd, by name: what waits for you, what is new, what runs, what rests. */
|
|
619
|
+
tones: tonesSchema.prefault({})
|
|
546
620
|
}).prefault({}),
|
|
547
621
|
/**
|
|
548
622
|
* Read by `jh watch`; without `deploy` it reports no deploy runs. `feedback` is on unless set to `false`:
|
|
549
623
|
* it reports new feedback on the lanes' pull requests except from the account the watcher runs as and `ignore`.
|
|
550
624
|
* `parallelTasks.maxTokens` caps the cost of the extra agents in a proposal of parallel tasks at the heartbeat.
|
|
625
|
+
* `taskSignalMinutes` is how long a lane works without a task signal before it reports `no task signal`, once per phase.
|
|
551
626
|
* `checks` reads the failed tests of a red lane pull request (`failedTestPattern`), also with `feedback` off.
|
|
552
627
|
* Without `previews` it reads no deploy preview; with it, one `preview` per new head commit. Each of `sources` is a
|
|
553
628
|
* command whose JSON output reports `source` for a new or changed entry.
|
|
@@ -557,6 +632,7 @@ var jackherdConfigSchema = z2.strictObject({
|
|
|
557
632
|
feedback: z2.union([z2.literal(false), feedbackSchema]).prefault({}),
|
|
558
633
|
checks: checksSchema.prefault({}),
|
|
559
634
|
parallelTasks: parallelTasksSchema.prefault({}),
|
|
635
|
+
taskSignalMinutes: z2.number().int().positive().default(DEFAULT_TASK_SIGNAL_MINUTES),
|
|
560
636
|
previews: previewsSchema.optional(),
|
|
561
637
|
sources: z2.array(sourceSchema).refine(hasUniqueNames, "source names must be unique").default([])
|
|
562
638
|
}).prefault({}),
|
package/dist/plan-format.d.ts
CHANGED
|
@@ -23,14 +23,20 @@ declare const ADDRESS_FORM: Record<PlanLanguage, Record<ResponseAddress, string>
|
|
|
23
23
|
*/
|
|
24
24
|
declare const SELF_MERGE_LABEL: Record<PlanLanguage, string>;
|
|
25
25
|
/**
|
|
26
|
-
* The
|
|
27
|
-
*
|
|
28
|
-
*
|
|
26
|
+
* The standing approval: the option that approves (`approve`) and the word between the plan and the end in its
|
|
27
|
+
* question (``Plan `<path>` <until> <YYYY-MM-DD HH:MM>``). The merge guard hook lifts its question for a plain merge of
|
|
28
|
+
* that plan's lanes only when the newest answer to such a question is exactly `approve`, so both are fixed per
|
|
29
|
+
* language and not a config value.
|
|
29
30
|
*/
|
|
30
|
-
declare const
|
|
31
|
+
declare const STANDING_APPROVAL_TEXTS: Record<PlanLanguage, {
|
|
31
32
|
approve: string;
|
|
32
|
-
|
|
33
|
+
until: string;
|
|
33
34
|
}>;
|
|
35
|
+
/**
|
|
36
|
+
* The words a lane prompt opens with, followed by the lane's name. The ask hint hook and the project's lane exception
|
|
37
|
+
* recognise a lane by them, so they are fixed per language and not a config value.
|
|
38
|
+
*/
|
|
39
|
+
declare const LANE_PROMPT_OPENING: Record<PlanLanguage, string>;
|
|
34
40
|
/**
|
|
35
41
|
* The words of an approval: the two option labels of the coordinator's question about a lane's call, which the
|
|
36
42
|
* approval gate hook matches exactly and which follow the response language, and what the hook tells the lane when it
|
|
@@ -58,6 +64,12 @@ declare const APPOINTMENT_VOCABULARY: Record<PlanLanguage, {
|
|
|
58
64
|
/** Further header names that count as the status column. */
|
|
59
65
|
statusAlso: readonly string[];
|
|
60
66
|
open: readonly string[];
|
|
67
|
+
/** The lower-case word an action starts with when it is the weekly board grooming, which the herd's header counts from. */
|
|
68
|
+
grooming: string;
|
|
69
|
+
}>;
|
|
70
|
+
/** The heading of the board section of what the coordinator does next, a numbered list of at most three lines. */
|
|
71
|
+
declare const NEXT_VOCABULARY: Record<PlanLanguage, {
|
|
72
|
+
heading: string;
|
|
61
73
|
}>;
|
|
62
74
|
/**
|
|
63
75
|
* The board section of what waits on the user's answer (a sent preview, an open question, a report to accept): its
|
|
@@ -500,6 +512,39 @@ declare const activeItem: (phase: Pick<PhaseProgress, "items">) => ChecklistItem
|
|
|
500
512
|
/** The sidebar title of a lane, e.g. `P4 · 7/12` or `P4 · 12/12` while ticked and not merged; null when nothing of the lane could be counted. */
|
|
501
513
|
declare const shortTitle: (progress: LaneProgress) => string | null;
|
|
502
514
|
|
|
515
|
+
/** Characters a task's short name may have, so the herd shows it whole. */
|
|
516
|
+
declare const SHORT_NAME_LIMIT = 40;
|
|
517
|
+
/** Characters a phase title may have; the title is the phase's name. */
|
|
518
|
+
declare const PHASE_TITLE_LIMIT = 50;
|
|
519
|
+
declare const SHORT_NAME_PROBLEM: {
|
|
520
|
+
readonly missing: "missing";
|
|
521
|
+
readonly long: "long";
|
|
522
|
+
};
|
|
523
|
+
/** What is wrong with an open task's short name: none at all, or one of `length` characters over the limit. */
|
|
524
|
+
type ShortNameProblem = {
|
|
525
|
+
problem: typeof SHORT_NAME_PROBLEM.missing;
|
|
526
|
+
} | {
|
|
527
|
+
problem: typeof SHORT_NAME_PROBLEM.long;
|
|
528
|
+
length: number;
|
|
529
|
+
};
|
|
530
|
+
/** An open task (1-based line) of phase `phase` whose short name is missing or too long. */
|
|
531
|
+
type ShortNameFinding = {
|
|
532
|
+
line: number;
|
|
533
|
+
phase: string;
|
|
534
|
+
} & ShortNameProblem;
|
|
535
|
+
/** A phase heading whose title has `length` characters, over the limit. */
|
|
536
|
+
type LongPhaseTitle = PhaseHeading & {
|
|
537
|
+
length: number;
|
|
538
|
+
};
|
|
539
|
+
/**
|
|
540
|
+
* The open tasks a change wrote (a line not in `previousLines`) that do not start with `**Name**:` or whose name is
|
|
541
|
+
* longer than `SHORT_NAME_LIMIT`. Only tasks of a phase section before its review gate count: a gate item keeps its
|
|
542
|
+
* own form.
|
|
543
|
+
*/
|
|
544
|
+
declare const shortNameFindings: (markdown: string, language: PlanLanguage, previousLines: ReadonlySet<string>) => ShortNameFinding[];
|
|
545
|
+
/** The phase headings a change wrote whose title is longer than `PHASE_TITLE_LIMIT`. */
|
|
546
|
+
declare const longPhaseTitles: (markdown: string, language: PlanLanguage, previousLines: ReadonlySet<string>) => LongPhaseTitle[];
|
|
547
|
+
|
|
503
548
|
/** What a sprint document says around its lanes table: its goal and its history. */
|
|
504
549
|
type SprintPlanRow = {
|
|
505
550
|
cells: string[];
|
|
@@ -529,4 +574,4 @@ declare const readSprintDocument: (markdown: string, language: PlanLanguage) =>
|
|
|
529
574
|
*/
|
|
530
575
|
declare const readPhaseDependencies: (planMarkdown: string, language: PlanLanguage) => Record<string, string>;
|
|
531
576
|
|
|
532
|
-
export { ADDRESS_FORM, APPOINTMENT_VOCABULARY, APPROVAL_TEXTS, type ChecklistItem, type ColumnModel, type DeferredItem, type DriftFinding, LANGUAGE_NAME, type LaneProgress, type MergedPhaseWithOpenBoxes, type MisplacedDeferral, PHASE_CELL_LIMIT, PHASE_ID_PREFIX,
|
|
577
|
+
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, NEXT_VOCABULARY, PHASE_CELL_LIMIT, PHASE_ID_PREFIX, PHASE_TITLE_LIMIT, PLAN_LANGUAGE, PLAN_STATUS, PLAN_VOCABULARY, PROSE_REASON, type PhaseDependency, type PhaseHeading, type PhaseProgress, type PhaseSchedule, type PhaseTally, type PhaseWithoutGate, type PhaseWithoutRow, 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, parsePlanPhases, parseSprintLanes, phaseHeading, phaseHeadingPattern, phaseHeadings, phaseIdsOfCell, phaseIdsOfPlan, phaseLabel, phaseLabels, phaseProgress, phaseWaves, phasesText, phasesWithoutDependencyRow, readPhaseDependencies, readPlanHeader, readSprintDocument, resolvePlanPath, sectionHeading, shortNameFindings, shortTitle, sprintTitleOf, sumPhases, tallyPhases, wavesOfPlan, withoutRepeats };
|
package/dist/plan-format.js
CHANGED
|
@@ -2,19 +2,21 @@ import {
|
|
|
2
2
|
ADDRESS_FORM,
|
|
3
3
|
APPOINTMENT_VOCABULARY,
|
|
4
4
|
APPROVAL_TEXTS,
|
|
5
|
+
LANE_PROMPT_OPENING,
|
|
5
6
|
LANGUAGE_NAME,
|
|
6
|
-
|
|
7
|
+
NEXT_VOCABULARY,
|
|
7
8
|
PLAN_LANGUAGE,
|
|
8
9
|
PLAN_STATUS,
|
|
9
10
|
PLAN_VOCABULARY,
|
|
10
11
|
RESPONSE_ADDRESS,
|
|
11
12
|
SELF_MERGE_LABEL,
|
|
13
|
+
STANDING_APPROVAL_TEXTS,
|
|
12
14
|
WAITING_VOCABULARY,
|
|
13
15
|
classifyStatus,
|
|
14
16
|
isPlanLanguage,
|
|
15
17
|
phaseHeading,
|
|
16
18
|
sectionHeading
|
|
17
|
-
} from "./chunk-
|
|
19
|
+
} from "./chunk-RMMXD4G5.js";
|
|
18
20
|
|
|
19
21
|
// src/plan-format/lanes.ts
|
|
20
22
|
import { posix } from "path";
|
|
@@ -498,7 +500,7 @@ var dependsOf = (cell, language) => {
|
|
|
498
500
|
return { after, external };
|
|
499
501
|
};
|
|
500
502
|
var pathOf = (entry) => {
|
|
501
|
-
if (/\s/.test(entry)) return null;
|
|
503
|
+
if (/\s/.test(entry) || entry.includes("[]")) return null;
|
|
502
504
|
const glob = entry.search(GLOB_CHARACTER);
|
|
503
505
|
const literal = glob === -1 ? entry : entry.slice(0, Math.max(entry.lastIndexOf("/", glob), 0));
|
|
504
506
|
const path = literal.replace(/^\.\//, "").replace(/\/+$/, "");
|
|
@@ -573,12 +575,13 @@ var phasesWithoutDependencyRow = (markdown, language) => {
|
|
|
573
575
|
};
|
|
574
576
|
var longestChain = (chains) => chains.reduce((longest, chain) => chain.length > longest.length ? chain : longest, []);
|
|
575
577
|
var mergeSets = (sets) => sets.some((set) => set === null) ? null : sets.flatMap((set) => set ?? []);
|
|
576
|
-
var
|
|
578
|
+
var writeSetWithin = (row, byId, chain) => {
|
|
577
579
|
if (chain.includes(row.id)) return null;
|
|
578
580
|
const targets = row.writesLike.map((id) => byId.get(id));
|
|
579
|
-
const like = mergeSets(targets.map((target) => target ?
|
|
581
|
+
const like = mergeSets(targets.map((target) => target ? writeSetWithin(target, byId, [...chain, row.id]) : null));
|
|
580
582
|
return like === null ? null : [...row.writes, ...like];
|
|
581
583
|
};
|
|
584
|
+
var writeSetOf = (row, byId) => writeSetWithin(row, byId, []);
|
|
582
585
|
var overlaps = (left, right) => left === "" || right === "" || left === right || left.startsWith(`${right}/`) || right.startsWith(`${left}/`);
|
|
583
586
|
var sharesEntry = (first, second) => {
|
|
584
587
|
const entries = new Set(second.map((entry) => entry.trim().toLowerCase()));
|
|
@@ -587,7 +590,7 @@ var sharesEntry = (first, second) => {
|
|
|
587
590
|
var sharesMarker = (first, second) => sharesEntry(first.serialMarkers, second.serialMarkers) || sharesEntry(first.releases, second.releases);
|
|
588
591
|
var collides = (first, second) => sharesMarker(first, second) || first.writes.some((path) => second.writes.some((other) => overlaps(path, other)));
|
|
589
592
|
var candidateOf = (row, byId) => {
|
|
590
|
-
const writes = writeSetOf(row, byId
|
|
593
|
+
const writes = writeSetOf(row, byId);
|
|
591
594
|
if (writes === null || writes.length === 0) return [];
|
|
592
595
|
const releases = [.../* @__PURE__ */ new Set([...row.releases, ...releasedByFiles(writes)])];
|
|
593
596
|
return [{ id: row.id, writes, serialMarkers: row.serialMarkers, releases }];
|
|
@@ -824,6 +827,38 @@ var boardRowsWithoutPhase = (markdown, language, previousLines) => changedOpenLa
|
|
|
824
827
|
({ phases }) => phaseIdsOfCell(phases, language).length === 0
|
|
825
828
|
);
|
|
826
829
|
|
|
830
|
+
// src/plan-format/short-names.ts
|
|
831
|
+
var SHORT_NAME_LIMIT = 40;
|
|
832
|
+
var PHASE_TITLE_LIMIT = 50;
|
|
833
|
+
var SHORT_NAME_PROBLEM = { missing: "missing", long: "long" };
|
|
834
|
+
var SHORT_NAME = /^\s*[-*]\s+\[ \]\s+\*\*(.+?)\*\*:/;
|
|
835
|
+
var characterCount = (text) => [...text].length;
|
|
836
|
+
var shortNameProblem = (line) => {
|
|
837
|
+
const name = line.match(SHORT_NAME)?.[1];
|
|
838
|
+
if (name === void 0) return { problem: SHORT_NAME_PROBLEM.missing };
|
|
839
|
+
const length = characterCount(name);
|
|
840
|
+
return length > SHORT_NAME_LIMIT ? { problem: SHORT_NAME_PROBLEM.long, length } : null;
|
|
841
|
+
};
|
|
842
|
+
var shortNameFindings = (markdown, language, previousLines) => {
|
|
843
|
+
const headings = new Map(phaseHeadings(markdown, language).map((heading) => [heading.line, heading.id]));
|
|
844
|
+
const found = [];
|
|
845
|
+
let phase = null;
|
|
846
|
+
for (const [index, line] of markdown.split("\n").entries()) {
|
|
847
|
+
const heading = headings.get(index + 1);
|
|
848
|
+
if (heading !== void 0) phase = heading;
|
|
849
|
+
else if (endsPhase(line) || isReviewGateLine(line)) phase = null;
|
|
850
|
+
else if (phase !== null && isOpenCheckboxLine(line) && !previousLines.has(line)) {
|
|
851
|
+
const problem = shortNameProblem(line);
|
|
852
|
+
if (problem) found.push({ line: index + 1, phase, ...problem });
|
|
853
|
+
}
|
|
854
|
+
}
|
|
855
|
+
return found;
|
|
856
|
+
};
|
|
857
|
+
var longPhaseTitles = (markdown, language, previousLines) => {
|
|
858
|
+
const lines = markdown.split("\n");
|
|
859
|
+
return phaseHeadings(markdown, language).filter(({ line }) => !previousLines.has(lines[line - 1] ?? "")).map((heading) => ({ ...heading, length: characterCount(heading.title) })).filter(({ length }) => length > PHASE_TITLE_LIMIT);
|
|
860
|
+
};
|
|
861
|
+
|
|
827
862
|
// src/plan-format/sprint-document.ts
|
|
828
863
|
var PHASE_ID_IN_CELL = /^(\d+[a-z]?)\b/;
|
|
829
864
|
var paragraphs = (lines) => lines.join("\n").split(/\n\s*\n/).map((paragraph) => paragraph.trim()).filter(Boolean);
|
|
@@ -866,16 +901,21 @@ export {
|
|
|
866
901
|
ADDRESS_FORM,
|
|
867
902
|
APPOINTMENT_VOCABULARY,
|
|
868
903
|
APPROVAL_TEXTS,
|
|
904
|
+
LANE_PROMPT_OPENING,
|
|
869
905
|
LANGUAGE_NAME,
|
|
906
|
+
NEXT_VOCABULARY,
|
|
870
907
|
PHASE_CELL_LIMIT,
|
|
871
908
|
PHASE_ID_PREFIX,
|
|
872
|
-
|
|
909
|
+
PHASE_TITLE_LIMIT,
|
|
873
910
|
PLAN_LANGUAGE,
|
|
874
911
|
PLAN_STATUS,
|
|
875
912
|
PLAN_VOCABULARY,
|
|
876
913
|
PROSE_REASON,
|
|
877
914
|
RESPONSE_ADDRESS,
|
|
878
915
|
SELF_MERGE_LABEL,
|
|
916
|
+
SHORT_NAME_LIMIT,
|
|
917
|
+
SHORT_NAME_PROBLEM,
|
|
918
|
+
STANDING_APPROVAL_TEXTS,
|
|
879
919
|
WAITING_VOCABULARY,
|
|
880
920
|
activeItem,
|
|
881
921
|
boardRowsWithProse,
|
|
@@ -901,6 +941,7 @@ export {
|
|
|
901
941
|
isTickedUnmerged,
|
|
902
942
|
laneNameOf,
|
|
903
943
|
laneProgress,
|
|
944
|
+
longPhaseTitles,
|
|
904
945
|
mergedPhasesWithOpenBoxes,
|
|
905
946
|
misplacedDeferrals,
|
|
906
947
|
newPhasesWithoutReviewGate,
|
|
@@ -926,6 +967,7 @@ export {
|
|
|
926
967
|
readSprintDocument,
|
|
927
968
|
resolvePlanPath,
|
|
928
969
|
sectionHeading,
|
|
970
|
+
shortNameFindings,
|
|
929
971
|
shortTitle,
|
|
930
972
|
sprintTitleOf,
|
|
931
973
|
sumPhases,
|