jackherd 1.8.1 → 1.10.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +60 -0
- package/INSTALL.md +3 -1
- package/README.md +75 -64
- package/dist/{chunk-OU2KALXP.js → chunk-5MQBCUQ5.js} +24 -4
- package/dist/config.d.ts +45 -4
- package/dist/config.js +84 -27
- package/dist/plan-format.d.ts +63 -7
- package/dist/plan-format.js +66 -12
- 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/approval-gate/index.ts +22 -4
- package/kit/hooks/hooks/approval-gate/run.ts +29 -6
- package/kit/hooks/hooks/approval-gate/subagent.ts +18 -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 -140
- 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 +52 -0
- package/kit/hooks/hooks/open-question/store.ts +39 -0
- package/kit/hooks/hooks/sent-file/index.ts +19 -0
- package/kit/hooks/hooks/sent-file/record.ts +128 -0
- package/kit/hooks/hooks/sent-file/run.ts +48 -0
- package/kit/hooks/hooks/sent-file/store.ts +9 -0
- package/kit/hooks/hooks/shared/board-lanes.ts +15 -2
- 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 +21 -4
- package/kit/hooks/statusline.sh +1 -1
- package/kit/review/agents/skill-review.md +34 -0
- package/kit/review/skills/pre-commit/SKILL.md +1 -1
- package/kit/workflow/skills/follow/SKILL.md +1 -8
- package/kit/workflow/skills/implement/SKILL.md +10 -6
- package/kit/workflow/skills/orchestrate/SKILL.md +213 -412
- 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 +4 -1
- package/recipes/README.md +2 -1
- package/recipes/sources.md +1 -1
- package/schemas/jackherd.schema.json +85 -7
- 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 +4 -0
- package/src/config/auto-window.ts +8 -0
- package/src/config/link-kinds.ts +7 -1
- package/src/config/schema.ts +80 -26
- package/src/context/fill.ts +12 -0
- package/src/context/session-log.ts +43 -1
- 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/build.ts +74 -0
- package/src/lane-prompt/command.ts +87 -0
- package/src/lane-prompt/input.ts +116 -0
- package/src/lane-prompt/options.ts +68 -0
- package/src/lane-prompt/texts.ts +101 -0
- package/src/plan-format/clock.ts +12 -0
- package/src/plan-format/dependencies.ts +9 -9
- package/src/plan-format/index.ts +1 -0
- package/src/plan-format/lanes.ts +30 -5
- package/src/plan-format/short-names.ts +71 -0
- package/src/plan-format/terminal-text.ts +4 -0
- package/src/plan-format/waiting.ts +53 -0
- package/src/plans/active-tasks.ts +60 -0
- package/src/plans/appointment-rows.ts +59 -0
- package/src/plans/ask-coordinator.ts +77 -0
- package/src/plans/ask-target.ts +25 -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/current-phase.ts +17 -7
- package/src/plans/current-task.ts +63 -5
- package/src/plans/fetch.ts +26 -3
- package/src/plans/git.ts +19 -5
- 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-standing.ts +9 -4
- package/src/plans/lane-summary.ts +367 -92
- package/src/plans/lane-tickets.ts +15 -0
- package/src/plans/layout-phase.ts +80 -19
- package/src/plans/layout-summary.ts +163 -74
- package/src/plans/layout.ts +472 -114
- package/src/plans/link-chips.ts +1 -0
- package/src/plans/live.ts +119 -10
- package/src/plans/phase-ids.ts +8 -0
- package/src/plans/phase-models.ts +1 -1
- package/src/plans/phase-ranges.ts +45 -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/pull-requests.ts +23 -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/selection.ts +117 -34
- package/src/plans/sent-files-read.ts +13 -0
- package/src/plans/sent-files.ts +114 -0
- package/src/plans/settings.ts +2 -1
- package/src/plans/signalled-phase.ts +42 -0
- package/src/plans/signals-read.ts +62 -0
- package/src/plans/snapshot.ts +279 -60
- 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 +131 -0
- package/src/plans/subagents.ts +190 -0
- 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 +36 -14
- package/src/plans/unlisted-lanes.ts +10 -5
- package/src/plans/view.ts +20 -12
- package/src/plans/waiting-click.ts +231 -0
- package/src/plans/waiting-records.ts +39 -0
- package/src/plans/waiting-window.ts +8 -0
- package/src/preview/command.ts +105 -0
- package/src/preview/embed.ts +209 -0
- package/src/preview/images.ts +89 -0
- package/src/preview/open.ts +12 -0
- package/src/preview/options.ts +32 -0
- package/src/preview/page.ts +89 -0
- package/src/preview/render.ts +41 -0
- package/src/settled/board-row.ts +1 -2
- package/src/shared/is-record.ts +2 -0
- package/src/shared/private-location.ts +89 -0
- package/src/shared/private-records.ts +48 -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/agent.ts +15 -0
- package/src/task-signals/claude-read.ts +89 -0
- package/src/task-signals/claude-signals.ts +112 -0
- package/src/task-signals/command.ts +71 -0
- package/src/task-signals/fixtures/session.jsonl +7 -0
- package/src/task-signals/matcher.ts +96 -0
- package/src/task-signals/record-store.ts +40 -0
- package/src/task-signals/record.ts +68 -0
- package/src/task-signals/text.ts +13 -0
- package/src/task-signals/types.ts +14 -0
- package/src/terminal/open.ts +13 -6
- package/src/terminal/text.ts +16 -4
- package/src/terminal/width.ts +2 -1
- package/src/version/skill-diff.ts +46 -0
- package/src/vocabulary/index.ts +44 -6
- package/src/waiting/command.ts +50 -0
- package/src/watch/appointments.ts +2 -4
- package/src/watch/approvals.ts +2 -2
- package/src/watch/asking-lanes.ts +40 -0
- package/src/watch/base-tip.ts +11 -0
- package/src/watch/command.ts +401 -90
- package/src/watch/failed-tests.ts +56 -10
- package/src/watch/failure-read.ts +18 -6
- package/src/watch/failures.ts +29 -5
- package/src/watch/feedback.ts +1 -1
- package/src/watch/gh-process.ts +9 -3
- package/src/watch/heartbeat-progress.ts +6 -18
- package/src/watch/json-guards.ts +0 -3
- package/src/watch/kit-update.ts +28 -9
- package/src/watch/lane-fill.ts +1 -1
- package/src/watch/lane-model.ts +1 -1
- package/src/watch/lane-name.ts +6 -0
- package/src/watch/lane-parallel.ts +1 -1
- package/src/watch/lane-progress.ts +58 -14
- package/src/watch/lane-report-file.ts +20 -5
- package/src/watch/lane-report.ts +37 -5
- package/src/watch/lane-rest.ts +28 -0
- package/src/watch/lane-sources.ts +31 -3
- package/src/watch/main-checkout.ts +34 -0
- package/src/watch/plan-badges-read.ts +13 -7
- package/src/watch/plan-badges.ts +9 -2
- package/src/watch/previews.ts +1 -1
- package/src/watch/pull-requests.ts +8 -3
- package/src/watch/reference-paths.ts +87 -0
- 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-path.ts +1 -1
- package/src/watch/sources.ts +7 -4
- package/src/watch/stream-output.ts +14 -8
- 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 +108 -0
- package/src/watch/task-parallel-read.ts +151 -0
- package/src/watch/task-parallel-touched.ts +39 -0
- package/src/watch/task-parallel.ts +235 -0
- 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/waiting-line.ts +19 -0
- package/src/watch/watch-events.ts +36 -1
- package/src/watch/watcher-lock.ts +25 -29
- package/src/watch/workflow-runs.ts +2 -1
- 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-5MQBCUQ5.js";
|
|
5
5
|
|
|
6
6
|
// src/config/json-schema.ts
|
|
7
7
|
import { z as z3 } from "zod";
|
|
@@ -129,6 +129,7 @@ var publicContent = z.strictObject({
|
|
|
129
129
|
/** The agent that judges such a change; a global install names it with its prefix. */
|
|
130
130
|
reviewer: nonEmpty.default("public-content")
|
|
131
131
|
});
|
|
132
|
+
var skillBudgets = z.record(glob, z.number().int().positive());
|
|
132
133
|
var checkConfigSchema = z.strictObject({
|
|
133
134
|
/**
|
|
134
135
|
* Run once over the changed files, which are appended as arguments. Separate from `commands.lint`,
|
|
@@ -141,6 +142,11 @@ var checkConfigSchema = z.strictObject({
|
|
|
141
142
|
buildArtifacts: z.array(glob).default([...DEFAULT_BUILD_ARTIFACT_GLOBS]),
|
|
142
143
|
reviewerRoutes: z.array(reviewerRoute).default([]),
|
|
143
144
|
publicContent: publicContent.optional(),
|
|
145
|
+
/**
|
|
146
|
+
* Word budgets for the files an agent loads again and again. An entry replaces the kit's budget for the same
|
|
147
|
+
* path and sets one for the project's own skills; a file with none is measured against the kit's default.
|
|
148
|
+
*/
|
|
149
|
+
skillBudgets: skillBudgets.optional(),
|
|
144
150
|
/** Also run tests in directories that hold test files but belong to no package. */
|
|
145
151
|
looseTests: z.boolean().default(false)
|
|
146
152
|
});
|
|
@@ -182,11 +188,23 @@ var isSourcePath = (path) => pathSteps(path) !== null;
|
|
|
182
188
|
var CLEAN_REVIEW_TEXT = "## Code review\n\nNo issues found. Checked for bugs and CLAUDE.md compliance.";
|
|
183
189
|
var BUN_FAILED_TEST_PATTERN = "^\\(fail\\) (.+?)(?: \\[[\\d.]+m?s\\])?$";
|
|
184
190
|
|
|
191
|
+
// src/plan-format/clock.ts
|
|
192
|
+
var DAY_MS = 864e5;
|
|
193
|
+
|
|
194
|
+
// src/config/auto-window.ts
|
|
195
|
+
var MAX_AUTO_WINDOW_DAYS = 7;
|
|
196
|
+
var windowDays = (since, until) => Math.round((Date.parse(`${until}T00:00:00Z`) - Date.parse(`${since}T00:00:00Z`)) / DAY_MS);
|
|
197
|
+
|
|
185
198
|
// src/config/link-kinds.ts
|
|
199
|
+
var ARTIFACT_LINK_KIND = {
|
|
200
|
+
pattern: "^https://claude\\.ai/(code/)?artifact/",
|
|
201
|
+
label: "Artifact",
|
|
202
|
+
icon: "\u2726"
|
|
203
|
+
};
|
|
186
204
|
var DEFAULT_LINK_KINDS = [
|
|
187
205
|
{ pattern: "^https://github\\.com/[^/]+/[^/]+/(pull|issues)/\\d+", label: "GitHub", icon: "\u2442" },
|
|
188
206
|
{ pattern: "^https://(www\\.)?figma\\.com/", label: "Figma", icon: "\u25C8" },
|
|
189
|
-
|
|
207
|
+
ARTIFACT_LINK_KIND
|
|
190
208
|
];
|
|
191
209
|
var isRegularExpression = (source) => {
|
|
192
210
|
try {
|
|
@@ -313,6 +331,25 @@ var feedbackSchema = z2.strictObject({
|
|
|
313
331
|
*/
|
|
314
332
|
noFindingTemplates: z2.array(z2.string().min(1)).default([CLEAN_REVIEW_TEXT])
|
|
315
333
|
});
|
|
334
|
+
var DEFAULT_PARALLEL_TASKS_MAX_TOKENS = 1e5;
|
|
335
|
+
var parallelTasksSchema = z2.strictObject({
|
|
336
|
+
/**
|
|
337
|
+
* The most the extra agents of a parallel proposal may cost before they do anything: the token baseline of a fresh
|
|
338
|
+
* agent times the agents to add. Above it the watcher does not propose parallel tasks. The baseline is the context
|
|
339
|
+
* the first answer of a lane's session carried.
|
|
340
|
+
*/
|
|
341
|
+
maxTokens: z2.number().int().positive().default(DEFAULT_PARALLEL_TASKS_MAX_TOKENS),
|
|
342
|
+
/**
|
|
343
|
+
* The days, both included, on which a proposal that keeps every limit is marked `auto` and the coordinator starts it
|
|
344
|
+
* without asking: subagents beside running work in a lane, at most two extra agents, no path that always asks.
|
|
345
|
+
* `since` is the day it was switched on, `until` at most seven days later; outside the window it is off. Unset by
|
|
346
|
+
* default, so every proposal asks.
|
|
347
|
+
*/
|
|
348
|
+
auto: z2.strictObject({ since: z2.iso.date(), until: z2.iso.date() }).refine(({ since, until }) => until >= since, "until must not be before since").refine(
|
|
349
|
+
({ since, until }) => windowDays(since, until) <= MAX_AUTO_WINDOW_DAYS,
|
|
350
|
+
`until must be at most ${MAX_AUTO_WINDOW_DAYS} days after since`
|
|
351
|
+
).optional()
|
|
352
|
+
});
|
|
316
353
|
var checksSchema = z2.strictObject({
|
|
317
354
|
/**
|
|
318
355
|
* Finds a failed test in a line of a red run's log (`gh run view --log-failed`) after its job, step and timestamp
|
|
@@ -397,9 +434,9 @@ var sourceSchema = z2.strictObject({
|
|
|
397
434
|
var hasUniqueNames = (sources) => new Set(sources.map(({ name }) => name)).size === sources.length;
|
|
398
435
|
var coordinatorSchema = z2.strictObject({
|
|
399
436
|
/** Tokens in the coordinator's context from which the watcher reports `coordinator warn`. */
|
|
400
|
-
warnTokens: tokenCount.default(
|
|
437
|
+
warnTokens: tokenCount.default(35e4),
|
|
401
438
|
/** Tokens in the coordinator's context from which the watcher reports `coordinator alert`. */
|
|
402
|
-
alertTokens: tokenCount.default(
|
|
439
|
+
alertTokens: tokenCount.default(5e5)
|
|
403
440
|
}).refine(warnBelowAlert, WARN_BELOW_ALERT);
|
|
404
441
|
var guardedCommand = z2.string().regex(/^[a-z][a-z0-9-]*( [a-z][a-z0-9-]*){1,3}$/, "a program and one to three plain words, such as gh pr merge");
|
|
405
442
|
var checkFreePattern = z2.string().regex(/^[A-Za-z0-9_.*/-]+$/, "path words with * and ** only, such as docs/**/*.md").refine(
|
|
@@ -425,13 +462,13 @@ var mergeSchema = z2.strictObject({
|
|
|
425
462
|
*/
|
|
426
463
|
withoutChecks: z2.array(checkFreePattern).default([])
|
|
427
464
|
});
|
|
428
|
-
var
|
|
465
|
+
var guardFields = z2.strictObject({
|
|
429
466
|
/**
|
|
430
|
-
* The commands the merge guard hook asks the user about before they run,
|
|
431
|
-
* a wrapper or `sh -c`. `gh api
|
|
432
|
-
* guard off.
|
|
467
|
+
* The commands the merge guard hook asks the user about before they run, where they stand at a program position, also
|
|
468
|
+
* behind options before the verb, a chain, a wrapper or `sh -c`. `gh api`, when listed, asks only for a write to a
|
|
469
|
+
* merge, a file, a dispatch, a rerun or a ref. An empty list switches the guard off.
|
|
433
470
|
*/
|
|
434
|
-
commands: z2.array(guardedCommand).default(["gh pr merge", "gh workflow run"
|
|
471
|
+
commands: z2.array(guardedCommand).default(["gh pr merge", "gh workflow run"]),
|
|
435
472
|
selfMerge: z2.strictObject({
|
|
436
473
|
/** Whether `/orchestrate` offers "merge it yourself after green checks" in the pull request question; off also stops the hook honouring such an answer. */
|
|
437
474
|
offer: z2.boolean().default(true),
|
|
@@ -442,28 +479,39 @@ var guardSchema = z2.strictObject({
|
|
|
442
479
|
/** How long the approval gate hook holds a lane's call for the coordinator's dialog before it denies it; below the hook's 10 minutes. */
|
|
443
480
|
waitMinutes: z2.number().int().min(1).max(9).default(8)
|
|
444
481
|
}).prefault({}),
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
}).prefault({})
|
|
482
|
+
/**
|
|
483
|
+
* Paths that always ask, on top of the ones jackherd brings (`.github/**`, `**\/migrations/**`, `**\/.env*`,
|
|
484
|
+
* `.claude/**`, `.jackherd/**`, `bunfig.toml`, `**\/CLAUDE.md`, `**\/AGENTS.md`, `.agents/**`, `.mcp.json`,
|
|
485
|
+
* `.husky/**`, `lefthook.yml`, the lockfiles of bun, npm, pnpm and yarn, `**\/.npmrc`, `**\/CODEOWNERS`): a
|
|
486
|
+
* `merge.withoutChecks` pattern never opens them, and the watcher never starts parallel work on them by itself. They
|
|
487
|
+
* add to that list, never replace it. A standing approval covers them; only workflows, migrations and env files ask.
|
|
488
|
+
*/
|
|
489
|
+
alwaysAsk: z2.array(alwaysAskPattern).default([]),
|
|
490
|
+
/**
|
|
491
|
+
* Checks that must be among the head commit's and green before a standing approval lets a merge pass, by their names
|
|
492
|
+
* on GitHub. Unset, the merge needs at least one check and every one green; set, also each of these. Either way
|
|
493
|
+
* GitHub must be able to merge it.
|
|
494
|
+
*/
|
|
495
|
+
requiredChecks: z2.array(checkName).optional(),
|
|
496
|
+
/** Read until 2.0.0 as `alwaysAsk` (`exceptPaths`) and `requiredChecks`; the plan approval it was for is gone. */
|
|
497
|
+
planApproval: z2.strictObject({ exceptPaths: z2.array(alwaysAskPattern).optional(), requiredChecks: z2.array(checkName).optional() }).optional().meta({ deprecated: true })
|
|
462
498
|
});
|
|
499
|
+
var guardWithoutPlanApproval = ({ planApproval, ...guard }) => {
|
|
500
|
+
const requiredChecks = guard.requiredChecks ?? planApproval?.requiredChecks;
|
|
501
|
+
return {
|
|
502
|
+
...guard,
|
|
503
|
+
alwaysAsk: [...guard.alwaysAsk, ...planApproval?.exceptPaths ?? []],
|
|
504
|
+
...requiredChecks === void 0 ? {} : { requiredChecks }
|
|
505
|
+
};
|
|
506
|
+
};
|
|
507
|
+
var guardSchema = guardFields.transform(guardWithoutPlanApproval);
|
|
463
508
|
var hintsSchema = z2.strictObject({
|
|
464
509
|
/** Whether the `ask-hint` hook points out a last message that ends in a question while the turn used no `AskUserQuestion`; a lane session is never hinted. */
|
|
465
|
-
askUserQuestion: z2.boolean().default(true)
|
|
510
|
+
askUserQuestion: z2.boolean().default(true),
|
|
511
|
+
/** Whether the `task-hint` hook points a lane that edits a file without an open task signal to `jh task start`, once per task. */
|
|
512
|
+
taskSignal: z2.boolean().default(true)
|
|
466
513
|
});
|
|
514
|
+
var DEFAULT_TASK_SIGNAL_MINUTES = 10;
|
|
467
515
|
var contextSchema = z2.strictObject({
|
|
468
516
|
/** Tokens in a lane's context from which its fill shows yellow and `/implement` offers a new chat. */
|
|
469
517
|
warnTokens: tokenCount.default(25e4),
|
|
@@ -517,6 +565,11 @@ var jackherdConfigSchema = z2.strictObject({
|
|
|
517
565
|
}).prefault({}),
|
|
518
566
|
models: z2.record(z2.enum(Object.values(MODEL_ROLE)), modelId),
|
|
519
567
|
terminal: z2.strictObject({ adapter: z2.enum(Object.values(TERMINAL_ADAPTER)).default(TERMINAL_ADAPTER.none) }).prefault({}),
|
|
568
|
+
/** How the coordinator shows a draft for review (`orchestrate` § 4). */
|
|
569
|
+
preview: z2.strictObject({
|
|
570
|
+
/** Publish the draft as a private Artifact when the session has the tool; `false` sends a file card or `jh preview --open` instead. */
|
|
571
|
+
publish: z2.boolean().default(true)
|
|
572
|
+
}).prefault({}),
|
|
520
573
|
plans: z2.strictObject({
|
|
521
574
|
/** Minutes between background fetches of the base branch while `jh herd` runs; 0 turns them off. */
|
|
522
575
|
fetchIntervalMinutes: z2.number().min(0).default(5),
|
|
@@ -533,6 +586,8 @@ var jackherdConfigSchema = z2.strictObject({
|
|
|
533
586
|
/**
|
|
534
587
|
* Read by `jh watch`; without `deploy` it reports no deploy runs. `feedback` is on unless set to `false`:
|
|
535
588
|
* it reports new feedback on the lanes' pull requests except from the account the watcher runs as and `ignore`.
|
|
589
|
+
* `parallelTasks.maxTokens` caps the cost of the extra agents in a proposal of parallel tasks at the heartbeat.
|
|
590
|
+
* `taskSignalMinutes` is how long a lane works without a task signal before it reports `no task signal`, once per phase.
|
|
536
591
|
* `checks` reads the failed tests of a red lane pull request (`failedTestPattern`), also with `feedback` off.
|
|
537
592
|
* Without `previews` it reads no deploy preview; with it, one `preview` per new head commit. Each of `sources` is a
|
|
538
593
|
* command whose JSON output reports `source` for a new or changed entry.
|
|
@@ -541,6 +596,8 @@ var jackherdConfigSchema = z2.strictObject({
|
|
|
541
596
|
deploy: deploySchema.optional(),
|
|
542
597
|
feedback: z2.union([z2.literal(false), feedbackSchema]).prefault({}),
|
|
543
598
|
checks: checksSchema.prefault({}),
|
|
599
|
+
parallelTasks: parallelTasksSchema.prefault({}),
|
|
600
|
+
taskSignalMinutes: z2.number().int().positive().default(DEFAULT_TASK_SIGNAL_MINUTES),
|
|
544
601
|
previews: previewsSchema.optional(),
|
|
545
602
|
sources: z2.array(sourceSchema).refine(hasUniqueNames, "source names must be unique").default([])
|
|
546
603
|
}).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
|
|
@@ -59,6 +65,22 @@ declare const APPOINTMENT_VOCABULARY: Record<PlanLanguage, {
|
|
|
59
65
|
statusAlso: readonly string[];
|
|
60
66
|
open: readonly string[];
|
|
61
67
|
}>;
|
|
68
|
+
/**
|
|
69
|
+
* The board section of what waits on the user's answer (a sent preview, an open question, a report to accept): its
|
|
70
|
+
* heading, the header names of its columns (since, item, origin, status, found by name in any order; the status
|
|
71
|
+
* column may also carry a name of `statusAlso`) and the lower-case words a status cell of a still open item starts with.
|
|
72
|
+
*/
|
|
73
|
+
declare const WAITING_VOCABULARY: Record<PlanLanguage, {
|
|
74
|
+
heading: string;
|
|
75
|
+
columns: {
|
|
76
|
+
since: string;
|
|
77
|
+
item: string;
|
|
78
|
+
origin: string;
|
|
79
|
+
status: string;
|
|
80
|
+
};
|
|
81
|
+
statusAlso: readonly string[];
|
|
82
|
+
open: readonly string[];
|
|
83
|
+
}>;
|
|
62
84
|
declare const isPlanLanguage: (value: string) => value is PlanLanguage;
|
|
63
85
|
declare const PLAN_STATUS: {
|
|
64
86
|
readonly done: "done";
|
|
@@ -348,7 +370,8 @@ declare const withoutRepeats: (lane: SprintLane, step: PlanStep) => PlanStep;
|
|
|
348
370
|
/**
|
|
349
371
|
* The lanes of the sprint document's lanes table (`## Spuren` in `de`) in table order. A row whose
|
|
350
372
|
* first cell names a lane in backticks belongs to that lane; a `↳` row without a name continues
|
|
351
|
-
* the lane above it.
|
|
373
|
+
* the lane above it. A cleaned-up lane stays closed through its `↳` rows, but a later row that names it again
|
|
374
|
+
* without the mark starts the lane anew: the closed rows are history and drop out.
|
|
352
375
|
*/
|
|
353
376
|
declare const parseSprintLanes: (markdown: string, language: PlanLanguage) => SprintLane[];
|
|
354
377
|
declare const sprintTitleOf: (markdown: string, fallback: string) => string;
|
|
@@ -483,6 +506,39 @@ declare const activeItem: (phase: Pick<PhaseProgress, "items">) => ChecklistItem
|
|
|
483
506
|
/** 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. */
|
|
484
507
|
declare const shortTitle: (progress: LaneProgress) => string | null;
|
|
485
508
|
|
|
509
|
+
/** Characters a task's short name may have, so the herd shows it whole. */
|
|
510
|
+
declare const SHORT_NAME_LIMIT = 40;
|
|
511
|
+
/** Characters a phase title may have; the title is the phase's name. */
|
|
512
|
+
declare const PHASE_TITLE_LIMIT = 50;
|
|
513
|
+
declare const SHORT_NAME_PROBLEM: {
|
|
514
|
+
readonly missing: "missing";
|
|
515
|
+
readonly long: "long";
|
|
516
|
+
};
|
|
517
|
+
/** What is wrong with an open task's short name: none at all, or one of `length` characters over the limit. */
|
|
518
|
+
type ShortNameProblem = {
|
|
519
|
+
problem: typeof SHORT_NAME_PROBLEM.missing;
|
|
520
|
+
} | {
|
|
521
|
+
problem: typeof SHORT_NAME_PROBLEM.long;
|
|
522
|
+
length: number;
|
|
523
|
+
};
|
|
524
|
+
/** An open task (1-based line) of phase `phase` whose short name is missing or too long. */
|
|
525
|
+
type ShortNameFinding = {
|
|
526
|
+
line: number;
|
|
527
|
+
phase: string;
|
|
528
|
+
} & ShortNameProblem;
|
|
529
|
+
/** A phase heading whose title has `length` characters, over the limit. */
|
|
530
|
+
type LongPhaseTitle = PhaseHeading & {
|
|
531
|
+
length: number;
|
|
532
|
+
};
|
|
533
|
+
/**
|
|
534
|
+
* The open tasks a change wrote (a line not in `previousLines`) that do not start with `**Name**:` or whose name is
|
|
535
|
+
* longer than `SHORT_NAME_LIMIT`. Only tasks of a phase section before its review gate count: a gate item keeps its
|
|
536
|
+
* own form.
|
|
537
|
+
*/
|
|
538
|
+
declare const shortNameFindings: (markdown: string, language: PlanLanguage, previousLines: ReadonlySet<string>) => ShortNameFinding[];
|
|
539
|
+
/** The phase headings a change wrote whose title is longer than `PHASE_TITLE_LIMIT`. */
|
|
540
|
+
declare const longPhaseTitles: (markdown: string, language: PlanLanguage, previousLines: ReadonlySet<string>) => LongPhaseTitle[];
|
|
541
|
+
|
|
486
542
|
/** What a sprint document says around its lanes table: its goal and its history. */
|
|
487
543
|
type SprintPlanRow = {
|
|
488
544
|
cells: string[];
|
|
@@ -512,4 +568,4 @@ declare const readSprintDocument: (markdown: string, language: PlanLanguage) =>
|
|
|
512
568
|
*/
|
|
513
569
|
declare const readPhaseDependencies: (planMarkdown: string, language: PlanLanguage) => Record<string, string>;
|
|
514
570
|
|
|
515
|
-
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,
|
|
571
|
+
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, 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,18 +2,20 @@ import {
|
|
|
2
2
|
ADDRESS_FORM,
|
|
3
3
|
APPOINTMENT_VOCABULARY,
|
|
4
4
|
APPROVAL_TEXTS,
|
|
5
|
+
LANE_PROMPT_OPENING,
|
|
5
6
|
LANGUAGE_NAME,
|
|
6
|
-
PLAN_APPROVAL_LABELS,
|
|
7
7
|
PLAN_LANGUAGE,
|
|
8
8
|
PLAN_STATUS,
|
|
9
9
|
PLAN_VOCABULARY,
|
|
10
10
|
RESPONSE_ADDRESS,
|
|
11
11
|
SELF_MERGE_LABEL,
|
|
12
|
+
STANDING_APPROVAL_TEXTS,
|
|
13
|
+
WAITING_VOCABULARY,
|
|
12
14
|
classifyStatus,
|
|
13
15
|
isPlanLanguage,
|
|
14
16
|
phaseHeading,
|
|
15
17
|
sectionHeading
|
|
16
|
-
} from "./chunk-
|
|
18
|
+
} from "./chunk-5MQBCUQ5.js";
|
|
17
19
|
|
|
18
20
|
// src/plan-format/lanes.ts
|
|
19
21
|
import { posix } from "path";
|
|
@@ -100,6 +102,8 @@ var CONTINUATION = "\u21B3";
|
|
|
100
102
|
var PLAN_LINK = /\[([^\]]+)\]\(([^)]+)\)/;
|
|
101
103
|
var PHASE_ID_PREFIX = "P";
|
|
102
104
|
var phaseLabel = (id) => `${PHASE_ID_PREFIX}${id}`;
|
|
105
|
+
var MAX_RANGE = 50;
|
|
106
|
+
var spannedPhaseIds = (first, last) => first > last || last - first >= MAX_RANGE ? [] : Array.from({ length: last - first + 1 }, (_, offset) => String(first + offset));
|
|
103
107
|
var phaseLabels = (ids, separator) => ids.map(phaseLabel).join(separator);
|
|
104
108
|
var PHASES_COLUMN = 3;
|
|
105
109
|
var CLAUSE_SEPARATOR = /([;,]|\.(?=\s))\s*/;
|
|
@@ -109,10 +113,18 @@ var phaseIdPatterns = (language) => {
|
|
|
109
113
|
const id = `(?:\\*\\*)?${PHASE_ID_PREFIX}(\\d+[a-z]?)\\b`;
|
|
110
114
|
return {
|
|
111
115
|
leading: new RegExp(`^\\s*(?:(?:${connectors})\\s+)*${id}`),
|
|
116
|
+
rangeEnd: new RegExp(`^\\s*[\u2013-]\\s*(?:\\*\\*)?${PHASE_ID_PREFIX}(\\d+)\\b`),
|
|
112
117
|
afterConnector: new RegExp(`^\\s*(?:${connectors})\\s`),
|
|
113
118
|
connector: new RegExp(`\\b(?:${connectors})\\s+${id}`, "g")
|
|
114
119
|
};
|
|
115
120
|
};
|
|
121
|
+
var leadingIds = (clause, leading, patterns) => {
|
|
122
|
+
const first = leading[1];
|
|
123
|
+
const end = clause.slice(leading[0].length).match(patterns.rangeEnd);
|
|
124
|
+
if (!end || !/^\d+$/.test(first)) return [first];
|
|
125
|
+
const spanned = spannedPhaseIds(Number(first), Number(end[1]));
|
|
126
|
+
return spanned.length > 0 ? spanned : [first];
|
|
127
|
+
};
|
|
116
128
|
var phaseIdsOf = (cell, patterns) => {
|
|
117
129
|
const parts = cell.split(CLAUSE_SEPARATOR);
|
|
118
130
|
const ids = [];
|
|
@@ -125,7 +137,7 @@ var phaseIdsOf = (cell, patterns) => {
|
|
|
125
137
|
const leading = clause.match(patterns.leading);
|
|
126
138
|
const startsEntry = entryStart || listRunning || patterns.afterConnector.test(clause);
|
|
127
139
|
const counts = leading !== null && startsEntry;
|
|
128
|
-
if (leading && counts) ids.push(leading
|
|
140
|
+
if (leading && counts) ids.push(...leadingIds(clause, leading, patterns));
|
|
129
141
|
ids.push(...[...clause.matchAll(patterns.connector)].map((match) => match[1]));
|
|
130
142
|
listRunning ||= counts;
|
|
131
143
|
}
|
|
@@ -162,7 +174,12 @@ var parseSprintLanes = (markdown, language) => {
|
|
|
162
174
|
if (!name) continue;
|
|
163
175
|
const port = Number.parseInt(cells[1] ?? "", 10);
|
|
164
176
|
const lane = lanes.get(name) ?? { name, port: Number.isNaN(port) ? null : port, steps: [], closed: false };
|
|
165
|
-
|
|
177
|
+
const marked = (cells[0] ?? "").includes(laneClosed);
|
|
178
|
+
if (lane.closed && !marked && !(cells[0] ?? "").startsWith(CONTINUATION)) {
|
|
179
|
+
lane.closed = false;
|
|
180
|
+
lane.steps = [];
|
|
181
|
+
}
|
|
182
|
+
lane.closed ||= marked;
|
|
166
183
|
lane.steps.push(withoutRepeats(lane, toStep(cells, phaseId)));
|
|
167
184
|
lanes.set(name, lane);
|
|
168
185
|
previous = name;
|
|
@@ -451,7 +468,6 @@ var GLOB_CHARACTER = /[*?{[]/;
|
|
|
451
468
|
var VERSION = /^v?\d+(?:[.\-+][\w.-]*)?$/i;
|
|
452
469
|
var PACKAGE_SCOPE = /^@[^/]+\//;
|
|
453
470
|
var RELEASE_FILE = /(?:^|\/)(?:CHANGELOG\.md|package\.json)$/;
|
|
454
|
-
var MAX_RANGE = 50;
|
|
455
471
|
var phaseIdOf = (cell) => PHASE_ID.exec(cell)?.[1]?.toLowerCase() ?? null;
|
|
456
472
|
var splitOutsideParentheses = (cell, separator) => {
|
|
457
473
|
const entries = [];
|
|
@@ -470,9 +486,7 @@ var splitOutsideParentheses = (cell, separator) => {
|
|
|
470
486
|
var rangeOf = (entry) => {
|
|
471
487
|
const match = PHASE_RANGE.exec(entry);
|
|
472
488
|
if (!match) return null;
|
|
473
|
-
|
|
474
|
-
if (first > last || last - first >= MAX_RANGE) return [];
|
|
475
|
-
return Array.from({ length: last - first + 1 }, (_, offset) => String(first + offset));
|
|
489
|
+
return spannedPhaseIds(Number(match[1]), Number(match[2]));
|
|
476
490
|
};
|
|
477
491
|
var dependsOf = (cell, language) => {
|
|
478
492
|
const noDependency = PLAN_VOCABULARY[language].noDependency;
|
|
@@ -560,12 +574,13 @@ var phasesWithoutDependencyRow = (markdown, language) => {
|
|
|
560
574
|
};
|
|
561
575
|
var longestChain = (chains) => chains.reduce((longest, chain) => chain.length > longest.length ? chain : longest, []);
|
|
562
576
|
var mergeSets = (sets) => sets.some((set) => set === null) ? null : sets.flatMap((set) => set ?? []);
|
|
563
|
-
var
|
|
577
|
+
var writeSetWithin = (row, byId, chain) => {
|
|
564
578
|
if (chain.includes(row.id)) return null;
|
|
565
579
|
const targets = row.writesLike.map((id) => byId.get(id));
|
|
566
|
-
const like = mergeSets(targets.map((target) => target ?
|
|
580
|
+
const like = mergeSets(targets.map((target) => target ? writeSetWithin(target, byId, [...chain, row.id]) : null));
|
|
567
581
|
return like === null ? null : [...row.writes, ...like];
|
|
568
582
|
};
|
|
583
|
+
var writeSetOf = (row, byId) => writeSetWithin(row, byId, []);
|
|
569
584
|
var overlaps = (left, right) => left === "" || right === "" || left === right || left.startsWith(`${right}/`) || right.startsWith(`${left}/`);
|
|
570
585
|
var sharesEntry = (first, second) => {
|
|
571
586
|
const entries = new Set(second.map((entry) => entry.trim().toLowerCase()));
|
|
@@ -574,7 +589,7 @@ var sharesEntry = (first, second) => {
|
|
|
574
589
|
var sharesMarker = (first, second) => sharesEntry(first.serialMarkers, second.serialMarkers) || sharesEntry(first.releases, second.releases);
|
|
575
590
|
var collides = (first, second) => sharesMarker(first, second) || first.writes.some((path) => second.writes.some((other) => overlaps(path, other)));
|
|
576
591
|
var candidateOf = (row, byId) => {
|
|
577
|
-
const writes = writeSetOf(row, byId
|
|
592
|
+
const writes = writeSetOf(row, byId);
|
|
578
593
|
if (writes === null || writes.length === 0) return [];
|
|
579
594
|
const releases = [.../* @__PURE__ */ new Set([...row.releases, ...releasedByFiles(writes)])];
|
|
580
595
|
return [{ id: row.id, writes, serialMarkers: row.serialMarkers, releases }];
|
|
@@ -811,6 +826,38 @@ var boardRowsWithoutPhase = (markdown, language, previousLines) => changedOpenLa
|
|
|
811
826
|
({ phases }) => phaseIdsOfCell(phases, language).length === 0
|
|
812
827
|
);
|
|
813
828
|
|
|
829
|
+
// src/plan-format/short-names.ts
|
|
830
|
+
var SHORT_NAME_LIMIT = 40;
|
|
831
|
+
var PHASE_TITLE_LIMIT = 50;
|
|
832
|
+
var SHORT_NAME_PROBLEM = { missing: "missing", long: "long" };
|
|
833
|
+
var SHORT_NAME = /^\s*[-*]\s+\[ \]\s+\*\*(.+?)\*\*:/;
|
|
834
|
+
var characterCount = (text) => [...text].length;
|
|
835
|
+
var shortNameProblem = (line) => {
|
|
836
|
+
const name = line.match(SHORT_NAME)?.[1];
|
|
837
|
+
if (name === void 0) return { problem: SHORT_NAME_PROBLEM.missing };
|
|
838
|
+
const length = characterCount(name);
|
|
839
|
+
return length > SHORT_NAME_LIMIT ? { problem: SHORT_NAME_PROBLEM.long, length } : null;
|
|
840
|
+
};
|
|
841
|
+
var shortNameFindings = (markdown, language, previousLines) => {
|
|
842
|
+
const headings = new Map(phaseHeadings(markdown, language).map((heading) => [heading.line, heading.id]));
|
|
843
|
+
const found = [];
|
|
844
|
+
let phase = null;
|
|
845
|
+
for (const [index, line] of markdown.split("\n").entries()) {
|
|
846
|
+
const heading = headings.get(index + 1);
|
|
847
|
+
if (heading !== void 0) phase = heading;
|
|
848
|
+
else if (endsPhase(line) || isReviewGateLine(line)) phase = null;
|
|
849
|
+
else if (phase !== null && isOpenCheckboxLine(line) && !previousLines.has(line)) {
|
|
850
|
+
const problem = shortNameProblem(line);
|
|
851
|
+
if (problem) found.push({ line: index + 1, phase, ...problem });
|
|
852
|
+
}
|
|
853
|
+
}
|
|
854
|
+
return found;
|
|
855
|
+
};
|
|
856
|
+
var longPhaseTitles = (markdown, language, previousLines) => {
|
|
857
|
+
const lines = markdown.split("\n");
|
|
858
|
+
return phaseHeadings(markdown, language).filter(({ line }) => !previousLines.has(lines[line - 1] ?? "")).map((heading) => ({ ...heading, length: characterCount(heading.title) })).filter(({ length }) => length > PHASE_TITLE_LIMIT);
|
|
859
|
+
};
|
|
860
|
+
|
|
814
861
|
// src/plan-format/sprint-document.ts
|
|
815
862
|
var PHASE_ID_IN_CELL = /^(\d+[a-z]?)\b/;
|
|
816
863
|
var paragraphs = (lines) => lines.join("\n").split(/\n\s*\n/).map((paragraph) => paragraph.trim()).filter(Boolean);
|
|
@@ -853,16 +900,21 @@ export {
|
|
|
853
900
|
ADDRESS_FORM,
|
|
854
901
|
APPOINTMENT_VOCABULARY,
|
|
855
902
|
APPROVAL_TEXTS,
|
|
903
|
+
LANE_PROMPT_OPENING,
|
|
856
904
|
LANGUAGE_NAME,
|
|
857
905
|
PHASE_CELL_LIMIT,
|
|
858
906
|
PHASE_ID_PREFIX,
|
|
859
|
-
|
|
907
|
+
PHASE_TITLE_LIMIT,
|
|
860
908
|
PLAN_LANGUAGE,
|
|
861
909
|
PLAN_STATUS,
|
|
862
910
|
PLAN_VOCABULARY,
|
|
863
911
|
PROSE_REASON,
|
|
864
912
|
RESPONSE_ADDRESS,
|
|
865
913
|
SELF_MERGE_LABEL,
|
|
914
|
+
SHORT_NAME_LIMIT,
|
|
915
|
+
SHORT_NAME_PROBLEM,
|
|
916
|
+
STANDING_APPROVAL_TEXTS,
|
|
917
|
+
WAITING_VOCABULARY,
|
|
866
918
|
activeItem,
|
|
867
919
|
boardRowsWithProse,
|
|
868
920
|
boardRowsWithoutPhase,
|
|
@@ -887,6 +939,7 @@ export {
|
|
|
887
939
|
isTickedUnmerged,
|
|
888
940
|
laneNameOf,
|
|
889
941
|
laneProgress,
|
|
942
|
+
longPhaseTitles,
|
|
890
943
|
mergedPhasesWithOpenBoxes,
|
|
891
944
|
misplacedDeferrals,
|
|
892
945
|
newPhasesWithoutReviewGate,
|
|
@@ -912,6 +965,7 @@ export {
|
|
|
912
965
|
readSprintDocument,
|
|
913
966
|
resolvePlanPath,
|
|
914
967
|
sectionHeading,
|
|
968
|
+
shortNameFindings,
|
|
915
969
|
shortTitle,
|
|
916
970
|
sprintTitleOf,
|
|
917
971
|
sumPhases,
|