@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,335 @@
|
|
|
1
|
+
import { execFileSync, spawnSync } from 'node:child_process'
|
|
2
|
+
import { randomUUID } from 'node:crypto'
|
|
3
|
+
import { readFileSync, rmSync } from 'node:fs'
|
|
4
|
+
import { createServer, type Server } from 'node:net'
|
|
5
|
+
import path from 'node:path'
|
|
6
|
+
import { PLATFORM_TEMP_ROOT } from './platform-temporary'
|
|
7
|
+
|
|
8
|
+
// Whether the process a record names is still the one that wrote it (joshuafolkken/kit#1245).
|
|
9
|
+
//
|
|
10
|
+
// **A pid is not a process identity, and every record here was treating it as one.** `process.kill(
|
|
11
|
+
// pid, 0)` answers "alive" for whatever holds that number *now*, so a marker left behind by a gate
|
|
12
|
+
// that was killed starts reading as live again the moment the operating system reissues its pid. For
|
|
13
|
+
// the in-flight gate marker that means `josh review:brief` printing "a gate is running on this tree"
|
|
14
|
+
// about a gate that no longer exists — a verification saying something that is not true, which is the
|
|
15
|
+
// one thing these records exist to prevent.
|
|
16
|
+
//
|
|
17
|
+
// **The identity is the pair (pid, start time).** A reissued pid necessarily started *after* the
|
|
18
|
+
// record was written, so its start time differs and the pair separates the two processes outright.
|
|
19
|
+
// This is the fix rather than a narrowing of the window: a lifetime on the record, the other
|
|
20
|
+
// candidate, only shortens how long a phantom can be believed.
|
|
21
|
+
//
|
|
22
|
+
// **The start time is an opaque token, never a date.** Both sides use the same ordered probes and
|
|
23
|
+
// compare the resulting token for equality; readers never parse it. Linux reads `/proc` directly,
|
|
24
|
+
// other platforms try `ps`, and a sandbox that permits neither uses a process-owned Unix socket.
|
|
25
|
+
// That socket accepts connections only while its writer lives, so a reused pid cannot revive it.
|
|
26
|
+
//
|
|
27
|
+
// **The token is only opaque if it is also stable, which it is not by default.** `ps` renders the
|
|
28
|
+
// start time through the caller's own time zone and locale, so the *same* process prints
|
|
29
|
+
// `Tue Sep 8 09:15:33 2026` in one shell and `Tue Sep 8 02:15:33 2026` in another that exported
|
|
30
|
+
// `TZ=UTC` — and a writer and reader disagreeing about `TZ` would read a live process as a stranger.
|
|
31
|
+
// That failure is worse than the one this module fixes, because it fires on a gate that really is
|
|
32
|
+
// running: `unit-worker-share` would sweep the marker of a live run and put every suite back on the
|
|
33
|
+
// whole machine, and `bench-guard` would clear the caches out from under a running gate. The probe
|
|
34
|
+
// therefore pins `TZ` and `LC_ALL` rather than inheriting them.
|
|
35
|
+
|
|
36
|
+
// The `ps` fallback uses an absolute path rather than a name resolved through `PATH`, which removes
|
|
37
|
+
// the lookup from the caller's environment and lets the probe run with no inherited environment.
|
|
38
|
+
// Where neither `/proc` nor `ps` is available, the probe answers "cannot tell", a case every caller
|
|
39
|
+
// already handles.
|
|
40
|
+
const PS_COMMAND = '/bin/ps'
|
|
41
|
+
const FORMAT_FLAG = '-o'
|
|
42
|
+
// `lstart` is the process's start time, printed as `Tue Sep 8 08:39:04 2026`. Both implementations
|
|
43
|
+
// these records are read on print it that way — BSD `ps` on macOS, procps-ng on Linux — which is why
|
|
44
|
+
// one command covers both without a platform branch.
|
|
45
|
+
const START_TIME_FORMAT = 'lstart='
|
|
46
|
+
const PID_FLAG = '-p'
|
|
47
|
+
// The two variables that decide how `ps` renders that field, and **the whole environment the probe
|
|
48
|
+
// runs in** — nothing is inherited, so the token depends on the process being asked about and on
|
|
49
|
+
// nothing whatever about who is asking.
|
|
50
|
+
const PROBE_ENVIRONMENT = { TZ: 'UTC', LC_ALL: 'C' }
|
|
51
|
+
// A probe that hung would hang `josh review:brief`, `josh bench` and the unit suite's worker share
|
|
52
|
+
// alike. Failing is harmless here: it answers "cannot tell", which every caller already handles.
|
|
53
|
+
const PROBE_TIMEOUT_MS = 2000
|
|
54
|
+
// `process.kill(pid, 0)` runs every permission check and delivers nothing, so it is the standard
|
|
55
|
+
// liveness probe: it throws `ESRCH` where the process is gone.
|
|
56
|
+
const LIVENESS_SIGNAL = 0
|
|
57
|
+
// Below this a pid is not a process: `0` addresses the caller's own process group and anything
|
|
58
|
+
// negative addresses another group, so a truncated or hand-edited record carrying either would
|
|
59
|
+
// otherwise answer "alive" forever.
|
|
60
|
+
const LOWEST_REAL_PID = 1
|
|
61
|
+
const PROC_START_FIELD_INDEX = 19
|
|
62
|
+
const BEACON_SCHEME = 'socket:'
|
|
63
|
+
const BEACON_SUFFIX = '.sock'
|
|
64
|
+
const BEACON_PREFIX = `josh-process-identity-${String(process.getuid?.() ?? '')}-`
|
|
65
|
+
const BEACON_ID_LENGTH = 36
|
|
66
|
+
const BEACON_ID_PATTERN = /^[\da-f-]+$/u
|
|
67
|
+
const WINDOWS_PLATFORM = 'win32'
|
|
68
|
+
const SOCKET_PROBE_SOURCE =
|
|
69
|
+
"const net=require('node:net');const socket=net.createConnection(process.argv[1]);socket.once('connect',()=>process.exit(0));socket.once('error',()=>process.exit(1));socket.setTimeout(1000,()=>process.exit(1));"
|
|
70
|
+
const beacons = new Map<string, Server>()
|
|
71
|
+
const exit_cleanup = { is_registered: false }
|
|
72
|
+
|
|
73
|
+
interface StartProbes {
|
|
74
|
+
read_proc_start: (pid: number) => string | undefined
|
|
75
|
+
read_ps_start: (pid: number) => string | undefined
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
function is_live_pid(pid: number): boolean {
|
|
79
|
+
if (pid < LOWEST_REAL_PID) return false
|
|
80
|
+
|
|
81
|
+
try {
|
|
82
|
+
process.kill(pid, LIVENESS_SIGNAL)
|
|
83
|
+
|
|
84
|
+
return true
|
|
85
|
+
} catch {
|
|
86
|
+
return false
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
// Linux exposes field 22 as the process start time in clock ticks since boot. The command name in
|
|
91
|
+
// field 2 may itself contain spaces or `)`, so parsing starts after its final closing parenthesis;
|
|
92
|
+
// `state` is then index 0 and `starttime` index 19. A direct file read avoids process enumeration and
|
|
93
|
+
// remains available to a workspace sandbox that refuses `ps`.
|
|
94
|
+
function read_proc_start(pid: number): string | undefined {
|
|
95
|
+
try {
|
|
96
|
+
const stat = readFileSync(`/proc/${String(pid)}/stat`, 'utf8')
|
|
97
|
+
const fields = stat
|
|
98
|
+
.slice(stat.lastIndexOf(')') + 1)
|
|
99
|
+
.trim()
|
|
100
|
+
.split(/\s+/u)
|
|
101
|
+
const start = fields[PROC_START_FIELD_INDEX]
|
|
102
|
+
|
|
103
|
+
return start === undefined ? undefined : `proc:${start}`
|
|
104
|
+
} catch {
|
|
105
|
+
return undefined
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
function read_ps_start(pid: number): string | undefined {
|
|
110
|
+
try {
|
|
111
|
+
const printed = execFileSync(
|
|
112
|
+
PS_COMMAND,
|
|
113
|
+
[FORMAT_FLAG, START_TIME_FORMAT, PID_FLAG, String(pid)],
|
|
114
|
+
{
|
|
115
|
+
encoding: 'utf8',
|
|
116
|
+
timeout: PROBE_TIMEOUT_MS,
|
|
117
|
+
stdio: ['ignore', 'pipe', 'ignore'],
|
|
118
|
+
env: PROBE_ENVIRONMENT,
|
|
119
|
+
},
|
|
120
|
+
).trim()
|
|
121
|
+
|
|
122
|
+
return printed.length > 0 ? printed : undefined
|
|
123
|
+
} catch {
|
|
124
|
+
return undefined
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
const START_PROBES: StartProbes = { read_proc_start, read_ps_start }
|
|
129
|
+
|
|
130
|
+
function safe_probe(probe: (pid: number) => string | undefined, pid: number): string | undefined {
|
|
131
|
+
try {
|
|
132
|
+
return probe(pid)
|
|
133
|
+
} catch {
|
|
134
|
+
return undefined
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
function read_start(pid: number, probes: StartProbes = START_PROBES): string | undefined {
|
|
139
|
+
return safe_probe(probes.read_proc_start, pid) ?? safe_probe(probes.read_ps_start, pid)
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
function close_beacon(token: string): void {
|
|
143
|
+
const server = beacons.get(token)
|
|
144
|
+
if (server === undefined) return
|
|
145
|
+
|
|
146
|
+
beacons.delete(token)
|
|
147
|
+
server.close()
|
|
148
|
+
rmSync(token.slice(BEACON_SCHEME.length), { force: true })
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
function close_beacons(): void {
|
|
152
|
+
for (const token of beacons.keys()) close_beacon(token)
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
function ensure_exit_cleanup(): void {
|
|
156
|
+
if (exit_cleanup.is_registered) return
|
|
157
|
+
|
|
158
|
+
exit_cleanup.is_registered = true
|
|
159
|
+
process.once('exit', close_beacons)
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
function ignore_beacon_error(): void {
|
|
163
|
+
/* an unavailable beacon makes the identity safely unverifiable */
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
function bind_beacon(target: string): Server {
|
|
167
|
+
return createServer().on('error', ignore_beacon_error).listen(target)
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
function try_bind_beacon(bind: (target: string) => Server, target: string): Server | undefined {
|
|
171
|
+
try {
|
|
172
|
+
const server = bind(target)
|
|
173
|
+
|
|
174
|
+
return server.listening ? server : undefined
|
|
175
|
+
} catch {
|
|
176
|
+
return undefined
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
function open_beacon(bind: (target: string) => Server = bind_beacon): string | undefined {
|
|
181
|
+
const target = path.join(PLATFORM_TEMP_ROOT, `${BEACON_PREFIX}${randomUUID()}${BEACON_SUFFIX}`)
|
|
182
|
+
const token = `${BEACON_SCHEME}${target}`
|
|
183
|
+
const server = try_bind_beacon(bind, target)
|
|
184
|
+
if (server === undefined) return undefined
|
|
185
|
+
|
|
186
|
+
server.unref()
|
|
187
|
+
beacons.set(token, server)
|
|
188
|
+
ensure_exit_cleanup()
|
|
189
|
+
|
|
190
|
+
return token
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
// A Unix-domain listener is the macOS sandbox fallback rather than a weaker pid-only answer. The
|
|
194
|
+
// random path is the generation token and the kernel-held listener is its lifetime: SIGKILL closes
|
|
195
|
+
// it even when no cleanup handler runs, while a stale filesystem entry accepts no connection. A
|
|
196
|
+
// process that later receives the same pid therefore cannot make the old pair live again.
|
|
197
|
+
function resolve_own_start(
|
|
198
|
+
probes: StartProbes = START_PROBES,
|
|
199
|
+
platform: NodeJS.Platform = process.platform,
|
|
200
|
+
): string | undefined {
|
|
201
|
+
const start = read_start(process.pid, probes)
|
|
202
|
+
if (start !== undefined || platform === WINDOWS_PLATFORM) return start
|
|
203
|
+
|
|
204
|
+
return open_beacon()
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
function is_valid_beacon_name(name: string): boolean {
|
|
208
|
+
if (!name.startsWith(BEACON_PREFIX) || !name.endsWith(BEACON_SUFFIX)) return false
|
|
209
|
+
|
|
210
|
+
const id = name.slice(BEACON_PREFIX.length, -BEACON_SUFFIX.length)
|
|
211
|
+
|
|
212
|
+
return id.length === BEACON_ID_LENGTH && BEACON_ID_PATTERN.test(id)
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
function beacon_target(token: string): string | undefined {
|
|
216
|
+
if (!token.startsWith(BEACON_SCHEME)) return undefined
|
|
217
|
+
|
|
218
|
+
const target = token.slice(BEACON_SCHEME.length)
|
|
219
|
+
if (path.dirname(target) !== PLATFORM_TEMP_ROOT) return undefined
|
|
220
|
+
|
|
221
|
+
return is_valid_beacon_name(path.basename(target)) ? target : undefined
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
function is_live_beacon(target: string): boolean {
|
|
225
|
+
return (
|
|
226
|
+
spawnSync(process.execPath, ['-e', SOCKET_PROBE_SOURCE, target], {
|
|
227
|
+
stdio: 'ignore',
|
|
228
|
+
timeout: PROBE_TIMEOUT_MS,
|
|
229
|
+
}).status === 0
|
|
230
|
+
)
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
function has_matching_start(
|
|
234
|
+
pid: number,
|
|
235
|
+
recorded_start: string,
|
|
236
|
+
read: (pid: number) => string | undefined,
|
|
237
|
+
): boolean | undefined {
|
|
238
|
+
const observed = read(pid)
|
|
239
|
+
|
|
240
|
+
return observed === undefined ? undefined : observed === recorded_start
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
function matches_recorded_start(
|
|
244
|
+
pid: number,
|
|
245
|
+
recorded_start: string,
|
|
246
|
+
read: (pid: number) => string | undefined,
|
|
247
|
+
): boolean | undefined {
|
|
248
|
+
const target = beacon_target(recorded_start)
|
|
249
|
+
|
|
250
|
+
return target === undefined
|
|
251
|
+
? has_matching_start(pid, recorded_start, read)
|
|
252
|
+
: is_live_beacon(target)
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
// Read once and kept: a process's own start time cannot change, and the probe costs a subprocess that
|
|
256
|
+
// every record write would otherwise pay again. A `Map` rather than a nullable variable because
|
|
257
|
+
// `has` distinguishes "not asked yet" from "asked, and the platform could not answer" — the second is
|
|
258
|
+
// a real result, and re-probing on every write would pay the subprocess exactly where it cannot help.
|
|
259
|
+
const own_probe = new Map<number, string | undefined>()
|
|
260
|
+
|
|
261
|
+
function own_start(): string | undefined {
|
|
262
|
+
if (!own_probe.has(process.pid)) own_probe.set(process.pid, resolve_own_start())
|
|
263
|
+
|
|
264
|
+
return own_probe.get(process.pid)
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
// The fields a record carries to name its writer, in one place so no caller assembles them itself.
|
|
268
|
+
//
|
|
269
|
+
// **Spread rather than assigned, because `exactOptionalPropertyTypes` makes an explicit `undefined` a
|
|
270
|
+
// different thing from an absent key** — and a platform whose start time cannot be read must leave
|
|
271
|
+
// the key absent, which is exactly what every reader below reads as "cannot tell".
|
|
272
|
+
function own_fields(): { pid: number; process_start?: string } {
|
|
273
|
+
const start = own_start()
|
|
274
|
+
|
|
275
|
+
return { pid: process.pid, ...(start !== undefined && { process_start: start }) }
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
// **Three answers, because the third one is real and the two callers resolve it in opposite
|
|
279
|
+
// directions.** `true` is "the writer is still running", `false` is "it is not", and `undefined` is
|
|
280
|
+
// "this platform cannot tell" — a pid that is alive paired with a start time nobody could read, on
|
|
281
|
+
// either side of the record.
|
|
282
|
+
//
|
|
283
|
+
// Collapsing `undefined` into either boolean here would be a decision taken in the wrong place. What
|
|
284
|
+
// being wrong costs differs per caller: the in-flight gate marker's readers pay a redundant unit run
|
|
285
|
+
// for a false negative, so they take `=== true`; the unit-suite worker share pays a wider share of
|
|
286
|
+
// the machine for a false negative and an oversubscribed machine for a false positive, so it takes
|
|
287
|
+
// `!== false`. Each one chooses the answer that costs wall time rather than correctness.
|
|
288
|
+
//
|
|
289
|
+
// **A record written before this field existed lands on `undefined` rather than `true`**, the same
|
|
290
|
+
// direction `describes_base` takes for a missing base: a reader that knows less than the writer did
|
|
291
|
+
// must never come out ahead of one that knows the same.
|
|
292
|
+
function is_same_process(
|
|
293
|
+
pid: number | undefined,
|
|
294
|
+
recorded_start: string | undefined,
|
|
295
|
+
read: (pid: number) => string | undefined = read_start,
|
|
296
|
+
): boolean | undefined {
|
|
297
|
+
if (pid === undefined || !is_live_pid(pid)) return false
|
|
298
|
+
if (recorded_start === undefined) return undefined
|
|
299
|
+
|
|
300
|
+
return matches_recorded_start(pid, recorded_start, read)
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
// **Whether a record is the caller's own, which is a different question from whether its writer is
|
|
304
|
+
// alive** (joshuafolkken/kit#1727). `is_same_process` asks about the recorded process; this asks
|
|
305
|
+
// whether that process *is this one*. A supervisor that has been replaced reads its successor's
|
|
306
|
+
// record as perfectly live, so liveness alone can never tell a hand-over from an ordinary pass — and
|
|
307
|
+
// a writer deciding by liveness overwrites whatever it finds.
|
|
308
|
+
//
|
|
309
|
+
// **Compared against `own_fields()`, so the two can never disagree about what identity means.** The
|
|
310
|
+
// pair a record carries is written there and read here, which is why this lives beside it rather
|
|
311
|
+
// than in each record's own module.
|
|
312
|
+
//
|
|
313
|
+
// **A start time neither side can read leaves the pid as the whole comparison, and that is the
|
|
314
|
+
// correct fallback rather than a gap.** Where the platform answers for nobody, both sides are
|
|
315
|
+
// `undefined` and equal; where it answers for one and not the other, they differ and the record is
|
|
316
|
+
// treated as someone else's — the safe direction, since refusing to write a record that is in fact
|
|
317
|
+
// this process's own costs one supervisor pass, and writing over another's costs the run.
|
|
318
|
+
function is_own_process(pid: number | undefined, recorded_start: string | undefined): boolean {
|
|
319
|
+
return pid === process.pid && recorded_start === own_start()
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
const process_identity = {
|
|
323
|
+
close_beacon,
|
|
324
|
+
is_live_pid,
|
|
325
|
+
is_own_process,
|
|
326
|
+
is_same_process,
|
|
327
|
+
open_beacon,
|
|
328
|
+
own_fields,
|
|
329
|
+
own_start,
|
|
330
|
+
read_start,
|
|
331
|
+
resolve_own_start,
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
export { process_identity }
|
|
335
|
+
export type { StartProbes }
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
#!/usr/bin/env tsx
|
|
2
|
+
import { fileURLToPath } from 'node:url'
|
|
3
|
+
import { josh_environment_file } from './josh-environment-file'
|
|
4
|
+
import { session_language, type Resolution } from './session-language'
|
|
5
|
+
|
|
6
|
+
// `josh session:lang` — say, on stdout, which language this session writes in, so a
|
|
7
|
+
// `SessionStart` / `UserPromptSubmit` hook injects the resolved value into context every turn
|
|
8
|
+
// (joshuafolkken/kit#1903). Before this, `JOSH_SESSION_LANG` lived only in `.env` — a personal,
|
|
9
|
+
// non-committed file the harness never loads into the process environment — so the value was
|
|
10
|
+
// invisible unless the agent read the file itself, and a one-word prompt like `diag` had nothing to
|
|
11
|
+
// infer a language from and drifted to the surrounding English.
|
|
12
|
+
//
|
|
13
|
+
// **The `.env` read reuses the one loader** (`josh_environment_file.load_environment_file`) rather
|
|
14
|
+
// than a second parser, and keeps node's precedence: a value already in the process environment wins
|
|
15
|
+
// over the file's. Unset, empty and no-`.env` all resolve to `ja`, because a worktree without a
|
|
16
|
+
// `.env`, a cloud session and a consumer repository are exactly where the drift recurs.
|
|
17
|
+
//
|
|
18
|
+
// The line itself is a script-fixed string, so it stays English by the same rule that pins the
|
|
19
|
+
// Telegram header labels and the `--notify-message` default (`CLAUDE.md` → "Output language").
|
|
20
|
+
|
|
21
|
+
const { DEFAULT_SESSION_LANG, ENV_KEY, resolve_session_lang } = session_language
|
|
22
|
+
const ENGLISH_OPT_IN_LANG = 'en'
|
|
23
|
+
const SCOPE_NOTE =
|
|
24
|
+
'write session dialogue, AskUserQuestion labels/descriptions and artifact prose (Issue bodies, comments, Telegram) in this language. English stays only for Issue/PR titles, code comments/test titles/commit messages, and script-fixed strings.'
|
|
25
|
+
|
|
26
|
+
// The file load happens in `main`, on the command path only; the resolver reads the environment.
|
|
27
|
+
function format_line(resolution: Resolution): string {
|
|
28
|
+
const suffix = resolution.is_default
|
|
29
|
+
? ` (default; set ${ENV_KEY}=${ENGLISH_OPT_IN_LANG} for English)`
|
|
30
|
+
: ''
|
|
31
|
+
|
|
32
|
+
return `Session language (${ENV_KEY}): ${resolution.lang}${suffix} — ${SCOPE_NOTE}`
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
function main(): void {
|
|
36
|
+
josh_environment_file.load_environment_file()
|
|
37
|
+
console.info(format_line(resolve_session_lang()))
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
const session_language_cli = {
|
|
41
|
+
DEFAULT_SESSION_LANG,
|
|
42
|
+
ENV_KEY,
|
|
43
|
+
format_line,
|
|
44
|
+
main,
|
|
45
|
+
resolve_session_lang,
|
|
46
|
+
SCOPE_NOTE,
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
if (process.argv[1] === fileURLToPath(import.meta.url)) main()
|
|
50
|
+
|
|
51
|
+
export { session_language_cli }
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
// The session language, resolved from the process environment alone — the one resolver both
|
|
2
|
+
// `josh session:lang` and the Stop hook read. It lives apart from `session-language-cli.ts` because
|
|
3
|
+
// that file is its own hook bundle entry: an entry imported by another entry is moved into a shared
|
|
4
|
+
// esbuild chunk, so its `import.meta.url` stops matching `argv[1]` and `dist/hooks/session-lang.js`
|
|
5
|
+
// prints nothing (`build-hooks.ts` → "`splitting: true` is load-bearing").
|
|
6
|
+
//
|
|
7
|
+
// Unset and empty resolve to `ja`, because a worktree without a `.env`, a cloud session and a
|
|
8
|
+
// consumer repository are exactly where the drift recurs (joshuafolkken/kit#1903).
|
|
9
|
+
|
|
10
|
+
const ENV_KEY = 'JOSH_SESSION_LANG'
|
|
11
|
+
const DEFAULT_SESSION_LANG = 'ja'
|
|
12
|
+
|
|
13
|
+
interface Resolution {
|
|
14
|
+
lang: string
|
|
15
|
+
is_default: boolean
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
// Reads the process environment alone, so a unit test sets `process.env[ENV_KEY]` and never touches a
|
|
19
|
+
// real `.env`. Loading the file is the caller's job.
|
|
20
|
+
function resolve_session_lang(): Resolution {
|
|
21
|
+
const value = process.env[ENV_KEY]?.trim()
|
|
22
|
+
if (value) return { lang: value, is_default: false }
|
|
23
|
+
|
|
24
|
+
return { lang: DEFAULT_SESSION_LANG, is_default: true }
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
const session_language = { DEFAULT_SESSION_LANG, ENV_KEY, resolve_session_lang }
|
|
28
|
+
|
|
29
|
+
export type { Resolution }
|
|
30
|
+
export { session_language }
|
|
@@ -0,0 +1,194 @@
|
|
|
1
|
+
import { createHash, randomUUID } from 'node:crypto'
|
|
2
|
+
import { lstatSync, readFileSync, renameSync, rmSync, writeFileSync } from 'node:fs'
|
|
3
|
+
import path from 'node:path'
|
|
4
|
+
import { PACKAGE_DIR } from '#scripts/init/init-paths'
|
|
5
|
+
import { PLATFORM_TEMP_ROOT } from './platform-temporary'
|
|
6
|
+
|
|
7
|
+
// A small record one josh command writes and another reads, kept per checkout in the temp directory.
|
|
8
|
+
//
|
|
9
|
+
// Extracted from `scripts/eval/eval-stamp.ts` when `josh latest:scope` needed the same handoff
|
|
10
|
+
// (joshuafolkken/kit#1215). The mechanics below are not boilerplate — the symlink defense on the
|
|
11
|
+
// write, the ownership check on the read, and the deterministic per-checkout path are each load
|
|
12
|
+
// bearing — so a second copy of them is the clone `CLAUDE.md` prohibits. What differs between the
|
|
13
|
+
// two records is their *payload* and the question asked of it, and that is all either module keeps.
|
|
14
|
+
|
|
15
|
+
const HASH_ALGORITHM = 'sha256'
|
|
16
|
+
const STAMP_SUFFIX = '.json'
|
|
17
|
+
// Enough of the package path's digest to keep one checkout's record apart from another's in a shared
|
|
18
|
+
// temp directory, and short enough to read in a printed path.
|
|
19
|
+
const STAMP_KEY_LENGTH = 16
|
|
20
|
+
// The path is deterministic — both commands have to reach it without being told where it is — and it
|
|
21
|
+
// sits in a directory every account on the host may write to. A mode alone does not make that safe:
|
|
22
|
+
// it applies on creation, and a plain write follows a symlink someone else put there.
|
|
23
|
+
const STAMP_FILE_MODE = 0o600
|
|
24
|
+
// Create-exclusively. Refusing an existing path is what keeps a write from being redirected into a
|
|
25
|
+
// file the running user owns; the unlink below is what keeps the normal rewrite working.
|
|
26
|
+
const STAMP_WRITE_FLAG = 'wx'
|
|
27
|
+
// The same flag under the name that says what `create_stamp` uses it for: there the exclusivity is
|
|
28
|
+
// the contract rather than a defense, because no unlink precedes it.
|
|
29
|
+
const STAMP_CREATE_FLAG = STAMP_WRITE_FLAG
|
|
30
|
+
const EXISTS_ERROR_CODE = 'EEXIST'
|
|
31
|
+
const ACCOUNT_KEY_SEPARATOR = ':'
|
|
32
|
+
|
|
33
|
+
// Bytes rather than a decoded string. UTF-8 decoding is lossy — every invalid sequence collapses to
|
|
34
|
+
// the same replacement character — so hashing the decoded form would let two different files agree,
|
|
35
|
+
// and the checks built on this exist precisely to notice that they do not.
|
|
36
|
+
function digest(content: Buffer | string): string {
|
|
37
|
+
return createHash(HASH_ALGORITHM).update(content).digest('hex')
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
// **The account is part of the key, because `/tmp` is shared between accounts where `TMPDIR` was not.**
|
|
41
|
+
// A root such as a globally installed `PACKAGE_DIR` is the same path for every account on the host, so
|
|
42
|
+
// without this two accounts would name one file — and the second one's unlink of a file the first owns
|
|
43
|
+
// fails on the sticky directory, ending its command. Folding the account in keeps accidental
|
|
44
|
+
// collisions apart; a hostile local account can still pre-create a predictable name, which fails the
|
|
45
|
+
// write rather than redirecting it (joshuafolkken/kit#1909). Windows has no `getuid` and keeps the
|
|
46
|
+
// per-user `os.tmpdir()`, so it contributes an empty account.
|
|
47
|
+
function account_key(): string {
|
|
48
|
+
return String(process.getuid?.() ?? '')
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
// The temp directory rather than the repository: this is a handoff between two commands of one
|
|
52
|
+
// loop, and a file in the tree would have to be gitignored in kit and in every consumer `josh sync`
|
|
53
|
+
// reaches — a distributed ignore entry bought for something nobody is meant to keep.
|
|
54
|
+
//
|
|
55
|
+
// **`PLATFORM_TEMP_ROOT` rather than `os.tmpdir()`, because the two commands need not share a
|
|
56
|
+
// `TMPDIR`.** A record this module keeps is read by a *different* process than wrote it — the second
|
|
57
|
+
// half of a handoff — and `os.tmpdir()` honors each process's own `TMPDIR`, so a session a
|
|
58
|
+
// `run:wake` or a `lane:dispatch` launched resolves a different directory than its launcher and the
|
|
59
|
+
// handoff is lost (joshuafolkken/kit#1909). The keyed digest below already keeps one checkout apart
|
|
60
|
+
// from another; the root is what has to stop moving per process, which `platform-temporary.ts` pins.
|
|
61
|
+
//
|
|
62
|
+
// **`root` is what the record is keyed to, and the right answer differs per caller.** `PACKAGE_DIR`
|
|
63
|
+
// is the default because `josh eval` measures the kit package's own files. A record about the
|
|
64
|
+
// *project* must key on `PROJECT_ROOT` instead: a globally installed `josh` has one `PACKAGE_DIR`
|
|
65
|
+
// for every project on the machine, so keying on it would let a run in one project answer for
|
|
66
|
+
// another (joshuafolkken/kit#1215).
|
|
67
|
+
//
|
|
68
|
+
// **`suffix` is what the file is, not decoration.** Every record here was JSON until `josh gate`
|
|
69
|
+
// needed to keep another tool's output verbatim (joshuafolkken/kit#1227), and a log named `.json`
|
|
70
|
+
// tells every reader — a person, an editor, a `less` — that it is something it is not.
|
|
71
|
+
function stamp_path(
|
|
72
|
+
prefix: string,
|
|
73
|
+
root: string = PACKAGE_DIR,
|
|
74
|
+
suffix: string = STAMP_SUFFIX,
|
|
75
|
+
): string {
|
|
76
|
+
const key = digest(`${account_key()}${ACCOUNT_KEY_SEPARATOR}${root}`).slice(0, STAMP_KEY_LENGTH)
|
|
77
|
+
|
|
78
|
+
return path.join(PLATFORM_TEMP_ROOT, `${prefix}${key}${suffix}`)
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
// **The write unlinks first, then creates exclusively.** `rmSync` removes a symlink rather than
|
|
82
|
+
// following it, and `wx` refuses an existing path outright, so someone who re-creates the path in
|
|
83
|
+
// the window between the two can make this write *fail* — never make it land somewhere else. A
|
|
84
|
+
// failed write leaves no record, and no record is the safe answer for every reader here. On a sticky
|
|
85
|
+
// temp directory a file another account owns cannot be unlinked at all, so that case fails here too
|
|
86
|
+
// rather than being silently trusted later.
|
|
87
|
+
function write_exclusively(target: string, content: string): string {
|
|
88
|
+
rmSync(target, { force: true })
|
|
89
|
+
writeFileSync(target, content, { flag: STAMP_WRITE_FLAG, mode: STAMP_FILE_MODE })
|
|
90
|
+
|
|
91
|
+
return target
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
function write_stamp(target: string, payload: unknown): string {
|
|
95
|
+
return write_exclusively(target, JSON.stringify(payload))
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
// Mutable coordination state cannot disappear between an unlink and its replacement: a reader that
|
|
99
|
+
// mistakes that gap for "no child" can start the same generation twice. The temporary file lives
|
|
100
|
+
// beside the target, uses the same exclusive/symlink-safe create, and rename publishes it atomically.
|
|
101
|
+
function replace_stamp(target: string, payload: unknown): string {
|
|
102
|
+
const temporary = `${target}.${String(process.pid)}.${randomUUID()}`
|
|
103
|
+
|
|
104
|
+
try {
|
|
105
|
+
writeFileSync(temporary, JSON.stringify(payload), {
|
|
106
|
+
flag: STAMP_CREATE_FLAG,
|
|
107
|
+
mode: STAMP_FILE_MODE,
|
|
108
|
+
})
|
|
109
|
+
renameSync(temporary, target)
|
|
110
|
+
|
|
111
|
+
return target
|
|
112
|
+
} finally {
|
|
113
|
+
rmSync(temporary, { force: true })
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
// The same write for a payload that is already text. `josh gate`'s log is another tool's output read
|
|
118
|
+
// by a person or by an agent's `tail` (joshuafolkken/kit#1227), and JSON-encoding it would put `\n`
|
|
119
|
+
// escapes between the reader and the thing they came to read. **It shares the body above rather than
|
|
120
|
+
// repeating it**: the unlink-then-create-exclusively pair is the symlink defense, and a second copy
|
|
121
|
+
// of it is the clone `CLAUDE.md` prohibits.
|
|
122
|
+
function write_text_stamp(target: string, text: string): string {
|
|
123
|
+
return write_exclusively(target, text)
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
// `write_stamp` for a record whose *absence* is what the caller checked, and which two processes may
|
|
127
|
+
// therefore try to create at once (joshuafolkken/kit#1091). It does **not** unlink first, so `wx` is
|
|
128
|
+
// doing the job it exists for: the second writer loses and is told so, rather than both being told
|
|
129
|
+
// they won. `false` is that loss and never an error, because "someone else got here first" is an
|
|
130
|
+
// answer; anything else still throws, since a record that could not be written for another reason
|
|
131
|
+
// must not read as a record somebody else holds.
|
|
132
|
+
function create_stamp(target: string, payload: unknown): boolean {
|
|
133
|
+
try {
|
|
134
|
+
writeFileSync(target, JSON.stringify(payload), {
|
|
135
|
+
flag: STAMP_CREATE_FLAG,
|
|
136
|
+
mode: STAMP_FILE_MODE,
|
|
137
|
+
})
|
|
138
|
+
|
|
139
|
+
return true
|
|
140
|
+
} catch (error) {
|
|
141
|
+
if ((error as NodeJS.ErrnoException).code === EXISTS_ERROR_CODE) return false
|
|
142
|
+
|
|
143
|
+
throw error
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
// The counterpart to `write_stamp`, for a record that means something only while it exists — the
|
|
148
|
+
// in-flight gate marker `josh gate` clears when its checks finish (joshuafolkken/kit#1242). `force`
|
|
149
|
+
// makes an absent path a success, because "there is no record" is the state the caller wanted; and
|
|
150
|
+
// `rmSync` removes a symlink rather than following it, the same reason the write unlinks first.
|
|
151
|
+
function remove_stamp(target: string): void {
|
|
152
|
+
rmSync(target, { force: true })
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
// The read side of the write's problem. A record is trusted to say something is still current, so a
|
|
156
|
+
// planted one suppresses the work the check exists to force. `lstatSync` reports the link rather than
|
|
157
|
+
// what it points at, and it throws where the path is simply absent — both of which are "no record".
|
|
158
|
+
// `getuid` is missing on Windows, where a shared temp directory is not the same hazard; the check
|
|
159
|
+
// reduces to "is it a regular file" there.
|
|
160
|
+
function is_own_regular_file(source: string): boolean {
|
|
161
|
+
const stats = lstatSync(source)
|
|
162
|
+
|
|
163
|
+
if (!stats.isFile()) return false
|
|
164
|
+
|
|
165
|
+
const uid = process.getuid?.()
|
|
166
|
+
|
|
167
|
+
return uid === undefined || stats.uid === uid
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
// `undefined` rather than a throw or an empty string: "there is no record" and "the record says
|
|
171
|
+
// nothing has to be done" are two different answers, and only the first of them is a reason to act.
|
|
172
|
+
function read_stamp_text(source: string): string | undefined {
|
|
173
|
+
try {
|
|
174
|
+
if (!is_own_regular_file(source)) return undefined
|
|
175
|
+
|
|
176
|
+
return readFileSync(source, 'utf8')
|
|
177
|
+
} catch {
|
|
178
|
+
return undefined
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
const stamp_file = {
|
|
183
|
+
create_stamp,
|
|
184
|
+
digest,
|
|
185
|
+
is_own_regular_file,
|
|
186
|
+
read_stamp_text,
|
|
187
|
+
replace_stamp,
|
|
188
|
+
remove_stamp,
|
|
189
|
+
stamp_path,
|
|
190
|
+
write_stamp,
|
|
191
|
+
write_text_stamp,
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
export { stamp_file }
|