@joshuafolkken/kit 1.1373.0 → 1.1383.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 (92) hide show
  1. package/.claude/skills/dependency-update/SKILL.md +6 -3
  2. package/.claude/skills/workflow-commands/backlogrun-progress.md +2 -3
  3. package/.claude/skills/workflow-commands/backlogrun-steps.md +2 -4
  4. package/.claude/skills/workflow-commands/retrospective.md +1 -2
  5. package/dist/hooks/batch-guard.js +6 -6
  6. package/dist/hooks/{chunk-UTHMB4O2.js → chunk-2X2ART5G.js} +4 -33
  7. package/dist/hooks/{chunk-NZN2UFI6.js → chunk-4MGHBTOM.js} +162 -233
  8. package/dist/hooks/{chunk-YLBBSSTF.js → chunk-6AC4C2IO.js} +91 -67
  9. package/dist/hooks/{chunk-AKGCP4LX.js → chunk-6MAW7BCM.js} +12 -12
  10. package/dist/hooks/{chunk-JCRDBQXB.js → chunk-7VSZ2N4B.js} +3 -3
  11. package/dist/hooks/{chunk-GG72ANKI.js → chunk-KC6LUZZ2.js} +1 -1
  12. package/dist/hooks/{chunk-BZMPVFDY.js → chunk-KCF2YE7O.js} +5 -5
  13. package/dist/hooks/{chunk-7WKYHEGU.js → chunk-LC56R53A.js} +3 -3
  14. package/dist/hooks/{chunk-V4TKZHER.js → chunk-LOJTBU5V.js} +3 -3
  15. package/dist/hooks/{chunk-JGW24KKM.js → chunk-RYP3TSUJ.js} +3 -3
  16. package/dist/hooks/{chunk-SLSFIWTX.js → chunk-TT742SIO.js} +8 -5
  17. package/dist/hooks/{chunk-Z5TMJIPW.js → chunk-Y5R47QHU.js} +1 -1
  18. package/dist/hooks/{chunk-7UBFU3CL.js → chunk-YKUITFT7.js} +2 -2
  19. package/dist/hooks/codex-hook-adapter.js +13 -13
  20. package/dist/hooks/duplicate-read-guard.js +7 -7
  21. package/dist/hooks/format-edited.js +3 -3
  22. package/dist/hooks/inputs.json +190 -191
  23. package/dist/hooks/investigation-guard.js +7 -7
  24. package/dist/hooks/pretool-guard.js +12 -12
  25. package/dist/hooks/session-lang.js +3 -3
  26. package/dist/hooks/stop-guard.js +19 -15
  27. package/dist/josh.js +40 -5
  28. package/package.json +4 -4
  29. package/prompts/collaboration-workflow/upstream-interrupt.md +1 -1
  30. package/scripts/adopt/adopt-logic.ts +4 -1
  31. package/scripts/adopt/adopt.ts +7 -4
  32. package/scripts/backlog/backlog-budget-cli.ts +2 -20
  33. package/scripts/backlog/backlog-budget.ts +72 -109
  34. package/scripts/backlog/backlog-drive-cli.ts +10 -3
  35. package/scripts/backlog/backlog-drive.ts +5 -2
  36. package/scripts/backlog/backlog-offer-cli.ts +1 -15
  37. package/scripts/cost-runtime/context-cut-payback.ts +9 -4
  38. package/scripts/cost-runtime/cost-pricing.ts +94 -33
  39. package/scripts/document/document-byte-budget.ts +1 -1
  40. package/scripts/document/read-set-pricing.ts +7 -5
  41. package/scripts/epic/epic-candidate-confirm.ts +1 -1
  42. package/scripts/epic/epic-cross-repo.ts +4 -4
  43. package/scripts/epic/epic-next.ts +1 -1
  44. package/scripts/git/git-spawn-sync.ts +9 -1
  45. package/scripts/hooks/stop-guard.ts +20 -9
  46. package/scripts/init/init-ai-copy.ts +10 -3
  47. package/scripts/init/init-copy-content.ts +8 -1
  48. package/scripts/init/security-policy.ts +79 -0
  49. package/scripts/josh/josh-commands-ai.ts +1 -1
  50. package/scripts/josh/josh-commands-maintenance.ts +3 -0
  51. package/scripts/josh/josh-commands-project.ts +1 -1
  52. package/scripts/josh/josh-commands-versioning.ts +2 -1
  53. package/scripts/lane/lane-cli.ts +1 -3
  54. package/scripts/propagate/propagate-run.ts +17 -1
  55. package/scripts/propagate/propagate-steps.ts +9 -1
  56. package/scripts/run/board/run-board-header.ts +107 -84
  57. package/scripts/run/board/run-board-labels.ts +18 -17
  58. package/scripts/run/board/run-board-machine.ts +21 -8
  59. package/scripts/run/board/run-board-render.ts +1 -5
  60. package/scripts/run/board/run-board-span.ts +0 -1
  61. package/scripts/run/board/run-board-status.ts +0 -17
  62. package/scripts/run/board/run-board-tick.ts +1 -0
  63. package/scripts/run/carry/run-carry-cli.ts +8 -62
  64. package/scripts/run/carry/run-carry-ended.ts +1 -1
  65. package/scripts/run/carry/run-carry.ts +22 -68
  66. package/scripts/run/cut/run-cut.ts +4 -4
  67. package/scripts/run/event/run-event-render.ts +0 -1
  68. package/scripts/run/event/run-event-scope.ts +4 -8
  69. package/scripts/run/event/run-event-stream-emit.ts +0 -25
  70. package/scripts/run/event/run-event-stream.ts +23 -31
  71. package/scripts/run/merge/run-merge-collect.ts +1 -1
  72. package/scripts/run/merge/run-merge-steps.ts +1 -1
  73. package/scripts/run/progress/run-progress-read.ts +1 -1
  74. package/scripts/run/run-headless.ts +2 -2
  75. package/scripts/run/run-status-cli.ts +1 -5
  76. package/scripts/run/run-step.ts +2 -9
  77. package/scripts/run/run-stop-notify.ts +1 -1
  78. package/scripts/run/run-stranded-detect.ts +3 -3
  79. package/scripts/run/run-stranded.ts +25 -17
  80. package/scripts/run/wake/run-wake-cli.ts +7 -9
  81. package/scripts/run/wake/run-wake-describe.ts +3 -5
  82. package/scripts/run/wake/run-wake-driver.ts +3 -3
  83. package/scripts/run/wake/run-wake-loop.ts +4 -21
  84. package/scripts/run/wake/run-wake-restart.ts +1 -1
  85. package/scripts/run/wake/run-wake.ts +6 -7
  86. package/scripts/sync/sync-ai-files.ts +2 -0
  87. package/scripts/version/latest-update.ts +20 -6
  88. package/scripts/version/peer-floor.ts +99 -0
  89. package/scripts/version/playwright-browsers.ts +109 -0
  90. package/scripts/version/publishable-range-check.ts +41 -4
  91. package/templates/SECURITY.md +23 -0
  92. package/scripts/backlog/backlog-idle.ts +0 -88
@@ -25,134 +25,6 @@ function is_record(value) {
25
25
  }
26
26
  var json_value = { parse_or_undefined, parse_with, is_record };
27
27
 
28
- // scripts/backlog/backlog-budget.ts
29
- var MS_PER_MINUTE = 6e4;
30
- var MINUTES_PER_HOUR = 60;
31
- var WHOLE_RUN_BUDGET_HOURS = 8;
32
- var WHOLE_RUN_BUDGET_MINUTES = WHOLE_RUN_BUDGET_HOURS * MINUTES_PER_HOUR;
33
- var WHOLE_RUN_BUDGET_MS = WHOLE_RUN_BUDGET_MINUTES * MS_PER_MINUTE;
34
- var DEFAULT_IDLE_MINUTES = 30;
35
- var DEFAULT_IDLE_MS = DEFAULT_IDLE_MINUTES * MS_PER_MINUTE;
36
- var IDLE_POLL_MINUTES = 5;
37
- var RUN_VERDICT = "run";
38
- var WATCH_VERDICT = "watch";
39
- var STOP_VERDICT = "stop";
40
- var TRIAGE_VERDICT = "triage";
41
- var ANSWERS = ["blocked", "candidates", "exhausted", "parked", "unreadable", "untriaged"];
42
- var WHOLE_RUN_REASON = `The whole-run bound of ${String(WHOLE_RUN_BUDGET_HOURS)} hours has passed. An unattended run that has not finished overnight needs a person, not more waiting.`;
43
- var PARKED_REASON = "Nothing opted in can proceed without a person. Report the parked issues and finish.";
44
- var UNREADABLE_REASON = "The backlog listing could not be read, so nothing was offered. Report what the command printed and finish \u2014 it is not an empty backlog.";
45
- var BLOCKED_REASON = "Everything opted in is blocked or already running, so waiting can still change the answer. Poll again at the polling interval.";
46
- var TRIAGE_REASON = "An opted-in candidate carries neither `run:solo` nor `run:lane`, so nothing starts until it is judged. Read each issue named above, record its order and one of the two labels with a comment, then ask again.";
47
- var NO_IDLE_WATCH_REASON = "The backlog is empty and the idle watch was turned off with `--idle 0`, so the run finishes here.";
48
- function to_minutes(duration_ms) {
49
- return String(Math.ceil(duration_ms / MS_PER_MINUTE));
50
- }
51
- function max_reached_reason(merged, max_issues) {
52
- return `This run's maximum of ${String(max_issues)} is reached, with ${String(merged)} merged. Report what was done, including how many were picked up during an idle watch, and finish.`;
53
- }
54
- function draining_reason(running, reason) {
55
- return `Start nothing more and poll until the running children merge (running: ${String(running)}). Then report and finish \u2014 ${reason}`;
56
- }
57
- function idle_expired_reason(idle_budget_ms) {
58
- return `The backlog stayed empty for the whole ${to_minutes(idle_budget_ms)}-minute idle watch. Report what was done, including how many were picked up during the watch, and finish.`;
59
- }
60
- function next_ask_minutes(left_ms) {
61
- const left_minutes = Number(to_minutes(left_ms));
62
- return String(Math.min(IDLE_POLL_MINUTES, left_minutes));
63
- }
64
- function idle_watch_reason(left_ms) {
65
- return `The backlog is empty and the idle watch has about ${to_minutes(left_ms)} minutes left. Release the working tree, ask again in ${next_ask_minutes(left_ms)} minutes, and restart the watch the moment a candidate appears.`;
66
- }
67
- function running_watch_reason(running, left_ms) {
68
- return `The backlog is empty and the idle watch has about ${to_minutes(left_ms)} minutes left, with ${String(running)} still running. Keep the working tree until they merge and poll at the polling interval, not the idle poll \u2014 the watch is running down meanwhile.`;
69
- }
70
- function watching_reason(input, left_ms) {
71
- if (input.running === 0) return idle_watch_reason(left_ms);
72
- return running_watch_reason(input.running, left_ms);
73
- }
74
- function taken_by(input) {
75
- return input.merged + input.running;
76
- }
77
- function run_reason(input) {
78
- const { max_issues } = input;
79
- if (max_issues === void 0) return "No maximum was given, so start every issue offered.";
80
- return `Start at most ${String(max_issues - taken_by(input))} more: this run's maximum is ${String(max_issues)}, with ${String(input.merged)} merged and ${String(input.running)} running.`;
81
- }
82
- var ANSWER_STOP_REASONS = {
83
- parked: PARKED_REASON,
84
- unreadable: UNREADABLE_REASON
85
- };
86
- function whole_run_reason(input) {
87
- if (input.now_ms - input.started_at_ms < WHOLE_RUN_BUDGET_MS) return void 0;
88
- return WHOLE_RUN_REASON;
89
- }
90
- function stop_reason(input) {
91
- return ANSWER_STOP_REASONS[input.answer] ?? whole_run_reason(input);
92
- }
93
- function stopping(input, reason) {
94
- if (input.running === 0) return { verdict: STOP_VERDICT, reason };
95
- return { verdict: WATCH_VERDICT, reason: draining_reason(input.running, reason) };
96
- }
97
- function max_decision(input) {
98
- const { max_issues } = input;
99
- if (max_issues === void 0 || taken_by(input) < max_issues) return void 0;
100
- return stopping(input, max_reached_reason(input.merged, max_issues));
101
- }
102
- function idle_decision(input) {
103
- const { idle_budget_ms } = input;
104
- if (idle_budget_ms === void 0) return stopping(input, NO_IDLE_WATCH_REASON);
105
- const left_ms = idle_budget_ms - (input.now_ms - input.active_at_ms);
106
- if (left_ms <= 0) return stopping(input, idle_expired_reason(idle_budget_ms));
107
- return { verdict: WATCH_VERDICT, reason: watching_reason(input, left_ms) };
108
- }
109
- function is_finish(input) {
110
- return stop_reason(input) === void 0 && max_decision(input) === void 0;
111
- }
112
- var FIXED_DECISIONS = {
113
- blocked: { verdict: WATCH_VERDICT, reason: BLOCKED_REASON },
114
- untriaged: { verdict: TRIAGE_VERDICT, reason: TRIAGE_REASON }
115
- };
116
- function answer_decision(input) {
117
- if (input.answer === "candidates") return { verdict: RUN_VERDICT, reason: run_reason(input) };
118
- return FIXED_DECISIONS[input.answer] ?? idle_decision(input);
119
- }
120
- function decide(input) {
121
- const reason = stop_reason(input);
122
- if (reason !== void 0) return stopping(input, reason);
123
- const capped = max_decision(input);
124
- if (capped !== void 0) return capped;
125
- return answer_decision(input);
126
- }
127
- var backlog_budget = {
128
- ANSWERS,
129
- BLOCKED_REASON,
130
- DEFAULT_IDLE_MINUTES,
131
- DEFAULT_IDLE_MS,
132
- IDLE_POLL_MINUTES,
133
- MS_PER_MINUTE,
134
- NO_IDLE_WATCH_REASON,
135
- PARKED_REASON,
136
- RUN_VERDICT,
137
- STOP_VERDICT,
138
- TRIAGE_REASON,
139
- TRIAGE_VERDICT,
140
- UNREADABLE_REASON,
141
- WATCH_VERDICT,
142
- WHOLE_RUN_BUDGET_HOURS,
143
- WHOLE_RUN_BUDGET_MS,
144
- WHOLE_RUN_REASON,
145
- decide,
146
- draining_reason,
147
- idle_expired_reason,
148
- idle_watch_reason,
149
- is_finish,
150
- max_reached_reason,
151
- run_reason,
152
- running_watch_reason,
153
- taken_by
154
- };
155
-
156
28
  // scripts/git/constants.ts
157
29
  import { platform } from "node:os";
158
30
  var GIT_COMMAND_UNIX = "/usr/bin/git";
@@ -521,6 +393,37 @@ function read_spawn_stderr(result) {
521
393
 
522
394
  // scripts/git/git-spawn-sync.ts
523
395
  import { execaSync } from "execa";
396
+
397
+ // scripts/git/git-location-environment.ts
398
+ var GIT_LOCATION_VARIABLES = [
399
+ "GIT_ALTERNATE_OBJECT_DIRECTORIES",
400
+ "GIT_COMMON_DIR",
401
+ "GIT_DIR",
402
+ "GIT_INDEX_FILE",
403
+ "GIT_NAMESPACE",
404
+ "GIT_OBJECT_DIRECTORY",
405
+ "GIT_WORK_TREE"
406
+ ];
407
+ function location_free_environment() {
408
+ return Object.fromEntries(GIT_LOCATION_VARIABLES.map((name) => [name, void 0]));
409
+ }
410
+ function forget(name) {
411
+ Reflect.deleteProperty(process.env, name);
412
+ }
413
+ function restore_one(name, value) {
414
+ if (value === void 0) forget(name);
415
+ else process.env[name] = value;
416
+ }
417
+ function clear_git_location_variables() {
418
+ const previous = GIT_LOCATION_VARIABLES.map((name) => [name, process.env[name]]);
419
+ for (const [name] of previous) forget(name);
420
+ return () => {
421
+ for (const [name, value] of previous) restore_one(name, value);
422
+ };
423
+ }
424
+ var git_location_environment = { clear_git_location_variables, location_free_environment };
425
+
426
+ // scripts/git/git-spawn-sync.ts
524
427
  var SUCCESS_EXIT_CODE = 0;
525
428
  var C_LOCALE = { LC_ALL: "C", LANGUAGE: "C" };
526
429
  function run2(args, options = {}) {
@@ -541,7 +444,10 @@ function read2(args, options = {}) {
541
444
  return result.exit_code === SUCCESS_EXIT_CODE ? result.stdout.trim() : void 0;
542
445
  }
543
446
  function origin_url(cwd) {
544
- return read2(["config", "--get", "remote.origin.url"], { cwd });
447
+ return read2(["config", "--get", "remote.origin.url"], {
448
+ cwd,
449
+ env: git_location_environment.location_free_environment()
450
+ });
545
451
  }
546
452
  function toplevel(timeout = GIT_TIMEOUT_MS) {
547
453
  return run2(["rev-parse", "--show-toplevel"], { env: C_LOCALE, timeout });
@@ -918,23 +824,6 @@ var process_identity = {
918
824
  resolve_own_start
919
825
  };
920
826
 
921
- // scripts/josh/stamp-record.ts
922
- var NONE_READ = { kind: "none" };
923
- var UNREADABLE_READ = { kind: "unreadable" };
924
- function is_older_than(iso, max_age_ms, now) {
925
- const at = Date.parse(iso);
926
- if (Number.isNaN(at)) return true;
927
- return now.getTime() - at > max_age_ms;
928
- }
929
- function classify(raw, spec, now) {
930
- if (raw === void 0) return NONE_READ;
931
- const record = spec.parse(raw);
932
- if (record === void 0) return UNREADABLE_READ;
933
- const is_expired2 = is_older_than(spec.timestamp_of(record), spec.max_age_ms, now);
934
- return { kind: is_expired2 ? "expired" : "carried", record };
935
- }
936
- var stamp_record = { classify, is_older_than };
937
-
938
827
  // scripts/backlog/backlog-budget-cli.ts
939
828
  import { fileURLToPath } from "node:url";
940
829
 
@@ -1082,55 +971,125 @@ var cli_flags = {
1082
971
  is_value_unusable
1083
972
  };
1084
973
 
1085
- // scripts/backlog/backlog-idle.ts
1086
- var TEXT_PATTERN = /^idle since (?<since>\S+) until (?<until>\S+) \((?<bound>idle|whole-run)\) asked (?<asked>\S+)$/u;
1087
- var IDLE_BOUND = "idle";
1088
- var WHOLE_RUN_BOUND = "whole-run";
1089
- function window_of(input) {
1090
- if (input.idle_budget_ms === void 0) return void 0;
1091
- const idle_until = input.active_at_ms + input.idle_budget_ms;
1092
- const run_until = input.started_at_ms + backlog_budget.WHOLE_RUN_BUDGET_MS;
1093
- const bound = run_until < idle_until ? WHOLE_RUN_BOUND : IDLE_BOUND;
1094
- return {
1095
- since_ms: input.active_at_ms,
1096
- until_ms: Math.min(idle_until, run_until),
1097
- bound,
1098
- asked_ms: input.now_ms
1099
- };
974
+ // scripts/backlog/backlog-budget.ts
975
+ var MS_PER_MINUTE = 6e4;
976
+ var MINUTES_PER_HOUR = 60;
977
+ var IDLE_WAIT_HOURS = 8;
978
+ var DEFAULT_IDLE_MINUTES = IDLE_WAIT_HOURS * MINUTES_PER_HOUR;
979
+ var DEFAULT_IDLE_MS = DEFAULT_IDLE_MINUTES * MS_PER_MINUTE;
980
+ var OFFER_INTERVAL_SECONDS = 60;
981
+ var MS_PER_SECOND3 = 1e3;
982
+ var OFFER_INTERVAL_MS = OFFER_INTERVAL_SECONDS * MS_PER_SECOND3;
983
+ var RUN_VERDICT = "run";
984
+ var WATCH_VERDICT = "watch";
985
+ var STOP_VERDICT = "stop";
986
+ var TRIAGE_VERDICT = "triage";
987
+ var ANSWERS = ["blocked", "candidates", "exhausted", "parked", "unreadable", "untriaged"];
988
+ var BLOCKED_REASON = "Everything opted in is blocked or already running, so waiting can still change the answer. Poll again at the polling interval.";
989
+ var TRIAGE_REASON = "An opted-in candidate carries neither `run:solo` nor `run:lane`, so nothing starts until it is judged. Read each issue named above, record its order and one of the two labels with a comment, then ask again.";
990
+ var EMPTY_BACKLOG = "The backlog is empty";
991
+ var WAITING_ON = {
992
+ exhausted: EMPTY_BACKLOG,
993
+ parked: "Nothing opted in can proceed without a person",
994
+ unreadable: "The backlog listing could not be read"
995
+ };
996
+ function waiting_on(answer) {
997
+ return WAITING_ON[answer] ?? EMPTY_BACKLOG;
998
+ }
999
+ function no_idle_watch_reason(answer) {
1000
+ return `${waiting_on(answer)} and the idle watch was turned off with \`--idle 0\`, so the run finishes here.`;
1001
+ }
1002
+ function to_minutes(duration_ms) {
1003
+ return String(Math.ceil(duration_ms / MS_PER_MINUTE));
1004
+ }
1005
+ function max_reached_reason(merged, max_issues) {
1006
+ return `This run's maximum of ${String(max_issues)} is reached, with ${String(merged)} merged. Report what was done, including how many were picked up during an idle watch, and finish.`;
1007
+ }
1008
+ function draining_reason(running, reason) {
1009
+ return `Start nothing more and poll until the running children merge (running: ${String(running)}). Then report and finish \u2014 ${reason}`;
1100
1010
  }
1101
- function iso_of(ms) {
1102
- return new Date(ms).toISOString();
1011
+ function idle_expired_reason(answer, idle_budget_ms) {
1012
+ return `${waiting_on(answer)}, and stayed so for the whole ${to_minutes(idle_budget_ms)}-minute idle watch. Report what was done, including how many were picked up during the watch, and finish.`;
1103
1013
  }
1104
- function text_of(window) {
1105
- const { since_ms, until_ms, bound, asked_ms } = window;
1106
- return `idle since ${iso_of(since_ms)} until ${iso_of(until_ms)} (${bound}) asked ${iso_of(asked_ms)}`;
1014
+ function idle_watch_reason(answer, left_ms) {
1015
+ return `${waiting_on(answer)} and the idle watch has about ${to_minutes(left_ms)} minutes left. Release the working tree, ask again in ${String(OFFER_INTERVAL_SECONDS)} seconds, and restart the watch the moment a candidate appears.`;
1107
1016
  }
1108
- function is_bound(value) {
1109
- return value === IDLE_BOUND || value === WHOLE_RUN_BOUND;
1017
+ function running_watch_reason(answer, running, left_ms) {
1018
+ return `${waiting_on(answer)} and the idle watch has about ${to_minutes(left_ms)} minutes left, with ${String(running)} still running. Keep the working tree until they merge and poll at the polling interval \u2014 the watch is running down meanwhile.`;
1110
1019
  }
1111
- function window_from(groups, bound) {
1112
- const since_ms = Date.parse(String(groups["since"]));
1113
- const until_ms = Date.parse(String(groups["until"]));
1114
- const asked_ms = Date.parse(String(groups["asked"]));
1115
- return Number.isNaN(since_ms + until_ms + asked_ms) ? void 0 : { since_ms, until_ms, bound, asked_ms };
1020
+ function watching_reason(input, left_ms) {
1021
+ if (input.running === 0) return idle_watch_reason(input.answer, left_ms);
1022
+ return running_watch_reason(input.answer, input.running, left_ms);
1116
1023
  }
1117
- function parse(text) {
1118
- const groups = TEXT_PATTERN.exec(text)?.groups;
1119
- if (groups === void 0 || !is_bound(groups["bound"])) return void 0;
1120
- return window_from(groups, groups["bound"]);
1024
+ function taken_by(input) {
1025
+ return input.merged + input.running;
1121
1026
  }
1122
- function next_check_ms(window) {
1123
- const poll_ms = backlog_budget.IDLE_POLL_MINUTES * backlog_budget.MS_PER_MINUTE;
1124
- return Math.min(window.asked_ms + poll_ms, window.until_ms);
1027
+ function run_reason(input) {
1028
+ const { max_issues } = input;
1029
+ if (max_issues === void 0) return "No maximum was given, so start every issue offered.";
1030
+ return `Start at most ${String(max_issues - taken_by(input))} more: this run's maximum is ${String(max_issues)}, with ${String(input.merged)} merged and ${String(input.running)} running.`;
1125
1031
  }
1126
- var backlog_idle = { next_check_ms, parse, text_of, window_of };
1032
+ function stopping(input, reason) {
1033
+ if (input.running === 0) return { verdict: STOP_VERDICT, reason };
1034
+ return { verdict: WATCH_VERDICT, reason: draining_reason(input.running, reason) };
1035
+ }
1036
+ function max_decision(input) {
1037
+ const { max_issues } = input;
1038
+ if (max_issues === void 0 || taken_by(input) < max_issues) return void 0;
1039
+ return stopping(input, max_reached_reason(input.merged, max_issues));
1040
+ }
1041
+ function idle_decision(input) {
1042
+ const { idle_budget_ms } = input;
1043
+ if (idle_budget_ms === void 0) return stopping(input, no_idle_watch_reason(input.answer));
1044
+ const left_ms = idle_budget_ms - (input.now_ms - input.active_at_ms);
1045
+ if (left_ms > 0) return { verdict: WATCH_VERDICT, reason: watching_reason(input, left_ms) };
1046
+ return stopping(input, idle_expired_reason(input.answer, idle_budget_ms));
1047
+ }
1048
+ function is_finish(input) {
1049
+ return input.answer === "exhausted" && max_decision(input) === void 0;
1050
+ }
1051
+ var FIXED_DECISIONS = {
1052
+ blocked: { verdict: WATCH_VERDICT, reason: BLOCKED_REASON },
1053
+ untriaged: { verdict: TRIAGE_VERDICT, reason: TRIAGE_REASON }
1054
+ };
1055
+ function answer_decision(input) {
1056
+ if (input.answer === "candidates") return { verdict: RUN_VERDICT, reason: run_reason(input) };
1057
+ return FIXED_DECISIONS[input.answer] ?? idle_decision(input);
1058
+ }
1059
+ function decide(input) {
1060
+ return max_decision(input) ?? answer_decision(input);
1061
+ }
1062
+ var backlog_budget = {
1063
+ ANSWERS,
1064
+ BLOCKED_REASON,
1065
+ DEFAULT_IDLE_MINUTES,
1066
+ DEFAULT_IDLE_MS,
1067
+ IDLE_WAIT_HOURS,
1068
+ MS_PER_MINUTE,
1069
+ OFFER_INTERVAL_MS,
1070
+ RUN_VERDICT,
1071
+ STOP_VERDICT,
1072
+ TRIAGE_REASON,
1073
+ TRIAGE_VERDICT,
1074
+ WAITING_ON,
1075
+ WATCH_VERDICT,
1076
+ decide,
1077
+ draining_reason,
1078
+ idle_expired_reason,
1079
+ idle_watch_reason,
1080
+ is_finish,
1081
+ max_reached_reason,
1082
+ no_idle_watch_reason,
1083
+ run_reason,
1084
+ running_watch_reason,
1085
+ taken_by
1086
+ };
1127
1087
 
1128
1088
  // scripts/backlog/backlog-budget-cli.ts
1129
1089
  var ARGV_OFFSET = 2;
1130
1090
  var SUCCESS_EXIT_CODE2 = 0;
1131
1091
  var FAILURE_EXIT_CODE2 = 1;
1132
1092
  var JSON_KEY = "budget";
1133
- var EXHAUSTED_ANSWER = "exhausted";
1134
1093
  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]`;
1135
1094
  var OPTIONS = {
1136
1095
  active: { type: "string" },
@@ -1209,18 +1168,10 @@ function build_input(values, now_ms) {
1209
1168
  if (!are_values_readable(values)) return void 0;
1210
1169
  return input_of(values, { answer, now_ms, started_at_ms });
1211
1170
  }
1212
- function idle_of(input, verdict) {
1213
- const is_idle_watch = verdict === backlog_budget.WATCH_VERDICT && input.answer === EXHAUSTED_ANSWER && backlog_budget.is_finish(input);
1214
- const window = is_idle_watch ? backlog_idle.window_of(input) : void 0;
1215
- return window === void 0 ? void 0 : backlog_idle.text_of(window);
1216
- }
1217
1171
  function print_json(input, decision) {
1218
1172
  const { verdict, reason } = decision;
1219
- const idle = idle_of(input, verdict);
1220
1173
  const is_finish2 = backlog_budget.is_finish(input);
1221
- console.info(
1222
- JSON.stringify({ [JSON_KEY]: verdict, reason, is_finish: is_finish2, ...idle !== void 0 && { idle } })
1223
- );
1174
+ console.info(JSON.stringify({ [JSON_KEY]: verdict, reason, is_finish: is_finish2 }));
1224
1175
  }
1225
1176
  function refuse() {
1226
1177
  console.error(USAGE);
@@ -1329,7 +1280,7 @@ function argument_tokens(invocation) {
1329
1280
  tokens: true
1330
1281
  })?.tokens;
1331
1282
  }
1332
- function parse2(invocation) {
1283
+ function parse(invocation) {
1333
1284
  const tokens = argument_tokens(invocation);
1334
1285
  if (tokens === void 0) return void 0;
1335
1286
  const stop = tokens.findIndex((token) => token.kind !== "positional");
@@ -1346,7 +1297,7 @@ function joined(command, rendered) {
1346
1297
  return [command, ...rendered].join(TOKEN_SEPARATOR);
1347
1298
  }
1348
1299
  function rebuild(invocation) {
1349
- const parsed = parse2(invocation);
1300
+ const parsed = parse(invocation);
1350
1301
  if (parsed === void 0) return void 0;
1351
1302
  return joined(BACKLOG_COMMAND, [
1352
1303
  ...parsed.issues.map((issue) => issue_token(issue)),
@@ -1357,12 +1308,12 @@ function issue_numbers(invocation) {
1357
1308
  const tokens = tokens_of(invocation);
1358
1309
  const stop = tokens.findIndex((token) => token.startsWith(FLAG_PREFIX));
1359
1310
  const leading = stop === NOT_FOUND ? tokens : tokens.slice(FIRST_TOKEN, stop);
1360
- const issues = parse2(leading.join(TOKEN_SEPARATOR))?.issues;
1311
+ const issues = parse(leading.join(TOKEN_SEPARATOR))?.issues;
1361
1312
  if (issues === void 0 || issues.length === EMPTY_LENGTH) return void 0;
1362
1313
  return issues;
1363
1314
  }
1364
1315
  function has_only(invocation) {
1365
- return parse2(invocation)?.flags.includes(ONLY_FLAG) === true;
1316
+ return parse(invocation)?.flags.includes(ONLY_FLAG) === true;
1366
1317
  }
1367
1318
  var run_invocation = {
1368
1319
  has_only,
@@ -1661,7 +1612,7 @@ var ASSISTANT_TYPE = "assistant";
1661
1612
  function is_priced_line(data) {
1662
1613
  return data.type === ASSISTANT_TYPE && data.message?.model !== SYNTHETIC_MODEL;
1663
1614
  }
1664
- function classify2(data) {
1615
+ function classify(data) {
1665
1616
  if (!is_priced_line(data)) return { kind: "skipped" };
1666
1617
  const usage = data.message?.usage;
1667
1618
  return usage ? { kind: "record", record: to_record(data, usage) } : { kind: "no_usage" };
@@ -1669,7 +1620,7 @@ function classify2(data) {
1669
1620
  function parse_line(line) {
1670
1621
  if (line.trim() === "") return { kind: "skipped" };
1671
1622
  const parsed = LINE_SCHEMA.safeParse(json_value.parse_or_undefined(line));
1672
- return parsed.success ? classify2(parsed.data) : { kind: "malformed" };
1623
+ return parsed.success ? classify(parsed.data) : { kind: "malformed" };
1673
1624
  }
1674
1625
  function later_instant(left, right) {
1675
1626
  if (left === void 0) return right;
@@ -2074,8 +2025,6 @@ var run_carry_conversation = {
2074
2025
  // scripts/run/carry/run-carry.ts
2075
2026
  var CARRY_PREFIX = "josh-run-carry-";
2076
2027
  var REPOSITORY_DIRECTORY_INDEX = 1;
2077
- var CARRY_MAX_AGE_HOURS = backlog_budget.WHOLE_RUN_BUDGET_HOURS;
2078
- var CARRY_MAX_AGE_MS = backlog_budget.WHOLE_RUN_BUDGET_MS;
2079
2028
  var NO_INCREMENT3 = 0;
2080
2029
  var MAX_CUTS = 6;
2081
2030
  var END_COMMAND = "pnpm josh run:carry --end";
@@ -2116,24 +2065,16 @@ async function repository_directory() {
2116
2065
  function parse_carry(raw) {
2117
2066
  return json_value.parse_with(raw, run_carry_schema);
2118
2067
  }
2119
- function started_at_of(carry) {
2120
- return carry.started_at;
2121
- }
2122
- var CARRY_SPEC = {
2123
- parse: parse_carry,
2124
- timestamp_of: started_at_of,
2125
- max_age_ms: CARRY_MAX_AGE_MS
2126
- };
2127
- function is_expired(carry, now) {
2128
- return stamp_record.is_older_than(started_at_of(carry), CARRY_MAX_AGE_MS, now);
2129
- }
2130
- function classify3(raw, now) {
2131
- const read3 = stamp_record.classify(raw, CARRY_SPEC, now);
2132
- if (!("record" in read3)) return read3;
2133
- return { kind: read3.kind, carry: read3.record };
2068
+ function classify2(raw) {
2069
+ if (raw === void 0) return { kind: "none" };
2070
+ const carry = parse_carry(raw);
2071
+ if (carry === void 0 || Number.isNaN(Date.parse(carry.started_at))) {
2072
+ return { kind: "unreadable" };
2073
+ }
2074
+ return { kind: "carried", carry };
2134
2075
  }
2135
- function read_carry(target, now = /* @__PURE__ */ new Date()) {
2136
- return classify3(stamp_file.read_stamp_text(target), now);
2076
+ function read_carry(target) {
2077
+ return classify2(stamp_file.read_stamp_text(target));
2137
2078
  }
2138
2079
  function owner_of(pid, read3 = process_identity.read_start, transcript = run_carry_conversation.own_transcript()) {
2139
2080
  return { pid, start: read3(pid), transcript };
@@ -2156,10 +2097,6 @@ function begin_carry(target, invocation, owner = NO_OWNER, now = /* @__PURE__ */
2156
2097
  const carry = fresh_carry(invocation, owner, now);
2157
2098
  return stamp_file.create_stamp(target, carry) ? carry : void 0;
2158
2099
  }
2159
- function replace_carry(target, invocation, owner = NO_OWNER, now = /* @__PURE__ */ new Date()) {
2160
- stamp_file.remove_stamp(target);
2161
- return begin_carry(target, invocation, owner, now);
2162
- }
2163
2100
  function adopt_carry(target, carry, owner = NO_OWNER) {
2164
2101
  const next = {
2165
2102
  ...carry,
@@ -2201,8 +2138,7 @@ function end_carry(target) {
2201
2138
  stamp_file.remove_stamp(target);
2202
2139
  }
2203
2140
  function retrospective_done_of(read3) {
2204
- if (read3.kind !== "carried" && read3.kind !== "expired") return false;
2205
- return read3.carry.retrospective === true;
2141
+ return read3.kind === "carried" && read3.carry.retrospective === true;
2206
2142
  }
2207
2143
  function remaining_of(carry) {
2208
2144
  if (carry === void 0) return void 0;
@@ -2218,9 +2154,6 @@ function done_note(carry) {
2218
2154
  function describe_carry(carry) {
2219
2155
  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)}`;
2220
2156
  }
2221
- function expired_message(carry) {
2222
- return `The ${String(CARRY_MAX_AGE_HOURS)}-hour whole-run bound is spent: ${describe_carry(carry)}. Clear it with \`${END_COMMAND}\`.`;
2223
- }
2224
2157
  function mismatch_message(carry, invocation) {
2225
2158
  return `A different invocation is carried here \u2014 ${describe_carry(carry)}. This one is \`${invocation}\`. End the standing record with \`${END_COMMAND}\` once you know its run is over, then begin again.`;
2226
2159
  }
@@ -2252,8 +2185,6 @@ function unknown_message() {
2252
2185
  return "This repository\u2019s git directory could not be read, so no run record was established.";
2253
2186
  }
2254
2187
  var run_carry = {
2255
- CARRY_MAX_AGE_HOURS,
2256
- CARRY_MAX_AGE_MS,
2257
2188
  END_COMMAND,
2258
2189
  MAX_CUTS,
2259
2190
  NO_OWNER,
@@ -2264,17 +2195,15 @@ var run_carry = {
2264
2195
  busy_message,
2265
2196
  carry_path,
2266
2197
  claim_command,
2267
- classify: classify3,
2198
+ classify: classify2,
2268
2199
  classify_claim,
2269
2200
  count_refused_message,
2270
2201
  is_at_cut_cap,
2271
2202
  describe_carry,
2272
2203
  end_carry,
2273
- expired_message,
2274
2204
  mismatch_message,
2275
2205
  fresh_carry,
2276
2206
  is_count_refused,
2277
- is_expired,
2278
2207
  is_foreign_live_owner,
2279
2208
  is_owner_live,
2280
2209
  lost_message,
@@ -2283,7 +2212,6 @@ var run_carry = {
2283
2212
  parse_carry,
2284
2213
  read_carry,
2285
2214
  remaining_of,
2286
- replace_carry,
2287
2215
  repository_directory,
2288
2216
  retrospective_done_of,
2289
2217
  standing_message,
@@ -2387,15 +2315,16 @@ export {
2387
2315
  ports,
2388
2316
  process_identity,
2389
2317
  process_owner_schema,
2390
- backlog_budget,
2391
2318
  PORCELAIN_FLAG,
2392
2319
  git_utilities,
2393
2320
  has_timed_out,
2394
2321
  git_spawn,
2322
+ GIT_LOCATION_VARIABLES,
2323
+ git_location_environment,
2395
2324
  git_spawn_sync,
2396
2325
  git_command,
2397
- stamp_record,
2398
2326
  cli_flags,
2327
+ backlog_budget,
2399
2328
  issue_number_shape,
2400
2329
  run_invocation,
2401
2330
  lane_paths,