@joshuafolkken/kit 1.1010.0 → 1.1050.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (350) hide show
  1. package/.claude/settings.json +1 -5
  2. package/.claude/skills/dependency-update/SKILL.md +6 -3
  3. package/.claude/skills/epic-commands/SKILL.md +46 -17
  4. package/.claude/skills/verify-ui/SKILL.md +1 -1
  5. package/.claude/skills/workflow-commands/SKILL.md +10 -25
  6. package/.claude/skills/workflow-commands/background-commands.md +1 -1
  7. package/.claude/skills/workflow-commands/backlogrun-child.md +6 -4
  8. package/.claude/skills/workflow-commands/backlogrun-lanes.md +1 -1
  9. package/.claude/skills/workflow-commands/backlogrun-park.md +6 -0
  10. package/.claude/skills/workflow-commands/backlogrun-progress.md +30 -36
  11. package/.claude/skills/workflow-commands/backlogrun-steps.md +16 -16
  12. package/.claude/skills/workflow-commands/backlogrun.md +3 -12
  13. package/.claude/skills/workflow-commands/chain-rule.md +2 -2
  14. package/.claude/skills/workflow-commands/followup-reference.md +2 -2
  15. package/.claude/skills/workflow-commands/fullrun-steps.md +4 -3
  16. package/.claude/skills/workflow-commands/fullrun.md +1 -1
  17. package/.claude/skills/workflow-commands/halfrun.md +2 -1
  18. package/.claude/skills/workflow-commands/into-target.md +1 -1
  19. package/.claude/skills/workflow-commands/issue-comments.md +1 -1
  20. package/.claude/skills/workflow-commands/observation-filing.md +80 -126
  21. package/.claude/skills/workflow-commands/pre-gate-cut.md +33 -36
  22. package/.claude/skills/workflow-commands/prerequisite.md +1 -1
  23. package/.claude/skills/workflow-commands/working-tree-hold.md +2 -2
  24. package/.codex/hooks.json +1 -5
  25. package/.github/workflows/auto-tag.yml +21 -0
  26. package/.github/workflows/publish.yml +9 -10
  27. package/.github/workflows/sonar-qube.yml +1 -1
  28. package/AGENTS.md +4 -25
  29. package/CLAUDE.md +24 -38
  30. package/GEMINI.md +4 -24
  31. package/README.md +22 -56
  32. package/dist/CLAUDE.md +24 -38
  33. package/dist/hooks/batch-guard.js +4 -4
  34. package/dist/hooks/{chunk-6PSTVOLH.js → chunk-5BK3K7V4.js} +114 -90
  35. package/dist/hooks/{chunk-PAXMLGSS.js → chunk-7VPDUBRN.js} +93 -175
  36. package/dist/hooks/{chunk-KPZN42RT.js → chunk-DMQN45FN.js} +3 -3
  37. package/dist/hooks/{chunk-WMIR5SNX.js → chunk-EJDX2TOB.js} +13 -3
  38. package/dist/hooks/{chunk-A2H5I2VD.js → chunk-GQ7KP45M.js} +2 -2
  39. package/dist/hooks/{chunk-4GDR2YTV.js → chunk-HJIISEAC.js} +4 -4
  40. package/dist/hooks/{chunk-AWTFKKFW.js → chunk-NAS4C7GP.js} +6 -33
  41. package/dist/hooks/{chunk-56LGMJJV.js → chunk-NJQOBE6L.js} +2 -2
  42. package/dist/hooks/{chunk-EZLHXKJ6.js → chunk-RLDX5SOC.js} +1222 -941
  43. package/dist/hooks/{chunk-GBJUERYK.js → chunk-WQ4OSBSS.js} +10 -3
  44. package/dist/hooks/codex-hook-adapter.js +10 -10
  45. package/dist/hooks/duplicate-read-guard.js +5 -5
  46. package/dist/hooks/format-edited.js +15 -34
  47. package/dist/hooks/investigation-guard.js +5 -5
  48. package/dist/hooks/pretool-guard.js +23 -16
  49. package/dist/hooks/stop-guard.js +21 -200
  50. package/dist/josh.js +42 -125
  51. package/dist/version/index.js +466 -42
  52. package/eslint/base.js +2 -11
  53. package/package.json +36 -10
  54. package/prompts/coding-standards.md +15 -290
  55. package/prompts/collaboration-workflow/cross-repo-epic.md +2 -51
  56. package/prompts/collaboration-workflow/epic-audit.md +2 -53
  57. package/prompts/collaboration-workflow/epic-bundle.md +2 -106
  58. package/prompts/collaboration-workflow/file-edits.md +6 -53
  59. package/prompts/collaboration-workflow/issue-template.md +4 -4
  60. package/prompts/collaboration-workflow/operating-rules.md +18 -75
  61. package/prompts/collaboration-workflow/principles.md +74 -0
  62. package/prompts/collaboration-workflow/residency.md +55 -15
  63. package/prompts/collaboration-workflow/rule-delivery.md +5 -46
  64. package/prompts/collaboration-workflow/shell-body.md +7 -9
  65. package/prompts/collaboration-workflow/turn-batching.md +21 -37
  66. package/prompts/collaboration-workflow/upstream-interrupt.md +1 -1
  67. package/prompts/collaboration-workflow/wip-cap.md +15 -18
  68. package/prompts/collaboration-workflow.md +22 -28
  69. package/prompts/sonar-hotspot-handling.md +1 -1
  70. package/scripts/adopt/adopt-logic.ts +2 -2
  71. package/scripts/adopt/adopt-toolkits.ts +3 -1
  72. package/scripts/adopt/adopt.ts +2 -2
  73. package/scripts/agent/agent-event.ts +2 -9
  74. package/scripts/agent/agent-exit-record.ts +2 -9
  75. package/scripts/auto-ok/auto-ok-cli.ts +9 -9
  76. package/scripts/backlog/backlog-budget-cli.ts +2 -6
  77. package/scripts/backlog/backlog-drive-cli.ts +20 -10
  78. package/scripts/backlog/backlog-drive.ts +50 -16
  79. package/scripts/backlog/backlog-next.ts +24 -8
  80. package/scripts/backlog/backlog-offer-cli.ts +6 -5
  81. package/scripts/backlog/backlog-plan-cli.ts +5 -1
  82. package/scripts/backlog/backlog-plan.ts +1 -1
  83. package/scripts/backlog/backlog-pool.ts +31 -11
  84. package/scripts/backlog/backlog-rank.ts +106 -0
  85. package/scripts/backlog/backlog-waves.ts +42 -29
  86. package/scripts/build/build-bin.ts +2 -1
  87. package/scripts/build/build-config-merge.ts +2 -1
  88. package/scripts/build/build-hooks.ts +31 -6
  89. package/scripts/build/build-managed-marker.ts +2 -1
  90. package/scripts/build/build-self-sync-guard.ts +2 -1
  91. package/scripts/build/build-version.ts +2 -1
  92. package/scripts/cases/cases-logic.ts +2 -6
  93. package/scripts/ci/pr-classification.ts +13 -11
  94. package/scripts/cost-runtime/cost-transcript.ts +4 -11
  95. package/scripts/delegation/delegation-policy.ts +11 -0
  96. package/scripts/doctor/doctor-consumer.ts +5 -12
  97. package/scripts/doctor/doctor-ports.ts +2 -2
  98. package/scripts/document/document-byte-budget.ts +13 -21
  99. package/scripts/document/document-section.ts +2 -6
  100. package/scripts/document/entry-read-budget.ts +6 -5
  101. package/scripts/{git/git-epic-add-body.ts → epic/epic-add-body.ts} +23 -23
  102. package/scripts/{git/git-epic-add-plan.ts → epic/epic-add-plan.ts} +23 -26
  103. package/scripts/{git/git-epic-add.ts → epic/epic-add.ts} +17 -17
  104. package/scripts/epic/epic-audit-cli.ts +4 -4
  105. package/scripts/epic/epic-audit-orphans.ts +2 -2
  106. package/scripts/epic/epic-audit-rationale.ts +1 -1
  107. package/scripts/epic/epic-audit.ts +1 -1
  108. package/scripts/{git/git-epic-body.ts → epic/epic-body.ts} +6 -6
  109. package/scripts/epic/epic-bundle-cli.ts +1 -1
  110. package/scripts/epic/epic-bundle.ts +2 -2
  111. package/scripts/epic/epic-candidate-confirm.ts +2 -1
  112. package/scripts/{git/git-epic-chains.ts → epic/epic-chains.ts} +4 -4
  113. package/{scripts-ai → scripts/epic}/epic-check.ts +5 -4
  114. package/{scripts-ai → scripts/epic}/epic-cli.ts +4 -4
  115. package/scripts/{git/git-epic-close-comment.ts → epic/epic-close-comment.ts} +9 -9
  116. package/scripts/{git/git-epic-close.ts → epic/epic-close.ts} +23 -19
  117. package/scripts/epic/epic-cross-repo.ts +21 -38
  118. package/scripts/{git/git-epic-decision.ts → epic/epic-decision.ts} +12 -14
  119. package/scripts/epic/epic-fetch.ts +3 -3
  120. package/scripts/epic/epic-graph.ts +3 -3
  121. package/scripts/epic/epic-index.ts +2 -2
  122. package/scripts/epic/epic-issue.ts +1 -1
  123. package/scripts/epic/epic-lane-offer.ts +11 -22
  124. package/scripts/epic/epic-next.ts +3 -3
  125. package/scripts/{git/git-epic-parse.ts → epic/epic-parse.ts} +3 -3
  126. package/scripts/{git/git-epic-promote.ts → epic/epic-promote.ts} +10 -10
  127. package/scripts/epic/epic-rank.ts +37 -0
  128. package/scripts/{git/git-epic-read.ts → epic/epic-read.ts} +9 -9
  129. package/scripts/{git/git-epic-reconcile.ts → epic/epic-reconcile.ts} +18 -20
  130. package/scripts/{git/git-epic-reference.ts → epic/epic-reference.ts} +4 -23
  131. package/scripts/epic/epic-relation-recheck.ts +4 -3
  132. package/scripts/{git/git-epic-relations.ts → epic/epic-relations.ts} +5 -5
  133. package/scripts/{git/git-epic-remove-plan.ts → epic/epic-remove-plan.ts} +17 -19
  134. package/scripts/{git/git-epic-remove.ts → epic/epic-remove.ts} +13 -13
  135. package/scripts/{git/git-epic-run.ts → epic/epic-run.ts} +27 -27
  136. package/scripts/{git/git-epic-sections.ts → epic/epic-sections.ts} +6 -6
  137. package/scripts/{git/git-epic-shape.ts → epic/epic-shape.ts} +7 -7
  138. package/scripts/epic/epic-solo.ts +29 -28
  139. package/scripts/{git/git-epic-validate.ts → epic/epic-validate.ts} +13 -13
  140. package/{scripts-ai → scripts/epic}/epic.ts +17 -16
  141. package/scripts/gh/fix-gh-packages.ts +2 -1
  142. package/scripts/gh/gh-spawn.ts +12 -10
  143. package/scripts/git/constants.ts +1 -1
  144. package/scripts/git/git-branch.ts +10 -1
  145. package/scripts/git/git-command.ts +2 -2
  146. package/scripts/git/git-error.ts +2 -5
  147. package/scripts/git/git-execa-error.ts +6 -1
  148. package/{scripts-ai → scripts/git}/git-followup-finish.ts +7 -7
  149. package/scripts/git/git-followup-flush.ts +74 -48
  150. package/scripts/git/git-followup-issue-close.ts +2 -2
  151. package/scripts/git/git-followup-label.ts +1 -1
  152. package/scripts/git/git-followup-pending.ts +1 -1
  153. package/{scripts-ai → scripts/git}/git-followup-workflow.ts +23 -11
  154. package/scripts/git/git-gh-exec.ts +85 -26
  155. package/scripts/git/git-gh-issue-read.ts +2 -2
  156. package/scripts/git/git-gh-issue-rest.ts +2 -2
  157. package/scripts/git/git-gh-issue-write.ts +6 -6
  158. package/scripts/git/git-gh-pr-read.ts +0 -21
  159. package/scripts/git/git-gh-pr.ts +2 -23
  160. package/scripts/git/git-next-issues.ts +34 -5
  161. package/scripts/git/git-pr-followup-wrapup.ts +2 -6
  162. package/scripts/git/git-preflight.ts +25 -10
  163. package/scripts/git/git-push-transport.ts +12 -69
  164. package/scripts/git/git-spawn.ts +79 -3
  165. package/scripts/git/git-ssh-keepalive.ts +74 -0
  166. package/scripts/git/git-staging.ts +21 -9
  167. package/{scripts-ai → scripts/git}/git-workflow.ts +11 -11
  168. package/scripts/git/github-issue-url.ts +1 -1
  169. package/scripts/git/gone-branch.ts +3 -2
  170. package/scripts/git/issue-labels.ts +13 -1
  171. package/scripts/git/issue-rank.ts +89 -0
  172. package/scripts/git/issue-reference.ts +19 -0
  173. package/scripts/git/main-merge.ts +2 -1
  174. package/scripts/git/main-sync.ts +3 -2
  175. package/scripts/git/schemas.ts +1 -1
  176. package/scripts/git/stash-pop-args.ts +7 -13
  177. package/scripts/git/stash-pop-cli.ts +2 -1
  178. package/{scripts-ai → scripts/git}/telegram-test-logic.ts +2 -2
  179. package/{scripts-ai → scripts/git}/telegram-test.ts +7 -7
  180. package/scripts/hooks/format-edited-cli.ts +15 -0
  181. package/scripts/hooks/format-edited-file.ts +2 -9
  182. package/scripts/hooks/format-rewrite-notice.ts +2 -8
  183. package/scripts/hooks/pretool-guard-cli.ts +21 -0
  184. package/scripts/hooks/pretool-guard.ts +14 -15
  185. package/scripts/init/init-ai-copy.ts +2 -5
  186. package/scripts/init/init.ts +8 -4
  187. package/scripts/init/start-exec.ts +7 -1
  188. package/scripts/init/start-plan.ts +18 -5
  189. package/scripts/init/start-setup-pr.ts +46 -19
  190. package/scripts/init/start-steps.ts +43 -10
  191. package/scripts/init/start.ts +42 -11
  192. package/scripts/issue/issue-cite.ts +19 -1
  193. package/scripts/issue/issue-comment-cli.ts +2 -1
  194. package/scripts/issue/issue-file-cli.ts +2 -1
  195. package/scripts/issue/issue-fold-cli.ts +9 -16
  196. package/scripts/issue/issue-scout-cli.ts +8 -13
  197. package/scripts/issue/measure-rerun-cli.ts +7 -6
  198. package/scripts/josh/josh-command-map.ts +0 -102
  199. package/scripts/josh/josh-commands-ai.ts +2 -2
  200. package/scripts/josh/josh-commands-backlog.ts +1 -1
  201. package/scripts/josh/josh-commands-development.ts +3 -3
  202. package/scripts/josh/josh-commands-project.ts +1 -1
  203. package/scripts/josh/josh-commands-workflow.ts +4 -4
  204. package/scripts/lane/lane-child-marker.ts +16 -2
  205. package/scripts/lane/lane-cli.ts +34 -15
  206. package/scripts/lane/lane-open.ts +3 -2
  207. package/scripts/lane/lane-registry.ts +3 -6
  208. package/scripts/lane/lane-report.ts +10 -4
  209. package/scripts/lib/cli-flags.ts +20 -2
  210. package/scripts/lib/error-message.ts +9 -0
  211. package/scripts/lib/read-file.ts +26 -2
  212. package/scripts/observations/observation-ledger-home.ts +98 -26
  213. package/scripts/observations/observation-ledger-migrate.ts +26 -21
  214. package/scripts/observations/observation-ledger-prepare.ts +10 -20
  215. package/scripts/observations/observation-ledger.ts +58 -25
  216. package/scripts/observations/observations-flush-cli.ts +6 -23
  217. package/scripts/observations/observations-flush-landing.ts +2 -1
  218. package/scripts/observations/observations-flush.ts +29 -20
  219. package/scripts/package/package-scout-cli.ts +8 -16
  220. package/scripts/ports/port-command.ts +2 -1
  221. package/scripts/propagate/propagate-plan.ts +185 -0
  222. package/scripts/propagate/propagate-publish.ts +6 -7
  223. package/scripts/propagate/propagate-run.ts +5 -1
  224. package/scripts/propagate/propagate-steps.ts +18 -2
  225. package/scripts/propagate/propagate-targets.ts +13 -2
  226. package/scripts/propagate/propagate.ts +63 -44
  227. package/scripts/refactor/import-graph.ts +2 -6
  228. package/scripts/registry-migration/migrate-logic.ts +1 -0
  229. package/scripts/registry-migration/migrate.ts +3 -2
  230. package/scripts/registry-migration/user-npmrc.ts +2 -2
  231. package/scripts/release/github-release.ts +1 -1
  232. package/scripts/release/release-cli.ts +2 -1
  233. package/scripts/repo/repo-setting.ts +10 -8
  234. package/scripts/review/review-attest.ts +44 -0
  235. package/scripts/review/review-brief-cli.ts +49 -2
  236. package/scripts/review/review-brief.ts +46 -1
  237. package/scripts/review/review-finding-ledger.ts +3 -3
  238. package/scripts/review/review-findings-cli.ts +4 -11
  239. package/scripts/review/review-record-cli.ts +14 -11
  240. package/scripts/review/review-record.ts +6 -16
  241. package/scripts/review/review-stamps.ts +7 -0
  242. package/scripts/rules/decision-oracle.ts +2 -2
  243. package/scripts/rules/implementation-cut.ts +1 -1
  244. package/scripts/rules/issue-fold.ts +1 -2
  245. package/scripts/rules/lane-carry-conflict.ts +1 -2
  246. package/scripts/rules/lane-park.ts +1 -2
  247. package/scripts/rules/pre-gate-cut.ts +2 -2
  248. package/scripts/rules/rule-body-guard.ts +2 -2
  249. package/scripts/rules/rule-value-cache.ts +99 -0
  250. package/scripts/rules/rule-value-cli.ts +10 -9
  251. package/scripts/rules/rule-value.ts +1 -1
  252. package/scripts/run/run-carry-args.ts +2 -6
  253. package/scripts/run/run-carry-cli.ts +5 -7
  254. package/scripts/run/run-cut-args.ts +7 -13
  255. package/scripts/run/run-ending-cli.ts +4 -7
  256. package/scripts/run/run-event-stream.ts +2 -5
  257. package/scripts/run/run-headless.ts +22 -6
  258. package/scripts/run/run-liveness-cli.ts +4 -7
  259. package/scripts/run/run-parent-cut-hook.ts +144 -0
  260. package/scripts/run/run-progress-args.ts +2 -6
  261. package/scripts/run/run-progress-cli.ts +2 -1
  262. package/scripts/run/run-progress-read.ts +2 -1
  263. package/scripts/run/run-progress.ts +21 -8
  264. package/scripts/run/run-ship-cli.ts +6 -74
  265. package/scripts/run/run-ship-preflight.ts +87 -0
  266. package/scripts/run/run-ship-review-steps.ts +5 -26
  267. package/scripts/run/run-ship-review.ts +9 -0
  268. package/scripts/run/run-ship-scoped.ts +44 -0
  269. package/scripts/run/run-ship-stage.ts +5 -1
  270. package/scripts/run/run-ship-steps.ts +105 -0
  271. package/scripts/run/run-ship.ts +2 -0
  272. package/scripts/run/run-tail-cli.ts +3 -3
  273. package/scripts/run/run-tidy-stashes.ts +7 -14
  274. package/scripts/run/run-wake-cli.ts +4 -8
  275. package/scripts/run/run-wake-describe.ts +19 -1
  276. package/scripts/run/run-wake-driver.ts +15 -2
  277. package/scripts/run/run-wake-session.ts +14 -4
  278. package/scripts/run/run-watcher-hook.ts +1 -1
  279. package/scripts/safe-chain/project-config.ts +3 -6
  280. package/scripts/security/security-audit-provision.ts +6 -7
  281. package/scripts/sonar/sonar-hotspots-cli.ts +2 -1
  282. package/scripts/sync/directory-copy-guard.ts +2 -1
  283. package/scripts/sync/sync-ai-files.ts +3 -3
  284. package/scripts/sync/template-source-logic.ts +3 -4
  285. package/scripts/test/e2e-retry-check.ts +3 -6
  286. package/scripts/time-runtime/time-bundle-call.ts +2 -2
  287. package/scripts/ui/ui-routes-cli.ts +3 -6
  288. package/scripts/version/bump-version.ts +2 -1
  289. package/scripts/version/npm-registry.ts +97 -0
  290. package/scripts/version/version-commands.ts +5 -4
  291. package/scripts/version/version-remote.ts +38 -33
  292. package/scripts/version/version-targets.ts +3 -3
  293. package/.claude/skills/workflow-commands/rule-residency.md +0 -267
  294. package/evals/scenarios/consult-not-execute.json +0 -20
  295. package/evals/scenarios/no-implicit-workflow.json +0 -26
  296. package/evals/scenarios/overrides-both-locations.json +0 -29
  297. package/evals/scenarios/upstream-interrupt.json +0 -23
  298. package/evals/scenarios/workflow-skill-loaded.json +0 -16
  299. package/prompts/agent-lint-protocol.json +0 -79
  300. package/prompts/agent-rules.md +0 -63
  301. package/prompts/collaboration-workflow/consultation-vs-execution.md +0 -8
  302. package/prompts/collaboration-workflow/distributed-docs.md +0 -7
  303. package/prompts/collaboration-workflow/durable-rules.md +0 -24
  304. package/prompts/collaboration-workflow/latest-first.md +0 -14
  305. package/prompts/collaboration-workflow/no-clones.md +0 -11
  306. package/prompts/collaboration-workflow/simplicity-first.md +0 -9
  307. package/prompts/collaboration-workflow/single-source-rules.md +0 -13
  308. package/scripts/build/shim-shell.ts +0 -25
  309. package/scripts/claude/skill-meta.ts +0 -37
  310. package/scripts/document/document-scan.ts +0 -108
  311. package/scripts/document/generate-catalog.ts +0 -69
  312. package/scripts/document/retired-phrases.ts +0 -143
  313. package/scripts/document/review-agent-type.ts +0 -46
  314. package/scripts/document/single-source.ts +0 -73
  315. package/scripts/git/git-fixture-workspace.ts +0 -88
  316. package/scripts/hooks/format-edited-file-fixtures.ts +0 -109
  317. package/scripts/hooks/prompt-hooks.ts +0 -38
  318. package/scripts/init/hook-command-bootstrap.ts +0 -83
  319. package/scripts/issue/issue-logic.ts +0 -50
  320. package/scripts/josh/__snapshots__/josh-command-map.test.ts.snap +0 -1227
  321. package/scripts/josh/josh-script-reader.ts +0 -11
  322. package/scripts/package/production-dependency-graph.ts +0 -68
  323. package/scripts/retrospective/retrospective-cli.ts +0 -97
  324. package/scripts/retrospective/retrospective.ts +0 -127
  325. package/scripts/rules/delivered-rules-harness.ts +0 -72
  326. package/scripts/rules/top-level-scripts-guard.ts +0 -47
  327. package/scripts/run/run-carry-flush.ts +0 -58
  328. package/scripts/run/run-transcript.golden.txt +0 -57
  329. package/scripts/run/run-wake-work.ts +0 -108
  330. package/scripts/sync/dependabot-pin-logic.ts +0 -135
  331. package/scripts/test/classify-isolation.ts +0 -149
  332. package/scripts/test/http-fault-injection.ts +0 -79
  333. package/scripts/test/josh-harness-environment.ts +0 -260
  334. package/scripts/test/josh-harness.ts +0 -96
  335. package/scripts/test/pilot-files.ts +0 -452
  336. package/scripts/test/pilot-measure.ts +0 -78
  337. package/scripts/test/test-network-guard.ts +0 -453
  338. package/scripts/test/test-repository-guard.ts +0 -317
  339. package/scripts/test/test-state-guard.ts +0 -85
  340. package/scripts/test/test-stdout-guard.ts +0 -54
  341. package/scripts/test/test-telegram-guard.ts +0 -90
  342. package/scripts/test/unit-guard-environment.ts +0 -12
  343. package/scripts/test/unit-projects.ts +0 -139
  344. package/scripts/test/vitest-include-globs.ts +0 -12
  345. package/scripts/time-runtime/time-batch-guard-fixtures.ts +0 -66
  346. package/scripts-ai/completion-gate.ts +0 -13
  347. package/scripts-ai/environment-loader.ts +0 -9
  348. /package/{scripts-ai → scripts/epic}/epic-cli-argv.ts +0 -0
  349. /package/{scripts-ai → scripts/epic}/epic-cli-remove.ts +0 -0
  350. /package/{scripts-ai → scripts/git}/followup-issue-number.ts +0 -0
@@ -1,18 +1,19 @@
1
1
  import {
2
2
  lane_child_marker,
3
3
  run_issue_number
4
- } from "./chunk-GBJUERYK.js";
4
+ } from "./chunk-WQ4OSBSS.js";
5
5
  import {
6
6
  PLATFORM_TEMP_ROOT,
7
7
  cost_blocks,
8
8
  cost_usage,
9
+ file_reader,
9
10
  json_value,
10
11
  lane_paths,
11
12
  stamp_file,
12
13
  time_density_hook,
13
14
  time_shell,
14
15
  time_transcript_line
15
- } from "./chunk-PAXMLGSS.js";
16
+ } from "./chunk-7VPDUBRN.js";
16
17
  import {
17
18
  ENV_FILE_NAME,
18
19
  LANE_SEAT_KEY,
@@ -152,7 +153,7 @@ var git_utilities = {
152
153
  };
153
154
 
154
155
  // scripts/git/git-push-transport.ts
155
- import { execa } from "execa";
156
+ import { execa as execa2 } from "execa";
156
157
 
157
158
  // scripts/git/git-execa-error.ts
158
159
  function get_exit_code(error) {
@@ -165,25 +166,20 @@ function has_timed_out(error) {
165
166
  if (typeof error !== "object" || error === null || !("timedOut" in error)) return false;
166
167
  return error.timedOut === true;
167
168
  }
169
+ var TIMEOUT_EXIT_CODE = "timeout";
168
170
  function create_spawn_error(command, exit_code) {
169
171
  const exit_code_string = exit_code === void 0 ? "unknown" : String(exit_code);
170
172
  const error_message = `git ${command} exited with code ${exit_code_string}`;
171
173
  return new Error(error_message, { cause: { exit_code: exit_code_string } });
172
174
  }
173
175
 
174
- // scripts/git/git-push-transport.ts
175
- var MS_PER_SECOND = 1e3;
176
- var PUSH_TIMEOUT_SECONDS = 120;
177
- var PUSH_TIMEOUT_MS = PUSH_TIMEOUT_SECONDS * MS_PER_SECOND;
178
- var PUSH_TIMEOUT_LABEL = `${String(PUSH_TIMEOUT_SECONDS)}s`;
179
- var PUSH_TIMEOUT_MESSAGE = "git push timed out";
180
- var PUSH_TIMEOUT_EXIT_CODE = "timeout";
176
+ // scripts/git/git-ssh-keepalive.ts
177
+ import { execa } from "execa";
181
178
  var SSH_KEEPALIVE_OPTIONS = "-o ServerAliveInterval=15 -o ServerAliveCountMax=3";
182
179
  var SSH_COMMAND_VARIABLE = "GIT_SSH_COMMAND";
183
180
  var SSH_LEGACY_VARIABLE = "GIT_SSH";
184
181
  var KEEPALIVE_SSH_COMMAND = `ssh ${SSH_KEEPALIVE_OPTIONS}`;
185
182
  var SSH_COMMAND_CONFIG_KEY = "core.sshCommand";
186
- var PUSH_SUBCOMMAND = "push";
187
183
  function to_environment_value(name) {
188
184
  return process.env[name]?.trim() ?? "";
189
185
  }
@@ -201,19 +197,33 @@ async function has_configured_ssh_command() {
201
197
  return false;
202
198
  }
203
199
  }
204
- async function to_push_options() {
200
+ async function to_environment() {
205
201
  const should_keep_alive = !await has_configured_ssh_command();
202
+ return should_keep_alive ? { [SSH_COMMAND_VARIABLE]: KEEPALIVE_SSH_COMMAND } : {};
203
+ }
204
+ var git_ssh_keepalive = {
205
+ to_environment
206
+ };
207
+
208
+ // scripts/git/git-push-transport.ts
209
+ var MS_PER_SECOND = 1e3;
210
+ var PUSH_TIMEOUT_SECONDS = 120;
211
+ var PUSH_TIMEOUT_MS = PUSH_TIMEOUT_SECONDS * MS_PER_SECOND;
212
+ var PUSH_TIMEOUT_LABEL = `${String(PUSH_TIMEOUT_SECONDS)}s`;
213
+ var PUSH_TIMEOUT_MESSAGE = "git push timed out";
214
+ var PUSH_SUBCOMMAND = "push";
215
+ async function to_push_options() {
206
216
  return {
207
217
  stdio: "inherit",
208
218
  timeout: PUSH_TIMEOUT_MS,
209
- env: should_keep_alive ? { [SSH_COMMAND_VARIABLE]: KEEPALIVE_SSH_COMMAND } : {}
219
+ env: await git_ssh_keepalive.to_environment()
210
220
  };
211
221
  }
212
222
  async function did_push_time_out(arguments_list) {
213
223
  const git_command_bin = git_utilities.get_git_command_for_spawn();
214
224
  const options = await to_push_options();
215
225
  try {
216
- await execa(git_command_bin, [PUSH_SUBCOMMAND, ...arguments_list], options);
226
+ await execa2(git_command_bin, [PUSH_SUBCOMMAND, ...arguments_list], options);
217
227
  return false;
218
228
  } catch (error) {
219
229
  if (has_timed_out(error)) return true;
@@ -228,7 +238,7 @@ function to_retry_notice(arguments_list) {
228
238
  }
229
239
  function create_timeout_error(arguments_list) {
230
240
  const message = `${PUSH_TIMEOUT_MESSAGE} twice (${PUSH_TIMEOUT_LABEL} each). Re-run it by hand: ${to_manual_command(arguments_list)}`;
231
- return new Error(message, { cause: { exit_code: PUSH_TIMEOUT_EXIT_CODE } });
241
+ return new Error(message, { cause: { exit_code: TIMEOUT_EXIT_CODE } });
232
242
  }
233
243
  async function push(arguments_list) {
234
244
  if (!await did_push_time_out(arguments_list)) return;
@@ -241,23 +251,56 @@ var git_push_transport = {
241
251
  };
242
252
 
243
253
  // scripts/git/git-spawn.ts
244
- import { execa as execa2 } from "execa";
254
+ import { execa as execa3 } from "execa";
245
255
  async function read(arguments_) {
246
256
  const git_cmd = git_utilities.get_git_command_for_spawn();
247
- const { stdout } = await execa2(git_cmd, arguments_);
257
+ const { stdout } = await execa3(git_cmd, arguments_);
248
258
  return stdout.trimEnd();
249
259
  }
260
+ var REMOTE_TIMEOUT_MS = PUSH_TIMEOUT_MS;
261
+ var MS_PER_SECOND2 = 1e3;
262
+ function to_remote_error(command, error, timeout_ms) {
263
+ if (!has_timed_out(error)) return create_spawn_error(command, get_exit_code(error));
264
+ const seconds = String(Math.round(timeout_ms / MS_PER_SECOND2));
265
+ return new Error(`git ${command} timed out after ${seconds}s`, {
266
+ cause: { exit_code: TIMEOUT_EXIT_CODE }
267
+ });
268
+ }
269
+ async function spawn_remote(arguments_, options) {
270
+ const git_cmd = git_utilities.get_git_command_for_spawn();
271
+ try {
272
+ const { stdout } = await execa3(git_cmd, arguments_, options);
273
+ return typeof stdout === "string" ? stdout.trimEnd() : "";
274
+ } catch (error) {
275
+ throw to_remote_error(arguments_[0] ?? "", error, options.timeout);
276
+ }
277
+ }
278
+ async function read_remote(arguments_, timeout_ms = REMOTE_TIMEOUT_MS) {
279
+ return await spawn_remote(arguments_, {
280
+ timeout: timeout_ms,
281
+ env: await git_ssh_keepalive.to_environment()
282
+ });
283
+ }
284
+ async function with_output_remote(command, arguments_list) {
285
+ await spawn_remote([command, ...arguments_list], {
286
+ stdio: "inherit",
287
+ timeout: REMOTE_TIMEOUT_MS,
288
+ env: await git_ssh_keepalive.to_environment()
289
+ });
290
+ }
250
291
  async function with_output(command, arguments_list) {
251
292
  const git_command_bin = git_utilities.get_git_command_for_spawn();
252
293
  try {
253
- await execa2(git_command_bin, [command, ...arguments_list], { stdio: "inherit" });
294
+ await execa3(git_command_bin, [command, ...arguments_list], { stdio: "inherit" });
254
295
  } catch (error) {
255
296
  throw create_spawn_error(command, get_exit_code(error));
256
297
  }
257
298
  }
258
299
  var git_spawn = {
259
300
  read,
260
- with_output
301
+ read_remote,
302
+ with_output,
303
+ with_output_remote
261
304
  };
262
305
 
263
306
  // scripts/git/git-command.ts
@@ -432,7 +475,7 @@ async function push2() {
432
475
  }
433
476
  }
434
477
  async function pull_fast_forward() {
435
- await git_spawn.with_output("pull", ["--ff-only"]);
478
+ await git_spawn.with_output_remote("pull", ["--ff-only"]);
436
479
  }
437
480
  var SHORT_NAME_FORMAT = "--format=%(refname:short)";
438
481
  var REMOTES_FLAG = "--remotes";
@@ -839,18 +882,18 @@ function trimmed(value) {
839
882
  const result = value?.trim();
840
883
  return result === void 0 || result.length === EMPTY_LENGTH ? void 0 : result;
841
884
  }
842
- function override_value(role, field, environment) {
885
+ function override_value(role, field, environment2) {
843
886
  const key = ENV_KEYS[role][field];
844
- const value = trimmed(environment[key]);
887
+ const value = trimmed(environment2[key]);
845
888
  if (value !== void 0) return { key, value };
846
889
  if (role !== WORKER) return { key, value: void 0 };
847
890
  const legacy = LEGACY_WORKER_KEYS[field];
848
- const legacy_value = trimmed(environment[legacy]);
891
+ const legacy_value = trimmed(environment2[legacy]);
849
892
  return legacy_value === void 0 ? { key, value: void 0 } : { key: legacy, value: legacy_value };
850
893
  }
851
- function model_override(role, environment, provider) {
894
+ function model_override(role, environment2, provider) {
852
895
  const key = ENV_KEYS[role].model;
853
- return provider === OPENAI_PROVIDER ? { key, value: void 0 } : override_value(role, "model", environment);
896
+ return provider === OPENAI_PROVIDER ? { key, value: void 0 } : override_value(role, "model", environment2);
854
897
  }
855
898
  function rejection(key, value, expected) {
856
899
  return { kind: "rejected", note: `${key}=${value} ${expected}, so the agent was not started` };
@@ -859,35 +902,35 @@ function resolved_effort(override, fallback) {
859
902
  const parsed = EFFORT_SCHEMA.safeParse(override.value ?? fallback);
860
903
  return parsed.success ? { kind: "effort", effort: parsed.data } : rejection(override.key, override.value ?? "", "is not an allowed effort");
861
904
  }
862
- function has_session(environment, keys) {
863
- return keys.some((key) => trimmed(environment[key]) !== void 0);
905
+ function has_session(environment2, keys) {
906
+ return keys.some((key) => trimmed(environment2[key]) !== void 0);
864
907
  }
865
908
  function session_rejection(is_conflicting) {
866
909
  const note = is_conflicting ? "both Codex and Claude Code sessions were detected" : "no Codex or Claude Code session was detected";
867
910
  return { kind: "rejected", note };
868
911
  }
869
912
  var HANDED_PROVIDER_KEY = "JOSH_AGENT_PROVIDER";
870
- function handed_provider(environment) {
871
- const value = trimmed(environment[HANDED_PROVIDER_KEY]);
913
+ function handed_provider(environment2) {
914
+ const value = trimmed(environment2[HANDED_PROVIDER_KEY]);
872
915
  if (value === void 0) return session_rejection(false);
873
916
  const parsed = PROVIDER_SCHEMA.safeParse(value);
874
917
  return parsed.success ? { kind: "provider", provider: parsed.data } : rejection(HANDED_PROVIDER_KEY, value, "is not an allowed provider");
875
918
  }
876
- function detected_provider(environment) {
877
- const has_codex = has_session(environment, [CODEX_SESSION_KEY]);
878
- const has_claude = has_session(environment, agent_session_environment.PARENT_SESSION_KEYS);
919
+ function detected_provider(environment2) {
920
+ const has_codex = has_session(environment2, [CODEX_SESSION_KEY]);
921
+ const has_claude = has_session(environment2, agent_session_environment.PARENT_SESSION_KEYS);
879
922
  if (has_codex === has_claude) return has_codex ? session_rejection(true) : void 0;
880
923
  return { kind: "provider", provider: has_codex ? OPENAI_PROVIDER : ANTHROPIC_PROVIDER };
881
924
  }
882
- function resolve_provider(environment = process.env) {
883
- return detected_provider(environment) ?? handed_provider(environment);
925
+ function resolve_provider(environment2 = process.env) {
926
+ return detected_provider(environment2) ?? handed_provider(environment2);
884
927
  }
885
- function has_completion_callback(environment = process.env) {
886
- const selected = resolve_provider(environment);
928
+ function has_completion_callback(environment2 = process.env) {
929
+ const selected = resolve_provider(environment2);
887
930
  return selected.kind !== "provider" || selected.provider !== OPENAI_PROVIDER;
888
931
  }
889
- function handoff_environment(environment = process.env) {
890
- const selected = resolve_provider(environment);
932
+ function handoff_environment(environment2 = process.env) {
933
+ const selected = resolve_provider(environment2);
891
934
  return selected.kind === "provider" ? { [HANDED_PROVIDER_KEY]: selected.provider } : {};
892
935
  }
893
936
  function validate(profile, model_key) {
@@ -901,19 +944,19 @@ function phase_effort(role, phase, fallback) {
901
944
  if (!parsed.success) return fallback;
902
945
  return PHASE_EFFORT[role]?.[parsed.data] ?? fallback;
903
946
  }
904
- function overridden_effort(profile, phase, environment) {
905
- const parsed = EFFORT_SCHEMA.safeParse(override_value(profile.role, "effort", environment).value);
947
+ function overridden_effort(profile, phase, environment2) {
948
+ const parsed = EFFORT_SCHEMA.safeParse(override_value(profile.role, "effort", environment2).value);
906
949
  return parsed.success ? parsed.data : phase_effort(profile.role, phase, profile.effort);
907
950
  }
908
- function with_phase_effort(profile, phase, environment = process.env) {
909
- return { ...profile, effort: overridden_effort(profile, phase, environment) };
951
+ function with_phase_effort(profile, phase, environment2 = process.env) {
952
+ return { ...profile, effort: overridden_effort(profile, phase, environment2) };
910
953
  }
911
- function resolve(role, environment = process.env, phase) {
912
- const selected = resolve_provider(environment);
954
+ function resolve(role, environment2 = process.env, phase) {
955
+ const selected = resolve_provider(environment2);
913
956
  if (selected.kind === "rejected") return selected;
914
957
  const defaults = PROVIDER_PROFILES[selected.provider][role];
915
- const model = model_override(role, environment, selected.provider);
916
- const effort = override_value(role, "effort", environment);
958
+ const model = model_override(role, environment2, selected.provider);
959
+ const effort = override_value(role, "effort", environment2);
917
960
  const resolved = resolved_effort(effort, phase_effort(role, phase, defaults.effort));
918
961
  if (resolved.kind === "rejected") return resolved;
919
962
  const profile = {
@@ -1311,10 +1354,10 @@ function load_handoff(handoff_path, max_bytes) {
1311
1354
  }
1312
1355
  return { kind: "ok", handoff };
1313
1356
  }
1314
- function describe_list(label, items) {
1315
- if (items.length === 0) return `${label}: ${EMPTY_LIST_NOTE}`;
1357
+ function describe_list(label2, items) {
1358
+ if (items.length === 0) return `${label2}: ${EMPTY_LIST_NOTE}`;
1316
1359
  const bullets = items.map((item) => ` - ${item}`).join("\n");
1317
- return `${label}:
1360
+ return `${label2}:
1318
1361
  ${bullets}`;
1319
1362
  }
1320
1363
  function describe_handoff(handoff) {
@@ -1670,6 +1713,7 @@ var IN_PROGRESS_LABEL = "in-progress";
1670
1713
  var NEEDS_DECISION_LABEL = "needs-decision";
1671
1714
  var RUN_SOLO_LABEL = "run:solo";
1672
1715
  var RUN_LANE_LABEL = "run:lane";
1716
+ var PRIORITY_HIGH_LABEL = "priority:high";
1673
1717
  var NEEDS_HUMAN_REVIEW_LABEL = "needs-human-review";
1674
1718
  var ALREADY_DONE_LABEL = "already-done";
1675
1719
  var INTERRUPT_ROUTE_LABEL = "route:interrupt";
@@ -1739,6 +1783,11 @@ var WORKFLOW_LABELS = [
1739
1783
  color: "0e8a16",
1740
1784
  description: "Judged safe to run beside others in a backlogrun"
1741
1785
  },
1786
+ {
1787
+ name: PRIORITY_HIGH_LABEL,
1788
+ color: "e99695",
1789
+ description: "Offered first in a backlogrun; only a person removes it"
1790
+ },
1742
1791
  ...FILING_ROUTE_LABELS,
1743
1792
  ...DEPTH_LABELS
1744
1793
  ];
@@ -1765,11 +1814,11 @@ var REPOSITORY_LABELS = [
1765
1814
  ...RELEASE_CLASSIFICATION_LABELS
1766
1815
  ];
1767
1816
  function has_any_label(labels, wanted) {
1768
- return (labels ?? []).some((label) => wanted.has(label.name.toLowerCase()));
1817
+ return (labels ?? []).some((label2) => wanted.has(label2.name.toLowerCase()));
1769
1818
  }
1770
1819
  function label_name_of(labels, wanted) {
1771
1820
  const target = wanted.toLowerCase();
1772
- return labels.find((label) => label.toLowerCase() === target);
1821
+ return labels.find((label2) => label2.toLowerCase() === target);
1773
1822
  }
1774
1823
  function has_label_name(labels, wanted) {
1775
1824
  return label_name_of(labels, wanted) !== void 0;
@@ -2002,14 +2051,14 @@ function main_worktree(repository_path) {
2002
2051
  if (path.basename(git_directory) !== GIT_DIRECTORY) return repository_path;
2003
2052
  return path.dirname(git_directory);
2004
2053
  }
2005
- function discover_repositories(repository_path, environment = process.env) {
2054
+ function discover_repositories(repository_path, environment2 = process.env) {
2006
2055
  const main2 = main_worktree(repository_path);
2007
2056
  const current_owner2 = resolve_current_owner(main2);
2008
2057
  if (current_owner2 === void 0) return /* @__PURE__ */ new Map();
2009
2058
  return repo_map_logic.build_repository_map(
2010
2059
  scan_siblings(path.dirname(main2)),
2011
2060
  current_owner2,
2012
- environment[OVERRIDE_ENV_KEY]
2061
+ environment2[OVERRIDE_ENV_KEY]
2013
2062
  );
2014
2063
  }
2015
2064
  var repo_discovery = {
@@ -2037,36 +2086,97 @@ function current_owner(repository_path = process.cwd()) {
2037
2086
  }
2038
2087
  var repo_party = { FIRST_PARTY, THIRD_PARTY, UNKNOWN, classify: classify3, current_owner, target_owner };
2039
2088
 
2040
- // scripts/lane/lane-registry.ts
2041
- import { existsSync as existsSync2, readFileSync as readFileSync4 } from "node:fs";
2042
- import path3 from "node:path";
2043
-
2044
- // scripts/git/git-ls-remote.ts
2045
- var LS_REMOTE_COMMAND = "ls-remote";
2046
- var HEADS_FLAG = "--heads";
2047
- var ORIGIN_REMOTE = "origin";
2048
- var HEADS_REF_PREFIX = "refs/heads/";
2049
- function ls_remote_branch_arguments(branch_name) {
2050
- return [LS_REMOTE_COMMAND, HEADS_FLAG, ORIGIN_REMOTE, `${HEADS_REF_PREFIX}${branch_name}`];
2089
+ // scripts/lane/lane-reap.ts
2090
+ import { execFileSync as execFileSync2 } from "node:child_process";
2091
+ var PGREP_COMMAND = "pgrep";
2092
+ var MATCH_FULL_COMMAND_LINE = "-f";
2093
+ var CHILDREN_OF_FLAG = "-P";
2094
+ var PS_COMMAND = "/bin/ps";
2095
+ var PARENT_FORMAT = ["-o", "ppid=", "-p"];
2096
+ var TERMINATE_SIGNAL = "SIGTERM";
2097
+ var PROBE_TIMEOUT_MS = 2e3;
2098
+ var LOWEST_CHILD_PID = 2;
2099
+ function parse_pids(printed) {
2100
+ return printed.split("\n").map((line) => Number(line.trim())).filter((pid) => Number.isSafeInteger(pid) && pid >= LOWEST_CHILD_PID);
2051
2101
  }
2052
-
2053
- // scripts/git/repository-lock.ts
2054
- import { randomUUID as randomUUID2 } from "node:crypto";
2055
- import { linkSync, renameSync, rmSync as rmSync2 } from "node:fs";
2056
- import { setTimeout as sleep } from "node:timers/promises";
2102
+ function run_probe(command, args) {
2103
+ try {
2104
+ return execFileSync2(command, [...args], {
2105
+ encoding: "utf8",
2106
+ timeout: PROBE_TIMEOUT_MS,
2107
+ stdio: ["ignore", "pipe", "ignore"]
2108
+ });
2109
+ } catch {
2110
+ return "";
2111
+ }
2112
+ }
2113
+ function parent_of(pid) {
2114
+ return parse_pids(run_probe(PS_COMMAND, [...PARENT_FORMAT, String(pid)]))[0];
2115
+ }
2116
+ function terminate(pid) {
2117
+ try {
2118
+ process.kill(pid, TERMINATE_SIGNAL);
2119
+ return true;
2120
+ } catch {
2121
+ return false;
2122
+ }
2123
+ }
2124
+ var SYSTEM_PROBES = {
2125
+ matching: (pattern) => parse_pids(run_probe(PGREP_COMMAND, [MATCH_FULL_COMMAND_LINE, pattern])),
2126
+ children_of: (pid) => parse_pids(run_probe(PGREP_COMMAND, [CHILDREN_OF_FLAG, String(pid)])),
2127
+ parent_of,
2128
+ terminate
2129
+ };
2130
+ function ancestry_of(pid, probes) {
2131
+ const ancestry = /* @__PURE__ */ new Set([pid]);
2132
+ let current = probes.parent_of(pid);
2133
+ while (current !== void 0 && !ancestry.has(current)) {
2134
+ ancestry.add(current);
2135
+ current = probes.parent_of(current);
2136
+ }
2137
+ return ancestry;
2138
+ }
2139
+ function own_ancestry(probes = SYSTEM_PROBES) {
2140
+ return ancestry_of(process.pid, probes);
2141
+ }
2142
+ function tree_of(roots, probes) {
2143
+ const tree = [];
2144
+ const pending = [...roots];
2145
+ let next = pending.shift();
2146
+ while (next !== void 0) {
2147
+ if (!tree.includes(next)) {
2148
+ tree.push(next);
2149
+ pending.push(...probes.children_of(next));
2150
+ }
2151
+ next = pending.shift();
2152
+ }
2153
+ return tree;
2154
+ }
2155
+ function reap_child(issue, probes = SYSTEM_PROBES) {
2156
+ const own = own_ancestry(probes);
2157
+ const pattern = lane_child_invocation.process_pattern(issue);
2158
+ const roots = probes.matching(pattern).filter((pid) => !own.has(pid));
2159
+ const doomed = tree_of(roots, probes).filter((pid) => !own.has(pid));
2160
+ return doomed.filter((pid) => probes.terminate(pid));
2161
+ }
2162
+ var lane_reap = {
2163
+ own_ancestry,
2164
+ parse_pids,
2165
+ reap_child
2166
+ };
2057
2167
 
2058
2168
  // scripts/josh/process-identity.ts
2059
- import { execFileSync as execFileSync2, spawnSync } from "node:child_process";
2169
+ import { execFileSync as execFileSync3, spawnSync } from "node:child_process";
2060
2170
  import { randomUUID } from "node:crypto";
2061
2171
  import { readFileSync as readFileSync3, rmSync } from "node:fs";
2062
2172
  import { createServer } from "node:net";
2063
2173
  import path2 from "node:path";
2064
- var PS_COMMAND = "/bin/ps";
2174
+ var PS_COMMAND2 = "/bin/ps";
2065
2175
  var FORMAT_FLAG = "-o";
2066
2176
  var START_TIME_FORMAT = "lstart=";
2067
2177
  var PID_FLAG = "-p";
2068
2178
  var PROBE_ENVIRONMENT = { TZ: "UTC", LC_ALL: "C" };
2069
- var PROBE_TIMEOUT_MS = 2e3;
2179
+ var PROBE_TIMEOUT_MS2 = 2e3;
2070
2180
  var LIVENESS_SIGNAL = 0;
2071
2181
  var LOWEST_REAL_PID = 1;
2072
2182
  var PROC_START_FIELD_INDEX = 19;
@@ -2100,12 +2210,12 @@ function read_proc_start(pid) {
2100
2210
  }
2101
2211
  function read_ps_start(pid) {
2102
2212
  try {
2103
- const printed = execFileSync2(
2104
- PS_COMMAND,
2213
+ const printed = execFileSync3(
2214
+ PS_COMMAND2,
2105
2215
  [FORMAT_FLAG, START_TIME_FORMAT, PID_FLAG, String(pid)],
2106
2216
  {
2107
2217
  encoding: "utf8",
2108
- timeout: PROBE_TIMEOUT_MS,
2218
+ timeout: PROBE_TIMEOUT_MS2,
2109
2219
  stdio: ["ignore", "pipe", "ignore"],
2110
2220
  env: PROBE_ENVIRONMENT
2111
2221
  }
@@ -2183,7 +2293,7 @@ function beacon_target(token) {
2183
2293
  function is_live_beacon(target) {
2184
2294
  return spawnSync(process.execPath, ["-e", SOCKET_PROBE_SOURCE, target], {
2185
2295
  stdio: "ignore",
2186
- timeout: PROBE_TIMEOUT_MS
2296
+ timeout: PROBE_TIMEOUT_MS2
2187
2297
  }).status === 0;
2188
2298
  }
2189
2299
  function has_matching_start(pid, recorded_start, read2) {
@@ -2223,975 +2333,1002 @@ var process_identity = {
2223
2333
  resolve_own_start
2224
2334
  };
2225
2335
 
2226
- // scripts/git/git-common-directory.ts
2227
- import { execFileSync as execFileSync3 } from "node:child_process";
2228
- var OWN_GIT_DIRECTORY = 0;
2229
- var COMMON_GIT_DIRECTORY = 1;
2230
- var PROBE_TIMEOUT_MS2 = 2e3;
2231
- function read_directories(cwd) {
2232
- const git = git_utilities.get_git_command_for_spawn();
2233
- const output = execFileSync3(git, [...git_command.GIT_DIRECTORY_ARGUMENTS], {
2234
- cwd,
2235
- encoding: "utf8",
2236
- stdio: ["ignore", "pipe", "ignore"],
2237
- timeout: PROBE_TIMEOUT_MS2
2238
- });
2239
- return output.trim().split("\n");
2336
+ // scripts/run/run-carry.ts
2337
+ import { z as z5 } from "zod";
2338
+
2339
+ // scripts/run/run-carry-streak.ts
2340
+ var NO_INCREMENT = 0;
2341
+ var OUTAGE_FOLD_WINDOW_MS = 12e4;
2342
+ function has_increment(value) {
2343
+ return (value ?? NO_INCREMENT) > NO_INCREMENT;
2240
2344
  }
2241
- function select_linked(directories) {
2242
- const own = directories[OWN_GIT_DIRECTORY];
2243
- const common = directories[COMMON_GIT_DIRECTORY];
2244
- return own !== void 0 && common !== void 0 && own !== common ? common : void 0;
2345
+ function next_failures(carry, change) {
2346
+ if (has_increment(change.merged)) return NO_INCREMENT;
2347
+ return carry.failures + (change.failures ?? NO_INCREMENT);
2245
2348
  }
2246
- function resolve2(cwd) {
2247
- try {
2248
- return select_linked(read_directories(cwd));
2249
- } catch {
2250
- return void 0;
2251
- }
2349
+ function is_within_fold_window(last_outage_at, now) {
2350
+ if (last_outage_at === void 0) return false;
2351
+ const last = Date.parse(last_outage_at);
2352
+ if (Number.isNaN(last)) return false;
2353
+ return now.getTime() - last <= OUTAGE_FOLD_WINDOW_MS;
2252
2354
  }
2253
- function repository(cwd) {
2254
- try {
2255
- return read_directories(cwd)[COMMON_GIT_DIRECTORY];
2256
- } catch {
2257
- return void 0;
2258
- }
2355
+ function folded_outage(carry) {
2356
+ return { outages: carry.outages, last_outage_at: carry.last_outage_at };
2259
2357
  }
2260
- var git_common_directory = { repository, resolve: resolve2, select_linked };
2261
-
2262
- // scripts/git/repository-lock.ts
2263
- var POLL_INTERVAL_MS = 200;
2264
- function lock_path(prefix, cwd = process.cwd()) {
2265
- return stamp_file.stamp_path(prefix, git_common_directory.repository(cwd) ?? cwd);
2358
+ function counted_outage(carry, change, now) {
2359
+ const outages = carry.outages + (change.outages ?? NO_INCREMENT);
2360
+ return { outages, last_outage_at: now.toISOString() };
2266
2361
  }
2267
- function parse_owner(raw) {
2268
- if (raw === void 0) return void 0;
2269
- try {
2270
- return JSON.parse(raw);
2271
- } catch {
2272
- return void 0;
2362
+ function next_outage(carry, change, now) {
2363
+ if (has_increment(change.outages)) {
2364
+ return is_within_fold_window(carry.last_outage_at, now) ? folded_outage(carry) : counted_outage(carry, change, now);
2273
2365
  }
2366
+ const is_reachable = has_increment(change.merged) || has_increment(change.failures);
2367
+ return is_reachable ? { outages: NO_INCREMENT, last_outage_at: void 0 } : folded_outage(carry);
2274
2368
  }
2275
- function read_owner(target) {
2276
- return parse_owner(stamp_file.read_stamp_text(target));
2277
- }
2278
- function is_owner_gone(owner) {
2279
- return process_identity.is_same_process(owner.pid, owner.process_start) === false;
2280
- }
2281
- function restore(aside, target) {
2282
- try {
2283
- linkSync(aside, target);
2284
- } catch {
2285
- }
2369
+ function next_state(carry, change, now) {
2370
+ const outage = next_outage(carry, change, now);
2371
+ return {
2372
+ failures: next_failures(carry, change),
2373
+ outages: outage.outages,
2374
+ last_outage_at: outage.last_outage_at
2375
+ };
2286
2376
  }
2287
- function stale_record(target, is_gone2) {
2288
- const raw = stamp_file.read_stamp_text(target);
2289
- const owner = parse_owner(raw);
2290
- return owner !== void 0 && is_gone2(owner) ? raw : void 0;
2377
+ var run_carry_streak = { OUTAGE_FOLD_WINDOW_MS, next_state };
2378
+
2379
+ // scripts/run/run-carry-change.ts
2380
+ var NO_INCREMENT2 = 0;
2381
+ function next_done(carry, done) {
2382
+ if (done === void 0) return carry.done;
2383
+ const current = carry.done ?? [];
2384
+ return current.includes(done) ? current : [...current, done];
2291
2385
  }
2292
- function move_aside(target) {
2293
- const aside = `${target}.${String(process.pid)}.${randomUUID2()}`;
2294
- try {
2295
- renameSync(target, aside);
2296
- return aside;
2297
- } catch {
2298
- return void 0;
2299
- }
2386
+ function next_merged_issues(carry, issue) {
2387
+ if (issue === void 0) return carry.merged_issues;
2388
+ return [...carry.merged_issues ?? [], issue];
2300
2389
  }
2301
- function clear_stale(target, is_gone2 = is_owner_gone) {
2302
- const raw = stale_record(target, is_gone2);
2303
- if (raw === void 0) return;
2304
- const aside = move_aside(target);
2305
- if (aside === void 0) return;
2306
- if (stamp_file.read_stamp_text(aside) !== raw) restore(aside, target);
2307
- rmSync2(aside, { force: true });
2390
+ function is_duplicate_merge(carry, change) {
2391
+ const issue = change.merged_issue;
2392
+ return issue !== void 0 && carry.merged_issues?.includes(issue) === true;
2308
2393
  }
2309
- function claim(target) {
2310
- clear_stale(target);
2311
- return stamp_file.create_stamp(target, process_identity.own_fields());
2394
+ function next_counts(carry, change) {
2395
+ const cuts = change.cuts ?? NO_INCREMENT2;
2396
+ return {
2397
+ merged: carry.merged + (change.merged ?? NO_INCREMENT2),
2398
+ filed: carry.filed + (change.filed ?? NO_INCREMENT2),
2399
+ cuts: carry.cuts + cuts,
2400
+ is_handed_off: cuts > NO_INCREMENT2
2401
+ };
2312
2402
  }
2313
- function release(target) {
2314
- const owner = read_owner(target);
2315
- if (owner !== void 0 && process_identity.is_own_process(owner.pid, owner.process_start)) {
2316
- stamp_file.remove_stamp(target);
2317
- }
2403
+ function apply_change(target, carry, change, now = /* @__PURE__ */ new Date()) {
2404
+ if (is_duplicate_merge(carry, change)) return carry;
2405
+ const streak = run_carry_streak.next_state(carry, change, now);
2406
+ const next = {
2407
+ ...carry,
2408
+ ...next_counts(carry, change),
2409
+ merged_issues: next_merged_issues(carry, change.merged_issue),
2410
+ failures: streak.failures,
2411
+ outages: streak.outages,
2412
+ last_outage_at: streak.last_outage_at,
2413
+ done: next_done(carry, change.done),
2414
+ retrospective: change.retrospective === true || carry.retrospective
2415
+ };
2416
+ stamp_file.write_stamp(target, next);
2417
+ return next;
2318
2418
  }
2319
- async function acquire(target, max_wait_ms) {
2320
- const deadline = Date.now() + max_wait_ms;
2321
- while (!claim(target)) {
2322
- if (Date.now() >= deadline) return false;
2323
- await sleep(POLL_INTERVAL_MS);
2324
- }
2325
- return true;
2419
+ var run_carry_change = { apply_change };
2420
+
2421
+ // scripts/backlog/backlog-budget-cli.ts
2422
+ import { fileURLToPath } from "node:url";
2423
+
2424
+ // scripts/git/changed-paths.ts
2425
+ function to_paths(raw) {
2426
+ return raw.split("\n").map((line) => line.trim()).filter((line) => line !== "");
2326
2427
  }
2327
- async function with_lock(work, target, max_wait_ms) {
2328
- if (!await acquire(target, max_wait_ms)) return void 0;
2329
- try {
2330
- return await work();
2331
- } finally {
2332
- release(target);
2333
- }
2428
+ async function read_changed_paths(is_staged) {
2429
+ if (is_staged) return to_paths(await git_command.diff_cached_names());
2430
+ const tracked = to_paths(await git_command.diff_main_names());
2431
+ const untracked = to_paths(await git_command.untracked_names());
2432
+ return [...tracked, ...untracked];
2334
2433
  }
2335
- var repository_lock = { clear_stale, lock_path, with_lock };
2434
+ var changed_paths = { read_changed_paths, to_paths };
2336
2435
 
2337
- // scripts/git/git-worktree.ts
2338
- var WORKTREE = "worktree";
2339
- var WORKTREE_LOCK_PREFIX = "josh-worktree-lock-";
2340
- var WORKTREE_LOCK_MAX_WAIT_MS = 12e4;
2341
- async function locked(arguments_) {
2342
- const target = repository_lock.lock_path(WORKTREE_LOCK_PREFIX);
2343
- const output = await repository_lock.with_lock(
2344
- async () => await git_spawn.read(arguments_),
2345
- target,
2346
- WORKTREE_LOCK_MAX_WAIT_MS
2347
- );
2348
- if (output === void 0) {
2349
- throw new Error(
2350
- `git ${arguments_.join(" ")}: timed out waiting for the work-tree lock ${target}`
2351
- );
2352
- }
2353
- return output;
2354
- }
2355
- async function worktree_list() {
2356
- return await git_spawn.read([WORKTREE, "list", PORCELAIN_FLAG]);
2357
- }
2358
- async function worktree_add(directory, branch_name, start_point) {
2359
- const flags = start_point === void 0 ? ["add"] : ["add", "--no-track", "-b", branch_name];
2360
- return await locked([WORKTREE, ...flags, directory, start_point ?? branch_name]);
2361
- }
2362
- async function worktree_add_detached(directory, commit2) {
2363
- return await locked([WORKTREE, "add", "--detach", directory, commit2]);
2436
+ // scripts/josh/path-decision.ts
2437
+ var STAGED_FLAG = "--staged";
2438
+ var JSON_FLAG = "--json";
2439
+ var KNOWN_FLAGS = [STAGED_FLAG, JSON_FLAG];
2440
+ var FAILURE_EXIT_CODE = 1;
2441
+ var MAX_LISTED_PATHS = 5;
2442
+ function has_unknown_flag(argv, known) {
2443
+ return argv.some((argument) => !known.includes(argument));
2364
2444
  }
2365
- async function worktree_remove(directory) {
2366
- return await locked([WORKTREE, "remove", "--force", directory]);
2445
+ function parse_options(argv) {
2446
+ if (has_unknown_flag(argv, KNOWN_FLAGS)) return void 0;
2447
+ return { is_staged: argv.includes(STAGED_FLAG), is_json: argv.includes(JSON_FLAG) };
2367
2448
  }
2368
- async function worktree_prune() {
2369
- return await locked([WORKTREE, "prune"]);
2449
+ function format_path_list(paths) {
2450
+ const listed = paths.slice(0, MAX_LISTED_PATHS).join(", ");
2451
+ if (paths.length <= MAX_LISTED_PATHS) return listed;
2452
+ return `${listed}, +${String(paths.length - MAX_LISTED_PATHS)} more`;
2370
2453
  }
2371
- async function ls_remote_branch(branch_name) {
2372
- return await git_spawn.read(ls_remote_branch_arguments(branch_name));
2454
+ function print_decision(key, answer, reason, is_json) {
2455
+ if (is_json) {
2456
+ console.info(JSON.stringify({ [key]: answer, reason }));
2457
+ return;
2458
+ }
2459
+ console.info(answer);
2460
+ console.error(reason);
2373
2461
  }
2374
- async function branch_delete(branch_name) {
2375
- return await git_spawn.read(["branch", "-D", branch_name]);
2462
+ async function run_path_decision(argv, command) {
2463
+ const options = parse_options(argv);
2464
+ if (options === void 0) {
2465
+ console.error(command.usage);
2466
+ return FAILURE_EXIT_CODE;
2467
+ }
2468
+ const paths = await changed_paths.read_changed_paths(options.is_staged);
2469
+ const answer = command.decide(paths);
2470
+ print_decision(command.key, answer, command.explain(paths, answer), options.is_json);
2471
+ return 0;
2376
2472
  }
2377
- var git_worktree = {
2378
- branch_delete,
2379
- ls_remote_branch,
2380
- worktree_add,
2381
- worktree_add_detached,
2382
- worktree_list,
2383
- worktree_prune,
2384
- worktree_remove
2473
+ var path_decision = {
2474
+ format_path_list,
2475
+ has_unknown_flag,
2476
+ JSON_FLAG,
2477
+ KNOWN_FLAGS,
2478
+ MAX_LISTED_PATHS,
2479
+ parse_options,
2480
+ print_decision,
2481
+ run_path_decision,
2482
+ STAGED_FLAG
2385
2483
  };
2386
2484
 
2387
- // scripts/lane/lane-environment.ts
2388
- var LANE_OUTPUT_KEY = "JOSH_LANE_OUTPUT";
2389
- var LANE_PROVIDER_KEY = "JOSH_LANE_AGENT_PROVIDER";
2390
- var LANE_ROLE_KEY = "JOSH_LANE_AGENT_ROLE";
2391
- var LANE_MODEL_KEY = "JOSH_LANE_AGENT_MODEL";
2392
- var LANE_EFFORT_KEY = "JOSH_LANE_AGENT_EFFORT";
2393
- var ASSIGNMENT_SEPARATOR = "=";
2394
- var EXPORT_PREFIX = "export ";
2395
- var QUOTE_CHARACTERS = /* @__PURE__ */ new Set(['"', "'"]);
2396
- var MIN_QUOTED_LENGTH = 2;
2397
- var FIRST_CHARACTER_END = 1;
2398
- var LAST_CHARACTER_START = -1;
2399
- function strip_export(line) {
2400
- const trimmed2 = line.trimStart();
2401
- return trimmed2.startsWith(EXPORT_PREFIX) ? trimmed2.slice(EXPORT_PREFIX.length).trimStart() : trimmed2;
2485
+ // scripts/lib/cli-flags.ts
2486
+ import { parseArgs } from "node:util";
2487
+ function refuse_unknown_flags(argv, known_flags, command) {
2488
+ const unknown = argv.filter((argument) => !known_flags.includes(argument));
2489
+ if (unknown.length === 0) return void 0;
2490
+ const usage = `Usage: josh ${command} [${known_flags.join("] [")}]`;
2491
+ return `Unknown argument(s): ${unknown.join(" ")}
2492
+ ${usage}`;
2493
+ }
2494
+ function parse_or_undefined(config) {
2495
+ try {
2496
+ return parseArgs(config);
2497
+ } catch {
2498
+ return void 0;
2499
+ }
2402
2500
  }
2403
- function is_key_line(line, key) {
2404
- const body = strip_export(line);
2405
- if (!body.startsWith(key)) return false;
2406
- return body.slice(key.length).trimStart().startsWith(ASSIGNMENT_SEPARATOR);
2501
+ var cli_flags = { refuse_unknown_flags, parse_or_undefined };
2502
+
2503
+ // scripts/backlog/backlog-budget-cli.ts
2504
+ var ARGV_OFFSET = 2;
2505
+ var SUCCESS_EXIT_CODE = 0;
2506
+ var FAILURE_EXIT_CODE2 = 1;
2507
+ var JSON_KEY = "budget";
2508
+ var USAGE = `Usage: josh backlog:budget --answer <${backlog_budget.ANSWERS.join("|")}> --started <ISO-8601> [--active <ISO-8601>, required unless the watch is off] [--merged <count>] [--running <count>] [--max <count>] [--idle <minutes>, 0 to turn the watch off] [--json]`;
2509
+ var OPTIONS = {
2510
+ active: { type: "string" },
2511
+ answer: { type: "string" },
2512
+ idle: { type: "string" },
2513
+ json: { type: "boolean" },
2514
+ max: { type: "string" },
2515
+ merged: { type: "string" },
2516
+ running: { type: "string" },
2517
+ started: { type: "string" }
2518
+ };
2519
+ function read_arguments(argv) {
2520
+ return cli_flags.parse_or_undefined({ args: [...argv], options: OPTIONS, strict: true })?.values;
2407
2521
  }
2408
- function is_seed_line(line) {
2409
- return is_key_line(line, PORT_SEED_KEY);
2522
+ function text_of(value) {
2523
+ return typeof value === "string" ? value : void 0;
2410
2524
  }
2411
- function is_seat_line(line) {
2412
- return is_key_line(line, LANE_SEAT_KEY);
2525
+ function to_answer(raw) {
2526
+ return backlog_budget.ANSWERS.find((answer) => answer === raw);
2413
2527
  }
2414
- function is_output_line(line) {
2415
- return is_key_line(line, LANE_OUTPUT_KEY);
2528
+ function to_time_ms(raw) {
2529
+ if (raw === void 0) return void 0;
2530
+ const parsed = Date.parse(raw);
2531
+ return Number.isNaN(parsed) ? void 0 : parsed;
2416
2532
  }
2417
- function split_lines(content) {
2418
- const trimmed2 = content.trimEnd();
2419
- return trimmed2.length === 0 ? [] : trimmed2.split("\n");
2533
+ var COUNT_PATTERN = /^\d+$/u;
2534
+ function to_count(raw) {
2535
+ if (raw === void 0 || !COUNT_PATTERN.test(raw)) return void 0;
2536
+ const parsed = Number(raw);
2537
+ return Number.isSafeInteger(parsed) ? parsed : void 0;
2420
2538
  }
2421
- function is_quoted(raw) {
2422
- const first = raw.slice(0, FIRST_CHARACTER_END);
2423
- if (!QUOTE_CHARACTERS.has(first)) return false;
2424
- return raw.length >= MIN_QUOTED_LENGTH && raw.endsWith(first);
2539
+ function is_readable(raw, parsed) {
2540
+ return raw === void 0 || parsed !== void 0;
2425
2541
  }
2426
- function unquote(raw) {
2427
- return is_quoted(raw) ? raw.slice(FIRST_CHARACTER_END, LAST_CHARACTER_START) : raw;
2542
+ var COUNT_NAMES = ["idle", "max", "merged", "running"];
2543
+ function counts_of(values) {
2544
+ return {
2545
+ idle: to_count(text_of(values.idle)),
2546
+ max: to_count(text_of(values.max)),
2547
+ merged: to_count(text_of(values.merged)),
2548
+ running: to_count(text_of(values.running))
2549
+ };
2428
2550
  }
2429
- function assignment_value(line) {
2430
- return unquote(line.slice(line.indexOf(ASSIGNMENT_SEPARATOR) + FIRST_CHARACTER_END).trim());
2551
+ function are_counts_readable(values, counts) {
2552
+ return COUNT_NAMES.every((name) => is_readable(text_of(values[name]), counts[name]));
2431
2553
  }
2432
- function read_assignment(content, key) {
2433
- const line = split_lines(content).findLast((entry) => is_key_line(entry, key));
2434
- return line === void 0 ? void 0 : assignment_value(line);
2554
+ var NO_IDLE_MINUTES = 0;
2555
+ function to_idle_ms(idle_minutes) {
2556
+ if (idle_minutes === void 0) return backlog_budget.DEFAULT_IDLE_MS;
2557
+ if (idle_minutes === NO_IDLE_MINUTES) return void 0;
2558
+ return idle_minutes * backlog_budget.MS_PER_MINUTE;
2435
2559
  }
2436
- function read_root_seed(content) {
2437
- return ports.resolve_seed({ [PORT_SEED_KEY]: read_assignment(content, PORT_SEED_KEY) });
2560
+ function is_watch_readable(values) {
2561
+ if (to_idle_ms(to_count(text_of(values.idle))) === void 0) return true;
2562
+ return text_of(values.active) !== void 0;
2438
2563
  }
2439
- function read_lane_seat(content) {
2440
- const raw = read_assignment(content, LANE_SEAT_KEY);
2441
- return raw === void 0 ? void 0 : ports.resolve_lane({ [LANE_SEAT_KEY]: raw });
2564
+ function are_values_readable(values) {
2565
+ return is_readable(text_of(values.active), to_time_ms(text_of(values.active))) && is_watch_readable(values) && are_counts_readable(values, counts_of(values));
2442
2566
  }
2443
- function read_lane_ports(content) {
2444
- const environment = {
2445
- [PORT_SEED_KEY]: read_assignment(content, PORT_SEED_KEY),
2446
- [LANE_SEAT_KEY]: read_assignment(content, LANE_SEAT_KEY)
2447
- };
2567
+ function input_of(values, base) {
2568
+ const counts = counts_of(values);
2448
2569
  return {
2449
- development: ports.resolve_development_port(environment),
2450
- preview: ports.resolve_preview_port(environment)
2570
+ ...base,
2571
+ active_at_ms: to_time_ms(text_of(values.active)) ?? base.started_at_ms,
2572
+ merged: counts.merged ?? 0,
2573
+ running: counts.running ?? 0,
2574
+ max_issues: counts.max,
2575
+ idle_budget_ms: to_idle_ms(counts.idle)
2451
2576
  };
2452
2577
  }
2453
- function read_lane_output(content) {
2454
- const line = split_lines(content).findLast((entry) => is_output_line(entry));
2455
- const raw = line === void 0 ? void 0 : assignment_value(line);
2456
- return raw === void 0 || raw.length === 0 ? void 0 : raw;
2457
- }
2458
- function read_lane_profile(content) {
2459
- return agent_role_profile.parse({
2460
- provider: read_assignment(content, LANE_PROVIDER_KEY),
2461
- role: read_assignment(content, LANE_ROLE_KEY),
2462
- model: read_assignment(content, LANE_MODEL_KEY),
2463
- effort: read_assignment(content, LANE_EFFORT_KEY)
2464
- });
2578
+ function build_input(values, now_ms) {
2579
+ const answer = to_answer(text_of(values.answer));
2580
+ if (answer === void 0) return void 0;
2581
+ const started_at_ms = to_time_ms(text_of(values.started));
2582
+ if (started_at_ms === void 0) return void 0;
2583
+ if (!are_values_readable(values)) return void 0;
2584
+ return input_of(values, { answer, now_ms, started_at_ms });
2465
2585
  }
2466
- function replace_assignment(lines, keep, assignment, key) {
2467
- return lines.flatMap((entry, index) => {
2468
- if (index === keep) return [assignment];
2469
- return is_key_line(entry, key) ? [] : [entry];
2470
- });
2586
+ function refuse() {
2587
+ console.error(USAGE);
2588
+ return FAILURE_EXIT_CODE2;
2471
2589
  }
2472
- function upsert_assignment(content, key, value) {
2473
- const assignment = `${key}${ASSIGNMENT_SEPARATOR}${value}`;
2474
- const lines = split_lines(content);
2475
- const last = lines.findLastIndex((entry) => is_key_line(entry, key));
2476
- const body = last === -1 ? [...lines, assignment] : replace_assignment(lines, last, assignment, key);
2477
- return `${body.join("\n")}
2478
- `;
2590
+ function run(argv, now_ms = Date.now()) {
2591
+ const values = read_arguments(argv);
2592
+ if (values === void 0) return refuse();
2593
+ const input = build_input(values, now_ms);
2594
+ if (input === void 0) return refuse();
2595
+ const { verdict, reason } = backlog_budget.decide(input);
2596
+ if (values.json === true) {
2597
+ console.info(
2598
+ JSON.stringify({ [JSON_KEY]: verdict, reason, is_finish: backlog_budget.is_finish(input) })
2599
+ );
2600
+ return SUCCESS_EXIT_CODE;
2601
+ }
2602
+ path_decision.print_decision(JSON_KEY, verdict, reason, false);
2603
+ return SUCCESS_EXIT_CODE;
2479
2604
  }
2480
- function lane_file_content(root_content, seat) {
2481
- return upsert_assignment(root_content, LANE_SEAT_KEY, String(seat));
2605
+ function main(argv) {
2606
+ process.exitCode = run(argv);
2482
2607
  }
2483
- function with_lane_output(content, output, profile) {
2484
- const recorded = upsert_assignment(content, LANE_OUTPUT_KEY, `"${output}"`);
2485
- if (profile === void 0) return recorded;
2486
- const with_provider = upsert_assignment(recorded, LANE_PROVIDER_KEY, profile.provider);
2487
- const with_role = upsert_assignment(with_provider, LANE_ROLE_KEY, profile.role);
2488
- const with_model = upsert_assignment(with_role, LANE_MODEL_KEY, profile.model);
2489
- return upsert_assignment(with_model, LANE_EFFORT_KEY, profile.effort);
2490
- }
2491
- var lane_environment = {
2492
- LANE_OUTPUT_KEY,
2493
- LANE_PROVIDER_KEY,
2494
- LANE_ROLE_KEY,
2495
- is_output_line,
2496
- is_seat_line,
2497
- is_seed_line,
2498
- lane_file_content,
2499
- read_lane_output,
2500
- read_lane_profile,
2501
- read_lane_ports,
2502
- read_lane_seat,
2503
- read_root_seed,
2504
- with_lane_output
2608
+ var backlog_budget_cli = {
2609
+ JSON_KEY,
2610
+ OPTIONS,
2611
+ SUCCESS_EXIT_CODE,
2612
+ USAGE,
2613
+ build_input,
2614
+ main,
2615
+ read_arguments,
2616
+ run,
2617
+ to_answer,
2618
+ to_count,
2619
+ to_time_ms
2505
2620
  };
2621
+ if (process.argv[1] === fileURLToPath(import.meta.url)) main(process.argv.slice(ARGV_OFFSET));
2506
2622
 
2507
- // scripts/lane/lane-registry.ts
2508
- var WORKTREE_PREFIX = "worktree ";
2509
- var BRANCH_PREFIX = "branch refs/heads/";
2510
- var BLOCK_SEPARATOR = "\n\n";
2511
- var ISSUE_PATTERN = /^[1-9]\d*$/u;
2512
- function line_value(lines, prefix) {
2513
- return lines.find((line) => line.startsWith(prefix))?.slice(prefix.length);
2623
+ // scripts/run/run-invocation.ts
2624
+ var BACKLOG_COMMAND = "backlogrun";
2625
+ var KNOWN_FLAGS2 = ["--max", "--idle"];
2626
+ var ONLY_FLAG = "--only";
2627
+ var ISSUE_PREFIX = "#";
2628
+ var TOKEN_SEPARATOR = " ";
2629
+ var FIRST_TOKEN = 0;
2630
+ var FIRST_ARGUMENT_INDEX = 1;
2631
+ var VALUE_OFFSET = 1;
2632
+ var FLAG_PAIR_LENGTH = 2;
2633
+ var ONLY_STEP = 1;
2634
+ var EMPTY_LENGTH2 = 0;
2635
+ var NOT_FOUND2 = -1;
2636
+ function tokens_of(invocation) {
2637
+ return invocation.split(TOKEN_SEPARATOR).filter((token) => token.length > EMPTY_LENGTH2);
2514
2638
  }
2515
- function seat_from_content(content) {
2516
- const seat = lane_environment.read_lane_seat(content);
2517
- if (seat === void 0) return void 0;
2518
- const port_pair = lane_environment.read_lane_ports(content);
2519
- return { seat, development_port: port_pair.development, preview_port: port_pair.preview };
2639
+ function flag_token(token, raw) {
2640
+ const flag = KNOWN_FLAGS2.find((known) => known === token);
2641
+ const value = backlog_budget_cli.to_count(raw);
2642
+ if (flag === void 0 || value === void 0) return void 0;
2643
+ return `${flag} ${String(value)}`;
2520
2644
  }
2521
- var EMPTY_SEAT = {
2522
- seat: void 0,
2523
- development_port: void 0,
2524
- preview_port: void 0
2645
+ function read_flag(tokens, index) {
2646
+ const token = tokens[index] ?? "";
2647
+ if (token === ONLY_FLAG) return { part: ONLY_FLAG, advance: ONLY_STEP };
2648
+ const part = flag_token(token, tokens[index + VALUE_OFFSET]);
2649
+ return part === void 0 ? void 0 : { part, advance: FLAG_PAIR_LENGTH };
2650
+ }
2651
+ function flag_tokens(tokens, start) {
2652
+ const rendered = [];
2653
+ let index = start;
2654
+ while (index < tokens.length) {
2655
+ const read2 = read_flag(tokens, index);
2656
+ if (read2 === void 0) return void 0;
2657
+ rendered.push(read2.part);
2658
+ index += read2.advance;
2659
+ }
2660
+ return rendered;
2661
+ }
2662
+ function issue_of(token) {
2663
+ if (!token.startsWith(ISSUE_PREFIX)) return void 0;
2664
+ const digits = token.slice(ISSUE_PREFIX.length);
2665
+ if (!run_issue_number.ISSUE_NUMBER_PATTERN.test(digits)) return void 0;
2666
+ const issue = Number(digits);
2667
+ return Number.isSafeInteger(issue) ? issue : void 0;
2668
+ }
2669
+ function take_leading_prefixed(args) {
2670
+ const stop = args.findIndex((token) => !token.startsWith(ISSUE_PREFIX));
2671
+ return args.slice(FIRST_TOKEN, stop === NOT_FOUND2 ? args.length : stop);
2672
+ }
2673
+ function leading_issues(tokens) {
2674
+ const issues = [];
2675
+ const prefixed = take_leading_prefixed(tokens.slice(FIRST_ARGUMENT_INDEX));
2676
+ for (const token of prefixed) {
2677
+ const issue = issue_of(token);
2678
+ if (issue === void 0) return void 0;
2679
+ issues.push(issue);
2680
+ }
2681
+ return issues;
2682
+ }
2683
+ function issue_token(issue) {
2684
+ return `${ISSUE_PREFIX}${String(issue)}`;
2685
+ }
2686
+ function joined(command, rendered) {
2687
+ return [command, ...rendered].join(TOKEN_SEPARATOR);
2688
+ }
2689
+ function rebuilt_backlog(tokens) {
2690
+ const issues = leading_issues(tokens);
2691
+ if (issues === void 0) return void 0;
2692
+ const flags = flag_tokens(tokens, FIRST_ARGUMENT_INDEX + issues.length);
2693
+ if (flags === void 0) return void 0;
2694
+ return joined(BACKLOG_COMMAND, [...issues.map((issue) => issue_token(issue)), ...flags]);
2695
+ }
2696
+ function rebuild(invocation) {
2697
+ const tokens = tokens_of(invocation);
2698
+ if (tokens[FIRST_TOKEN] !== BACKLOG_COMMAND) return void 0;
2699
+ return rebuilt_backlog(tokens);
2700
+ }
2701
+ function issue_numbers(invocation) {
2702
+ const tokens = tokens_of(invocation);
2703
+ if (tokens[FIRST_TOKEN] !== BACKLOG_COMMAND) return void 0;
2704
+ const issues = leading_issues(tokens);
2705
+ if (issues === void 0 || issues.length === EMPTY_LENGTH2) return void 0;
2706
+ return issues;
2707
+ }
2708
+ function has_only(invocation) {
2709
+ const tokens = tokens_of(invocation);
2710
+ if (tokens[FIRST_TOKEN] !== BACKLOG_COMMAND) return false;
2711
+ return tokens.slice(FIRST_ARGUMENT_INDEX).includes(ONLY_FLAG);
2712
+ }
2713
+ var run_invocation = {
2714
+ has_only,
2715
+ issue_numbers,
2716
+ rebuild
2525
2717
  };
2526
- function held_seat(content) {
2527
- if (content === void 0) return EMPTY_SEAT;
2528
- return seat_from_content(content) ?? EMPTY_SEAT;
2718
+
2719
+ // scripts/run/run-carry.ts
2720
+ var CARRY_PREFIX = "josh-run-carry-";
2721
+ var REPOSITORY_DIRECTORY_INDEX = 1;
2722
+ var CARRY_MAX_AGE_HOURS = backlog_budget.WHOLE_RUN_BUDGET_HOURS;
2723
+ var CARRY_MAX_AGE_MS = backlog_budget.WHOLE_RUN_BUDGET_MS;
2724
+ var NO_INCREMENT3 = 0;
2725
+ var MAX_CUTS = 6;
2726
+ var END_COMMAND2 = "pnpm josh run:carry --end";
2727
+ var RESUME_COMMAND = "pnpm josh run:carry --resume";
2728
+ var READ_COMMAND2 = "pnpm josh run:carry --json";
2729
+ var OWNER_ARGUMENT = '--owner "$PPID"';
2730
+ var NONE_READ2 = { kind: "none" };
2731
+ var UNREADABLE_READ3 = { kind: "unreadable" };
2732
+ var NO_OWNER = {};
2733
+ var run_carry_schema = z5.object({
2734
+ invocation: z5.string(),
2735
+ started_at: z5.string(),
2736
+ merged: z5.number(),
2737
+ merged_issues: z5.array(z5.number()).optional(),
2738
+ filed: z5.number(),
2739
+ cuts: z5.number(),
2740
+ // Defaulted, so a record written before this field existed parses with a zero streak rather than
2741
+ // failing to read — the same backward-compatibility the optional owner fields below carry.
2742
+ failures: z5.number().default(0),
2743
+ // Defaulted for the same backward-compatibility as `failures` (joshuafolkken/kit#2240).
2744
+ outages: z5.number().default(0),
2745
+ // Optional, so a record written before the outage fold existed still parses (joshuafolkken/kit#2317).
2746
+ last_outage_at: z5.string().optional(),
2747
+ // Optional, so a record written by the previous shape still parses. Read as "no owner declared",
2748
+ // which is the not-provably-live answer rather than a live one.
2749
+ owner_pid: z5.number().optional(),
2750
+ owner_start: z5.string().optional(),
2751
+ is_handed_off: z5.boolean().optional(),
2752
+ done: z5.array(z5.number()).optional(),
2753
+ // Optional, so a record written before the retrospective existed still parses (joshuafolkken/kit#2328).
2754
+ retrospective: z5.boolean().optional()
2755
+ });
2756
+ function carry_path(git_directory) {
2757
+ return stamp_file.stamp_path(CARRY_PREFIX, git_directory);
2529
2758
  }
2530
- function read_environment(directory) {
2759
+ async function repository_directory() {
2760
+ const directories = await git_command.git_directories();
2761
+ return directories[REPOSITORY_DIRECTORY_INDEX];
2762
+ }
2763
+ function parse_carry(raw) {
2531
2764
  try {
2532
- return readFileSync4(path3.join(directory, ENV_FILE_NAME), "utf8");
2765
+ const parsed = run_carry_schema.safeParse(JSON.parse(raw));
2766
+ return parsed.success ? parsed.data : void 0;
2533
2767
  } catch {
2534
2768
  return void 0;
2535
2769
  }
2536
2770
  }
2537
- function branch_issue(branch2) {
2538
- if (!branch2?.endsWith(lane_paths.LANE_BRANCH_SUFFIX)) return void 0;
2539
- const issue = branch2.slice(0, -lane_paths.LANE_BRANCH_SUFFIX.length);
2540
- return ISSUE_PATTERN.test(issue) ? issue : void 0;
2541
- }
2542
- function is_gone(directory) {
2543
- return !existsSync2(directory);
2544
- }
2545
- function build_lane(issue, branch2, directory, is_stranded) {
2546
- const content = is_stranded ? void 0 : read_environment(directory);
2547
- const held = held_seat(content);
2548
- const output = content === void 0 ? void 0 : lane_environment.read_lane_output(content);
2549
- const profile = content === void 0 ? void 0 : lane_environment.read_lane_profile(content);
2550
- return {
2551
- issue,
2552
- branch: branch2,
2553
- directory,
2554
- seat: held.seat,
2555
- development_port: held.development_port,
2556
- preview_port: held.preview_port,
2557
- output,
2558
- profile,
2559
- is_stranded
2560
- };
2771
+ function is_expired2(carry, now) {
2772
+ const started = Date.parse(carry.started_at);
2773
+ if (Number.isNaN(started)) return true;
2774
+ return now.getTime() - started > CARRY_MAX_AGE_MS;
2561
2775
  }
2562
- function is_lane_directory(root, issue, directory) {
2563
- return directory === lane_paths.lane_directory(root, issue);
2776
+ function classify4(raw, now) {
2777
+ if (raw === void 0) return NONE_READ2;
2778
+ const carry = parse_carry(raw);
2779
+ if (carry === void 0) return UNREADABLE_READ3;
2780
+ return { kind: is_expired2(carry, now) ? "expired" : "carried", carry };
2564
2781
  }
2565
- function parse_block(block, root) {
2566
- const lines = block.split("\n");
2567
- const directory = line_value(lines, WORKTREE_PREFIX);
2568
- const branch2 = line_value(lines, BRANCH_PREFIX);
2569
- const issue = branch_issue(branch2);
2570
- if (directory === void 0 || branch2 === void 0 || issue === void 0) return void 0;
2571
- if (!is_lane_directory(root, issue, directory)) return void 0;
2572
- return build_lane(issue, branch2, directory, is_gone(directory));
2782
+ function read_carry(target, now = /* @__PURE__ */ new Date()) {
2783
+ return classify4(stamp_file.read_stamp_text(target), now);
2573
2784
  }
2574
- function is_lane(lane) {
2575
- return lane !== void 0;
2785
+ function owner_of(pid, read2 = process_identity.read_start) {
2786
+ return { pid, start: read2(pid) };
2576
2787
  }
2577
- async function worktree_blocks() {
2578
- const listing = await git_worktree.worktree_list();
2579
- return listing.split(BLOCK_SEPARATOR);
2788
+ function fresh_carry(invocation, owner, now) {
2789
+ return {
2790
+ invocation,
2791
+ started_at: now.toISOString(),
2792
+ merged: NO_INCREMENT3,
2793
+ filed: NO_INCREMENT3,
2794
+ cuts: NO_INCREMENT3,
2795
+ failures: NO_INCREMENT3,
2796
+ outages: NO_INCREMENT3,
2797
+ owner_pid: owner.pid,
2798
+ owner_start: owner.start
2799
+ };
2580
2800
  }
2581
- async function main_repository_root() {
2582
- return repo_discovery.main_worktree(await git_command.repository_root());
2801
+ function begin_carry(target, invocation, owner = NO_OWNER, now = /* @__PURE__ */ new Date()) {
2802
+ const carry = fresh_carry(invocation, owner, now);
2803
+ return stamp_file.create_stamp(target, carry) ? carry : void 0;
2583
2804
  }
2584
- async function list_lanes() {
2585
- const root = lane_paths.lane_root(await main_repository_root());
2586
- const blocks = await worktree_blocks();
2587
- const lanes = blocks.map((block) => parse_block(block, root)).filter(is_lane);
2588
- return lanes.toSorted((left, right) => Number(left.issue) - Number(right.issue));
2805
+ function replace_carry(target, invocation, owner = NO_OWNER, now = /* @__PURE__ */ new Date()) {
2806
+ stamp_file.remove_stamp(target);
2807
+ return begin_carry(target, invocation, owner, now);
2589
2808
  }
2590
- function used_seats(lanes) {
2591
- return lanes.map((lane) => lane.seat).filter((seat) => seat !== void 0);
2809
+ function adopt_carry(target, carry, owner = NO_OWNER) {
2810
+ const next = {
2811
+ ...carry,
2812
+ owner_pid: owner.pid,
2813
+ owner_start: owner.start,
2814
+ is_handed_off: false
2815
+ };
2816
+ stamp_file.remove_stamp(target);
2817
+ return stamp_file.create_stamp(target, next) ? next : void 0;
2592
2818
  }
2593
- function unreadable_lanes(lanes) {
2594
- return lanes.filter((lane) => !lane.is_stranded && lane.seat === void 0);
2819
+ function is_owner_live(carry) {
2820
+ return process_identity.is_same_process(carry.owner_pid, carry.owner_start) !== false;
2595
2821
  }
2596
- function find_lane(lanes, issue) {
2597
- return lanes.find((lane) => lane.issue === issue);
2822
+ function is_owned_by(carry, owner) {
2823
+ return carry.owner_pid === owner.pid && carry.owner_start === owner.start;
2598
2824
  }
2599
- async function find_open_lane(issue) {
2600
- return find_lane(await list_lanes(), issue);
2825
+ function is_foreign_live_owner(carry, owner) {
2826
+ return is_owner_live(carry) && !is_owned_by(carry, owner);
2601
2827
  }
2602
- var lane_registry = {
2603
- find_lane,
2604
- find_open_lane,
2605
- list_lanes,
2606
- main_repository_root,
2607
- parse_block,
2608
- read_environment,
2609
- unreadable_lanes,
2610
- used_seats
2611
- };
2612
-
2613
- // scripts/backlog/backlog-budget-cli.ts
2614
- import { fileURLToPath } from "node:url";
2615
- import { parseArgs } from "node:util";
2616
-
2617
- // scripts/git/changed-paths.ts
2618
- function to_paths(raw) {
2619
- return raw.split("\n").map((line) => line.trim()).filter((line) => line !== "");
2828
+ function is_handed_off_to(carry, invocation) {
2829
+ return carry.is_handed_off === true && carry.invocation === invocation;
2620
2830
  }
2621
- async function read_changed_paths(is_staged) {
2622
- if (is_staged) return to_paths(await git_command.diff_cached_names());
2623
- const tracked = to_paths(await git_command.diff_main_names());
2624
- const untracked = to_paths(await git_command.untracked_names());
2625
- return [...tracked, ...untracked];
2831
+ function classify_claim(carry, request) {
2832
+ if (is_handed_off_to(carry, request.invocation)) return "resume";
2833
+ if (is_foreign_live_owner(carry, request.owner)) return "busy";
2834
+ if (carry.invocation !== request.invocation) return "mismatch";
2835
+ return request.is_adoption ? "resume" : "standing";
2626
2836
  }
2627
- var changed_paths = { read_changed_paths, to_paths };
2628
-
2629
- // scripts/josh/path-decision.ts
2630
- var STAGED_FLAG = "--staged";
2631
- var JSON_FLAG = "--json";
2632
- var KNOWN_FLAGS = [STAGED_FLAG, JSON_FLAG];
2633
- var FAILURE_EXIT_CODE = 1;
2634
- var MAX_LISTED_PATHS = 5;
2635
- function has_unknown_flag(argv, known) {
2636
- return argv.some((argument) => !known.includes(argument));
2837
+ function is_count_refused(carry, owner) {
2838
+ return carry.is_handed_off === true || is_foreign_live_owner(carry, owner);
2637
2839
  }
2638
- function parse_options(argv) {
2639
- if (has_unknown_flag(argv, KNOWN_FLAGS)) return void 0;
2640
- return { is_staged: argv.includes(STAGED_FLAG), is_json: argv.includes(JSON_FLAG) };
2840
+ function is_at_cut_cap(carry) {
2841
+ return carry.cuts >= MAX_CUTS;
2641
2842
  }
2642
- function format_path_list(paths) {
2643
- const listed = paths.slice(0, MAX_LISTED_PATHS).join(", ");
2644
- if (paths.length <= MAX_LISTED_PATHS) return listed;
2645
- return `${listed}, +${String(paths.length - MAX_LISTED_PATHS)} more`;
2843
+ function end_carry(target) {
2844
+ stamp_file.remove_stamp(target);
2646
2845
  }
2647
- function print_decision(key, answer, reason, is_json) {
2648
- if (is_json) {
2649
- console.info(JSON.stringify({ [key]: answer, reason }));
2650
- return;
2651
- }
2652
- console.info(answer);
2653
- console.error(reason);
2846
+ function retrospective_done_of(read2) {
2847
+ if (read2.kind !== "carried" && read2.kind !== "expired") return false;
2848
+ return read2.carry.retrospective === true;
2654
2849
  }
2655
- async function run_path_decision(argv, command) {
2656
- const options = parse_options(argv);
2657
- if (options === void 0) {
2658
- console.error(command.usage);
2659
- return FAILURE_EXIT_CODE;
2660
- }
2661
- const paths = await changed_paths.read_changed_paths(options.is_staged);
2662
- const answer = command.decide(paths);
2663
- print_decision(command.key, answer, command.explain(paths, answer), options.is_json);
2664
- return 0;
2850
+ function remaining_of(carry) {
2851
+ if (carry === void 0) return void 0;
2852
+ const declared = run_invocation.issue_numbers(carry.invocation);
2853
+ if (declared === void 0) return void 0;
2854
+ const done = carry.done ?? [];
2855
+ return declared.filter((issue) => !done.includes(issue));
2665
2856
  }
2666
- var path_decision = {
2667
- format_path_list,
2668
- has_unknown_flag,
2669
- JSON_FLAG,
2670
- KNOWN_FLAGS,
2671
- MAX_LISTED_PATHS,
2672
- parse_options,
2673
- print_decision,
2674
- run_path_decision,
2675
- STAGED_FLAG
2676
- };
2677
-
2678
- // scripts/backlog/backlog-budget-cli.ts
2679
- var ARGV_OFFSET = 2;
2680
- var SUCCESS_EXIT_CODE = 0;
2681
- var FAILURE_EXIT_CODE2 = 1;
2682
- var JSON_KEY = "budget";
2683
- var USAGE = `Usage: josh backlog:budget --answer <${backlog_budget.ANSWERS.join("|")}> --started <ISO-8601> [--active <ISO-8601>, required unless the watch is off] [--merged <count>] [--running <count>] [--max <count>] [--idle <minutes>, 0 to turn the watch off] [--json]`;
2684
- var OPTIONS = {
2685
- active: { type: "string" },
2686
- answer: { type: "string" },
2687
- idle: { type: "string" },
2688
- json: { type: "boolean" },
2689
- max: { type: "string" },
2690
- merged: { type: "string" },
2691
- running: { type: "string" },
2692
- started: { type: "string" }
2693
- };
2694
- function read_arguments(argv) {
2695
- try {
2696
- return parseArgs({ args: [...argv], options: OPTIONS, strict: true }).values;
2697
- } catch {
2698
- return void 0;
2699
- }
2857
+ function done_note(carry) {
2858
+ const done = carry.done ?? [];
2859
+ return done.length === 0 ? "" : `, issues ${done.join(", ")} done`;
2700
2860
  }
2701
- function text_of(value) {
2702
- return typeof value === "string" ? value : void 0;
2861
+ function describe_carry(carry) {
2862
+ return `${carry.invocation} started ${carry.started_at}; ${String(carry.merged)} merged, ${String(carry.filed)} filed, ${String(carry.failures)} failed in a row, ${String(carry.outages)} outage(s) in a row, ${String(carry.cuts)} cut(s) crossed${done_note(carry)}`;
2703
2863
  }
2704
- function to_answer(raw) {
2705
- return backlog_budget.ANSWERS.find((answer) => answer === raw);
2864
+ function expired_message(carry) {
2865
+ return `The ${String(CARRY_MAX_AGE_HOURS)}-hour whole-run bound is spent: ${describe_carry(carry)}. Clear it with \`${END_COMMAND2}\`.`;
2706
2866
  }
2707
- function to_time_ms(raw) {
2708
- if (raw === void 0) return void 0;
2709
- const parsed = Date.parse(raw);
2710
- return Number.isNaN(parsed) ? void 0 : parsed;
2867
+ function mismatch_message(carry, invocation) {
2868
+ return `A different invocation is carried here \u2014 ${describe_carry(carry)}. This one is \`${invocation}\`. End the standing record with \`${END_COMMAND2}\` once you know its run is over, then begin again.`;
2711
2869
  }
2712
- var COUNT_PATTERN = /^\d+$/u;
2713
- function to_count(raw) {
2714
- if (raw === void 0 || !COUNT_PATTERN.test(raw)) return void 0;
2715
- const parsed = Number(raw);
2716
- return Number.isSafeInteger(parsed) ? parsed : void 0;
2870
+ function busy_message2(carry) {
2871
+ return `A live run holds this budget \u2014 ${describe_carry(carry)}, owner pid ${String(carry.owner_pid)} still running. Nothing was established. Wait for that run, or end its record with \`${END_COMMAND2}\` once you know it is over.`;
2717
2872
  }
2718
- function is_readable(raw, parsed) {
2719
- return raw === void 0 || parsed !== void 0;
2873
+ function standing_message(carry) {
2874
+ return `A run record is standing here that no cut handed off \u2014 ${describe_carry(carry)}. Nothing was established. Carry that budget with \`${RESUME_COMMAND} "${carry.invocation}" ${OWNER_ARGUMENT}\`, or discard it with \`${END_COMMAND2}\` and begin again.`;
2720
2875
  }
2721
- var COUNT_NAMES = ["idle", "max", "merged", "running"];
2722
- function counts_of(values) {
2723
- return {
2724
- idle: to_count(text_of(values.idle)),
2725
- max: to_count(text_of(values.max)),
2726
- merged: to_count(text_of(values.merged)),
2727
- running: to_count(text_of(values.running))
2728
- };
2876
+ function count_refused_message(carry) {
2877
+ if (carry.is_handed_off === true) {
2878
+ return `This budget was handed off at a cut and is waiting for its successor \u2014 ${describe_carry(carry)}. Nothing was counted; the cutting session must not advance a handed-off budget.`;
2879
+ }
2880
+ return `A live run owns this budget \u2014 ${describe_carry(carry)}, owner pid ${String(carry.owner_pid)} still running. Nothing was counted; only its owner advances the budget.`;
2729
2881
  }
2730
- function are_counts_readable(values, counts) {
2731
- return COUNT_NAMES.every((name) => is_readable(text_of(values[name]), counts[name]));
2882
+ function lost_message() {
2883
+ return `Another process established a run record here first, so nothing was established. Read it with \`${READ_COMMAND2}\`.`;
2732
2884
  }
2733
- var NO_IDLE_MINUTES = 0;
2734
- function to_idle_ms(idle_minutes) {
2735
- if (idle_minutes === void 0) return backlog_budget.DEFAULT_IDLE_MS;
2736
- if (idle_minutes === NO_IDLE_MINUTES) return void 0;
2737
- return idle_minutes * backlog_budget.MS_PER_MINUTE;
2885
+ function nothing_to_resume_message() {
2886
+ return `Nothing is carried here, so there is no budget to resume. Begin one with \`pnpm josh run:carry --begin "<invocation>"\`.`;
2738
2887
  }
2739
- function is_watch_readable(values) {
2740
- if (to_idle_ms(to_count(text_of(values.idle))) === void 0) return true;
2741
- return text_of(values.active) !== void 0;
2888
+ function unreadable_message3() {
2889
+ return `A run record is here but could not be read; clear it with \`${END_COMMAND2}\` once you know no run is using it.`;
2742
2890
  }
2743
- function are_values_readable(values) {
2744
- return is_readable(text_of(values.active), to_time_ms(text_of(values.active))) && is_watch_readable(values) && are_counts_readable(values, counts_of(values));
2891
+ function unknown_message3() {
2892
+ return "This repository\u2019s git directory could not be read, so no run record was established.";
2745
2893
  }
2746
- function input_of(values, base) {
2747
- const counts = counts_of(values);
2748
- return {
2749
- ...base,
2750
- active_at_ms: to_time_ms(text_of(values.active)) ?? base.started_at_ms,
2751
- merged: counts.merged ?? 0,
2752
- running: counts.running ?? 0,
2753
- max_issues: counts.max,
2754
- idle_budget_ms: to_idle_ms(counts.idle)
2755
- };
2894
+ var run_carry = {
2895
+ CARRY_MAX_AGE_HOURS,
2896
+ CARRY_MAX_AGE_MS,
2897
+ END_COMMAND: END_COMMAND2,
2898
+ MAX_CUTS,
2899
+ NO_OWNER,
2900
+ RESUME_COMMAND,
2901
+ adopt_carry,
2902
+ apply_change: run_carry_change.apply_change,
2903
+ begin_carry,
2904
+ busy_message: busy_message2,
2905
+ carry_path,
2906
+ classify: classify4,
2907
+ classify_claim,
2908
+ count_refused_message,
2909
+ is_at_cut_cap,
2910
+ describe_carry,
2911
+ end_carry,
2912
+ expired_message,
2913
+ mismatch_message,
2914
+ fresh_carry,
2915
+ is_count_refused,
2916
+ is_expired: is_expired2,
2917
+ is_foreign_live_owner,
2918
+ is_owner_live,
2919
+ lost_message,
2920
+ nothing_to_resume_message,
2921
+ owner_of,
2922
+ parse_carry,
2923
+ read_carry,
2924
+ remaining_of,
2925
+ replace_carry,
2926
+ repository_directory,
2927
+ retrospective_done_of,
2928
+ standing_message,
2929
+ unknown_message: unknown_message3,
2930
+ unreadable_message: unreadable_message3
2931
+ };
2932
+
2933
+ // scripts/lane/lane-registry.ts
2934
+ import { existsSync as existsSync2 } from "node:fs";
2935
+ import path3 from "node:path";
2936
+
2937
+ // scripts/git/git-ls-remote.ts
2938
+ var LS_REMOTE_COMMAND = "ls-remote";
2939
+ var HEADS_FLAG = "--heads";
2940
+ var ORIGIN_REMOTE = "origin";
2941
+ var HEADS_REF_PREFIX = "refs/heads/";
2942
+ function ls_remote_branch_arguments(branch_name) {
2943
+ return [LS_REMOTE_COMMAND, HEADS_FLAG, ORIGIN_REMOTE, `${HEADS_REF_PREFIX}${branch_name}`];
2756
2944
  }
2757
- function build_input(values, now_ms) {
2758
- const answer = to_answer(text_of(values.answer));
2759
- if (answer === void 0) return void 0;
2760
- const started_at_ms = to_time_ms(text_of(values.started));
2761
- if (started_at_ms === void 0) return void 0;
2762
- if (!are_values_readable(values)) return void 0;
2763
- return input_of(values, { answer, now_ms, started_at_ms });
2945
+
2946
+ // scripts/git/repository-lock.ts
2947
+ import { randomUUID as randomUUID2 } from "node:crypto";
2948
+ import { linkSync, renameSync, rmSync as rmSync2 } from "node:fs";
2949
+ import { setTimeout as sleep } from "node:timers/promises";
2950
+
2951
+ // scripts/git/git-common-directory.ts
2952
+ import { execFileSync as execFileSync4 } from "node:child_process";
2953
+ var OWN_GIT_DIRECTORY = 0;
2954
+ var COMMON_GIT_DIRECTORY = 1;
2955
+ var PROBE_TIMEOUT_MS3 = 2e3;
2956
+ function read_directories(cwd) {
2957
+ const git = git_utilities.get_git_command_for_spawn();
2958
+ const output = execFileSync4(git, [...git_command.GIT_DIRECTORY_ARGUMENTS], {
2959
+ cwd,
2960
+ encoding: "utf8",
2961
+ stdio: ["ignore", "pipe", "ignore"],
2962
+ timeout: PROBE_TIMEOUT_MS3
2963
+ });
2964
+ return output.trim().split("\n");
2764
2965
  }
2765
- function refuse() {
2766
- console.error(USAGE);
2767
- return FAILURE_EXIT_CODE2;
2966
+ function select_linked(directories) {
2967
+ const own = directories[OWN_GIT_DIRECTORY];
2968
+ const common = directories[COMMON_GIT_DIRECTORY];
2969
+ return own !== void 0 && common !== void 0 && own !== common ? common : void 0;
2768
2970
  }
2769
- function run(argv, now_ms = Date.now()) {
2770
- const values = read_arguments(argv);
2771
- if (values === void 0) return refuse();
2772
- const input = build_input(values, now_ms);
2773
- if (input === void 0) return refuse();
2774
- const { verdict, reason } = backlog_budget.decide(input);
2775
- if (values.json === true) {
2776
- console.info(
2777
- JSON.stringify({ [JSON_KEY]: verdict, reason, is_finish: backlog_budget.is_finish(input) })
2778
- );
2779
- return SUCCESS_EXIT_CODE;
2971
+ function resolve2(cwd) {
2972
+ try {
2973
+ return select_linked(read_directories(cwd));
2974
+ } catch {
2975
+ return void 0;
2780
2976
  }
2781
- path_decision.print_decision(JSON_KEY, verdict, reason, false);
2782
- return SUCCESS_EXIT_CODE;
2783
2977
  }
2784
- function main(argv) {
2785
- process.exitCode = run(argv);
2978
+ function repository(cwd) {
2979
+ try {
2980
+ return read_directories(cwd)[COMMON_GIT_DIRECTORY];
2981
+ } catch {
2982
+ return void 0;
2983
+ }
2786
2984
  }
2787
- var backlog_budget_cli = {
2788
- JSON_KEY,
2789
- OPTIONS,
2790
- SUCCESS_EXIT_CODE,
2791
- USAGE,
2792
- build_input,
2793
- main,
2794
- read_arguments,
2795
- run,
2796
- to_answer,
2797
- to_count,
2798
- to_time_ms
2799
- };
2800
- if (process.argv[1] === fileURLToPath(import.meta.url)) main(process.argv.slice(ARGV_OFFSET));
2985
+ var git_common_directory = { repository, resolve: resolve2, select_linked };
2801
2986
 
2802
- // scripts/run/run-invocation.ts
2803
- var BACKLOG_COMMAND = "backlogrun";
2804
- var KNOWN_FLAGS2 = ["--max", "--idle"];
2805
- var ONLY_FLAG = "--only";
2806
- var ISSUE_PREFIX = "#";
2807
- var TOKEN_SEPARATOR = " ";
2808
- var FIRST_TOKEN = 0;
2809
- var FIRST_ARGUMENT_INDEX = 1;
2810
- var VALUE_OFFSET = 1;
2811
- var FLAG_PAIR_LENGTH = 2;
2812
- var ONLY_STEP = 1;
2813
- var EMPTY_LENGTH2 = 0;
2814
- var NOT_FOUND2 = -1;
2815
- function tokens_of(invocation) {
2816
- return invocation.split(TOKEN_SEPARATOR).filter((token) => token.length > EMPTY_LENGTH2);
2817
- }
2818
- function flag_token(token, raw) {
2819
- const flag = KNOWN_FLAGS2.find((known) => known === token);
2820
- const value = backlog_budget_cli.to_count(raw);
2821
- if (flag === void 0 || value === void 0) return void 0;
2822
- return `${flag} ${String(value)}`;
2823
- }
2824
- function read_flag(tokens, index) {
2825
- const token = tokens[index] ?? "";
2826
- if (token === ONLY_FLAG) return { part: ONLY_FLAG, advance: ONLY_STEP };
2827
- const part = flag_token(token, tokens[index + VALUE_OFFSET]);
2828
- return part === void 0 ? void 0 : { part, advance: FLAG_PAIR_LENGTH };
2987
+ // scripts/git/repository-lock.ts
2988
+ var POLL_INTERVAL_MS = 200;
2989
+ function lock_path(prefix, cwd = process.cwd()) {
2990
+ return stamp_file.stamp_path(prefix, git_common_directory.repository(cwd) ?? cwd);
2829
2991
  }
2830
- function flag_tokens(tokens, start) {
2831
- const rendered = [];
2832
- let index = start;
2833
- while (index < tokens.length) {
2834
- const read2 = read_flag(tokens, index);
2835
- if (read2 === void 0) return void 0;
2836
- rendered.push(read2.part);
2837
- index += read2.advance;
2992
+ function parse_owner(raw) {
2993
+ if (raw === void 0) return void 0;
2994
+ try {
2995
+ return JSON.parse(raw);
2996
+ } catch {
2997
+ return void 0;
2838
2998
  }
2839
- return rendered;
2840
2999
  }
2841
- function issue_of(token) {
2842
- if (!token.startsWith(ISSUE_PREFIX)) return void 0;
2843
- const digits = token.slice(ISSUE_PREFIX.length);
2844
- if (!run_issue_number.ISSUE_NUMBER_PATTERN.test(digits)) return void 0;
2845
- const issue = Number(digits);
2846
- return Number.isSafeInteger(issue) ? issue : void 0;
3000
+ function read_owner(target) {
3001
+ return parse_owner(stamp_file.read_stamp_text(target));
2847
3002
  }
2848
- function take_leading_prefixed(args) {
2849
- const stop = args.findIndex((token) => !token.startsWith(ISSUE_PREFIX));
2850
- return args.slice(FIRST_TOKEN, stop === NOT_FOUND2 ? args.length : stop);
3003
+ function is_owner_gone(owner) {
3004
+ return process_identity.is_same_process(owner.pid, owner.process_start) === false;
2851
3005
  }
2852
- function leading_issues(tokens) {
2853
- const issues = [];
2854
- const prefixed = take_leading_prefixed(tokens.slice(FIRST_ARGUMENT_INDEX));
2855
- for (const token of prefixed) {
2856
- const issue = issue_of(token);
2857
- if (issue === void 0) return void 0;
2858
- issues.push(issue);
3006
+ function restore(aside, target) {
3007
+ try {
3008
+ linkSync(aside, target);
3009
+ } catch {
2859
3010
  }
2860
- return issues;
2861
3011
  }
2862
- function issue_token(issue) {
2863
- return `${ISSUE_PREFIX}${String(issue)}`;
3012
+ function stale_record(target, is_gone2) {
3013
+ const raw = stamp_file.read_stamp_text(target);
3014
+ const owner = parse_owner(raw);
3015
+ return owner !== void 0 && is_gone2(owner) ? raw : void 0;
2864
3016
  }
2865
- function joined(command, rendered) {
2866
- return [command, ...rendered].join(TOKEN_SEPARATOR);
3017
+ function move_aside(target) {
3018
+ const aside = `${target}.${String(process.pid)}.${randomUUID2()}`;
3019
+ try {
3020
+ renameSync(target, aside);
3021
+ return aside;
3022
+ } catch {
3023
+ return void 0;
3024
+ }
2867
3025
  }
2868
- function rebuilt_backlog(tokens) {
2869
- const issues = leading_issues(tokens);
2870
- if (issues === void 0) return void 0;
2871
- const flags = flag_tokens(tokens, FIRST_ARGUMENT_INDEX + issues.length);
2872
- if (flags === void 0) return void 0;
2873
- return joined(BACKLOG_COMMAND, [...issues.map((issue) => issue_token(issue)), ...flags]);
3026
+ function clear_stale(target, is_gone2 = is_owner_gone) {
3027
+ const raw = stale_record(target, is_gone2);
3028
+ if (raw === void 0) return;
3029
+ const aside = move_aside(target);
3030
+ if (aside === void 0) return;
3031
+ if (stamp_file.read_stamp_text(aside) !== raw) restore(aside, target);
3032
+ rmSync2(aside, { force: true });
2874
3033
  }
2875
- function rebuild(invocation) {
2876
- const tokens = tokens_of(invocation);
2877
- if (tokens[FIRST_TOKEN] !== BACKLOG_COMMAND) return void 0;
2878
- return rebuilt_backlog(tokens);
3034
+ function claim(target) {
3035
+ clear_stale(target);
3036
+ return stamp_file.create_stamp(target, process_identity.own_fields());
2879
3037
  }
2880
- function issue_numbers(invocation) {
2881
- const tokens = tokens_of(invocation);
2882
- if (tokens[FIRST_TOKEN] !== BACKLOG_COMMAND) return void 0;
2883
- const issues = leading_issues(tokens);
2884
- if (issues === void 0 || issues.length === EMPTY_LENGTH2) return void 0;
2885
- return issues;
3038
+ function release(target) {
3039
+ const owner = read_owner(target);
3040
+ if (owner !== void 0 && process_identity.is_own_process(owner.pid, owner.process_start)) {
3041
+ stamp_file.remove_stamp(target);
3042
+ }
2886
3043
  }
2887
- function has_only(invocation) {
2888
- const tokens = tokens_of(invocation);
2889
- if (tokens[FIRST_TOKEN] !== BACKLOG_COMMAND) return false;
2890
- return tokens.slice(FIRST_ARGUMENT_INDEX).includes(ONLY_FLAG);
3044
+ async function acquire(target, max_wait_ms) {
3045
+ const deadline = Date.now() + max_wait_ms;
3046
+ while (!claim(target)) {
3047
+ if (Date.now() >= deadline) return false;
3048
+ await sleep(POLL_INTERVAL_MS);
3049
+ }
3050
+ return true;
2891
3051
  }
2892
- var run_invocation = {
2893
- has_only,
2894
- issue_numbers,
2895
- rebuild
2896
- };
2897
-
2898
- // scripts/run/run-carry.ts
2899
- import { z as z5 } from "zod";
3052
+ async function with_lock(work, target, max_wait_ms) {
3053
+ if (!await acquire(target, max_wait_ms)) return void 0;
3054
+ try {
3055
+ return await work();
3056
+ } finally {
3057
+ release(target);
3058
+ }
3059
+ }
3060
+ var repository_lock = { clear_stale, lock_path, with_lock };
2900
3061
 
2901
- // scripts/run/run-carry-streak.ts
2902
- var NO_INCREMENT = 0;
2903
- var OUTAGE_FOLD_WINDOW_MS = 12e4;
2904
- function has_increment(value) {
2905
- return (value ?? NO_INCREMENT) > NO_INCREMENT;
3062
+ // scripts/git/git-worktree.ts
3063
+ var WORKTREE = "worktree";
3064
+ var WORKTREE_LOCK_PREFIX = "josh-worktree-lock-";
3065
+ var WORKTREE_LOCK_MAX_WAIT_MS = 12e4;
3066
+ async function locked(arguments_) {
3067
+ const target = repository_lock.lock_path(WORKTREE_LOCK_PREFIX);
3068
+ const output = await repository_lock.with_lock(
3069
+ async () => await git_spawn.read(arguments_),
3070
+ target,
3071
+ WORKTREE_LOCK_MAX_WAIT_MS
3072
+ );
3073
+ if (output === void 0) {
3074
+ throw new Error(
3075
+ `git ${arguments_.join(" ")}: timed out waiting for the work-tree lock ${target}`
3076
+ );
3077
+ }
3078
+ return output;
2906
3079
  }
2907
- function next_failures(carry, change) {
2908
- if (has_increment(change.merged)) return NO_INCREMENT;
2909
- return carry.failures + (change.failures ?? NO_INCREMENT);
3080
+ async function worktree_list() {
3081
+ return await git_spawn.read([WORKTREE, "list", PORCELAIN_FLAG]);
3082
+ }
3083
+ async function worktree_add(directory, branch_name, start_point) {
3084
+ const flags = start_point === void 0 ? ["add"] : ["add", "--no-track", "-b", branch_name];
3085
+ return await locked([WORKTREE, ...flags, directory, start_point ?? branch_name]);
2910
3086
  }
2911
- function is_within_fold_window(last_outage_at, now) {
2912
- if (last_outage_at === void 0) return false;
2913
- const last = Date.parse(last_outage_at);
2914
- if (Number.isNaN(last)) return false;
2915
- return now.getTime() - last <= OUTAGE_FOLD_WINDOW_MS;
3087
+ async function worktree_add_detached(directory, commit2) {
3088
+ return await locked([WORKTREE, "add", "--detach", directory, commit2]);
2916
3089
  }
2917
- function folded_outage(carry) {
2918
- return { outages: carry.outages, last_outage_at: carry.last_outage_at };
3090
+ async function worktree_remove(directory) {
3091
+ return await locked([WORKTREE, "remove", "--force", directory]);
2919
3092
  }
2920
- function counted_outage(carry, change, now) {
2921
- const outages = carry.outages + (change.outages ?? NO_INCREMENT);
2922
- return { outages, last_outage_at: now.toISOString() };
3093
+ async function worktree_prune() {
3094
+ return await locked([WORKTREE, "prune"]);
2923
3095
  }
2924
- function next_outage(carry, change, now) {
2925
- if (has_increment(change.outages)) {
2926
- return is_within_fold_window(carry.last_outage_at, now) ? folded_outage(carry) : counted_outage(carry, change, now);
2927
- }
2928
- const is_reachable = has_increment(change.merged) || has_increment(change.failures);
2929
- return is_reachable ? { outages: NO_INCREMENT, last_outage_at: void 0 } : folded_outage(carry);
3096
+ async function ls_remote_branch(branch_name) {
3097
+ return await git_spawn.read(ls_remote_branch_arguments(branch_name));
2930
3098
  }
2931
- function next_state(carry, change, now) {
2932
- const outage = next_outage(carry, change, now);
2933
- return {
2934
- failures: next_failures(carry, change),
2935
- outages: outage.outages,
2936
- last_outage_at: outage.last_outage_at
2937
- };
3099
+ async function branch_delete(branch_name) {
3100
+ return await git_spawn.read(["branch", "-D", branch_name]);
2938
3101
  }
2939
- var run_carry_streak = { OUTAGE_FOLD_WINDOW_MS, next_state };
3102
+ var git_worktree = {
3103
+ branch_delete,
3104
+ ls_remote_branch,
3105
+ worktree_add,
3106
+ worktree_add_detached,
3107
+ worktree_list,
3108
+ worktree_prune,
3109
+ worktree_remove
3110
+ };
2940
3111
 
2941
- // scripts/run/run-carry-change.ts
2942
- var NO_INCREMENT2 = 0;
2943
- function next_done(carry, done) {
2944
- if (done === void 0) return carry.done;
2945
- const current = carry.done ?? [];
2946
- return current.includes(done) ? current : [...current, done];
3112
+ // scripts/lane/lane-environment.ts
3113
+ var LANE_OUTPUT_KEY = "JOSH_LANE_OUTPUT";
3114
+ var LANE_PROVIDER_KEY = "JOSH_LANE_AGENT_PROVIDER";
3115
+ var LANE_ROLE_KEY = "JOSH_LANE_AGENT_ROLE";
3116
+ var LANE_MODEL_KEY = "JOSH_LANE_AGENT_MODEL";
3117
+ var LANE_EFFORT_KEY = "JOSH_LANE_AGENT_EFFORT";
3118
+ var ASSIGNMENT_SEPARATOR = "=";
3119
+ var EXPORT_PREFIX = "export ";
3120
+ var QUOTE_CHARACTERS = /* @__PURE__ */ new Set(['"', "'"]);
3121
+ var MIN_QUOTED_LENGTH = 2;
3122
+ var FIRST_CHARACTER_END = 1;
3123
+ var LAST_CHARACTER_START = -1;
3124
+ function strip_export(line) {
3125
+ const trimmed2 = line.trimStart();
3126
+ return trimmed2.startsWith(EXPORT_PREFIX) ? trimmed2.slice(EXPORT_PREFIX.length).trimStart() : trimmed2;
2947
3127
  }
2948
- function next_merged_issues(carry, issue) {
2949
- if (issue === void 0) return carry.merged_issues;
2950
- return [...carry.merged_issues ?? [], issue];
3128
+ function is_key_line(line, key) {
3129
+ const body = strip_export(line);
3130
+ if (!body.startsWith(key)) return false;
3131
+ return body.slice(key.length).trimStart().startsWith(ASSIGNMENT_SEPARATOR);
2951
3132
  }
2952
- function is_duplicate_merge(carry, change) {
2953
- const issue = change.merged_issue;
2954
- return issue !== void 0 && carry.merged_issues?.includes(issue) === true;
3133
+ function is_seed_line(line) {
3134
+ return is_key_line(line, PORT_SEED_KEY);
2955
3135
  }
2956
- function next_counts(carry, change) {
2957
- const cuts = change.cuts ?? NO_INCREMENT2;
2958
- return {
2959
- merged: carry.merged + (change.merged ?? NO_INCREMENT2),
2960
- filed: carry.filed + (change.filed ?? NO_INCREMENT2),
2961
- cuts: carry.cuts + cuts,
2962
- is_handed_off: cuts > NO_INCREMENT2
2963
- };
3136
+ function is_seat_line(line) {
3137
+ return is_key_line(line, LANE_SEAT_KEY);
2964
3138
  }
2965
- function apply_change(target, carry, change, now = /* @__PURE__ */ new Date()) {
2966
- if (is_duplicate_merge(carry, change)) return carry;
2967
- const streak = run_carry_streak.next_state(carry, change, now);
2968
- const next = {
2969
- ...carry,
2970
- ...next_counts(carry, change),
2971
- merged_issues: next_merged_issues(carry, change.merged_issue),
2972
- failures: streak.failures,
2973
- outages: streak.outages,
2974
- last_outage_at: streak.last_outage_at,
2975
- done: next_done(carry, change.done),
2976
- retrospective: change.retrospective === true || carry.retrospective
2977
- };
2978
- stamp_file.write_stamp(target, next);
2979
- return next;
3139
+ function is_output_line(line) {
3140
+ return is_key_line(line, LANE_OUTPUT_KEY);
2980
3141
  }
2981
- var run_carry_change = { apply_change };
2982
-
2983
- // scripts/run/run-carry.ts
2984
- var CARRY_PREFIX = "josh-run-carry-";
2985
- var REPOSITORY_DIRECTORY_INDEX = 1;
2986
- var CARRY_MAX_AGE_HOURS = backlog_budget.WHOLE_RUN_BUDGET_HOURS;
2987
- var CARRY_MAX_AGE_MS = backlog_budget.WHOLE_RUN_BUDGET_MS;
2988
- var NO_INCREMENT3 = 0;
2989
- var MAX_CUTS = 6;
2990
- var END_COMMAND2 = "pnpm josh run:carry --end";
2991
- var RESUME_COMMAND = "pnpm josh run:carry --resume";
2992
- var READ_COMMAND2 = "pnpm josh run:carry --json";
2993
- var OWNER_ARGUMENT = '--owner "$PPID"';
2994
- var NONE_READ2 = { kind: "none" };
2995
- var UNREADABLE_READ3 = { kind: "unreadable" };
2996
- var NO_OWNER = {};
2997
- var run_carry_schema = z5.object({
2998
- invocation: z5.string(),
2999
- started_at: z5.string(),
3000
- merged: z5.number(),
3001
- merged_issues: z5.array(z5.number()).optional(),
3002
- filed: z5.number(),
3003
- cuts: z5.number(),
3004
- // Defaulted, so a record written before this field existed parses with a zero streak rather than
3005
- // failing to read — the same backward-compatibility the optional owner fields below carry.
3006
- failures: z5.number().default(0),
3007
- // Defaulted for the same backward-compatibility as `failures` (joshuafolkken/kit#2240).
3008
- outages: z5.number().default(0),
3009
- // Optional, so a record written before the outage fold existed still parses (joshuafolkken/kit#2317).
3010
- last_outage_at: z5.string().optional(),
3011
- // Optional, so a record written by the previous shape still parses. Read as "no owner declared",
3012
- // which is the not-provably-live answer rather than a live one.
3013
- owner_pid: z5.number().optional(),
3014
- owner_start: z5.string().optional(),
3015
- is_handed_off: z5.boolean().optional(),
3016
- done: z5.array(z5.number()).optional(),
3017
- // Optional, so a record written before the retrospective existed still parses (joshuafolkken/kit#2328).
3018
- retrospective: z5.boolean().optional()
3019
- });
3020
- function carry_path(git_directory) {
3021
- return stamp_file.stamp_path(CARRY_PREFIX, git_directory);
3142
+ function split_lines(content) {
3143
+ const trimmed2 = content.trimEnd();
3144
+ return trimmed2.length === 0 ? [] : trimmed2.split("\n");
3022
3145
  }
3023
- async function repository_directory() {
3024
- const directories = await git_command.git_directories();
3025
- return directories[REPOSITORY_DIRECTORY_INDEX];
3146
+ function is_quoted(raw) {
3147
+ const first = raw.slice(0, FIRST_CHARACTER_END);
3148
+ if (!QUOTE_CHARACTERS.has(first)) return false;
3149
+ return raw.length >= MIN_QUOTED_LENGTH && raw.endsWith(first);
3026
3150
  }
3027
- function parse_carry(raw) {
3028
- try {
3029
- const parsed = run_carry_schema.safeParse(JSON.parse(raw));
3030
- return parsed.success ? parsed.data : void 0;
3031
- } catch {
3032
- return void 0;
3033
- }
3151
+ function unquote(raw) {
3152
+ return is_quoted(raw) ? raw.slice(FIRST_CHARACTER_END, LAST_CHARACTER_START) : raw;
3034
3153
  }
3035
- function is_expired2(carry, now) {
3036
- const started = Date.parse(carry.started_at);
3037
- if (Number.isNaN(started)) return true;
3038
- return now.getTime() - started > CARRY_MAX_AGE_MS;
3154
+ function assignment_value(line) {
3155
+ return unquote(line.slice(line.indexOf(ASSIGNMENT_SEPARATOR) + FIRST_CHARACTER_END).trim());
3039
3156
  }
3040
- function classify4(raw, now) {
3041
- if (raw === void 0) return NONE_READ2;
3042
- const carry = parse_carry(raw);
3043
- if (carry === void 0) return UNREADABLE_READ3;
3044
- return { kind: is_expired2(carry, now) ? "expired" : "carried", carry };
3157
+ function read_assignment(content, key) {
3158
+ const line = split_lines(content).findLast((entry) => is_key_line(entry, key));
3159
+ return line === void 0 ? void 0 : assignment_value(line);
3045
3160
  }
3046
- function read_carry(target, now = /* @__PURE__ */ new Date()) {
3047
- return classify4(stamp_file.read_stamp_text(target), now);
3161
+ function read_root_seed(content) {
3162
+ return ports.resolve_seed({ [PORT_SEED_KEY]: read_assignment(content, PORT_SEED_KEY) });
3048
3163
  }
3049
- function owner_of(pid, read2 = process_identity.read_start) {
3050
- return { pid, start: read2(pid) };
3164
+ function read_lane_seat(content) {
3165
+ const raw = read_assignment(content, LANE_SEAT_KEY);
3166
+ return raw === void 0 ? void 0 : ports.resolve_lane({ [LANE_SEAT_KEY]: raw });
3051
3167
  }
3052
- function fresh_carry(invocation, owner, now) {
3168
+ function read_lane_ports(content) {
3169
+ const environment2 = {
3170
+ [PORT_SEED_KEY]: read_assignment(content, PORT_SEED_KEY),
3171
+ [LANE_SEAT_KEY]: read_assignment(content, LANE_SEAT_KEY)
3172
+ };
3053
3173
  return {
3054
- invocation,
3055
- started_at: now.toISOString(),
3056
- merged: NO_INCREMENT3,
3057
- filed: NO_INCREMENT3,
3058
- cuts: NO_INCREMENT3,
3059
- failures: NO_INCREMENT3,
3060
- outages: NO_INCREMENT3,
3061
- owner_pid: owner.pid,
3062
- owner_start: owner.start
3174
+ development: ports.resolve_development_port(environment2),
3175
+ preview: ports.resolve_preview_port(environment2)
3063
3176
  };
3064
3177
  }
3065
- function begin_carry(target, invocation, owner = NO_OWNER, now = /* @__PURE__ */ new Date()) {
3066
- const carry = fresh_carry(invocation, owner, now);
3067
- return stamp_file.create_stamp(target, carry) ? carry : void 0;
3068
- }
3069
- function replace_carry(target, invocation, owner = NO_OWNER, now = /* @__PURE__ */ new Date()) {
3070
- stamp_file.remove_stamp(target);
3071
- return begin_carry(target, invocation, owner, now);
3178
+ function read_lane_output(content) {
3179
+ const line = split_lines(content).findLast((entry) => is_output_line(entry));
3180
+ const raw = line === void 0 ? void 0 : assignment_value(line);
3181
+ return raw === void 0 || raw.length === 0 ? void 0 : raw;
3072
3182
  }
3073
- function adopt_carry(target, carry, owner = NO_OWNER) {
3074
- const next = {
3075
- ...carry,
3076
- owner_pid: owner.pid,
3077
- owner_start: owner.start,
3078
- is_handed_off: false
3079
- };
3080
- stamp_file.remove_stamp(target);
3081
- return stamp_file.create_stamp(target, next) ? next : void 0;
3183
+ function read_lane_profile(content) {
3184
+ return agent_role_profile.parse({
3185
+ provider: read_assignment(content, LANE_PROVIDER_KEY),
3186
+ role: read_assignment(content, LANE_ROLE_KEY),
3187
+ model: read_assignment(content, LANE_MODEL_KEY),
3188
+ effort: read_assignment(content, LANE_EFFORT_KEY)
3189
+ });
3082
3190
  }
3083
- function is_owner_live(carry) {
3084
- return process_identity.is_same_process(carry.owner_pid, carry.owner_start) !== false;
3191
+ function replace_assignment(lines, keep, assignment, key) {
3192
+ return lines.flatMap((entry, index) => {
3193
+ if (index === keep) return [assignment];
3194
+ return is_key_line(entry, key) ? [] : [entry];
3195
+ });
3085
3196
  }
3086
- function is_owned_by(carry, owner) {
3087
- return carry.owner_pid === owner.pid && carry.owner_start === owner.start;
3197
+ function upsert_assignment(content, key, value) {
3198
+ const assignment = `${key}${ASSIGNMENT_SEPARATOR}${value}`;
3199
+ const lines = split_lines(content);
3200
+ const last = lines.findLastIndex((entry) => is_key_line(entry, key));
3201
+ const body = last === -1 ? [...lines, assignment] : replace_assignment(lines, last, assignment, key);
3202
+ return `${body.join("\n")}
3203
+ `;
3088
3204
  }
3089
- function is_foreign_live_owner(carry, owner) {
3090
- return is_owner_live(carry) && !is_owned_by(carry, owner);
3205
+ function lane_file_content(root_content, seat) {
3206
+ return upsert_assignment(root_content, LANE_SEAT_KEY, String(seat));
3091
3207
  }
3092
- function is_handed_off_to(carry, invocation) {
3093
- return carry.is_handed_off === true && carry.invocation === invocation;
3208
+ function with_lane_output(content, output, profile) {
3209
+ const recorded = upsert_assignment(content, LANE_OUTPUT_KEY, `"${output}"`);
3210
+ if (profile === void 0) return recorded;
3211
+ const with_provider = upsert_assignment(recorded, LANE_PROVIDER_KEY, profile.provider);
3212
+ const with_role = upsert_assignment(with_provider, LANE_ROLE_KEY, profile.role);
3213
+ const with_model = upsert_assignment(with_role, LANE_MODEL_KEY, profile.model);
3214
+ return upsert_assignment(with_model, LANE_EFFORT_KEY, profile.effort);
3094
3215
  }
3095
- function classify_claim(carry, request) {
3096
- if (is_handed_off_to(carry, request.invocation)) return "resume";
3097
- if (is_foreign_live_owner(carry, request.owner)) return "busy";
3098
- if (carry.invocation !== request.invocation) return "mismatch";
3099
- return request.is_adoption ? "resume" : "standing";
3216
+ var lane_environment = {
3217
+ LANE_OUTPUT_KEY,
3218
+ LANE_PROVIDER_KEY,
3219
+ LANE_ROLE_KEY,
3220
+ is_output_line,
3221
+ is_seat_line,
3222
+ is_seed_line,
3223
+ lane_file_content,
3224
+ read_lane_output,
3225
+ read_lane_profile,
3226
+ read_lane_ports,
3227
+ read_lane_seat,
3228
+ read_root_seed,
3229
+ with_lane_output
3230
+ };
3231
+
3232
+ // scripts/lane/lane-registry.ts
3233
+ var WORKTREE_PREFIX = "worktree ";
3234
+ var BRANCH_PREFIX = "branch refs/heads/";
3235
+ var BLOCK_SEPARATOR = "\n\n";
3236
+ var ISSUE_PATTERN = /^[1-9]\d*$/u;
3237
+ function line_value(lines, prefix) {
3238
+ return lines.find((line) => line.startsWith(prefix))?.slice(prefix.length);
3100
3239
  }
3101
- function is_count_refused(carry, owner) {
3102
- return carry.is_handed_off === true || is_foreign_live_owner(carry, owner);
3240
+ function seat_from_content(content) {
3241
+ const seat = lane_environment.read_lane_seat(content);
3242
+ if (seat === void 0) return void 0;
3243
+ const port_pair = lane_environment.read_lane_ports(content);
3244
+ return { seat, development_port: port_pair.development, preview_port: port_pair.preview };
3103
3245
  }
3104
- function is_at_cut_cap(carry) {
3105
- return carry.cuts >= MAX_CUTS;
3246
+ var EMPTY_SEAT = {
3247
+ seat: void 0,
3248
+ development_port: void 0,
3249
+ preview_port: void 0
3250
+ };
3251
+ function held_seat(content) {
3252
+ if (content === void 0) return EMPTY_SEAT;
3253
+ return seat_from_content(content) ?? EMPTY_SEAT;
3106
3254
  }
3107
- function end_carry(target) {
3108
- stamp_file.remove_stamp(target);
3255
+ function read_environment(directory) {
3256
+ return file_reader.read_if_readable(path3.join(directory, ENV_FILE_NAME));
3109
3257
  }
3110
- function retrospective_done_of(read2) {
3111
- if (read2.kind !== "carried" && read2.kind !== "expired") return false;
3112
- return read2.carry.retrospective === true;
3258
+ function branch_issue(branch2) {
3259
+ if (!branch2?.endsWith(lane_paths.LANE_BRANCH_SUFFIX)) return void 0;
3260
+ const issue = branch2.slice(0, -lane_paths.LANE_BRANCH_SUFFIX.length);
3261
+ return ISSUE_PATTERN.test(issue) ? issue : void 0;
3113
3262
  }
3114
- function remaining_of(carry) {
3115
- if (carry === void 0) return void 0;
3116
- const declared = run_invocation.issue_numbers(carry.invocation);
3117
- if (declared === void 0) return void 0;
3118
- const done = carry.done ?? [];
3119
- return declared.filter((issue) => !done.includes(issue));
3263
+ function is_gone(directory) {
3264
+ return !existsSync2(directory);
3120
3265
  }
3121
- function done_note(carry) {
3122
- const done = carry.done ?? [];
3123
- return done.length === 0 ? "" : `, issues ${done.join(", ")} done`;
3266
+ function build_lane(issue, branch2, directory, is_stranded) {
3267
+ const content = is_stranded ? void 0 : read_environment(directory);
3268
+ const held = held_seat(content);
3269
+ const output = content === void 0 ? void 0 : lane_environment.read_lane_output(content);
3270
+ const profile = content === void 0 ? void 0 : lane_environment.read_lane_profile(content);
3271
+ return {
3272
+ issue,
3273
+ branch: branch2,
3274
+ directory,
3275
+ seat: held.seat,
3276
+ development_port: held.development_port,
3277
+ preview_port: held.preview_port,
3278
+ output,
3279
+ profile,
3280
+ is_stranded
3281
+ };
3124
3282
  }
3125
- function describe_carry(carry) {
3126
- return `${carry.invocation} started ${carry.started_at}; ${String(carry.merged)} merged, ${String(carry.filed)} filed, ${String(carry.failures)} failed in a row, ${String(carry.outages)} outage(s) in a row, ${String(carry.cuts)} cut(s) crossed${done_note(carry)}`;
3283
+ function is_lane_directory(root, issue, directory) {
3284
+ return directory === lane_paths.lane_directory(root, issue);
3127
3285
  }
3128
- function expired_message(carry) {
3129
- return `The ${String(CARRY_MAX_AGE_HOURS)}-hour whole-run bound is spent: ${describe_carry(carry)}. Clear it with \`${END_COMMAND2}\`.`;
3286
+ function parse_block(block, root) {
3287
+ const lines = block.split("\n");
3288
+ const directory = line_value(lines, WORKTREE_PREFIX);
3289
+ const branch2 = line_value(lines, BRANCH_PREFIX);
3290
+ const issue = branch_issue(branch2);
3291
+ if (directory === void 0 || branch2 === void 0 || issue === void 0) return void 0;
3292
+ if (!is_lane_directory(root, issue, directory)) return void 0;
3293
+ return build_lane(issue, branch2, directory, is_gone(directory));
3130
3294
  }
3131
- function mismatch_message(carry, invocation) {
3132
- return `A different invocation is carried here \u2014 ${describe_carry(carry)}. This one is \`${invocation}\`. End the standing record with \`${END_COMMAND2}\` once you know its run is over, then begin again.`;
3295
+ function is_lane(lane) {
3296
+ return lane !== void 0;
3133
3297
  }
3134
- function busy_message2(carry) {
3135
- return `A live run holds this budget \u2014 ${describe_carry(carry)}, owner pid ${String(carry.owner_pid)} still running. Nothing was established. Wait for that run, or end its record with \`${END_COMMAND2}\` once you know it is over.`;
3298
+ async function worktree_blocks() {
3299
+ const listing = await git_worktree.worktree_list();
3300
+ return listing.split(BLOCK_SEPARATOR);
3136
3301
  }
3137
- function standing_message(carry) {
3138
- return `A run record is standing here that no cut handed off \u2014 ${describe_carry(carry)}. Nothing was established. Carry that budget with \`${RESUME_COMMAND} "${carry.invocation}" ${OWNER_ARGUMENT}\`, or discard it with \`${END_COMMAND2}\` and begin again.`;
3302
+ async function main_repository_root() {
3303
+ return repo_discovery.main_worktree(await git_command.repository_root());
3139
3304
  }
3140
- function count_refused_message(carry) {
3141
- if (carry.is_handed_off === true) {
3142
- return `This budget was handed off at a cut and is waiting for its successor \u2014 ${describe_carry(carry)}. Nothing was counted; the cutting session must not advance a handed-off budget.`;
3143
- }
3144
- return `A live run owns this budget \u2014 ${describe_carry(carry)}, owner pid ${String(carry.owner_pid)} still running. Nothing was counted; only its owner advances the budget.`;
3305
+ async function list_lanes() {
3306
+ const root = lane_paths.lane_root(await main_repository_root());
3307
+ const blocks = await worktree_blocks();
3308
+ const lanes = blocks.map((block) => parse_block(block, root)).filter(is_lane);
3309
+ return lanes.toSorted((left, right) => Number(left.issue) - Number(right.issue));
3145
3310
  }
3146
- function lost_message() {
3147
- return `Another process established a run record here first, so nothing was established. Read it with \`${READ_COMMAND2}\`.`;
3311
+ function used_seats(lanes) {
3312
+ return lanes.map((lane) => lane.seat).filter((seat) => seat !== void 0);
3148
3313
  }
3149
- function nothing_to_resume_message() {
3150
- return `Nothing is carried here, so there is no budget to resume. Begin one with \`pnpm josh run:carry --begin "<invocation>"\`.`;
3314
+ function unreadable_lanes(lanes) {
3315
+ return lanes.filter((lane) => !lane.is_stranded && lane.seat === void 0);
3151
3316
  }
3152
- function unreadable_message3() {
3153
- return `A run record is here but could not be read; clear it with \`${END_COMMAND2}\` once you know no run is using it.`;
3317
+ function find_lane(lanes, issue) {
3318
+ return lanes.find((lane) => lane.issue === issue);
3154
3319
  }
3155
- function unknown_message3() {
3156
- return "This repository\u2019s git directory could not be read, so no run record was established.";
3320
+ async function find_open_lane(issue) {
3321
+ return find_lane(await list_lanes(), issue);
3157
3322
  }
3158
- var run_carry = {
3159
- CARRY_MAX_AGE_HOURS,
3160
- CARRY_MAX_AGE_MS,
3161
- END_COMMAND: END_COMMAND2,
3162
- MAX_CUTS,
3163
- NO_OWNER,
3164
- RESUME_COMMAND,
3165
- adopt_carry,
3166
- apply_change: run_carry_change.apply_change,
3167
- begin_carry,
3168
- busy_message: busy_message2,
3169
- carry_path,
3170
- classify: classify4,
3171
- classify_claim,
3172
- count_refused_message,
3173
- is_at_cut_cap,
3174
- describe_carry,
3175
- end_carry,
3176
- expired_message,
3177
- mismatch_message,
3178
- fresh_carry,
3179
- is_count_refused,
3180
- is_expired: is_expired2,
3181
- is_foreign_live_owner,
3182
- is_owner_live,
3183
- lost_message,
3184
- nothing_to_resume_message,
3185
- owner_of,
3186
- parse_carry,
3187
- read_carry,
3188
- remaining_of,
3189
- replace_carry,
3190
- repository_directory,
3191
- retrospective_done_of,
3192
- standing_message,
3193
- unknown_message: unknown_message3,
3194
- unreadable_message: unreadable_message3
3323
+ var lane_registry = {
3324
+ find_lane,
3325
+ find_open_lane,
3326
+ list_lanes,
3327
+ main_repository_root,
3328
+ parse_block,
3329
+ read_environment,
3330
+ unreadable_lanes,
3331
+ used_seats
3195
3332
  };
3196
3333
 
3197
3334
  // scripts/run/run-progress-clock.ts
@@ -3338,9 +3475,9 @@ var run_progress_clock = {
3338
3475
  // scripts/run/run-watcher-guard.ts
3339
3476
  var WATCHER_TICK_MS = 3e4;
3340
3477
  var TICK_MULTIPLIER = 3;
3341
- var MS_PER_SECOND2 = 1e3;
3478
+ var MS_PER_SECOND3 = 1e3;
3342
3479
  var STALE_THRESHOLD_MS = WATCHER_TICK_MS * TICK_MULTIPLIER;
3343
- var STALE_THRESHOLD_S = STALE_THRESHOLD_MS / MS_PER_SECOND2;
3480
+ var STALE_THRESHOLD_S = STALE_THRESHOLD_MS / MS_PER_SECOND3;
3344
3481
  var STALE_NOTE = `Lane children are in-flight but the progress watcher has not pinged in the last ${String(STALE_THRESHOLD_S)}s \u2014 run \`pnpm josh run:progress --wait\` in the background before proceeding`;
3345
3482
  var OK_RESULT = { kind: "ok" };
3346
3483
  async function has_lanes_in_flight() {
@@ -3359,6 +3496,114 @@ var run_watcher_guard = {
3359
3496
  has_lanes_in_flight
3360
3497
  };
3361
3498
 
3499
+ // scripts/run/run-headless.ts
3500
+ var HEADLESS_ENV_KEY = "JOSH_RUN_HEADLESS";
3501
+ var HEADLESS_VALUE = "1";
3502
+ function environment() {
3503
+ return { [HEADLESS_ENV_KEY]: HEADLESS_VALUE };
3504
+ }
3505
+ function is_headless(source = process.env) {
3506
+ return source[HEADLESS_ENV_KEY] === HEADLESS_VALUE;
3507
+ }
3508
+ function is_driving(read2) {
3509
+ return read2?.kind === "carried" && read2.carry.is_handed_off !== true;
3510
+ }
3511
+ async function read_carry_here() {
3512
+ const repository2 = await run_carry.repository_directory();
3513
+ return repository2 === void 0 ? void 0 : run_carry.read_carry(run_carry.carry_path(repository2));
3514
+ }
3515
+ async function must_keep_waiting(source = process.env) {
3516
+ const is_candidate2 = is_headless(source) && lane_child_marker.marked_issue(source) === void 0;
3517
+ if (!is_candidate2 || !await run_watcher_guard.has_lanes_in_flight()) return false;
3518
+ return is_driving(await read_carry_here());
3519
+ }
3520
+ async function current_carry() {
3521
+ const read2 = await read_carry_here();
3522
+ return read2?.kind === "carried" ? read2.carry : void 0;
3523
+ }
3524
+ function is_owned_here(carry, ancestry) {
3525
+ return carry.owner_pid !== void 0 && ancestry().has(carry.owner_pid);
3526
+ }
3527
+ function driven_here(read2, ancestry) {
3528
+ if (read2?.kind !== "carried" || !is_driving(read2)) return void 0;
3529
+ return is_owned_here(read2.carry, ancestry) ? read2.carry : void 0;
3530
+ }
3531
+ async function driving_carry(source = process.env, ancestry = lane_reap.own_ancestry) {
3532
+ if (lane_child_marker.marked_issue(source) !== void 0) return void 0;
3533
+ return driven_here(await read_carry_here(), ancestry);
3534
+ }
3535
+ async function is_backlog_parent(source = process.env, ancestry = lane_reap.own_ancestry) {
3536
+ return await driving_carry(source, ancestry) !== void 0;
3537
+ }
3538
+ var run_headless = {
3539
+ HEADLESS_ENV_KEY,
3540
+ current_carry,
3541
+ driving_carry,
3542
+ environment,
3543
+ is_backlog_parent,
3544
+ is_headless,
3545
+ is_owned_here,
3546
+ must_keep_waiting
3547
+ };
3548
+
3549
+ // scripts/issue/issue-cite.ts
3550
+ var GITHUB_URL_PREFIX = "https://github.com/";
3551
+ var ISSUE_PATH = "/issues/";
3552
+ var SUMMARY_SEPARATOR = " \u2014 ";
3553
+ var REPO_QUALIFIED = /^([^\s/#]+\/[^\s/#]+)#(\d+)$/u;
3554
+ var BARE_NUMBER = /^#?(\d+)$/u;
3555
+ function issue_url(slug, number) {
3556
+ return `${GITHUB_URL_PREFIX}${slug}${ISSUE_PATH}${number}`;
3557
+ }
3558
+ function citation_line(slug, number, summary) {
3559
+ return `[#${number}](${issue_url(slug, number)})${SUMMARY_SEPARATOR}${summary}`;
3560
+ }
3561
+ function citer(slug, titles) {
3562
+ return function cite(number) {
3563
+ if (slug === void 0) return `#${number}`;
3564
+ const title = titles.get(number);
3565
+ return title === void 0 ? `[#${number}](${issue_url(slug, number)})` : citation_line(slug, number, title);
3566
+ };
3567
+ }
3568
+ function qualified_target(token) {
3569
+ const match = REPO_QUALIFIED.exec(token);
3570
+ if (match === null) return void 0;
3571
+ const [, repo = "", number = ""] = match;
3572
+ return { number, repo };
3573
+ }
3574
+ function bare_target(token, default_repo) {
3575
+ const match = BARE_NUMBER.exec(token);
3576
+ if (match === null) return void 0;
3577
+ const [, number = ""] = match;
3578
+ return { number, repo: default_repo };
3579
+ }
3580
+ function parse_target(token, default_repo) {
3581
+ return qualified_target(token) ?? bare_target(token, default_repo);
3582
+ }
3583
+ function label(target) {
3584
+ return target.repo === void 0 ? `#${target.number}` : `${target.repo}#${target.number}`;
3585
+ }
3586
+ function missing_line(target) {
3587
+ return `\u2716 ${label(target)}: issue does not resolve \u2014 check the number and the repository`;
3588
+ }
3589
+ function unreadable_line(target) {
3590
+ return `\u2716 ${label(target)}: could not be read \u2014 a rate limit, expired auth, or a dropped connection`;
3591
+ }
3592
+ function no_repo_line(target) {
3593
+ return `\u2716 ${label(target)}: could not read this repository \u2014 check gh auth and the origin remote`;
3594
+ }
3595
+ var issue_cite = {
3596
+ SUMMARY_SEPARATOR,
3597
+ citation_line,
3598
+ citer,
3599
+ issue_url,
3600
+ label,
3601
+ missing_line,
3602
+ no_repo_line,
3603
+ parse_target,
3604
+ unreadable_line
3605
+ };
3606
+
3362
3607
  // scripts/git/git-gh-api-path.ts
3363
3608
  var FULL_PAGE_QUERY = "?per_page=100";
3364
3609
  function repo_api_path(repo) {
@@ -3421,6 +3666,35 @@ var git_gh_api_path = {
3421
3666
  commit_status_api_path
3422
3667
  };
3423
3668
 
3669
+ // scripts/lib/error-message.ts
3670
+ function message_of(error) {
3671
+ return error instanceof Error ? error.message : String(error);
3672
+ }
3673
+ var error_text = { message_of };
3674
+
3675
+ // scripts/git/git-gh-exec.ts
3676
+ import { execa as execa5, execaSync as execaSync2 } from "execa";
3677
+
3678
+ // scripts/git/git-gh-check.ts
3679
+ import { execa as execa4 } from "execa";
3680
+ var GH_NOT_INSTALLED_MSG = "gh CLI is not installed. Install it from https://cli.github.com/";
3681
+ var gh_check = {};
3682
+ async function run_gh_check() {
3683
+ try {
3684
+ await execa4("gh", ["--version"]);
3685
+ } catch {
3686
+ delete gh_check.promise;
3687
+ throw new Error(GH_NOT_INSTALLED_MSG);
3688
+ }
3689
+ }
3690
+ async function check_gh_installed() {
3691
+ gh_check.promise ??= run_gh_check();
3692
+ await gh_check.promise;
3693
+ }
3694
+
3695
+ // scripts/git/git-gh-failure.ts
3696
+ import { z as z7 } from "zod";
3697
+
3424
3698
  // scripts/git/parse-json-array.ts
3425
3699
  var MALFORMED_JSON = /* @__PURE__ */ Symbol("malformed-json");
3426
3700
  function parse_json_loose(raw_json) {
@@ -3461,28 +3735,7 @@ var parse_json = {
3461
3735
  read_json_listing
3462
3736
  };
3463
3737
 
3464
- // scripts/git/git-gh-exec.ts
3465
- import { execa as execa4, execaSync as execaSync2 } from "execa";
3466
-
3467
- // scripts/git/git-gh-check.ts
3468
- import { execa as execa3 } from "execa";
3469
- var GH_NOT_INSTALLED_MSG = "gh CLI is not installed. Install it from https://cli.github.com/";
3470
- var gh_check = {};
3471
- async function run_gh_check() {
3472
- try {
3473
- await execa3("gh", ["--version"]);
3474
- } catch {
3475
- delete gh_check.promise;
3476
- throw new Error(GH_NOT_INSTALLED_MSG);
3477
- }
3478
- }
3479
- async function check_gh_installed() {
3480
- gh_check.promise ??= run_gh_check();
3481
- await gh_check.promise;
3482
- }
3483
-
3484
3738
  // scripts/git/git-gh-failure.ts
3485
- import { z as z7 } from "zod";
3486
3739
  var error_document_schema = z7.object({ status: z7.union([z7.string(), z7.number()]).optional() }).catch({});
3487
3740
  var GH_FAILURE_FIELD = "gh_failure";
3488
3741
  function has_gh_failure(error) {
@@ -3520,8 +3773,12 @@ function direct_environment() {
3520
3773
  const removed = agent_session_environment.removed_proxy_environment();
3521
3774
  return Object.keys(removed).length > 0 ? { env: removed } : {};
3522
3775
  }
3523
- function to_spawn_options(timeout_ms) {
3524
- return { ...to_timeout_option(timeout_ms), ...direct_environment() };
3776
+ function to_spawn_options(timeout_ms, cwd) {
3777
+ return {
3778
+ ...to_timeout_option(timeout_ms),
3779
+ ...direct_environment(),
3780
+ ...cwd !== void 0 && { cwd }
3781
+ };
3525
3782
  }
3526
3783
  var GH_REQUEST_TIMEOUT_MESSAGE = "gh request timed out";
3527
3784
  function has_stderr_field(error) {
@@ -3533,13 +3790,10 @@ function has_stdout_field(error) {
3533
3790
  function to_timeout_prefix(error) {
3534
3791
  return has_timed_out(error) ? `${GH_REQUEST_TIMEOUT_MESSAGE}: ` : "";
3535
3792
  }
3536
- function to_error_message(error) {
3537
- return error instanceof Error ? error.message : String(error);
3538
- }
3539
3793
  function to_gh_error(error) {
3540
3794
  const stderr = has_stderr_field(error) ? error.stderr.trim() : "";
3541
3795
  const stdout = has_stdout_field(error) ? error.stdout.trim() : "";
3542
- const summary = stderr.length > 0 ? stderr : to_error_message(error);
3796
+ const summary = stderr.length > 0 ? stderr : error_text.message_of(error);
3543
3797
  const detail = stdout.length > 0 ? `${summary}
3544
3798
  ${stdout}` : summary;
3545
3799
  return gh_failure.attach(
@@ -3547,10 +3801,11 @@ ${stdout}` : summary;
3547
3801
  gh_failure.classify_stdout(stdout)
3548
3802
  );
3549
3803
  }
3550
- async function exec_gh_command(arguments_, timeout_ms) {
3804
+ async function exec_gh_command(arguments_, timeout_ms, cwd) {
3551
3805
  await check_gh_installed();
3806
+ const options = to_spawn_options(timeout_ms, cwd);
3552
3807
  try {
3553
- const { stdout } = await execa4("gh", arguments_, to_spawn_options(timeout_ms));
3808
+ const { stdout } = await execa5("gh", arguments_, options);
3554
3809
  return stdout.trimEnd();
3555
3810
  } catch (error) {
3556
3811
  throw to_gh_error(error);
@@ -3558,9 +3813,9 @@ async function exec_gh_command(arguments_, timeout_ms) {
3558
3813
  }
3559
3814
  async function exec_gh_command_with_stdin(input) {
3560
3815
  await check_gh_installed();
3561
- const options = { input: input.stdin_body, ...to_spawn_options(input.timeout_ms) };
3816
+ const options = { input: input.stdin_body, ...to_spawn_options(input.timeout_ms, input.cwd) };
3562
3817
  try {
3563
- const { stdout } = await execa4("gh", input.args, options);
3818
+ const { stdout } = await execa5("gh", input.args, options);
3564
3819
  return stdout.trimEnd();
3565
3820
  } catch (error) {
3566
3821
  throw to_gh_error(error);
@@ -3571,14 +3826,26 @@ function parse_status_line(output) {
3571
3826
  const { status: status2 } = STATUS_LINE_PATTERN.exec(output)?.groups ?? {};
3572
3827
  return status2 === void 0 ? void 0 : Number(status2);
3573
3828
  }
3829
+ function status_of_failure(error) {
3830
+ return has_stdout_field(error) ? parse_status_line(error.stdout) : void 0;
3831
+ }
3574
3832
  async function exec_gh_api_status(path4, timeout_ms) {
3575
3833
  const options = to_spawn_options(timeout_ms);
3576
3834
  try {
3577
3835
  await check_gh_installed();
3578
- const { stdout } = await execa4("gh", ["api", "--include", "--silent", path4], options);
3836
+ const { stdout } = await execa5("gh", ["api", "--include", "--silent", path4], options);
3837
+ return parse_status_line(stdout);
3838
+ } catch (error) {
3839
+ return status_of_failure(error);
3840
+ }
3841
+ }
3842
+ function exec_gh_api_status_sync(path4, timeout_ms) {
3843
+ const options = to_spawn_options(timeout_ms);
3844
+ try {
3845
+ const { stdout } = execaSync2("gh", ["api", "--include", "--silent", path4], options);
3579
3846
  return parse_status_line(stdout);
3580
3847
  } catch (error) {
3581
- return has_stdout_field(error) ? parse_status_line(error.stdout) : void 0;
3848
+ return status_of_failure(error);
3582
3849
  }
3583
3850
  }
3584
3851
  var METHOD_FLAG = "--method";
@@ -3605,16 +3872,19 @@ function to_gh_api_args(request) {
3605
3872
  }
3606
3873
  async function exec_gh_api(request) {
3607
3874
  const args = to_gh_api_args(request);
3608
- if (request.body === void 0) return await exec_gh_command(args, request.timeout_ms);
3875
+ if (request.body === void 0) {
3876
+ return await exec_gh_command(args, request.timeout_ms, request.cwd);
3877
+ }
3609
3878
  return await exec_gh_command_with_stdin({
3610
3879
  args,
3611
3880
  stdin_body: request.body,
3612
- timeout_ms: request.timeout_ms
3881
+ timeout_ms: request.timeout_ms,
3882
+ cwd: request.cwd
3613
3883
  });
3614
3884
  }
3615
3885
  function exec_gh_api_sync(request) {
3616
3886
  const body_option = request.body === void 0 ? {} : { input: request.body };
3617
- const options = { ...body_option, ...to_spawn_options(request.timeout_ms) };
3887
+ const options = { ...body_option, ...to_spawn_options(request.timeout_ms, request.cwd) };
3618
3888
  try {
3619
3889
  const { stdout } = execaSync2("gh", to_gh_api_args(request), options);
3620
3890
  return stdout.trimEnd();
@@ -3622,19 +3892,29 @@ function exec_gh_api_sync(request) {
3622
3892
  throw to_gh_error(error);
3623
3893
  }
3624
3894
  }
3895
+ function read_gh_api_sync(request) {
3896
+ try {
3897
+ return exec_gh_api_sync(request);
3898
+ } catch {
3899
+ return void 0;
3900
+ }
3901
+ }
3625
3902
  var git_gh_exec = {
3626
3903
  direct_environment,
3627
3904
  exec_gh_command,
3628
3905
  exec_gh_command_with_stdin,
3629
3906
  exec_gh_api,
3630
3907
  exec_gh_api_sync,
3908
+ read_gh_api_sync,
3631
3909
  exec_gh_api_status,
3910
+ exec_gh_api_status_sync,
3632
3911
  parse_status_line
3633
3912
  };
3634
3913
 
3635
3914
  export {
3636
3915
  shell_segments,
3637
3916
  bash_triggers,
3917
+ issue_cite,
3638
3918
  run_progress_clock,
3639
3919
  tail_commands,
3640
3920
  filing_cap,
@@ -3644,7 +3924,6 @@ export {
3644
3924
  agent_role_profile,
3645
3925
  backlog_budget,
3646
3926
  git_command,
3647
- lane_child_invocation,
3648
3927
  run_hold,
3649
3928
  run_cut,
3650
3929
  run_tail,
@@ -3664,17 +3943,19 @@ export {
3664
3943
  lane_park,
3665
3944
  repo_origin,
3666
3945
  repo_party,
3946
+ process_identity,
3947
+ run_invocation,
3948
+ run_carry,
3949
+ lane_registry,
3950
+ run_watcher_guard,
3951
+ run_headless,
3667
3952
  FULL_PAGE_QUERY,
3668
3953
  git_gh_api_path,
3954
+ error_text,
3669
3955
  parse_json_array_or_undefined,
3670
3956
  read_json_listing,
3671
3957
  parse_json_object_safe,
3672
3958
  gh_failure,
3673
3959
  has_stderr_field,
3674
- git_gh_exec,
3675
- process_identity,
3676
- lane_registry,
3677
- run_invocation,
3678
- run_carry,
3679
- run_watcher_guard
3960
+ git_gh_exec
3680
3961
  };