@joshuafolkken/kit 1.1050.0 → 1.1128.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 +9 -8
- package/.claude/skills/epic-commands/SKILL.md +5 -7
- package/.claude/skills/workflow-commands/SKILL.md +20 -12
- package/.claude/skills/workflow-commands/background-commands.md +36 -84
- package/.claude/skills/workflow-commands/backlogrun-child.md +1 -1
- package/.claude/skills/workflow-commands/backlogrun-lanes.md +8 -6
- package/.claude/skills/workflow-commands/backlogrun-progress.md +30 -33
- package/.claude/skills/workflow-commands/backlogrun-steps.md +12 -12
- package/.claude/skills/workflow-commands/backlogrun.md +2 -2
- package/.claude/skills/workflow-commands/chain-rule.md +4 -2
- package/.claude/skills/workflow-commands/delegation.md +3 -3
- package/.claude/skills/workflow-commands/followup-reference.md +1 -1
- package/.claude/skills/workflow-commands/followup.md +7 -3
- package/.claude/skills/workflow-commands/fullrun-steps.md +3 -2
- package/.claude/skills/workflow-commands/fullrun.md +8 -6
- package/.claude/skills/workflow-commands/halfrun.md +19 -21
- package/.claude/skills/workflow-commands/issue-comments.md +1 -1
- package/.claude/skills/workflow-commands/issue-fold-existing.md +10 -10
- package/.claude/skills/workflow-commands/issue-scout.md +1 -1
- package/.claude/skills/workflow-commands/kickoff.md +9 -4
- package/.claude/skills/workflow-commands/latest-gate.md +8 -16
- package/.claude/skills/workflow-commands/needs-human-review.md +1 -1
- package/.claude/skills/workflow-commands/observation-filing.md +3 -3
- package/.claude/skills/workflow-commands/pre-gate-cut.md +4 -8
- package/.claude/skills/workflow-commands/prerequisite.md +1 -1
- package/.claude/skills/workflow-commands/prrun.md +36 -0
- package/.claude/skills/workflow-commands/retrospective.md +1 -1
- package/.claude/skills/workflow-commands/split-assessment.md +4 -18
- package/.claude/skills/workflow-commands/working-tree-hold.md +22 -2
- package/.codex/hooks.json +16 -20
- package/.github/actions/setup-node/action.yml +33 -0
- package/.github/actions/setup-pnpm/action.yml +93 -0
- package/.github/dependabot.yml +5 -1
- package/.github/workflows/auto-tag.yml +2 -3
- package/.github/workflows/ci.yml +31 -134
- package/.github/workflows/dependabot-auto-merge.yml +1 -0
- package/.github/workflows/pr-classification.yml +6 -44
- package/.github/workflows/production.yml +1 -0
- package/.github/workflows/publish.yml +43 -47
- package/.github/workflows/sonar-qube.yml +41 -4
- package/.pnpmfile.mjs +29 -0
- package/AGENTS.md +5 -2
- package/CLAUDE.md +33 -27
- package/GEMINI.md +5 -2
- package/README.md +66 -20
- package/SECURITY.md +37 -6
- package/cspell/index.yaml +6 -1
- package/dist/CLAUDE.basic.md +6 -6
- package/dist/CLAUDE.md +33 -27
- package/dist/CLAUDE.static.md +6 -6
- package/dist/config-merge/index.js +48 -12
- package/dist/hooks/batch-guard.js +4 -4
- package/dist/hooks/{chunk-7VPDUBRN.js → chunk-4JZDP5X5.js} +152 -155
- package/dist/hooks/{chunk-HJIISEAC.js → chunk-5SD2YM7Z.js} +5 -13
- package/dist/hooks/{chunk-NAS4C7GP.js → chunk-77J7IEQM.js} +2 -2
- package/dist/hooks/{chunk-DMQN45FN.js → chunk-7HVB7SZG.js} +3 -3
- package/dist/hooks/{chunk-RLDX5SOC.js → chunk-AIOIOMPX.js} +4413 -1970
- package/dist/hooks/{chunk-5BK3K7V4.js → chunk-M7QKEKWO.js} +873 -2049
- package/dist/hooks/{chunk-WQ4OSBSS.js → chunk-MKVGBQPA.js} +2 -2
- package/dist/hooks/{chunk-EJDX2TOB.js → chunk-NXYMNPUQ.js} +7 -88
- package/dist/hooks/{chunk-NJQOBE6L.js → chunk-QVLT52OV.js} +2 -7
- package/dist/hooks/{chunk-GQ7KP45M.js → chunk-XYUIQOW3.js} +2 -2
- package/dist/hooks/codex-hook-adapter.js +12 -48
- package/dist/hooks/duplicate-read-guard.js +5 -5
- package/dist/hooks/format-edited.js +2 -2
- package/dist/hooks/inputs.json +280 -0
- package/dist/hooks/investigation-guard.js +5 -5
- package/dist/hooks/pretool-guard.js +9 -9
- package/dist/hooks/session-lang.js +0 -1
- package/dist/hooks/stop-guard.js +192 -333
- package/dist/josh.js +240 -148
- package/dist/managed-marker/index.js +2 -1
- package/dist/version/index.js +70 -78
- package/eslint/rules/promise.js +5 -0
- package/eslint/rules/unicorn.js +3 -2
- package/package.json +26 -5
- package/pnpm-workspace.yaml +5 -7
- package/prompts/collaboration-workflow/glossary.md +17 -0
- package/prompts/collaboration-workflow/issue-citation.md +5 -5
- package/prompts/collaboration-workflow/issue-template.md +1 -1
- package/prompts/collaboration-workflow/operating-rules.md +5 -5
- package/prompts/collaboration-workflow/overview.md +13 -1
- package/prompts/collaboration-workflow/principles.md +9 -0
- package/prompts/collaboration-workflow/report-format.md +1 -1
- package/prompts/collaboration-workflow/residency.md +3 -3
- package/prompts/collaboration-workflow/rule-delivery.md +1 -1
- package/prompts/collaboration-workflow/turn-batching.md +3 -3
- package/prompts/collaboration-workflow/upstream-interrupt.md +1 -1
- package/prompts/collaboration-workflow/wip-cap.md +25 -22
- package/prompts/collaboration-workflow.md +6 -8
- package/prompts/review-rubric.md +1 -1
- package/prompts/review.md +3 -3
- package/prompts/sonar-hotspot-handling.md +11 -3
- package/prompts/testing-guide.md +3 -3
- package/scripts/adopt/adopt-toolkits.ts +0 -2
- package/scripts/adopt/adopt.ts +0 -2
- package/scripts/agent/agent-argv.ts +0 -2
- package/scripts/agent/agent-diagnostics.ts +1 -1
- package/scripts/agent/agent-role-profile.ts +0 -7
- package/scripts/agent/claude-agent-argv.ts +0 -2
- package/scripts/agent/codex-agent-argv.ts +1 -1
- package/scripts/agent/codex-hooks.ts +92 -0
- package/scripts/agent/interactive-ask.ts +1 -1
- package/scripts/auto-ok/auto-ok-cli.ts +6 -16
- package/scripts/backlog/backlog-arrival.ts +30 -10
- package/scripts/backlog/backlog-budget-cli.ts +15 -16
- package/scripts/backlog/backlog-budget.ts +0 -2
- package/scripts/backlog/backlog-defect-priority.ts +2 -2
- package/scripts/backlog/backlog-drive-cli.ts +10 -8
- package/scripts/backlog/backlog-drive-named-offer.ts +16 -3
- package/scripts/backlog/backlog-drive-named.ts +2 -1
- package/scripts/backlog/backlog-drive.ts +76 -16
- package/scripts/backlog/backlog-named.ts +0 -3
- package/scripts/backlog/backlog-next.ts +9 -15
- package/scripts/backlog/backlog-offer-cli.ts +6 -19
- package/scripts/backlog/backlog-plan-cli.ts +1 -7
- package/scripts/backlog/backlog-plan.ts +30 -19
- package/scripts/backlog/backlog-pool.ts +3 -6
- package/scripts/backlog/backlog-rank.ts +2 -2
- package/scripts/backlog/backlog-ready.ts +39 -10
- package/scripts/backlog/backlog-scope.ts +1 -3
- package/scripts/backlog/backlog-stalled-detect.ts +2 -2
- package/scripts/backlog/backlog-stalled.ts +0 -1
- package/scripts/backlog/backlog-waves.ts +5 -2
- package/scripts/behavior/behavior-cli.ts +1 -1
- package/scripts/build/build-codex-hooks.ts +26 -0
- package/scripts/build/build-hooks.ts +85 -9
- package/scripts/build/hook-bundle-stamp.ts +111 -0
- package/scripts/cases/cases-cli.ts +1 -1
- package/scripts/cases/cases-logic.ts +1 -1
- package/scripts/ci/composite-actions.ts +30 -0
- package/scripts/ci/pr-classification.ts +3 -3
- package/scripts/claude/workflow-destination.ts +10 -1
- package/scripts/clone/clone-fingerprint.ts +0 -1
- package/scripts/clone/clone-scan-cli.ts +1 -1
- package/scripts/clone/clone-scan.ts +1 -1
- package/scripts/config-merge/json-format.ts +0 -1
- package/scripts/config-merge/parse-jsonc.ts +33 -3
- package/scripts/config-merge/patch-json-key.ts +13 -6
- package/scripts/config-merge/patch-yaml-list.ts +1 -1
- package/scripts/cost-runtime/codex-usage.ts +2 -9
- package/scripts/cost-runtime/cost-attribute.ts +0 -1
- package/scripts/cost-runtime/cost-cli.ts +3 -10
- package/scripts/cost-runtime/cost-corpus.ts +0 -2
- package/scripts/cost-runtime/cost-format.ts +0 -3
- package/scripts/cost-runtime/cost-pricing.ts +0 -1
- package/scripts/cost-runtime/cost-tokens.ts +0 -1
- package/scripts/delegation/delegation-cli.ts +1 -1
- package/scripts/delegation/delegation-policy.ts +0 -1
- package/scripts/delegation/duplicate-read-guard.ts +0 -8
- package/scripts/delegation/fanout-cli.ts +1 -1
- package/scripts/delegation/fanout.ts +0 -2
- package/scripts/delegation/investigation-reads.ts +1 -164
- package/scripts/discovery/repo-map-logic.ts +0 -1
- package/scripts/discovery/repo-party-cli.ts +1 -1
- package/scripts/doctor/doctor-consumer.ts +6 -10
- package/scripts/doctor/doctor-io.ts +9 -13
- package/scripts/doctor/doctor-logic.ts +0 -4
- package/scripts/doctor/doctor.ts +14 -9
- package/scripts/document/backlogrun-parent-read-set.ts +12 -1
- package/scripts/document/document-byte-budget.ts +5 -5
- package/scripts/document/document-byte-check.ts +0 -1
- package/scripts/document/document-read-cli.ts +0 -2
- package/scripts/document/document-section-cli.ts +1 -1
- package/scripts/document/document-section.ts +0 -3
- package/scripts/document/edit-files-cli.ts +0 -1
- package/scripts/document/entry-read-budget.ts +21 -7
- package/scripts/document/entry-read-set.ts +32 -8
- package/scripts/document/lane-child-read-set.ts +8 -6
- package/scripts/document/read-files-cli.ts +0 -4
- package/scripts/document/read-set-cli.ts +0 -2
- package/scripts/document/read-set-point-of-use.ts +9 -3
- package/scripts/document/read-set-pricing.ts +0 -1
- package/scripts/document/read-set-trim.ts +4 -2
- package/scripts/epic/epic-add.ts +1 -1
- package/scripts/epic/epic-audit-cli.ts +1 -4
- package/scripts/epic/epic-audit-orphans.ts +1 -3
- package/scripts/epic/epic-audit-rationale.ts +0 -1
- package/scripts/epic/epic-audit-report.ts +0 -2
- package/scripts/epic/epic-audit.ts +8 -22
- package/scripts/epic/epic-bundle-cli.ts +1 -1
- package/scripts/epic/epic-bundle-evidence.ts +0 -1
- package/scripts/epic/epic-bundle-referenced.ts +1 -5
- package/scripts/epic/epic-bundle.ts +0 -4
- package/scripts/epic/epic-busy.ts +4 -5
- package/scripts/epic/epic-candidate-confirm.ts +0 -5
- package/scripts/epic/epic-classify.ts +2 -2
- package/scripts/epic/epic-cli-argv.ts +0 -1
- package/scripts/epic/epic-close-comment.ts +1 -1
- package/scripts/epic/epic-close.ts +5 -2
- package/scripts/epic/epic-cross-repo.ts +2 -4
- package/scripts/epic/epic-decision.ts +0 -1
- package/scripts/epic/epic-fetch.ts +2 -2
- package/scripts/epic/epic-index.ts +2 -3
- package/scripts/epic/epic-issue.ts +1 -15
- package/scripts/epic/epic-lane-offer.ts +9 -8
- package/scripts/epic/epic-nested.ts +1 -1
- package/scripts/epic/epic-next-read.ts +1 -3
- package/scripts/epic/epic-next-views.ts +0 -1
- package/scripts/epic/epic-next.ts +1 -10
- package/scripts/epic/epic-parse.ts +0 -1
- package/scripts/epic/epic-promote.ts +0 -9
- package/scripts/epic/epic-rank.ts +2 -2
- package/scripts/epic/epic-read.ts +1 -2
- package/scripts/epic/epic-reconcile.ts +1 -1
- package/scripts/epic/epic-reference.ts +1 -13
- package/scripts/epic/epic-relation-recheck.ts +1 -1
- package/scripts/epic/epic-relations.ts +1 -1
- package/scripts/epic/epic-remove.ts +1 -1
- package/scripts/epic/epic-report.ts +0 -1
- package/scripts/epic/epic-run.ts +2 -3
- package/scripts/epic/epic-shape.ts +1 -1
- package/scripts/epic/epic-solo-stale.ts +107 -0
- package/scripts/epic/epic-solo.ts +3 -2
- package/scripts/epic/epic-triage.ts +1 -1
- package/scripts/epic/epic-validate.ts +1 -1
- package/scripts/epic/epic.ts +1 -1
- package/scripts/exports/unused-members-cli.ts +53 -0
- package/scripts/{git → followup}/followup-issue-number.ts +1 -6
- package/scripts/{git → followup}/git-followup-cleanup.ts +2 -1
- package/scripts/{git → followup}/git-followup-finish.ts +1 -3
- package/scripts/{git → followup}/git-followup-flush.ts +2 -2
- package/scripts/{git → followup}/git-followup-issue-close.ts +2 -2
- package/scripts/{git → followup}/git-followup-label.ts +4 -4
- package/scripts/followup/git-followup-merged.ts +112 -0
- package/scripts/{git → followup}/git-followup-pending.ts +1 -1
- package/scripts/{git → followup}/git-followup-workflow.ts +19 -11
- package/scripts/{git → followup}/git-pr-followup-wrapup.ts +9 -3
- package/scripts/{git → followup}/git-pr-followup.ts +53 -16
- package/scripts/gate/core-budget.ts +3 -9
- package/scripts/gate/gate-plan.ts +7 -4
- package/scripts/gate/gate-report.ts +0 -6
- package/scripts/gate/gate-tree.ts +1 -1
- package/scripts/gate/pre-commit-type-check.ts +0 -6
- package/scripts/gate/project-checks.ts +7 -8
- package/scripts/gate/scoped-green.ts +0 -1
- package/scripts/gate/type-check-step.ts +1 -1
- package/scripts/gate/verification-gate.ts +0 -2
- package/scripts/gh/fix-gh-packages.ts +15 -12
- package/scripts/gh/gh-spawn.ts +2 -2
- package/scripts/{git → gh}/git-ai-review-scan.ts +1 -5
- package/scripts/{git → gh}/git-gh-check.ts +2 -1
- package/scripts/{git → gh}/git-gh-exec.ts +1 -1
- package/scripts/{git → gh}/git-gh-failure.ts +1 -1
- package/scripts/{git → gh}/git-gh-helpers.ts +0 -1
- package/scripts/{git → gh}/git-gh-issue-list.ts +13 -10
- package/scripts/{git → gh}/git-gh-issue-read.ts +1 -1
- package/scripts/{git → gh}/git-gh-issue-rest.ts +6 -3
- package/scripts/{git → gh}/git-gh-issue-write.ts +4 -1
- package/scripts/{git → gh}/git-gh-pr-auto-merge.ts +4 -1
- package/scripts/{git → gh}/git-gh-pr-read.ts +24 -0
- package/scripts/{git → gh}/git-gh-pr-rest.ts +5 -2
- package/scripts/{git → gh}/git-gh-pr-review.ts +2 -2
- package/scripts/{git → gh}/git-gh-pr-rollup.ts +1 -3
- package/scripts/{git → gh}/git-gh-pr.ts +3 -1
- package/scripts/{git → gh}/git-pr-ai-review.ts +2 -5
- package/scripts/{git → gh}/git-pr-checks-eval.ts +0 -10
- package/scripts/{git → gh}/git-pr-checks-parse.ts +4 -20
- package/scripts/{git → gh}/git-pr-checks.ts +2 -1
- package/scripts/{git → gh}/git-pr-coderabbit.ts +2 -5
- package/scripts/{git → gh}/git-pr-confirmation.ts +1 -3
- package/scripts/{git → gh}/git-pr-managed-config.ts +2 -3
- package/scripts/{git → gh}/git-pr.ts +3 -3
- package/scripts/git/changed-file-scope.ts +0 -1
- package/scripts/git/git-branch.ts +3 -6
- package/scripts/git/git-command.ts +6 -1
- package/scripts/git/git-commit.ts +1 -1
- package/scripts/git/git-common-directory.ts +80 -24
- package/scripts/git/git-error.ts +66 -9
- package/scripts/git/git-issue.ts +1 -1
- package/scripts/git/git-preflight.ts +2 -2
- package/scripts/git/git-prompt.ts +6 -31
- package/scripts/git/git-push.ts +1 -1
- package/scripts/git/git-spawn-sync.ts +65 -0
- package/scripts/git/git-spawn.ts +5 -4
- package/scripts/git/git-staging.ts +1 -0
- package/scripts/git/git-status.ts +1 -1
- package/scripts/git/git-workflow.ts +1 -1
- package/scripts/git/gone-branch.ts +1 -0
- package/scripts/git/listing-cutoff.ts +1 -1
- package/scripts/git/main-merge.ts +1 -1
- package/scripts/git/main-sync.ts +19 -1
- package/scripts/git/parse-json-array.ts +0 -3
- package/scripts/git/repository-lock.ts +16 -16
- package/scripts/git/{git-stash.ts → stash/git-stash.ts} +1 -1
- package/scripts/git/{stash-pop-args.ts → stash/stash-pop-args.ts} +2 -7
- package/scripts/git/{stash-pop-cli.ts → stash/stash-pop-cli.ts} +0 -1
- package/scripts/git/{stash-sweep-lock.ts → stash/stash-sweep-lock.ts} +3 -3
- package/scripts/hooks/codex-hook-adapter.ts +0 -1
- package/scripts/hooks/edited-cspell.ts +1 -1
- package/scripts/hooks/format-edited-file.ts +2 -0
- package/scripts/hooks/pre-push-unit.ts +0 -4
- package/scripts/hooks/pretool-guard.ts +22 -4
- package/scripts/hooks/reserved-run.ts +1 -1
- package/scripts/hooks/step-zero-notice.ts +76 -0
- package/scripts/hooks/stop-guard.ts +47 -21
- package/scripts/init/claude-plugin-config.ts +4 -5
- package/scripts/init/hook-command-rewrite.ts +16 -8
- package/scripts/init/hook-launch.ts +41 -15
- package/scripts/init/init-basic.ts +2 -5
- package/scripts/init/init-file-action.ts +54 -0
- package/scripts/init/init-logic-migrate.ts +35 -17
- package/scripts/init/init-logic-secretlint.ts +1 -1
- package/scripts/init/init-logic.ts +10 -19
- package/scripts/init/init.ts +11 -71
- package/scripts/init/kit-written-paths.ts +2 -0
- package/scripts/init/project-profile.ts +9 -17
- package/scripts/init/start-exec.ts +1 -1
- package/scripts/init/start-setup-pr.ts +6 -6
- package/scripts/init/start-steps.ts +3 -1
- package/scripts/issue/behavior-change-lint.ts +0 -1
- package/scripts/issue/defect-rate-cli.ts +64 -23
- package/scripts/issue/defect-rate.ts +1 -2
- package/scripts/{git → issue}/git-next-issues.ts +3 -3
- package/scripts/issue/issue-backlinks-cli.ts +1 -5
- package/scripts/issue/issue-backlinks.ts +7 -14
- package/scripts/issue/issue-cite-cli.ts +2 -6
- package/scripts/issue/issue-cite.ts +12 -8
- package/scripts/issue/issue-classification.ts +1 -9
- package/scripts/issue/issue-closing-pr.ts +4 -4
- package/scripts/issue/issue-comment-cli.ts +2 -2
- package/scripts/issue/issue-file-cli.ts +5 -5
- package/scripts/issue/issue-file.ts +7 -14
- package/scripts/issue/issue-fold-cli.ts +1 -1
- package/scripts/issue/issue-fold-existing-cli.ts +1 -1
- package/scripts/{git → issue}/issue-labels.ts +8 -0
- package/scripts/issue/issue-lint-cli.ts +1 -1
- package/scripts/issue/issue-merged.ts +2 -2
- package/scripts/{git → issue}/issue-rank.ts +1 -1
- package/scripts/issue/issue-read-cli.ts +8 -47
- package/scripts/issue/issue-read.ts +0 -2
- package/scripts/issue/issue-report-failures.ts +80 -0
- package/scripts/issue/issue-scout-cli.ts +1 -7
- package/scripts/issue/issue-scout-closed.ts +2 -2
- package/scripts/issue/issue-scout.ts +0 -2
- package/scripts/issue/issue-state-cli.ts +5 -46
- package/scripts/issue/issue-state.ts +1 -5
- package/scripts/issue/markdown-section.ts +21 -6
- package/scripts/issue/measure-rerun-cli.ts +74 -15
- package/scripts/issue/reproduction-measure.ts +1 -1
- package/scripts/josh/cli-body.ts +1 -1
- package/scripts/josh/file-map-stamp.ts +0 -3
- package/scripts/josh/josh-command-map.ts +2 -0
- package/scripts/josh/josh-commands-ai.ts +9 -102
- package/scripts/josh/josh-commands-backlog.ts +5 -1
- package/scripts/josh/josh-commands-development.ts +8 -1
- package/scripts/josh/josh-commands-issue.ts +2 -2
- package/scripts/josh/josh-commands-maintenance.ts +7 -0
- package/scripts/josh/josh-commands-project.ts +7 -1
- package/scripts/josh/josh-commands-run.ts +114 -0
- package/scripts/josh/josh-commands-versioning.ts +8 -0
- package/scripts/josh/josh-commands-workflow.ts +3 -3
- package/scripts/josh/path-decision.ts +0 -3
- package/scripts/josh/process-owner.ts +16 -0
- package/scripts/josh/run-github-prerequisite.ts +3 -6
- package/scripts/josh/session-language-cli.ts +0 -1
- package/scripts/lane/lane-await.ts +46 -19
- package/scripts/lane/lane-capacity.ts +6 -3
- package/scripts/lane/lane-child-invocation.ts +20 -7
- package/scripts/lane/lane-cli.ts +1 -4
- package/scripts/lane/lane-close.ts +5 -4
- package/scripts/lane/lane-dispatch.ts +3 -4
- package/scripts/lane/lane-environment.ts +0 -9
- package/scripts/lane/lane-handoff.ts +38 -0
- package/scripts/lane/lane-launch-cli.ts +31 -27
- package/scripts/lane/lane-open.ts +0 -3
- package/scripts/lane/lane-output.ts +0 -1
- package/scripts/lane/lane-registry.ts +9 -0
- package/scripts/lane/openai-lane-ship.ts +2 -0
- package/scripts/lane/openai-lane-supervisor-decision.ts +2 -1
- package/scripts/lane/openai-lane-supervisor.ts +6 -4
- package/scripts/lane/openai-review-broker.ts +7 -1
- package/scripts/lib/bounded-pool.ts +1 -0
- package/scripts/lib/cli-flags.ts +31 -2
- package/scripts/lib/error-message.ts +12 -1
- package/scripts/lib/json-value.ts +5 -4
- package/scripts/lib/poll.ts +3 -1
- package/scripts/lib/timed-fetch.ts +15 -0
- package/scripts/lib/timeouts.ts +13 -0
- package/scripts/{yaml → lib}/yaml-document.ts +3 -1
- package/scripts/{bytes → lines}/bytes-command.ts +0 -2
- package/scripts/lines/line-targets.ts +0 -11
- package/scripts/lines/lines-command.ts +5 -4
- package/scripts/lint/lint-related-scope.ts +0 -1
- package/scripts/lint/lint-related.ts +1 -1
- package/scripts/{git → notify}/telegram-notify.ts +2 -2
- package/scripts/{git → notify}/telegram-test.ts +4 -4
- package/scripts/observations/observation-ledger-home.ts +0 -1
- package/scripts/observations/observation-ledger-line.ts +0 -1
- package/scripts/observations/observation-ledger-migrate.ts +1 -1
- package/scripts/observations/observations-flush-landing.ts +3 -3
- package/scripts/observations/observations-flush.ts +2 -2
- package/scripts/overrides/lockfile-overrides.ts +1 -1
- package/scripts/overrides/overrides-logic.ts +1 -1
- package/scripts/package/package-scout-cli.ts +2 -7
- package/scripts/package/package-scout.ts +0 -2
- package/scripts/propagate/propagate-git-failure.ts +0 -1
- package/scripts/propagate/propagate-git.ts +3 -10
- package/scripts/propagate/propagate-publish.ts +2 -3
- package/scripts/propagate/propagate-run.ts +0 -2
- package/scripts/propagate/propagate-steps.ts +3 -5
- package/scripts/propagate/propagate-targets.ts +0 -4
- package/scripts/propagate/propagate.ts +0 -4
- package/scripts/refactor/import-graph.ts +0 -1
- package/scripts/refactor/refactor-lint.ts +0 -2
- package/scripts/refactor/refactor-scan.ts +2 -2
- package/scripts/refactor/refactor-targets.ts +0 -1
- package/scripts/registry-migration/migrate-logic.ts +28 -21
- package/scripts/registry-migration/migrate.ts +25 -14
- package/scripts/release/github-release-cli.ts +19 -6
- package/scripts/release/github-release-environment.ts +46 -0
- package/scripts/release/github-release.ts +151 -94
- package/scripts/release/release-history.ts +0 -1
- package/scripts/release/release-plan.ts +0 -1
- package/scripts/release/release-publish.ts +38 -5
- package/scripts/release/release-scope-cli.ts +1 -2
- package/scripts/release/release-tag.ts +2 -4
- package/scripts/release/release-worktree.ts +97 -2
- package/scripts/repo/auto-merge-setting-logic.ts +1 -4
- package/scripts/repo/repo-setting.ts +1 -1
- package/scripts/repo/repository-labels.ts +3 -3
- package/scripts/repo/required-checks-logic.ts +206 -0
- package/scripts/repo/required-checks-report.ts +71 -0
- package/scripts/repo/ruleset-check-cli.ts +53 -0
- package/scripts/repo/ruleset-check.ts +147 -0
- package/scripts/report/report-lint-cli.ts +0 -4
- package/scripts/review/disposition-cli.ts +1 -1
- package/scripts/review/live-evidence.ts +1 -1
- package/scripts/review/review-attest-cli.ts +0 -6
- package/scripts/review/review-attest.ts +4 -16
- package/scripts/review/review-brief-cli.ts +23 -6
- package/scripts/review/review-brief.ts +21 -8
- package/scripts/review/review-checkout.ts +3 -2
- package/scripts/review/review-diff-parts.ts +89 -0
- package/scripts/review/review-diff-write.ts +97 -0
- package/scripts/review/review-finding-ledger.ts +0 -1
- package/scripts/review/review-record-cli.ts +10 -17
- package/scripts/review/review-record.ts +0 -1
- package/scripts/review/review-round2-cli.ts +1 -1
- package/scripts/review/review-stamps.ts +0 -3
- package/scripts/review/review-tree.ts +14 -2
- package/scripts/rules/decision-oracle-stage.ts +18 -0
- package/scripts/rules/decision-oracle.ts +10 -7
- package/scripts/rules/delivered-rules.ts +9 -1
- package/scripts/rules/destructive-command.ts +119 -0
- package/scripts/rules/direct-filing.ts +1 -1
- package/scripts/rules/early-heartbeat.ts +4 -3
- package/scripts/rules/file-body.ts +0 -3
- package/scripts/rules/filing-cap.ts +0 -2
- package/scripts/rules/gh-api.ts +7 -2
- package/scripts/rules/git-argv.ts +18 -7
- package/scripts/rules/git-force.ts +0 -2
- package/scripts/rules/implementation-cut.ts +0 -1
- package/scripts/rules/index-guard.ts +71 -0
- package/scripts/rules/issue-citation.ts +50 -25
- package/scripts/rules/lane-background.ts +15 -7
- package/scripts/rules/lane-park.ts +22 -6
- package/scripts/rules/last-prompt.ts +1 -0
- package/scripts/rules/oracle-consulted.ts +1 -1
- package/scripts/rules/oracle-firing.ts +1 -0
- package/scripts/rules/oracle-list-cli.ts +0 -3
- package/scripts/rules/permission-guards.ts +14 -0
- package/scripts/rules/protected-files.ts +74 -0
- package/scripts/rules/reply-language.ts +1 -1
- package/scripts/rules/rule-body-guard.ts +0 -3
- package/scripts/rules/rule-value-cli.ts +0 -1
- package/scripts/rules/shell-body-trigger.ts +0 -1
- package/scripts/rules/shell-segments.ts +0 -1
- package/scripts/rules/stop-rules.ts +20 -9
- package/scripts/rules/tail-commands.ts +1 -1
- package/scripts/rules/test-declared-commit.ts +1 -1
- package/scripts/rules/third-party-write.ts +0 -1
- package/scripts/rules/worktree-guard.ts +26 -9
- package/scripts/run/run-carry-args.ts +8 -12
- package/scripts/run/run-carry-cli.ts +0 -2
- package/scripts/run/run-carry-stash.ts +2 -2
- package/scripts/run/run-carry-streak.ts +1 -1
- package/scripts/run/run-cut-args.ts +1 -6
- package/scripts/run/run-cut-cli.ts +0 -7
- package/scripts/run/run-cut.ts +8 -54
- package/scripts/run/run-ending-cli.ts +2 -6
- package/scripts/run/run-ending.ts +1 -4
- package/scripts/run/run-entry-cli.ts +133 -25
- package/scripts/run/run-entry-stop.ts +104 -0
- package/scripts/run/run-entry.ts +0 -1
- package/scripts/run/run-event-cli.ts +0 -2
- package/scripts/run/run-event-follow.ts +1 -0
- package/scripts/run/run-event-render.ts +1 -1
- package/scripts/run/run-event-scope.ts +53 -4
- package/scripts/run/run-event-stream-emit.ts +7 -2
- package/scripts/run/run-event-stream.ts +0 -3
- package/scripts/run/run-event-watch.ts +1 -0
- package/scripts/run/run-halfrun-resume.ts +20 -7
- package/scripts/run/run-headless.ts +2 -2
- package/scripts/run/run-hold-cli.ts +33 -10
- package/scripts/run/run-hold.ts +29 -6
- package/scripts/run/run-issue-number.ts +1 -1
- package/scripts/run/run-liveness-cli.ts +2 -6
- package/scripts/run/run-liveness.ts +2 -3
- package/scripts/run/run-merge-cli.ts +7 -23
- package/scripts/run/run-merge-steps.ts +5 -5
- package/scripts/run/run-merge.ts +1 -1
- package/scripts/run/run-next-cli.ts +1 -1
- package/scripts/run/run-parent-cut-hook.ts +4 -2
- package/scripts/run/run-preflight.ts +2 -9
- package/scripts/run/run-prep-cli.ts +6 -3
- package/scripts/run/run-prep-locate.ts +1 -1
- package/scripts/run/run-progress-args.ts +20 -4
- package/scripts/run/run-progress-cli.ts +35 -36
- package/scripts/run/run-progress-clock.ts +10 -35
- package/scripts/run/run-progress-config.ts +0 -4
- package/scripts/run/run-progress-read.ts +1 -2
- package/scripts/run/run-progress.ts +1 -2
- package/scripts/run/run-prrun-resume.ts +117 -0
- package/scripts/run/run-report-cli.ts +1 -2
- package/scripts/run/run-report.ts +0 -1
- package/scripts/run/run-retrospective.ts +1 -1
- package/scripts/run/run-review-cli.ts +0 -3
- package/scripts/run/run-review-steps.ts +2 -0
- package/scripts/run/run-review.ts +0 -2
- package/scripts/run/run-ship-cli.ts +29 -19
- package/scripts/run/run-ship-detach.ts +1 -0
- package/scripts/run/run-ship-next.ts +106 -0
- package/scripts/run/run-ship-preflight.ts +1 -1
- package/scripts/run/run-ship-probe.ts +4 -1
- package/scripts/run/run-ship-return.ts +8 -3
- package/scripts/run/run-ship-review-steps.ts +18 -4
- package/scripts/run/run-ship-review.ts +11 -0
- package/scripts/run/run-ship-scoped.ts +1 -0
- package/scripts/run/run-ship-steps.ts +1 -1
- package/scripts/run/run-ship.ts +0 -1
- package/scripts/run/run-stage-read.ts +62 -0
- package/scripts/run/run-stage.ts +160 -0
- package/scripts/run/run-status-cli.ts +3 -16
- package/scripts/run/run-step-cli.ts +21 -14
- package/scripts/run/run-step.ts +1 -1
- package/scripts/run/run-stop-notify.ts +1 -3
- package/scripts/run/run-stranded-cli.ts +0 -4
- package/scripts/run/run-stranded-detect.ts +2 -2
- package/scripts/run/run-tail-cli.ts +17 -11
- package/scripts/run/run-tail.ts +2 -0
- package/scripts/run/run-tidy-lanes.ts +6 -2
- package/scripts/run/run-tidy-stashes.ts +12 -12
- package/scripts/run/run-wake-cli.ts +4 -13
- package/scripts/run/run-wake-loop.ts +2 -2
- package/scripts/run/run-wake.ts +2 -4
- package/scripts/run/run-watcher-guard-cli.ts +0 -4
- package/scripts/run/run-watcher-guard.ts +18 -12
- package/scripts/run/run-watcher-hook.ts +1 -1
- package/scripts/safe-chain/ci-installer-pin.ts +11 -5
- package/scripts/safe-chain/pack-hook.ts +92 -0
- package/scripts/safe-chain/project-config.ts +1 -1
- package/scripts/security/security-audit-provision-logic.ts +0 -1
- package/scripts/security/security-audit-provision.ts +3 -2
- package/scripts/security/security-audit.ts +0 -1
- package/scripts/security/security-updates-logic.ts +0 -1
- package/scripts/sonar/sonar-hotspots-cli.ts +10 -52
- package/scripts/sonar/sonar-hotspots.ts +0 -1
- package/scripts/sonar/sonar-new-code-cli.ts +150 -0
- package/scripts/sonar/sonar-new-code.ts +79 -0
- package/scripts/sonar/sonar-project.ts +97 -0
- package/scripts/split/split-assess-cli.ts +1 -1
- package/scripts/split/split-assess.ts +2 -5
- package/scripts/sync/managed-config-scope-cli.ts +1 -1
- package/scripts/sync/skill-migration.ts +0 -1
- package/scripts/sync/sync-ai-files.ts +4 -0
- package/scripts/sync/sync-hook-safety.ts +2 -3
- package/scripts/sync/sync.ts +1 -8
- package/scripts/sync/template-source-logic.ts +0 -1
- package/scripts/sync/workflow-pin-logic.ts +16 -28
- package/scripts/test/test-declared-changed.ts +13 -16
- package/scripts/test/test-declared-logic.ts +0 -1
- package/scripts/test/test-declared.ts +3 -9
- package/scripts/test/test-red-commit.ts +2 -2
- package/scripts/test/test-red.ts +14 -7
- package/scripts/test/test-type-logic.ts +1 -1
- package/scripts/test/unit-worker-share.ts +7 -16
- package/scripts/time-runtime/time-batch-guard.ts +1 -2
- package/scripts/time-runtime/time-bundle-call.ts +0 -1
- package/scripts/time-runtime/time-bundles.ts +0 -4
- package/scripts/time-runtime/time-density-hook.ts +0 -2
- package/scripts/time-runtime/time-followup-stage.ts +1 -1
- package/scripts/time-runtime/time-format.ts +0 -73
- package/scripts/time-runtime/time-round-trips.ts +0 -4
- package/scripts/time-runtime/time-single-check.ts +1 -1
- package/scripts/time-runtime/time-spans.ts +1 -1
- package/scripts/time-runtime/time-transcript-line.ts +1 -2
- package/scripts/ui/ui-routes-cli.ts +0 -5
- package/scripts/ui/ui-routes.ts +0 -5
- package/scripts/version/effective-upstream.ts +2 -2
- package/scripts/version/latest-corepack.ts +7 -9
- package/scripts/version/latest-scope-cli.ts +0 -2
- package/scripts/version/latest-update.ts +0 -2
- package/scripts/version/npm-registry.ts +2 -2
- package/scripts/version/publishable-range-check.ts +1 -1
- package/scripts/version/release-hold.ts +0 -4
- package/scripts/version/running-binary.ts +2 -2
- package/scripts/version/version-check-logic.ts +0 -2
- package/scripts/version/version-commands.ts +11 -1
- package/scripts/version/version-remote.ts +3 -3
- package/scripts/version/version-targets.ts +4 -4
- package/templates/CLAUDE.basic.md +6 -6
- package/templates/sonar-project.properties +28 -1
- package/templates/workflows/ci.yml +16 -82
- package/templates/workflows/dependabot-auto-merge.yml +6 -3
- package/templates/workflows/github-release.yml +79 -0
- package/tsconfig.sonar.json +4 -22
- package/prompts/collaboration-workflow/cross-repo-epic.md +0 -5
- package/prompts/collaboration-workflow/epic-audit.md +0 -5
- package/prompts/collaboration-workflow/epic-bundle.md +0 -5
- package/scripts/init/plugin-install-hint.ts +0 -47
- package/scripts/version/parse-json.ts +0 -13
- /package/scripts/{git → followup}/git-followup-stages.ts +0 -0
- /package/scripts/{git → gh}/git-gh-api-path.ts +0 -0
- /package/scripts/{git → gh}/git-gh-command.ts +0 -0
- /package/scripts/{git → gh}/git-gh-issue.ts +0 -0
- /package/scripts/{git → gh}/git-gh-pr-snapshot.ts +0 -0
- /package/scripts/{git → gh}/git-gh-reachability.ts +0 -0
- /package/scripts/{git → gh}/git-gh-repo.ts +0 -0
- /package/scripts/{git → gh}/git-gh-rest-state.ts +0 -0
- /package/scripts/{git → gh}/git-pr-checks-watch.ts +0 -0
- /package/scripts/{git → gh}/git-pr-error.ts +0 -0
- /package/scripts/{git → gh}/git-pr-messages.ts +0 -0
- /package/scripts/{git → gh}/github-issue-url.ts +0 -0
- /package/scripts/git/{stash-orphans.ts → stash/stash-orphans.ts} +0 -0
- /package/scripts/{git → issue}/issue-reference.ts +0 -0
- /package/scripts/{git → lib}/animation-helpers.ts +0 -0
- /package/scripts/{git → lib}/git-animation.ts +0 -0
- /package/scripts/{git → notify}/git-notify.ts +0 -0
- /package/scripts/{git → notify}/telegram-test-logic.ts +0 -0
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { agent_role_profile, type AgentProfile } from '#scripts/agent/agent-role-profile'
|
|
2
2
|
import { file_map_stamp, type FileMapStamp } from '#scripts/josh/file-map-stamp'
|
|
3
3
|
import { review_checkout, type ReviewCheckout } from './review-checkout'
|
|
4
|
+
import { review_diff_parts, type DiffParts } from './review-diff-parts'
|
|
4
5
|
|
|
5
6
|
// The text `josh review:brief` prints — the `/code-review` invocation, composed from what the run
|
|
6
7
|
// already knows (joshuafolkken/kit#1241).
|
|
@@ -56,7 +57,7 @@ function attest_line(nonce: string): string {
|
|
|
56
57
|
const RUBRIC_RELATIVE_PATH = 'prompts/review-rubric.md'
|
|
57
58
|
|
|
58
59
|
function rubric_line(rubric_path: string): string {
|
|
59
|
-
return `Rubric: read \`${rubric_path}\` first
|
|
60
|
+
return `Rubric: read \`${rubric_path}\` first, with the Read tool rather than \`cat\` — it is larger than the Bash output cap, so a Bash print comes back truncated (joshuafolkken/kit#2963) — and apply it: it is the severity tests, the round output format, the nine categories and the stop conditions this review is scored against. You do not carry them otherwise, so a review that skips it is scored on the wrong rules.`
|
|
60
61
|
}
|
|
61
62
|
|
|
62
63
|
function checkout_block(checkout: ReviewCheckout, nonce: string): string {
|
|
@@ -413,6 +414,9 @@ interface BriefInput {
|
|
|
413
414
|
// The tree a finished, clean review of this branch was briefed on, so round 1 is a resume
|
|
414
415
|
// (joshuafolkken/kit#2945). Absent, round 1 reads the whole change.
|
|
415
416
|
resumed_from?: FileMapStamp | undefined
|
|
417
|
+
// The change written as parts under the Bash output cap (joshuafolkken/kit#2963). Absent when the
|
|
418
|
+
// write failed, and the target line alone then names the change.
|
|
419
|
+
diff?: { parts: DiffParts; cap: number } | undefined
|
|
416
420
|
}
|
|
417
421
|
|
|
418
422
|
const SECOND_ROUND = 2
|
|
@@ -431,10 +435,24 @@ function target_block(input: BriefInput): string {
|
|
|
431
435
|
return round_two_block(input.stamps.round_one, input.tree, input.checkout.root, input.base)
|
|
432
436
|
}
|
|
433
437
|
|
|
438
|
+
// **Only beside a whole-change target** (joshuafolkken/kit#2963): each part is a path's diff against
|
|
439
|
+
// the base, so beside a narrowed round 2 or a resumed round 1 the list would hand the reviewer the
|
|
440
|
+
// code that narrowing exists to keep out of scope. Decided by the target actually printed, not by
|
|
441
|
+
// the round: every round that cannot narrow widens through `whole_change_target`, and that round
|
|
442
|
+
// needs the parts as much as a first one does.
|
|
443
|
+
function diff_block(input: BriefInput, target: string): ReadonlyArray<string> {
|
|
444
|
+
const is_whole_change = target.includes(whole_change_target(input.checkout.root, input.base))
|
|
445
|
+
|
|
446
|
+
if (!is_whole_change || input.diff === undefined) return []
|
|
447
|
+
|
|
448
|
+
return ['', review_diff_parts.reading_block(input.diff.parts, input.diff.cap)]
|
|
449
|
+
}
|
|
450
|
+
|
|
434
451
|
// The level alone on the first line, because the level-only mode's contract — a caller reading the
|
|
435
452
|
// answer with `$(...)` — is the one thing a brief must not break.
|
|
436
453
|
function compose(input: BriefInput): string {
|
|
437
454
|
const profile = input.profile ?? agent_role_profile.DEFAULT_PROFILES.reviewer
|
|
455
|
+
const target = target_block(input)
|
|
438
456
|
|
|
439
457
|
return [
|
|
440
458
|
input.level,
|
|
@@ -448,29 +466,24 @@ function compose(input: BriefInput): string {
|
|
|
448
466
|
gate_line(input.stamps, input.tree, input.base),
|
|
449
467
|
TEST_COMMAND_LINE,
|
|
450
468
|
'',
|
|
451
|
-
|
|
469
|
+
target,
|
|
470
|
+
...diff_block(input, target),
|
|
452
471
|
].join('\n')
|
|
453
472
|
}
|
|
454
473
|
|
|
455
474
|
const review_brief = {
|
|
456
475
|
attest_line,
|
|
457
476
|
BASE_MOVED_PREFIX,
|
|
458
|
-
base_moved_line,
|
|
459
477
|
carried_line,
|
|
460
|
-
checkout_block,
|
|
461
478
|
checkout_warning,
|
|
462
479
|
compose,
|
|
463
480
|
dropped_line,
|
|
464
481
|
EMPTY_DELTA_LINE,
|
|
465
482
|
gate_line,
|
|
466
|
-
green_stamp,
|
|
467
|
-
in_flight_line,
|
|
468
|
-
live_marker,
|
|
469
483
|
RESUMED_HEADING,
|
|
470
484
|
matching_stamp,
|
|
471
485
|
no_snapshot_line,
|
|
472
486
|
NOTHING_LEFT_LINE,
|
|
473
|
-
NOT_VERIFIED_LINE,
|
|
474
487
|
rubric_line,
|
|
475
488
|
RUBRIC_RELATIVE_PATH,
|
|
476
489
|
ROUND_TWO_HEADING,
|
|
@@ -21,8 +21,9 @@ interface ReviewCheckout {
|
|
|
21
21
|
head: string
|
|
22
22
|
}
|
|
23
23
|
|
|
24
|
-
// Three git
|
|
25
|
-
//
|
|
24
|
+
// Three git reads that do not depend on each other, so they are issued together rather than one after
|
|
25
|
+
// another: the brief is printed in front of a review the run is waiting on. The root is a near-instant
|
|
26
|
+
// synchronous `rev-parse` (joshuafolkken/kit#3065); the branch and head still overlap.
|
|
26
27
|
async function read_checkout(): Promise<ReviewCheckout> {
|
|
27
28
|
const [root, branch, head] = await Promise.all([
|
|
28
29
|
git_command.repository_root(),
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
// The change handed to the reviewer as files no larger than the Bash output cap
|
|
2
|
+
// (joshuafolkken/kit#2963). The brief used to name the whole change as one `git diff` and the rubric
|
|
3
|
+
// as one path, and the forked reviewer printed both through Bash: every print past
|
|
4
|
+
// `BASH_MAX_OUTPUT_LENGTH` came back as a truncated preview plus a saved file it then re-read, and both
|
|
5
|
+
// stayed in the cached prefix of every later request — 120 overflows and 68 re-reads in one review.
|
|
6
|
+
//
|
|
7
|
+
// **The diff is split, not shortened.** Each changed path gets its own parts, every part fits under the
|
|
8
|
+
// cap, and the parts of one path concatenate back to exactly its diff — so the scope a review covers
|
|
9
|
+
// is the one `git diff` lists, and only the size of a single read changes.
|
|
10
|
+
|
|
11
|
+
// A map from each changed path to the absolute part files that carry its diff, in order.
|
|
12
|
+
type DiffParts = Readonly<Record<string, ReadonlyArray<string>>>
|
|
13
|
+
|
|
14
|
+
const LINE_BREAK = '\n'
|
|
15
|
+
|
|
16
|
+
// A line longer than the budget is cut by characters: a minified file or a lockfile line would
|
|
17
|
+
// otherwise produce one part past the cap, which is the read this module exists to remove.
|
|
18
|
+
function cut_line(line: string, budget: number): Array<string> {
|
|
19
|
+
const pieces: Array<string> = []
|
|
20
|
+
|
|
21
|
+
for (let start = 0; start < line.length; start += budget) {
|
|
22
|
+
pieces.push(line.slice(start, start + budget))
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
return pieces
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
function pieces_of(text: string, budget: number): Array<string> {
|
|
29
|
+
return text.split(/(?<=\n)/u).flatMap((line) => cut_line(line, budget))
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
// Line-preserving where a line fits, so a part ends on a line boundary and a reader never sees a hunk
|
|
33
|
+
// cut mid-line unless that one line is itself over the budget.
|
|
34
|
+
function is_full(current: string, piece: string, budget: number): boolean {
|
|
35
|
+
return current.length > 0 && current.length + piece.length > budget
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
function split_text(text: string, budget: number): Array<string> {
|
|
39
|
+
const parts: Array<string> = []
|
|
40
|
+
let current = ''
|
|
41
|
+
|
|
42
|
+
for (const piece of pieces_of(text, budget)) {
|
|
43
|
+
if (is_full(current, piece, budget)) {
|
|
44
|
+
parts.push(current)
|
|
45
|
+
current = ''
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
current += piece
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
return current.length > 0 ? [...parts, current] : parts
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
function part_header(relative: string, index: number, count: number): string {
|
|
55
|
+
return `# ${relative} — part ${String(index + 1)} of ${String(count)}${LINE_BREAK}`
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
// The header is counted inside the cap, so the file a reader opens — header included — is what fits.
|
|
59
|
+
// The count in the header is the final one, so it is sized against the widest header it can carry.
|
|
60
|
+
function part_texts(relative: string, diff: string, cap: number): Array<string> {
|
|
61
|
+
const budget = Math.max(1, cap - part_header(relative, diff.length, diff.length).length)
|
|
62
|
+
const bodies = split_text(diff, budget)
|
|
63
|
+
|
|
64
|
+
return bodies.map((body, index) => `${part_header(relative, index, bodies.length)}${body}`)
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
function body_of(part: string): string {
|
|
68
|
+
return part.slice(part.indexOf(LINE_BREAK) + 1)
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
function part_list(parts: DiffParts): string {
|
|
72
|
+
return Object.entries(parts)
|
|
73
|
+
.flatMap(([relative, files]) => [` ${relative}`, ...files.map((file) => ` ${file}`)])
|
|
74
|
+
.join(LINE_BREAK)
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
// **The read instruction names the tool as well as the files.** A part under the cap would survive a
|
|
78
|
+
// `cat`, but the Read tool is the one read that is never truncated, and naming it keeps the reviewer
|
|
79
|
+
// from reaching back for the whole `git diff` the target line defines the change by.
|
|
80
|
+
function reading_block(parts: DiffParts, cap: number): string {
|
|
81
|
+
return [
|
|
82
|
+
`Reading the diff: Bash output is cut at ${String(cap)} characters, so the change is written below as one or more parts per path, each under that cap, and together exactly the diff. Read the parts of every path you review with the Read tool. Do not print the diff, or any file this brief names, through Bash (\`git diff\`, \`cat\`).`,
|
|
83
|
+
part_list(parts),
|
|
84
|
+
].join(LINE_BREAK)
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
const review_diff_parts = { body_of, part_texts, reading_block, split_text }
|
|
88
|
+
|
|
89
|
+
export { review_diff_parts, type DiffParts }
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
import { mkdirSync, readFileSync, rmSync, writeFileSync } from 'node:fs'
|
|
2
|
+
import path from 'node:path'
|
|
3
|
+
import { git_spawn } from '#scripts/git/git-spawn'
|
|
4
|
+
import { stamp_file } from '#scripts/josh/stamp-file'
|
|
5
|
+
import { review_diff_parts, type DiffParts } from './review-diff-parts'
|
|
6
|
+
|
|
7
|
+
// Writes the change as the parts `review-diff-parts.ts` cuts it into (joshuafolkken/kit#2963).
|
|
8
|
+
//
|
|
9
|
+
// **One diff per path, read the way the target line defines the change** — `git -C <root> diff
|
|
10
|
+
// <base> -- <path>` for a tracked path, and the file's own content for an untracked one, which no
|
|
11
|
+
// `git diff` lists. The parts are written to the temp directory keyed per checkout, the same place the
|
|
12
|
+
// brief's other records live, and the directory is replaced on every brief so a part from an earlier
|
|
13
|
+
// tree is never offered beside this one's.
|
|
14
|
+
|
|
15
|
+
const DIRECTORY_PREFIX = 'josh-review-diff-'
|
|
16
|
+
const DIRECTORY_MODE = 0o700
|
|
17
|
+
const FILE_MODE = 0o600
|
|
18
|
+
const PART_SUFFIX = '.diff'
|
|
19
|
+
const INDEX_WIDTH = 3
|
|
20
|
+
const NUL = '\0'
|
|
21
|
+
const SLUG_SEPARATOR = '__'
|
|
22
|
+
|
|
23
|
+
function untracked_text(root: string, relative: string): string {
|
|
24
|
+
try {
|
|
25
|
+
const content = readFileSync(path.join(root, relative), 'utf8')
|
|
26
|
+
|
|
27
|
+
if (content.includes(NUL)) return `untracked binary file ${relative}, not shown\n`
|
|
28
|
+
|
|
29
|
+
return `untracked file ${relative}, shown whole:\n${content}`
|
|
30
|
+
} catch {
|
|
31
|
+
return `${relative} is listed by the change but holds no diff and no file\n`
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
async function path_diff(root: string, base: string, relative: string): Promise<string> {
|
|
36
|
+
const diff = await git_spawn.read(['-C', root, 'diff', '--no-color', base, '--', relative])
|
|
37
|
+
|
|
38
|
+
return diff.length > 0 ? diff : untracked_text(root, relative)
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
function part_name(position: number, relative: string, index: number): string {
|
|
42
|
+
const prefix = String(position).padStart(INDEX_WIDTH, '0')
|
|
43
|
+
|
|
44
|
+
return `${prefix}-${relative.split('/').join(SLUG_SEPARATOR)}-${String(index + 1)}${PART_SUFFIX}`
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
interface PathText {
|
|
48
|
+
relative: string
|
|
49
|
+
text: string
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
function write_path(
|
|
53
|
+
directory: string,
|
|
54
|
+
position: number,
|
|
55
|
+
entry: PathText,
|
|
56
|
+
cap: number,
|
|
57
|
+
): Array<string> {
|
|
58
|
+
return review_diff_parts.part_texts(entry.relative, entry.text, cap).map((part, index) => {
|
|
59
|
+
const file = path.join(directory, part_name(position, entry.relative, index))
|
|
60
|
+
|
|
61
|
+
writeFileSync(file, part, { flag: 'wx', mode: FILE_MODE })
|
|
62
|
+
|
|
63
|
+
return file
|
|
64
|
+
})
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
function fresh_directory(root: string): string {
|
|
68
|
+
const directory = stamp_file.stamp_path(DIRECTORY_PREFIX, root, '')
|
|
69
|
+
|
|
70
|
+
rmSync(directory, { recursive: true, force: true })
|
|
71
|
+
mkdirSync(directory, { mode: DIRECTORY_MODE })
|
|
72
|
+
|
|
73
|
+
return directory
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
interface DiffRequest {
|
|
77
|
+
root: string
|
|
78
|
+
base: string
|
|
79
|
+
paths: ReadonlyArray<string>
|
|
80
|
+
cap: number
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
async function write_parts(request: DiffRequest, directory?: string): Promise<DiffParts> {
|
|
84
|
+
const { root, base, paths, cap } = request
|
|
85
|
+
const texts = await Promise.all(
|
|
86
|
+
paths.map(async (relative) => ({ relative, text: await path_diff(root, base, relative) })),
|
|
87
|
+
)
|
|
88
|
+
const target = directory ?? fresh_directory(root)
|
|
89
|
+
|
|
90
|
+
return Object.fromEntries(
|
|
91
|
+
texts.map((entry, position) => [entry.relative, write_path(target, position, entry, cap)]),
|
|
92
|
+
)
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
const review_diff_write = { path_diff, write_parts }
|
|
96
|
+
|
|
97
|
+
export { review_diff_write }
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
#!/usr/bin/env tsx
|
|
2
2
|
import { fileURLToPath } from 'node:url'
|
|
3
|
-
import {
|
|
3
|
+
import { cli_flags } from '#scripts/lib/cli-flags'
|
|
4
4
|
import { observation_ledger_home } from '#scripts/observations/observation-ledger-home'
|
|
5
5
|
import { review_finding_ledger, type Finding } from './review-finding-ledger'
|
|
6
6
|
import { review_record, type RecordVerdict } from './review-record'
|
|
@@ -71,21 +71,14 @@ function parse_findings(specs: ReadonlyArray<string>): ReadonlyArray<Finding> |
|
|
|
71
71
|
}
|
|
72
72
|
|
|
73
73
|
function parse_argv(argv: ReadonlyArray<string>): Parsed | undefined {
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
return {
|
|
83
|
-
check: parsed.values.check === true,
|
|
84
|
-
issue: parsed.values.issue,
|
|
85
|
-
positionals: parsed.positionals,
|
|
86
|
-
}
|
|
87
|
-
} catch {
|
|
88
|
-
return undefined
|
|
74
|
+
const parsed = cli_flags.arguments_of(argv, OPTIONS)
|
|
75
|
+
|
|
76
|
+
if (parsed === undefined) return undefined
|
|
77
|
+
|
|
78
|
+
return {
|
|
79
|
+
check: parsed.values.check === true,
|
|
80
|
+
issue: parsed.values.issue,
|
|
81
|
+
positionals: parsed.positionals,
|
|
89
82
|
}
|
|
90
83
|
}
|
|
91
84
|
|
|
@@ -186,7 +179,7 @@ async function main(argv: ReadonlyArray<string>): Promise<void> {
|
|
|
186
179
|
process.exitCode = await run(argv, new Date())
|
|
187
180
|
}
|
|
188
181
|
|
|
189
|
-
const review_record_cli = {
|
|
182
|
+
const review_record_cli = { build_lines, parse_finding, run }
|
|
190
183
|
|
|
191
184
|
if (process.argv[1] === fileURLToPath(import.meta.url)) await main(process.argv.slice(ARGV_OFFSET))
|
|
192
185
|
|
|
@@ -77,7 +77,7 @@ async function main(argv: ReadonlyArray<string>): Promise<void> {
|
|
|
77
77
|
process.exitCode = await run(argv)
|
|
78
78
|
}
|
|
79
79
|
|
|
80
|
-
const review_round2_cli = { CLOSED_FLAG,
|
|
80
|
+
const review_round2_cli = { CLOSED_FLAG, parse_options, run }
|
|
81
81
|
|
|
82
82
|
if (process.argv[1] === fileURLToPath(import.meta.url)) await main(process.argv.slice(ARGV_OFFSET))
|
|
83
83
|
|
|
@@ -102,18 +102,15 @@ function clear_round_one(target?: string): void {
|
|
|
102
102
|
}
|
|
103
103
|
|
|
104
104
|
const review_stamps = {
|
|
105
|
-
BRIEFED_PREFIX,
|
|
106
105
|
briefed_stamp,
|
|
107
106
|
clear_round_one,
|
|
108
107
|
GATE_PREFIX,
|
|
109
108
|
gate_stamp,
|
|
110
109
|
IN_FLIGHT_PREFIX,
|
|
111
110
|
in_flight_stamp,
|
|
112
|
-
LINT_RELATED_PREFIX,
|
|
113
111
|
lint_related_stamp,
|
|
114
112
|
ROUND_ONE_PREFIX,
|
|
115
113
|
round_one_stamp,
|
|
116
|
-
TEST_RELATED_PREFIX,
|
|
117
114
|
test_related_stamp,
|
|
118
115
|
}
|
|
119
116
|
|
|
@@ -3,6 +3,7 @@ import path from 'node:path'
|
|
|
3
3
|
import { changed_paths } from '#scripts/git/changed-paths'
|
|
4
4
|
import { git_command } from '#scripts/git/git-command'
|
|
5
5
|
import { stamp_file } from '#scripts/josh/stamp-file'
|
|
6
|
+
import { observation_ledger } from '#scripts/observations/observation-ledger'
|
|
6
7
|
|
|
7
8
|
// The tree `josh review:brief` records: every path the change touches, and the digest of its content
|
|
8
9
|
// (joshuafolkken/kit#1241).
|
|
@@ -30,8 +31,18 @@ function digest_of(root: string, relative: string): string {
|
|
|
30
31
|
|
|
31
32
|
// Sorted so two readings of one tree produce the same record byte for byte, which is what lets a
|
|
32
33
|
// stored stamp be compared against a fresh reading without a normalization step in between.
|
|
34
|
+
//
|
|
35
|
+
// **The observation ledger is left out of the map**, for the reason `hook-gate-reuse.ts` drops it from
|
|
36
|
+
// the status it reads: it is no code any check runs (joshuafolkken/kit#1756). Kept in, a review's line
|
|
37
|
+
// appended after the gate moved the map, so the ledger commit `pnpm josh followup` pushes carried a
|
|
38
|
+
// tree the record no longer matched — the pre-push hook re-ran the whole unit suite inside the push's
|
|
39
|
+
// 120-second budget, the timed-out push was retried over the still-running suite, and the commit's
|
|
40
|
+
// undo then moved the tree under both (joshuafolkken/kit#2998). Filtered here, every reader of the map
|
|
41
|
+
// — the gate's record, both hooks, the brief and the round-2 fix delta — agrees on it.
|
|
33
42
|
function tree_of(root: string, paths: ReadonlyArray<string>): Record<string, string> {
|
|
34
|
-
const sorted =
|
|
43
|
+
const sorted = paths
|
|
44
|
+
.filter((relative) => !observation_ledger.is_ledger_path(relative))
|
|
45
|
+
.toSorted((left, right) => left.localeCompare(right))
|
|
35
46
|
|
|
36
47
|
return Object.fromEntries(sorted.map((relative) => [relative, digest_of(root, relative)]))
|
|
37
48
|
}
|
|
@@ -43,6 +54,7 @@ function tree_of(root: string, paths: ReadonlyArray<string>): Record<string, str
|
|
|
43
54
|
// say the gate had verified an arbitrarily edited tree, and `--round 2` would report an empty fix
|
|
44
55
|
// delta. **A defect that answers "all clear" is the one shape this record cannot take**, which is why
|
|
45
56
|
// the root is resolved rather than assumed (measured from `scripts/`: 18 of 25 entries `absent`).
|
|
57
|
+
// The observation ledger is dropped inside `tree_of`, above (joshuafolkken/kit#3017).
|
|
46
58
|
async function read_changed_tree(
|
|
47
59
|
paths?: ReadonlyArray<string>,
|
|
48
60
|
root?: string,
|
|
@@ -53,6 +65,6 @@ async function read_changed_tree(
|
|
|
53
65
|
)
|
|
54
66
|
}
|
|
55
67
|
|
|
56
|
-
const review_tree = { ABSENT_DIGEST,
|
|
68
|
+
const review_tree = { ABSENT_DIGEST, read_changed_tree, tree_of }
|
|
57
69
|
|
|
58
70
|
export { review_tree }
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { run_stage } from '#scripts/run/run-stage'
|
|
2
|
+
import type { DecisionOracle } from './decision-oracle'
|
|
3
|
+
|
|
4
|
+
// The stage-ladder oracle (joshuafolkken/kit#3042), kept beside the enumeration rather than inside it
|
|
5
|
+
// because `decision-oracle.ts` sits at its file-size limit. Where a run on an issue starts — and whether
|
|
6
|
+
// the command typed has already been reached — is computed by `run:entry` from the issue and the tree's
|
|
7
|
+
// hold, so the skill's stage table points at the command rather than re-deciding it in prose. The
|
|
8
|
+
// vocabulary is `run_stage`'s own start tokens, never a second copy.
|
|
9
|
+
const RUN_ENTRY_ORACLE: DecisionOracle = {
|
|
10
|
+
name: 'run:entry',
|
|
11
|
+
decision:
|
|
12
|
+
'Where a run on an issue starts, and whether the typed command has already been reached',
|
|
13
|
+
args: '<N> [--to kickoff|halfrun|prrun|fullrun]',
|
|
14
|
+
vocabulary: run_stage.START_TOKENS,
|
|
15
|
+
single_source: '.claude/skills/workflow-commands/SKILL.md → §2k',
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export { RUN_ENTRY_ORACLE }
|
|
@@ -6,6 +6,7 @@
|
|
|
6
6
|
// not by prose. Single source: `prompts/collaboration-workflow/residency.md` → question 0.
|
|
7
7
|
//
|
|
8
8
|
// Anything not on this list is answered by prose or by a hook-delivered rule, never by a command.
|
|
9
|
+
import { RUN_ENTRY_ORACLE } from './decision-oracle-stage'
|
|
9
10
|
|
|
10
11
|
// Paths that appear in more than one entry's `single_source` field.
|
|
11
12
|
const CHAIN_RULE_MD = '.claude/skills/workflow-commands/chain-rule.md'
|
|
@@ -22,11 +23,11 @@ const BACKLOGRUN_MD = '.claude/skills/workflow-commands/backlogrun.md'
|
|
|
22
23
|
// now has its verdict contract only in this reference. That makes it the single source and the section
|
|
23
24
|
// a reader is routed to, in the same `file.md → \`josh <command>\`` form the `epic:reconcile` and
|
|
24
25
|
// `lane:list` entries already use. joshuafolkken/kit#2254.
|
|
25
|
-
const RUN_MERGE_REFERENCE = 'docs/josh-commands.md → `josh run:merge`'
|
|
26
|
-
const EPIC_NEXT_REFERENCE = 'docs/josh-commands.md → `josh epic:next`'
|
|
27
|
-
const BACKLOG_BUDGET_REFERENCE = 'docs/josh-commands.md → `josh backlog:budget`'
|
|
28
|
-
const RUN_LIVENESS_REFERENCE = 'docs/josh-commands.md → `josh run:liveness`'
|
|
29
|
-
const AUTO_OK_NEXT_REFERENCE = 'docs/josh-commands.md → `josh auto-ok:next`'
|
|
26
|
+
const RUN_MERGE_REFERENCE = 'docs/josh-commands-automation.md → `josh run:merge`'
|
|
27
|
+
const EPIC_NEXT_REFERENCE = 'docs/josh-commands-automation.md → `josh epic:next`'
|
|
28
|
+
const BACKLOG_BUDGET_REFERENCE = 'docs/josh-commands-automation.md → `josh backlog:budget`'
|
|
29
|
+
const RUN_LIVENESS_REFERENCE = 'docs/josh-commands-automation.md → `josh run:liveness`'
|
|
30
|
+
const AUTO_OK_NEXT_REFERENCE = 'docs/josh-commands-automation.md → `josh auto-ok:next`'
|
|
30
31
|
|
|
31
32
|
// Command used by two separate oracle entries (run:cut:resume and run:cut:gate).
|
|
32
33
|
const RUN_CUT_CMD = 'run:cut'
|
|
@@ -311,7 +312,7 @@ const DECISION_ORACLES: ReadonlyArray<DecisionOracle> = [
|
|
|
311
312
|
"Whether an epic's declaration matches its recorded relations, and the repair when it does not",
|
|
312
313
|
args: '--reconcile <E>',
|
|
313
314
|
vocabulary: [RECONCILED, NOTHING_TO_RECONCILE],
|
|
314
|
-
single_source: 'docs/josh-commands.md → `josh epic --reconcile`',
|
|
315
|
+
single_source: 'docs/josh-commands-automation.md → `josh epic --reconcile`',
|
|
315
316
|
},
|
|
316
317
|
{
|
|
317
318
|
name: 'lane:list',
|
|
@@ -319,8 +320,9 @@ const DECISION_ORACLES: ReadonlyArray<DecisionOracle> = [
|
|
|
319
320
|
'Whether each in-progress issue holds a live lane, and the two-directional difference',
|
|
320
321
|
args: '',
|
|
321
322
|
vocabulary: [LIVE, STOPPED, UNKNOWN],
|
|
322
|
-
single_source: 'docs/josh-commands.md → The `in-progress` / lane difference',
|
|
323
|
+
single_source: 'docs/josh-commands-automation.md → The `in-progress` / lane difference',
|
|
323
324
|
},
|
|
325
|
+
RUN_ENTRY_ORACLE,
|
|
324
326
|
]
|
|
325
327
|
|
|
326
328
|
// Returns the command name for an oracle entry. When `command` is omitted from the entry,
|
|
@@ -339,4 +341,5 @@ const decision_oracle = {
|
|
|
339
341
|
get_command,
|
|
340
342
|
}
|
|
341
343
|
|
|
344
|
+
export type { DecisionOracle }
|
|
342
345
|
export { decision_oracle }
|
|
@@ -18,6 +18,7 @@ import { lane_interactive_ask } from './lane-interactive-ask'
|
|
|
18
18
|
import { lane_park } from './lane-park'
|
|
19
19
|
import { lane_switch_main } from './lane-switch-main'
|
|
20
20
|
import { oracle_consulted } from './oracle-consulted'
|
|
21
|
+
import { permission_guards } from './permission-guards'
|
|
21
22
|
import { piped_verification } from './piped-verification'
|
|
22
23
|
import { poll_loop } from './poll-loop'
|
|
23
24
|
import { pre_gate_cut } from './pre-gate-cut'
|
|
@@ -143,8 +144,10 @@ const { is_issue_filing, on_bash_command } = bash_triggers
|
|
|
143
144
|
// exemptions and the three tests that decide the second one. The three tests are spelled out rather
|
|
144
145
|
// than named, because a delivery that said only "an interrupt is exempt" would hand the deciding back
|
|
145
146
|
// to judgement at exactly the moment nothing else is open to read (joshuafolkken/kit#1518).
|
|
147
|
+
// `WIP_CAP` is the number's single source; `wip-cap.md` states it once and a test pins the two equal.
|
|
148
|
+
const WIP_CAP = 30
|
|
146
149
|
const WIP_CAP_REASON =
|
|
147
|
-
|
|
150
|
+
`⛔ backlog WIP cap: count the target repository's open Issues before filing. With more than ${String(WIP_CAP)} ` +
|
|
148
151
|
'open, close one first; nothing honestly closable means do not file. Two filings are exempt and ' +
|
|
149
152
|
'proceed while stating the overage — one the run is blocked by, and an interrupt, decided by ' +
|
|
150
153
|
'three tests rather than judgement: a verification answers wrongly, a documented workflow cannot ' +
|
|
@@ -482,6 +485,10 @@ const DELIVERED_RULES: ReadonlyArray<DeliveredRule> = [
|
|
|
482
485
|
git_force.ROW,
|
|
483
486
|
worktree_guard.ROW,
|
|
484
487
|
file_body.ROW,
|
|
488
|
+
// **The deny-list bypasses closed by argv and path rather than glob** (joshuafolkken/kit#2983) — an
|
|
489
|
+
// index mutation, a recursive forced `rm` or destructive `gh` call, and a protected file. Each
|
|
490
|
+
// claims a command or file no row above does and refuses every occurrence (`permission-guards.ts`).
|
|
491
|
+
...permission_guards.ROWS,
|
|
485
492
|
// **The hand-written wait loop a lane child improvises over a backgrounded command's output**
|
|
486
493
|
// (joshuafolkken/kit#2371). `early-heartbeat` refuses a bare `sleep` and stands down on any loop
|
|
487
494
|
// keyword; this is the loop it left — a `while`/`until` that sleeps between probes of an output
|
|
@@ -788,6 +795,7 @@ const delivered_rules = {
|
|
|
788
795
|
SHELL_BODY_REASON,
|
|
789
796
|
SWITCH_ENV_KEY,
|
|
790
797
|
THIRD_PARTY_WRITE_REASON: third_party_write.THIRD_PARTY_WRITE_REASON,
|
|
798
|
+
WIP_CAP,
|
|
791
799
|
WIP_CAP_REASON,
|
|
792
800
|
WORKTREE_MUTATION_REASON: worktree_guard.WORKTREE_MUTATION_REASON,
|
|
793
801
|
delivery,
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
import { bash_triggers } from './bash-triggers'
|
|
2
|
+
import { gh_api } from './gh-api'
|
|
3
|
+
import { git_argv } from './git-argv'
|
|
4
|
+
import { shell_segments } from './shell-segments'
|
|
5
|
+
|
|
6
|
+
// The trigger and the delivered text behind the `destructive-command` row of `delivered-rules.ts`
|
|
7
|
+
// (joshuafolkken/kit#2983): a recursive forced `rm`, and the `gh` calls that delete or close shared
|
|
8
|
+
// state — none of which the deny list stopped, or stopped only in one spelling (`Bash(rm -rf *)`
|
|
9
|
+
// misses `rm -fr`, `rm -r -f` and `rm --recursive --force`). The argv is read through `git-argv.ts`'s
|
|
10
|
+
// wrapper cut, so `sudo rm -fr` and `env gh pr close` are seen too.
|
|
11
|
+
//
|
|
12
|
+
// **What is refused is the decision recorded on the Issue, not every destructive act.** `gh issue
|
|
13
|
+
// close` passes — the WIP-cap procedure closes Issues legitimately — and so does removing a label from
|
|
14
|
+
// an Issue (`DELETE repos/<o>/<r>/issues/<N>/labels/<name>`), which the `in-progress` and
|
|
15
|
+
// `needs-decision` steps of the workflow documents issue. Every other `DELETE repos/…` is refused.
|
|
16
|
+
//
|
|
17
|
+
// **It fires on every occurrence**, for the reason `git-force.ts` does.
|
|
18
|
+
|
|
19
|
+
const RM_COMMAND = 'rm'
|
|
20
|
+
const RECURSIVE_LONG = '--recursive'
|
|
21
|
+
const RECURSIVE_LETTERS = 'rR'
|
|
22
|
+
const FORCE_LONG = '--force'
|
|
23
|
+
const FORCE_LETTER = 'f'
|
|
24
|
+
|
|
25
|
+
function has_flag(args: ReadonlyArray<string>, long: string, letters: string): boolean {
|
|
26
|
+
return args.some((argument) => argument === long || git_argv.short_cluster_has(argument, letters))
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
function is_recursive_forced_rm(segment: string): boolean {
|
|
30
|
+
const args = git_argv.arguments_of(segment, RM_COMMAND)
|
|
31
|
+
|
|
32
|
+
if (args === undefined) return false
|
|
33
|
+
|
|
34
|
+
return (
|
|
35
|
+
has_flag(args, RECURSIVE_LONG, RECURSIVE_LETTERS) && has_flag(args, FORCE_LONG, FORCE_LETTER)
|
|
36
|
+
)
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
const GH_COMMAND = 'gh'
|
|
40
|
+
const REPO_GROUP = 'repo'
|
|
41
|
+
const REPO_REMOVALS: ReadonlySet<string> = new Set(['delete', 'archive'])
|
|
42
|
+
const REPO_EDIT = 'edit'
|
|
43
|
+
const VISIBILITY_FLAG = '--visibility'
|
|
44
|
+
const PR_GROUP = 'pr'
|
|
45
|
+
const PR_CLOSE = 'close'
|
|
46
|
+
|
|
47
|
+
// `gh repo delete` / `archive`, and `gh repo edit --visibility …`.
|
|
48
|
+
function is_destructive_repo_action(action: string, args: ReadonlyArray<string>): boolean {
|
|
49
|
+
if (REPO_REMOVALS.has(action)) return true
|
|
50
|
+
|
|
51
|
+
return action === REPO_EDIT && args.some((argument) => argument.startsWith(VISIBILITY_FLAG))
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
function is_destructive_gh_command(args: ReadonlyArray<string>): boolean {
|
|
55
|
+
const [group, action = ''] = args
|
|
56
|
+
|
|
57
|
+
if (group === PR_GROUP) return action === PR_CLOSE
|
|
58
|
+
|
|
59
|
+
return group === REPO_GROUP && is_destructive_repo_action(action, args)
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
const DELETE_METHOD = 'DELETE'
|
|
63
|
+
const REPOSITORY_PATH = 'repos/'
|
|
64
|
+
// Removing one label from one Issue — the workflow's own `in-progress` / `needs-decision` step.
|
|
65
|
+
// Anchored on the whole argument, so a label path smuggled into a field value (`-f x=repos/…/labels/a`)
|
|
66
|
+
// does not exempt the real endpoint beside it.
|
|
67
|
+
const ISSUE_LABEL_PATH =
|
|
68
|
+
/^['"]?\/?repos\/[^/\s'"]+\/[^/\s'"]+\/issues\/\d+\/labels\/[^/\s'"]+['"]?$/u
|
|
69
|
+
|
|
70
|
+
// Judged on the call with its wrapper cut off, since `gh_api.is_gh_api` anchors on a leading `gh` and
|
|
71
|
+
// would otherwise miss `env gh api -X DELETE …` that the `gh pr close` branch already sees. Every
|
|
72
|
+
// argument naming a repository path must be a label removal for the call to pass.
|
|
73
|
+
function is_destructive_api_call(args: ReadonlyArray<string>): boolean {
|
|
74
|
+
const call = [GH_COMMAND, ...args].join(' ')
|
|
75
|
+
|
|
76
|
+
if (!gh_api.is_gh_api(call) || gh_api.method_of(call) !== DELETE_METHOD) return false
|
|
77
|
+
|
|
78
|
+
return args.some(
|
|
79
|
+
(argument) => argument.includes(REPOSITORY_PATH) && !ISSUE_LABEL_PATH.test(argument),
|
|
80
|
+
)
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
function is_destructive_gh(segment: string): boolean {
|
|
84
|
+
const args = git_argv.arguments_of(segment, GH_COMMAND)
|
|
85
|
+
|
|
86
|
+
if (args === undefined) return false
|
|
87
|
+
|
|
88
|
+
return is_destructive_gh_command(args) || is_destructive_api_call(args)
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
function is_destructive_segment(segment: string): boolean {
|
|
92
|
+
return is_recursive_forced_rm(segment) || is_destructive_gh(segment)
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
function is_destructive_command(command: string): boolean {
|
|
96
|
+
return shell_segments.segments_of(command).some((segment) => is_destructive_segment(segment))
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
const DESTRUCTIVE_COMMAND_REASON =
|
|
100
|
+
'⛔ destructive command: a recursive forced `rm` (`rm -rf`, `rm -fr`, `rm -r -f`, `rm --recursive ' +
|
|
101
|
+
'--force`), `gh repo delete` / `archive`, `gh repo edit --visibility`, `gh pr close`, or a `gh api ' +
|
|
102
|
+
'-X DELETE repos/…` call deletes or closes something that cannot be restored, which is Tier C and ' +
|
|
103
|
+
'needs explicit current-turn user instruction (`prompts/collaboration-workflow/operating-rules.md`). ' +
|
|
104
|
+
'The `.claude/settings.json` deny list matches prefix globs, so it misses these spellings; this row ' +
|
|
105
|
+
'reads the argv instead. Remove a file or directory one path at a time without `-f`, or ask the user ' +
|
|
106
|
+
'to run the command in their own terminal. `gh issue close` and removing a label from an Issue ' +
|
|
107
|
+
'(`DELETE repos/<o>/<r>/issues/<N>/labels/<name>`) are not refused. **This rule fires on every ' +
|
|
108
|
+
'occurrence, not once per run.**'
|
|
109
|
+
|
|
110
|
+
const ROW = {
|
|
111
|
+
id: 'destructive-command',
|
|
112
|
+
is_trigger: bash_triggers.on_bash_command(is_destructive_command),
|
|
113
|
+
reason: DESTRUCTIVE_COMMAND_REASON,
|
|
114
|
+
decide: (): boolean => true,
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
const destructive_command = { DESTRUCTIVE_COMMAND_REASON, ROW, is_destructive_command }
|
|
118
|
+
|
|
119
|
+
export { destructive_command }
|
|
@@ -14,7 +14,7 @@ const DIRECT_FILING_REASON =
|
|
|
14
14
|
'lint and its classification labels, the depth and `route:` labels, the `## Origin` check when filing ' +
|
|
15
15
|
'into another repository, and `epic:bundle` after the create — so none of them is a step to remember. ' +
|
|
16
16
|
'When the scout prints candidates it holds the filing: read each, then reissue with `--distinct <N,…>` ' +
|
|
17
|
-
'for the ones that are separate deliverables. Reference: `docs/josh-commands.md` → "`josh issue:file`". ' +
|
|
17
|
+
'for the ones that are separate deliverables. Reference: `docs/josh-commands-automation.md` → "`josh issue:file`". ' +
|
|
18
18
|
'**This rule fires on every occurrence, not once per run.**'
|
|
19
19
|
|
|
20
20
|
function is_any_filing(command: string): boolean {
|