@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.
- package/.claude/skills/dependency-update/SKILL.md +6 -3
- package/.claude/skills/workflow-commands/backlogrun-progress.md +2 -3
- package/.claude/skills/workflow-commands/backlogrun-steps.md +2 -4
- package/.claude/skills/workflow-commands/retrospective.md +1 -2
- package/dist/hooks/batch-guard.js +6 -6
- package/dist/hooks/{chunk-UTHMB4O2.js → chunk-2X2ART5G.js} +4 -33
- package/dist/hooks/{chunk-NZN2UFI6.js → chunk-4MGHBTOM.js} +162 -233
- package/dist/hooks/{chunk-YLBBSSTF.js → chunk-6AC4C2IO.js} +91 -67
- package/dist/hooks/{chunk-AKGCP4LX.js → chunk-6MAW7BCM.js} +12 -12
- package/dist/hooks/{chunk-JCRDBQXB.js → chunk-7VSZ2N4B.js} +3 -3
- package/dist/hooks/{chunk-GG72ANKI.js → chunk-KC6LUZZ2.js} +1 -1
- package/dist/hooks/{chunk-BZMPVFDY.js → chunk-KCF2YE7O.js} +5 -5
- package/dist/hooks/{chunk-7WKYHEGU.js → chunk-LC56R53A.js} +3 -3
- package/dist/hooks/{chunk-V4TKZHER.js → chunk-LOJTBU5V.js} +3 -3
- package/dist/hooks/{chunk-JGW24KKM.js → chunk-RYP3TSUJ.js} +3 -3
- package/dist/hooks/{chunk-SLSFIWTX.js → chunk-TT742SIO.js} +8 -5
- package/dist/hooks/{chunk-Z5TMJIPW.js → chunk-Y5R47QHU.js} +1 -1
- package/dist/hooks/{chunk-7UBFU3CL.js → chunk-YKUITFT7.js} +2 -2
- package/dist/hooks/codex-hook-adapter.js +13 -13
- package/dist/hooks/duplicate-read-guard.js +7 -7
- package/dist/hooks/format-edited.js +3 -3
- package/dist/hooks/inputs.json +190 -191
- package/dist/hooks/investigation-guard.js +7 -7
- package/dist/hooks/pretool-guard.js +12 -12
- package/dist/hooks/session-lang.js +3 -3
- package/dist/hooks/stop-guard.js +19 -15
- package/dist/josh.js +40 -5
- package/package.json +4 -4
- package/prompts/collaboration-workflow/upstream-interrupt.md +1 -1
- package/scripts/adopt/adopt-logic.ts +4 -1
- package/scripts/adopt/adopt.ts +7 -4
- package/scripts/backlog/backlog-budget-cli.ts +2 -20
- package/scripts/backlog/backlog-budget.ts +72 -109
- package/scripts/backlog/backlog-drive-cli.ts +10 -3
- package/scripts/backlog/backlog-drive.ts +5 -2
- package/scripts/backlog/backlog-offer-cli.ts +1 -15
- package/scripts/cost-runtime/context-cut-payback.ts +9 -4
- package/scripts/cost-runtime/cost-pricing.ts +94 -33
- package/scripts/document/document-byte-budget.ts +1 -1
- package/scripts/document/read-set-pricing.ts +7 -5
- package/scripts/epic/epic-candidate-confirm.ts +1 -1
- package/scripts/epic/epic-cross-repo.ts +4 -4
- package/scripts/epic/epic-next.ts +1 -1
- package/scripts/git/git-spawn-sync.ts +9 -1
- package/scripts/hooks/stop-guard.ts +20 -9
- package/scripts/init/init-ai-copy.ts +10 -3
- package/scripts/init/init-copy-content.ts +8 -1
- package/scripts/init/security-policy.ts +79 -0
- package/scripts/josh/josh-commands-ai.ts +1 -1
- package/scripts/josh/josh-commands-maintenance.ts +3 -0
- package/scripts/josh/josh-commands-project.ts +1 -1
- package/scripts/josh/josh-commands-versioning.ts +2 -1
- package/scripts/lane/lane-cli.ts +1 -3
- package/scripts/propagate/propagate-run.ts +17 -1
- package/scripts/propagate/propagate-steps.ts +9 -1
- package/scripts/run/board/run-board-header.ts +107 -84
- package/scripts/run/board/run-board-labels.ts +18 -17
- package/scripts/run/board/run-board-machine.ts +21 -8
- package/scripts/run/board/run-board-render.ts +1 -5
- package/scripts/run/board/run-board-span.ts +0 -1
- package/scripts/run/board/run-board-status.ts +0 -17
- package/scripts/run/board/run-board-tick.ts +1 -0
- package/scripts/run/carry/run-carry-cli.ts +8 -62
- package/scripts/run/carry/run-carry-ended.ts +1 -1
- package/scripts/run/carry/run-carry.ts +22 -68
- package/scripts/run/cut/run-cut.ts +4 -4
- package/scripts/run/event/run-event-render.ts +0 -1
- package/scripts/run/event/run-event-scope.ts +4 -8
- package/scripts/run/event/run-event-stream-emit.ts +0 -25
- package/scripts/run/event/run-event-stream.ts +23 -31
- package/scripts/run/merge/run-merge-collect.ts +1 -1
- package/scripts/run/merge/run-merge-steps.ts +1 -1
- package/scripts/run/progress/run-progress-read.ts +1 -1
- package/scripts/run/run-headless.ts +2 -2
- package/scripts/run/run-status-cli.ts +1 -5
- package/scripts/run/run-step.ts +2 -9
- package/scripts/run/run-stop-notify.ts +1 -1
- package/scripts/run/run-stranded-detect.ts +3 -3
- package/scripts/run/run-stranded.ts +25 -17
- package/scripts/run/wake/run-wake-cli.ts +7 -9
- package/scripts/run/wake/run-wake-describe.ts +3 -5
- package/scripts/run/wake/run-wake-driver.ts +3 -3
- package/scripts/run/wake/run-wake-loop.ts +4 -21
- package/scripts/run/wake/run-wake-restart.ts +1 -1
- package/scripts/run/wake/run-wake.ts +6 -7
- package/scripts/sync/sync-ai-files.ts +2 -0
- package/scripts/version/latest-update.ts +20 -6
- package/scripts/version/peer-floor.ts +99 -0
- package/scripts/version/playwright-browsers.ts +109 -0
- package/scripts/version/publishable-range-check.ts +41 -4
- package/templates/SECURITY.md +23 -0
- 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"], {
|
|
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-
|
|
1086
|
-
var
|
|
1087
|
-
var
|
|
1088
|
-
var
|
|
1089
|
-
|
|
1090
|
-
|
|
1091
|
-
|
|
1092
|
-
|
|
1093
|
-
|
|
1094
|
-
|
|
1095
|
-
|
|
1096
|
-
|
|
1097
|
-
|
|
1098
|
-
|
|
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
|
|
1102
|
-
return
|
|
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
|
|
1105
|
-
|
|
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
|
|
1109
|
-
return
|
|
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
|
|
1112
|
-
|
|
1113
|
-
|
|
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
|
|
1118
|
-
|
|
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
|
|
1123
|
-
const
|
|
1124
|
-
|
|
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
|
-
|
|
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
|
|
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 =
|
|
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 =
|
|
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
|
|
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
|
|
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 ?
|
|
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
|
|
2120
|
-
return
|
|
2121
|
-
|
|
2122
|
-
|
|
2123
|
-
|
|
2124
|
-
|
|
2125
|
-
|
|
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
|
|
2136
|
-
return
|
|
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
|
-
|
|
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:
|
|
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,
|