@joshuafolkken/kit 1.1005.0 → 1.1035.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/workflow-commands/SKILL.md +8 -23
- package/.claude/skills/workflow-commands/backlogrun-child.md +4 -2
- package/.claude/skills/workflow-commands/backlogrun.md +3 -12
- 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/observation-filing.md +42 -43
- package/.claude/skills/workflow-commands/prerequisite.md +1 -1
- package/.codex/hooks.json +1 -5
- 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 -58
- package/dist/CLAUDE.md +24 -38
- package/dist/hooks/batch-guard.js +4 -4
- package/dist/hooks/{chunk-WMIR5SNX.js → chunk-D67KVGGY.js} +3 -3
- package/dist/hooks/{chunk-EZLHXKJ6.js → chunk-EIFCAJRB.js} +70 -27
- package/dist/hooks/{chunk-KPZN42RT.js → chunk-GJT4BNVE.js} +3 -3
- package/dist/hooks/{chunk-PAXMLGSS.js → chunk-GLBDBJOK.js} +44 -24
- package/dist/hooks/{chunk-GBJUERYK.js → chunk-HOYJU5RM.js} +1 -1
- package/dist/hooks/{chunk-6PSTVOLH.js → chunk-HU66PWWL.js} +33 -57
- package/dist/hooks/{chunk-A2H5I2VD.js → chunk-KG6PZD72.js} +2 -2
- package/dist/hooks/{chunk-AWTFKKFW.js → chunk-XPLP27WA.js} +3 -8
- package/dist/hooks/{chunk-4GDR2YTV.js → chunk-Y27M6A7X.js} +4 -4
- package/dist/hooks/{chunk-56LGMJJV.js → chunk-ZXNE6NY3.js} +2 -2
- package/dist/hooks/codex-hook-adapter.js +10 -10
- package/dist/hooks/duplicate-read-guard.js +5 -5
- package/dist/hooks/format-edited.js +2 -2
- package/dist/hooks/investigation-guard.js +5 -5
- package/dist/hooks/pretool-guard.js +9 -9
- package/dist/hooks/stop-guard.js +11 -15
- package/dist/josh.js +38 -19
- package/dist/version/index.js +93 -41
- package/eslint/base.js +2 -11
- package/package.json +32 -6
- 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/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 +54 -14
- package/prompts/collaboration-workflow/rule-delivery.md +1 -11
- package/prompts/collaboration-workflow/upstream-interrupt.md +1 -1
- package/prompts/collaboration-workflow.md +22 -28
- package/scripts/adopt/adopt-logic.ts +2 -2
- package/scripts/adopt/adopt-toolkits.ts +1 -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/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-offer-cli.ts +6 -5
- package/scripts/backlog/backlog-plan.ts +1 -1
- package/scripts/build/build-bin.ts +2 -1
- package/scripts/build/build-config-merge.ts +2 -1
- package/scripts/build/build-hooks.ts +24 -4
- 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/doctor/doctor-consumer.ts +5 -12
- package/scripts/doctor/doctor-ports.ts +2 -2
- package/scripts/document/document-byte-budget.ts +8 -16
- package/scripts/document/document-section.ts +2 -6
- package/scripts/document/entry-read-budget.ts +1 -1
- 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-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/{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/{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 +1 -1
- package/scripts/git/git-error.ts +2 -5
- 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-pr-followup-wrapup.ts +2 -6
- package/scripts/git/git-preflight.ts +3 -6
- 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/issue-labels.ts +1 -1
- 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-rewrite-notice.ts +2 -8
- package/scripts/init/init-ai-copy.ts +23 -14
- package/scripts/init/init.ts +8 -4
- package/scripts/init/start-steps.ts +2 -1
- package/scripts/init/start.ts +2 -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-commands-ai.ts +2 -2
- package/scripts/josh/josh-commands-backlog.ts +1 -1
- package/scripts/josh/josh-commands-development.ts +1 -1
- package/scripts/josh/josh-commands-workflow.ts +4 -4
- package/scripts/lane/lane-cli.ts +2 -1
- package/scripts/lane/lane-open.ts +3 -2
- package/scripts/lane/lane-registry.ts +3 -6
- 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-publish.ts +6 -7
- package/scripts/propagate/propagate-steps.ts +2 -1
- package/scripts/propagate/propagate.ts +3 -2
- package/scripts/refactor/import-graph.ts +2 -6
- package/scripts/registry-migration/migrate.ts +3 -2
- package/scripts/registry-migration/user-npmrc.ts +2 -2
- package/scripts/release/release-cli.ts +2 -1
- package/scripts/repo/repo-setting.ts +10 -8
- 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/rules/decision-oracle.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-liveness-cli.ts +4 -7
- package/scripts/run/run-progress-args.ts +2 -6
- package/scripts/run/run-progress-cli.ts +2 -1
- 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 +2 -6
- package/scripts/run/run-wake-driver.ts +15 -2
- package/scripts/run/run-wake-session.ts +14 -4
- 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/ui/ui-routes-cli.ts +3 -6
- package/scripts/version/bump-version.ts +2 -1
- package/scripts/version/version-remote.ts +18 -27
- 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,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 },
|
|
@@ -79,37 +79,29 @@ const DOCUMENT_BYTE_BUDGET: ReadonlyArray<DocumentBudget> = [
|
|
|
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
84
|
{ path: 'docs/josh-commands.md', bytes: 180_224 },
|
|
86
|
-
{ path: 'prompts/
|
|
87
|
-
{ path: 'prompts/coding-standards.md', bytes: 16_384 },
|
|
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:
|
|
102
|
+
{ path: 'prompts/collaboration-workflow/residency.md', bytes: 28_672 },
|
|
109
103
|
{ path: 'prompts/collaboration-workflow/rule-delivery.md', bytes: 57_344 },
|
|
110
104
|
{ path: 'prompts/collaboration-workflow/shell-body.md', bytes: 12_288 },
|
|
111
|
-
{ path: 'prompts/collaboration-workflow/simplicity-first.md', bytes: 4096 },
|
|
112
|
-
{ path: 'prompts/collaboration-workflow/single-source-rules.md', bytes: 4096 },
|
|
113
105
|
{ path: 'prompts/collaboration-workflow/turn-batching.md', bytes: 24_576 },
|
|
114
106
|
{ path: 'prompts/collaboration-workflow/upstream-interrupt.md', bytes: 12_288 },
|
|
115
107
|
{ path: 'prompts/collaboration-workflow/wip-cap.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 = {
|
|
@@ -82,7 +82,7 @@ const ENTRY_READ_BUDGET: ReadonlyArray<EntryBudget> = [
|
|
|
82
82
|
{ entry: 'fullrun', bytes: 229_376 },
|
|
83
83
|
{ entry: 'halfrun', bytes: 229_376 },
|
|
84
84
|
{ entry: 'backlogrun', bytes: 233_472 },
|
|
85
|
-
{ entry: LANE_CHILD, bytes:
|
|
85
|
+
{ entry: LANE_CHILD, bytes: 77_824 },
|
|
86
86
|
]
|
|
87
87
|
|
|
88
88
|
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 }
|
|
@@ -1,7 +1,5 @@
|
|
|
1
1
|
#!/usr/bin/env tsx
|
|
2
2
|
import { fileURLToPath } from 'node:url'
|
|
3
|
-
import { git_epic_decision } from '#scripts/git/git-epic-decision'
|
|
4
|
-
import { git_epic_parse, type DependencyLink } from '#scripts/git/git-epic-parse'
|
|
5
3
|
import { git_gh_command } from '#scripts/git/git-gh-command'
|
|
6
4
|
import { issue_read } from '#scripts/issue/issue-read'
|
|
7
5
|
import { epic_audit_logic, type AuditFinding, type ReferenceState } from './epic-audit'
|
|
@@ -10,10 +8,12 @@ import { epic_audit_orphans, type ClaimingSearch } from './epic-audit-orphans'
|
|
|
10
8
|
import { epic_audit_rationale, type OrderPair } from './epic-audit-rationale'
|
|
11
9
|
import { epic_audit_report, type AuditResult } from './epic-audit-report'
|
|
12
10
|
import { epic_cross_repo } from './epic-cross-repo'
|
|
11
|
+
import { epic_decision } from './epic-decision'
|
|
13
12
|
import { epic_fetch, type EpicSnapshot } from './epic-fetch'
|
|
14
13
|
import { epic_graph, type IssueReference } from './epic-graph'
|
|
15
14
|
import { epic_issue } from './epic-issue'
|
|
16
15
|
import { epic_next } from './epic-next'
|
|
16
|
+
import { epic_parse, type DependencyLink } from './epic-parse'
|
|
17
17
|
|
|
18
18
|
// `josh epic:audit <E>` — read an epic's children against each other and report what contradicts
|
|
19
19
|
// what (joshuafolkken/kit#870).
|
|
@@ -278,7 +278,7 @@ async function to_audit_input(
|
|
|
278
278
|
epic_number: number,
|
|
279
279
|
repo: string,
|
|
280
280
|
): Promise<AuditInput> {
|
|
281
|
-
const links =
|
|
281
|
+
const links = epic_parse.parse_dependency_links(snapshot.body)
|
|
282
282
|
const order_pairs = epic_audit_rationale.order_pairs(links, children, snapshot.repo, repo)
|
|
283
283
|
|
|
284
284
|
return {
|
|
@@ -291,7 +291,7 @@ async function to_audit_input(
|
|
|
291
291
|
anomalies: fetch_anomalies(snapshot, children, links),
|
|
292
292
|
contradictions: epic_audit_checks.find_order_contradictions(children, repo),
|
|
293
293
|
order_pairs,
|
|
294
|
-
decisions:
|
|
294
|
+
decisions: epic_decision.read_recorded_reasons(snapshot.body),
|
|
295
295
|
order_comments: await read_order_comments(order_pairs),
|
|
296
296
|
is_epic_closed: await read_epic_closed(epic_number, epic_fetch.scope_for(snapshot.repo, repo)),
|
|
297
297
|
}
|
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
import { git_epic_parse } from '#scripts/git/git-epic-parse'
|
|
2
1
|
import { git_gh_command } from '#scripts/git/git-gh-command'
|
|
3
2
|
import { cutoff_of, PAGE_CEILING_CAUSE, type ScanCutoff } from '#scripts/git/listing-cutoff'
|
|
4
3
|
import { parse_json_array_safe } from '#scripts/git/parse-json-array'
|
|
5
4
|
import { z } from 'zod'
|
|
6
5
|
import { epic_audit_logic, type AuditFinding, type FindingLevel } from './epic-audit'
|
|
7
6
|
import type { EpicSnapshot } from './epic-fetch'
|
|
7
|
+
import { epic_parse } from './epic-parse'
|
|
8
8
|
|
|
9
9
|
// Check 4's half of `epic:audit`: an issue that names this epic as its parent but that the epic's
|
|
10
10
|
// task list does not track. It would never be run, and the epic would close without it.
|
|
@@ -125,7 +125,7 @@ function claimed_numbers(search: ClaimingSearch): Array<number> {
|
|
|
125
125
|
// reported (joshuafolkken/kit#1014). Read with the same parser `epic_fetch` reads the local rows
|
|
126
126
|
// with, which never matches a `- [ ] owner/repo#N` row.
|
|
127
127
|
function locally_tracked(snapshot: EpicSnapshot): Array<number> {
|
|
128
|
-
return
|
|
128
|
+
return epic_parse.parse_task_list_issue_numbers(snapshot.body)
|
|
129
129
|
}
|
|
130
130
|
|
|
131
131
|
const epic_audit_orphans = {
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import type { DependencyLink } from '#scripts/git/git-epic-parse'
|
|
2
1
|
import { epic_audit_logic, type AuditFinding } from './epic-audit'
|
|
3
2
|
import type { AuditChild } from './epic-audit-checks'
|
|
4
3
|
import { epic_graph, type IssueReference } from './epic-graph'
|
|
4
|
+
import type { DependencyLink } from './epic-parse'
|
|
5
5
|
|
|
6
6
|
// Check 6 — a declared order between two open children that nobody wrote a reason for
|
|
7
7
|
// (joshuafolkken/kit#1712).
|
|
@@ -20,7 +20,7 @@ const REFERENCE_PATTERN = /#(\d+)\b/gu
|
|
|
20
20
|
// What may appear in the `owner/repo` written in front of a `#`: exactly the set the previous
|
|
21
21
|
// lookbehind refused a bare reference after, plus the `.` a repository name may legitimately contain.
|
|
22
22
|
//
|
|
23
|
-
// The dot is where this parse and the task-list parse used to disagree. `
|
|
23
|
+
// The dot is where this parse and the task-list parse used to disagree. `epic_parse`'s
|
|
24
24
|
// `EXTERNAL_REFERENCE_SOURCE` allows one, so `- [ ] owner/site.com#40` is tracked as a genuine
|
|
25
25
|
// cross-repository child; this one excluded it, so a sibling quoting `owner/site.com#40` was read
|
|
26
26
|
// back as `com` — no repository — and every check skipped it in silence. A child that can be tracked
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import { UNORDERED_DEPENDENCIES } from './
|
|
2
|
-
import { DEPENDENCY_ARROW, to_issue_reference } from './
|
|
1
|
+
import { UNORDERED_DEPENDENCIES } from './epic-parse'
|
|
2
|
+
import { DEPENDENCY_ARROW, to_issue_reference } from './epic-reference'
|
|
3
3
|
|
|
4
|
-
// The epic body is the machine-readable half of the epic contract: `scripts/
|
|
4
|
+
// The epic body is the machine-readable half of the epic contract: `scripts/epic/epic-close.ts`
|
|
5
5
|
// reads the task list to decide when the batch is finished, and the order-unrecorded warning reads
|
|
6
6
|
// the `Dependencies` section. Generating both from the same input is what keeps them from
|
|
7
7
|
// disagreeing with each other, or with the run command printed next to them.
|
|
@@ -25,7 +25,7 @@ interface EpicBodyInput {
|
|
|
25
25
|
epic_number?: number | undefined
|
|
26
26
|
}
|
|
27
27
|
|
|
28
|
-
// Task-list syntax, never a bare `#N` link: `
|
|
28
|
+
// Task-list syntax, never a bare `#N` link: `epic_parse.parse_task_list_issue_numbers` matches
|
|
29
29
|
// nothing else, and GitHub only auto-checks a row written this way.
|
|
30
30
|
function format_progress(children: ReadonlyArray<number>): string {
|
|
31
31
|
return children.map((child) => `- [ ] ${to_issue_reference(child)}`).join('\n')
|
|
@@ -139,12 +139,12 @@ function build_dependency_pairs(
|
|
|
139
139
|
.filter((pair) => pair.blocked !== pair.blocker)
|
|
140
140
|
}
|
|
141
141
|
|
|
142
|
-
const
|
|
142
|
+
const epic_body = {
|
|
143
143
|
EPIC_PLACEHOLDER,
|
|
144
144
|
build_epic_body,
|
|
145
145
|
build_dependency_pairs,
|
|
146
146
|
format_run_command,
|
|
147
147
|
}
|
|
148
148
|
|
|
149
|
-
export {
|
|
149
|
+
export { epic_body, SPLIT_RATIONALE_HEADING }
|
|
150
150
|
export type { EpicBodyInput }
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
#!/usr/bin/env tsx
|
|
2
2
|
import { fileURLToPath } from 'node:url'
|
|
3
|
-
import type { IssueReference } from '#scripts/git/git-epic-reference'
|
|
4
3
|
import { git_gh_command } from '#scripts/git/git-gh-command'
|
|
5
4
|
import { cutoff_of, type ScanCutoff } from '#scripts/git/listing-cutoff'
|
|
6
5
|
import { parse_json_array_or_undefined } from '#scripts/git/parse-json-array'
|
|
@@ -18,6 +17,7 @@ import { epic_bundle_gaps } from './epic-bundle-gaps'
|
|
|
18
17
|
import { epic_bundle_referenced, type ReferencedContext } from './epic-bundle-referenced'
|
|
19
18
|
import { epic_index, epic_schema, type FetchedEpics } from './epic-index'
|
|
20
19
|
import { epic_issue } from './epic-issue'
|
|
20
|
+
import type { IssueReference } from './epic-reference'
|
|
21
21
|
|
|
22
22
|
// `josh epic:bundle <N>` — after an issue is filed, look at the open backlog and say whether it
|
|
23
23
|
// belongs with something already there (joshuafolkken/kit#873).
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import type { DependencyLink } from '#scripts/git/git-epic-parse'
|
|
2
|
-
import type { IssueReference } from '#scripts/git/git-epic-reference'
|
|
3
1
|
import { epic_audit_logic } from './epic-audit'
|
|
4
2
|
import { epic_graph } from './epic-graph'
|
|
3
|
+
import type { DependencyLink } from './epic-parse'
|
|
4
|
+
import type { IssueReference } from './epic-reference'
|
|
5
5
|
|
|
6
6
|
// Bundling a newly filed issue with the ones it turns out to be related to.
|
|
7
7
|
//
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { error_text } from '#scripts/lib/error-message'
|
|
1
2
|
import { epic_classify, type ResolveDependency } from './epic-classify'
|
|
2
3
|
import { epic_graph, type EpicChild, type IssueReference } from './epic-graph'
|
|
3
4
|
import type { BlockersReader } from './epic-relation-recheck'
|
|
@@ -118,7 +119,7 @@ async function read_or_withhold(
|
|
|
118
119
|
try {
|
|
119
120
|
return await read_blockers(candidate)
|
|
120
121
|
} catch (error) {
|
|
121
|
-
const reason =
|
|
122
|
+
const reason = error_text.message_of(error)
|
|
122
123
|
|
|
123
124
|
console.warn(
|
|
124
125
|
`⚠ could not confirm the blockers of #${String(candidate.number)}: ${reason}\n` +
|