@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,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` +
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { chain_links, type DependencyLink } from './
|
|
2
|
-
import { DEPENDENCY_ARROW, format_dependency_link, to_issue_reference } from './
|
|
1
|
+
import { chain_links, type DependencyLink } from './epic-parse'
|
|
2
|
+
import { DEPENDENCY_ARROW, format_dependency_link, to_issue_reference } from './epic-reference'
|
|
3
3
|
|
|
4
4
|
// The declared dependency order of an epic, as the shape an insertion needs.
|
|
5
5
|
//
|
|
@@ -392,7 +392,7 @@ function insert_children(
|
|
|
392
392
|
return duplicated === undefined ? outcome : { error: already_declared_error(duplicated) }
|
|
393
393
|
}
|
|
394
394
|
|
|
395
|
-
const
|
|
395
|
+
const epic_chains = {
|
|
396
396
|
render_chains,
|
|
397
397
|
links_of,
|
|
398
398
|
diff_links,
|
|
@@ -407,5 +407,5 @@ const git_epic_chains = {
|
|
|
407
407
|
insert_children,
|
|
408
408
|
}
|
|
409
409
|
|
|
410
|
-
export {
|
|
410
|
+
export { epic_chains }
|
|
411
411
|
export type { Chain, Chains, InsertKind, InsertOutcome, InsertPosition, LinkDiff }
|
|
@@ -2,10 +2,11 @@
|
|
|
2
2
|
/**
|
|
3
3
|
* Check an existing epic Issue against the four requirements, reporting each as pass or fail.
|
|
4
4
|
*
|
|
5
|
-
* Usage: tsx scripts
|
|
5
|
+
* Usage: tsx scripts/epic/epic-check.ts <issue-number>
|
|
6
6
|
*/
|
|
7
|
-
import {
|
|
7
|
+
import { error_text } from '#scripts/lib/error-message'
|
|
8
8
|
import { epic_cli } from './epic-cli'
|
|
9
|
+
import { epic_run } from './epic-run'
|
|
9
10
|
|
|
10
11
|
const ARGV_OFFSET = 2
|
|
11
12
|
const USAGE = 'Usage: josh epic:check <issue-number>'
|
|
@@ -19,7 +20,7 @@ async function main(): Promise<void> {
|
|
|
19
20
|
process.exit(FAILURE_EXIT_CODE)
|
|
20
21
|
}
|
|
21
22
|
|
|
22
|
-
const exit_code = await
|
|
23
|
+
const exit_code = await epic_run.check_epic(epic_number)
|
|
23
24
|
|
|
24
25
|
if (exit_code !== 0) process.exit(exit_code)
|
|
25
26
|
}
|
|
@@ -29,6 +30,6 @@ async function main(): Promise<void> {
|
|
|
29
30
|
try {
|
|
30
31
|
await main()
|
|
31
32
|
} catch (error) {
|
|
32
|
-
console.error(`✖ ${
|
|
33
|
+
console.error(`✖ ${error_text.message_of(error)}`)
|
|
33
34
|
process.exit(FAILURE_EXIT_CODE)
|
|
34
35
|
}
|
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import
|
|
2
|
-
import {
|
|
3
|
-
import { cli_body } from '../scripts/josh/cli-body'
|
|
1
|
+
import { cli_body } from '#scripts/josh/cli-body'
|
|
2
|
+
import type { InsertKind, InsertPosition } from './epic-chains'
|
|
4
3
|
import { epic_cli_argv, ISSUE_NUMBER_PATTERN } from './epic-cli-argv'
|
|
5
4
|
import { epic_cli_remove } from './epic-cli-remove'
|
|
5
|
+
import { epic_parse, type ExternalChild } from './epic-parse'
|
|
6
6
|
|
|
7
7
|
// Parsing lives apart from the entry point so the argument rules can be asserted without spawning a
|
|
8
8
|
// process or reaching GitHub. The entry point is then a thin shell around these two functions.
|
|
@@ -290,7 +290,7 @@ interface CrossRepoAddTarget {
|
|
|
290
290
|
function find_cross_repo_add_target(argv: ReadonlyArray<string>): CrossRepoAddTarget | undefined {
|
|
291
291
|
const [raw_epic] = to_positional_arguments(argv, ADD_VALUE_FLAGS)
|
|
292
292
|
if (raw_epic === undefined) return undefined
|
|
293
|
-
const epic =
|
|
293
|
+
const epic = epic_parse.parse_external_reference(raw_epic)
|
|
294
294
|
if (epic === undefined) return undefined
|
|
295
295
|
|
|
296
296
|
const local = parse_add_arguments(
|
|
@@ -1,12 +1,12 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
1
|
+
import { git_gh_command } from '#scripts/git/git-gh-command'
|
|
2
|
+
import { read_json_listing } from '#scripts/git/parse-json-array'
|
|
3
|
+
import { rest_comment_schema, type RestCommentData } from '#scripts/git/schemas'
|
|
4
|
+
import { epic_parse, type ExternalChild } from './epic-parse'
|
|
5
5
|
|
|
6
6
|
// The comment the epic auto-close announces itself with, and how a run recognizes the one a previous
|
|
7
7
|
// run already posted.
|
|
8
8
|
//
|
|
9
|
-
// Kept apart from `
|
|
9
|
+
// Kept apart from `epic-close.ts` because the two answer different questions — that file decides
|
|
10
10
|
// *whether* an epic may close, this one decides what the announcement says and whether it is already
|
|
11
11
|
// there — and because the file it was split from is within twenty lines of the three hundred a file
|
|
12
12
|
// may hold (joshuafolkken/kit#1039).
|
|
@@ -131,7 +131,7 @@ function is_legacy_announcement(body: string): boolean {
|
|
|
131
131
|
// both: a listing that could not be read says nothing about what is on the issue, and reporting it
|
|
132
132
|
// as "nothing is there" would have the run state a fact it never established — the misread
|
|
133
133
|
// joshuafolkken/kit#973 and joshuafolkken/kit#959 were each about. Keeping it apart is what lets
|
|
134
|
-
// `
|
|
134
|
+
// `epic-close.ts` say out loud that the duplicate check did not run, which is the whole
|
|
135
135
|
// difference between the two answers there.
|
|
136
136
|
//
|
|
137
137
|
// **Both of `read_json_listing`'s gaps arrive as `unreadable`**, rows the schema rejects included.
|
|
@@ -147,7 +147,7 @@ type CloseCommentState = 'present' | 'absent' | 'unreadable'
|
|
|
147
147
|
// verbatim — a fenced one caught by the mask, an indented one by the indent check. `fence_mask` is the same walk every other body predicate
|
|
148
148
|
// in this codebase reads through, rather than a second one written here (joshuafolkken/kit#890).
|
|
149
149
|
function has_marker(body: string, children: ReadonlyArray<string>): boolean {
|
|
150
|
-
const mask =
|
|
150
|
+
const mask = epic_parse.fence_mask(body)
|
|
151
151
|
|
|
152
152
|
return body.split('\n').some((line, index) => {
|
|
153
153
|
if (mask[index] !== true) return false
|
|
@@ -190,10 +190,10 @@ async function read_close_comment_state(
|
|
|
190
190
|
return has_announcement(listing.rows, child_references(epic)) ? 'present' : 'absent'
|
|
191
191
|
}
|
|
192
192
|
|
|
193
|
-
const
|
|
193
|
+
const epic_close_comment = {
|
|
194
194
|
build_close_comment,
|
|
195
195
|
read_close_comment_state,
|
|
196
196
|
}
|
|
197
197
|
|
|
198
198
|
export type { CloseCommentState, EpicChildren }
|
|
199
|
-
export {
|
|
199
|
+
export { epic_close_comment, CLOSE_ANNOUNCEMENT }
|
|
@@ -1,10 +1,14 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
1
|
+
import { git_gh_command } from '#scripts/git/git-gh-command'
|
|
2
|
+
import { EPIC_LABEL } from '#scripts/git/issue-labels'
|
|
3
|
+
import { cutoff_cause, cutoff_of, type ScanCutoff } from '#scripts/git/listing-cutoff'
|
|
4
|
+
import {
|
|
5
|
+
parse_json_array_or_undefined,
|
|
6
|
+
parse_json_object_safe,
|
|
7
|
+
} from '#scripts/git/parse-json-array'
|
|
8
|
+
import { epic_child_schema, epic_issue_schema, type EpicChildData } from '#scripts/git/schemas'
|
|
9
|
+
import { error_text } from '#scripts/lib/error-message'
|
|
10
|
+
import { epic_close_comment } from './epic-close-comment'
|
|
11
|
+
import { epic_parse, type ExternalChild } from './epic-parse'
|
|
8
12
|
|
|
9
13
|
// How many open epics the auto-close will look at. `git-gh-issue-list.ts` pages until `limit` rows
|
|
10
14
|
// have been selected or the backlog runs out, so this is what keeps the ordinary case at exactly one
|
|
@@ -44,9 +48,9 @@ interface SiblingState {
|
|
|
44
48
|
function to_epic_issue(raw: { number: number; body?: string | undefined }): EpicIssue {
|
|
45
49
|
return {
|
|
46
50
|
number: raw.number,
|
|
47
|
-
children:
|
|
48
|
-
external_children:
|
|
49
|
-
has_declared_order:
|
|
51
|
+
children: epic_parse.parse_task_list_issue_numbers(raw.body),
|
|
52
|
+
external_children: epic_parse.parse_external_task_list_children(raw.body),
|
|
53
|
+
has_declared_order: epic_parse.has_declared_dependency_chain(raw.body),
|
|
50
54
|
}
|
|
51
55
|
}
|
|
52
56
|
|
|
@@ -91,7 +95,7 @@ function to_sibling_state(raw_json: string | undefined): SiblingState {
|
|
|
91
95
|
const child = parse_child(raw_json)
|
|
92
96
|
|
|
93
97
|
return {
|
|
94
|
-
is_closed:
|
|
98
|
+
is_closed: epic_parse.is_state_closed(child?.state),
|
|
95
99
|
has_blocked_by: has_blocked_by(child),
|
|
96
100
|
}
|
|
97
101
|
}
|
|
@@ -124,7 +128,7 @@ async function inspect_siblings(
|
|
|
124
128
|
}
|
|
125
129
|
|
|
126
130
|
// `pnpm josh epic --ordered` and `--add` record the declared chain themselves, through the REST
|
|
127
|
-
// dependencies endpoint (`
|
|
131
|
+
// dependencies endpoint (`epic-relations.ts`, joshuafolkken/kit#1026), so the default route
|
|
128
132
|
// arrives here with the relations already in place. Three routes still reach a declared order with
|
|
129
133
|
// none recorded: an epic body written by hand, the manual fallback in `issue-template.md`, and
|
|
130
134
|
// `apply_relations` failing — it counts the refusals, reports that the body is intact and lets the
|
|
@@ -200,11 +204,11 @@ async function close_epic_with(epic_number: string, comment: string | undefined)
|
|
|
200
204
|
// `absent` and one of `unreadable` both post it, and the second says so.
|
|
201
205
|
async function close_epic(epic: EpicIssue): Promise<boolean> {
|
|
202
206
|
const epic_number = String(epic.number)
|
|
203
|
-
const state = await
|
|
207
|
+
const state = await epic_close_comment.read_close_comment_state(epic_number, epic)
|
|
204
208
|
|
|
205
209
|
if (state === 'unreadable') console.info(`ℹ️ Epic #${epic_number}${UNREADABLE_COMMENTS_NOTE}`)
|
|
206
210
|
|
|
207
|
-
const comment = state === 'present' ? undefined :
|
|
211
|
+
const comment = state === 'present' ? undefined : epic_close_comment.build_close_comment(epic)
|
|
208
212
|
|
|
209
213
|
return await close_epic_with(epic_number, comment)
|
|
210
214
|
}
|
|
@@ -256,7 +260,7 @@ async function close_epic_isolated(epic: EpicIssue, merged_number: number): Prom
|
|
|
256
260
|
try {
|
|
257
261
|
return await close_epic_when_complete(epic, merged_number)
|
|
258
262
|
} catch (error) {
|
|
259
|
-
const message =
|
|
263
|
+
const message = error_text.message_of(error)
|
|
260
264
|
|
|
261
265
|
console.info(`⚠️ Skipped epic #${String(epic.number)}: ${message}`)
|
|
262
266
|
|
|
@@ -265,7 +269,7 @@ async function close_epic_isolated(epic: EpicIssue, merged_number: number): Prom
|
|
|
265
269
|
}
|
|
266
270
|
|
|
267
271
|
function parents_of(open_epics: ReadonlyArray<EpicIssue>, child_number: number): Array<EpicIssue> {
|
|
268
|
-
return open_epics.filter((epic) =>
|
|
272
|
+
return open_epics.filter((epic) => epic_parse.has_child(epic.children, child_number))
|
|
269
273
|
}
|
|
270
274
|
|
|
271
275
|
// Closes one epic unless a cycle or a shared child already reached it — the closed number, or
|
|
@@ -351,7 +355,7 @@ async function resolve_and_close_safely(merged_number: number): Promise<void> {
|
|
|
351
355
|
try {
|
|
352
356
|
await resolve_and_close(merged_number)
|
|
353
357
|
} catch (error) {
|
|
354
|
-
const message =
|
|
358
|
+
const message = error_text.message_of(error)
|
|
355
359
|
|
|
356
360
|
console.info(`⚠️ Skipped the epic auto-close check: ${message}`)
|
|
357
361
|
}
|
|
@@ -376,12 +380,12 @@ async function close_completed_epics(input: {
|
|
|
376
380
|
await resolve_and_close_safely(merged_number)
|
|
377
381
|
}
|
|
378
382
|
|
|
379
|
-
const
|
|
383
|
+
const epic_close = {
|
|
380
384
|
close_completed_epics,
|
|
381
385
|
}
|
|
382
386
|
|
|
383
387
|
export {
|
|
384
|
-
|
|
388
|
+
epic_close,
|
|
385
389
|
close_completed_epics,
|
|
386
390
|
truncated_epic_list_note,
|
|
387
391
|
EPIC_LIST_LIMIT,
|
|
@@ -4,7 +4,6 @@ import { git_gh_api_path } from '#scripts/git/git-gh-api-path'
|
|
|
4
4
|
import { git_gh_exec } from '#scripts/git/git-gh-exec'
|
|
5
5
|
import { propagate_publish } from '#scripts/propagate/propagate-publish'
|
|
6
6
|
import { derive_versions_endpoint } from '#scripts/version/version-command-config'
|
|
7
|
-
import { execaSync } from 'execa'
|
|
8
7
|
import { load } from 'js-yaml'
|
|
9
8
|
import type { DependencyVerdict } from './epic-classify'
|
|
10
9
|
import type { EpicChild } from './epic-graph'
|
|
@@ -71,7 +70,6 @@ function is_published(repo: string, version: string): boolean {
|
|
|
71
70
|
}
|
|
72
71
|
|
|
73
72
|
const GH_TIMEOUT_MS = 20_000
|
|
74
|
-
const SUCCESS_EXIT_CODE = 0
|
|
75
73
|
// The manifest arrives base64-encoded in the API's `content` field; `jq` decodes and reads it.
|
|
76
74
|
const VERSION_JQ = '.content | @base64d | fromjson | {version, private, workspaces} | tojson'
|
|
77
75
|
const PNPM_WORKSPACE_FILE = 'pnpm-workspace.yaml'
|
|
@@ -147,24 +145,20 @@ function pnpm_workspace_path(repo: string): string {
|
|
|
147
145
|
|
|
148
146
|
// A read that failed for any reason other than a missing file leaves the layout unknown.
|
|
149
147
|
function classify_workspace_failure(repo: string): WorkspaceCheck {
|
|
150
|
-
const
|
|
151
|
-
...git_gh_exec.direct_environment(),
|
|
152
|
-
reject: false,
|
|
153
|
-
timeout: GH_TIMEOUT_MS,
|
|
154
|
-
})
|
|
148
|
+
const status = git_gh_exec.exec_gh_api_status_sync(pnpm_workspace_path(repo), GH_TIMEOUT_MS)
|
|
155
149
|
|
|
156
|
-
return
|
|
150
|
+
return status === NOT_FOUND_STATUS ? 'single' : 'unreadable'
|
|
157
151
|
}
|
|
158
152
|
|
|
159
153
|
function read_pnpm_workspace(repo: string): WorkspaceCheck {
|
|
160
|
-
const
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
154
|
+
const stdout = git_gh_exec.read_gh_api_sync({
|
|
155
|
+
path: pnpm_workspace_path(repo),
|
|
156
|
+
jq_filter: WORKSPACE_JQ,
|
|
157
|
+
timeout_ms: GH_TIMEOUT_MS,
|
|
164
158
|
})
|
|
165
|
-
if (
|
|
159
|
+
if (stdout === undefined) return classify_workspace_failure(repo)
|
|
166
160
|
|
|
167
|
-
return declares_packages(
|
|
161
|
+
return declares_packages(stdout) ? 'workspace' : 'single'
|
|
168
162
|
}
|
|
169
163
|
|
|
170
164
|
// Whether the repository declares a workspace. `workspaces` covers npm and yarn; pnpm keeps its
|
|
@@ -227,38 +221,27 @@ function to_manifest(repo: string, stdout: string): ManifestAnswer {
|
|
|
227
221
|
}
|
|
228
222
|
|
|
229
223
|
// Why the read failed, asked only when it did. The status line is the protocol rather than prose, so
|
|
230
|
-
// a missing manifest is told from a rate limit by matching a contract instead of a message
|
|
231
|
-
// `git_gh_exec.parse_status_line` rather than a second copy of it.
|
|
224
|
+
// a missing manifest is told from a rate limit by matching a contract instead of a message.
|
|
232
225
|
//
|
|
233
|
-
//
|
|
234
|
-
//
|
|
235
|
-
//
|
|
236
|
-
//
|
|
237
|
-
//
|
|
238
|
-
// what is missing is the file.
|
|
226
|
+
// The probe is `exec_gh_api_status_sync`, the synchronous twin of `exec_gh_api_status`: every caller
|
|
227
|
+
// down to `resolve_cross_repo` is synchronous, and turning the classifier, the resolver and
|
|
228
|
+
// `epic_classify` async for one probe would cost more than the twin (joshuafolkken/kit#2901). A 404
|
|
229
|
+
// here is trustworthy in a way a registry 404 is not: the repository's issues are already being read,
|
|
230
|
+
// so access is established and what is missing is the file.
|
|
239
231
|
function classify_manifest_failure(repo: string): ManifestAnswer {
|
|
240
|
-
const
|
|
241
|
-
...git_gh_exec.direct_environment(),
|
|
242
|
-
reject: false,
|
|
243
|
-
timeout: GH_TIMEOUT_MS,
|
|
244
|
-
})
|
|
232
|
+
const status = git_gh_exec.exec_gh_api_status_sync(manifest_path(repo), GH_TIMEOUT_MS)
|
|
245
233
|
|
|
246
|
-
return {
|
|
247
|
-
kind:
|
|
248
|
-
git_gh_exec.parse_status_line(probe.stdout) === NOT_FOUND_STATUS ? 'absent' : 'unreadable',
|
|
249
|
-
}
|
|
234
|
+
return { kind: status === NOT_FOUND_STATUS ? 'absent' : 'unreadable' }
|
|
250
235
|
}
|
|
251
236
|
|
|
252
237
|
function fetch_manifest(repo: string): ManifestAnswer {
|
|
253
|
-
const
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
238
|
+
const stdout = git_gh_exec.read_gh_api_sync({
|
|
239
|
+
path: manifest_path(repo),
|
|
240
|
+
jq_filter: VERSION_JQ,
|
|
241
|
+
timeout_ms: GH_TIMEOUT_MS,
|
|
257
242
|
})
|
|
258
243
|
|
|
259
|
-
return
|
|
260
|
-
? to_manifest(repo, result.stdout)
|
|
261
|
-
: classify_manifest_failure(repo)
|
|
244
|
+
return stdout === undefined ? classify_manifest_failure(repo) : to_manifest(repo, stdout)
|
|
262
245
|
}
|
|
263
246
|
|
|
264
247
|
// Read once per pass, for the reason `published_cache` holds the other half: every edge to one
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { format_replaced_relations } from './
|
|
3
|
-
import {
|
|
1
|
+
import { epic_parse, type DependencyLink } from './epic-parse'
|
|
2
|
+
import { format_replaced_relations } from './epic-reference'
|
|
3
|
+
import { epic_sections, type BodyLines, type SectionRange } from './epic-sections'
|
|
4
4
|
|
|
5
5
|
// Placing a decision record inside an epic body's `## Decisions` section.
|
|
6
6
|
//
|
|
@@ -45,10 +45,8 @@ function to_declaration_error(line_number: number): string {
|
|
|
45
45
|
function find_decision_error(record: string): string | undefined {
|
|
46
46
|
if (record.trim().length === 0) return EMPTY_RECORD
|
|
47
47
|
|
|
48
|
-
const input =
|
|
49
|
-
const [found] =
|
|
50
|
-
git_epic_parse.is_declaration_line(line),
|
|
51
|
-
)
|
|
48
|
+
const input = epic_sections.to_body_lines(record)
|
|
49
|
+
const [found] = epic_sections.find_indices(input, (line) => epic_parse.is_declaration_line(line))
|
|
52
50
|
|
|
53
51
|
return found === undefined ? undefined : to_declaration_error(found + FIRST_LINE_NUMBER)
|
|
54
52
|
}
|
|
@@ -81,8 +79,8 @@ function last_content_index(input: BodyLines, range: SectionRange): number {
|
|
|
81
79
|
// The section's *end* rather than its start: the entries are a log, and a reader looking for the most
|
|
82
80
|
// recent decision reads down. Nothing else in the body moves.
|
|
83
81
|
function append_decision(body: string, record: string): string {
|
|
84
|
-
const input =
|
|
85
|
-
const range =
|
|
82
|
+
const input = epic_sections.to_body_lines(body)
|
|
83
|
+
const range = epic_sections.find_section_range(input, DECISIONS_HEADING_PATTERN)
|
|
86
84
|
if (range === undefined) return to_new_section(input.lines, record).join('\n')
|
|
87
85
|
|
|
88
86
|
const at = last_content_index(input, range)
|
|
@@ -129,13 +127,13 @@ function format_decision_report(input: { total: number; failures: number }): str
|
|
|
129
127
|
}
|
|
130
128
|
|
|
131
129
|
// The heading a creation writes its reasoning under. Beside a pattern exactly as `DECISIONS_HEADING`
|
|
132
|
-
// is, and tied to `
|
|
130
|
+
// is, and tied to `epic_body`'s own literal by a test that reads a body the builder produced
|
|
133
131
|
// rather than by a string comparison (joshuafolkken/kit#1712).
|
|
134
132
|
const RATIONALE_HEADING_PATTERN = /^#{1,6}[ \t]+Split rationale\b/u
|
|
135
133
|
|
|
136
134
|
function read_section(body: string, heading: RegExp): string {
|
|
137
|
-
const input =
|
|
138
|
-
const range =
|
|
135
|
+
const input = epic_sections.to_body_lines(body)
|
|
136
|
+
const range = epic_sections.find_section_range(input, heading)
|
|
139
137
|
if (range === undefined) return ''
|
|
140
138
|
|
|
141
139
|
return input.lines.slice(range.start, range.end).join('\n')
|
|
@@ -159,7 +157,7 @@ function read_recorded_reasons(body: string | undefined): string {
|
|
|
159
157
|
].join('\n')
|
|
160
158
|
}
|
|
161
159
|
|
|
162
|
-
const
|
|
160
|
+
const epic_decision = {
|
|
163
161
|
DECISIONS_HEADING,
|
|
164
162
|
append_decision,
|
|
165
163
|
append_replacements,
|
|
@@ -168,4 +166,4 @@ const git_epic_decision = {
|
|
|
168
166
|
format_decision_report,
|
|
169
167
|
}
|
|
170
168
|
|
|
171
|
-
export {
|
|
169
|
+
export { epic_decision }
|
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
import { git_epic_parse, type ExternalChild } from '#scripts/git/git-epic-parse'
|
|
2
1
|
import { git_gh_command } from '#scripts/git/git-gh-command'
|
|
3
2
|
import type { IssueReadFailure } from '#scripts/git/git-gh-issue-read'
|
|
4
3
|
import { epic_cross_repo } from './epic-cross-repo'
|
|
5
4
|
import type { EpicChild, IssueReference } from './epic-graph'
|
|
6
5
|
import { epic_issue, type EpicIssue } from './epic-issue'
|
|
6
|
+
import { epic_parse, type ExternalChild } from './epic-parse'
|
|
7
7
|
import { epic_relation_recheck } from './epic-relation-recheck'
|
|
8
8
|
|
|
9
9
|
// Reading an epic and its children from GitHub.
|
|
@@ -73,8 +73,8 @@ async function fetch_epic_body(epic_number: number, scope?: string): Promise<Epi
|
|
|
73
73
|
return {
|
|
74
74
|
body,
|
|
75
75
|
body_failure: read.kind === 'read' ? undefined : read,
|
|
76
|
-
child_numbers:
|
|
77
|
-
external:
|
|
76
|
+
child_numbers: epic_parse.parse_task_list_issue_numbers(body),
|
|
77
|
+
external: epic_parse.parse_external_task_list_children(body),
|
|
78
78
|
}
|
|
79
79
|
}
|
|
80
80
|
|
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
import type { DependencyLink } from '
|
|
1
|
+
import type { DependencyLink } from './epic-parse'
|
|
2
2
|
import {
|
|
3
3
|
format_dependency_link,
|
|
4
4
|
join_references,
|
|
5
5
|
to_issue_reference,
|
|
6
6
|
type IssueReference,
|
|
7
|
-
} from '
|
|
7
|
+
} from './epic-reference'
|
|
8
8
|
|
|
9
9
|
// The dependency graph an epic's children form, and the two ways it can be wrong.
|
|
10
10
|
//
|
|
@@ -275,5 +275,5 @@ const epic_graph = {
|
|
|
275
275
|
}
|
|
276
276
|
|
|
277
277
|
export type { EpicChild, GraphAnomaly, GraphAnomalyKind }
|
|
278
|
-
export type { IssueReference } from '
|
|
278
|
+
export type { IssueReference } from './epic-reference'
|
|
279
279
|
export { epic_graph }
|
|
@@ -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 { EPIC_LABEL, has_any_label } from '#scripts/git/issue-labels'
|
|
4
3
|
import { cutoff_of, type ScanCutoff } from '#scripts/git/listing-cutoff'
|
|
5
4
|
import { parse_json_array_or_undefined } from '#scripts/git/parse-json-array'
|
|
6
5
|
import type { OpenIssueData } from '#scripts/git/schemas'
|
|
7
6
|
import { z } from 'zod'
|
|
7
|
+
import { epic_parse } from './epic-parse'
|
|
8
8
|
|
|
9
9
|
// Which epic tracks which issue — the one answer, in one place (joshuafolkken/kit#1633).
|
|
10
10
|
//
|
|
@@ -40,7 +40,7 @@ function build_tracking_index(
|
|
|
40
40
|
const index = new Map<number, ReadonlyArray<number>>()
|
|
41
41
|
|
|
42
42
|
for (const epic of epics) {
|
|
43
|
-
for (const child of
|
|
43
|
+
for (const child of epic_parse.parse_task_list_issue_numbers(epic.body)) {
|
|
44
44
|
index.set(child, [...(index.get(child) ?? []), epic.number])
|
|
45
45
|
}
|
|
46
46
|
}
|
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import type { IssueReference } from '#scripts/git/git-epic-reference'
|
|
2
1
|
import { git_gh_issue_rest } from '#scripts/git/git-gh-issue-rest'
|
|
3
2
|
import { parse_json_object_safe } from '#scripts/git/parse-json-array'
|
|
4
3
|
import { blocked_by_schema } from '#scripts/git/schemas'
|
|
5
4
|
import { z } from 'zod'
|
|
5
|
+
import type { IssueReference } from './epic-reference'
|
|
6
6
|
|
|
7
7
|
// The shapes one issue is read as, and the argument every epic command takes.
|
|
8
8
|
//
|