@joshuafolkken/kit 1.1010.0 → 1.1050.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/.claude/settings.json +1 -5
- package/.claude/skills/dependency-update/SKILL.md +6 -3
- package/.claude/skills/epic-commands/SKILL.md +46 -17
- package/.claude/skills/verify-ui/SKILL.md +1 -1
- package/.claude/skills/workflow-commands/SKILL.md +10 -25
- package/.claude/skills/workflow-commands/background-commands.md +1 -1
- package/.claude/skills/workflow-commands/backlogrun-child.md +6 -4
- package/.claude/skills/workflow-commands/backlogrun-lanes.md +1 -1
- package/.claude/skills/workflow-commands/backlogrun-park.md +6 -0
- package/.claude/skills/workflow-commands/backlogrun-progress.md +30 -36
- package/.claude/skills/workflow-commands/backlogrun-steps.md +16 -16
- package/.claude/skills/workflow-commands/backlogrun.md +3 -12
- package/.claude/skills/workflow-commands/chain-rule.md +2 -2
- package/.claude/skills/workflow-commands/followup-reference.md +2 -2
- package/.claude/skills/workflow-commands/fullrun-steps.md +4 -3
- package/.claude/skills/workflow-commands/fullrun.md +1 -1
- package/.claude/skills/workflow-commands/halfrun.md +2 -1
- package/.claude/skills/workflow-commands/into-target.md +1 -1
- package/.claude/skills/workflow-commands/issue-comments.md +1 -1
- package/.claude/skills/workflow-commands/observation-filing.md +80 -126
- package/.claude/skills/workflow-commands/pre-gate-cut.md +33 -36
- package/.claude/skills/workflow-commands/prerequisite.md +1 -1
- package/.claude/skills/workflow-commands/working-tree-hold.md +2 -2
- package/.codex/hooks.json +1 -5
- package/.github/workflows/auto-tag.yml +21 -0
- package/.github/workflows/publish.yml +9 -10
- package/.github/workflows/sonar-qube.yml +1 -1
- package/AGENTS.md +4 -25
- package/CLAUDE.md +24 -38
- package/GEMINI.md +4 -24
- package/README.md +22 -56
- package/dist/CLAUDE.md +24 -38
- package/dist/hooks/batch-guard.js +4 -4
- package/dist/hooks/{chunk-6PSTVOLH.js → chunk-5BK3K7V4.js} +114 -90
- package/dist/hooks/{chunk-PAXMLGSS.js → chunk-7VPDUBRN.js} +93 -175
- package/dist/hooks/{chunk-KPZN42RT.js → chunk-DMQN45FN.js} +3 -3
- package/dist/hooks/{chunk-WMIR5SNX.js → chunk-EJDX2TOB.js} +13 -3
- package/dist/hooks/{chunk-A2H5I2VD.js → chunk-GQ7KP45M.js} +2 -2
- package/dist/hooks/{chunk-4GDR2YTV.js → chunk-HJIISEAC.js} +4 -4
- package/dist/hooks/{chunk-AWTFKKFW.js → chunk-NAS4C7GP.js} +6 -33
- package/dist/hooks/{chunk-56LGMJJV.js → chunk-NJQOBE6L.js} +2 -2
- package/dist/hooks/{chunk-EZLHXKJ6.js → chunk-RLDX5SOC.js} +1222 -941
- package/dist/hooks/{chunk-GBJUERYK.js → chunk-WQ4OSBSS.js} +10 -3
- package/dist/hooks/codex-hook-adapter.js +10 -10
- package/dist/hooks/duplicate-read-guard.js +5 -5
- package/dist/hooks/format-edited.js +15 -34
- package/dist/hooks/investigation-guard.js +5 -5
- package/dist/hooks/pretool-guard.js +23 -16
- package/dist/hooks/stop-guard.js +21 -200
- package/dist/josh.js +42 -125
- package/dist/version/index.js +466 -42
- package/eslint/base.js +2 -11
- package/package.json +36 -10
- package/prompts/coding-standards.md +15 -290
- package/prompts/collaboration-workflow/cross-repo-epic.md +2 -51
- package/prompts/collaboration-workflow/epic-audit.md +2 -53
- package/prompts/collaboration-workflow/epic-bundle.md +2 -106
- package/prompts/collaboration-workflow/file-edits.md +6 -53
- package/prompts/collaboration-workflow/issue-template.md +4 -4
- package/prompts/collaboration-workflow/operating-rules.md +18 -75
- package/prompts/collaboration-workflow/principles.md +74 -0
- package/prompts/collaboration-workflow/residency.md +55 -15
- package/prompts/collaboration-workflow/rule-delivery.md +5 -46
- package/prompts/collaboration-workflow/shell-body.md +7 -9
- package/prompts/collaboration-workflow/turn-batching.md +21 -37
- package/prompts/collaboration-workflow/upstream-interrupt.md +1 -1
- package/prompts/collaboration-workflow/wip-cap.md +15 -18
- package/prompts/collaboration-workflow.md +22 -28
- package/prompts/sonar-hotspot-handling.md +1 -1
- package/scripts/adopt/adopt-logic.ts +2 -2
- package/scripts/adopt/adopt-toolkits.ts +3 -1
- package/scripts/adopt/adopt.ts +2 -2
- package/scripts/agent/agent-event.ts +2 -9
- package/scripts/agent/agent-exit-record.ts +2 -9
- package/scripts/auto-ok/auto-ok-cli.ts +9 -9
- package/scripts/backlog/backlog-budget-cli.ts +2 -6
- package/scripts/backlog/backlog-drive-cli.ts +20 -10
- package/scripts/backlog/backlog-drive.ts +50 -16
- package/scripts/backlog/backlog-next.ts +24 -8
- package/scripts/backlog/backlog-offer-cli.ts +6 -5
- package/scripts/backlog/backlog-plan-cli.ts +5 -1
- package/scripts/backlog/backlog-plan.ts +1 -1
- package/scripts/backlog/backlog-pool.ts +31 -11
- package/scripts/backlog/backlog-rank.ts +106 -0
- package/scripts/backlog/backlog-waves.ts +42 -29
- package/scripts/build/build-bin.ts +2 -1
- package/scripts/build/build-config-merge.ts +2 -1
- package/scripts/build/build-hooks.ts +31 -6
- package/scripts/build/build-managed-marker.ts +2 -1
- package/scripts/build/build-self-sync-guard.ts +2 -1
- package/scripts/build/build-version.ts +2 -1
- package/scripts/cases/cases-logic.ts +2 -6
- package/scripts/ci/pr-classification.ts +13 -11
- package/scripts/cost-runtime/cost-transcript.ts +4 -11
- package/scripts/delegation/delegation-policy.ts +11 -0
- package/scripts/doctor/doctor-consumer.ts +5 -12
- package/scripts/doctor/doctor-ports.ts +2 -2
- package/scripts/document/document-byte-budget.ts +13 -21
- package/scripts/document/document-section.ts +2 -6
- package/scripts/document/entry-read-budget.ts +6 -5
- package/scripts/{git/git-epic-add-body.ts → epic/epic-add-body.ts} +23 -23
- package/scripts/{git/git-epic-add-plan.ts → epic/epic-add-plan.ts} +23 -26
- package/scripts/{git/git-epic-add.ts → epic/epic-add.ts} +17 -17
- package/scripts/epic/epic-audit-cli.ts +4 -4
- package/scripts/epic/epic-audit-orphans.ts +2 -2
- package/scripts/epic/epic-audit-rationale.ts +1 -1
- package/scripts/epic/epic-audit.ts +1 -1
- package/scripts/{git/git-epic-body.ts → epic/epic-body.ts} +6 -6
- package/scripts/epic/epic-bundle-cli.ts +1 -1
- package/scripts/epic/epic-bundle.ts +2 -2
- package/scripts/epic/epic-candidate-confirm.ts +2 -1
- package/scripts/{git/git-epic-chains.ts → epic/epic-chains.ts} +4 -4
- package/{scripts-ai → scripts/epic}/epic-check.ts +5 -4
- package/{scripts-ai → scripts/epic}/epic-cli.ts +4 -4
- package/scripts/{git/git-epic-close-comment.ts → epic/epic-close-comment.ts} +9 -9
- package/scripts/{git/git-epic-close.ts → epic/epic-close.ts} +23 -19
- package/scripts/epic/epic-cross-repo.ts +21 -38
- package/scripts/{git/git-epic-decision.ts → epic/epic-decision.ts} +12 -14
- package/scripts/epic/epic-fetch.ts +3 -3
- package/scripts/epic/epic-graph.ts +3 -3
- package/scripts/epic/epic-index.ts +2 -2
- package/scripts/epic/epic-issue.ts +1 -1
- package/scripts/epic/epic-lane-offer.ts +11 -22
- package/scripts/epic/epic-next.ts +3 -3
- package/scripts/{git/git-epic-parse.ts → epic/epic-parse.ts} +3 -3
- package/scripts/{git/git-epic-promote.ts → epic/epic-promote.ts} +10 -10
- package/scripts/epic/epic-rank.ts +37 -0
- package/scripts/{git/git-epic-read.ts → epic/epic-read.ts} +9 -9
- package/scripts/{git/git-epic-reconcile.ts → epic/epic-reconcile.ts} +18 -20
- package/scripts/{git/git-epic-reference.ts → epic/epic-reference.ts} +4 -23
- package/scripts/epic/epic-relation-recheck.ts +4 -3
- package/scripts/{git/git-epic-relations.ts → epic/epic-relations.ts} +5 -5
- package/scripts/{git/git-epic-remove-plan.ts → epic/epic-remove-plan.ts} +17 -19
- package/scripts/{git/git-epic-remove.ts → epic/epic-remove.ts} +13 -13
- package/scripts/{git/git-epic-run.ts → epic/epic-run.ts} +27 -27
- package/scripts/{git/git-epic-sections.ts → epic/epic-sections.ts} +6 -6
- package/scripts/{git/git-epic-shape.ts → epic/epic-shape.ts} +7 -7
- package/scripts/epic/epic-solo.ts +29 -28
- package/scripts/{git/git-epic-validate.ts → epic/epic-validate.ts} +13 -13
- package/{scripts-ai → scripts/epic}/epic.ts +17 -16
- package/scripts/gh/fix-gh-packages.ts +2 -1
- package/scripts/gh/gh-spawn.ts +12 -10
- package/scripts/git/constants.ts +1 -1
- package/scripts/git/git-branch.ts +10 -1
- package/scripts/git/git-command.ts +2 -2
- package/scripts/git/git-error.ts +2 -5
- package/scripts/git/git-execa-error.ts +6 -1
- package/{scripts-ai → scripts/git}/git-followup-finish.ts +7 -7
- package/scripts/git/git-followup-flush.ts +74 -48
- package/scripts/git/git-followup-issue-close.ts +2 -2
- package/scripts/git/git-followup-label.ts +1 -1
- package/scripts/git/git-followup-pending.ts +1 -1
- package/{scripts-ai → scripts/git}/git-followup-workflow.ts +23 -11
- package/scripts/git/git-gh-exec.ts +85 -26
- package/scripts/git/git-gh-issue-read.ts +2 -2
- package/scripts/git/git-gh-issue-rest.ts +2 -2
- package/scripts/git/git-gh-issue-write.ts +6 -6
- package/scripts/git/git-gh-pr-read.ts +0 -21
- package/scripts/git/git-gh-pr.ts +2 -23
- package/scripts/git/git-next-issues.ts +34 -5
- package/scripts/git/git-pr-followup-wrapup.ts +2 -6
- package/scripts/git/git-preflight.ts +25 -10
- package/scripts/git/git-push-transport.ts +12 -69
- package/scripts/git/git-spawn.ts +79 -3
- package/scripts/git/git-ssh-keepalive.ts +74 -0
- package/scripts/git/git-staging.ts +21 -9
- package/{scripts-ai → scripts/git}/git-workflow.ts +11 -11
- package/scripts/git/github-issue-url.ts +1 -1
- package/scripts/git/gone-branch.ts +3 -2
- package/scripts/git/issue-labels.ts +13 -1
- package/scripts/git/issue-rank.ts +89 -0
- package/scripts/git/issue-reference.ts +19 -0
- package/scripts/git/main-merge.ts +2 -1
- package/scripts/git/main-sync.ts +3 -2
- package/scripts/git/schemas.ts +1 -1
- package/scripts/git/stash-pop-args.ts +7 -13
- package/scripts/git/stash-pop-cli.ts +2 -1
- package/{scripts-ai → scripts/git}/telegram-test-logic.ts +2 -2
- package/{scripts-ai → scripts/git}/telegram-test.ts +7 -7
- package/scripts/hooks/format-edited-cli.ts +15 -0
- package/scripts/hooks/format-edited-file.ts +2 -9
- package/scripts/hooks/format-rewrite-notice.ts +2 -8
- package/scripts/hooks/pretool-guard-cli.ts +21 -0
- package/scripts/hooks/pretool-guard.ts +14 -15
- package/scripts/init/init-ai-copy.ts +2 -5
- package/scripts/init/init.ts +8 -4
- package/scripts/init/start-exec.ts +7 -1
- package/scripts/init/start-plan.ts +18 -5
- package/scripts/init/start-setup-pr.ts +46 -19
- package/scripts/init/start-steps.ts +43 -10
- package/scripts/init/start.ts +42 -11
- package/scripts/issue/issue-cite.ts +19 -1
- package/scripts/issue/issue-comment-cli.ts +2 -1
- package/scripts/issue/issue-file-cli.ts +2 -1
- package/scripts/issue/issue-fold-cli.ts +9 -16
- package/scripts/issue/issue-scout-cli.ts +8 -13
- package/scripts/issue/measure-rerun-cli.ts +7 -6
- package/scripts/josh/josh-command-map.ts +0 -102
- package/scripts/josh/josh-commands-ai.ts +2 -2
- package/scripts/josh/josh-commands-backlog.ts +1 -1
- package/scripts/josh/josh-commands-development.ts +3 -3
- package/scripts/josh/josh-commands-project.ts +1 -1
- package/scripts/josh/josh-commands-workflow.ts +4 -4
- package/scripts/lane/lane-child-marker.ts +16 -2
- package/scripts/lane/lane-cli.ts +34 -15
- package/scripts/lane/lane-open.ts +3 -2
- package/scripts/lane/lane-registry.ts +3 -6
- package/scripts/lane/lane-report.ts +10 -4
- package/scripts/lib/cli-flags.ts +20 -2
- package/scripts/lib/error-message.ts +9 -0
- package/scripts/lib/read-file.ts +26 -2
- package/scripts/observations/observation-ledger-home.ts +98 -26
- package/scripts/observations/observation-ledger-migrate.ts +26 -21
- package/scripts/observations/observation-ledger-prepare.ts +10 -20
- package/scripts/observations/observation-ledger.ts +58 -25
- package/scripts/observations/observations-flush-cli.ts +6 -23
- package/scripts/observations/observations-flush-landing.ts +2 -1
- package/scripts/observations/observations-flush.ts +29 -20
- package/scripts/package/package-scout-cli.ts +8 -16
- package/scripts/ports/port-command.ts +2 -1
- package/scripts/propagate/propagate-plan.ts +185 -0
- package/scripts/propagate/propagate-publish.ts +6 -7
- package/scripts/propagate/propagate-run.ts +5 -1
- package/scripts/propagate/propagate-steps.ts +18 -2
- package/scripts/propagate/propagate-targets.ts +13 -2
- package/scripts/propagate/propagate.ts +63 -44
- package/scripts/refactor/import-graph.ts +2 -6
- package/scripts/registry-migration/migrate-logic.ts +1 -0
- package/scripts/registry-migration/migrate.ts +3 -2
- package/scripts/registry-migration/user-npmrc.ts +2 -2
- package/scripts/release/github-release.ts +1 -1
- package/scripts/release/release-cli.ts +2 -1
- package/scripts/repo/repo-setting.ts +10 -8
- package/scripts/review/review-attest.ts +44 -0
- package/scripts/review/review-brief-cli.ts +49 -2
- package/scripts/review/review-brief.ts +46 -1
- package/scripts/review/review-finding-ledger.ts +3 -3
- package/scripts/review/review-findings-cli.ts +4 -11
- package/scripts/review/review-record-cli.ts +14 -11
- package/scripts/review/review-record.ts +6 -16
- package/scripts/review/review-stamps.ts +7 -0
- package/scripts/rules/decision-oracle.ts +2 -2
- package/scripts/rules/implementation-cut.ts +1 -1
- package/scripts/rules/issue-fold.ts +1 -2
- package/scripts/rules/lane-carry-conflict.ts +1 -2
- package/scripts/rules/lane-park.ts +1 -2
- package/scripts/rules/pre-gate-cut.ts +2 -2
- package/scripts/rules/rule-body-guard.ts +2 -2
- package/scripts/rules/rule-value-cache.ts +99 -0
- package/scripts/rules/rule-value-cli.ts +10 -9
- package/scripts/rules/rule-value.ts +1 -1
- package/scripts/run/run-carry-args.ts +2 -6
- package/scripts/run/run-carry-cli.ts +5 -7
- package/scripts/run/run-cut-args.ts +7 -13
- package/scripts/run/run-ending-cli.ts +4 -7
- package/scripts/run/run-event-stream.ts +2 -5
- package/scripts/run/run-headless.ts +22 -6
- package/scripts/run/run-liveness-cli.ts +4 -7
- package/scripts/run/run-parent-cut-hook.ts +144 -0
- package/scripts/run/run-progress-args.ts +2 -6
- package/scripts/run/run-progress-cli.ts +2 -1
- package/scripts/run/run-progress-read.ts +2 -1
- package/scripts/run/run-progress.ts +21 -8
- package/scripts/run/run-ship-cli.ts +6 -74
- package/scripts/run/run-ship-preflight.ts +87 -0
- package/scripts/run/run-ship-review-steps.ts +5 -26
- package/scripts/run/run-ship-review.ts +9 -0
- package/scripts/run/run-ship-scoped.ts +44 -0
- package/scripts/run/run-ship-stage.ts +5 -1
- package/scripts/run/run-ship-steps.ts +105 -0
- package/scripts/run/run-ship.ts +2 -0
- package/scripts/run/run-tail-cli.ts +3 -3
- package/scripts/run/run-tidy-stashes.ts +7 -14
- package/scripts/run/run-wake-cli.ts +4 -8
- package/scripts/run/run-wake-describe.ts +19 -1
- package/scripts/run/run-wake-driver.ts +15 -2
- package/scripts/run/run-wake-session.ts +14 -4
- package/scripts/run/run-watcher-hook.ts +1 -1
- package/scripts/safe-chain/project-config.ts +3 -6
- package/scripts/security/security-audit-provision.ts +6 -7
- package/scripts/sonar/sonar-hotspots-cli.ts +2 -1
- package/scripts/sync/directory-copy-guard.ts +2 -1
- package/scripts/sync/sync-ai-files.ts +3 -3
- package/scripts/sync/template-source-logic.ts +3 -4
- package/scripts/test/e2e-retry-check.ts +3 -6
- package/scripts/time-runtime/time-bundle-call.ts +2 -2
- package/scripts/ui/ui-routes-cli.ts +3 -6
- package/scripts/version/bump-version.ts +2 -1
- package/scripts/version/npm-registry.ts +97 -0
- package/scripts/version/version-commands.ts +5 -4
- package/scripts/version/version-remote.ts +38 -33
- package/scripts/version/version-targets.ts +3 -3
- package/.claude/skills/workflow-commands/rule-residency.md +0 -267
- package/evals/scenarios/consult-not-execute.json +0 -20
- package/evals/scenarios/no-implicit-workflow.json +0 -26
- package/evals/scenarios/overrides-both-locations.json +0 -29
- package/evals/scenarios/upstream-interrupt.json +0 -23
- package/evals/scenarios/workflow-skill-loaded.json +0 -16
- package/prompts/agent-lint-protocol.json +0 -79
- package/prompts/agent-rules.md +0 -63
- package/prompts/collaboration-workflow/consultation-vs-execution.md +0 -8
- package/prompts/collaboration-workflow/distributed-docs.md +0 -7
- package/prompts/collaboration-workflow/durable-rules.md +0 -24
- package/prompts/collaboration-workflow/latest-first.md +0 -14
- package/prompts/collaboration-workflow/no-clones.md +0 -11
- package/prompts/collaboration-workflow/simplicity-first.md +0 -9
- package/prompts/collaboration-workflow/single-source-rules.md +0 -13
- package/scripts/build/shim-shell.ts +0 -25
- package/scripts/claude/skill-meta.ts +0 -37
- package/scripts/document/document-scan.ts +0 -108
- package/scripts/document/generate-catalog.ts +0 -69
- package/scripts/document/retired-phrases.ts +0 -143
- package/scripts/document/review-agent-type.ts +0 -46
- package/scripts/document/single-source.ts +0 -73
- package/scripts/git/git-fixture-workspace.ts +0 -88
- package/scripts/hooks/format-edited-file-fixtures.ts +0 -109
- package/scripts/hooks/prompt-hooks.ts +0 -38
- package/scripts/init/hook-command-bootstrap.ts +0 -83
- package/scripts/issue/issue-logic.ts +0 -50
- package/scripts/josh/__snapshots__/josh-command-map.test.ts.snap +0 -1227
- package/scripts/josh/josh-script-reader.ts +0 -11
- package/scripts/package/production-dependency-graph.ts +0 -68
- package/scripts/retrospective/retrospective-cli.ts +0 -97
- package/scripts/retrospective/retrospective.ts +0 -127
- package/scripts/rules/delivered-rules-harness.ts +0 -72
- package/scripts/rules/top-level-scripts-guard.ts +0 -47
- package/scripts/run/run-carry-flush.ts +0 -58
- package/scripts/run/run-transcript.golden.txt +0 -57
- package/scripts/run/run-wake-work.ts +0 -108
- package/scripts/sync/dependabot-pin-logic.ts +0 -135
- package/scripts/test/classify-isolation.ts +0 -149
- package/scripts/test/http-fault-injection.ts +0 -79
- package/scripts/test/josh-harness-environment.ts +0 -260
- package/scripts/test/josh-harness.ts +0 -96
- package/scripts/test/pilot-files.ts +0 -452
- package/scripts/test/pilot-measure.ts +0 -78
- package/scripts/test/test-network-guard.ts +0 -453
- package/scripts/test/test-repository-guard.ts +0 -317
- package/scripts/test/test-state-guard.ts +0 -85
- package/scripts/test/test-stdout-guard.ts +0 -54
- package/scripts/test/test-telegram-guard.ts +0 -90
- package/scripts/test/unit-guard-environment.ts +0 -12
- package/scripts/test/unit-projects.ts +0 -139
- package/scripts/test/vitest-include-globs.ts +0 -12
- package/scripts/time-runtime/time-batch-guard-fixtures.ts +0 -66
- package/scripts-ai/completion-gate.ts +0 -13
- package/scripts-ai/environment-loader.ts +0 -9
- /package/{scripts-ai → scripts/epic}/epic-cli-argv.ts +0 -0
- /package/{scripts-ai → scripts/epic}/epic-cli-remove.ts +0 -0
- /package/{scripts-ai → scripts/git}/followup-issue-number.ts +0 -0
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { readFileSync } from 'node:fs'
|
|
2
2
|
import { fileURLToPath } from 'node:url'
|
|
3
|
+
import { git_gh_exec } from '#scripts/git/git-gh-exec'
|
|
3
4
|
import {
|
|
4
5
|
BREAKING_CHANGE_LABEL as BREAKING,
|
|
5
6
|
BUGFIX_LABEL as BUGFIX,
|
|
@@ -7,7 +8,7 @@ import {
|
|
|
7
8
|
ENHANCEMENT_LABEL,
|
|
8
9
|
type ReleaseClassification,
|
|
9
10
|
} from '#scripts/git/issue-labels'
|
|
10
|
-
import {
|
|
11
|
+
import { error_text } from '#scripts/lib/error-message'
|
|
11
12
|
import { z } from 'zod'
|
|
12
13
|
|
|
13
14
|
const CLASSIFICATION_SET: ReadonlySet<string> = new Set(CLASSIFICATION_LABELS)
|
|
@@ -95,17 +96,17 @@ function select_issue_classification(issue_json: string): ReleaseClassification
|
|
|
95
96
|
// payload, that run fails, and when it lands in the newer check suite GitHub keeps its red result —
|
|
96
97
|
// a rerun replays the same payload and fails again (joshuafolkken/kit#2712).
|
|
97
98
|
function fetch_current_labels(repository: string, pull_number: number): ReadonlyArray<string> {
|
|
98
|
-
const
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
{
|
|
102
|
-
)
|
|
103
|
-
|
|
104
|
-
if (result.exitCode !== 0) {
|
|
105
|
-
throw new Error(`Could not read the pull request labels: ${result.stderr}`)
|
|
106
|
-
}
|
|
99
|
+
const path = `repos/${repository}/issues/${String(pull_number)}/labels`
|
|
100
|
+
|
|
101
|
+
try {
|
|
102
|
+
const names = git_gh_exec.exec_gh_api_sync({ path, jq_filter: '.[].name' })
|
|
107
103
|
|
|
108
|
-
|
|
104
|
+
return names.split('\n').filter((name) => name !== '')
|
|
105
|
+
} catch (error) {
|
|
106
|
+
const detail = error_text.message_of(error)
|
|
107
|
+
|
|
108
|
+
throw new Error(`Could not read the pull request labels: ${detail}`, { cause: error })
|
|
109
|
+
}
|
|
109
110
|
}
|
|
110
111
|
|
|
111
112
|
function check_event(
|
|
@@ -133,6 +134,7 @@ if (process.argv[1] === fileURLToPath(import.meta.url)) {
|
|
|
133
134
|
const pr_classification = {
|
|
134
135
|
classification_error,
|
|
135
136
|
check_event,
|
|
137
|
+
fetch_current_labels,
|
|
136
138
|
declaration,
|
|
137
139
|
is_classification,
|
|
138
140
|
select_issue_classification,
|
|
@@ -2,6 +2,7 @@ import { readdirSync, readFileSync, statSync } from 'node:fs'
|
|
|
2
2
|
import { homedir } from 'node:os'
|
|
3
3
|
import path from 'node:path'
|
|
4
4
|
import { lane_paths } from '#scripts/lane/lane-paths'
|
|
5
|
+
import { file_reader } from '#scripts/lib/read-file'
|
|
5
6
|
import { cost_usage, type UsageRecord } from './cost-usage'
|
|
6
7
|
|
|
7
8
|
// Finding and reading Claude Code's session transcripts (joshuafolkken/kit#962).
|
|
@@ -404,16 +405,8 @@ function tally(content: string): Omit<SessionUsage, 'session_id'> {
|
|
|
404
405
|
}
|
|
405
406
|
}
|
|
406
407
|
|
|
407
|
-
function read_text(file_path: string): string | undefined {
|
|
408
|
-
try {
|
|
409
|
-
return readFileSync(file_path, 'utf8')
|
|
410
|
-
} catch {
|
|
411
|
-
return undefined
|
|
412
|
-
}
|
|
413
|
-
}
|
|
414
|
-
|
|
415
408
|
function read_session(file: SessionFile): SessionUsage {
|
|
416
|
-
const content =
|
|
409
|
+
const content = file_reader.read_if_readable(file.path)
|
|
417
410
|
|
|
418
411
|
if (content === undefined) return { session_id: file.session_id, ...empty_session() }
|
|
419
412
|
|
|
@@ -426,7 +419,7 @@ function read_session(file: SessionFile): SessionUsage {
|
|
|
426
419
|
// "could not be read" is already reported through `is_readable`, and throwing here would turn one
|
|
427
420
|
// missing session into a failed command.
|
|
428
421
|
function read_raw(file: SessionFile): string {
|
|
429
|
-
return
|
|
422
|
+
return file_reader.read_if_readable(file.path) ?? ''
|
|
430
423
|
}
|
|
431
424
|
|
|
432
425
|
// The same read, with the failure left visible (joshuafolkken/kit#1439). **An unreadable transcript
|
|
@@ -435,7 +428,7 @@ function read_raw(file: SessionFile): string {
|
|
|
435
428
|
// measured* rather than as measured at zero. `is_readable` on `SessionUsage` is the same distinction
|
|
436
429
|
// on the cost side; this is it for a caller that wants the text.
|
|
437
430
|
function read_optional(file: SessionFile): string | undefined {
|
|
438
|
-
return
|
|
431
|
+
return file_reader.read_if_readable(file.path)
|
|
439
432
|
}
|
|
440
433
|
|
|
441
434
|
// One directory the search looked in, and whether it was there to look in. `exists` tells a directory
|
|
@@ -92,6 +92,17 @@ const DELEGATABLE_STEPS: ReadonlyArray<DelegatableStep> = [
|
|
|
92
92
|
verifier:
|
|
93
93
|
'the parent opens the cited lines; a conclusion those lines do not support fails there, and reading a handful of cited regions costs far less than redoing the reading. The unit reports what the code does and never what to change — deciding that is `diagnosis`, which stays kept, so a unit returning a root cause would be delegating the rejected row under this name',
|
|
94
94
|
},
|
|
95
|
+
{
|
|
96
|
+
name: 'lane-failure-investigation',
|
|
97
|
+
// The `backlogrun` parent's counterpart of `investigation` (joshuafolkken/kit#2947). The parent
|
|
98
|
+
// orchestrates and never implements, yet it read a failed lane's logs, records and sources in its
|
|
99
|
+
// own context — about 150 grep / cat / sed calls on 2026-10-02 — and every later request of the
|
|
100
|
+
// batch re-read them. The verifier is `investigation`'s, and so is the boundary: the unit reports
|
|
101
|
+
// what happened, and what the parent does about it (park, re-dispatch, file) stays its own call.
|
|
102
|
+
does: "find out why a lane's child stopped, failed or conflicted — its output log, run records, PR checks and the files they name — and return the conclusion plus the `file:line` or log-line citations that support it, never the text read, so the batch parent's context stays the size its orchestration needs",
|
|
103
|
+
verifier:
|
|
104
|
+
'the parent opens the cited lines and re-reads the child with `pnpm josh issue:state`; a conclusion those do not support fails there. The unit reports what happened and never what to do — parking, re-dispatching or filing stays with the parent, so a wrong account is caught before it is acted on',
|
|
105
|
+
},
|
|
95
106
|
]
|
|
96
107
|
|
|
97
108
|
// Steps that were considered and are **not** delegatable, kept as a list rather than dropped: the
|
|
@@ -1,5 +1,6 @@
|
|
|
1
|
-
import { existsSync
|
|
1
|
+
import { existsSync } from 'node:fs'
|
|
2
2
|
import path from 'node:path'
|
|
3
|
+
import { file_reader } from '#scripts/lib/read-file'
|
|
3
4
|
import { read_spawn_stdout } from '#scripts/lib/spawn-exit'
|
|
4
5
|
import { KIT_PACKAGE_NAME } from '#scripts/version/kit-descriptor'
|
|
5
6
|
import { safe_json_parse } from '#scripts/version/parse-json'
|
|
@@ -36,16 +37,8 @@ const STATE_TARGET_MISSING = 'target-missing'
|
|
|
36
37
|
const STATE_MISSING = 'missing'
|
|
37
38
|
type ClaudeMdState = typeof STATE_OK | typeof STATE_TARGET_MISSING | typeof STATE_MISSING
|
|
38
39
|
|
|
39
|
-
function read_optional(file_path: string): string | undefined {
|
|
40
|
-
try {
|
|
41
|
-
return existsSync(file_path) ? readFileSync(file_path, 'utf8') : undefined
|
|
42
|
-
} catch {
|
|
43
|
-
return undefined
|
|
44
|
-
}
|
|
45
|
-
}
|
|
46
|
-
|
|
47
40
|
function parse_manifest(root: string): ManifestShape | undefined {
|
|
48
|
-
const content =
|
|
41
|
+
const content = file_reader.read_if_readable(path.join(root, 'package.json'))
|
|
49
42
|
if (content === undefined) return undefined
|
|
50
43
|
const parsed = manifest_schema.safeParse(safe_json_parse(content))
|
|
51
44
|
|
|
@@ -74,7 +67,7 @@ function is_kit_consumer(root: string): boolean {
|
|
|
74
67
|
}
|
|
75
68
|
|
|
76
69
|
function is_plugin_declared(root: string): boolean {
|
|
77
|
-
const content =
|
|
70
|
+
const content = file_reader.read_if_readable(path.join(root, SETTINGS_PATH))
|
|
78
71
|
if (content === undefined) return false
|
|
79
72
|
const parsed = settings_schema.safeParse(safe_json_parse(content))
|
|
80
73
|
|
|
@@ -94,7 +87,7 @@ function configured_hooks_path(root: string): string | undefined {
|
|
|
94
87
|
}
|
|
95
88
|
|
|
96
89
|
function claude_md_state(root: string): ClaudeMdState {
|
|
97
|
-
const content =
|
|
90
|
+
const content = file_reader.read_if_readable(path.join(root, CLAUDE_MD))
|
|
98
91
|
if (content === undefined) return STATE_MISSING
|
|
99
92
|
if (!content.includes(CLAUDE_MD_POINTER)) return STATE_OK
|
|
100
93
|
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import { existsSync, readFileSync } from 'node:fs'
|
|
2
1
|
import path from 'node:path'
|
|
3
2
|
import { ENV_FILE_NAME, PORT_SEED_KEY, ports } from '#ports'
|
|
4
3
|
import { lane_environment } from '#scripts/lane/lane-environment'
|
|
4
|
+
import { file_reader } from '#scripts/lib/read-file'
|
|
5
5
|
|
|
6
6
|
// Each discovered repository's port seed and the dev / preview ports it resolves to, plus which
|
|
7
7
|
// repositories share a seed (joshuafolkken/kit#1494).
|
|
@@ -40,7 +40,7 @@ interface SharedSeed {
|
|
|
40
40
|
// the one key that is actually wrong.
|
|
41
41
|
function read_repository_ports(name: string, repository_path: string): RepoPorts {
|
|
42
42
|
const file = path.join(repository_path, ENV_FILE_NAME)
|
|
43
|
-
const content =
|
|
43
|
+
const content = file_reader.read_file_or_empty(file)
|
|
44
44
|
|
|
45
45
|
try {
|
|
46
46
|
const seed = lane_environment.read_root_seed(content)
|
|
@@ -65,7 +65,7 @@ function block_ceiling(size: number): number {
|
|
|
65
65
|
// unreached file, so the definition cannot rot as documents move on or off the execution path.
|
|
66
66
|
const DOCUMENT_BYTE_BUDGET: ReadonlyArray<DocumentBudget> = [
|
|
67
67
|
{ path: '.claude/skills/dependency-update/SKILL.md', bytes: 8192 },
|
|
68
|
-
{ path: '.claude/skills/epic-commands/SKILL.md', bytes:
|
|
68
|
+
{ path: '.claude/skills/epic-commands/SKILL.md', bytes: 36_864 },
|
|
69
69
|
{ path: '.claude/skills/verify-ui/SKILL.md', bytes: 8192 },
|
|
70
70
|
{ path: '.claude/skills/workflow-commands/followup-reference.md', bytes: 16_384 },
|
|
71
71
|
{ path: '.claude/skills/workflow-commands/fullrun-steps.md', bytes: 8192 },
|
|
@@ -73,46 +73,38 @@ const DOCUMENT_BYTE_BUDGET: ReadonlyArray<DocumentBudget> = [
|
|
|
73
73
|
{ path: '.claude/skills/workflow-commands/issue-comments.md', bytes: 8192 },
|
|
74
74
|
{ path: '.claude/skills/workflow-commands/issue-fold-existing.md', bytes: 4096 },
|
|
75
75
|
{ path: '.claude/skills/workflow-commands/needs-human-review.md', bytes: 8192 },
|
|
76
|
-
{ path: '.claude/skills/workflow-commands/observation-filing.md', bytes:
|
|
76
|
+
{ path: '.claude/skills/workflow-commands/observation-filing.md', bytes: 24_576 },
|
|
77
77
|
// `pre-gate-cut.md` left this per-document budget in joshuafolkken/kit#2289: it became a point-of-use
|
|
78
78
|
// document, so its bytes are now held by every entry's total read (`entry-read-budget.ts`) and the
|
|
79
79
|
// two budgets must not hold it twice — the reachability line moved it from `unreached` to
|
|
80
80
|
// `point-of-use`.
|
|
81
81
|
{ path: '.claude/skills/workflow-commands/prerequisite.md', bytes: 8192 },
|
|
82
|
-
{ path: '.claude/skills/workflow-commands/rule-residency.md', bytes: 24_576 },
|
|
83
82
|
{ path: '.claude/skills/workflow-commands/target-repository.md', bytes: 8192 },
|
|
84
83
|
{ path: '.claude/skills/workflow-commands/working-tree-hold.md', bytes: 8192 },
|
|
85
|
-
{ path: 'docs/josh-commands.md', bytes:
|
|
86
|
-
{ path: 'prompts/
|
|
87
|
-
{ path: 'prompts/coding-standards.md', bytes: 16_384 },
|
|
84
|
+
{ path: 'docs/josh-commands.md', bytes: 184_320 },
|
|
85
|
+
{ path: 'prompts/coding-standards.md', bytes: 8192 },
|
|
88
86
|
{ path: 'prompts/collaboration-workflow.md', bytes: 8192 },
|
|
89
|
-
{ path: 'prompts/collaboration-workflow/
|
|
90
|
-
{ path: 'prompts/collaboration-workflow/
|
|
91
|
-
{ path: 'prompts/collaboration-workflow/
|
|
92
|
-
{ path: 'prompts/collaboration-workflow/durable-rules.md', bytes: 4096 },
|
|
93
|
-
{ path: 'prompts/collaboration-workflow/epic-audit.md', bytes: 12_288 },
|
|
94
|
-
{ path: 'prompts/collaboration-workflow/epic-bundle.md', bytes: 24_576 },
|
|
87
|
+
{ path: 'prompts/collaboration-workflow/cross-repo-epic.md', bytes: 4096 },
|
|
88
|
+
{ path: 'prompts/collaboration-workflow/epic-audit.md', bytes: 4096 },
|
|
89
|
+
{ path: 'prompts/collaboration-workflow/epic-bundle.md', bytes: 4096 },
|
|
95
90
|
{ path: 'prompts/collaboration-workflow/file-edits.md', bytes: 12_288 },
|
|
96
91
|
{ path: 'prompts/collaboration-workflow/gh-rest.md', bytes: 8192 },
|
|
97
92
|
{ path: 'prompts/collaboration-workflow/issue-citation.md', bytes: 8192 },
|
|
98
93
|
// #2662 adds the feature and compatibility declarations to the canonical filing procedure.
|
|
99
94
|
{ path: 'prompts/collaboration-workflow/issue-template.md', bytes: 28_672 },
|
|
100
|
-
{ path: 'prompts/collaboration-workflow/
|
|
101
|
-
{ path: 'prompts/collaboration-workflow/no-clones.md', bytes: 4096 },
|
|
102
|
-
{ path: 'prompts/collaboration-workflow/operating-rules.md', bytes: 24_576 },
|
|
95
|
+
{ path: 'prompts/collaboration-workflow/operating-rules.md', bytes: 16_384 },
|
|
103
96
|
{ path: 'prompts/collaboration-workflow/output-bounds.md', bytes: 16_384 },
|
|
104
97
|
{ path: 'prompts/collaboration-workflow/overview.md', bytes: 8192 },
|
|
105
98
|
{ path: 'prompts/collaboration-workflow/plan-comment.md', bytes: 20_480 },
|
|
99
|
+
{ path: 'prompts/collaboration-workflow/principles.md', bytes: 12_288 },
|
|
106
100
|
{ path: 'prompts/collaboration-workflow/proposal-request.md', bytes: 4096 },
|
|
107
101
|
{ path: 'prompts/collaboration-workflow/report-format.md', bytes: 28_672 },
|
|
108
|
-
{ path: 'prompts/collaboration-workflow/residency.md', bytes:
|
|
109
|
-
{ path: 'prompts/collaboration-workflow/rule-delivery.md', bytes:
|
|
102
|
+
{ path: 'prompts/collaboration-workflow/residency.md', bytes: 28_672 },
|
|
103
|
+
{ path: 'prompts/collaboration-workflow/rule-delivery.md', bytes: 45_056 },
|
|
110
104
|
{ path: 'prompts/collaboration-workflow/shell-body.md', bytes: 12_288 },
|
|
111
|
-
{ path: 'prompts/collaboration-workflow/
|
|
112
|
-
{ path: 'prompts/collaboration-workflow/single-source-rules.md', bytes: 4096 },
|
|
113
|
-
{ path: 'prompts/collaboration-workflow/turn-batching.md', bytes: 24_576 },
|
|
105
|
+
{ path: 'prompts/collaboration-workflow/turn-batching.md', bytes: 20_480 },
|
|
114
106
|
{ path: 'prompts/collaboration-workflow/upstream-interrupt.md', bytes: 12_288 },
|
|
115
|
-
{ path: 'prompts/collaboration-workflow/wip-cap.md', bytes:
|
|
107
|
+
{ path: 'prompts/collaboration-workflow/wip-cap.md', bytes: 16_384 },
|
|
116
108
|
{ path: 'prompts/refactoring.md', bytes: 8192 },
|
|
117
109
|
{ path: 'prompts/review-rubric.md', bytes: 20_480 },
|
|
118
110
|
{ path: 'prompts/review.md', bytes: 20_480 },
|
|
@@ -12,7 +12,7 @@
|
|
|
12
12
|
// is the distinction joshuafolkken/kit#1344 and joshuafolkken/kit#1460 were written after: a rule
|
|
13
13
|
// moved to "read it later" is a rule that measurably never fires, and this moves nothing to later.
|
|
14
14
|
|
|
15
|
-
import {
|
|
15
|
+
import { file_reader } from '#scripts/lib/read-file'
|
|
16
16
|
|
|
17
17
|
// `#` to `######`, one space, then the title. Written with a literal space rather than `\s+` and a
|
|
18
18
|
// lazy title so it cannot backtrack: `sonarjs/super-linear-regex` rejects the `\s+…+?\s*$` shape,
|
|
@@ -134,11 +134,7 @@ function candidates(markdown: string, wanted: string): Array<string> {
|
|
|
134
134
|
}
|
|
135
135
|
|
|
136
136
|
function read_optional(file_path: string): string | undefined {
|
|
137
|
-
|
|
138
|
-
return readFileSync(file_path, 'utf8')
|
|
139
|
-
} catch {
|
|
140
|
-
return undefined
|
|
141
|
-
}
|
|
137
|
+
return file_reader.read_if_readable(file_path)
|
|
142
138
|
}
|
|
143
139
|
|
|
144
140
|
const document_section = {
|
|
@@ -77,12 +77,13 @@ const ENTRY_READ_BUDGET: ReadonlyArray<EntryBudget> = [
|
|
|
77
77
|
// planning entries): kickoff has no headroom, so the addition was offset by tightening that file's
|
|
78
78
|
// heartbeat prose, keeping every entry within its existing block.
|
|
79
79
|
// joshuafolkken/kit#2762 moved the rationale of five point-of-use documents to
|
|
80
|
-
// `docs/maintainers/*-rationale.md`, and the ratchet holds the reduction.
|
|
80
|
+
// `docs/maintainers/*-rationale.md`, and the ratchet holds the reduction. joshuafolkken/kit#2895 did
|
|
81
|
+
// the same for eight more, lowering fullrun, halfrun and backlogrun one block each.
|
|
81
82
|
{ entry: 'kickoff', bytes: 229_376 },
|
|
82
|
-
{ entry: 'fullrun', bytes:
|
|
83
|
-
{ entry: 'halfrun', bytes:
|
|
84
|
-
{ entry: 'backlogrun', bytes:
|
|
85
|
-
{ entry: LANE_CHILD, bytes:
|
|
83
|
+
{ entry: 'fullrun', bytes: 225_280 },
|
|
84
|
+
{ entry: 'halfrun', bytes: 225_280 },
|
|
85
|
+
{ entry: 'backlogrun', bytes: 229_376 },
|
|
86
|
+
{ entry: LANE_CHILD, bytes: 77_824 },
|
|
86
87
|
]
|
|
87
88
|
|
|
88
89
|
function byte_size(root: string, relative_path: string): number {
|
|
@@ -1,12 +1,12 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
1
|
+
import { epic_chains, type Chains, type InsertPosition } from './epic-chains'
|
|
2
|
+
import { epic_decision } from './epic-decision'
|
|
3
|
+
import { epic_parse, UNORDERED_DEPENDENCIES } from './epic-parse'
|
|
4
4
|
import {
|
|
5
5
|
format_dependency_link,
|
|
6
6
|
format_issue_references,
|
|
7
7
|
to_issue_reference,
|
|
8
|
-
} from './
|
|
9
|
-
import {
|
|
8
|
+
} from './epic-reference'
|
|
9
|
+
import { epic_sections, type BodyLines, type SectionRange } from './epic-sections'
|
|
10
10
|
|
|
11
11
|
// Rewriting an epic body so its task list and its dependency declaration both name a newly inserted
|
|
12
12
|
// child, and refusing to hand back a body that would not.
|
|
@@ -50,7 +50,7 @@ interface RewriteInput {
|
|
|
50
50
|
|
|
51
51
|
type RewriteOutcome = { body: string } | { error: string }
|
|
52
52
|
|
|
53
|
-
const { find_indices, is_in_range, to_body_lines } =
|
|
53
|
+
const { find_indices, is_in_range, to_body_lines } = epic_sections
|
|
54
54
|
|
|
55
55
|
function to_task_row(issue_number: number): string {
|
|
56
56
|
return `- [ ] ${to_issue_reference(issue_number)}`
|
|
@@ -66,7 +66,7 @@ function to_task_rows(placed: ReadonlyArray<number>): Array<string> {
|
|
|
66
66
|
// insertion that declared no order takes, and the only one it can take: with no position named there
|
|
67
67
|
// is nothing for a row to be relative to.
|
|
68
68
|
function append_task_rows(input: BodyLines, placed: ReadonlyArray<number>): Array<string> {
|
|
69
|
-
const rows = find_indices(input, (line) =>
|
|
69
|
+
const rows = find_indices(input, (line) => epic_parse.is_task_list_line(line))
|
|
70
70
|
const last = rows.at(-1)
|
|
71
71
|
if (last === undefined) return [...input.lines]
|
|
72
72
|
|
|
@@ -80,7 +80,7 @@ function append_task_rows(input: BodyLines, placed: ReadonlyArray<number>): Arra
|
|
|
80
80
|
// The issue a task-list row names, read through the parser rather than a second pattern of its own —
|
|
81
81
|
// a row this recognizes is exactly a row `epic:next` and the auto-close read.
|
|
82
82
|
function to_row_number(line: string): number | undefined {
|
|
83
|
-
const [issue_number] =
|
|
83
|
+
const [issue_number] = epic_parse.parse_task_list_issue_numbers(line)
|
|
84
84
|
|
|
85
85
|
return issue_number
|
|
86
86
|
}
|
|
@@ -136,7 +136,7 @@ function to_placed_lines(input: RewriteInput): Array<string> {
|
|
|
136
136
|
const DEPENDENCIES_HEADING = /^#{1,6}[ \t]+Dependencies\b/u
|
|
137
137
|
|
|
138
138
|
function find_dependencies_range(input: BodyLines): SectionRange | undefined {
|
|
139
|
-
return
|
|
139
|
+
return epic_sections.find_section_range(input, DEPENDENCIES_HEADING)
|
|
140
140
|
}
|
|
141
141
|
|
|
142
142
|
function splice_declaration(
|
|
@@ -162,7 +162,7 @@ function replace_declaration(
|
|
|
162
162
|
const range = find_dependencies_range(input)
|
|
163
163
|
if (range === undefined) return undefined
|
|
164
164
|
|
|
165
|
-
const found = find_indices(input, (line) =>
|
|
165
|
+
const found = find_indices(input, (line) => epic_parse.is_declaration_line(line))
|
|
166
166
|
const declarations = found.filter((index) => is_in_range(index, range))
|
|
167
167
|
const [first] = declarations
|
|
168
168
|
|
|
@@ -182,7 +182,7 @@ function replace_declaration(
|
|
|
182
182
|
// The unordered sentence parses to no chain, and its own constant stands in for it: that branch has no
|
|
183
183
|
// numbers to render, and the sentence is a literal this module already shares with the parser.
|
|
184
184
|
function to_safe_declaration(line: string): string {
|
|
185
|
-
const [rendered] =
|
|
185
|
+
const [rendered] = epic_chains.render_chains(epic_parse.parse_dependency_chains(line))
|
|
186
186
|
|
|
187
187
|
return rendered ?? UNORDERED_DEPENDENCIES
|
|
188
188
|
}
|
|
@@ -194,7 +194,7 @@ function find_stray_declaration(lines: ReadonlyArray<string>): string | undefine
|
|
|
194
194
|
const input = to_body_lines(lines)
|
|
195
195
|
const range = find_dependencies_range(input)
|
|
196
196
|
if (range === undefined) return undefined
|
|
197
|
-
const stray = find_indices(input, (line) =>
|
|
197
|
+
const stray = find_indices(input, (line) => epic_parse.is_declaration_line(line)).find(
|
|
198
198
|
(index) => !is_in_range(index, range),
|
|
199
199
|
)
|
|
200
200
|
|
|
@@ -212,7 +212,7 @@ function to_written_lines(input: RewriteInput): Array<string> {
|
|
|
212
212
|
const { decision } = input
|
|
213
213
|
if (decision === undefined) return with_rows
|
|
214
214
|
|
|
215
|
-
return
|
|
215
|
+
return epic_decision.append_decision(with_rows.join('\n'), decision).split('\n')
|
|
216
216
|
}
|
|
217
217
|
|
|
218
218
|
// Whether the body already declares exactly the order the caller computed, chain structure included:
|
|
@@ -222,9 +222,9 @@ function is_declaration_unchanged(
|
|
|
222
222
|
lines: ReadonlyArray<string>,
|
|
223
223
|
rendered: ReadonlyArray<string>,
|
|
224
224
|
): boolean {
|
|
225
|
-
const declared =
|
|
225
|
+
const declared = epic_parse.parse_dependency_chains(lines.join('\n'))
|
|
226
226
|
|
|
227
|
-
return
|
|
227
|
+
return epic_chains.render_chains(declared).join('\n') === rendered.join('\n')
|
|
228
228
|
}
|
|
229
229
|
|
|
230
230
|
// The declaration work, once the task rows and any record are in.
|
|
@@ -261,7 +261,7 @@ function to_declared_body(
|
|
|
261
261
|
// say — the unordered sentence, since an emptied section carries no machine-readable declaration at
|
|
262
262
|
// all (joshuafolkken/kit#1712).
|
|
263
263
|
function to_rendered_declaration(input: RewriteInput): Array<string> {
|
|
264
|
-
const rendered =
|
|
264
|
+
const rendered = epic_chains.render_chains(input.chains_after)
|
|
265
265
|
if (rendered.length > 0) return rendered
|
|
266
266
|
|
|
267
267
|
return input.does_clear_declaration === true ? [UNORDERED_DEPENDENCIES] : []
|
|
@@ -277,7 +277,7 @@ function build_body(input: RewriteInput): BodyOutcome {
|
|
|
277
277
|
}
|
|
278
278
|
|
|
279
279
|
function to_link_keys(chains: Chains): Array<string> {
|
|
280
|
-
return
|
|
280
|
+
return epic_chains
|
|
281
281
|
.links_of(chains)
|
|
282
282
|
.map((link) => format_dependency_link(link))
|
|
283
283
|
.toSorted((left, right) => left.localeCompare(right))
|
|
@@ -287,7 +287,7 @@ function to_link_keys(chains: Chains): Array<string> {
|
|
|
287
287
|
// key lists rather than by reading the text back by eye — the round trip is the only evidence that
|
|
288
288
|
// the rewrite and the parser agree.
|
|
289
289
|
function has_intended_links(body: string, chains_after: Chains): boolean {
|
|
290
|
-
const written = to_link_keys(
|
|
290
|
+
const written = to_link_keys(epic_parse.parse_dependency_chains(body))
|
|
291
291
|
const intended = to_link_keys(chains_after)
|
|
292
292
|
|
|
293
293
|
return (
|
|
@@ -296,7 +296,7 @@ function has_intended_links(body: string, chains_after: Chains): boolean {
|
|
|
296
296
|
}
|
|
297
297
|
|
|
298
298
|
function missing_rows(body: string, expected: ReadonlyArray<number>): Array<number> {
|
|
299
|
-
const tracked =
|
|
299
|
+
const tracked = epic_parse.parse_task_list_issue_numbers(body)
|
|
300
300
|
|
|
301
301
|
return expected.filter((issue_number) => !tracked.includes(issue_number))
|
|
302
302
|
}
|
|
@@ -319,7 +319,7 @@ function to_placement_start(position: InsertPosition, target: number, count: num
|
|
|
319
319
|
function has_intended_rows(body: string, input: RewriteInput): boolean {
|
|
320
320
|
const { position, placed } = input
|
|
321
321
|
if (position === undefined || placed.length === 0) return true
|
|
322
|
-
const tracked =
|
|
322
|
+
const tracked = epic_parse.parse_task_list_issue_numbers(body)
|
|
323
323
|
const target = tracked.indexOf(position.target)
|
|
324
324
|
if (target === -1) return false
|
|
325
325
|
const start = to_placement_start(position, target, placed.length)
|
|
@@ -334,7 +334,7 @@ function find_rewrite_error(body: string, input: RewriteInput): string | undefin
|
|
|
334
334
|
return `The rewritten body would not track ${format_issue_references(missing)} as a task-list row.`
|
|
335
335
|
}
|
|
336
336
|
|
|
337
|
-
if (!
|
|
337
|
+
if (!epic_parse.has_machine_readable_declaration(body)) {
|
|
338
338
|
return 'The rewritten body would carry no unambiguous machine-readable `Dependencies` declaration.'
|
|
339
339
|
}
|
|
340
340
|
|
|
@@ -357,9 +357,9 @@ function rewrite_body(input: RewriteInput): RewriteOutcome {
|
|
|
357
357
|
return error === undefined ? built : { error }
|
|
358
358
|
}
|
|
359
359
|
|
|
360
|
-
const
|
|
360
|
+
const epic_add_body = {
|
|
361
361
|
rewrite_body,
|
|
362
362
|
}
|
|
363
363
|
|
|
364
|
-
export {
|
|
364
|
+
export { epic_add_body }
|
|
365
365
|
export type { RewriteInput, RewriteOutcome }
|
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
6
|
-
import { format_dependency_links, to_issue_reference } from './
|
|
7
|
-
import {
|
|
1
|
+
import { epic_add_body, type RewriteInput } from './epic-add-body'
|
|
2
|
+
import { epic_chains, type InsertOutcome, type InsertPosition } from './epic-chains'
|
|
3
|
+
import { epic_decision } from './epic-decision'
|
|
4
|
+
import { epic_graph, type EpicChild } from './epic-graph'
|
|
5
|
+
import { epic_parse, type DependencyLink } from './epic-parse'
|
|
6
|
+
import { format_dependency_links, to_issue_reference } from './epic-reference'
|
|
7
|
+
import { epic_shape } from './epic-shape'
|
|
8
8
|
|
|
9
9
|
// Everything `josh epic --add` decides before it writes anything.
|
|
10
10
|
//
|
|
@@ -273,16 +273,13 @@ function to_contradicted(
|
|
|
273
273
|
}
|
|
274
274
|
|
|
275
275
|
function to_plan(context: PlanContext): PlanOutcome {
|
|
276
|
-
const { removed: replaced } =
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
)
|
|
280
|
-
const decision = git_epic_decision.append_replacements(context.input.decision, replaced)
|
|
281
|
-
const rewritten = git_epic_add_body.rewrite_body(to_rewrite_input(context, decision))
|
|
276
|
+
const { removed: replaced } = epic_chains.diff_links(context.chains_before, context.chains_after)
|
|
277
|
+
const decision = epic_decision.append_replacements(context.input.decision, replaced)
|
|
278
|
+
const rewritten = epic_add_body.rewrite_body(to_rewrite_input(context, decision))
|
|
282
279
|
if ('error' in rewritten) return { error: rewritten.error }
|
|
283
280
|
|
|
284
|
-
const links_after =
|
|
285
|
-
const result_rows =
|
|
281
|
+
const links_after = epic_chains.links_of(context.chains_after)
|
|
282
|
+
const result_rows = epic_parse.parse_task_list_issue_numbers(rewritten.body)
|
|
286
283
|
|
|
287
284
|
return {
|
|
288
285
|
plan: {
|
|
@@ -304,7 +301,7 @@ function to_plan(context: PlanContext): PlanOutcome {
|
|
|
304
301
|
// given because the record has to exist, so writing the insertion without it would report success for
|
|
305
302
|
// half the job (joshuafolkken/kit#1350). `undefined` is "none was asked for", which is not a refusal.
|
|
306
303
|
function find_decision_error(decision: string | undefined): string | undefined {
|
|
307
|
-
return decision === undefined ? undefined :
|
|
304
|
+
return decision === undefined ? undefined : epic_decision.find_decision_error(decision)
|
|
308
305
|
}
|
|
309
306
|
|
|
310
307
|
// Asked of the declaration as it stands, because a relocation's removal can collapse the very
|
|
@@ -320,7 +317,7 @@ function find_ambiguity_error(
|
|
|
320
317
|
): string | undefined {
|
|
321
318
|
if (input.is_order_only === true) return undefined
|
|
322
319
|
|
|
323
|
-
return
|
|
320
|
+
return epic_chains.find_position_ambiguity(chains_before, input.position)
|
|
324
321
|
}
|
|
325
322
|
|
|
326
323
|
// The refusals about what is being placed and where: the position may not name one of the issues
|
|
@@ -355,9 +352,9 @@ function find_input_error(
|
|
|
355
352
|
chains_before: ReadonlyArray<ReadonlyArray<number>>,
|
|
356
353
|
): string | undefined {
|
|
357
354
|
return (
|
|
358
|
-
|
|
355
|
+
epic_shape.find_subject_error(input, tracked) ??
|
|
359
356
|
find_placement_error(input, tracked, chains_before) ??
|
|
360
|
-
find_relation_error(
|
|
357
|
+
find_relation_error(epic_chains.links_of(chains_before), input.recorded, input.repo) ??
|
|
361
358
|
find_decision_error(input.decision)
|
|
362
359
|
)
|
|
363
360
|
}
|
|
@@ -377,10 +374,10 @@ function to_chains_after(
|
|
|
377
374
|
moves: { placed: ReadonlyArray<number>; relocations: ReadonlyArray<number> },
|
|
378
375
|
tracked: ReadonlyArray<number>,
|
|
379
376
|
): InsertOutcome {
|
|
380
|
-
if (input.is_order_only === true) return
|
|
377
|
+
if (input.is_order_only === true) return epic_chains.keep_declaration(chains_before)
|
|
381
378
|
|
|
382
|
-
return
|
|
383
|
-
|
|
379
|
+
return epic_chains.insert_children(
|
|
380
|
+
epic_chains.remove_children(chains_before, moves.relocations),
|
|
384
381
|
moves.placed,
|
|
385
382
|
input.position,
|
|
386
383
|
tracked,
|
|
@@ -388,8 +385,8 @@ function to_chains_after(
|
|
|
388
385
|
}
|
|
389
386
|
|
|
390
387
|
function build_plan(input: PlanInput): PlanOutcome {
|
|
391
|
-
const tracked =
|
|
392
|
-
const chains_before =
|
|
388
|
+
const tracked = epic_parse.parse_task_list_issue_numbers(input.body)
|
|
389
|
+
const chains_before = epic_parse.parse_dependency_chains(input.body)
|
|
393
390
|
const error = find_input_error(input, tracked, chains_before)
|
|
394
391
|
if (error !== undefined) return { error }
|
|
395
392
|
|
|
@@ -413,9 +410,9 @@ function build_plan(input: PlanInput): PlanOutcome {
|
|
|
413
410
|
})
|
|
414
411
|
}
|
|
415
412
|
|
|
416
|
-
const
|
|
413
|
+
const epic_add_plan = {
|
|
417
414
|
build_plan,
|
|
418
415
|
}
|
|
419
416
|
|
|
420
|
-
export {
|
|
417
|
+
export { epic_add_plan }
|
|
421
418
|
export type { AddPlan, PlanInput, PlanOutcome }
|
|
@@ -1,16 +1,16 @@
|
|
|
1
1
|
import { backlog_ready } from '#scripts/backlog/backlog-ready'
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
import type { InsertPosition } from './
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
2
|
+
import { git_gh_command } from '#scripts/git/git-gh-command'
|
|
3
|
+
import { epic_add_plan, type AddPlan } from './epic-add-plan'
|
|
4
|
+
import type { InsertPosition } from './epic-chains'
|
|
5
|
+
import { epic_decision } from './epic-decision'
|
|
6
|
+
import { epic_issue } from './epic-issue'
|
|
7
|
+
import { epic_read, type EpicReading } from './epic-read'
|
|
7
8
|
import {
|
|
8
9
|
format_issue_references,
|
|
9
10
|
format_replaced_relations,
|
|
10
11
|
to_issue_reference,
|
|
11
|
-
} from './
|
|
12
|
-
import {
|
|
13
|
-
import { git_gh_command } from './git-gh-command'
|
|
12
|
+
} from './epic-reference'
|
|
13
|
+
import { epic_relations } from './epic-relations'
|
|
14
14
|
|
|
15
15
|
// `josh epic --add <E> <N...> [--before <M> | --after <M> | --order-before <M> | --order-after <M>]
|
|
16
16
|
// [--decision-file <path|->]` — insert children into an existing epic.
|
|
@@ -65,7 +65,7 @@ interface AddChildrenInput {
|
|
|
65
65
|
function report_relations(plan: AddPlan, failures: { added: number; removed: number }): void {
|
|
66
66
|
if (plan.removed.length > 0) {
|
|
67
67
|
console.info(
|
|
68
|
-
|
|
68
|
+
epic_relations.format_relation_report({
|
|
69
69
|
links: plan.removed,
|
|
70
70
|
failures: failures.removed,
|
|
71
71
|
action: 'drop',
|
|
@@ -76,7 +76,7 @@ function report_relations(plan: AddPlan, failures: { added: number; removed: num
|
|
|
76
76
|
if (plan.added.length === 0) return
|
|
77
77
|
|
|
78
78
|
console.info(
|
|
79
|
-
|
|
79
|
+
epic_relations.format_relation_report({
|
|
80
80
|
links: plan.added,
|
|
81
81
|
failures: failures.added,
|
|
82
82
|
action: 'record',
|
|
@@ -87,8 +87,8 @@ function report_relations(plan: AddPlan, failures: { added: number; removed: num
|
|
|
87
87
|
// Dropped before recorded: inserting `#N` between `#B` and `#M` replaces `#B -> #M`, and applying the
|
|
88
88
|
// new link first would leave `#M` momentarily blocked by both.
|
|
89
89
|
async function apply_plan(plan: AddPlan): Promise<void> {
|
|
90
|
-
const removed = await
|
|
91
|
-
const added = await
|
|
90
|
+
const removed = await epic_relations.apply_relations(plan.removed, 'drop')
|
|
91
|
+
const added = await epic_relations.apply_relations(plan.added, 'record')
|
|
92
92
|
|
|
93
93
|
report_relations(plan, { added, removed })
|
|
94
94
|
}
|
|
@@ -167,7 +167,7 @@ async function comment_decision(children: ReadonlyArray<number>, decision: strin
|
|
|
167
167
|
)
|
|
168
168
|
|
|
169
169
|
console.info(
|
|
170
|
-
|
|
170
|
+
epic_decision.format_decision_report({
|
|
171
171
|
total: children.length,
|
|
172
172
|
failures: posted.filter((is_posted) => !is_posted).length,
|
|
173
173
|
}),
|
|
@@ -200,7 +200,7 @@ async function write_plan(input: AddChildrenInput, plan: AddPlan): Promise<void>
|
|
|
200
200
|
// (joshuafolkken/kit#2337). Both reasons come back as a bare string the one caller prefixes `✖` onto,
|
|
201
201
|
// so the two refusals read identically on stderr.
|
|
202
202
|
async function read_open_epic(epic_number: number): Promise<EpicReading | { error: string }> {
|
|
203
|
-
const epic = await
|
|
203
|
+
const epic = await epic_read.read_epic(epic_number)
|
|
204
204
|
if ('error' in epic) return epic
|
|
205
205
|
if (is_closed_epic(epic.subject.state)) return { error: closed_epic_error(epic_number) }
|
|
206
206
|
|
|
@@ -218,7 +218,7 @@ async function add_children(input: AddChildrenInput): Promise<number> {
|
|
|
218
218
|
return FAILURE_EXIT_CODE
|
|
219
219
|
}
|
|
220
220
|
|
|
221
|
-
const outcome =
|
|
221
|
+
const outcome = epic_add_plan.build_plan({
|
|
222
222
|
epic_number: input.epic_number,
|
|
223
223
|
body: epic.subject.body,
|
|
224
224
|
labels: epic.subject.labels,
|
|
@@ -241,9 +241,9 @@ async function add_children(input: AddChildrenInput): Promise<number> {
|
|
|
241
241
|
return SUCCESS_EXIT_CODE
|
|
242
242
|
}
|
|
243
243
|
|
|
244
|
-
const
|
|
244
|
+
const epic_add = {
|
|
245
245
|
add_children,
|
|
246
246
|
}
|
|
247
247
|
|
|
248
|
-
export {
|
|
248
|
+
export { epic_add }
|
|
249
249
|
export type { AddChildrenInput }
|