@joshuafolkken/kit 1.872.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/.claude-plugin/plugin.json +11 -0
- package/.claude/settings.json +110 -0
- package/.claude/skills/dependency-update/SKILL.md +76 -0
- package/.claude/skills/epic-commands/SKILL.md +463 -0
- package/.claude/skills/verify-ui/SKILL.md +96 -0
- package/.claude/skills/workflow-commands/SKILL.md +265 -0
- package/.claude/skills/workflow-commands/background-commands.md +130 -0
- package/.claude/skills/workflow-commands/backlogrun-child.md +364 -0
- package/.claude/skills/workflow-commands/backlogrun-lanes.md +345 -0
- package/.claude/skills/workflow-commands/backlogrun-park.md +179 -0
- package/.claude/skills/workflow-commands/backlogrun-progress.md +600 -0
- package/.claude/skills/workflow-commands/backlogrun-steps.md +604 -0
- package/.claude/skills/workflow-commands/backlogrun.md +109 -0
- package/.claude/skills/workflow-commands/chain-rule.md +98 -0
- package/.claude/skills/workflow-commands/delegation.md +52 -0
- package/.claude/skills/workflow-commands/followup-reference.md +118 -0
- package/.claude/skills/workflow-commands/followup.md +222 -0
- package/.claude/skills/workflow-commands/fullrun-steps.md +93 -0
- package/.claude/skills/workflow-commands/fullrun.md +82 -0
- package/.claude/skills/workflow-commands/halfrun.md +73 -0
- package/.claude/skills/workflow-commands/into-target.md +48 -0
- package/.claude/skills/workflow-commands/issue-comments.md +109 -0
- package/.claude/skills/workflow-commands/issue-fold-existing.md +27 -0
- package/.claude/skills/workflow-commands/issue-scout.md +45 -0
- package/.claude/skills/workflow-commands/kickoff.md +82 -0
- package/.claude/skills/workflow-commands/latest-gate.md +85 -0
- package/.claude/skills/workflow-commands/needs-human-review.md +64 -0
- package/.claude/skills/workflow-commands/observation-filing.md +349 -0
- package/.claude/skills/workflow-commands/pre-gate-cut.md +340 -0
- package/.claude/skills/workflow-commands/prerequisite.md +62 -0
- package/.claude/skills/workflow-commands/retrospective.md +81 -0
- package/.claude/skills/workflow-commands/rule-residency.md +267 -0
- package/.claude/skills/workflow-commands/split-assessment.md +130 -0
- package/.claude/skills/workflow-commands/target-repository.md +58 -0
- package/.claude/skills/workflow-commands/working-tree-hold.md +66 -0
- package/.claude-plugin/marketplace.json +15 -0
- package/.coderabbit.yaml +50 -0
- package/.codex/config.toml +8 -0
- package/.codex/hooks.json +71 -0
- package/.cursorrules +45 -0
- package/.gitattributes +7 -0
- package/.github/dependabot.yml +19 -0
- package/.github/pull_request_template.md +1 -0
- package/.github/release.yml +18 -0
- package/.github/workflows/auto-tag.yml +111 -0
- package/.github/workflows/ci.yml +681 -0
- package/.github/workflows/dependabot-auto-merge.yml +55 -0
- package/.github/workflows/production.yml +47 -0
- package/.github/workflows/publish.yml +39 -0
- package/.github/workflows/sonar-qube.yml +34 -0
- package/.mcp.json +8 -0
- package/.ncurc.json +3 -0
- package/.prettierignore +20 -0
- package/.vscode/extensions.json +21 -0
- package/.vscode/settings.json +41 -0
- package/AGENTS.md +29 -0
- package/CLAUDE.md +207 -0
- package/CODE_OF_CONDUCT.md +132 -0
- package/GEMINI.md +30 -0
- package/LICENSE +21 -0
- package/README.md +41 -0
- package/SECURITY.md +34 -0
- package/cspell/index.yaml +177 -0
- package/dist/CLAUDE.md +207 -0
- package/dist/config-merge/index.d.ts +29 -0
- package/dist/config-merge/index.js +459 -0
- package/dist/hooks/batch-guard.js +28 -0
- package/dist/hooks/chunk-25JL6FMS.js +4970 -0
- package/dist/hooks/chunk-2ANGT3XM.js +375 -0
- package/dist/hooks/chunk-2EI5GBSW.js +42 -0
- package/dist/hooks/chunk-2OT5XE2N.js +5014 -0
- package/dist/hooks/chunk-2RUPMGGV.js +137 -0
- package/dist/hooks/chunk-2XIGZQMQ.js +3299 -0
- package/dist/hooks/chunk-33ELOKEV.js +910 -0
- package/dist/hooks/chunk-33YRTKM7.js +230 -0
- package/dist/hooks/chunk-36JQU5NN.js +77 -0
- package/dist/hooks/chunk-3EJBTWVS.js +1769 -0
- package/dist/hooks/chunk-3FK5SFAC.js +432 -0
- package/dist/hooks/chunk-3PWPYDD2.js +240 -0
- package/dist/hooks/chunk-3TQU7TDH.js +375 -0
- package/dist/hooks/chunk-3TVLSFZ6.js +432 -0
- package/dist/hooks/chunk-3VXYCB4E.js +77 -0
- package/dist/hooks/chunk-44M6JXGV.js +3398 -0
- package/dist/hooks/chunk-46EEEHC3.js +240 -0
- package/dist/hooks/chunk-4A7UVP2P.js +364 -0
- package/dist/hooks/chunk-4LFQEZS4.js +137 -0
- package/dist/hooks/chunk-4OCX3V2T.js +469 -0
- package/dist/hooks/chunk-4OHA5DE7.js +49 -0
- package/dist/hooks/chunk-4PMDW3YD.js +68 -0
- package/dist/hooks/chunk-4QHBNAI4.js +68 -0
- package/dist/hooks/chunk-4SSB3FEE.js +3273 -0
- package/dist/hooks/chunk-5A2WQ2CQ.js +5390 -0
- package/dist/hooks/chunk-5EVA2JQB.js +5390 -0
- package/dist/hooks/chunk-5P7VF5VE.js +3230 -0
- package/dist/hooks/chunk-5QWJGGZP.js +49 -0
- package/dist/hooks/chunk-5U5EZYG6.js +77 -0
- package/dist/hooks/chunk-5VUUF3ZV.js +240 -0
- package/dist/hooks/chunk-5WA7UBT3.js +374 -0
- package/dist/hooks/chunk-6EBGRQ3Q.js +301 -0
- package/dist/hooks/chunk-6TVHWMD3.js +432 -0
- package/dist/hooks/chunk-6V5OPIUU.js +13 -0
- package/dist/hooks/chunk-6VAGLEMB.js +68 -0
- package/dist/hooks/chunk-75BTXKFJ.js +3424 -0
- package/dist/hooks/chunk-75RHN67S.js +137 -0
- package/dist/hooks/chunk-76KC3BDD.js +3414 -0
- package/dist/hooks/chunk-76SLS7CZ.js +898 -0
- package/dist/hooks/chunk-7DHZ4DWT.js +5015 -0
- package/dist/hooks/chunk-7INSJDGE.js +458 -0
- package/dist/hooks/chunk-7VJ4FTDX.js +268 -0
- package/dist/hooks/chunk-7XCYNHQK.js +77 -0
- package/dist/hooks/chunk-A5YJ3H7E.js +375 -0
- package/dist/hooks/chunk-AAEXXUUL.js +268 -0
- package/dist/hooks/chunk-ABVEVV2M.js +432 -0
- package/dist/hooks/chunk-ACN3YXFL.js +68 -0
- package/dist/hooks/chunk-AD3XE3QZ.js +397 -0
- package/dist/hooks/chunk-AENFZPZH.js +230 -0
- package/dist/hooks/chunk-AFKRFQ6W.js +68 -0
- package/dist/hooks/chunk-AHD5IPWI.js +375 -0
- package/dist/hooks/chunk-AMK5JGV7.js +77 -0
- package/dist/hooks/chunk-ARN7NCQI.js +425 -0
- package/dist/hooks/chunk-AUFY653N.js +3425 -0
- package/dist/hooks/chunk-AXQBNFEF.js +375 -0
- package/dist/hooks/chunk-AYC7R6U4.js +3316 -0
- package/dist/hooks/chunk-AYEA2FJ7.js +5015 -0
- package/dist/hooks/chunk-B2LIXYFL.js +77 -0
- package/dist/hooks/chunk-B4HPNY2I.js +375 -0
- package/dist/hooks/chunk-B4YNCUGB.js +469 -0
- package/dist/hooks/chunk-B57S3VWT.js +922 -0
- package/dist/hooks/chunk-BINZ3PLI.js +364 -0
- package/dist/hooks/chunk-BIWLABFK.js +432 -0
- package/dist/hooks/chunk-BODUSXKK.js +410 -0
- package/dist/hooks/chunk-BQBAT2YK.js +375 -0
- package/dist/hooks/chunk-BUJEBW75.js +391 -0
- package/dist/hooks/chunk-BVNWORKA.js +72 -0
- package/dist/hooks/chunk-BWI47LQS.js +5205 -0
- package/dist/hooks/chunk-C25HRMJX.js +137 -0
- package/dist/hooks/chunk-CBFZNC74.js +375 -0
- package/dist/hooks/chunk-CECYXS4Y.js +3424 -0
- package/dist/hooks/chunk-CELGUP5F.js +49 -0
- package/dist/hooks/chunk-CMHANU3F.js +3442 -0
- package/dist/hooks/chunk-CMN372LI.js +68 -0
- package/dist/hooks/chunk-CPGZLMMK.js +3299 -0
- package/dist/hooks/chunk-CTQKXDC7.js +268 -0
- package/dist/hooks/chunk-CYAT4RIT.js +68 -0
- package/dist/hooks/chunk-CYTWYCH2.js +5014 -0
- package/dist/hooks/chunk-CZSOLRBA.js +77 -0
- package/dist/hooks/chunk-D2PIDYJR.js +3438 -0
- package/dist/hooks/chunk-DE57HI6T.js +137 -0
- package/dist/hooks/chunk-DGXDGAPG.js +5306 -0
- package/dist/hooks/chunk-DNYRSYQG.js +5014 -0
- package/dist/hooks/chunk-DTAXH2A2.js +240 -0
- package/dist/hooks/chunk-E2IZUWI5.js +375 -0
- package/dist/hooks/chunk-E4L2K2BL.js +397 -0
- package/dist/hooks/chunk-E55NKX5F.js +68 -0
- package/dist/hooks/chunk-ECXVCCVD.js +3079 -0
- package/dist/hooks/chunk-EPHO443D.js +68 -0
- package/dist/hooks/chunk-EUFK2BI2.js +375 -0
- package/dist/hooks/chunk-EYCD5ZAV.js +137 -0
- package/dist/hooks/chunk-F2SDYJU4.js +240 -0
- package/dist/hooks/chunk-F4B5D5TF.js +68 -0
- package/dist/hooks/chunk-FGEO3NDX.js +432 -0
- package/dist/hooks/chunk-FJYI52RE.js +137 -0
- package/dist/hooks/chunk-FXAKJKTB.js +18 -0
- package/dist/hooks/chunk-FYTZHIY4.js +3085 -0
- package/dist/hooks/chunk-G7JL2A5K.js +375 -0
- package/dist/hooks/chunk-GA3ITDBW.js +42 -0
- package/dist/hooks/chunk-GDR4H4SV.js +469 -0
- package/dist/hooks/chunk-GFKDXPSM.js +2855 -0
- package/dist/hooks/chunk-GIMRTKFU.js +566 -0
- package/dist/hooks/chunk-GOPHRLGO.js +240 -0
- package/dist/hooks/chunk-GX4HO7SJ.js +230 -0
- package/dist/hooks/chunk-GXKKK23T.js +240 -0
- package/dist/hooks/chunk-GZ6FUVIK.js +566 -0
- package/dist/hooks/chunk-H5HPY5M5.js +2869 -0
- package/dist/hooks/chunk-HCF3OGEE.js +1829 -0
- package/dist/hooks/chunk-HDEXAOUM.js +469 -0
- package/dist/hooks/chunk-HM2TY3TN.js +469 -0
- package/dist/hooks/chunk-HOA7NXA2.js +55 -0
- package/dist/hooks/chunk-HOFEWH6H.js +240 -0
- package/dist/hooks/chunk-HUUI6S5J.js +2669 -0
- package/dist/hooks/chunk-I4D3NVUD.js +469 -0
- package/dist/hooks/chunk-I4Q6WAJE.js +68 -0
- package/dist/hooks/chunk-IB6N4CR2.js +432 -0
- package/dist/hooks/chunk-IFLDYJMD.js +469 -0
- package/dist/hooks/chunk-IGU466YD.js +77 -0
- package/dist/hooks/chunk-IJCQUHKY.js +375 -0
- package/dist/hooks/chunk-IKUMBDJB.js +230 -0
- package/dist/hooks/chunk-IM3BDIBJ.js +5005 -0
- package/dist/hooks/chunk-IMYMAODK.js +3431 -0
- package/dist/hooks/chunk-IN2ZVGXA.js +5005 -0
- package/dist/hooks/chunk-ITVJ2MKQ.js +18 -0
- package/dist/hooks/chunk-J5RIJYTM.js +2669 -0
- package/dist/hooks/chunk-JFLMH4K5.js +3460 -0
- package/dist/hooks/chunk-JR25IFML.js +77 -0
- package/dist/hooks/chunk-JT5AUXFI.js +432 -0
- package/dist/hooks/chunk-JY5YVN6N.js +3234 -0
- package/dist/hooks/chunk-K5Q4ETVI.js +391 -0
- package/dist/hooks/chunk-K67V2F24.js +137 -0
- package/dist/hooks/chunk-KABG6AEW.js +3460 -0
- package/dist/hooks/chunk-KATZESEC.js +3273 -0
- package/dist/hooks/chunk-KFH4H7W7.js +469 -0
- package/dist/hooks/chunk-KMUEI6RX.js +910 -0
- package/dist/hooks/chunk-KMV4OY77.js +3438 -0
- package/dist/hooks/chunk-KQAZNWEY.js +137 -0
- package/dist/hooks/chunk-KZZR6JER.js +5015 -0
- package/dist/hooks/chunk-LAR6DREP.js +77 -0
- package/dist/hooks/chunk-LNCJWEZD.js +5015 -0
- package/dist/hooks/chunk-LRYD2GLM.js +6 -0
- package/dist/hooks/chunk-LXBKYHDV.js +77 -0
- package/dist/hooks/chunk-LYLKIUNE.js +5024 -0
- package/dist/hooks/chunk-LZKCFOVC.js +432 -0
- package/dist/hooks/chunk-M4FCIYRI.js +77 -0
- package/dist/hooks/chunk-M5M7327Q.js +3425 -0
- package/dist/hooks/chunk-MF72MHSX.js +3455 -0
- package/dist/hooks/chunk-MK56LKDO.js +397 -0
- package/dist/hooks/chunk-N3MJADY7.js +240 -0
- package/dist/hooks/chunk-NFQSUDBN.js +458 -0
- package/dist/hooks/chunk-NHREKY45.js +137 -0
- package/dist/hooks/chunk-NP5TTAAW.js +375 -0
- package/dist/hooks/chunk-NQTLTKTJ.js +2993 -0
- package/dist/hooks/chunk-NVRCZPBZ.js +391 -0
- package/dist/hooks/chunk-O46HMMLJ.js +469 -0
- package/dist/hooks/chunk-OFJGH2L4.js +240 -0
- package/dist/hooks/chunk-OLFMCZH5.js +240 -0
- package/dist/hooks/chunk-OMCZS46I.js +49 -0
- package/dist/hooks/chunk-OQZEZW5G.js +469 -0
- package/dist/hooks/chunk-P2MDAT2Y.js +1897 -0
- package/dist/hooks/chunk-P3M2JJIF.js +68 -0
- package/dist/hooks/chunk-PDVHQITQ.js +3453 -0
- package/dist/hooks/chunk-PNT2AMDH.js +391 -0
- package/dist/hooks/chunk-PR2LVSY6.js +3445 -0
- package/dist/hooks/chunk-PRR5R4JK.js +137 -0
- package/dist/hooks/chunk-PXT44QVN.js +566 -0
- package/dist/hooks/chunk-PZLWJLAP.js +469 -0
- package/dist/hooks/chunk-Q2FEAZJ2.js +77 -0
- package/dist/hooks/chunk-Q32WETDO.js +3265 -0
- package/dist/hooks/chunk-Q45DUBLE.js +3600 -0
- package/dist/hooks/chunk-Q7NZSWQ5.js +240 -0
- package/dist/hooks/chunk-QH2JNAMK.js +3085 -0
- package/dist/hooks/chunk-QNNWYQ2R.js +375 -0
- package/dist/hooks/chunk-QPU5H2OS.js +68 -0
- package/dist/hooks/chunk-QRAHZ5TO.js +1829 -0
- package/dist/hooks/chunk-QUZ5HPIE.js +469 -0
- package/dist/hooks/chunk-QVCRG34O.js +2893 -0
- package/dist/hooks/chunk-R3MY4E3S.js +1675 -0
- package/dist/hooks/chunk-R6TXJJXP.js +77 -0
- package/dist/hooks/chunk-REJU6VT7.js +1829 -0
- package/dist/hooks/chunk-REQAQLRC.js +2993 -0
- package/dist/hooks/chunk-RFCXPAHH.js +391 -0
- package/dist/hooks/chunk-RHI6Y22X.js +137 -0
- package/dist/hooks/chunk-RKZVOBBS.js +469 -0
- package/dist/hooks/chunk-RL57TT2Y.js +566 -0
- package/dist/hooks/chunk-RORDOD4S.js +3321 -0
- package/dist/hooks/chunk-RZKYW5P2.js +77 -0
- package/dist/hooks/chunk-SJPJ6VSI.js +391 -0
- package/dist/hooks/chunk-SKSIJQQQ.js +432 -0
- package/dist/hooks/chunk-SSQDZKXG.js +268 -0
- package/dist/hooks/chunk-SSX23SJ2.js +89 -0
- package/dist/hooks/chunk-SYHFKE4C.js +77 -0
- package/dist/hooks/chunk-TCGNI2ZY.js +42 -0
- package/dist/hooks/chunk-TG3WXJF5.js +137 -0
- package/dist/hooks/chunk-TH7IJAY5.js +77 -0
- package/dist/hooks/chunk-TLZ27PCY.js +240 -0
- package/dist/hooks/chunk-U652FXTN.js +566 -0
- package/dist/hooks/chunk-UEK47YUF.js +268 -0
- package/dist/hooks/chunk-UIO45H3O.js +432 -0
- package/dist/hooks/chunk-UNLZEEVH.js +5005 -0
- package/dist/hooks/chunk-UNRVF3SZ.js +432 -0
- package/dist/hooks/chunk-USLFAFOI.js +3453 -0
- package/dist/hooks/chunk-VAEELMLL.js +442 -0
- package/dist/hooks/chunk-VALSWLDQ.js +3453 -0
- package/dist/hooks/chunk-VC5JEPNY.js +3398 -0
- package/dist/hooks/chunk-VEZVK6C7.js +230 -0
- package/dist/hooks/chunk-VK5JKFAR.js +49 -0
- package/dist/hooks/chunk-VNF2KP7Z.js +469 -0
- package/dist/hooks/chunk-VWWCUMW6.js +2669 -0
- package/dist/hooks/chunk-W4JARUQV.js +137 -0
- package/dist/hooks/chunk-WCYPBPJC.js +5014 -0
- package/dist/hooks/chunk-WEOBMNJG.js +2993 -0
- package/dist/hooks/chunk-WKJU3J5Y.js +5390 -0
- package/dist/hooks/chunk-WMN4TSVF.js +3299 -0
- package/dist/hooks/chunk-WNON54PT.js +3469 -0
- package/dist/hooks/chunk-WOVKBPPX.js +137 -0
- package/dist/hooks/chunk-WRVFORYJ.js +432 -0
- package/dist/hooks/chunk-WUBVYGQZ.js +240 -0
- package/dist/hooks/chunk-WWULTUOM.js +68 -0
- package/dist/hooks/chunk-WXHKKB45.js +4962 -0
- package/dist/hooks/chunk-XAI3YTHJ.js +469 -0
- package/dist/hooks/chunk-XFZH6JZJ.js +77 -0
- package/dist/hooks/chunk-XGPWX6IA.js +137 -0
- package/dist/hooks/chunk-XIXXIVGY.js +566 -0
- package/dist/hooks/chunk-XKDOYJGA.js +922 -0
- package/dist/hooks/chunk-YALTVBHM.js +432 -0
- package/dist/hooks/chunk-YEBEPBFK.js +5014 -0
- package/dist/hooks/chunk-YI2BQ3DK.js +3441 -0
- package/dist/hooks/chunk-YVNBCFGG.js +1829 -0
- package/dist/hooks/chunk-YXBN3RGG.js +432 -0
- package/dist/hooks/chunk-Z2OOJSW3.js +3163 -0
- package/dist/hooks/chunk-ZA3HVBOY.js +2644 -0
- package/dist/hooks/chunk-ZKID46U3.js +137 -0
- package/dist/hooks/chunk-ZPMU2JV6.js +49 -0
- package/dist/hooks/chunk-ZRKY4W7O.js +432 -0
- package/dist/hooks/chunk-ZTMRB7LJ.js +268 -0
- package/dist/hooks/chunk-ZVPPBQ3L.js +3413 -0
- package/dist/hooks/codex-hook-adapter.js +164 -0
- package/dist/hooks/duplicate-read-guard.js +29 -0
- package/dist/hooks/format-edited.js +37 -0
- package/dist/hooks/investigation-guard.js +31 -0
- package/dist/hooks/pretool-guard.js +21 -0
- package/dist/hooks/session-lang.js +34 -0
- package/dist/hooks/stop-guard.js +1597 -0
- package/dist/josh.js +1795 -0
- package/dist/managed-marker/index.d.ts +10 -0
- package/dist/managed-marker/index.js +32 -0
- package/dist/self-sync-guard/index.d.ts +10 -0
- package/dist/self-sync-guard/index.js +49 -0
- package/dist/version/index.d.ts +93 -0
- package/dist/version/index.js +1024 -0
- package/env/index.d.ts +7 -0
- package/env/index.js +62 -0
- package/eslint/base.js +233 -0
- package/eslint/checkout-rooted-parser.js +34 -0
- package/eslint/config-fingerprint.js +103 -0
- package/eslint/rules/code-quality.js +292 -0
- package/eslint/rules/english-only-prose.js +135 -0
- package/eslint/rules/formatting.js +44 -0
- package/eslint/rules/import.js +83 -0
- package/eslint/rules/namespace-object-export.js +107 -0
- package/eslint/rules/naming-convention.js +109 -0
- package/eslint/rules/promise.js +17 -0
- package/eslint/rules/sonarjs.js +45 -0
- package/eslint/rules/test-filename.js +91 -0
- package/eslint/rules/typescript.js +186 -0
- package/eslint/rules/unicorn.js +191 -0
- package/eslint/vanilla.js +1 -0
- package/evals/scenarios/consult-not-execute.json +20 -0
- package/evals/scenarios/no-implicit-workflow.json +26 -0
- package/evals/scenarios/overrides-both-locations.json +29 -0
- package/evals/scenarios/upstream-interrupt.json +23 -0
- package/evals/scenarios/workflow-skill-loaded.json +16 -0
- package/lefthook/base.yml +131 -0
- package/lefthook/vanilla.yml +7 -0
- package/package.json +169 -0
- package/playwright.config.ts +160 -0
- package/pnpm-workspace.yaml +41 -0
- package/ports/index.d.ts +17 -0
- package/ports/index.js +256 -0
- package/prettier/index.d.ts +3 -0
- package/prettier/index.js +31 -0
- package/prompts/agent-lint-protocol.json +79 -0
- package/prompts/agent-rules.md +63 -0
- package/prompts/coding-standards.md +382 -0
- package/prompts/collaboration-workflow/consultation-vs-execution.md +8 -0
- package/prompts/collaboration-workflow/cross-repo-epic.md +54 -0
- package/prompts/collaboration-workflow/distributed-docs.md +7 -0
- package/prompts/collaboration-workflow/durable-rules.md +24 -0
- package/prompts/collaboration-workflow/epic-audit.md +56 -0
- package/prompts/collaboration-workflow/epic-bundle.md +109 -0
- package/prompts/collaboration-workflow/file-edits.md +124 -0
- package/prompts/collaboration-workflow/gh-rest.md +57 -0
- package/prompts/collaboration-workflow/issue-citation.md +38 -0
- package/prompts/collaboration-workflow/issue-template.md +190 -0
- package/prompts/collaboration-workflow/latest-first.md +14 -0
- package/prompts/collaboration-workflow/no-clones.md +11 -0
- package/prompts/collaboration-workflow/operating-rules.md +123 -0
- package/prompts/collaboration-workflow/output-bounds.md +107 -0
- package/prompts/collaboration-workflow/overview.md +35 -0
- package/prompts/collaboration-workflow/plan-comment.md +85 -0
- package/prompts/collaboration-workflow/proposal-request.md +23 -0
- package/prompts/collaboration-workflow/report-format.md +206 -0
- package/prompts/collaboration-workflow/residency.md +94 -0
- package/prompts/collaboration-workflow/rule-delivery.md +146 -0
- package/prompts/collaboration-workflow/shell-body.md +82 -0
- package/prompts/collaboration-workflow/simplicity-first.md +9 -0
- package/prompts/collaboration-workflow/single-source-rules.md +13 -0
- package/prompts/collaboration-workflow/turn-batching.md +137 -0
- package/prompts/collaboration-workflow/upstream-interrupt.md +51 -0
- package/prompts/collaboration-workflow/wip-cap.md +118 -0
- package/prompts/collaboration-workflow.md +52 -0
- package/prompts/refactoring.md +103 -0
- package/prompts/review-rubric.md +338 -0
- package/prompts/review.md +217 -0
- package/prompts/sonar-hotspot-handling.md +77 -0
- package/prompts/testing-guide.md +277 -0
- package/scripts/adopt/adopt-logic.ts +107 -0
- package/scripts/adopt/adopt-toolkits.ts +181 -0
- package/scripts/adopt/adopt.ts +171 -0
- package/scripts/agent/agent-argv.ts +140 -0
- package/scripts/agent/agent-diagnostics.ts +120 -0
- package/scripts/agent/agent-event.ts +173 -0
- package/scripts/agent/agent-exit-record.ts +32 -0
- package/scripts/agent/agent-launch-environment.ts +71 -0
- package/scripts/agent/agent-role-profile.ts +329 -0
- package/scripts/agent/api-outage.ts +51 -0
- package/scripts/agent/claude-agent-argv.ts +151 -0
- package/scripts/agent/claude-result-event.ts +98 -0
- package/scripts/agent/codex-agent-argv.ts +94 -0
- package/scripts/agent/codex-home-source.ts +11 -0
- package/scripts/agent/interactive-ask.ts +91 -0
- package/scripts/auto-ok/auto-ok-cli.ts +516 -0
- package/scripts/backlog/backlog-arrival.ts +147 -0
- package/scripts/backlog/backlog-budget-cli.ts +220 -0
- package/scripts/backlog/backlog-budget.ts +284 -0
- package/scripts/backlog/backlog-defect-priority.ts +77 -0
- package/scripts/backlog/backlog-drive-cli.ts +377 -0
- package/scripts/backlog/backlog-drive-finish.ts +23 -0
- package/scripts/backlog/backlog-drive-named-offer.ts +81 -0
- package/scripts/backlog/backlog-drive-named.ts +101 -0
- package/scripts/backlog/backlog-drive-offer-argv.ts +29 -0
- package/scripts/backlog/backlog-drive-owner.ts +22 -0
- package/scripts/backlog/backlog-drive-restore.ts +67 -0
- package/scripts/backlog/backlog-drive.ts +322 -0
- package/scripts/backlog/backlog-named.ts +164 -0
- package/scripts/backlog/backlog-next.ts +376 -0
- package/scripts/backlog/backlog-offer-cli.ts +279 -0
- package/scripts/backlog/backlog-offer.ts +93 -0
- package/scripts/backlog/backlog-plan-cli.ts +235 -0
- package/scripts/backlog/backlog-plan.ts +231 -0
- package/scripts/backlog/backlog-pool.ts +322 -0
- package/scripts/backlog/backlog-ready.ts +146 -0
- package/scripts/backlog/backlog-scope.ts +144 -0
- package/scripts/backlog/backlog-stalled-cli.ts +30 -0
- package/scripts/backlog/backlog-stalled-detect.ts +143 -0
- package/scripts/backlog/backlog-stalled.ts +118 -0
- package/scripts/behavior/behavior-assertion.ts +87 -0
- package/scripts/behavior/behavior-cli.ts +88 -0
- package/scripts/behavior/behavior-rules.ts +128 -0
- package/scripts/build/build-bin.ts +36 -0
- package/scripts/build/build-claude-md.ts +42 -0
- package/scripts/build/build-config-merge.ts +26 -0
- package/scripts/build/build-hooks.ts +79 -0
- package/scripts/build/build-library.ts +83 -0
- package/scripts/build/build-managed-marker.ts +26 -0
- package/scripts/build/build-self-sync-guard.ts +26 -0
- package/scripts/build/build-version.ts +25 -0
- package/scripts/build/local-bin.ts +97 -0
- package/scripts/build/shim-shell.ts +25 -0
- package/scripts/bytes/bytes-command.ts +181 -0
- package/scripts/cases/cases-cli.ts +48 -0
- package/scripts/cases/cases-logic.ts +100 -0
- package/scripts/claude/skill-meta.ts +37 -0
- package/scripts/claude/workflow-destination.ts +13 -0
- package/scripts/clone/clone-aggregate.ts +76 -0
- package/scripts/clone/clone-fingerprint.ts +96 -0
- package/scripts/clone/clone-scan-cli.ts +39 -0
- package/scripts/clone/clone-scan.ts +158 -0
- package/scripts/config-merge/index.ts +19 -0
- package/scripts/config-merge/json-format.ts +128 -0
- package/scripts/config-merge/list-patch.ts +61 -0
- package/scripts/config-merge/parse-jsonc.ts +14 -0
- package/scripts/config-merge/patch-json-key.ts +439 -0
- package/scripts/config-merge/patch-json-list.ts +38 -0
- package/scripts/config-merge/patch-yaml-list.ts +107 -0
- package/scripts/cost-runtime/codex-usage.ts +119 -0
- package/scripts/cost-runtime/context-cut-payback.ts +77 -0
- package/scripts/cost-runtime/context-cut-threshold.ts +21 -0
- package/scripts/cost-runtime/cost-attribute.ts +168 -0
- package/scripts/cost-runtime/cost-blocks.ts +149 -0
- package/scripts/cost-runtime/cost-cli.ts +318 -0
- package/scripts/cost-runtime/cost-corpus.ts +278 -0
- package/scripts/cost-runtime/cost-format.ts +32 -0
- package/scripts/cost-runtime/cost-pricing.ts +176 -0
- package/scripts/cost-runtime/cost-tokens.ts +84 -0
- package/scripts/cost-runtime/cost-transcript.ts +531 -0
- package/scripts/cost-runtime/cost-usage.ts +287 -0
- package/scripts/cost-runtime/cost-verdict.ts +89 -0
- package/scripts/cost-runtime/own-session.ts +58 -0
- package/scripts/cost-runtime/transcript-cwd.ts +14 -0
- package/scripts/delegation/delegation-cli.ts +91 -0
- package/scripts/delegation/delegation-policy.ts +178 -0
- package/scripts/delegation/duplicate-read-guard.ts +244 -0
- package/scripts/delegation/fanout-cli.ts +61 -0
- package/scripts/delegation/fanout.ts +95 -0
- package/scripts/delegation/investigation-guard.ts +131 -0
- package/scripts/delegation/investigation-reads.ts +637 -0
- package/scripts/discovery/repo-discovery.ts +166 -0
- package/scripts/discovery/repo-map-logic.ts +102 -0
- package/scripts/discovery/repo-origin.ts +153 -0
- package/scripts/discovery/repo-party-cli.ts +69 -0
- package/scripts/discovery/repo-party.ts +46 -0
- package/scripts/doctor/doctor-consumer.ts +181 -0
- package/scripts/doctor/doctor-io.ts +210 -0
- package/scripts/doctor/doctor-logic.ts +122 -0
- package/scripts/doctor/doctor-ports.ts +117 -0
- package/scripts/doctor/doctor.ts +232 -0
- package/scripts/document/backlogrun-parent-read-set.ts +48 -0
- package/scripts/document/bash-output-cap.ts +45 -0
- package/scripts/document/document-byte-budget.ts +183 -0
- package/scripts/document/document-byte-check.ts +131 -0
- package/scripts/document/document-reachability.ts +131 -0
- package/scripts/document/document-read-cli.ts +82 -0
- package/scripts/document/document-scan.ts +108 -0
- package/scripts/document/document-section-cli.ts +101 -0
- package/scripts/document/document-section.ts +156 -0
- package/scripts/document/edit-files-cli.ts +280 -0
- package/scripts/document/entry-read-budget.ts +154 -0
- package/scripts/document/entry-read-set.ts +471 -0
- package/scripts/document/generate-catalog.ts +69 -0
- package/scripts/document/lane-child-read-set.ts +88 -0
- package/scripts/document/read-files-cli.ts +135 -0
- package/scripts/document/read-set-cli.ts +295 -0
- package/scripts/document/read-set-point-of-use.ts +89 -0
- package/scripts/document/read-set-pricing.ts +52 -0
- package/scripts/document/read-set-trim.ts +78 -0
- package/scripts/document/retired-phrases.ts +143 -0
- package/scripts/document/review-agent-type.ts +46 -0
- package/scripts/document/single-source.ts +73 -0
- package/scripts/epic/epic-audit-checks.ts +452 -0
- package/scripts/epic/epic-audit-cli.ts +374 -0
- package/scripts/epic/epic-audit-orphans.ts +142 -0
- package/scripts/epic/epic-audit-rationale.ts +191 -0
- package/scripts/epic/epic-audit-report.ts +101 -0
- package/scripts/epic/epic-audit.ts +240 -0
- package/scripts/epic/epic-bundle-cli.ts +472 -0
- package/scripts/epic/epic-bundle-evidence.ts +147 -0
- package/scripts/epic/epic-bundle-gaps.ts +86 -0
- package/scripts/epic/epic-bundle-referenced.ts +195 -0
- package/scripts/epic/epic-bundle.ts +309 -0
- package/scripts/epic/epic-busy.ts +204 -0
- package/scripts/epic/epic-candidate-confirm.ts +279 -0
- package/scripts/epic/epic-classify.ts +294 -0
- package/scripts/epic/epic-cross-repo.ts +366 -0
- package/scripts/epic/epic-fetch.ts +370 -0
- package/scripts/epic/epic-graph.ts +279 -0
- package/scripts/epic/epic-index.ts +209 -0
- package/scripts/epic/epic-issue.ts +172 -0
- package/scripts/epic/epic-lane-offer.ts +207 -0
- package/scripts/epic/epic-nested.ts +37 -0
- package/scripts/epic/epic-next-read.ts +141 -0
- package/scripts/epic/epic-next-views.ts +144 -0
- package/scripts/epic/epic-next.ts +477 -0
- package/scripts/epic/epic-outside-blocker.ts +113 -0
- package/scripts/epic/epic-relation-recheck.ts +108 -0
- package/scripts/epic/epic-report.ts +200 -0
- package/scripts/gate/core-budget.ts +292 -0
- package/scripts/gate/gate-log.ts +95 -0
- package/scripts/gate/gate-plan.ts +356 -0
- package/scripts/gate/gate-report.ts +207 -0
- package/scripts/gate/gate-skip.ts +123 -0
- package/scripts/gate/gate-tree.ts +49 -0
- package/scripts/gate/hook-gate-reuse.ts +130 -0
- package/scripts/gate/pre-commit-type-check.ts +187 -0
- package/scripts/gate/scoped-green.ts +260 -0
- package/scripts/gate/type-check-step.ts +90 -0
- package/scripts/gate/verification-gate.ts +531 -0
- package/scripts/gh/fix-gh-packages-logic.ts +232 -0
- package/scripts/gh/fix-gh-packages.ts +129 -0
- package/scripts/gh/gh-cli-token.ts +18 -0
- package/scripts/gh/gh-spawn.ts +44 -0
- package/scripts/git/animation-helpers.ts +81 -0
- package/scripts/git/change-base.ts +26 -0
- package/scripts/git/changed-file-scope.ts +209 -0
- package/scripts/git/changed-paths.ts +30 -0
- package/scripts/git/constants.ts +41 -0
- package/scripts/git/git-ai-review-scan.ts +154 -0
- package/scripts/git/git-animation.ts +41 -0
- package/scripts/git/git-branch.ts +127 -0
- package/scripts/git/git-command.ts +587 -0
- package/scripts/git/git-commit.ts +27 -0
- package/scripts/git/git-common-directory.ts +38 -0
- package/scripts/git/git-epic-add-body.ts +365 -0
- package/scripts/git/git-epic-add-plan.ts +421 -0
- package/scripts/git/git-epic-add.ts +249 -0
- package/scripts/git/git-epic-body.ts +150 -0
- package/scripts/git/git-epic-chains.ts +411 -0
- package/scripts/git/git-epic-close-comment.ts +199 -0
- package/scripts/git/git-epic-close.ts +393 -0
- package/scripts/git/git-epic-decision.ts +171 -0
- package/scripts/git/git-epic-parse.ts +351 -0
- package/scripts/git/git-epic-promote.ts +102 -0
- package/scripts/git/git-epic-read.ts +73 -0
- package/scripts/git/git-epic-reconcile.ts +246 -0
- package/scripts/git/git-epic-reference.ts +98 -0
- package/scripts/git/git-epic-relations.ts +78 -0
- package/scripts/git/git-epic-remove-plan.ts +152 -0
- package/scripts/git/git-epic-remove.ts +101 -0
- package/scripts/git/git-epic-run.ts +223 -0
- package/scripts/git/git-epic-sections.ts +98 -0
- package/scripts/git/git-epic-shape.ts +81 -0
- package/scripts/git/git-epic-validate.ts +155 -0
- package/scripts/git/git-error.ts +78 -0
- package/scripts/git/git-execa-error.ts +35 -0
- package/scripts/git/git-fixture-workspace.ts +88 -0
- package/scripts/git/git-followup-cleanup.ts +88 -0
- package/scripts/git/git-followup-flush.ts +63 -0
- package/scripts/git/git-followup-label.ts +67 -0
- package/scripts/git/git-followup-pending.ts +95 -0
- package/scripts/git/git-followup-stages.ts +149 -0
- package/scripts/git/git-gh-api-path.ts +118 -0
- package/scripts/git/git-gh-check.ts +21 -0
- package/scripts/git/git-gh-command.ts +17 -0
- package/scripts/git/git-gh-exec.ts +344 -0
- package/scripts/git/git-gh-failure.ts +103 -0
- package/scripts/git/git-gh-helpers.ts +44 -0
- package/scripts/git/git-gh-issue-list.ts +257 -0
- package/scripts/git/git-gh-issue-read.ts +457 -0
- package/scripts/git/git-gh-issue-rest.ts +238 -0
- package/scripts/git/git-gh-issue-write.ts +285 -0
- package/scripts/git/git-gh-issue.ts +161 -0
- package/scripts/git/git-gh-pr-auto-merge.ts +85 -0
- package/scripts/git/git-gh-pr-read.ts +322 -0
- package/scripts/git/git-gh-pr-rest.ts +172 -0
- package/scripts/git/git-gh-pr-review.ts +103 -0
- package/scripts/git/git-gh-pr-rollup.ts +91 -0
- package/scripts/git/git-gh-pr-snapshot.ts +138 -0
- package/scripts/git/git-gh-pr.ts +219 -0
- package/scripts/git/git-gh-reachability.ts +56 -0
- package/scripts/git/git-gh-repo.ts +37 -0
- package/scripts/git/git-gh-rest-state.ts +14 -0
- package/scripts/git/git-issue.ts +168 -0
- package/scripts/git/git-location-environment.ts +62 -0
- package/scripts/git/git-ls-remote.ts +28 -0
- package/scripts/git/git-next-issues.ts +98 -0
- package/scripts/git/git-notify.ts +104 -0
- package/scripts/git/git-pr-ai-review.ts +180 -0
- package/scripts/git/git-pr-checks-eval.ts +265 -0
- package/scripts/git/git-pr-checks-parse.ts +153 -0
- package/scripts/git/git-pr-checks-watch.ts +184 -0
- package/scripts/git/git-pr-checks.ts +387 -0
- package/scripts/git/git-pr-coderabbit.ts +129 -0
- package/scripts/git/git-pr-confirmation.ts +64 -0
- package/scripts/git/git-pr-error.ts +25 -0
- package/scripts/git/git-pr-followup-wrapup.ts +225 -0
- package/scripts/git/git-pr-followup.ts +436 -0
- package/scripts/git/git-pr-managed-config.ts +69 -0
- package/scripts/git/git-pr-messages.ts +37 -0
- package/scripts/git/git-pr.ts +181 -0
- package/scripts/git/git-prompt-display.ts +22 -0
- package/scripts/git/git-prompt.ts +207 -0
- package/scripts/git/git-push-transport.ts +173 -0
- package/scripts/git/git-push.ts +27 -0
- package/scripts/git/git-remote-branch.ts +40 -0
- package/scripts/git/git-spawn.ts +47 -0
- package/scripts/git/git-staging.ts +128 -0
- package/scripts/git/git-stash.ts +124 -0
- package/scripts/git/git-status.ts +198 -0
- package/scripts/git/git-worktree.ts +110 -0
- package/scripts/git/github-issue-url.ts +105 -0
- package/scripts/git/gone-branch.ts +113 -0
- package/scripts/git/issue-labels.ts +279 -0
- package/scripts/git/listing-cutoff.ts +51 -0
- package/scripts/git/main-merge-guard.ts +134 -0
- package/scripts/git/main-merge.ts +120 -0
- package/scripts/git/main-sync.ts +121 -0
- package/scripts/git/parse-json-array.ts +102 -0
- package/scripts/git/schemas.ts +166 -0
- package/scripts/git/stash-orphans.ts +91 -0
- package/scripts/git/stash-pop-args.ts +59 -0
- package/scripts/git/stash-pop-cli.ts +105 -0
- package/scripts/git/telegram-notify.ts +404 -0
- package/scripts/hooks/batch-guard.ts +144 -0
- package/scripts/hooks/check-commit-message.ts +134 -0
- package/scripts/hooks/codex-hook-adapter.ts +155 -0
- package/scripts/hooks/edited-cspell.ts +128 -0
- package/scripts/hooks/format-edited-file-fixtures.ts +109 -0
- package/scripts/hooks/format-edited-file.ts +510 -0
- package/scripts/hooks/format-rewrite-notice.ts +99 -0
- package/scripts/hooks/pre-push-unit.ts +146 -0
- package/scripts/hooks/pretool-guard.ts +96 -0
- package/scripts/hooks/prevent-main-commit.ts +38 -0
- package/scripts/hooks/prompt-hooks.ts +38 -0
- package/scripts/hooks/reserved-run.ts +90 -0
- package/scripts/hooks/stop-guard.ts +135 -0
- package/scripts/init/claude-plugin-config.ts +51 -0
- package/scripts/init/distributed-paths.ts +100 -0
- package/scripts/init/hook-command-rewrite.ts +85 -0
- package/scripts/init/hook-launch.ts +31 -0
- package/scripts/init/init-actions.ts +147 -0
- package/scripts/init/init-ai-copy.ts +235 -0
- package/scripts/init/init-copy-content.ts +50 -0
- package/scripts/init/init-logic-deploy-vps.ts +43 -0
- package/scripts/init/init-logic-json-merge.ts +340 -0
- package/scripts/init/init-logic-migrate.ts +62 -0
- package/scripts/init/init-logic-package-key-order.ts +50 -0
- package/scripts/init/init-logic-secretlint.ts +36 -0
- package/scripts/init/init-logic-sonar.ts +197 -0
- package/scripts/init/init-logic-templates.ts +142 -0
- package/scripts/init/init-logic-workspace.ts +59 -0
- package/scripts/init/init-logic-yaml-merge.ts +57 -0
- package/scripts/init/init-logic.ts +474 -0
- package/scripts/init/init-paths.ts +11 -0
- package/scripts/init/init-sonar.ts +32 -0
- package/scripts/init/init.ts +219 -0
- package/scripts/init/kit-base-preset.ts +60 -0
- package/scripts/init/plugin-install-hint.ts +47 -0
- package/scripts/init/tsconfig-preset-migration.ts +67 -0
- package/scripts/issue/baseline-measure.ts +105 -0
- package/scripts/issue/behavior-change-lint.ts +122 -0
- package/scripts/issue/defect-rate-cli.ts +167 -0
- package/scripts/issue/defect-rate.ts +145 -0
- package/scripts/issue/issue-backlinks-cli.ts +97 -0
- package/scripts/issue/issue-backlinks.ts +164 -0
- package/scripts/issue/issue-cite-cli.ts +158 -0
- package/scripts/issue/issue-cite.ts +102 -0
- package/scripts/issue/issue-comment-cli.ts +95 -0
- package/scripts/issue/issue-fold-cli.ts +104 -0
- package/scripts/issue/issue-fold-existing-cli.ts +81 -0
- package/scripts/issue/issue-fold-existing.ts +73 -0
- package/scripts/issue/issue-fold.ts +42 -0
- package/scripts/issue/issue-lint-cli.ts +60 -0
- package/scripts/issue/issue-lint.ts +26 -0
- package/scripts/issue/issue-logic.ts +50 -0
- package/scripts/issue/issue-read-cli.ts +190 -0
- package/scripts/issue/issue-read.ts +145 -0
- package/scripts/issue/issue-scout-cli.ts +482 -0
- package/scripts/issue/issue-scout.ts +244 -0
- package/scripts/issue/issue-state-cli.ts +260 -0
- package/scripts/issue/issue-state.ts +109 -0
- package/scripts/issue/markdown-section.ts +39 -0
- package/scripts/issue/measure-rerun-cli.ts +98 -0
- package/scripts/issue/reproduction-measure.ts +59 -0
- package/scripts/josh/__snapshots__/josh-command-map.test.ts.snap +1171 -0
- package/scripts/josh/agent-session-environment.ts +188 -0
- package/scripts/josh/cli-body.ts +96 -0
- package/scripts/josh/command-suggest.ts +57 -0
- package/scripts/josh/file-map-stamp.ts +264 -0
- package/scripts/josh/hook-decision.ts +479 -0
- package/scripts/josh/josh-command-map.ts +196 -0
- package/scripts/josh/josh-command-types.ts +226 -0
- package/scripts/josh/josh-commands-ai.ts +327 -0
- package/scripts/josh/josh-commands-backlog.ts +54 -0
- package/scripts/josh/josh-commands-clone.ts +18 -0
- package/scripts/josh/josh-commands-development.ts +206 -0
- package/scripts/josh/josh-commands-document.ts +45 -0
- package/scripts/josh/josh-commands-guard.ts +38 -0
- package/scripts/josh/josh-commands-hooks.ts +42 -0
- package/scripts/josh/josh-commands-issue.ts +69 -0
- package/scripts/josh/josh-commands-lane.ts +75 -0
- package/scripts/josh/josh-commands-lint.ts +37 -0
- package/scripts/josh/josh-commands-maintenance.ts +80 -0
- package/scripts/josh/josh-commands-project.ts +53 -0
- package/scripts/josh/josh-commands-split.ts +20 -0
- package/scripts/josh/josh-commands-versioning.ts +48 -0
- package/scripts/josh/josh-commands-workflow.ts +116 -0
- package/scripts/josh/josh-composite-arguments.ts +55 -0
- package/scripts/josh/josh-environment-file.ts +31 -0
- package/scripts/josh/josh-in-process.ts +80 -0
- package/scripts/josh/josh-logic.ts +308 -0
- package/scripts/josh/josh-run.ts +53 -0
- package/scripts/josh/josh-script-reader.ts +11 -0
- package/scripts/josh/josh-verdict.ts +111 -0
- package/scripts/josh/josh.ts +60 -0
- package/scripts/josh/kit-only.ts +33 -0
- package/scripts/josh/path-decision.ts +104 -0
- package/scripts/josh/platform-temporary.ts +66 -0
- package/scripts/josh/process-identity.ts +335 -0
- package/scripts/josh/session-language-cli.ts +51 -0
- package/scripts/josh/session-language.ts +30 -0
- package/scripts/josh/stamp-file.ts +194 -0
- package/scripts/lane/lane-await.ts +165 -0
- package/scripts/lane/lane-cache-run.ts +39 -0
- package/scripts/lane/lane-cache.ts +135 -0
- package/scripts/lane/lane-capacity.ts +69 -0
- package/scripts/lane/lane-child-invocation.ts +73 -0
- package/scripts/lane/lane-child-marker.ts +58 -0
- package/scripts/lane/lane-cli.ts +410 -0
- package/scripts/lane/lane-close.ts +197 -0
- package/scripts/lane/lane-dispatch-log.ts +13 -0
- package/scripts/lane/lane-dispatch.ts +458 -0
- package/scripts/lane/lane-environment.ts +253 -0
- package/scripts/lane/lane-guard-policy.ts +136 -0
- package/scripts/lane/lane-install.ts +94 -0
- package/scripts/lane/lane-launch-cli.ts +133 -0
- package/scripts/lane/lane-occupancy.ts +112 -0
- package/scripts/lane/lane-open.ts +275 -0
- package/scripts/lane/lane-output.ts +124 -0
- package/scripts/lane/lane-paths.ts +96 -0
- package/scripts/lane/lane-reap.ts +137 -0
- package/scripts/lane/lane-registry.ts +261 -0
- package/scripts/lane/lane-relaunch.ts +68 -0
- package/scripts/lane/lane-report.ts +95 -0
- package/scripts/lane/lane-resume.ts +61 -0
- package/scripts/lane/lane-seed.ts +44 -0
- package/scripts/lane/lane-start-point.ts +175 -0
- package/scripts/lane/openai-lane-supervisor-cli.ts +32 -0
- package/scripts/lane/openai-lane-supervisor-decision.ts +63 -0
- package/scripts/lane/openai-lane-supervisor.ts +337 -0
- package/scripts/lib/bounded-pool.ts +109 -0
- package/scripts/lib/buffered-process.ts +82 -0
- package/scripts/lib/caddy-logs.sh +45 -0
- package/scripts/lib/cli-flags.ts +19 -0
- package/scripts/lib/json-value.ts +24 -0
- package/scripts/lib/poll.ts +57 -0
- package/scripts/lib/read-file.ts +16 -0
- package/scripts/lib/schemas.ts +67 -0
- package/scripts/lib/spawn-exit.ts +45 -0
- package/scripts/lib/status-icons.ts +24 -0
- package/scripts/lines/effective-limit.ts +156 -0
- package/scripts/lines/line-budget.ts +404 -0
- package/scripts/lines/line-targets.ts +80 -0
- package/scripts/lines/lines-command.ts +174 -0
- package/scripts/lint/cspell-cached.ts +41 -0
- package/scripts/lint/lint-parallel.ts +72 -0
- package/scripts/lint/lint-related-scope.ts +128 -0
- package/scripts/lint/lint-related.ts +104 -0
- package/scripts/managed-marker/index.ts +5 -0
- package/scripts/managed-marker/managed-marker-logic.ts +67 -0
- package/scripts/observations/observation-ledger-home.ts +32 -0
- package/scripts/observations/observation-ledger-line.ts +93 -0
- package/scripts/observations/observation-ledger.ts +35 -0
- package/scripts/observations/observations-flush-cli.ts +42 -0
- package/scripts/observations/observations-flush-landing.ts +97 -0
- package/scripts/observations/observations-flush.ts +420 -0
- package/scripts/overrides/lockfile-overrides.ts +105 -0
- package/scripts/overrides/overrides-check.ts +84 -0
- package/scripts/overrides/overrides-files.ts +19 -0
- package/scripts/overrides/overrides-logic.ts +211 -0
- package/scripts/overrides/schemas.ts +49 -0
- package/scripts/package/package-scout-cli.ts +195 -0
- package/scripts/package/package-scout-format.ts +151 -0
- package/scripts/package/package-scout.ts +121 -0
- package/scripts/ports/port-command.ts +87 -0
- package/scripts/propagate/propagate-git-failure.ts +191 -0
- package/scripts/propagate/propagate-git.ts +224 -0
- package/scripts/propagate/propagate-publish.ts +181 -0
- package/scripts/propagate/propagate-run.ts +201 -0
- package/scripts/propagate/propagate-steps.ts +421 -0
- package/scripts/propagate/propagate-targets.ts +160 -0
- package/scripts/propagate/propagate.ts +216 -0
- package/scripts/refactor/import-graph.ts +187 -0
- package/scripts/refactor/refactor-lint.ts +250 -0
- package/scripts/refactor/refactor-scan-cli.ts +10 -0
- package/scripts/refactor/refactor-scan.ts +124 -0
- package/scripts/refactor/refactor-targets.ts +103 -0
- package/scripts/release/release-cli.ts +88 -0
- package/scripts/release/release-history.ts +132 -0
- package/scripts/release/release-plan.ts +130 -0
- package/scripts/release/release-publish.ts +152 -0
- package/scripts/release/release-scope-cli.ts +103 -0
- package/scripts/release/release-tag.ts +93 -0
- package/scripts/release/release-worktree.ts +71 -0
- package/scripts/repo/auto-merge-setting-logic.ts +100 -0
- package/scripts/repo/auto-merge-setting.ts +37 -0
- package/scripts/repo/repo-setting.ts +112 -0
- package/scripts/report/report-lint-cli.ts +49 -0
- package/scripts/report/report-lint.ts +250 -0
- package/scripts/retrospective/retrospective-cli.ts +97 -0
- package/scripts/retrospective/retrospective.ts +127 -0
- package/scripts/review/disposition-cli.ts +44 -0
- package/scripts/review/disposition-logic.ts +45 -0
- package/scripts/review/live-evidence.ts +62 -0
- package/scripts/review/review-attest-cli.ts +115 -0
- package/scripts/review/review-attest.ts +319 -0
- package/scripts/review/review-brief-cli.ts +290 -0
- package/scripts/review/review-brief.ts +440 -0
- package/scripts/review/review-checkout.ts +56 -0
- package/scripts/review/review-finding-ledger.ts +167 -0
- package/scripts/review/review-findings-cli.ts +57 -0
- package/scripts/review/review-level.ts +86 -0
- package/scripts/review/review-record-cli.ts +196 -0
- package/scripts/review/review-record.ts +74 -0
- package/scripts/review/review-round2-cli.ts +84 -0
- package/scripts/review/review-round2.ts +139 -0
- package/scripts/review/review-stamps.ts +113 -0
- package/scripts/review/review-tree.ts +58 -0
- package/scripts/rules/bash-triggers.ts +44 -0
- package/scripts/rules/decision-oracle.ts +342 -0
- package/scripts/rules/delivered-rules-harness.ts +72 -0
- package/scripts/rules/delivered-rules.ts +793 -0
- package/scripts/rules/early-heartbeat.ts +241 -0
- package/scripts/rules/file-body.ts +150 -0
- package/scripts/rules/filing-cap.ts +133 -0
- package/scripts/rules/filing-offer.ts +81 -0
- package/scripts/rules/firing-point.ts +63 -0
- package/scripts/rules/gh-api.ts +65 -0
- package/scripts/rules/git-argv.ts +129 -0
- package/scripts/rules/git-force.ts +104 -0
- package/scripts/rules/implementation-cut-verdict.ts +84 -0
- package/scripts/rules/implementation-cut.ts +202 -0
- package/scripts/rules/issue-citation.ts +242 -0
- package/scripts/rules/issue-fold.ts +73 -0
- package/scripts/rules/issue-scout.ts +64 -0
- package/scripts/rules/josh-git-bare.ts +80 -0
- package/scripts/rules/lane-carry-conflict.ts +96 -0
- package/scripts/rules/lane-interactive-ask.ts +71 -0
- package/scripts/rules/lane-park.ts +122 -0
- package/scripts/rules/lane-switch-main.ts +139 -0
- package/scripts/rules/oracle-consulted.ts +81 -0
- package/scripts/rules/oracle-firing.ts +151 -0
- package/scripts/rules/oracle-list-cli.ts +81 -0
- package/scripts/rules/piped-verification.ts +126 -0
- package/scripts/rules/poll-loop.ts +125 -0
- package/scripts/rules/pre-gate-cut.ts +207 -0
- package/scripts/rules/prior-comment-read.ts +106 -0
- package/scripts/rules/raw-field-body.ts +56 -0
- package/scripts/rules/reply-language.ts +116 -0
- package/scripts/rules/rule-body-guard.ts +206 -0
- package/scripts/rules/rule-guard.ts +23 -0
- package/scripts/rules/rule-value-cli.ts +129 -0
- package/scripts/rules/rule-value.ts +397 -0
- package/scripts/rules/run-tail.ts +178 -0
- package/scripts/rules/shell-body-trigger.ts +204 -0
- package/scripts/rules/shell-segments.ts +45 -0
- package/scripts/rules/stop-rules.ts +329 -0
- package/scripts/rules/tail-commands.ts +35 -0
- package/scripts/rules/test-declared-commit.ts +51 -0
- package/scripts/rules/third-party-write.ts +90 -0
- package/scripts/rules/top-level-scripts-guard.ts +47 -0
- package/scripts/rules/worktree-guard.ts +130 -0
- package/scripts/run/detached-launch.ts +360 -0
- package/scripts/run/run-carry-args.ts +264 -0
- package/scripts/run/run-carry-change.ts +69 -0
- package/scripts/run/run-carry-cli.ts +441 -0
- package/scripts/run/run-carry-flush.ts +63 -0
- package/scripts/run/run-carry-stash.ts +71 -0
- package/scripts/run/run-carry-streak.ts +113 -0
- package/scripts/run/run-carry.ts +550 -0
- package/scripts/run/run-cut-args.ts +132 -0
- package/scripts/run/run-cut-cli.ts +461 -0
- package/scripts/run/run-cut-handoff.ts +126 -0
- package/scripts/run/run-cut.ts +531 -0
- package/scripts/run/run-ending-cli.ts +111 -0
- package/scripts/run/run-ending.ts +239 -0
- package/scripts/run/run-entry-cli.ts +135 -0
- package/scripts/run/run-entry.ts +95 -0
- package/scripts/run/run-event-cli.ts +210 -0
- package/scripts/run/run-event-follow.ts +65 -0
- package/scripts/run/run-event-render.ts +75 -0
- package/scripts/run/run-event-scope.ts +127 -0
- package/scripts/run/run-event-stream-emit.ts +140 -0
- package/scripts/run/run-event-stream.ts +258 -0
- package/scripts/run/run-event-watch.ts +37 -0
- package/scripts/run/run-headless.ts +105 -0
- package/scripts/run/run-hold-cli.ts +310 -0
- package/scripts/run/run-hold.ts +264 -0
- package/scripts/run/run-invocation.ts +237 -0
- package/scripts/run/run-issue-number.ts +19 -0
- package/scripts/run/run-liveness-cli.ts +169 -0
- package/scripts/run/run-liveness.ts +424 -0
- package/scripts/run/run-merge-cli.ts +385 -0
- package/scripts/run/run-merge-steps.ts +326 -0
- package/scripts/run/run-merge.ts +158 -0
- package/scripts/run/run-next-cli.ts +48 -0
- package/scripts/run/run-next.ts +80 -0
- package/scripts/run/run-preflight.ts +337 -0
- package/scripts/run/run-prep-cli.ts +151 -0
- package/scripts/run/run-prep.ts +84 -0
- package/scripts/run/run-progress-args.ts +65 -0
- package/scripts/run/run-progress-cli.ts +508 -0
- package/scripts/run/run-progress-clock.ts +320 -0
- package/scripts/run/run-progress-config.ts +119 -0
- package/scripts/run/run-progress-read.ts +199 -0
- package/scripts/run/run-progress.ts +366 -0
- package/scripts/run/run-report-cli.ts +71 -0
- package/scripts/run/run-report.ts +78 -0
- package/scripts/run/run-review-cli.ts +130 -0
- package/scripts/run/run-review-steps.ts +161 -0
- package/scripts/run/run-review.ts +121 -0
- package/scripts/run/run-ship-cli.ts +431 -0
- package/scripts/run/run-ship-detach.ts +200 -0
- package/scripts/run/run-ship-probe.ts +103 -0
- package/scripts/run/run-ship-return.ts +75 -0
- package/scripts/run/run-ship-review-steps.ts +227 -0
- package/scripts/run/run-ship-review.ts +177 -0
- package/scripts/run/run-ship-stage.ts +150 -0
- package/scripts/run/run-ship.ts +78 -0
- package/scripts/run/run-status-cli.ts +175 -0
- package/scripts/run/run-status.ts +77 -0
- package/scripts/run/run-step-cli.ts +111 -0
- package/scripts/run/run-step.ts +292 -0
- package/scripts/run/run-stop-notify.ts +52 -0
- package/scripts/run/run-stranded-cli.ts +31 -0
- package/scripts/run/run-stranded-detect.ts +134 -0
- package/scripts/run/run-stranded.ts +120 -0
- package/scripts/run/run-tail-cli.ts +105 -0
- package/scripts/run/run-tail.ts +61 -0
- package/scripts/run/run-transcript.golden.txt +57 -0
- package/scripts/run/run-wake-cli.ts +428 -0
- package/scripts/run/run-wake-describe.ts +97 -0
- package/scripts/run/run-wake-driver.ts +80 -0
- package/scripts/run/run-wake-handoff.ts +29 -0
- package/scripts/run/run-wake-loop.ts +223 -0
- package/scripts/run/run-wake-session.ts +150 -0
- package/scripts/run/run-wake-work.ts +104 -0
- package/scripts/run/run-wake.ts +607 -0
- package/scripts/run/run-watcher-guard-cli.ts +37 -0
- package/scripts/run/run-watcher-guard.ts +59 -0
- package/scripts/run/run-watcher-hook.ts +86 -0
- package/scripts/security/secretlint-scan.ts +86 -0
- package/scripts/security/security-audit-logic.ts +51 -0
- package/scripts/security/security-audit-provision-logic.ts +217 -0
- package/scripts/security/security-audit-provision.ts +186 -0
- package/scripts/security/security-audit.ts +132 -0
- package/scripts/security/security-updates-logic.ts +131 -0
- package/scripts/security/security-updates.ts +38 -0
- package/scripts/security/sonar-file.ts +44 -0
- package/scripts/self-sync-guard/index.ts +5 -0
- package/scripts/self-sync-guard/self-sync-guard-logic.ts +98 -0
- package/scripts/self-sync-guard/self-sync-refusal.ts +22 -0
- package/scripts/sonar/sonar-hotspots-cli.ts +149 -0
- package/scripts/sonar/sonar-hotspots.ts +111 -0
- package/scripts/split/split-assess-cli.ts +52 -0
- package/scripts/split/split-assess.ts +124 -0
- package/scripts/sync/dependabot-pin-logic.ts +135 -0
- package/scripts/sync/directory-copy-guard.ts +187 -0
- package/scripts/sync/file-content.ts +49 -0
- package/scripts/sync/managed-config-scope-cli.ts +64 -0
- package/scripts/sync/managed-config-scope.ts +123 -0
- package/scripts/sync/plugin-skill-directories.ts +15 -0
- package/scripts/sync/reconcile-templates.ts +53 -0
- package/scripts/sync/removed-skill-manifest.ts +27 -0
- package/scripts/sync/skill-migration.ts +192 -0
- package/scripts/sync/sync-configs.ts +156 -0
- package/scripts/sync/sync-hook-safety.ts +82 -0
- package/scripts/sync/sync.ts +417 -0
- package/scripts/sync/synced-paths.ts +52 -0
- package/scripts/sync/template-source-logic.ts +122 -0
- package/scripts/sync/workflow-pin-logic.ts +240 -0
- package/scripts/test/classify-isolation.ts +149 -0
- package/scripts/test/e2e-retry-check.ts +150 -0
- package/scripts/test/http-fault-injection.ts +79 -0
- package/scripts/test/josh-harness-environment.ts +260 -0
- package/scripts/test/josh-harness.ts +96 -0
- package/scripts/test/pilot-files.ts +457 -0
- package/scripts/test/pilot-measure.ts +78 -0
- package/scripts/test/test-declared-changed.ts +99 -0
- package/scripts/test/test-declared-logic.ts +91 -0
- package/scripts/test/test-declared-match.ts +106 -0
- package/scripts/test/test-declared.ts +180 -0
- package/scripts/test/test-e2e-guard.ts +86 -0
- package/scripts/test/test-network-guard.ts +453 -0
- package/scripts/test/test-red-commit.ts +51 -0
- package/scripts/test/test-red-logic.ts +76 -0
- package/scripts/test/test-red.ts +154 -0
- package/scripts/test/test-related-scope.ts +115 -0
- package/scripts/test/test-related.ts +73 -0
- package/scripts/test/test-repository-guard.ts +317 -0
- package/scripts/test/test-state-guard.ts +85 -0
- package/scripts/test/test-stdout-guard.ts +54 -0
- package/scripts/test/test-telegram-guard.ts +90 -0
- package/scripts/test/test-type-logic.ts +31 -0
- package/scripts/test/test-unit-guard.ts +185 -0
- package/scripts/test/unit-guard-environment.ts +12 -0
- package/scripts/test/unit-projects.ts +139 -0
- package/scripts/test/unit-worker-share.ts +251 -0
- package/scripts/test/vitest-include-globs.ts +12 -0
- package/scripts/time-runtime/time-agent-bundles.ts +128 -0
- package/scripts/time-runtime/time-background.ts +300 -0
- package/scripts/time-runtime/time-batch-guard-fixtures.ts +66 -0
- package/scripts/time-runtime/time-batch-guard.ts +529 -0
- package/scripts/time-runtime/time-bundle-call.ts +575 -0
- package/scripts/time-runtime/time-bundles.ts +536 -0
- package/scripts/time-runtime/time-call-identity.ts +37 -0
- package/scripts/time-runtime/time-command-key.ts +35 -0
- package/scripts/time-runtime/time-density-hook.ts +166 -0
- package/scripts/time-runtime/time-density.ts +147 -0
- package/scripts/time-runtime/time-followup-stage.ts +107 -0
- package/scripts/time-runtime/time-format.ts +186 -0
- package/scripts/time-runtime/time-hook-transcript.ts +82 -0
- package/scripts/time-runtime/time-instant.ts +20 -0
- package/scripts/time-runtime/time-markers.ts +228 -0
- package/scripts/time-runtime/time-phase-names.ts +123 -0
- package/scripts/time-runtime/time-reported-failure.ts +227 -0
- package/scripts/time-runtime/time-round-trips.ts +279 -0
- package/scripts/time-runtime/time-shell.ts +286 -0
- package/scripts/time-runtime/time-single-check.ts +65 -0
- package/scripts/time-runtime/time-spans.ts +545 -0
- package/scripts/time-runtime/time-transcript-line.ts +253 -0
- package/scripts/time-runtime/time-writes.ts +71 -0
- package/scripts/ui/ui-routes-cli.ts +122 -0
- package/scripts/ui/ui-routes.ts +102 -0
- package/scripts/version/bump-version.ts +71 -0
- package/scripts/version/effective-upstream.ts +93 -0
- package/scripts/version/index.ts +25 -0
- package/scripts/version/kit-descriptor.ts +10 -0
- package/scripts/version/kit-version-config.ts +30 -0
- package/scripts/version/latest-corepack.ts +404 -0
- package/scripts/version/latest-lane-guard.ts +49 -0
- package/scripts/version/latest-regression.ts +93 -0
- package/scripts/version/latest-scope-cli.ts +147 -0
- package/scripts/version/latest-stamp.ts +107 -0
- package/scripts/version/latest-update.ts +198 -0
- package/scripts/version/package-manager-version.ts +95 -0
- package/scripts/version/parse-json.ts +13 -0
- package/scripts/version/preinstall-version-update.ts +84 -0
- package/scripts/version/publishable-range-check.ts +74 -0
- package/scripts/version/publishable-range.ts +123 -0
- package/scripts/version/release-age.ts +146 -0
- package/scripts/version/release-hold.ts +142 -0
- package/scripts/version/running-binary.ts +42 -0
- package/scripts/version/upgrade-command-guard.ts +49 -0
- package/scripts/version/upgrade-shell-command.ts +30 -0
- package/scripts/version/version-check-logic.ts +333 -0
- package/scripts/version/version-check.ts +14 -0
- package/scripts/version/version-command-config.ts +177 -0
- package/scripts/version/version-commands.ts +296 -0
- package/scripts/version/version-remote.ts +86 -0
- package/scripts/version/version-targets.ts +81 -0
- package/scripts/yaml/yaml-document.ts +46 -0
- package/scripts-ai/completion-gate.ts +13 -0
- package/scripts-ai/environment-loader.ts +9 -0
- package/scripts-ai/epic-check.ts +34 -0
- package/scripts-ai/epic-cli-argv.ts +77 -0
- package/scripts-ai/epic-cli-remove.ts +71 -0
- package/scripts-ai/epic-cli.ts +413 -0
- package/scripts-ai/epic.ts +192 -0
- package/scripts-ai/followup-issue-number.ts +31 -0
- package/scripts-ai/git-followup-finish.ts +217 -0
- package/scripts-ai/git-followup-workflow.ts +252 -0
- package/scripts-ai/git-workflow.ts +246 -0
- package/scripts-ai/telegram-test-logic.ts +88 -0
- package/scripts-ai/telegram-test.ts +130 -0
- package/templates/gitignore +76 -0
- package/templates/sonar-project.properties +52 -0
- package/templates/workflows/ci.yml +639 -0
- package/templates/workflows/dependabot-auto-merge.yml +428 -0
- package/tsconfig/base.json +50 -0
- package/tsconfig.sonar.json +34 -0
|
@@ -0,0 +1,244 @@
|
|
|
1
|
+
// Duplicate candidates for an issue nobody has filed yet.
|
|
2
|
+
//
|
|
3
|
+
// `epic:bundle` answers "does this belong with something" for an issue that already exists, from two
|
|
4
|
+
// strong signals: the two issues citing each other in prose, or a `blocked-by` recorded between them.
|
|
5
|
+
// Neither reaches a draft — it has no number, so nothing can cite it and nothing can be recorded
|
|
6
|
+
// against it — and the question a filing entry actually asks first is a different one: has somebody
|
|
7
|
+
// already filed this? (joshuafolkken/kit#1252)
|
|
8
|
+
//
|
|
9
|
+
// So this is a second signal rather than a change to that threshold. `epic:bundle` deliberately
|
|
10
|
+
// refuses title resemblance as grounds for *bundling*, because "related" expands without limit; the
|
|
11
|
+
// output here is not a bundle but a short list to read before filing, where a false positive costs
|
|
12
|
+
// one issue read and a false negative costs a duplicate nobody notices for days.
|
|
13
|
+
|
|
14
|
+
// Words that sit in issue titles whatever the issue is about. Left in, they lift every pair's score
|
|
15
|
+
// together — which does not reorder the candidates, but does drag unrelated ones over the threshold.
|
|
16
|
+
const STOP_WORDS: ReadonlySet<string> = new Set([
|
|
17
|
+
'a',
|
|
18
|
+
'an',
|
|
19
|
+
'and',
|
|
20
|
+
'are',
|
|
21
|
+
'as',
|
|
22
|
+
'at',
|
|
23
|
+
'be',
|
|
24
|
+
'but',
|
|
25
|
+
'by',
|
|
26
|
+
'can',
|
|
27
|
+
'do',
|
|
28
|
+
'for',
|
|
29
|
+
'from',
|
|
30
|
+
'has',
|
|
31
|
+
'have',
|
|
32
|
+
'in',
|
|
33
|
+
'into',
|
|
34
|
+
'is',
|
|
35
|
+
'it',
|
|
36
|
+
'its',
|
|
37
|
+
'no',
|
|
38
|
+
'not',
|
|
39
|
+
'of',
|
|
40
|
+
'on',
|
|
41
|
+
'or',
|
|
42
|
+
'so',
|
|
43
|
+
'than',
|
|
44
|
+
'that',
|
|
45
|
+
'the',
|
|
46
|
+
'then',
|
|
47
|
+
'this',
|
|
48
|
+
'to',
|
|
49
|
+
'was',
|
|
50
|
+
'when',
|
|
51
|
+
'which',
|
|
52
|
+
'with',
|
|
53
|
+
'without',
|
|
54
|
+
])
|
|
55
|
+
|
|
56
|
+
// Everything that is not a letter or a digit separates tokens, so backticks, hyphens, colons and the
|
|
57
|
+
// `#` of an issue reference all fall away — `josh epic:bundle` reads as `josh`, `epic`, `bundle`.
|
|
58
|
+
const TOKEN_SEPARATOR = /[^a-z\d]+/u
|
|
59
|
+
|
|
60
|
+
// A single character is a fragment rather than a word: it survives as the tail of a split identifier
|
|
61
|
+
// far more often than it carries meaning.
|
|
62
|
+
const MIN_TOKEN_LENGTH = 2
|
|
63
|
+
|
|
64
|
+
// Dice: twice the shared tokens over the two sizes together.
|
|
65
|
+
const DICE_NUMERATOR_FACTOR = 2
|
|
66
|
+
|
|
67
|
+
// Titles are compared by which words they use, not by their spelling. Two issues about one job are
|
|
68
|
+
// written months apart by different sessions and share vocabulary, not word order — which is what
|
|
69
|
+
// rules out whole-string edit distance, and with it the maintained packages built on it.
|
|
70
|
+
//
|
|
71
|
+
// The threshold is what keeps the answer honest when there is nothing to report. It was set against
|
|
72
|
+
// this repository's own backlog: at 0.35 a draft's title has to share roughly a third of its
|
|
73
|
+
// significant words with an existing one, which admits genuine restatements and leaves pairs that
|
|
74
|
+
// merely share a subsystem below the line.
|
|
75
|
+
const SIMILARITY_THRESHOLD = 0.35
|
|
76
|
+
|
|
77
|
+
// One shared word is a coincidence — most often a subsystem name every second title carries. Two is
|
|
78
|
+
// the smallest number that can describe a subject rather than name a neighborhood.
|
|
79
|
+
const MIN_SHARED_TOKENS = 2
|
|
80
|
+
|
|
81
|
+
// The list is read by a person or an agent about to file. Past a handful it stops being read at all,
|
|
82
|
+
// and the ranking already puts the strongest first.
|
|
83
|
+
const MAX_CANDIDATES = 5
|
|
84
|
+
|
|
85
|
+
// An issue as the duplicate search sees it. `title` is optional for the same reason it is on
|
|
86
|
+
// `BacklogIssue`: an issue read one at a time by reference carries none, and one with no title simply
|
|
87
|
+
// scores nothing rather than failing the scan.
|
|
88
|
+
interface ScoutIssue {
|
|
89
|
+
number: number
|
|
90
|
+
title?: string
|
|
91
|
+
// The epic tracking it, printed beside the candidate: where similar work already lives is the
|
|
92
|
+
// other half of what a filing entry is about to decide.
|
|
93
|
+
epic?: number
|
|
94
|
+
// An epic is a container, never a duplicate of a deliverable. Reported as one it reads as "this is
|
|
95
|
+
// already filed as #<E>", and the caller is sent to run an epic that has no implementation of its
|
|
96
|
+
// own. `epic_bundle.is_strong_signal` excludes them from the other half for the same reason.
|
|
97
|
+
is_epic?: boolean
|
|
98
|
+
// A closed issue is a candidate, and the two are not interchangeable to whoever reads the list
|
|
99
|
+
// (joshuafolkken/kit#1679): an open one says "somebody is already tracking this", a closed one
|
|
100
|
+
// says "this may already be done". So the state travels with the row rather than being inferred
|
|
101
|
+
// from which listing it came out of, which the ranking mixes together.
|
|
102
|
+
is_closed?: boolean
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
interface DuplicateCandidate {
|
|
106
|
+
number: number
|
|
107
|
+
title: string
|
|
108
|
+
score: number
|
|
109
|
+
is_referenced?: boolean
|
|
110
|
+
epic?: number
|
|
111
|
+
is_closed?: boolean
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
// What the scan found: the candidates worth reading, and how many cleared the bar in total. The two
|
|
115
|
+
// differ once more than `MAX_CANDIDATES` match, and a caller that prints only the first number is
|
|
116
|
+
// reporting a truncation as a complete answer.
|
|
117
|
+
interface DuplicateSearch {
|
|
118
|
+
candidates: Array<DuplicateCandidate>
|
|
119
|
+
total: number
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
function is_significant(word: string): boolean {
|
|
123
|
+
return word.length >= MIN_TOKEN_LENGTH && !STOP_WORDS.has(word)
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
function tokenize(text: string): Set<string> {
|
|
127
|
+
const words = text.toLowerCase().split(TOKEN_SEPARATOR)
|
|
128
|
+
|
|
129
|
+
return new Set(words.filter((word) => is_significant(word)))
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
function shared_tokens(left: ReadonlySet<string>, right: ReadonlySet<string>): number {
|
|
133
|
+
return [...left].filter((token) => right.has(token)).length
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
function dice_similarity(left: ReadonlySet<string>, right: ReadonlySet<string>): number {
|
|
137
|
+
const total = left.size + right.size
|
|
138
|
+
|
|
139
|
+
if (total === 0) return 0
|
|
140
|
+
|
|
141
|
+
return (DICE_NUMERATOR_FACTOR * shared_tokens(left, right)) / total
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
// The score, or nothing when it does not clear both bars. Both are required rather than either: the
|
|
145
|
+
// count keeps a two-word title from scoring 1.0 on one shared word, and the ratio keeps a long title
|
|
146
|
+
// from qualifying on two words out of twenty.
|
|
147
|
+
function qualifying_score(
|
|
148
|
+
query: ReadonlySet<string>,
|
|
149
|
+
tokens: ReadonlySet<string>,
|
|
150
|
+
): number | undefined {
|
|
151
|
+
if (shared_tokens(query, tokens) < MIN_SHARED_TOKENS) return undefined
|
|
152
|
+
|
|
153
|
+
const score = dice_similarity(query, tokens)
|
|
154
|
+
|
|
155
|
+
return score < SIMILARITY_THRESHOLD ? undefined : score
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
function to_epic_field(epic: number | undefined): { epic?: number } {
|
|
159
|
+
return epic === undefined ? {} : { epic }
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
function to_closed_field(is_closed: boolean | undefined): { is_closed?: boolean } {
|
|
163
|
+
return is_closed === true ? { is_closed: true } : {}
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
function candidate_score(
|
|
167
|
+
query: ReadonlySet<string>,
|
|
168
|
+
issue: ScoutIssue,
|
|
169
|
+
references: ReadonlySet<number>,
|
|
170
|
+
): number | undefined {
|
|
171
|
+
const score = qualifying_score(query, tokenize(issue.title ?? ''))
|
|
172
|
+
|
|
173
|
+
if (score !== undefined) return score
|
|
174
|
+
|
|
175
|
+
return references.has(issue.number) ? 0 : undefined
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
function to_reference_field(is_referenced: boolean): { is_referenced?: boolean } {
|
|
179
|
+
return is_referenced ? { is_referenced: true } : {}
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
function to_candidate(
|
|
183
|
+
query: ReadonlySet<string>,
|
|
184
|
+
issue: ScoutIssue,
|
|
185
|
+
references: ReadonlySet<number>,
|
|
186
|
+
): DuplicateCandidate | undefined {
|
|
187
|
+
if (issue.is_epic === true) return undefined
|
|
188
|
+
|
|
189
|
+
const score = candidate_score(query, issue, references)
|
|
190
|
+
|
|
191
|
+
if (score === undefined) return undefined
|
|
192
|
+
|
|
193
|
+
return {
|
|
194
|
+
number: issue.number,
|
|
195
|
+
title: issue.title ?? '',
|
|
196
|
+
score,
|
|
197
|
+
...to_reference_field(references.has(issue.number)),
|
|
198
|
+
...to_epic_field(issue.epic),
|
|
199
|
+
...to_closed_field(issue.is_closed),
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
function compare_candidates(left: DuplicateCandidate, right: DuplicateCandidate): number {
|
|
204
|
+
const reference_order = Number(right.is_referenced === true) - Number(left.is_referenced === true)
|
|
205
|
+
|
|
206
|
+
return reference_order || right.score - left.score
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
// The issues whose titles look like this one, strongest first.
|
|
210
|
+
//
|
|
211
|
+
// Every row is scored — no prefix, no early exit. The issue this search exists to catch is the one
|
|
212
|
+
// another session filed minutes ago, and where that one sits in the listing is the single thing
|
|
213
|
+
// nobody controls (joshuafolkken/kit#1252). Since joshuafolkken/kit#1679 the rows may be closed as
|
|
214
|
+
// well as open, and the two rank together: a run about to file has to be shown the strongest match
|
|
215
|
+
// there is, and which listing it came out of says nothing about how well it matches.
|
|
216
|
+
function find_duplicates(
|
|
217
|
+
title: string,
|
|
218
|
+
issues: ReadonlyArray<ScoutIssue>,
|
|
219
|
+
referenced_numbers: ReadonlyArray<number> = [],
|
|
220
|
+
): DuplicateSearch {
|
|
221
|
+
const query = tokenize(title)
|
|
222
|
+
const references = new Set(referenced_numbers)
|
|
223
|
+
const matched = issues
|
|
224
|
+
.flatMap((issue) => to_candidate(query, issue, references) ?? [])
|
|
225
|
+
.toSorted(compare_candidates)
|
|
226
|
+
|
|
227
|
+
// `total` rather than the shown list's length: a caller printing only what it was handed would
|
|
228
|
+
// report a truncation as a complete answer, which is the one gap in this feature that nothing else
|
|
229
|
+
// reports — every other one the command has goes through `warn_about_gaps`.
|
|
230
|
+
return { candidates: matched.slice(0, MAX_CANDIDATES), total: matched.length }
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
const issue_scout = {
|
|
234
|
+
SIMILARITY_THRESHOLD,
|
|
235
|
+
MIN_SHARED_TOKENS,
|
|
236
|
+
MAX_CANDIDATES,
|
|
237
|
+
tokenize,
|
|
238
|
+
shared_tokens,
|
|
239
|
+
dice_similarity,
|
|
240
|
+
find_duplicates,
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
export type { DuplicateCandidate, DuplicateSearch, ScoutIssue }
|
|
244
|
+
export { issue_scout }
|
|
@@ -0,0 +1,260 @@
|
|
|
1
|
+
#!/usr/bin/env tsx
|
|
2
|
+
import { fileURLToPath } from 'node:url'
|
|
3
|
+
import { git_gh_command } from '#scripts/git/git-gh-command'
|
|
4
|
+
import { bounded_pool } from '#scripts/lib/bounded-pool'
|
|
5
|
+
import { issue_state, type IssueState } from './issue-state'
|
|
6
|
+
|
|
7
|
+
// `josh issue:state <N> [<N> ...]` — print each issue's state and labels, in the spelling the
|
|
8
|
+
// documents compare against (joshuafolkken/kit#1054; several numbers in one call,
|
|
9
|
+
// joshuafolkken/kit#1302).
|
|
10
|
+
//
|
|
11
|
+
// The numbers are read concurrently and reported together because the callers that need more than
|
|
12
|
+
// one need them all: `diag`'s ranking table reads a state per row, and one process start plus one
|
|
13
|
+
// round trip per row cost 1.6 seconds each — about 8 seconds of a five-row table spent on nothing
|
|
14
|
+
// but the states.
|
|
15
|
+
//
|
|
16
|
+
// It replaces the two reads the workflow documents told an agent to type — `gh issue view <N> --json
|
|
17
|
+
// state --jq .state` and `gh issue view <N> --json state,labels --jq …` — with one call that answers
|
|
18
|
+
// both. Those go through GraphQL, which a cloud session is refused, and that read is `epic-child`'s
|
|
19
|
+
// verifier: the whole reason a child of an epic may be delegated at all is that the parent re-reads
|
|
20
|
+
// the child's state from GitHub rather than trusting the unit's summary. A verifier that 403s leaves
|
|
21
|
+
// the delegation running with nothing checking it.
|
|
22
|
+
//
|
|
23
|
+
// **A failed read is never printed as a state.** `gh issue view` exited non-zero with nothing on
|
|
24
|
+
// stdout for a rate limit and for a number that does not exist alike, and a loop that read the empty
|
|
25
|
+
// answer as "not CLOSED" would report a child as failed because nobody could reach GitHub. The two
|
|
26
|
+
// are told apart here and neither is a state.
|
|
27
|
+
|
|
28
|
+
const SUCCESS_EXIT_CODE = 0
|
|
29
|
+
const FAILURE_EXIT_CODE = 1
|
|
30
|
+
const ARGV_OFFSET = 2
|
|
31
|
+
const REPO_FLAG = '--repo'
|
|
32
|
+
const FLAG_PREFIX = '-'
|
|
33
|
+
const INLINE_REPO_FLAG = `${REPO_FLAG}=`
|
|
34
|
+
const ISSUE_NUMBER_PATTERN = /^[1-9]\d*$/u
|
|
35
|
+
const USAGE = 'Usage: josh issue:state <issue-number> [<issue-number> ...] [--repo <owner/repo>]'
|
|
36
|
+
// A blank line between the blocks of a multi-number report, so a person sees where one issue ends
|
|
37
|
+
// while a reader matching `issue: ` still finds each block by its first line.
|
|
38
|
+
const BLOCK_SEPARATOR = '\n\n'
|
|
39
|
+
// The same bound `epic:bundle` puts on its reference lookup, for the same reason: every read is a
|
|
40
|
+
// `gh` process, and an unbounded fan-out is answered with secondary rate limiting rather than with
|
|
41
|
+
// states.
|
|
42
|
+
const READ_CONCURRENCY = 8
|
|
43
|
+
// The two fields, under the names `gh issue view --json` gave them. `git-gh-issue-rest.ts` maps them
|
|
44
|
+
// back from REST, which is what keeps `OPEN` / `CLOSED` / `MERGED` out of this file.
|
|
45
|
+
const STATE_FIELDS = 'state,labels'
|
|
46
|
+
|
|
47
|
+
interface StateRequest {
|
|
48
|
+
issue_numbers: ReadonlyArray<string>
|
|
49
|
+
repo?: string
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
// The two failure kinds a read can end in, kept apart all the way to the report: `missing` is the
|
|
53
|
+
// number resolving to nothing and `unreadable` is a read that failed, and the documents ask a
|
|
54
|
+
// caller to report the two differently. Folding them together to simplify the batch would destroy
|
|
55
|
+
// exactly that distinction.
|
|
56
|
+
type ReadFailureKind = 'missing' | 'unreadable'
|
|
57
|
+
|
|
58
|
+
// One issue's state, or the failure kind. Named for export because `run:prep` reads the state beside
|
|
59
|
+
// the body and the dependency-update scope (joshuafolkken/kit#1978), reusing this read rather than a
|
|
60
|
+
// second one that would drift from the `human_review` decision `parse_issue_state` owns.
|
|
61
|
+
type StateRead = { kind: 'state'; state: IssueState } | { kind: ReadFailureKind }
|
|
62
|
+
|
|
63
|
+
interface IssueReport {
|
|
64
|
+
issue_number: string
|
|
65
|
+
result: StateRead
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
// `absent` and "given but with nothing usable after it" are different answers. Falling back to the
|
|
69
|
+
// session's repository on the second would print a confident state for a *different* issue of the
|
|
70
|
+
// same number, which is the exact misread the `--repo` argument exists to prevent.
|
|
71
|
+
//
|
|
72
|
+
// A named flag carries the argv positions it actually occupied, never the token text it matched
|
|
73
|
+
// (joshuafolkken/kit#1355). Removing tokens by value removes every token that happens to look like
|
|
74
|
+
// one of them — a second `--repo`, a repeated `owner/repo` — and a token removed is a token nobody
|
|
75
|
+
// is told about, which is the whole failure this file refuses elsewhere.
|
|
76
|
+
type RepoFlag =
|
|
77
|
+
| { kind: 'absent' }
|
|
78
|
+
| { kind: 'named'; repo: string; consumed: ReadonlyArray<number> }
|
|
79
|
+
| { kind: 'incomplete' }
|
|
80
|
+
|
|
81
|
+
// Both spellings gh itself accepts. `--repo=owner/repo` falling through to the separate-word branch
|
|
82
|
+
// would read as no repository at all, which is the fall back to the session's repository above.
|
|
83
|
+
function read_inline_repo(argv: ReadonlyArray<string>): RepoFlag | undefined {
|
|
84
|
+
const inline = argv.find((argument) => argument.startsWith(INLINE_REPO_FLAG))
|
|
85
|
+
|
|
86
|
+
if (inline === undefined) return undefined
|
|
87
|
+
|
|
88
|
+
const repo = inline.slice(INLINE_REPO_FLAG.length)
|
|
89
|
+
|
|
90
|
+
if (repo.length === 0) return { kind: 'incomplete' }
|
|
91
|
+
|
|
92
|
+
return { kind: 'named', repo, consumed: [argv.indexOf(inline)] }
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
function read_separate_repo(argv: ReadonlyArray<string>): RepoFlag {
|
|
96
|
+
const index = argv.indexOf(REPO_FLAG)
|
|
97
|
+
|
|
98
|
+
if (index === -1) return { kind: 'absent' }
|
|
99
|
+
|
|
100
|
+
const value_index = index + 1
|
|
101
|
+
const repo = argv[value_index]
|
|
102
|
+
|
|
103
|
+
if (repo === undefined || repo.startsWith(FLAG_PREFIX)) return { kind: 'incomplete' }
|
|
104
|
+
|
|
105
|
+
return { kind: 'named', repo, consumed: [index, value_index] }
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
function read_repo_flag(argv: ReadonlyArray<string>): RepoFlag {
|
|
109
|
+
return read_inline_repo(argv) ?? read_separate_repo(argv)
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
// The argv positions the repository flag occupied, and nothing else. Everything outside this set was
|
|
113
|
+
// offered as an issue number, whatever it looks like — so a flag nobody recognizes reaches the check
|
|
114
|
+
// below and refuses the call instead of vanishing from it (joshuafolkken/kit#1355).
|
|
115
|
+
function consumed_indices(repo_flag: RepoFlag): ReadonlySet<number> {
|
|
116
|
+
return new Set(repo_flag.kind === 'named' ? repo_flag.consumed : [])
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
function parse_request(argv: ReadonlyArray<string>): StateRequest | undefined {
|
|
120
|
+
const repo_flag = read_repo_flag(argv)
|
|
121
|
+
|
|
122
|
+
if (repo_flag.kind === 'incomplete') return undefined
|
|
123
|
+
|
|
124
|
+
// The repository is read first, flag word and value together, so neither is ever mistaken for an
|
|
125
|
+
// issue number — and nothing else is removed here. Every remaining token is one the caller offered
|
|
126
|
+
// as an issue number, whatever it looks like.
|
|
127
|
+
const consumed = consumed_indices(repo_flag)
|
|
128
|
+
const rest = argv.filter((_argument, index) => !consumed.has(index))
|
|
129
|
+
const numbers = rest.filter((argument) => ISSUE_NUMBER_PATTERN.test(argument))
|
|
130
|
+
|
|
131
|
+
// A token that is not a number refuses the whole invocation rather than being dropped. Dropping
|
|
132
|
+
// it answers fewer numbers than were asked for and still exits zero — and with one number left,
|
|
133
|
+
// the surviving block prints in the single-number shape, so nothing in the output says a number
|
|
134
|
+
// went unanswered. `#1262` copied out of a `diag` table is exactly that token.
|
|
135
|
+
//
|
|
136
|
+
// An unrecognized flag reaches this same check rather than a lenient one of its own
|
|
137
|
+
// (joshuafolkken/kit#1355). `--rep=owner/repo` used to be discarded for starting with a dash, so
|
|
138
|
+
// the call fell back to the session's repository and printed a confident state for a *different*
|
|
139
|
+
// repository's issue of that number — the misread `incomplete` already exists to prevent.
|
|
140
|
+
if (numbers.length === 0 || numbers.length !== rest.length) return undefined
|
|
141
|
+
|
|
142
|
+
// In the order they were typed, with a repeat dropped: repeating a number would spend a second
|
|
143
|
+
// read on an answer already in hand, and print a second block a caller counting rows counts twice.
|
|
144
|
+
const issue_numbers = [...new Set(numbers)]
|
|
145
|
+
|
|
146
|
+
return repo_flag.kind === 'named' ? { issue_numbers, repo: repo_flag.repo } : { issue_numbers }
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
// A number that resolves to nothing is an answer — a typo, or another repository's number quoted in
|
|
150
|
+
// prose — and it is reported as one. Everything else is a gap, and the message says so, because the
|
|
151
|
+
// caller's next move differs: a gap is retried, an answer is not.
|
|
152
|
+
function report_failure(kind: ReadFailureKind, issue_number: string): void {
|
|
153
|
+
if (kind === 'missing') {
|
|
154
|
+
console.error(`✖ issue #${issue_number} does not resolve — check the number and the repository`)
|
|
155
|
+
} else {
|
|
156
|
+
console.error(
|
|
157
|
+
`✖ could not read issue #${issue_number} — a rate limit, expired auth, or a dropped connection. This is not "the issue is open"`,
|
|
158
|
+
)
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
// One number's read, reduced to what the report needs and nothing printed yet. Separating the two
|
|
163
|
+
// is what lets the whole batch be in flight at once: the numbers are independent, so reading them
|
|
164
|
+
// one after the other spends a round trip per number for no reason.
|
|
165
|
+
async function read_issue(issue_number: string, repo?: string): Promise<StateRead> {
|
|
166
|
+
const read = await git_gh_command.issue_view_json_classified(issue_number, STATE_FIELDS, repo)
|
|
167
|
+
|
|
168
|
+
if (read.kind !== 'read') return { kind: read.kind }
|
|
169
|
+
|
|
170
|
+
const parsed = issue_state.parse_issue_state(read.json)
|
|
171
|
+
|
|
172
|
+
return parsed === undefined ? { kind: 'unreadable' } : { kind: 'state', state: parsed }
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
// Through the shared pool rather than a raw `Promise.all`: each read spawns a `gh` process, and a
|
|
176
|
+
// whole epic's children fired at once draws GitHub's secondary rate limiting — which comes back as
|
|
177
|
+
// `unreadable` for issues that exist and are perfectly readable. Same bound `epic:bundle`'s
|
|
178
|
+
// reference lookup uses, and the pool is shared rather than spelled out again here.
|
|
179
|
+
async function read_all(request: StateRequest): Promise<ReadonlyArray<IssueReport>> {
|
|
180
|
+
return await bounded_pool.bounded_map(
|
|
181
|
+
request.issue_numbers,
|
|
182
|
+
READ_CONCURRENCY,
|
|
183
|
+
async (issue_number) => ({
|
|
184
|
+
issue_number,
|
|
185
|
+
result: await read_issue(issue_number, request.repo),
|
|
186
|
+
}),
|
|
187
|
+
)
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
// `[]` for a number that produced no state, so one unresolvable number costs the others nothing.
|
|
191
|
+
// The heading is withheld for a single number: `.claude/skills/workflow-commands/SKILL.md` §2z and
|
|
192
|
+
// `.claude/skills/diag/SKILL.md` read that report's three lines verbatim.
|
|
193
|
+
function state_blocks(report: IssueReport, should_attribute: boolean): ReadonlyArray<string> {
|
|
194
|
+
if (report.result.kind !== 'state') return []
|
|
195
|
+
|
|
196
|
+
const { state } = report.result
|
|
197
|
+
|
|
198
|
+
if (!should_attribute) return [issue_state.format_issue_state(state)]
|
|
199
|
+
|
|
200
|
+
return [issue_state.format_attributed_issue_state(report.issue_number, state)]
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
function print_states(reports: ReadonlyArray<IssueReport>): void {
|
|
204
|
+
// Derived from what was asked for rather than from what came back: a call for two numbers whose
|
|
205
|
+
// first resolves to nothing must still name the number the surviving block belongs to.
|
|
206
|
+
const should_attribute = reports.length > 1
|
|
207
|
+
const blocks = reports.flatMap((report) => state_blocks(report, should_attribute))
|
|
208
|
+
|
|
209
|
+
if (blocks.length === 0) return
|
|
210
|
+
|
|
211
|
+
console.info(blocks.join(BLOCK_SEPARATOR))
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
function report_one_failure(report: IssueReport): boolean {
|
|
215
|
+
if (report.result.kind === 'state') return false
|
|
216
|
+
|
|
217
|
+
report_failure(report.result.kind, report.issue_number)
|
|
218
|
+
|
|
219
|
+
return true
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
// Every number that produced no state is named, so a caller is told which ones it has no answer for
|
|
223
|
+
// rather than being left to subtract the printed blocks from what it asked. A single failure still
|
|
224
|
+
// makes the exit code non-zero, exactly as it did when only one number could be passed.
|
|
225
|
+
function report_failures(reports: ReadonlyArray<IssueReport>): number {
|
|
226
|
+
let has_failure = false
|
|
227
|
+
|
|
228
|
+
for (const report of reports) {
|
|
229
|
+
has_failure = report_one_failure(report) || has_failure
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
return has_failure ? FAILURE_EXIT_CODE : SUCCESS_EXIT_CODE
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
async function run(argv: ReadonlyArray<string>): Promise<number> {
|
|
236
|
+
const request = parse_request(argv)
|
|
237
|
+
|
|
238
|
+
if (request === undefined) {
|
|
239
|
+
console.error(USAGE)
|
|
240
|
+
|
|
241
|
+
return FAILURE_EXIT_CODE
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
const reports = await read_all(request)
|
|
245
|
+
|
|
246
|
+
print_states(reports)
|
|
247
|
+
|
|
248
|
+
return report_failures(reports)
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
async function main(argv: ReadonlyArray<string>): Promise<void> {
|
|
252
|
+
process.exitCode = await run(argv)
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
const issue_state_cli = { STATE_FIELDS, USAGE, main, parse_request, read_issue, run }
|
|
256
|
+
|
|
257
|
+
if (process.argv[1] === fileURLToPath(import.meta.url)) await main(process.argv.slice(ARGV_OFFSET))
|
|
258
|
+
|
|
259
|
+
export type { StateRead }
|
|
260
|
+
export { issue_state_cli }
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
import { has_any_label, NEEDS_HUMAN_REVIEW_LABEL } from '#scripts/git/issue-labels'
|
|
2
|
+
import { parse_json_object_safe } from '#scripts/git/parse-json-array'
|
|
3
|
+
import { z } from 'zod'
|
|
4
|
+
|
|
5
|
+
// The answer `josh issue:state` prints, kept apart from the reading and the printing so the shape of
|
|
6
|
+
// the report is decided by one pure function (joshuafolkken/kit#1054).
|
|
7
|
+
//
|
|
8
|
+
// The command exists because the *instructions* needed one. `gh issue view <N> --json state` is what
|
|
9
|
+
// an unattended epic run was told to type to confirm a delegated child, and gh answers that call
|
|
10
|
+
// over GraphQL — which a cloud session is refused (403). The REST endpoint is served normally, but
|
|
11
|
+
// it reports `open` / `closed` in lower case, so writing the REST call into the documents would have
|
|
12
|
+
// copied the casing rule out of `git-gh-rest-state.ts` and into prose. Routing the instruction
|
|
13
|
+
// through a command keeps that rule in the one place joshuafolkken/kit#1024 put it.
|
|
14
|
+
|
|
15
|
+
const NO_LABELS = '(none)'
|
|
16
|
+
const LABEL_SEPARATOR = ', '
|
|
17
|
+
const STATE_LABEL = 'state: '
|
|
18
|
+
const LABELS_LABEL = 'labels: '
|
|
19
|
+
// Whether this issue is one a run must stop on before committing. Printed as its own line rather than
|
|
20
|
+
// left to a reader matching `needs-human-review` against the `labels:` line above
|
|
21
|
+
// (joshuafolkken/kit#1132): GitHub keeps the spelling a label was created with and treats
|
|
22
|
+
// `Needs-Human-Review` as the same label, so an eye comparing against the lowercase string misses it —
|
|
23
|
+
// and a missed one is a run that does not stop and an artifact that ships, which is the whole thing
|
|
24
|
+
// the label exists to prevent. Every other workflow label already reaches its decision through
|
|
25
|
+
// `has_any_label`, which lowercases; this gives that one the same footing.
|
|
26
|
+
const HUMAN_REVIEW_LABEL_LINE = 'human_review: '
|
|
27
|
+
const YES = 'yes'
|
|
28
|
+
const NO = 'no'
|
|
29
|
+
// The number a block belongs to, printed above it when several were asked for
|
|
30
|
+
// (joshuafolkken/kit#1302). A block carries its own number rather than being matched by position,
|
|
31
|
+
// because a batch read drops the numbers that resolve to nothing — a `diag` table mixes closed
|
|
32
|
+
// issues and numbers that never existed — and a positional reading then attributes every block
|
|
33
|
+
// after the gap to the wrong issue.
|
|
34
|
+
const ISSUE_LABEL = 'issue: '
|
|
35
|
+
|
|
36
|
+
const HUMAN_REVIEW_LABELS: ReadonlySet<string> = new Set([NEEDS_HUMAN_REVIEW_LABEL])
|
|
37
|
+
|
|
38
|
+
// `state` and `labels` as `git_gh_issue_read` answers them — the field names and the casing
|
|
39
|
+
// `gh issue view --json` used, not REST's. A response missing `state` is not a state report, so it
|
|
40
|
+
// fails the parse rather than printing an empty verdict.
|
|
41
|
+
const label_schema = z.looseObject({ name: z.string() })
|
|
42
|
+
|
|
43
|
+
const issue_state_schema = z.looseObject({
|
|
44
|
+
state: z.string(),
|
|
45
|
+
labels: label_schema.array().optional(),
|
|
46
|
+
})
|
|
47
|
+
|
|
48
|
+
interface IssueState {
|
|
49
|
+
state: string
|
|
50
|
+
labels: ReadonlyArray<string>
|
|
51
|
+
// Decided here rather than by the caller, so the casing rule lives with the labels it is about.
|
|
52
|
+
is_human_review: boolean
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
// `undefined` for anything that is not a state report — malformed JSON, and equally a well-formed
|
|
56
|
+
// response carrying something else (`{"message":"API rate limit exceeded"}`). The shared parser
|
|
57
|
+
// throws on the second so a changed `gh` field stays visible to its other callers; here the two are
|
|
58
|
+
// the same answer, because printing either as a state is what this command exists to prevent.
|
|
59
|
+
function parse_issue_state(json: string): IssueState | undefined {
|
|
60
|
+
try {
|
|
61
|
+
const parsed = parse_json_object_safe(json, issue_state_schema)
|
|
62
|
+
|
|
63
|
+
if (parsed === undefined) return undefined
|
|
64
|
+
|
|
65
|
+
const labels = parsed.labels ?? []
|
|
66
|
+
|
|
67
|
+
return {
|
|
68
|
+
state: parsed.state,
|
|
69
|
+
labels: labels.map((label) => label.name),
|
|
70
|
+
is_human_review: has_any_label(labels, HUMAN_REVIEW_LABELS),
|
|
71
|
+
}
|
|
72
|
+
} catch {
|
|
73
|
+
return undefined
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
// `(none)` rather than an empty tail: the caller reads this to tell a parked child from a failed one,
|
|
78
|
+
// and a line that simply stops after `labels:` is indistinguishable from a truncated answer.
|
|
79
|
+
function format_labels(labels: ReadonlyArray<string>): string {
|
|
80
|
+
return labels.length === 0 ? NO_LABELS : labels.join(LABEL_SEPARATOR)
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
function format_issue_state(state: IssueState): string {
|
|
84
|
+
const human_review = `${HUMAN_REVIEW_LABEL_LINE}${state.is_human_review ? YES : NO}`
|
|
85
|
+
|
|
86
|
+
return `${STATE_LABEL}${state.state}\n${LABELS_LABEL}${format_labels(state.labels)}\n${human_review}`
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
// The same three lines, under the number they belong to. Kept as a separate function rather than a
|
|
90
|
+
// flag on the one above, so the single-number report has no branch that could ever prepend a fourth
|
|
91
|
+
// line to it: `.claude/skills/workflow-commands/SKILL.md` §2z and `.claude/skills/diag/SKILL.md`
|
|
92
|
+
// read those three verbatim, and §2z's `needs-human-review` stop is decided from them.
|
|
93
|
+
function format_attributed_issue_state(issue_number: string, state: IssueState): string {
|
|
94
|
+
return `${ISSUE_LABEL}${issue_number}\n${format_issue_state(state)}`
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
const issue_state = {
|
|
98
|
+
NO_LABELS,
|
|
99
|
+
STATE_LABEL,
|
|
100
|
+
LABELS_LABEL,
|
|
101
|
+
HUMAN_REVIEW_LABEL_LINE,
|
|
102
|
+
ISSUE_LABEL,
|
|
103
|
+
parse_issue_state,
|
|
104
|
+
format_issue_state,
|
|
105
|
+
format_attributed_issue_state,
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
export type { IssueState }
|
|
109
|
+
export { issue_state }
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
// The body lines that sit under one `## heading`, up to the next `## heading` or the end
|
|
2
|
+
// (joshuafolkken/kit#2212). Both the firing-point check and the baseline parser read a section this
|
|
3
|
+
// way, so the slicing lives in one place rather than being written twice.
|
|
4
|
+
|
|
5
|
+
const HEADING_PREFIX = '## '
|
|
6
|
+
|
|
7
|
+
function is_heading(line: string): boolean {
|
|
8
|
+
return line.trimStart().startsWith(HEADING_PREFIX)
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
function is_the_heading(line: string, heading: string): boolean {
|
|
12
|
+
return line.trim() === heading
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
// Whether some body line, trimmed, is exactly `text`. Both linters ask this — of a `## heading` and of
|
|
16
|
+
// the behavior-change declaration line — so the trim-and-match lives here rather than being written in
|
|
17
|
+
// each. A required token mentioned inside a sentence is not the token, so only an exact line matches.
|
|
18
|
+
function has_line(body: string, text: string): boolean {
|
|
19
|
+
return body.split('\n').some((line) => line.trim() === text)
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
// The lines between `heading` and the next `## ` heading, trimmed of the heading line itself; an empty
|
|
23
|
+
// array when the heading is absent. A heading matched inside a sentence is not the section heading, so
|
|
24
|
+
// only a line that is exactly the heading opens the section.
|
|
25
|
+
function section_lines(body: string, heading: string): ReadonlyArray<string> {
|
|
26
|
+
const lines = body.split('\n')
|
|
27
|
+
const start = lines.findIndex((line) => is_the_heading(line, heading))
|
|
28
|
+
|
|
29
|
+
if (start === -1) return []
|
|
30
|
+
|
|
31
|
+
const rest = lines.slice(start + 1)
|
|
32
|
+
const end = rest.findIndex((line) => is_heading(line))
|
|
33
|
+
|
|
34
|
+
return end === -1 ? rest : rest.slice(0, end)
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
const markdown_section = { section_lines, is_heading, has_line }
|
|
38
|
+
|
|
39
|
+
export { markdown_section }
|