@joshuafolkken/kit 1.1471.0 → 1.1514.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/settings.json +6 -6
- package/.claude/skills/epic-commands/SKILL.md +2 -14
- package/.claude/skills/epic-commands/epic-bundle.md +9 -5
- package/.claude/skills/epic-commands/execution-waves.md +1 -2
- package/.claude/skills/verify-ui/SKILL.md +1 -2
- package/.claude/skills/workflow-commands/SKILL.md +7 -9
- package/.claude/skills/workflow-commands/background-commands.md +3 -7
- package/.claude/skills/workflow-commands/backlogrun-child.md +3 -10
- package/.claude/skills/workflow-commands/backlogrun-lanes.md +6 -15
- package/.claude/skills/workflow-commands/backlogrun-park.md +2 -8
- package/.claude/skills/workflow-commands/backlogrun-progress.md +10 -19
- package/.claude/skills/workflow-commands/backlogrun-recovery.md +8 -8
- package/.claude/skills/workflow-commands/backlogrun-steps.md +2 -10
- package/.claude/skills/workflow-commands/backlogrun.md +31 -50
- package/.claude/skills/workflow-commands/chain-rule.md +7 -6
- package/.claude/skills/workflow-commands/delegation.md +12 -8
- package/.claude/skills/workflow-commands/entry-sequence.md +1 -3
- package/.claude/skills/workflow-commands/followup.md +3 -6
- package/.claude/skills/workflow-commands/fullrun-steps.md +3 -5
- package/.claude/skills/workflow-commands/fullrun.md +4 -6
- package/.claude/skills/workflow-commands/halfrun.md +3 -7
- package/.claude/skills/workflow-commands/into-target.md +1 -2
- package/.claude/skills/workflow-commands/issue-comments.md +3 -3
- package/.claude/skills/workflow-commands/issue-scout.md +3 -2
- package/.claude/skills/workflow-commands/latest-gate.md +2 -6
- package/.claude/skills/workflow-commands/needs-human-review.md +2 -5
- package/.claude/skills/workflow-commands/observation-filing.md +0 -14
- package/.claude/skills/workflow-commands/observation-ledger.md +4 -9
- package/.claude/skills/workflow-commands/pre-gate-cut.md +11 -16
- package/.claude/skills/workflow-commands/prerequisite.md +1 -2
- package/.claude/skills/workflow-commands/progress-watcher.md +2 -7
- package/.claude/skills/workflow-commands/prrun.md +3 -5
- package/.claude/skills/workflow-commands/retrospective.md +4 -4
- package/.claude/skills/workflow-commands/split-assessment.md +1 -3
- package/.claude/skills/workflow-commands/target-repository.md +1 -2
- package/.claude/skills/workflow-commands/working-tree-hold.md +2 -4
- package/.codex/hooks.json +6 -6
- package/.github/actions/setup-node/action.yml +1 -1
- package/.github/workflows/ci.yml +1 -1
- package/.github/workflows/publish.yml +2 -2
- package/CLAUDE.md +1 -1
- package/cspell/index.yaml +7 -0
- package/dist/CLAUDE.md +1 -1
- package/dist/commands/document-section-cli.js +67 -34
- package/dist/hooks/batch-guard.js +6 -6
- package/dist/hooks/chunk-4KKD2UTK.js +66 -0
- package/dist/hooks/{chunk-J47HU5H5.js → chunk-6TS2JD6D.js} +3 -3
- package/dist/hooks/chunk-7DSBJVFH.js +50 -0
- package/dist/hooks/{chunk-YHBZAJSV.js → chunk-A65Y4YOR.js} +3 -3
- package/dist/hooks/{chunk-E7OIKX5Z.js → chunk-APUO6U4J.js} +85 -7
- package/dist/hooks/{chunk-IX6TTTI6.js → chunk-EFXE6C37.js} +8 -22
- package/dist/hooks/chunk-GP4IEC4O.js +20 -0
- package/dist/hooks/{chunk-SHGPANFB.js → chunk-JP45SOCJ.js} +3 -3
- package/dist/hooks/{chunk-K4EOONYM.js → chunk-LAOV7NLM.js} +1 -1
- package/dist/hooks/chunk-M4HGZV7U.js +83 -0
- package/dist/hooks/{chunk-KC5AK2GF.js → chunk-O6GDRNJB.js} +3 -3
- package/dist/hooks/{chunk-YYPI7SJZ.js → chunk-PF2NZL2A.js} +5 -5
- package/dist/hooks/{chunk-H5HVWYXK.js → chunk-PTICX4OL.js} +3 -4
- package/dist/hooks/{chunk-IIGFYXKC.js → chunk-RT27QRJW.js} +581 -1438
- package/dist/hooks/{chunk-43ZHT22P.js → chunk-SYOTDYCI.js} +1127 -1089
- package/dist/hooks/{chunk-AS74OU3D.js → chunk-TNUMY6NL.js} +1507 -1336
- package/dist/hooks/chunk-UPUZF3AN.js +279 -0
- package/dist/hooks/{chunk-EUMACUK2.js → chunk-WK6SMBIC.js} +1948 -806
- package/dist/hooks/codex-hook-adapter.js +17 -14
- package/dist/hooks/duplicate-read-guard.js +7 -7
- package/dist/hooks/format-edited.js +5 -4
- package/dist/hooks/inputs.json +317 -264
- package/dist/hooks/investigation-guard.js +8 -11
- package/dist/hooks/pretool-guard.js +16 -13
- package/dist/hooks/session-lang.js +370 -25
- package/dist/hooks/stop-guard.js +2576 -176
- package/dist/josh.js +2124 -277
- package/lefthook/base.yml +5 -3
- package/package.json +12 -10
- package/prompts/collaboration-workflow/file-edits.md +1 -3
- package/prompts/collaboration-workflow/gh-rest.md +1 -2
- package/prompts/collaboration-workflow/issue-citation.md +2 -4
- package/prompts/collaboration-workflow/issue-template.md +10 -2
- package/prompts/collaboration-workflow/lane-instructions.md +35 -0
- package/prompts/collaboration-workflow/operating-rules.md +2 -6
- package/prompts/collaboration-workflow/output-bounds.md +3 -3
- package/prompts/collaboration-workflow/overview.md +1 -1
- package/prompts/collaboration-workflow/principles.md +14 -8
- package/prompts/collaboration-workflow/report-format.md +9 -0
- package/prompts/collaboration-workflow/residency.md +12 -8
- package/prompts/collaboration-workflow/rule-delivery.md +3 -11
- package/prompts/collaboration-workflow/shell-body.md +6 -6
- package/prompts/collaboration-workflow/turn-batching.md +5 -5
- package/prompts/collaboration-workflow/upstream-interrupt.md +2 -2
- package/prompts/collaboration-workflow/wip-cap.md +6 -6
- package/prompts/collaboration-workflow.md +1 -0
- package/prompts/review.md +4 -5
- package/prompts/testing-guide.md +2 -2
- package/scripts/agent/agent-argv.ts +20 -4
- package/scripts/agent/agent-role-profile.ts +101 -45
- package/scripts/agent/agent-safe-text.ts +60 -0
- package/scripts/agent/claude-agent-argv.ts +14 -0
- package/scripts/agent/codex-agent-argv.ts +2 -0
- package/scripts/agent/lane-instructions.ts +144 -0
- package/scripts/backlog/backlog-budget.ts +4 -4
- package/scripts/backlog/backlog-drive-named.ts +1 -1
- package/scripts/backlog/backlog-drive-restore.ts +9 -1
- package/scripts/backlog/backlog-occupied.ts +56 -0
- package/scripts/backlog/backlog-ready.ts +47 -10
- package/scripts/delegation/duplicate-read-guard.ts +2 -2
- package/scripts/delegation/investigation-guard.ts +15 -65
- package/scripts/delegation/investigation-reads.ts +0 -1
- package/scripts/document/document-byte-budget.ts +6 -5
- package/scripts/document/document-section-cli.ts +17 -5
- package/scripts/document/entry-read-budget.ts +2 -2
- package/scripts/document/read-set-cli.ts +4 -1
- package/scripts/epic/epic-audit-orphans.ts +5 -8
- package/scripts/epic/epic-audit.ts +52 -1
- package/scripts/epic/epic-bundle-evidence.ts +49 -26
- package/scripts/epic/epic-bundle-referenced.ts +14 -3
- package/scripts/epic/epic-bundle.ts +17 -9
- package/scripts/followup/git-followup-workflow.ts +19 -1
- package/scripts/gate/check-inputs.ts +129 -0
- package/scripts/gate/check-reuse.ts +162 -0
- package/scripts/gate/core-budget.ts +93 -44
- package/scripts/gate/core-wait.ts +130 -0
- package/scripts/gate/free-cores.ts +49 -0
- package/scripts/gate/gate-ledger.ts +17 -3
- package/scripts/gate/gate-plan.ts +23 -1
- package/scripts/gate/gate-report.ts +15 -2
- package/scripts/gate/gate-run-steps.ts +150 -0
- package/scripts/gate/held-mark.ts +55 -0
- package/scripts/gate/machine-capacity.ts +70 -11
- package/scripts/gate/pre-commit-type-check.ts +14 -1
- package/scripts/gate/verification-gate.ts +58 -125
- package/scripts/gh/git-gh-issue-rest.ts +1 -0
- package/scripts/gh/git-gh-issue-write.ts +24 -2
- package/scripts/gh/git-pr.ts +13 -1
- package/scripts/git/main-merge-regenerate.ts +78 -0
- package/scripts/git/main-merge.ts +14 -5
- package/scripts/hooks/batch-guard.ts +5 -8
- package/scripts/hooks/pre-commit-lint.ts +91 -0
- package/scripts/hooks/pre-push-unit.ts +1 -1
- package/scripts/hooks/pretool-guard.ts +27 -52
- package/scripts/hooks/reserved-run.ts +1 -0
- package/scripts/hooks/run-hook.sh +10 -12
- package/scripts/hooks/stop-actions.ts +204 -0
- package/scripts/hooks/stop-guard.ts +62 -21
- package/scripts/init/hook-launch.ts +12 -7
- package/scripts/issue/expected-effect-lint.ts +59 -0
- package/scripts/issue/issue-cite-cli.ts +35 -15
- package/scripts/issue/issue-classification.ts +1 -1
- package/scripts/issue/issue-lint-cli.ts +5 -3
- package/scripts/issue/issue-scout-cli.ts +19 -14
- package/scripts/issue/markdown-section.ts +19 -5
- package/scripts/josh/command-usage.ts +110 -0
- package/scripts/josh/josh-command-map.ts +2 -0
- package/scripts/josh/josh-command-types.ts +55 -9
- package/scripts/josh/josh-commands-ai.ts +13 -0
- package/scripts/josh/josh-commands-development.ts +1 -1
- package/scripts/josh/josh-commands-hooks.ts +6 -0
- package/scripts/josh/josh-commands-lane.ts +10 -1
- package/scripts/josh/josh-commands-workflow.ts +5 -0
- package/scripts/josh/josh-logic.ts +6 -15
- package/scripts/josh/josh-usage-automation.ts +121 -0
- package/scripts/josh/josh-usage-backlog.ts +180 -0
- package/scripts/josh/josh-usage-developer.ts +230 -0
- package/scripts/josh/josh-usage-hooks.ts +38 -0
- package/scripts/josh/josh-usage-issue.ts +205 -0
- package/scripts/josh/josh-usage-review.ts +101 -0
- package/scripts/josh/josh-usage-run.ts +252 -0
- package/scripts/josh/josh-usage-session.ts +238 -0
- package/scripts/josh/josh-usage-workflow.ts +212 -0
- package/scripts/josh/josh.ts +14 -5
- package/scripts/josh/package-directory.ts +20 -0
- package/scripts/lane/lane-capacity.ts +8 -2
- package/scripts/lane/lane-check-stats.ts +56 -0
- package/scripts/lane/lane-child-invocation.ts +35 -4
- package/scripts/lane/lane-dispatch.ts +3 -3
- package/scripts/lane/lane-guard-policy.ts +2 -2
- package/scripts/lane/lane-handoff.ts +8 -1
- package/scripts/lane/lane-install.ts +12 -4
- package/scripts/lane/lane-ledger.ts +43 -1
- package/scripts/lane/lane-limit-cli.ts +8 -9
- package/scripts/lane/lane-measure-cli.ts +2 -0
- package/scripts/lane/lane-preserve.ts +29 -1
- package/scripts/lane/lane-registry.ts +9 -7
- package/scripts/lane/lane-restart.ts +5 -26
- package/scripts/lane/lane-session-action.ts +146 -0
- package/scripts/lane/lane-session-broker.ts +57 -138
- package/scripts/lane/lane-withdraw-cli.ts +71 -0
- package/scripts/lane/lane-withdraw.ts +182 -0
- package/scripts/lint/format.ts +4 -4
- package/scripts/lint/lint-parallel.ts +10 -3
- package/scripts/lint/lint-related-scope.ts +4 -1
- package/scripts/notify/telegram-test.ts +10 -5
- package/scripts/report/report-format-reference.ts +2 -0
- package/scripts/review/acceptance-map-check.ts +61 -0
- package/scripts/review/acceptance-map.ts +205 -0
- package/scripts/review/review-brief-cli.ts +1 -1
- package/scripts/rules/agent-model.ts +59 -0
- package/scripts/rules/delivered-rules.ts +11 -2
- package/scripts/rules/filing-cap.ts +1 -0
- package/scripts/rules/firing-point.ts +6 -5
- package/scripts/rules/guard-remedy.ts +117 -0
- package/scripts/rules/issue-comments.ts +67 -6
- package/scripts/rules/last-prompt.ts +50 -5
- package/scripts/rules/oracle-list-cli.ts +7 -0
- package/scripts/rules/piped-verification.ts +3 -1
- package/scripts/rules/rule-list.ts +4 -1
- package/scripts/rules/rule-registry-rows.ts +10 -0
- package/scripts/rules/rule-registry.ts +7 -4
- package/scripts/rules/rule-rewrite.ts +114 -0
- package/scripts/rules/rule-value.ts +7 -1
- package/scripts/rules/shell-segments.ts +15 -0
- package/scripts/rules/stop-decision.ts +77 -0
- package/scripts/rules/stop-rules.ts +147 -47
- package/scripts/rules/tail-commands.ts +15 -1
- package/scripts/run/board/run-board-cli.ts +2 -0
- package/scripts/run/board/run-board-closed.ts +8 -2
- package/scripts/run/board/run-board-github.ts +9 -1
- package/scripts/run/board/run-board-header.ts +4 -1
- package/scripts/run/board/run-board-labels.ts +2 -0
- package/scripts/run/board/run-board-layout.ts +1 -1
- package/scripts/run/board/run-board-read.ts +16 -0
- package/scripts/run/board/run-board-reason.ts +10 -1
- package/scripts/run/board/run-board-render-legend.ts +1 -0
- package/scripts/run/board/run-board-render-slot.ts +1 -1
- package/scripts/run/board/run-board-render-waits.ts +41 -0
- package/scripts/run/board/run-board-render.ts +6 -1
- package/scripts/run/board/run-board-row-yield.ts +1 -0
- package/scripts/run/board/run-board-state.ts +5 -0
- package/scripts/run/board/run-board-status.ts +72 -21
- package/scripts/run/board/run-board-tick.ts +5 -1
- package/scripts/run/cut/run-cut-cli.ts +3 -2
- package/scripts/run/cut/run-cut-report.ts +16 -0
- package/scripts/run/detached-launch.ts +23 -4
- package/scripts/run/entry/run-entry-cli.ts +8 -1
- package/scripts/run/event/run-event-render.ts +1 -0
- package/scripts/run/event/run-event-scope.ts +19 -1
- package/scripts/run/event/run-event-stream-emit.ts +13 -6
- package/scripts/run/event/run-event-stream.ts +4 -0
- package/scripts/run/hold/run-hold-session.ts +46 -0
- package/scripts/run/hold/run-hold.ts +7 -1
- package/scripts/run/launch-priority.ts +32 -0
- package/scripts/run/merge/run-merge-cli.ts +16 -65
- package/scripts/run/merge/run-merge-signals.ts +99 -0
- package/scripts/run/merge/run-merge-steps.ts +11 -1
- package/scripts/run/merge/run-merge.ts +21 -9
- package/scripts/run/run-liveness.ts +1 -1
- package/scripts/run/run-poll-hook.ts +157 -0
- package/scripts/run/run-step-cli.ts +22 -15
- package/scripts/run/run-step-section.ts +85 -0
- package/scripts/run/ship/run-ship-cli.ts +5 -12
- package/scripts/run/ship/run-ship-detach.ts +9 -0
- package/scripts/run/ship/run-ship-next.ts +10 -2
- package/scripts/run/ship/run-ship-overlap.ts +195 -0
- package/scripts/run/ship/run-ship-pre-detach.ts +33 -15
- package/scripts/run/ship/run-ship-preflight.ts +28 -2
- package/scripts/run/ship/run-ship-return.ts +31 -7
- package/scripts/run/ship/run-ship-review-steps.ts +12 -10
- package/scripts/run/ship/run-ship-steps.ts +1 -1
- package/scripts/run/ship/run-ship-stop-context.ts +206 -0
- package/scripts/run/ship/run-ship-sync.ts +14 -6
- package/scripts/test/test-e2e-guard.ts +30 -5
- package/scripts/test/test-unit-guard.ts +16 -1
- package/scripts/test/unit-worker-share.ts +26 -4
- package/scripts/time-runtime/time-shell.ts +29 -4
- package/scripts/time-runtime/time-spans.ts +1 -0
- package/scripts/time-runtime/time-tool-call.ts +7 -0
- package/templates/gitignore +6 -2
- package/dist/hooks/chunk-K4LMBQC2.js +0 -68
|
@@ -2,11 +2,13 @@ import {
|
|
|
2
2
|
ENV_FILE_NAME,
|
|
3
3
|
cost_transcript,
|
|
4
4
|
json_value,
|
|
5
|
+
lane_paths,
|
|
6
|
+
pr_raw_schema,
|
|
5
7
|
process_identity,
|
|
6
8
|
process_owner_schema,
|
|
7
9
|
run_carry,
|
|
8
10
|
time_instant
|
|
9
|
-
} from "./chunk-
|
|
11
|
+
} from "./chunk-APUO6U4J.js";
|
|
10
12
|
import {
|
|
11
13
|
PLATFORM_TEMP_ROOT,
|
|
12
14
|
PROBE_TIMEOUT_MS,
|
|
@@ -100,1015 +102,438 @@ var COMMAND_PHASES = /* @__PURE__ */ new Map([
|
|
|
100
102
|
["josh followup", MERGE_PHASE]
|
|
101
103
|
]);
|
|
102
104
|
|
|
103
|
-
// scripts/
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
var CSPELL_CACHE_FILE = ".cspellcache";
|
|
109
|
-
var ESLINT_EDIT_CACHE_FILE = ".eslintcache.edit";
|
|
110
|
-
var ESLINT_RELATED_CACHE_FILE = ".eslintcache.related";
|
|
111
|
-
var ROOTED_JSON_PORTABILITY = "rooted-json";
|
|
112
|
-
var ESLINT_CACHE_SPEC = {
|
|
113
|
-
cache_file: ESLINT_CACHE_FILE,
|
|
114
|
-
portability: ROOTED_JSON_PORTABILITY
|
|
115
|
-
};
|
|
116
|
-
var TS_CACHE_SPEC = {
|
|
117
|
-
cache_file: TS_BUILD_INFO_FILE,
|
|
118
|
-
portability: "raw"
|
|
119
|
-
};
|
|
120
|
-
var CSPELL_CACHE_SPEC = {
|
|
121
|
-
cache_file: CSPELL_CACHE_FILE,
|
|
122
|
-
portability: "raw"
|
|
123
|
-
};
|
|
124
|
-
var GATE_CACHE_SPECS = [
|
|
125
|
-
ESLINT_CACHE_SPEC,
|
|
126
|
-
TS_CACHE_SPEC,
|
|
127
|
-
CSPELL_CACHE_SPEC
|
|
128
|
-
];
|
|
129
|
-
var GATE_CACHE_FILES = GATE_CACHE_SPECS.map((spec) => spec.cache_file);
|
|
130
|
-
var IGNORED_CACHE_FILES = [
|
|
131
|
-
...GATE_CACHE_FILES,
|
|
132
|
-
ESLINT_EDIT_CACHE_FILE,
|
|
133
|
-
ESLINT_RELATED_CACHE_FILE
|
|
134
|
-
];
|
|
135
|
-
var CONTENT_CACHE_FLAGS = ["--cache", "--cache-strategy", "content"];
|
|
136
|
-
var CACHE_LOCATION_FLAG = "--cache-location";
|
|
137
|
-
function content_cache_flags(cache_file) {
|
|
138
|
-
return [...CONTENT_CACHE_FLAGS, CACHE_LOCATION_FLAG, cache_file];
|
|
139
|
-
}
|
|
140
|
-
var ESLINT_CACHE_FLAGS = content_cache_flags(ESLINT_CACHE_FILE);
|
|
141
|
-
var ESLINT_EDIT_CACHE_FLAGS = content_cache_flags(ESLINT_EDIT_CACHE_FILE);
|
|
142
|
-
var ESLINT_RELATED_CACHE_FLAGS = content_cache_flags(ESLINT_RELATED_CACHE_FILE);
|
|
143
|
-
var CSPELL_CACHE_FLAGS = content_cache_flags(CSPELL_CACHE_FILE);
|
|
105
|
+
// scripts/gate/core-budget.ts
|
|
106
|
+
import { randomUUID } from "node:crypto";
|
|
107
|
+
import { readdirSync as readdirSync2 } from "node:fs";
|
|
108
|
+
import { availableParallelism as availableParallelism3 } from "node:os";
|
|
109
|
+
import path3 from "node:path";
|
|
144
110
|
|
|
145
|
-
// scripts/
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
}
|
|
170
|
-
|
|
171
|
-
script: "scripts/backlog/backlog-drive-cli.ts",
|
|
172
|
-
description: "Drive backlogrun through offer, launch and merge; restore this run\u2019s lanes and report when complete",
|
|
173
|
-
category: "AI tools",
|
|
174
|
-
reference: ["--owner <pid> [options]", "automation", ["network"]]
|
|
175
|
-
},
|
|
176
|
-
"backlog:plan": {
|
|
177
|
-
script: "scripts/backlog/backlog-plan-cli.ts",
|
|
178
|
-
description: "Print the whole backlog as a plan: ready now, waiting on what, waiting on a person, out of scope",
|
|
179
|
-
category: "AI tools",
|
|
180
|
-
reference: [
|
|
181
|
-
"[issue...] [--only] [--waves] [--exclude <n>[,<n>...]]",
|
|
182
|
-
"automation",
|
|
183
|
-
["network"]
|
|
184
|
-
]
|
|
185
|
-
},
|
|
186
|
-
"backlog:stalled": {
|
|
187
|
-
script: "scripts/backlog/backlog-stalled-cli.ts",
|
|
188
|
-
// A stall sends one Telegram warning. The `Stop` hook calls the detector
|
|
189
|
-
// directly, so this flag costs only the manual command its in-process dispatch.
|
|
190
|
-
tsx_arguments: OPTIONAL_ENV_FILE_FLAGS,
|
|
191
|
-
description: "Report whether ready backlog work is sitting undispatched with a free lane and no recent dispatch",
|
|
192
|
-
category: "AI tools",
|
|
193
|
-
reference: ["", "automation", ["network"]]
|
|
194
|
-
},
|
|
195
|
-
"backlog:budget": {
|
|
196
|
-
script: "scripts/backlog/backlog-budget-cli.ts",
|
|
197
|
-
description: "Say whether a backlogrun may start more work, keep watching, or finish",
|
|
198
|
-
category: "AI tools",
|
|
199
|
-
reference: ["[options]", "automation", ["none"]]
|
|
111
|
+
// scripts/test/unit-worker-share.ts
|
|
112
|
+
import { readdirSync } from "node:fs";
|
|
113
|
+
import { availableParallelism } from "node:os";
|
|
114
|
+
import path from "node:path";
|
|
115
|
+
var RUN_PREFIX = "josh-unit-running-";
|
|
116
|
+
var WORKER_FLAG = "--maxWorkers";
|
|
117
|
+
var WORKER_FLAGS = [WORKER_FLAG, "--max-workers"];
|
|
118
|
+
var MIN_WORKERS = 1;
|
|
119
|
+
var SOLO_RUNS = 1;
|
|
120
|
+
function marker_path(pid = process.pid) {
|
|
121
|
+
return stamp_file.stamp_path(RUN_PREFIX, String(pid));
|
|
122
|
+
}
|
|
123
|
+
function is_running(marker) {
|
|
124
|
+
return process_identity.is_same_process(marker.pid, marker.process_start) !== false;
|
|
125
|
+
}
|
|
126
|
+
function read_marker(source) {
|
|
127
|
+
const raw = stamp_file.read_stamp_text(source);
|
|
128
|
+
if (raw === void 0) return void 0;
|
|
129
|
+
const parsed = process_owner_schema.safeParse(json_value.parse_or_undefined(raw));
|
|
130
|
+
return parsed.success ? parsed.data : void 0;
|
|
131
|
+
}
|
|
132
|
+
function marker_files(directory = PLATFORM_TEMP_ROOT) {
|
|
133
|
+
try {
|
|
134
|
+
return readdirSync(directory).filter((name) => name.startsWith(RUN_PREFIX));
|
|
135
|
+
} catch {
|
|
136
|
+
return [];
|
|
200
137
|
}
|
|
201
|
-
}
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
script: "scripts/document/document-section-cli.ts",
|
|
207
|
-
description: 'Print one section of a markdown document, for a `file.md` \u2192 "Heading" reference',
|
|
208
|
-
category: "AI tools",
|
|
209
|
-
reference: ["<file> <heading>", "automation", ["none"]],
|
|
210
|
-
is_bundled: true
|
|
211
|
-
},
|
|
212
|
-
"read:set": {
|
|
213
|
-
script: "scripts/document/read-set-cli.ts",
|
|
214
|
-
description: "Print what an entry point reads before it starts, and what that read costs",
|
|
215
|
-
category: "AI tools",
|
|
216
|
-
reference: ["[<entry>] [--json]", "automation", ["none"]]
|
|
217
|
-
},
|
|
218
|
-
"doc:read": {
|
|
219
|
-
script: "scripts/document/document-read-cli.ts",
|
|
220
|
-
description: "Read a whole document safely: print it, or point at the Read tool when over the Bash cap",
|
|
221
|
-
category: "AI tools",
|
|
222
|
-
reference: ["<file>", "automation", ["none"]]
|
|
223
|
-
},
|
|
224
|
-
"read:files": {
|
|
225
|
-
script: "scripts/document/read-files-cli.ts",
|
|
226
|
-
description: "Read several files in one call so edit targets fold into one turn",
|
|
227
|
-
category: "AI tools",
|
|
228
|
-
reference: ["<path> [<path> ...]", "automation", ["none"]]
|
|
229
|
-
},
|
|
230
|
-
"edit:files": {
|
|
231
|
-
script: "scripts/document/edit-files-cli.ts",
|
|
232
|
-
description: "Apply several content-addressed edits from a plan in one call",
|
|
233
|
-
category: "AI tools",
|
|
234
|
-
reference: ["<plan-path | ->", "automation", ["none"]]
|
|
138
|
+
}
|
|
139
|
+
function sweep_marker(source) {
|
|
140
|
+
try {
|
|
141
|
+
stamp_file.remove_stamp(source);
|
|
142
|
+
} catch {
|
|
235
143
|
}
|
|
236
|
-
}
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
144
|
+
}
|
|
145
|
+
function is_marker_live(source) {
|
|
146
|
+
const marker = read_marker(source);
|
|
147
|
+
if (marker === void 0) return false;
|
|
148
|
+
if (is_running(marker)) return true;
|
|
149
|
+
sweep_marker(source);
|
|
150
|
+
return false;
|
|
151
|
+
}
|
|
152
|
+
function live_run_count(directory = PLATFORM_TEMP_ROOT) {
|
|
153
|
+
return marker_files(directory).filter((name) => is_marker_live(path.join(directory, name))).length;
|
|
154
|
+
}
|
|
155
|
+
var NESTED_KEY = "JOSH_UNIT_RUN_MARKED";
|
|
156
|
+
var NESTED_VALUE = "1";
|
|
157
|
+
function is_nested_run() {
|
|
158
|
+
return process.env[NESTED_KEY] === NESTED_VALUE;
|
|
159
|
+
}
|
|
160
|
+
function resolve_unit_workers(available_cores, runs2) {
|
|
161
|
+
if (runs2 <= SOLO_RUNS) return void 0;
|
|
162
|
+
return Math.max(MIN_WORKERS, Math.floor(available_cores / runs2));
|
|
163
|
+
}
|
|
164
|
+
function cap_to_free(workers, free_cores, available_cores) {
|
|
165
|
+
if (free_cores === void 0 || free_cores >= (workers ?? available_cores)) return workers;
|
|
166
|
+
return Math.max(MIN_WORKERS, Math.floor(free_cores));
|
|
167
|
+
}
|
|
168
|
+
function current_share(available_cores = availableParallelism(), live_runs = live_run_count(), free_cores) {
|
|
169
|
+
const own_run2 = is_nested_run() ? 0 : SOLO_RUNS;
|
|
170
|
+
const share = resolve_unit_workers(available_cores, live_runs + own_run2);
|
|
171
|
+
return cap_to_free(share, free_cores, available_cores);
|
|
172
|
+
}
|
|
173
|
+
function is_worker_flag(argument) {
|
|
174
|
+
return WORKER_FLAGS.some((flag) => argument === flag || argument.startsWith(`${flag}=`));
|
|
175
|
+
}
|
|
176
|
+
function has_worker_flag(vitest_arguments) {
|
|
177
|
+
return vitest_arguments.some((argument) => is_worker_flag(argument));
|
|
178
|
+
}
|
|
179
|
+
function worker_arguments(vitest_arguments, share) {
|
|
180
|
+
if (share === void 0 || has_worker_flag(vitest_arguments)) return [];
|
|
181
|
+
return [`${WORKER_FLAG}=${String(share)}`];
|
|
182
|
+
}
|
|
183
|
+
function mark_run_started(target2) {
|
|
184
|
+
try {
|
|
185
|
+
stamp_file.write_stamp(target2, process_identity.own_fields());
|
|
186
|
+
} catch {
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
async function with_run_marker(run) {
|
|
190
|
+
if (is_nested_run()) return await run();
|
|
191
|
+
const target2 = marker_path();
|
|
192
|
+
mark_run_started(target2);
|
|
193
|
+
process.env[NESTED_KEY] = NESTED_VALUE;
|
|
194
|
+
try {
|
|
195
|
+
return await run();
|
|
196
|
+
} finally {
|
|
197
|
+
process.env[NESTED_KEY] = "";
|
|
198
|
+
stamp_file.remove_stamp(target2);
|
|
257
199
|
}
|
|
200
|
+
}
|
|
201
|
+
var unit_worker_share = {
|
|
202
|
+
MIN_WORKERS,
|
|
203
|
+
NESTED_KEY,
|
|
204
|
+
NESTED_VALUE,
|
|
205
|
+
RUN_PREFIX,
|
|
206
|
+
SOLO_RUNS,
|
|
207
|
+
cap_to_free,
|
|
208
|
+
current_share,
|
|
209
|
+
has_worker_flag,
|
|
210
|
+
is_nested_run,
|
|
211
|
+
live_run_count,
|
|
212
|
+
marker_path,
|
|
213
|
+
read_marker,
|
|
214
|
+
resolve_unit_workers,
|
|
215
|
+
with_run_marker,
|
|
216
|
+
worker_arguments
|
|
258
217
|
};
|
|
259
218
|
|
|
260
|
-
// scripts/
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
reference: ["<title> [--body <summary> | --body-file <path>]", "automation", ["network"]]
|
|
286
|
-
},
|
|
287
|
-
"defect:rate": {
|
|
288
|
-
script: "scripts/issue/defect-rate-cli.ts",
|
|
289
|
-
description: "Print the defect rate of merged work: defects filed per enhancement completed",
|
|
290
|
-
category: "AI tools",
|
|
291
|
-
reference: ["[--days <n>]", "automation", ["network"]]
|
|
292
|
-
},
|
|
293
|
-
"issue:fold": {
|
|
294
|
-
script: "scripts/issue/issue-fold-cli.ts",
|
|
295
|
-
description: "Before a second filing: say whether findings from this session fold into one issue",
|
|
296
|
-
category: "AI tools",
|
|
297
|
-
reference: ["<title>... [--not-separable] [--json]", "automation", ["none"]]
|
|
298
|
-
},
|
|
299
|
-
"issue:fold-existing": {
|
|
300
|
-
script: "scripts/issue/issue-fold-existing-cli.ts",
|
|
301
|
-
description: "Assess whether a complete draft can join an unstarted issue",
|
|
302
|
-
category: "AI tools",
|
|
303
|
-
reference: ["<assessment.json> [--json]", "automation", ["none"]]
|
|
304
|
-
},
|
|
305
|
-
"issue:cite": {
|
|
306
|
-
script: "scripts/issue/issue-cite-cli.ts",
|
|
307
|
-
description: "Print the paste-ready number-link citation line for each issue, in one call",
|
|
308
|
-
category: "AI tools",
|
|
309
|
-
reference: [ISSUES_WITH_REPO, "automation", ["network"]]
|
|
310
|
-
},
|
|
311
|
-
"issue:comment": {
|
|
312
|
-
script: "scripts/issue/issue-comment-cli.ts",
|
|
313
|
-
description: "Post one comment to an issue from a file, so no shell expands the body",
|
|
314
|
-
category: "AI tools",
|
|
315
|
-
reference: ["<issue> --body <text> | --body-file <path>", "automation", ["network"]]
|
|
316
|
-
},
|
|
317
|
-
"issue:file": {
|
|
318
|
-
script: "scripts/issue/issue-file-cli.ts",
|
|
319
|
-
description: "File an Issue with every filing step: lint, Origin, fold, WIP cap, duplicate scout, labels, then epic:bundle",
|
|
320
|
-
category: "AI tools",
|
|
321
|
-
reference: [
|
|
322
|
-
"<title> --body-file <path> --depth <0|1|2> [--route <route>] [--label <name>] [--repo <owner/repo>] [--distinct <N,\u2026>] [--over-cap] [--no-auto-ok] [--release]",
|
|
323
|
-
"automation",
|
|
324
|
-
["network"]
|
|
325
|
-
]
|
|
326
|
-
},
|
|
327
|
-
"issue:release": {
|
|
328
|
-
script: "scripts/issue/issue-release-cli.ts",
|
|
329
|
-
description: "Link an Issue to the repository's open release Issue as its blocker (or, with --wait, as waiting on it), filing one when none is open",
|
|
330
|
-
category: "AI tools",
|
|
331
|
-
reference: ["<issue> [--wait]", "automation", ["network"]]
|
|
219
|
+
// scripts/gate/core-budget.ts
|
|
220
|
+
import { z as z2 } from "zod";
|
|
221
|
+
|
|
222
|
+
// scripts/gate/core-admission.ts
|
|
223
|
+
var MS_PER_SECOND = 1e3;
|
|
224
|
+
var RAMP_UP_MS = 3e4;
|
|
225
|
+
function compare_claim(left, right) {
|
|
226
|
+
const by_time = left.reservation.claimed_at - right.reservation.claimed_at;
|
|
227
|
+
return by_time === 0 ? left.key.localeCompare(right.key) : by_time;
|
|
228
|
+
}
|
|
229
|
+
function add_load(totals, reservation) {
|
|
230
|
+
totals.cores += reservation.weight;
|
|
231
|
+
totals.memory_mb += reservation.memory_mb ?? 0;
|
|
232
|
+
}
|
|
233
|
+
function exceeds(totals, budget) {
|
|
234
|
+
return totals.cores > budget.cores || totals.memory_mb > budget.memory_mb;
|
|
235
|
+
}
|
|
236
|
+
function admitted_keys(reservations, budget, memory_budget = Infinity) {
|
|
237
|
+
const limit = { cores: budget, memory_mb: memory_budget };
|
|
238
|
+
const totals = { cores: 0, memory_mb: 0 };
|
|
239
|
+
const admitted = /* @__PURE__ */ new Set();
|
|
240
|
+
for (const [index, entry] of reservations.toSorted(compare_claim).entries()) {
|
|
241
|
+
add_load(totals, entry.reservation);
|
|
242
|
+
if (index !== 0 && exceeds(totals, limit)) break;
|
|
243
|
+
admitted.add(entry.key);
|
|
332
244
|
}
|
|
245
|
+
return admitted;
|
|
246
|
+
}
|
|
247
|
+
function is_admitted(key, reservations, budget) {
|
|
248
|
+
return admitted_keys(reservations, budget.cores, budget.memory_mb).has(key);
|
|
249
|
+
}
|
|
250
|
+
function sum_load(reservations) {
|
|
251
|
+
const load = { cores: 0, memory_mb: 0 };
|
|
252
|
+
for (const entry of reservations) add_load(load, entry.reservation);
|
|
253
|
+
return load;
|
|
254
|
+
}
|
|
255
|
+
function is_ramped(reservation, now) {
|
|
256
|
+
const { admitted_at } = reservation;
|
|
257
|
+
return admitted_at !== void 0 && now - admitted_at >= RAMP_UP_MS;
|
|
258
|
+
}
|
|
259
|
+
function ledger_load(reservations, now) {
|
|
260
|
+
const admitted = reservations.filter((entry) => entry.reservation.admitted_at !== void 0);
|
|
261
|
+
const ramped = admitted.filter((entry) => is_ramped(entry.reservation, now));
|
|
262
|
+
return { cores: sum_load(admitted).cores, memory_mb: sum_load(ramped).memory_mb };
|
|
263
|
+
}
|
|
264
|
+
function total_weight(reservations) {
|
|
265
|
+
return sum_load(reservations).cores;
|
|
266
|
+
}
|
|
267
|
+
function admitted_load(reservations, budget) {
|
|
268
|
+
const admitted = admitted_keys(reservations, budget);
|
|
269
|
+
return total_weight(reservations.filter((entry) => admitted.has(entry.key)));
|
|
270
|
+
}
|
|
271
|
+
function describe_memory(ledger, budget) {
|
|
272
|
+
if (!Number.isFinite(budget.memory_mb)) return "";
|
|
273
|
+
return ` and ${String(Math.round(ledger.memory_mb))} MB of memory against ${String(Math.round(budget.memory_mb))} MB`;
|
|
274
|
+
}
|
|
275
|
+
function describe_overflow(admission) {
|
|
276
|
+
const { overflow } = admission;
|
|
277
|
+
if (overflow === void 0) return void 0;
|
|
278
|
+
const { ledger, budget } = overflow;
|
|
279
|
+
const waited = String(Math.round(overflow.waited_ms / MS_PER_SECOND));
|
|
280
|
+
const cores = `the ledger held ${String(ledger.cores)} cores against a budget of ${String(budget.cores)}`;
|
|
281
|
+
return `started past the core budget after ${waited}s \u2014 ${cores}${describe_memory(ledger, budget)}`;
|
|
282
|
+
}
|
|
283
|
+
var core_admission = {
|
|
284
|
+
RAMP_UP_MS,
|
|
285
|
+
admitted_keys,
|
|
286
|
+
admitted_load,
|
|
287
|
+
describe_overflow,
|
|
288
|
+
is_admitted,
|
|
289
|
+
ledger_load,
|
|
290
|
+
sum_load
|
|
333
291
|
};
|
|
334
292
|
|
|
335
|
-
// scripts/
|
|
336
|
-
|
|
337
|
-
var
|
|
338
|
-
var
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
293
|
+
// scripts/gate/core-wait.ts
|
|
294
|
+
import path2 from "node:path";
|
|
295
|
+
var WAIT_REASONS = ["cores", "memory", "pressure"];
|
|
296
|
+
var REASON_TEXT = {
|
|
297
|
+
cores: "not enough free cores",
|
|
298
|
+
memory: "not enough free memory",
|
|
299
|
+
pressure: "memory under pressure"
|
|
342
300
|
};
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
reference: ["", "automation", ["none"]],
|
|
363
|
-
default_script_arguments: ["list"]
|
|
364
|
-
},
|
|
365
|
-
"lane:prune": {
|
|
366
|
-
...LANE_ARGUMENTS,
|
|
367
|
-
description: "Close every lane an interruption left without its work tree and every spent lane, then sweep unregistered leftovers under the lanes root",
|
|
368
|
-
category: "AI tools",
|
|
369
|
-
reference: ["", "automation", ["files", "git"]],
|
|
370
|
-
default_script_arguments: ["prune"]
|
|
371
|
-
},
|
|
372
|
-
"lane:output": {
|
|
373
|
-
...LANE_ARGUMENTS,
|
|
374
|
-
description: "Record, or read back, where the unit running a lane\u2019s child writes",
|
|
375
|
-
category: "AI tools",
|
|
376
|
-
reference: ["<issue> [path]", "automation", ["files"]],
|
|
377
|
-
default_script_arguments: ["output"]
|
|
378
|
-
},
|
|
379
|
-
"lane:dispatch": {
|
|
380
|
-
...LANE_ARGUMENTS,
|
|
381
|
-
description: "Start a lane\u2019s child as a detached process that outlives this session",
|
|
382
|
-
category: "AI tools",
|
|
383
|
-
reference: ["<issue> <prompt>", "automation", ["processes"]],
|
|
384
|
-
default_script_arguments: ["dispatch"]
|
|
385
|
-
},
|
|
386
|
-
"lane:await": {
|
|
387
|
-
...LANE_ARGUMENTS,
|
|
388
|
-
description: "Block until any of the named in-flight lane children completes; prints which one finished",
|
|
389
|
-
category: "AI tools",
|
|
390
|
-
reference: ["<issue> [<issue>...] [--owner <pid>]", "automation", ["files", "processes"]],
|
|
391
|
-
default_script_arguments: ["await"]
|
|
392
|
-
},
|
|
393
|
-
"lane:launch": {
|
|
394
|
-
script: "scripts/lane/lane-launch-cli.ts",
|
|
395
|
-
description: "Collapse a backlogrun lane-start event into one call: open the lane, pop and re-install on the first, dispatch the child",
|
|
396
|
-
category: "AI tools",
|
|
397
|
-
reference: ["<issue> [--stash <message>]", "automation", ["files", "git", "processes"]]
|
|
398
|
-
},
|
|
399
|
-
// `.env` is read for `JOSH_LANE_ROOT`, as every lane command reads it, so the lane is found.
|
|
400
|
-
"lane:restart": {
|
|
401
|
-
script: "scripts/lane/lane-restart-cli.ts",
|
|
402
|
-
tsx_arguments: OPTIONAL_ENV_FILE_FLAGS,
|
|
403
|
-
description: "Stop a running lane child, stash its uncommitted work and re-dispatch its issue in a fresh session \u2014 not counted as an outage or a failure",
|
|
404
|
-
category: "AI tools",
|
|
405
|
-
reference: ["<issue>", "developer", ["files", "git", "processes", "network"]]
|
|
406
|
-
},
|
|
407
|
-
// A live run's lane limit: `.env` is read so the bare form reports the
|
|
408
|
-
// `JOSH_LANE_LIMIT` a person set there when no override stands.
|
|
409
|
-
"lane:limit": {
|
|
410
|
-
script: "scripts/lane/lane-limit-cli.ts",
|
|
411
|
-
tsx_arguments: OPTIONAL_ENV_FILE_FLAGS,
|
|
412
|
-
description: "Change a live backlogrun\u2019s lane limit without stopping it, or print the limit, lanes in use and free lanes",
|
|
413
|
-
category: "AI tools",
|
|
414
|
-
reference: ["[<limit>] [--reset]", "developer", ["files"]]
|
|
415
|
-
},
|
|
416
|
-
// The lane-limit measurement: `.env` is read so `lane:stats` labels the row
|
|
417
|
-
// with the `JOSH_LANE_LIMIT` a person set there.
|
|
418
|
-
"lane:sample": {
|
|
419
|
-
...MEASURE_ARGUMENTS,
|
|
420
|
-
description: "Record the machine load (load average, swap, free memory, working lanes) to the lane ledger \u2014 a backlogrun samples itself, this is for a reading outside one",
|
|
421
|
-
category: "AI tools",
|
|
422
|
-
reference: ["[--every <seconds>]", "maintainer", ["files"]],
|
|
423
|
-
default_script_arguments: ["sample"]
|
|
424
|
-
},
|
|
425
|
-
"lane:stats": {
|
|
426
|
-
...MEASURE_ARGUMENTS,
|
|
427
|
-
description: "Print one table row of lane throughput, gate duration and machine load over a period, the per-stage durations of a lane under it, and the wait after each ship stop reason under those",
|
|
428
|
-
category: "AI tools",
|
|
429
|
-
reference: ["--period <days> [--limit <lane-limit>]", "maintainer", ["none"]],
|
|
430
|
-
default_script_arguments: ["stats"]
|
|
301
|
+
function wait_reason(key, reservations, budget) {
|
|
302
|
+
const cores_only = { cores: budget.cores, memory_mb: Infinity };
|
|
303
|
+
if (!core_admission.is_admitted(key, reservations, cores_only)) return "cores";
|
|
304
|
+
return budget.memory_pressured === true ? "pressure" : "memory";
|
|
305
|
+
}
|
|
306
|
+
function wait_line(command, reason) {
|
|
307
|
+
return `\u23F3 ${command ?? "a step"} waits for the core budget \u2014 ${REASON_TEXT[reason]}`;
|
|
308
|
+
}
|
|
309
|
+
function lanes_root_of(checkout) {
|
|
310
|
+
if (lane_paths.lane_issue_of(checkout) === void 0) return lane_paths.lane_root(checkout);
|
|
311
|
+
return path2.dirname(path2.resolve(checkout));
|
|
312
|
+
}
|
|
313
|
+
function where_of(checkout) {
|
|
314
|
+
return { checkout, lane: lane_paths.lane_issue_of(checkout), lanes_root: lanes_root_of(checkout) };
|
|
315
|
+
}
|
|
316
|
+
function place_of(marker, home_root) {
|
|
317
|
+
const { checkout, lane, lanes_root } = marker;
|
|
318
|
+
if (checkout === void 0 || lanes_root === void 0 || lanes_root === home_root) {
|
|
319
|
+
return { lane, elsewhere: void 0 };
|
|
431
320
|
}
|
|
321
|
+
const name = lane === void 0 ? path2.basename(checkout) : `${path2.basename(lanes_root)}/${lane}`;
|
|
322
|
+
return { lane: void 0, elsewhere: name };
|
|
323
|
+
}
|
|
324
|
+
function wait_of(marker, now, home_root) {
|
|
325
|
+
const { command, wait_reason: reason } = marker;
|
|
326
|
+
if (reason === void 0 || marker.admitted_at !== void 0) return [];
|
|
327
|
+
return [{ command, ...place_of(marker, home_root), waited_ms: now - marker.claimed_at, reason }];
|
|
328
|
+
}
|
|
329
|
+
function waits_of(entries, now, home) {
|
|
330
|
+
const home_root = lanes_root_of(home);
|
|
331
|
+
return entries.flatMap((entry) => wait_of(entry.reservation, now, home_root)).toSorted((left, right) => right.waited_ms - left.waited_ms);
|
|
332
|
+
}
|
|
333
|
+
var core_wait = {
|
|
334
|
+
REASON_TEXT,
|
|
335
|
+
WAIT_REASONS,
|
|
336
|
+
wait_line,
|
|
337
|
+
wait_reason,
|
|
338
|
+
waits_of,
|
|
339
|
+
where_of
|
|
432
340
|
};
|
|
433
341
|
|
|
434
|
-
// scripts/
|
|
435
|
-
var
|
|
436
|
-
var
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
"
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
"
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
}
|
|
456
|
-
|
|
457
|
-
script: "scripts/run/carry/run-carry-cli.ts",
|
|
458
|
-
// `--stopped` sends the stop confirmation, so the Telegram credentials come from `.env` as for
|
|
459
|
-
// `run:wake` below.
|
|
460
|
-
tsx_arguments: OPTIONAL_ENV_FILE_FLAGS,
|
|
461
|
-
description: "Carry one invocation\u2019s budget across its own session cuts",
|
|
462
|
-
category: "AI tools",
|
|
463
|
-
reference: ["<operation> [arguments...]", "automation", ["files"]]
|
|
464
|
-
},
|
|
465
|
-
"run:add": {
|
|
466
|
-
script: "scripts/run/add/run-add-cli.ts",
|
|
467
|
-
description: "Add issues to a live backlogrun, ahead of the queue unless --no-priority",
|
|
468
|
-
category: "AI tools",
|
|
469
|
-
reference: ["<issue...> [--no-priority]", "automation", ["network", "files"]]
|
|
470
|
-
},
|
|
471
|
-
"run:wake": {
|
|
472
|
-
script: "scripts/run/wake/run-wake-cli.ts",
|
|
473
|
-
// `.env` rather than the ambient environment:
|
|
474
|
-
// the failure warning needs the Telegram credentials, the same
|
|
475
|
-
// reasons `notify` and `followup` carry these flags.
|
|
476
|
-
tsx_arguments: OPTIONAL_ENV_FILE_FLAGS,
|
|
477
|
-
description: "Wake the next session of a cut backlogrun from outside the conversation",
|
|
478
|
-
category: "AI tools",
|
|
479
|
-
reference: ["[options]", "automation", ["processes", "notifications"]]
|
|
480
|
-
},
|
|
481
|
-
"run:cut": {
|
|
482
|
-
script: "scripts/run/cut/run-cut-cli.ts",
|
|
483
|
-
description: "Cut a lane child before the gate and resume a fresh process",
|
|
484
|
-
category: "AI tools",
|
|
485
|
-
reference: ["[--resume] <issue> [--impl] [--handoff <path>]", "automation", ["files"]]
|
|
486
|
-
},
|
|
487
|
-
"run:liveness": {
|
|
488
|
-
script: "scripts/run/run-liveness-cli.ts",
|
|
489
|
-
description: "Say whether a delegated unit is still working, or stopped without reporting",
|
|
490
|
-
category: "AI tools",
|
|
491
|
-
reference: ["<issue> --output <path> [options]", "automation", ["processes"]]
|
|
492
|
-
},
|
|
493
|
-
"run:ending": {
|
|
494
|
-
script: "scripts/run/run-ending-cli.ts",
|
|
495
|
-
description: "Classify how a dispatched lane child ended (merged, cut, abandoned, unreadable)",
|
|
496
|
-
category: "AI tools",
|
|
497
|
-
reference: ["<issue> --output <path> [--repo <owner/repo>]", "automation", ["network"]]
|
|
498
|
-
},
|
|
499
|
-
"run:progress": {
|
|
500
|
-
script: "scripts/run/progress/run-progress-cli.ts",
|
|
501
|
-
description: "Report an unattended run\u2019s progress once it has gone quiet for an interval",
|
|
502
|
-
category: "AI tools",
|
|
503
|
-
reference: [
|
|
504
|
-
"[--once | --wait] [--interval <minutes>] [--hours <hours>] [--repo <owner/repo>] [--output <path>] | --mark | --path",
|
|
505
|
-
"automation",
|
|
506
|
-
["files"]
|
|
507
|
-
]
|
|
508
|
-
},
|
|
509
|
-
"run:board": {
|
|
510
|
-
script: "scripts/run/board/run-board-cli.ts",
|
|
511
|
-
// `--every` pushes its frame as a Telegram, so the credentials come from `.env` as for `run:carry`.
|
|
512
|
-
tsx_arguments: OPTIONAL_ENV_FILE_FLAGS,
|
|
513
|
-
description: "Draw a live board of the running backlogrun, redrawn every second",
|
|
514
|
-
category: "AI tools",
|
|
515
|
-
reference: ["[--once | --chat | --every <minutes>]", "automation", ["files", "network"]]
|
|
516
|
-
},
|
|
517
|
-
"run:prep": {
|
|
518
|
-
script: "scripts/run/run-prep-cli.ts",
|
|
519
|
-
description: "Bundle a run\u2019s pre-edit reads: body, comments, state, dependency scope",
|
|
520
|
-
category: "AI tools",
|
|
521
|
-
reference: ["<issue>", "automation", ["network"]]
|
|
522
|
-
},
|
|
523
|
-
// The entry sequence a lane opened on, folded into one call: claim the tree,
|
|
524
|
-
// read the budget, gather the issue reads and decide the pre-implementation step. `run:hold`,
|
|
525
|
-
// `cost --cut`, `run:prep` and `run:step` were four round trips re-billing a lane's full context each.
|
|
526
|
-
"run:entry": {
|
|
527
|
-
script: "scripts/run/entry/run-entry-cli.ts",
|
|
528
|
-
// A `busy` hold or an `over` budget sends the stop confirmation.
|
|
529
|
-
tsx_arguments: OPTIONAL_ENV_FILE_FLAGS,
|
|
530
|
-
description: "Open a run in one call: claim the tree, read the budget, bundle the reads, decide the pre-implementation step",
|
|
531
|
-
category: "AI tools",
|
|
532
|
-
reference: ["<issue> [--to <command>]", "automation", ["git", "network", "files"]]
|
|
533
|
-
},
|
|
534
|
-
"run:status": {
|
|
535
|
-
script: "scripts/run/run-status-cli.ts",
|
|
536
|
-
description: "Bundle a run\u2019s read-only status: issue state, cost verdict, carry counters",
|
|
537
|
-
category: "AI tools",
|
|
538
|
-
reference: ["<issue> [--repo <owner/repo>]", "automation", ["network"]]
|
|
539
|
-
},
|
|
540
|
-
"run:next": {
|
|
541
|
-
script: "scripts/run/run-next-cli.ts",
|
|
542
|
-
description: "Print the next step a fullrun takes, computed from the run\u2019s state",
|
|
543
|
-
category: "AI tools",
|
|
544
|
-
reference: ["<issue>", "automation", ["network"]]
|
|
545
|
-
},
|
|
546
|
-
"run:step": {
|
|
547
|
-
script: "scripts/run/run-step-cli.ts",
|
|
548
|
-
description: "Print the run\u2019s next single action, computed from the event stream, carry record and issue state",
|
|
549
|
-
category: "AI tools",
|
|
550
|
-
reference: ["[<issue>]", "automation", ["network", "files"]]
|
|
342
|
+
// scripts/gate/held-mark.ts
|
|
343
|
+
var HELD_KEY = "JOSH_CORE_RESERVED";
|
|
344
|
+
var HELD_VALUE = "1";
|
|
345
|
+
var HELD_RESERVATION_KEY = "JOSH_CORE_RESERVATION";
|
|
346
|
+
function is_held() {
|
|
347
|
+
return process.env[HELD_KEY] === HELD_VALUE;
|
|
348
|
+
}
|
|
349
|
+
function held_reservation() {
|
|
350
|
+
const key = process.env[HELD_RESERVATION_KEY] ?? "";
|
|
351
|
+
return key === "" ? void 0 : key;
|
|
352
|
+
}
|
|
353
|
+
function set_held_mark(value, reservation) {
|
|
354
|
+
process.env[HELD_KEY] = value;
|
|
355
|
+
process.env[HELD_RESERVATION_KEY] = reservation;
|
|
356
|
+
}
|
|
357
|
+
async function with_held_mark(run, reservation = "") {
|
|
358
|
+
const previous = process.env[HELD_KEY] ?? "";
|
|
359
|
+
const previous_reservation = process.env[HELD_RESERVATION_KEY] ?? "";
|
|
360
|
+
set_held_mark(HELD_VALUE, reservation);
|
|
361
|
+
try {
|
|
362
|
+
return await run();
|
|
363
|
+
} finally {
|
|
364
|
+
set_held_mark(previous, previous_reservation);
|
|
551
365
|
}
|
|
366
|
+
}
|
|
367
|
+
var held_mark = {
|
|
368
|
+
HELD_KEY,
|
|
369
|
+
HELD_RESERVATION_KEY,
|
|
370
|
+
HELD_VALUE,
|
|
371
|
+
held_reservation,
|
|
372
|
+
is_held,
|
|
373
|
+
with_held_mark
|
|
552
374
|
};
|
|
553
375
|
|
|
554
|
-
// scripts/
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
376
|
+
// scripts/gate/machine-capacity.ts
|
|
377
|
+
import { readFile } from "node:fs/promises";
|
|
378
|
+
import { availableParallelism as availableParallelism2, cpus, totalmem } from "node:os";
|
|
379
|
+
import { execa } from "execa";
|
|
380
|
+
var SAMPLE_WINDOW_MS = 250;
|
|
381
|
+
var BASELINE_CORES = 2;
|
|
382
|
+
var BYTES_PER_MB = 1048576;
|
|
383
|
+
var KB_PER_MB = 1024;
|
|
384
|
+
var PERCENT = 100;
|
|
385
|
+
var MEMINFO_PATH = "/proc/meminfo";
|
|
386
|
+
var VMSTAT_PATH = "/proc/vmstat";
|
|
387
|
+
var LINUX_AVAILABLE = /^MemAvailable:\s+(?<kb>\d+) kB/mu;
|
|
388
|
+
var LINUX_SWAP_IN = /^pswpin (?<pages>\d+)$/mu;
|
|
389
|
+
var LINUX_SWAP_OUT = /^pswpout (?<pages>\d+)$/mu;
|
|
390
|
+
var PAGE_SIZE_ARGUMENTS = ["PAGESIZE"];
|
|
391
|
+
var LEVEL_NAME = "kern.memorystatus_level";
|
|
392
|
+
var SWAP_IN_NAME = "vm.compressor.swapper.swapins_total";
|
|
393
|
+
var SWAP_OUT_NAME = "vm.compressor.swapper.swapouts_total";
|
|
394
|
+
var COMPRESSED_NAME = "vm.compressor_input_bytes";
|
|
395
|
+
var PAGE_SIZE_NAME = "hw.pagesize";
|
|
396
|
+
var PRESSURE_NAME = "kern.memorystatus_vm_pressure_level";
|
|
397
|
+
var PRESSURE_WARNING = 2;
|
|
398
|
+
var SWAP_PRESSURE_MB = 16;
|
|
399
|
+
var COMPRESSION_PRESSURE_MB = 64;
|
|
400
|
+
var SYSCTL_ARGUMENTS = [
|
|
401
|
+
LEVEL_NAME,
|
|
402
|
+
SWAP_IN_NAME,
|
|
403
|
+
SWAP_OUT_NAME,
|
|
404
|
+
COMPRESSED_NAME,
|
|
405
|
+
PAGE_SIZE_NAME,
|
|
406
|
+
PRESSURE_NAME
|
|
407
|
+
];
|
|
408
|
+
var SYSCTL_LINE = /^(?<name>[\w.]+): (?<value>\d+)$/gmu;
|
|
409
|
+
function cpu_times() {
|
|
410
|
+
let busy = 0;
|
|
411
|
+
let idle = 0;
|
|
412
|
+
for (const { times } of cpus()) {
|
|
413
|
+
busy += times.user + times.nice + times.sys + times.irq;
|
|
414
|
+
idle += times.idle;
|
|
415
|
+
}
|
|
416
|
+
return { busy, total: busy + idle };
|
|
417
|
+
}
|
|
418
|
+
function busy_share_between(before, after) {
|
|
419
|
+
const total = after.total - before.total;
|
|
420
|
+
if (total <= 0) return void 0;
|
|
421
|
+
return (after.busy - before.busy) / total;
|
|
422
|
+
}
|
|
423
|
+
function busy_cores_between(before, after, cores) {
|
|
424
|
+
const share = busy_share_between(before, after);
|
|
425
|
+
return share === void 0 ? void 0 : share * cores;
|
|
426
|
+
}
|
|
427
|
+
function sysctl_values(output) {
|
|
428
|
+
const values = [...output.matchAll(SYSCTL_LINE)].map(
|
|
429
|
+
(match) => [match.groups?.["name"] ?? "", Number(match.groups?.["value"])]
|
|
430
|
+
);
|
|
431
|
+
return new Map(values);
|
|
432
|
+
}
|
|
433
|
+
function swapped_pages(pages_in, pages_out) {
|
|
434
|
+
return pages_in === void 0 || pages_out === void 0 ? void 0 : pages_in + pages_out;
|
|
435
|
+
}
|
|
436
|
+
function mb_of(units, unit_bytes) {
|
|
437
|
+
if (units === void 0 || unit_bytes === void 0) return void 0;
|
|
438
|
+
return units * unit_bytes / BYTES_PER_MB;
|
|
439
|
+
}
|
|
440
|
+
function swapped_between(from, to) {
|
|
441
|
+
if (from === void 0 || to === void 0) return void 0;
|
|
442
|
+
return Math.max(0, to - from);
|
|
443
|
+
}
|
|
444
|
+
function parse_darwin_memory(output, total_bytes) {
|
|
445
|
+
const values = sysctl_values(output);
|
|
446
|
+
const level = values.get(LEVEL_NAME);
|
|
447
|
+
return {
|
|
448
|
+
available_mb: level === void 0 ? void 0 : level / PERCENT * (total_bytes / BYTES_PER_MB),
|
|
449
|
+
swapped_mb: mb_of(
|
|
450
|
+
swapped_pages(values.get(SWAP_IN_NAME), values.get(SWAP_OUT_NAME)),
|
|
451
|
+
values.get(PAGE_SIZE_NAME)
|
|
452
|
+
),
|
|
453
|
+
pressure_level: values.get(PRESSURE_NAME),
|
|
454
|
+
compressed_mb: mb_of(values.get(COMPRESSED_NAME), 1)
|
|
455
|
+
};
|
|
456
|
+
}
|
|
457
|
+
function parse_linux_available(meminfo) {
|
|
458
|
+
const kb = LINUX_AVAILABLE.exec(meminfo)?.groups?.["kb"];
|
|
459
|
+
return kb === void 0 ? void 0 : Number(kb) / KB_PER_MB;
|
|
460
|
+
}
|
|
461
|
+
function pages_of(pattern, vmstat) {
|
|
462
|
+
const pages = pattern.exec(vmstat)?.groups?.["pages"];
|
|
463
|
+
return pages === void 0 ? void 0 : Number(pages);
|
|
464
|
+
}
|
|
465
|
+
function parse_linux_swapped(vmstat, page_bytes) {
|
|
466
|
+
const pages = swapped_pages(pages_of(LINUX_SWAP_IN, vmstat), pages_of(LINUX_SWAP_OUT, vmstat));
|
|
467
|
+
return mb_of(pages, page_bytes);
|
|
468
|
+
}
|
|
469
|
+
async function settled(read) {
|
|
470
|
+
try {
|
|
471
|
+
return await read();
|
|
472
|
+
} catch {
|
|
473
|
+
return void 0;
|
|
561
474
|
}
|
|
475
|
+
}
|
|
476
|
+
async function read_darwin_memory() {
|
|
477
|
+
const { stdout } = await execa("sysctl", SYSCTL_ARGUMENTS, {
|
|
478
|
+
reject: false,
|
|
479
|
+
timeout: PROBE_TIMEOUT_MS
|
|
480
|
+
});
|
|
481
|
+
return parse_darwin_memory(stdout, totalmem());
|
|
482
|
+
}
|
|
483
|
+
async function read_page_size() {
|
|
484
|
+
const { stdout } = await execa("getconf", PAGE_SIZE_ARGUMENTS, { timeout: PROBE_TIMEOUT_MS });
|
|
485
|
+
return stdout;
|
|
486
|
+
}
|
|
487
|
+
async function read_linux_memory() {
|
|
488
|
+
const [meminfo, vmstat, page_size] = await Promise.all([
|
|
489
|
+
settled(async () => await readFile(MEMINFO_PATH, "utf8")),
|
|
490
|
+
settled(async () => await readFile(VMSTAT_PATH, "utf8")),
|
|
491
|
+
settled(read_page_size)
|
|
492
|
+
]);
|
|
493
|
+
const page_bytes = page_size === void 0 ? void 0 : Number(page_size);
|
|
494
|
+
return {
|
|
495
|
+
available_mb: meminfo === void 0 ? void 0 : parse_linux_available(meminfo),
|
|
496
|
+
swapped_mb: vmstat === void 0 || page_bytes === void 0 ? void 0 : parse_linux_swapped(vmstat, page_bytes),
|
|
497
|
+
pressure_level: void 0
|
|
498
|
+
};
|
|
499
|
+
}
|
|
500
|
+
var UNREAD_MEMORY = {
|
|
501
|
+
available_mb: void 0,
|
|
502
|
+
swapped_mb: void 0,
|
|
503
|
+
pressure_level: void 0
|
|
562
504
|
};
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
var AI_COMMANDS = {
|
|
567
|
-
...ISSUE_COMMANDS,
|
|
568
|
-
"pkg:scout": {
|
|
569
|
-
script: "scripts/package/package-scout-cli.ts",
|
|
570
|
-
description: "Rank package candidates by measured metrics so the Package-First tier decision is read, not judged",
|
|
571
|
-
category: "AI tools",
|
|
572
|
-
reference: ["<keywords> [--size <n>]", "automation", ["network"]]
|
|
573
|
-
},
|
|
574
|
-
"stash:pop": {
|
|
575
|
-
script: "scripts/git/stash/stash-pop-cli.ts",
|
|
576
|
-
description: "Pop the stash matching this message, not whichever a shared stack has on top",
|
|
577
|
-
category: "AI tools",
|
|
578
|
-
reference: ["<message>", "automation", ["git"]]
|
|
579
|
-
},
|
|
580
|
-
epic: {
|
|
581
|
-
script: "scripts/epic/epic.ts",
|
|
582
|
-
description: "Create an epic issue from its child issue numbers",
|
|
583
|
-
category: "AI tools",
|
|
584
|
-
reference: ["<title> <issue...> [--ordered]", "automation", ["network"]]
|
|
585
|
-
},
|
|
586
|
-
"epic:next": {
|
|
587
|
-
script: "scripts/epic/epic-next.ts",
|
|
588
|
-
description: "List an epic's runnable children, bundled per repository",
|
|
589
|
-
category: "AI tools",
|
|
590
|
-
reference: ["<epic>... [--repo <owner/repo>] [--lanes]", "automation", ["network"]]
|
|
591
|
-
},
|
|
592
|
-
"epic:bundle": {
|
|
593
|
-
script: "scripts/epic/epic-bundle-cli.ts",
|
|
594
|
-
description: "Say whether a newly filed issue belongs with ones already in the backlog",
|
|
595
|
-
category: "AI tools",
|
|
596
|
-
reference: ["<issue>", "automation", ["network"]]
|
|
597
|
-
},
|
|
598
|
-
"epic:audit": {
|
|
599
|
-
script: "scripts/epic/epic-audit-cli.ts",
|
|
600
|
-
description: "Audit an epic's children against each other for contradictions",
|
|
601
|
-
category: "AI tools",
|
|
602
|
-
reference: ["<epic>", "automation", ["network"]]
|
|
603
|
-
},
|
|
604
|
-
"epic:check": {
|
|
605
|
-
script: "scripts/epic/epic-check.ts",
|
|
606
|
-
description: "Check an epic issue against the tracking requirements",
|
|
607
|
-
category: "AI tools",
|
|
608
|
-
reference: ["<epic>", "automation", ["network"]]
|
|
609
|
-
},
|
|
610
|
-
"auto-ok:next": {
|
|
611
|
-
script: "scripts/auto-ok/auto-ok-cli.ts",
|
|
612
|
-
description: "Print the next opted-in issue an unattended run may pick up outside an epic",
|
|
613
|
-
category: "AI tools",
|
|
614
|
-
reference: ["[--exclude <n>[,<n>...]] [--label <name>]", "automation", ["network"]]
|
|
615
|
-
},
|
|
616
|
-
"auto-ok:judge": {
|
|
617
|
-
script: "scripts/auto-ok/auto-ok-judge-cli.ts",
|
|
618
|
-
description: "List the open issues backlogrun owes an auto-ok judgement, or record one (--apply / --decline)",
|
|
619
|
-
category: "AI tools",
|
|
620
|
-
reference: [
|
|
621
|
-
"[--apply <n> (--lane | --solo) | --decline <n> --reason <text>]",
|
|
622
|
-
"automation",
|
|
623
|
-
["network"]
|
|
624
|
-
]
|
|
625
|
-
},
|
|
626
|
-
...BACKLOG_COMMANDS,
|
|
627
|
-
cost: {
|
|
628
|
-
script: "scripts/cost-runtime/cost-cli.ts",
|
|
629
|
-
description: "Say whether the next turn of a run crosses the context-cut threshold (--cut) or a given tokens-per-request figure (--over)",
|
|
630
|
-
category: "AI tools",
|
|
631
|
-
reference: ["(--cut | --over <tokens-per-request>) [--path <dir>]", "automation", ["none"]]
|
|
632
|
-
},
|
|
633
|
-
...DOCUMENT_COMMANDS,
|
|
634
|
-
time: {
|
|
635
|
-
script: "scripts/time/time-cli.ts",
|
|
636
|
-
description: "Report where a run's wall clock went: model wait, tool execution, human wait",
|
|
637
|
-
category: "AI tools",
|
|
638
|
-
reference: ["[options]", "maintainer", ["none"]],
|
|
639
|
-
// Kit-only: it measures kit's own development runs and its report modules live under the
|
|
640
|
-
// undistributed `scripts/time/`, so it means nothing in a consumer project and is dropped from a
|
|
641
|
-
// consumer's help. The runtime analysis hooks and guards rely on stays
|
|
642
|
-
// distributed under `scripts/time-runtime/`.
|
|
643
|
-
is_kit_only: true
|
|
644
|
-
},
|
|
645
|
-
// The tool-calls-per-round-trip density the batching guard is measured on, aggregated across the
|
|
646
|
-
// recent lane sessions. Kit-only for the same reason `time` is: it reads
|
|
647
|
-
// kit's own lane transcripts and means nothing in a consumer project.
|
|
648
|
-
"time:density": {
|
|
649
|
-
script: "scripts/time/time-density-cli.ts",
|
|
650
|
-
description: "Report tool calls per round trip across the recent lane sessions",
|
|
651
|
-
category: "AI tools",
|
|
652
|
-
reference: ["[--lanes <n>] [--path <dir>]", "maintainer", ["none"]],
|
|
653
|
-
is_kit_only: true
|
|
654
|
-
},
|
|
655
|
-
retrospective: {
|
|
656
|
-
script: "scripts/retrospective/retrospective-cli.ts",
|
|
657
|
-
description: "Aggregate a finished run's cost, review findings, observation ledger and events into one digest",
|
|
658
|
-
category: "AI tools",
|
|
659
|
-
reference: ["", "maintainer", ["none"]],
|
|
660
|
-
// Kit-only: it reads kit's own development run — the transcript store, the review ledger and the
|
|
661
|
-
// run event stream — so it means nothing in a consumer project and is dropped from a consumer's
|
|
662
|
-
// help, through the same declaration `josh time` uses for the same reason.
|
|
663
|
-
is_kit_only: true
|
|
664
|
-
},
|
|
665
|
-
"review:brief": {
|
|
666
|
-
script: "scripts/review/review-brief-cli.ts",
|
|
667
|
-
description: "Print the whole /code-review invocation: level, what the gate already proved, target (--level-only for the level alone)",
|
|
668
|
-
category: "AI tools",
|
|
669
|
-
reference: ["[--round <1|2>] | --level-only [--staged] [--json]", "automation", ["files"]]
|
|
670
|
-
},
|
|
671
|
-
"review:attest": {
|
|
672
|
-
script: "scripts/review/review-attest-cli.ts",
|
|
673
|
-
description: "Record, or verify, which checkout a /code-review actually read",
|
|
674
|
-
category: "AI tools",
|
|
675
|
-
reference: ["<nonce> | --check", "automation", ["files"]]
|
|
676
|
-
},
|
|
677
|
-
"review:round2": {
|
|
678
|
-
script: "scripts/review/review-round2-cli.ts",
|
|
679
|
-
description: "Say whether the second /code-review round is due, or may be skipped entirely",
|
|
680
|
-
category: "AI tools",
|
|
681
|
-
reference: ["[--round-1-closed] [--json]", "automation", ["none"]]
|
|
682
|
-
},
|
|
683
|
-
delegate: {
|
|
684
|
-
script: "scripts/delegation/delegation-cli.ts",
|
|
685
|
-
description: "Say whether a run step may go to a cheaper execution tier",
|
|
686
|
-
category: "AI tools",
|
|
687
|
-
reference: ["<step> | --list", "automation", ["none"]]
|
|
688
|
-
},
|
|
689
|
-
fanout: {
|
|
690
|
-
script: "scripts/delegation/fanout-cli.ts",
|
|
691
|
-
description: "Say whether proposed implementation units are file-disjoint, so they may run in parallel",
|
|
692
|
-
category: "AI tools",
|
|
693
|
-
reference: ["<unit-files> <unit-files> [<unit-files> \u2026]", "automation", ["none"]]
|
|
694
|
-
},
|
|
695
|
-
...RUN_COMMANDS,
|
|
696
|
-
"repo:party": {
|
|
697
|
-
script: "scripts/discovery/repo-party-cli.ts",
|
|
698
|
-
description: "Say whether a repository is first-party or third-party by owner equality (computed, not judged)",
|
|
699
|
-
category: "AI tools",
|
|
700
|
-
reference: ["[<owner/repo>]", "automation", ["none"]]
|
|
701
|
-
},
|
|
702
|
-
"run:merge": {
|
|
703
|
-
script: "scripts/run/merge/run-merge-cli.ts",
|
|
704
|
-
description: "Collapse a backlogrun merge event into one call: confirm the child, do the post-merge steps, offer the next child",
|
|
705
|
-
category: "AI tools",
|
|
706
|
-
reference: [ISSUE_WITH_OPTIONS2, "automation", ["git", "network"]]
|
|
707
|
-
},
|
|
708
|
-
"run:review": {
|
|
709
|
-
script: "scripts/run/review/run-review-cli.ts",
|
|
710
|
-
description: "Start the gate in the background and print the /code-review brief in one call so the two overlap (--join to join the gate and check its verdict)",
|
|
711
|
-
category: "AI tools",
|
|
712
|
-
reference: ["[--join]", "automation", ["processes", "files"]]
|
|
713
|
-
},
|
|
714
|
-
// The post-merge sequence a run closed on, folded into one call: commit the
|
|
715
|
-
// observation ledger, read the completion citations and decide the release scope. `observations:flush`,
|
|
716
|
-
// `issue:cite` and `release:scope` were three round trips re-billing the run's full context each.
|
|
717
|
-
"run:tail": {
|
|
718
|
-
script: "scripts/run/run-tail-cli.ts",
|
|
719
|
-
description: "Close a run in one call: return to the default branch, commit the observation ledger, read the citations, decide the release scope",
|
|
720
|
-
category: "AI tools",
|
|
721
|
-
reference: ["[<issue> ...]", "automation", ["git", "network"]]
|
|
722
|
-
},
|
|
723
|
-
// The commit-to-report region a run ships a change on, folded into one call:
|
|
724
|
-
// the gate, the commit/push/PR (`git -y`), the CI-wait merge (`followup`) and the report bookkeeping
|
|
725
|
-
// (`run:tail`) were four round trips re-billing the run's full context each. It stops at the first
|
|
726
|
-
// failed step and names it, so the run reads only the step to fix.
|
|
727
|
-
ship: {
|
|
728
|
-
script: "scripts/run/ship/run-ship-cli.ts",
|
|
729
|
-
description: "Ship a change in one call: gate, commit/push/PR, the CI-wait merge and the report bookkeeping, stopping at the first failed step",
|
|
730
|
-
category: "AI tools",
|
|
731
|
-
reference: [
|
|
732
|
-
'"<title> #<N>" [<follow-up-N> ...] [--cite <N>] [--review] [--detach] [--body-file <path>] [--notify-message <text> | --notify-message-file <path>] | --log <N>',
|
|
733
|
-
"automation",
|
|
734
|
-
["git", "network"]
|
|
735
|
-
]
|
|
736
|
-
},
|
|
737
|
-
"run:event": {
|
|
738
|
-
script: "scripts/run/event/run-event-cli.ts",
|
|
739
|
-
description: "Append to, read or watch the run\u2019s append-only event stream (--append <kind> <text> | --from|--follow <n> | --watch [<n>] | --last)",
|
|
740
|
-
category: "AI tools",
|
|
741
|
-
reference: [
|
|
742
|
-
"--append <kind> <text> | --from|--follow <n> | --watch [<n>] | --last",
|
|
743
|
-
"automation",
|
|
744
|
-
["files"]
|
|
745
|
-
]
|
|
746
|
-
},
|
|
747
|
-
"run:report": {
|
|
748
|
-
script: "scripts/run/run-report-cli.ts",
|
|
749
|
-
description: "Generate the session-facing report for this invocation from the run\u2019s event stream (merges, parks, cuts) with the release tail \u2014 the same text josh notify sends",
|
|
750
|
-
category: "AI tools",
|
|
751
|
-
reference: ["", "automation", ["files", "network"]]
|
|
752
|
-
},
|
|
753
|
-
...LANE_COMMANDS,
|
|
754
|
-
...GUARD_COMMANDS,
|
|
755
|
-
"run:watcher:guard": {
|
|
756
|
-
script: "scripts/run/run-watcher-guard-cli.ts",
|
|
757
|
-
description: "Guard: exits non-zero when lane children are in-flight but the watcher has not pinged recently",
|
|
758
|
-
category: "AI tools",
|
|
759
|
-
reference: ["", "automation", ["none"]]
|
|
760
|
-
},
|
|
761
|
-
"run:stranded": {
|
|
762
|
-
script: "scripts/run/run-stranded-cli.ts",
|
|
763
|
-
// `.env` rather than the ambient environment: the strand notification needs the Telegram
|
|
764
|
-
// credentials, the same reason `run:wake` carries these flags.
|
|
765
|
-
tsx_arguments: OPTIONAL_ENV_FILE_FLAGS,
|
|
766
|
-
description: "Report whether the run is stranded \u2014 owner gone, no successor holding the budget, and no supervisor watching",
|
|
767
|
-
category: "AI tools",
|
|
768
|
-
reference: ["", "automation", ["processes", "notifications"]]
|
|
769
|
-
},
|
|
770
|
-
...SPLIT_COMMANDS,
|
|
771
|
-
"oracle:list": {
|
|
772
|
-
script: "scripts/rules/oracle-list-cli.ts",
|
|
773
|
-
description: "Print the decision oracles \u2014 commands that answer a rule question mechanically",
|
|
774
|
-
category: "AI tools",
|
|
775
|
-
reference: ["", "automation", ["none"]]
|
|
776
|
-
},
|
|
777
|
-
eval: {
|
|
778
|
-
script: "scripts/eval/eval-run.ts",
|
|
779
|
-
description: "Run the agent rule-compliance scenarios (real Claude sessions)",
|
|
780
|
-
category: "AI tools",
|
|
781
|
-
reference: ["[scenario...]", "maintainer", ["processes", "network"]],
|
|
782
|
-
// Kit-only: it replays kit's own distributed rules against real Claude sessions, so it means
|
|
783
|
-
// nothing in a consumer project and is dropped from a consumer's help.
|
|
784
|
-
is_kit_only: true
|
|
785
|
-
}
|
|
786
|
-
};
|
|
787
|
-
|
|
788
|
-
// scripts/josh/josh-commands-clone.ts
|
|
789
|
-
var CLONE_COMMANDS = {
|
|
790
|
-
"clone:scan": {
|
|
791
|
-
script: "scripts/clone/clone-scan-cli.ts",
|
|
792
|
-
description: "Count code duplication across files and first-party repositories, printing each clone as file:line pairs",
|
|
793
|
-
category: "AI tools",
|
|
794
|
-
reference: ["", "automation", ["files"]]
|
|
795
|
-
}
|
|
796
|
-
};
|
|
797
|
-
|
|
798
|
-
// scripts/gate/core-budget.ts
|
|
799
|
-
import { randomUUID } from "node:crypto";
|
|
800
|
-
import { readdirSync as readdirSync2 } from "node:fs";
|
|
801
|
-
import { availableParallelism as availableParallelism3 } from "node:os";
|
|
802
|
-
import path2 from "node:path";
|
|
803
|
-
|
|
804
|
-
// scripts/test/unit-worker-share.ts
|
|
805
|
-
import { readdirSync } from "node:fs";
|
|
806
|
-
import { availableParallelism } from "node:os";
|
|
807
|
-
import path from "node:path";
|
|
808
|
-
var RUN_PREFIX = "josh-unit-running-";
|
|
809
|
-
var WORKER_FLAG = "--maxWorkers";
|
|
810
|
-
var WORKER_FLAGS = [WORKER_FLAG, "--max-workers"];
|
|
811
|
-
var MIN_WORKERS = 1;
|
|
812
|
-
var SOLO_RUNS = 1;
|
|
813
|
-
function marker_path(pid = process.pid) {
|
|
814
|
-
return stamp_file.stamp_path(RUN_PREFIX, String(pid));
|
|
505
|
+
async function read_memory() {
|
|
506
|
+
if (process.platform === "darwin") return await settled(read_darwin_memory) ?? UNREAD_MEMORY;
|
|
507
|
+
return process.platform === "linux" ? await read_linux_memory() : UNREAD_MEMORY;
|
|
815
508
|
}
|
|
816
|
-
function
|
|
817
|
-
return
|
|
509
|
+
function is_unquoted() {
|
|
510
|
+
return cpus().length > 0 && cpus().length === availableParallelism2();
|
|
818
511
|
}
|
|
819
|
-
function
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
|
|
823
|
-
return parsed.success ? parsed.data : void 0;
|
|
512
|
+
async function default_sleep(ms) {
|
|
513
|
+
await new Promise((resolve) => {
|
|
514
|
+
setTimeout(resolve, ms);
|
|
515
|
+
});
|
|
824
516
|
}
|
|
825
|
-
function
|
|
826
|
-
|
|
827
|
-
return readdirSync(directory).filter((name) => name.startsWith(RUN_PREFIX));
|
|
828
|
-
} catch {
|
|
829
|
-
return [];
|
|
830
|
-
}
|
|
517
|
+
function moved_over(from, to, limit) {
|
|
518
|
+
return (swapped_between(from, to) ?? 0) > limit;
|
|
831
519
|
}
|
|
832
|
-
function
|
|
833
|
-
|
|
834
|
-
stamp_file.remove_stamp(source);
|
|
835
|
-
} catch {
|
|
836
|
-
}
|
|
520
|
+
function is_pressured(before, after) {
|
|
521
|
+
return (after.pressure_level ?? 0) >= PRESSURE_WARNING || moved_over(before.swapped_mb, after.swapped_mb, SWAP_PRESSURE_MB) || moved_over(before.compressed_mb, after.compressed_mb, COMPRESSION_PRESSURE_MB);
|
|
837
522
|
}
|
|
838
|
-
function
|
|
839
|
-
|
|
840
|
-
|
|
841
|
-
if (is_running(marker)) return true;
|
|
842
|
-
sweep_marker(source);
|
|
843
|
-
return false;
|
|
844
|
-
}
|
|
845
|
-
function live_run_count(directory = PLATFORM_TEMP_ROOT) {
|
|
846
|
-
return marker_files(directory).filter((name) => is_marker_live(path.join(directory, name))).length;
|
|
847
|
-
}
|
|
848
|
-
var NESTED_KEY = "JOSH_UNIT_RUN_MARKED";
|
|
849
|
-
var NESTED_VALUE = "1";
|
|
850
|
-
function is_nested_run() {
|
|
851
|
-
return process.env[NESTED_KEY] === NESTED_VALUE;
|
|
852
|
-
}
|
|
853
|
-
function resolve_unit_workers(available_cores, runs2) {
|
|
854
|
-
if (runs2 <= SOLO_RUNS) return void 0;
|
|
855
|
-
return Math.max(MIN_WORKERS, Math.floor(available_cores / runs2));
|
|
856
|
-
}
|
|
857
|
-
function current_share(available_cores = availableParallelism(), live_runs = live_run_count()) {
|
|
858
|
-
const own_run2 = is_nested_run() ? 0 : SOLO_RUNS;
|
|
859
|
-
return resolve_unit_workers(available_cores, live_runs + own_run2);
|
|
860
|
-
}
|
|
861
|
-
function is_worker_flag(argument) {
|
|
862
|
-
return WORKER_FLAGS.some((flag) => argument === flag || argument.startsWith(`${flag}=`));
|
|
863
|
-
}
|
|
864
|
-
function worker_arguments(vitest_arguments, share) {
|
|
865
|
-
if (share === void 0 || vitest_arguments.some((argument) => is_worker_flag(argument))) {
|
|
866
|
-
return [];
|
|
867
|
-
}
|
|
868
|
-
return [`${WORKER_FLAG}=${String(share)}`];
|
|
869
|
-
}
|
|
870
|
-
function mark_run_started(target2) {
|
|
871
|
-
try {
|
|
872
|
-
stamp_file.write_stamp(target2, process_identity.own_fields());
|
|
873
|
-
} catch {
|
|
874
|
-
}
|
|
875
|
-
}
|
|
876
|
-
async function with_run_marker(run) {
|
|
877
|
-
if (is_nested_run()) return await run();
|
|
878
|
-
const target2 = marker_path();
|
|
879
|
-
mark_run_started(target2);
|
|
880
|
-
process.env[NESTED_KEY] = NESTED_VALUE;
|
|
881
|
-
try {
|
|
882
|
-
return await run();
|
|
883
|
-
} finally {
|
|
884
|
-
process.env[NESTED_KEY] = "";
|
|
885
|
-
stamp_file.remove_stamp(target2);
|
|
886
|
-
}
|
|
887
|
-
}
|
|
888
|
-
var unit_worker_share = {
|
|
889
|
-
MIN_WORKERS,
|
|
890
|
-
NESTED_KEY,
|
|
891
|
-
NESTED_VALUE,
|
|
892
|
-
RUN_PREFIX,
|
|
893
|
-
SOLO_RUNS,
|
|
894
|
-
current_share,
|
|
895
|
-
is_nested_run,
|
|
896
|
-
live_run_count,
|
|
897
|
-
marker_path,
|
|
898
|
-
read_marker,
|
|
899
|
-
resolve_unit_workers,
|
|
900
|
-
with_run_marker,
|
|
901
|
-
worker_arguments
|
|
902
|
-
};
|
|
903
|
-
|
|
904
|
-
// scripts/gate/core-budget.ts
|
|
905
|
-
import { z as z2 } from "zod";
|
|
906
|
-
|
|
907
|
-
// scripts/gate/core-admission.ts
|
|
908
|
-
var MS_PER_SECOND = 1e3;
|
|
909
|
-
var RAMP_UP_MS = 3e4;
|
|
910
|
-
function compare_claim(left, right) {
|
|
911
|
-
const by_time = left.reservation.claimed_at - right.reservation.claimed_at;
|
|
912
|
-
return by_time === 0 ? left.key.localeCompare(right.key) : by_time;
|
|
913
|
-
}
|
|
914
|
-
function add_load(totals, reservation) {
|
|
915
|
-
totals.cores += reservation.weight;
|
|
916
|
-
totals.memory_mb += reservation.memory_mb ?? 0;
|
|
917
|
-
}
|
|
918
|
-
function exceeds(totals, budget) {
|
|
919
|
-
return totals.cores > budget.cores || totals.memory_mb > budget.memory_mb;
|
|
920
|
-
}
|
|
921
|
-
function admitted_keys(reservations, budget, memory_budget = Infinity) {
|
|
922
|
-
const limit = { cores: budget, memory_mb: memory_budget };
|
|
923
|
-
const totals = { cores: 0, memory_mb: 0 };
|
|
924
|
-
const admitted = /* @__PURE__ */ new Set();
|
|
925
|
-
for (const [index, entry] of reservations.toSorted(compare_claim).entries()) {
|
|
926
|
-
add_load(totals, entry.reservation);
|
|
927
|
-
if (index !== 0 && exceeds(totals, limit)) break;
|
|
928
|
-
admitted.add(entry.key);
|
|
929
|
-
}
|
|
930
|
-
return admitted;
|
|
931
|
-
}
|
|
932
|
-
function is_admitted(key, reservations, budget) {
|
|
933
|
-
return admitted_keys(reservations, budget.cores, budget.memory_mb).has(key);
|
|
934
|
-
}
|
|
935
|
-
function sum_load(reservations) {
|
|
936
|
-
const load = { cores: 0, memory_mb: 0 };
|
|
937
|
-
for (const entry of reservations) add_load(load, entry.reservation);
|
|
938
|
-
return load;
|
|
939
|
-
}
|
|
940
|
-
function is_ramped(reservation, now) {
|
|
941
|
-
const { admitted_at } = reservation;
|
|
942
|
-
return admitted_at !== void 0 && now - admitted_at >= RAMP_UP_MS;
|
|
943
|
-
}
|
|
944
|
-
function ledger_load(reservations, now) {
|
|
945
|
-
const admitted = reservations.filter((entry) => entry.reservation.admitted_at !== void 0);
|
|
946
|
-
const ramped = admitted.filter((entry) => is_ramped(entry.reservation, now));
|
|
947
|
-
return { cores: sum_load(admitted).cores, memory_mb: sum_load(ramped).memory_mb };
|
|
948
|
-
}
|
|
949
|
-
function total_weight(reservations) {
|
|
950
|
-
return sum_load(reservations).cores;
|
|
951
|
-
}
|
|
952
|
-
function admitted_load(reservations, budget) {
|
|
953
|
-
const admitted = admitted_keys(reservations, budget);
|
|
954
|
-
return total_weight(reservations.filter((entry) => admitted.has(entry.key)));
|
|
955
|
-
}
|
|
956
|
-
function describe_memory(ledger, budget) {
|
|
957
|
-
if (!Number.isFinite(budget.memory_mb)) return "";
|
|
958
|
-
return ` and ${String(Math.round(ledger.memory_mb))} MB of memory against ${String(Math.round(budget.memory_mb))} MB`;
|
|
959
|
-
}
|
|
960
|
-
function describe_overflow(admission) {
|
|
961
|
-
const { overflow } = admission;
|
|
962
|
-
if (overflow === void 0) return void 0;
|
|
963
|
-
const { ledger, budget } = overflow;
|
|
964
|
-
const waited = String(Math.round(overflow.waited_ms / MS_PER_SECOND));
|
|
965
|
-
const cores = `the ledger held ${String(ledger.cores)} cores against a budget of ${String(budget.cores)}`;
|
|
966
|
-
return `started past the core budget after ${waited}s \u2014 ${cores}${describe_memory(ledger, budget)}`;
|
|
967
|
-
}
|
|
968
|
-
var core_admission = {
|
|
969
|
-
RAMP_UP_MS,
|
|
970
|
-
admitted_keys,
|
|
971
|
-
admitted_load,
|
|
972
|
-
describe_overflow,
|
|
973
|
-
is_admitted,
|
|
974
|
-
ledger_load,
|
|
975
|
-
sum_load
|
|
976
|
-
};
|
|
977
|
-
|
|
978
|
-
// scripts/gate/machine-capacity.ts
|
|
979
|
-
import { readFile } from "node:fs/promises";
|
|
980
|
-
import { availableParallelism as availableParallelism2, cpus, totalmem } from "node:os";
|
|
981
|
-
import { execa } from "execa";
|
|
982
|
-
var SAMPLE_WINDOW_MS = 250;
|
|
983
|
-
var BASELINE_CORES = 2;
|
|
984
|
-
var BYTES_PER_MB = 1048576;
|
|
985
|
-
var KB_PER_MB = 1024;
|
|
986
|
-
var PERCENT = 100;
|
|
987
|
-
var MEMINFO_PATH = "/proc/meminfo";
|
|
988
|
-
var VMSTAT_PATH = "/proc/vmstat";
|
|
989
|
-
var LINUX_AVAILABLE = /^MemAvailable:\s+(?<kb>\d+) kB/mu;
|
|
990
|
-
var LINUX_SWAP_IN = /^pswpin (?<pages>\d+)$/mu;
|
|
991
|
-
var LINUX_SWAP_OUT = /^pswpout (?<pages>\d+)$/mu;
|
|
992
|
-
var PAGE_SIZE_ARGUMENTS = ["PAGESIZE"];
|
|
993
|
-
var LEVEL_NAME = "kern.memorystatus_level";
|
|
994
|
-
var SWAP_IN_NAME = "vm.compressor.swapper.swapins_total";
|
|
995
|
-
var SWAP_OUT_NAME = "vm.compressor.swapper.swapouts_total";
|
|
996
|
-
var PAGE_SIZE_NAME = "hw.pagesize";
|
|
997
|
-
var PRESSURE_NAME = "kern.memorystatus_vm_pressure_level";
|
|
998
|
-
var SYSCTL_ARGUMENTS = [LEVEL_NAME, SWAP_IN_NAME, SWAP_OUT_NAME, PAGE_SIZE_NAME, PRESSURE_NAME];
|
|
999
|
-
var SYSCTL_LINE = /^(?<name>[\w.]+): (?<value>\d+)$/gmu;
|
|
1000
|
-
function cpu_times() {
|
|
1001
|
-
let busy = 0;
|
|
1002
|
-
let idle = 0;
|
|
1003
|
-
for (const { times } of cpus()) {
|
|
1004
|
-
busy += times.user + times.nice + times.sys + times.irq;
|
|
1005
|
-
idle += times.idle;
|
|
1006
|
-
}
|
|
1007
|
-
return { busy, total: busy + idle };
|
|
1008
|
-
}
|
|
1009
|
-
function busy_share_between(before, after) {
|
|
1010
|
-
const total = after.total - before.total;
|
|
1011
|
-
if (total <= 0) return void 0;
|
|
1012
|
-
return (after.busy - before.busy) / total;
|
|
1013
|
-
}
|
|
1014
|
-
function busy_cores_between(before, after, cores) {
|
|
1015
|
-
const share = busy_share_between(before, after);
|
|
1016
|
-
return share === void 0 ? void 0 : share * cores;
|
|
1017
|
-
}
|
|
1018
|
-
function sysctl_values(output) {
|
|
1019
|
-
const values = [...output.matchAll(SYSCTL_LINE)].map(
|
|
1020
|
-
(match) => [match.groups?.["name"] ?? "", Number(match.groups?.["value"])]
|
|
1021
|
-
);
|
|
1022
|
-
return new Map(values);
|
|
1023
|
-
}
|
|
1024
|
-
function swapped_pages(pages_in, pages_out) {
|
|
1025
|
-
return pages_in === void 0 || pages_out === void 0 ? void 0 : pages_in + pages_out;
|
|
1026
|
-
}
|
|
1027
|
-
function swapped_mb(pages, page_bytes) {
|
|
1028
|
-
if (pages === void 0 || page_bytes === void 0) return void 0;
|
|
1029
|
-
return pages * page_bytes / BYTES_PER_MB;
|
|
1030
|
-
}
|
|
1031
|
-
function swapped_between(from, to) {
|
|
1032
|
-
if (from === void 0 || to === void 0) return void 0;
|
|
1033
|
-
return Math.max(0, to - from);
|
|
1034
|
-
}
|
|
1035
|
-
function parse_darwin_memory(output, total_bytes) {
|
|
1036
|
-
const values = sysctl_values(output);
|
|
1037
|
-
const level = values.get(LEVEL_NAME);
|
|
1038
|
-
return {
|
|
1039
|
-
available_mb: level === void 0 ? void 0 : level / PERCENT * (total_bytes / BYTES_PER_MB),
|
|
1040
|
-
swapped_mb: swapped_mb(
|
|
1041
|
-
swapped_pages(values.get(SWAP_IN_NAME), values.get(SWAP_OUT_NAME)),
|
|
1042
|
-
values.get(PAGE_SIZE_NAME)
|
|
1043
|
-
),
|
|
1044
|
-
pressure_level: values.get(PRESSURE_NAME)
|
|
1045
|
-
};
|
|
1046
|
-
}
|
|
1047
|
-
function parse_linux_available(meminfo) {
|
|
1048
|
-
const kb = LINUX_AVAILABLE.exec(meminfo)?.groups?.["kb"];
|
|
1049
|
-
return kb === void 0 ? void 0 : Number(kb) / KB_PER_MB;
|
|
1050
|
-
}
|
|
1051
|
-
function pages_of(pattern, vmstat) {
|
|
1052
|
-
const pages = pattern.exec(vmstat)?.groups?.["pages"];
|
|
1053
|
-
return pages === void 0 ? void 0 : Number(pages);
|
|
1054
|
-
}
|
|
1055
|
-
function parse_linux_swapped(vmstat, page_bytes) {
|
|
1056
|
-
const pages = swapped_pages(pages_of(LINUX_SWAP_IN, vmstat), pages_of(LINUX_SWAP_OUT, vmstat));
|
|
1057
|
-
return swapped_mb(pages, page_bytes);
|
|
1058
|
-
}
|
|
1059
|
-
async function settled(read) {
|
|
1060
|
-
try {
|
|
1061
|
-
return await read();
|
|
1062
|
-
} catch {
|
|
1063
|
-
return void 0;
|
|
1064
|
-
}
|
|
1065
|
-
}
|
|
1066
|
-
async function read_darwin_memory() {
|
|
1067
|
-
const { stdout } = await execa("sysctl", SYSCTL_ARGUMENTS, {
|
|
1068
|
-
reject: false,
|
|
1069
|
-
timeout: PROBE_TIMEOUT_MS
|
|
1070
|
-
});
|
|
1071
|
-
return parse_darwin_memory(stdout, totalmem());
|
|
1072
|
-
}
|
|
1073
|
-
async function read_page_size() {
|
|
1074
|
-
const { stdout } = await execa("getconf", PAGE_SIZE_ARGUMENTS, { timeout: PROBE_TIMEOUT_MS });
|
|
1075
|
-
return stdout;
|
|
1076
|
-
}
|
|
1077
|
-
async function read_linux_memory() {
|
|
1078
|
-
const [meminfo, vmstat, page_size] = await Promise.all([
|
|
1079
|
-
settled(async () => await readFile(MEMINFO_PATH, "utf8")),
|
|
1080
|
-
settled(async () => await readFile(VMSTAT_PATH, "utf8")),
|
|
1081
|
-
settled(read_page_size)
|
|
1082
|
-
]);
|
|
1083
|
-
const page_bytes = page_size === void 0 ? void 0 : Number(page_size);
|
|
1084
|
-
return {
|
|
1085
|
-
available_mb: meminfo === void 0 ? void 0 : parse_linux_available(meminfo),
|
|
1086
|
-
swapped_mb: vmstat === void 0 || page_bytes === void 0 ? void 0 : parse_linux_swapped(vmstat, page_bytes),
|
|
1087
|
-
pressure_level: void 0
|
|
1088
|
-
};
|
|
1089
|
-
}
|
|
1090
|
-
var UNREAD_MEMORY = {
|
|
1091
|
-
available_mb: void 0,
|
|
1092
|
-
swapped_mb: void 0,
|
|
1093
|
-
pressure_level: void 0
|
|
1094
|
-
};
|
|
1095
|
-
async function read_memory() {
|
|
1096
|
-
if (process.platform === "darwin") return await settled(read_darwin_memory) ?? UNREAD_MEMORY;
|
|
1097
|
-
return process.platform === "linux" ? await read_linux_memory() : UNREAD_MEMORY;
|
|
1098
|
-
}
|
|
1099
|
-
function is_unquoted() {
|
|
1100
|
-
return cpus().length > 0 && cpus().length === availableParallelism2();
|
|
1101
|
-
}
|
|
1102
|
-
async function default_sleep(ms) {
|
|
1103
|
-
await new Promise((resolve) => {
|
|
1104
|
-
setTimeout(resolve, ms);
|
|
1105
|
-
});
|
|
523
|
+
function pressured_available(before, after) {
|
|
524
|
+
if (after.available_mb === void 0) return void 0;
|
|
525
|
+
return is_pressured(before, after) ? 0 : after.available_mb;
|
|
1106
526
|
}
|
|
1107
527
|
async function read_machine(sleep = default_sleep) {
|
|
1108
528
|
const before = cpu_times();
|
|
1109
|
-
const [
|
|
529
|
+
const [memory_before] = await Promise.all([read_memory(), sleep(SAMPLE_WINDOW_MS)]);
|
|
530
|
+
const memory_after = await read_memory();
|
|
1110
531
|
const busy_cores = is_unquoted() ? busy_cores_between(before, cpu_times(), cpus().length) : void 0;
|
|
1111
|
-
return {
|
|
532
|
+
return {
|
|
533
|
+
busy_cores,
|
|
534
|
+
available_mb: pressured_available(memory_before, memory_after),
|
|
535
|
+
memory_pressured: is_pressured(memory_before, memory_after)
|
|
536
|
+
};
|
|
1112
537
|
}
|
|
1113
538
|
async function read_sample() {
|
|
1114
539
|
return { cpu: cpu_times(), memory: await read_memory(), total_mb: totalmem() / BYTES_PER_MB };
|
|
@@ -1120,210 +545,965 @@ function external_cores(busy_cores, ledger_cores) {
|
|
|
1120
545
|
function machine_budget(cores, reading, ledger) {
|
|
1121
546
|
return {
|
|
1122
547
|
cores: cores - external_cores(reading.busy_cores, ledger.cores),
|
|
1123
|
-
memory_mb: reading.available_mb === void 0 ? Infinity : reading.available_mb + ledger.memory_mb
|
|
548
|
+
memory_mb: reading.available_mb === void 0 ? Infinity : reading.available_mb + ledger.memory_mb,
|
|
549
|
+
memory_pressured: reading.memory_pressured
|
|
1124
550
|
};
|
|
1125
551
|
}
|
|
1126
552
|
var machine_capacity = {
|
|
1127
553
|
BASELINE_CORES,
|
|
1128
554
|
busy_cores_between,
|
|
1129
555
|
busy_share_between,
|
|
556
|
+
COMPRESSION_PRESSURE_MB,
|
|
1130
557
|
external_cores,
|
|
1131
558
|
machine_budget,
|
|
1132
559
|
parse_darwin_memory,
|
|
1133
560
|
parse_linux_available,
|
|
1134
561
|
parse_linux_swapped,
|
|
562
|
+
pressured_available,
|
|
1135
563
|
read_machine,
|
|
1136
564
|
read_memory,
|
|
1137
565
|
read_sample,
|
|
566
|
+
SWAP_PRESSURE_MB,
|
|
1138
567
|
swapped_between
|
|
1139
568
|
};
|
|
1140
569
|
|
|
1141
|
-
// scripts/gate/core-budget.ts
|
|
1142
|
-
var RESERVED_PREFIX = "josh-core-reserved-";
|
|
1143
|
-
var RESERVED_SUFFIX = ".json";
|
|
1144
|
-
var POLL_INTERVAL_MS = 50;
|
|
1145
|
-
var WAIT_CAP_MS = 12e4;
|
|
1146
|
-
var MIN_BUDGET = 1;
|
|
1147
|
-
var CORE_WEIGHTS = {
|
|
1148
|
-
lint: 2,
|
|
1149
|
-
type_check: 1,
|
|
1150
|
-
spell_check: 1,
|
|
1151
|
-
eslint_scan: 1
|
|
1152
|
-
|
|
1153
|
-
|
|
1154
|
-
|
|
1155
|
-
|
|
1156
|
-
|
|
1157
|
-
|
|
1158
|
-
|
|
1159
|
-
|
|
1160
|
-
|
|
1161
|
-
|
|
1162
|
-
|
|
1163
|
-
|
|
1164
|
-
|
|
1165
|
-
|
|
1166
|
-
|
|
1167
|
-
|
|
1168
|
-
|
|
1169
|
-
|
|
1170
|
-
|
|
1171
|
-
|
|
1172
|
-
|
|
1173
|
-
|
|
1174
|
-
|
|
1175
|
-
|
|
1176
|
-
|
|
1177
|
-
|
|
1178
|
-
|
|
1179
|
-
|
|
1180
|
-
|
|
1181
|
-
|
|
1182
|
-
|
|
1183
|
-
|
|
1184
|
-
|
|
1185
|
-
|
|
1186
|
-
|
|
1187
|
-
|
|
1188
|
-
|
|
1189
|
-
|
|
1190
|
-
|
|
1191
|
-
|
|
1192
|
-
|
|
1193
|
-
|
|
1194
|
-
|
|
1195
|
-
|
|
1196
|
-
|
|
1197
|
-
|
|
1198
|
-
|
|
1199
|
-
|
|
1200
|
-
|
|
1201
|
-
|
|
1202
|
-
|
|
1203
|
-
|
|
1204
|
-
return
|
|
1205
|
-
|
|
1206
|
-
|
|
1207
|
-
|
|
1208
|
-
|
|
1209
|
-
}
|
|
1210
|
-
|
|
1211
|
-
|
|
1212
|
-
|
|
1213
|
-
|
|
1214
|
-
}
|
|
1215
|
-
|
|
1216
|
-
|
|
1217
|
-
|
|
1218
|
-
|
|
1219
|
-
|
|
1220
|
-
|
|
1221
|
-
|
|
1222
|
-
|
|
1223
|
-
|
|
1224
|
-
|
|
1225
|
-
|
|
1226
|
-
|
|
1227
|
-
|
|
1228
|
-
|
|
1229
|
-
|
|
1230
|
-
|
|
1231
|
-
|
|
1232
|
-
|
|
1233
|
-
|
|
1234
|
-
|
|
1235
|
-
|
|
1236
|
-
|
|
1237
|
-
|
|
1238
|
-
|
|
1239
|
-
|
|
1240
|
-
|
|
1241
|
-
|
|
1242
|
-
|
|
1243
|
-
|
|
1244
|
-
|
|
1245
|
-
|
|
1246
|
-
|
|
1247
|
-
|
|
1248
|
-
|
|
1249
|
-
|
|
1250
|
-
|
|
1251
|
-
|
|
1252
|
-
|
|
1253
|
-
|
|
1254
|
-
|
|
1255
|
-
|
|
1256
|
-
|
|
1257
|
-
|
|
1258
|
-
|
|
1259
|
-
|
|
1260
|
-
|
|
1261
|
-
|
|
1262
|
-
|
|
1263
|
-
|
|
1264
|
-
|
|
1265
|
-
|
|
1266
|
-
const
|
|
1267
|
-
const
|
|
1268
|
-
|
|
1269
|
-
|
|
1270
|
-
|
|
1271
|
-
|
|
1272
|
-
|
|
1273
|
-
|
|
1274
|
-
|
|
1275
|
-
|
|
1276
|
-
|
|
1277
|
-
|
|
1278
|
-
|
|
1279
|
-
|
|
1280
|
-
}
|
|
1281
|
-
|
|
1282
|
-
|
|
1283
|
-
|
|
1284
|
-
|
|
1285
|
-
|
|
1286
|
-
|
|
1287
|
-
|
|
1288
|
-
|
|
1289
|
-
|
|
1290
|
-
}
|
|
1291
|
-
}
|
|
1292
|
-
|
|
1293
|
-
|
|
1294
|
-
|
|
1295
|
-
|
|
1296
|
-
|
|
1297
|
-
|
|
1298
|
-
|
|
1299
|
-
const
|
|
1300
|
-
|
|
1301
|
-
|
|
1302
|
-
|
|
1303
|
-
|
|
1304
|
-
|
|
570
|
+
// scripts/gate/core-budget.ts
|
|
571
|
+
var RESERVED_PREFIX = "josh-core-reserved-";
|
|
572
|
+
var RESERVED_SUFFIX = ".json";
|
|
573
|
+
var POLL_INTERVAL_MS = 50;
|
|
574
|
+
var WAIT_CAP_MS = 12e4;
|
|
575
|
+
var MIN_BUDGET = 1;
|
|
576
|
+
var CORE_WEIGHTS = {
|
|
577
|
+
lint: 2,
|
|
578
|
+
type_check: 1,
|
|
579
|
+
spell_check: 1,
|
|
580
|
+
eslint_scan: 1,
|
|
581
|
+
format: 1,
|
|
582
|
+
retrospective: 1,
|
|
583
|
+
install: 1,
|
|
584
|
+
e2e: 2
|
|
585
|
+
};
|
|
586
|
+
var MEMORY_MB = {
|
|
587
|
+
lint: 2400,
|
|
588
|
+
type_check: 1300,
|
|
589
|
+
spell_check: 350,
|
|
590
|
+
eslint_scan: 2400,
|
|
591
|
+
unit_worker: 350,
|
|
592
|
+
format: 300,
|
|
593
|
+
retrospective: 900,
|
|
594
|
+
install: 100
|
|
595
|
+
};
|
|
596
|
+
var reservation_schema = process_owner_schema.extend({
|
|
597
|
+
weight: z2.number(),
|
|
598
|
+
claimed_at: z2.number(),
|
|
599
|
+
memory_mb: z2.number().optional(),
|
|
600
|
+
admitted_at: z2.number().optional(),
|
|
601
|
+
command: z2.string().optional(),
|
|
602
|
+
checkout: z2.string().optional(),
|
|
603
|
+
lane: z2.string().optional(),
|
|
604
|
+
lanes_root: z2.string().optional(),
|
|
605
|
+
wait_reason: z2.enum(core_wait.WAIT_REASONS).optional()
|
|
606
|
+
});
|
|
607
|
+
function marker_name(key) {
|
|
608
|
+
return `${RESERVED_PREFIX}${key}${RESERVED_SUFFIX}`;
|
|
609
|
+
}
|
|
610
|
+
function is_running2(reservation) {
|
|
611
|
+
return process_identity.is_same_process(reservation.pid, reservation.process_start) !== false;
|
|
612
|
+
}
|
|
613
|
+
function read_reservation(source) {
|
|
614
|
+
const raw = stamp_file.read_stamp_text(source);
|
|
615
|
+
if (raw === void 0) return void 0;
|
|
616
|
+
return json_value.parse_with(raw, reservation_schema);
|
|
617
|
+
}
|
|
618
|
+
function sweep_marker2(source) {
|
|
619
|
+
try {
|
|
620
|
+
stamp_file.remove_stamp(source);
|
|
621
|
+
} catch {
|
|
622
|
+
}
|
|
623
|
+
}
|
|
624
|
+
function live_reservation(source) {
|
|
625
|
+
const reservation = read_reservation(source);
|
|
626
|
+
if (reservation === void 0) return void 0;
|
|
627
|
+
if (is_running2(reservation)) return reservation;
|
|
628
|
+
sweep_marker2(source);
|
|
629
|
+
return void 0;
|
|
630
|
+
}
|
|
631
|
+
function marker_files2(directory) {
|
|
632
|
+
try {
|
|
633
|
+
return readdirSync2(directory).filter(
|
|
634
|
+
(name) => name.startsWith(RESERVED_PREFIX) && name.endsWith(RESERVED_SUFFIX)
|
|
635
|
+
);
|
|
636
|
+
} catch {
|
|
637
|
+
return [];
|
|
638
|
+
}
|
|
639
|
+
}
|
|
640
|
+
function live_reservations(directory = PLATFORM_TEMP_ROOT) {
|
|
641
|
+
return marker_files2(directory).flatMap((name) => {
|
|
642
|
+
const reservation = live_reservation(path3.join(directory, name));
|
|
643
|
+
return reservation === void 0 ? [] : { key: name, reservation };
|
|
644
|
+
});
|
|
645
|
+
}
|
|
646
|
+
function normalize_budget(budget) {
|
|
647
|
+
return Number.isFinite(budget) ? Math.max(MIN_BUDGET, Math.floor(budget)) : MIN_BUDGET;
|
|
648
|
+
}
|
|
649
|
+
async function default_sleep2(ms) {
|
|
650
|
+
await new Promise((resolve) => {
|
|
651
|
+
setTimeout(resolve, ms);
|
|
652
|
+
});
|
|
653
|
+
}
|
|
654
|
+
function write_reservation(target2, fields) {
|
|
655
|
+
stamp_file.replace_stamp(target2, { ...process_identity.own_fields(), ...fields });
|
|
656
|
+
}
|
|
657
|
+
function report_wait(line) {
|
|
658
|
+
process.stderr.write(`${line}
|
|
659
|
+
`);
|
|
660
|
+
}
|
|
661
|
+
function resolve_timing(options) {
|
|
662
|
+
return {
|
|
663
|
+
now: options.now ?? Date.now,
|
|
664
|
+
sleep: options.sleep ?? default_sleep2,
|
|
665
|
+
poll_interval_ms: options.poll_interval_ms ?? POLL_INTERVAL_MS,
|
|
666
|
+
wait_cap_ms: options.wait_cap_ms ?? WAIT_CAP_MS
|
|
667
|
+
};
|
|
668
|
+
}
|
|
669
|
+
async function current_budget(context, reservations) {
|
|
670
|
+
if (reservations.length <= 1) return { cores: context.budget, memory_mb: Infinity };
|
|
671
|
+
const reading = await context.read_machine();
|
|
672
|
+
const ledger = core_admission.ledger_load(reservations, context.now());
|
|
673
|
+
const budget = machine_capacity.machine_budget(context.budget, reading, ledger);
|
|
674
|
+
return { ...budget, cores: normalize_budget(budget.cores) };
|
|
675
|
+
}
|
|
676
|
+
function admit(context, overflow) {
|
|
677
|
+
write_reservation(context.target, { ...context.fields, admitted_at: context.now() });
|
|
678
|
+
return { overflow };
|
|
679
|
+
}
|
|
680
|
+
function overflow_of(context, reservations, budget) {
|
|
681
|
+
return {
|
|
682
|
+
waited_ms: context.now() - context.fields.claimed_at,
|
|
683
|
+
ledger: core_admission.sum_load(reservations),
|
|
684
|
+
budget
|
|
685
|
+
};
|
|
686
|
+
}
|
|
687
|
+
function mark_waiting(context, reason) {
|
|
688
|
+
const previous = context.fields.wait_reason;
|
|
689
|
+
if (previous === reason) return;
|
|
690
|
+
context.fields = { ...context.fields, wait_reason: reason };
|
|
691
|
+
write_reservation(context.target, context.fields);
|
|
692
|
+
if (previous === void 0) context.report(core_wait.wait_line(context.fields.command, reason));
|
|
693
|
+
}
|
|
694
|
+
async function poll_admission(context) {
|
|
695
|
+
const reservations = live_reservations(context.directory);
|
|
696
|
+
const budget = await current_budget(context, reservations);
|
|
697
|
+
if (core_admission.is_admitted(context.key, reservations, budget)) return admit(context);
|
|
698
|
+
if (context.now() >= context.deadline) {
|
|
699
|
+
return admit(context, overflow_of(context, reservations, budget));
|
|
700
|
+
}
|
|
701
|
+
mark_waiting(context, core_wait.wait_reason(context.key, reservations, budget));
|
|
702
|
+
return void 0;
|
|
703
|
+
}
|
|
704
|
+
async function await_admission(context) {
|
|
705
|
+
let admission = await poll_admission(context);
|
|
706
|
+
while (admission === void 0) {
|
|
707
|
+
await context.sleep(context.poll_interval_ms);
|
|
708
|
+
admission = await poll_admission(context);
|
|
709
|
+
}
|
|
710
|
+
return admission;
|
|
711
|
+
}
|
|
712
|
+
function resolve_ports(options) {
|
|
713
|
+
return {
|
|
714
|
+
read_machine: options.read_machine ?? machine_capacity.read_machine,
|
|
715
|
+
report: options.report ?? report_wait
|
|
716
|
+
};
|
|
717
|
+
}
|
|
718
|
+
function claim_fields(weight, claimed_at, options) {
|
|
719
|
+
const { memory_mb, command } = options;
|
|
720
|
+
return { weight, claimed_at, memory_mb, command, ...core_wait.where_of(process.cwd()) };
|
|
721
|
+
}
|
|
722
|
+
async function reserve(weight, options = {}) {
|
|
723
|
+
const timing = resolve_timing(options);
|
|
724
|
+
const directory = options.directory ?? PLATFORM_TEMP_ROOT;
|
|
725
|
+
const claimed_at = timing.now();
|
|
726
|
+
const name = marker_name(randomUUID());
|
|
727
|
+
const target2 = path3.join(directory, name);
|
|
728
|
+
const fields = claim_fields(weight, claimed_at, options);
|
|
729
|
+
write_reservation(target2, fields);
|
|
730
|
+
const admission = await await_admission({
|
|
731
|
+
...timing,
|
|
732
|
+
key: name,
|
|
733
|
+
target: target2,
|
|
734
|
+
directory,
|
|
735
|
+
budget: normalize_budget(options.budget ?? availableParallelism3()),
|
|
736
|
+
deadline: claimed_at + timing.wait_cap_ms,
|
|
737
|
+
fields,
|
|
738
|
+
...resolve_ports(options)
|
|
739
|
+
});
|
|
740
|
+
return { target: target2, admission };
|
|
741
|
+
}
|
|
742
|
+
function budget_waits(directory = PLATFORM_TEMP_ROOT, now = Date.now(), home = process.cwd()) {
|
|
743
|
+
return core_wait.waits_of(live_reservations(directory), now, home);
|
|
744
|
+
}
|
|
745
|
+
function release(handle) {
|
|
746
|
+
sweep_marker2(handle.target);
|
|
747
|
+
}
|
|
748
|
+
async function with_core_reservation(weight, run, options = {}) {
|
|
749
|
+
const handle = await reserve(weight, options);
|
|
750
|
+
try {
|
|
751
|
+
return await run(handle.admission, path3.basename(handle.target));
|
|
752
|
+
} finally {
|
|
753
|
+
release(handle);
|
|
754
|
+
}
|
|
755
|
+
}
|
|
756
|
+
async function with_command_reservation(weight, run, options = {}) {
|
|
757
|
+
if (held_mark.is_held() || unit_worker_share.is_nested_run()) return await run();
|
|
758
|
+
return await with_core_reservation(
|
|
759
|
+
weight,
|
|
760
|
+
async (_admission, key) => await held_mark.with_held_mark(run, key),
|
|
761
|
+
options
|
|
762
|
+
);
|
|
763
|
+
}
|
|
764
|
+
var core_budget = {
|
|
765
|
+
CORE_WEIGHTS,
|
|
766
|
+
HELD_KEY: held_mark.HELD_KEY,
|
|
767
|
+
HELD_VALUE: held_mark.HELD_VALUE,
|
|
768
|
+
MEMORY_MB,
|
|
769
|
+
RESERVED_PREFIX,
|
|
770
|
+
admitted_keys: core_admission.admitted_keys,
|
|
771
|
+
admitted_load: core_admission.admitted_load,
|
|
772
|
+
budget_waits,
|
|
773
|
+
describe_overflow: core_admission.describe_overflow,
|
|
774
|
+
is_held: held_mark.is_held,
|
|
775
|
+
live_reservations,
|
|
776
|
+
release,
|
|
777
|
+
reserve,
|
|
778
|
+
with_command_reservation,
|
|
779
|
+
with_core_reservation,
|
|
780
|
+
with_held_mark: held_mark.with_held_mark
|
|
781
|
+
};
|
|
782
|
+
|
|
783
|
+
// scripts/josh/josh-command-types.ts
|
|
784
|
+
var OPTIONAL_ENV_FILE_FLAGS = [`--env-file-if-exists=${ENV_FILE_NAME}`];
|
|
785
|
+
var GATE_COMMAND2 = "gate";
|
|
786
|
+
var ESLINT_CACHE_FILE = ".eslintcache";
|
|
787
|
+
var TS_BUILD_INFO_FILE = ".tsbuildinfo";
|
|
788
|
+
var CSPELL_CACHE_FILE = ".cspellcache";
|
|
789
|
+
var ESLINT_EDIT_CACHE_FILE = ".eslintcache.edit";
|
|
790
|
+
var ESLINT_RELATED_CACHE_FILE = ".eslintcache.related";
|
|
791
|
+
var PRETTIER_CACHE_FILE = ".prettiercache";
|
|
792
|
+
var PRETTIER_RELATED_CACHE_FILE = ".prettiercache.related";
|
|
793
|
+
var CSPELL_COMMIT_CACHE_FILE = ".cspellcache.commit";
|
|
794
|
+
var ESLINT_COMMIT_CACHE_FILE = ".eslintcache.commit";
|
|
795
|
+
var ROOTED_JSON_PORTABILITY = "rooted-json";
|
|
796
|
+
var ESLINT_CACHE_SPEC = {
|
|
797
|
+
cache_file: ESLINT_CACHE_FILE,
|
|
798
|
+
portability: ROOTED_JSON_PORTABILITY
|
|
799
|
+
};
|
|
800
|
+
var TS_CACHE_SPEC = {
|
|
801
|
+
cache_file: TS_BUILD_INFO_FILE,
|
|
802
|
+
portability: "raw"
|
|
803
|
+
};
|
|
804
|
+
var CSPELL_CACHE_SPEC = {
|
|
805
|
+
cache_file: CSPELL_CACHE_FILE,
|
|
806
|
+
portability: "raw"
|
|
807
|
+
};
|
|
808
|
+
var GATE_CACHE_SPECS = [
|
|
809
|
+
ESLINT_CACHE_SPEC,
|
|
810
|
+
TS_CACHE_SPEC,
|
|
811
|
+
CSPELL_CACHE_SPEC
|
|
812
|
+
];
|
|
813
|
+
var GATE_CACHE_FILES = GATE_CACHE_SPECS.map((spec) => spec.cache_file);
|
|
814
|
+
var IGNORED_CACHE_FILES = [
|
|
815
|
+
...GATE_CACHE_FILES,
|
|
816
|
+
ESLINT_EDIT_CACHE_FILE,
|
|
817
|
+
ESLINT_RELATED_CACHE_FILE,
|
|
818
|
+
PRETTIER_CACHE_FILE,
|
|
819
|
+
PRETTIER_RELATED_CACHE_FILE,
|
|
820
|
+
CSPELL_COMMIT_CACHE_FILE,
|
|
821
|
+
ESLINT_COMMIT_CACHE_FILE
|
|
822
|
+
];
|
|
823
|
+
var CONTENT_CACHE_FLAGS = ["--cache", "--cache-strategy", "content"];
|
|
824
|
+
var CACHE_LOCATION_FLAG = "--cache-location";
|
|
825
|
+
function content_cache_flags(cache_file) {
|
|
826
|
+
return [...CONTENT_CACHE_FLAGS, CACHE_LOCATION_FLAG, cache_file];
|
|
827
|
+
}
|
|
828
|
+
var ESLINT_CACHE_FLAGS = content_cache_flags(ESLINT_CACHE_FILE);
|
|
829
|
+
var ESLINT_EDIT_CACHE_FLAGS = content_cache_flags(ESLINT_EDIT_CACHE_FILE);
|
|
830
|
+
var ESLINT_RELATED_CACHE_FLAGS = content_cache_flags(ESLINT_RELATED_CACHE_FILE);
|
|
831
|
+
var CSPELL_CACHE_FLAGS = content_cache_flags(CSPELL_CACHE_FILE);
|
|
832
|
+
var PRETTIER_CACHE_FLAGS = content_cache_flags(PRETTIER_CACHE_FILE);
|
|
833
|
+
var PRETTIER_RELATED_CACHE_FLAGS = content_cache_flags(PRETTIER_RELATED_CACHE_FILE);
|
|
834
|
+
var CSPELL_COMMIT_CACHE_FLAGS = content_cache_flags(CSPELL_COMMIT_CACHE_FILE);
|
|
835
|
+
var ESLINT_COMMIT_CACHE_FLAGS = content_cache_flags(ESLINT_COMMIT_CACHE_FILE);
|
|
836
|
+
|
|
837
|
+
// scripts/josh/josh-commands-backlog.ts
|
|
838
|
+
var BACKLOG_COMMANDS = {
|
|
839
|
+
// Started from a terminal: the `backlogrun` keyword's launcher, and the board.
|
|
840
|
+
backlogrun: {
|
|
841
|
+
script: "scripts/backlog/backlogrun-cli.ts",
|
|
842
|
+
description: "Start a backlogrun in the background and show its board; with one already running, only show the board, or replace it when it is idle and the invocation differs; --stop stops it",
|
|
843
|
+
category: "AI tools",
|
|
844
|
+
reference: [
|
|
845
|
+
"[<n>...] [--only] [--max <n>] [--idle <minutes>] [--agent claude|codex] | --stop",
|
|
846
|
+
"developer",
|
|
847
|
+
["files", "network", "processes"]
|
|
848
|
+
]
|
|
849
|
+
},
|
|
850
|
+
"backlog:next": {
|
|
851
|
+
script: "scripts/backlog/backlog-next.ts",
|
|
852
|
+
description: "Order the whole opted-in backlog: auto-ok issues and the descendants of auto-ok epics, transitively through nested epics",
|
|
853
|
+
category: "AI tools",
|
|
854
|
+
reference: ["[--exclude <n>[,<n>...]] [--repo <owner/repo>]", "automation", ["network"]]
|
|
855
|
+
},
|
|
856
|
+
"backlog:offer": {
|
|
857
|
+
script: "scripts/backlog/backlog-offer-cli.ts",
|
|
858
|
+
description: "Collapse a backlogrun loop-head event into one call: read backlog:next, ask backlog:budget, return the verdict and any issues to start",
|
|
859
|
+
category: "AI tools",
|
|
860
|
+
reference: ["[options]", "automation", ["files", "network", "processes"]]
|
|
861
|
+
},
|
|
862
|
+
"backlog:drive": {
|
|
863
|
+
script: "scripts/backlog/backlog-drive-cli.ts",
|
|
864
|
+
description: "Drive backlogrun through offer, launch and merge; restore this run\u2019s lanes and report when complete",
|
|
865
|
+
category: "AI tools",
|
|
866
|
+
reference: ["--owner <pid> [options]", "automation", ["network"]]
|
|
867
|
+
},
|
|
868
|
+
"backlog:plan": {
|
|
869
|
+
script: "scripts/backlog/backlog-plan-cli.ts",
|
|
870
|
+
description: "Print the whole backlog as a plan: ready now, waiting on what, waiting on a person, out of scope",
|
|
871
|
+
category: "AI tools",
|
|
872
|
+
reference: [
|
|
873
|
+
"[issue...] [--only] [--waves] [--exclude <n>[,<n>...]]",
|
|
874
|
+
"automation",
|
|
875
|
+
["network"]
|
|
876
|
+
]
|
|
877
|
+
},
|
|
878
|
+
"backlog:stalled": {
|
|
879
|
+
script: "scripts/backlog/backlog-stalled-cli.ts",
|
|
880
|
+
// A stall sends one Telegram warning. The `Stop` hook calls the detector
|
|
881
|
+
// directly, so this flag costs only the manual command its in-process dispatch.
|
|
882
|
+
tsx_arguments: OPTIONAL_ENV_FILE_FLAGS,
|
|
883
|
+
description: "Report whether ready backlog work is sitting undispatched with a free lane and no recent dispatch",
|
|
884
|
+
category: "AI tools",
|
|
885
|
+
reference: ["", "automation", ["network"]]
|
|
886
|
+
},
|
|
887
|
+
"backlog:budget": {
|
|
888
|
+
script: "scripts/backlog/backlog-budget-cli.ts",
|
|
889
|
+
description: "Say whether a backlogrun may start more work, keep watching, or finish",
|
|
890
|
+
category: "AI tools",
|
|
891
|
+
reference: ["[options]", "automation", ["none"]]
|
|
892
|
+
}
|
|
893
|
+
};
|
|
894
|
+
|
|
895
|
+
// scripts/josh/josh-commands-document.ts
|
|
896
|
+
var DOCUMENT_COMMANDS = {
|
|
897
|
+
"doc:section": {
|
|
898
|
+
script: "scripts/document/document-section-cli.ts",
|
|
899
|
+
description: 'Print one section of a markdown document, for a `file.md` \u2192 "Heading" reference',
|
|
900
|
+
category: "AI tools",
|
|
901
|
+
reference: ["<file> <heading>", "automation", ["none"]],
|
|
902
|
+
is_bundled: true
|
|
903
|
+
},
|
|
904
|
+
"read:set": {
|
|
905
|
+
script: "scripts/document/read-set-cli.ts",
|
|
906
|
+
description: "Print what an entry point reads before it starts, and what that read costs",
|
|
907
|
+
category: "AI tools",
|
|
908
|
+
reference: ["[<entry>] [--json]", "automation", ["none"]]
|
|
909
|
+
},
|
|
910
|
+
"doc:read": {
|
|
911
|
+
script: "scripts/document/document-read-cli.ts",
|
|
912
|
+
description: "Read a whole document safely: print it, or point at the Read tool when over the Bash cap",
|
|
913
|
+
category: "AI tools",
|
|
914
|
+
reference: ["<file>", "automation", ["none"]]
|
|
915
|
+
},
|
|
916
|
+
"read:files": {
|
|
917
|
+
script: "scripts/document/read-files-cli.ts",
|
|
918
|
+
description: "Read several files in one call so edit targets fold into one turn",
|
|
919
|
+
category: "AI tools",
|
|
920
|
+
reference: ["<path> [<path> ...]", "automation", ["none"]]
|
|
921
|
+
},
|
|
922
|
+
"edit:files": {
|
|
923
|
+
script: "scripts/document/edit-files-cli.ts",
|
|
924
|
+
description: "Apply several content-addressed edits from a plan in one call",
|
|
925
|
+
category: "AI tools",
|
|
926
|
+
reference: ["<plan-path | ->", "automation", ["none"]]
|
|
927
|
+
}
|
|
928
|
+
};
|
|
929
|
+
|
|
930
|
+
// scripts/josh/josh-commands-guard.ts
|
|
931
|
+
var GUARD_COMMANDS = {
|
|
932
|
+
"investigation:guard": {
|
|
933
|
+
script: "scripts/delegation/investigation-guard.ts",
|
|
934
|
+
description: "Claude Code hook: refuse a read once the unedited-read threshold is reached again (reads the tool call on stdin)",
|
|
935
|
+
category: "AI tools",
|
|
936
|
+
reference: ["", "automation", ["none"]]
|
|
937
|
+
},
|
|
938
|
+
"duplicate-read:guard": {
|
|
939
|
+
script: "scripts/delegation/duplicate-read-guard.ts",
|
|
940
|
+
description: "Claude Code hook: refuse a second whole-file read of a path whose content has not changed since the run last read it (reads the tool call on stdin)",
|
|
941
|
+
category: "AI tools",
|
|
942
|
+
reference: ["", "automation", ["none"]]
|
|
943
|
+
},
|
|
944
|
+
"rule:guard": {
|
|
945
|
+
script: "scripts/rules/rule-guard.ts",
|
|
946
|
+
description: "Claude Code hook: deliver a trigger-delivered rule at the call that binds it (reads the tool call on stdin)",
|
|
947
|
+
category: "AI tools",
|
|
948
|
+
reference: ["", "automation", ["none"]]
|
|
949
|
+
}
|
|
950
|
+
};
|
|
951
|
+
|
|
952
|
+
// scripts/josh/josh-commands-issue.ts
|
|
953
|
+
var ISSUES_WITH_REPO = "<issue...> [--repo <owner/repo>]";
|
|
954
|
+
var ISSUE_COMMANDS = {
|
|
955
|
+
"issue:ack": {
|
|
956
|
+
script: "scripts/issue/issue-ack-cli.ts",
|
|
957
|
+
description: "Record that the run read a person's update to its issue, printing the update it acknowledges",
|
|
958
|
+
category: "AI tools",
|
|
959
|
+
reference: ["<issue>", "automation", ["network"]]
|
|
960
|
+
},
|
|
961
|
+
"issue:read": {
|
|
962
|
+
script: "scripts/issue/issue-read-cli.ts",
|
|
963
|
+
description: "Print each issue's title, body and every comment on it, in one call",
|
|
964
|
+
category: "AI tools",
|
|
965
|
+
reference: ["<issue...>", "automation", ["network"]]
|
|
966
|
+
},
|
|
967
|
+
"issue:state": {
|
|
968
|
+
script: "scripts/issue/issue-state-cli.ts",
|
|
969
|
+
description: "Print each issue's state and labels, in the spelling the documents compare against",
|
|
970
|
+
category: "AI tools",
|
|
971
|
+
reference: [ISSUES_WITH_REPO, "automation", ["network"]]
|
|
972
|
+
},
|
|
973
|
+
"issue:scout": {
|
|
974
|
+
script: "scripts/issue/issue-scout-cli.ts",
|
|
975
|
+
description: "Before filing: say whether an issue like this exists and which epic it belongs to",
|
|
976
|
+
category: "AI tools",
|
|
977
|
+
reference: ["<title> [--body <summary> | --body-file <path>]", "automation", ["network"]]
|
|
978
|
+
},
|
|
979
|
+
"defect:rate": {
|
|
980
|
+
script: "scripts/issue/defect-rate-cli.ts",
|
|
981
|
+
description: "Print the defect rate of merged work: defects filed per enhancement completed",
|
|
982
|
+
category: "AI tools",
|
|
983
|
+
reference: ["[--days <n>]", "automation", ["network"]]
|
|
984
|
+
},
|
|
985
|
+
"issue:fold": {
|
|
986
|
+
script: "scripts/issue/issue-fold-cli.ts",
|
|
987
|
+
description: "Before a second filing: say whether findings from this session fold into one issue",
|
|
988
|
+
category: "AI tools",
|
|
989
|
+
reference: ["<title>... [--not-separable] [--json]", "automation", ["none"]]
|
|
990
|
+
},
|
|
991
|
+
"issue:fold-existing": {
|
|
992
|
+
script: "scripts/issue/issue-fold-existing-cli.ts",
|
|
993
|
+
description: "Assess whether a complete draft can join an unstarted issue",
|
|
994
|
+
category: "AI tools",
|
|
995
|
+
reference: ["<assessment.json> [--json]", "automation", ["none"]]
|
|
996
|
+
},
|
|
997
|
+
"issue:cite": {
|
|
998
|
+
script: "scripts/issue/issue-cite-cli.ts",
|
|
999
|
+
description: "Print the paste-ready number-link citation line for each issue, in one call",
|
|
1000
|
+
category: "AI tools",
|
|
1001
|
+
reference: [ISSUES_WITH_REPO, "automation", ["network"]]
|
|
1002
|
+
},
|
|
1003
|
+
"issue:comment": {
|
|
1004
|
+
script: "scripts/issue/issue-comment-cli.ts",
|
|
1005
|
+
description: "Post one comment to an issue from a file, so no shell expands the body",
|
|
1006
|
+
category: "AI tools",
|
|
1007
|
+
reference: ["<issue> --body <text> | --body-file <path>", "automation", ["network"]]
|
|
1008
|
+
},
|
|
1009
|
+
"issue:file": {
|
|
1010
|
+
script: "scripts/issue/issue-file-cli.ts",
|
|
1011
|
+
description: "File an Issue with every filing step: lint, Origin, fold, WIP cap, duplicate scout, labels, then epic:bundle",
|
|
1012
|
+
category: "AI tools",
|
|
1013
|
+
reference: [
|
|
1014
|
+
"<title> --body-file <path> --depth <0|1|2> [--route <route>] [--label <name>] [--repo <owner/repo>] [--distinct <N,\u2026>] [--over-cap] [--no-auto-ok] [--release]",
|
|
1015
|
+
"automation",
|
|
1016
|
+
["network"]
|
|
1017
|
+
]
|
|
1018
|
+
},
|
|
1019
|
+
"issue:release": {
|
|
1020
|
+
script: "scripts/issue/issue-release-cli.ts",
|
|
1021
|
+
description: "Link an Issue to the repository's open release Issue as its blocker (or, with --wait, as waiting on it), filing one when none is open",
|
|
1022
|
+
category: "AI tools",
|
|
1023
|
+
reference: ["<issue> [--wait]", "automation", ["network"]]
|
|
1024
|
+
}
|
|
1025
|
+
};
|
|
1026
|
+
|
|
1027
|
+
// scripts/josh/josh-commands-lane.ts
|
|
1028
|
+
var LANE_SCRIPT = "scripts/lane/lane-cli.ts";
|
|
1029
|
+
var LANE_ARGUMENTS = { script: LANE_SCRIPT, tsx_arguments: OPTIONAL_ENV_FILE_FLAGS };
|
|
1030
|
+
var ISSUE_WITH_OPTIONS = "<issue> [options]";
|
|
1031
|
+
var MEASURE_ARGUMENTS = {
|
|
1032
|
+
script: "scripts/lane/lane-measure-cli.ts",
|
|
1033
|
+
tsx_arguments: OPTIONAL_ENV_FILE_FLAGS
|
|
1034
|
+
};
|
|
1035
|
+
var LANE_COMMANDS = {
|
|
1036
|
+
"lane:open": {
|
|
1037
|
+
...LANE_ARGUMENTS,
|
|
1038
|
+
description: "Open a lane: a linked work tree with its own branch and its own port seed",
|
|
1039
|
+
category: "AI tools",
|
|
1040
|
+
reference: [ISSUE_WITH_OPTIONS, "automation", ["files", "git"]],
|
|
1041
|
+
default_script_arguments: ["open"]
|
|
1042
|
+
},
|
|
1043
|
+
"lane:close": {
|
|
1044
|
+
...LANE_ARGUMENTS,
|
|
1045
|
+
description: "Close a lane, leaving no work tree, branch or directory behind",
|
|
1046
|
+
category: "AI tools",
|
|
1047
|
+
reference: ["<issue>", "automation", ["files", "git"]],
|
|
1048
|
+
default_script_arguments: ["close"]
|
|
1049
|
+
},
|
|
1050
|
+
"lane:list": {
|
|
1051
|
+
...LANE_ARGUMENTS,
|
|
1052
|
+
description: "List the open lanes: which issue, which ports, and where each one is",
|
|
1053
|
+
category: "AI tools",
|
|
1054
|
+
reference: ["", "automation", ["none"]],
|
|
1055
|
+
default_script_arguments: ["list"]
|
|
1056
|
+
},
|
|
1057
|
+
"lane:prune": {
|
|
1058
|
+
...LANE_ARGUMENTS,
|
|
1059
|
+
description: "Close every lane an interruption left without its work tree and every spent lane, then sweep unregistered leftovers under the lanes root",
|
|
1060
|
+
category: "AI tools",
|
|
1061
|
+
reference: ["", "automation", ["files", "git"]],
|
|
1062
|
+
default_script_arguments: ["prune"]
|
|
1063
|
+
},
|
|
1064
|
+
"lane:output": {
|
|
1065
|
+
...LANE_ARGUMENTS,
|
|
1066
|
+
description: "Record, or read back, where the unit running a lane\u2019s child writes",
|
|
1067
|
+
category: "AI tools",
|
|
1068
|
+
reference: ["<issue> [path]", "automation", ["files"]],
|
|
1069
|
+
default_script_arguments: ["output"]
|
|
1070
|
+
},
|
|
1071
|
+
"lane:dispatch": {
|
|
1072
|
+
...LANE_ARGUMENTS,
|
|
1073
|
+
description: "Start a lane\u2019s child as a detached process that outlives this session",
|
|
1074
|
+
category: "AI tools",
|
|
1075
|
+
reference: ["<issue> <prompt>", "automation", ["processes"]],
|
|
1076
|
+
default_script_arguments: ["dispatch"]
|
|
1077
|
+
},
|
|
1078
|
+
"lane:await": {
|
|
1079
|
+
...LANE_ARGUMENTS,
|
|
1080
|
+
description: "Block until any of the named in-flight lane children completes; prints which one finished",
|
|
1081
|
+
category: "AI tools",
|
|
1082
|
+
reference: ["<issue> [<issue>...] [--owner <pid>]", "automation", ["files", "processes"]],
|
|
1083
|
+
default_script_arguments: ["await"]
|
|
1084
|
+
},
|
|
1085
|
+
"lane:launch": {
|
|
1086
|
+
script: "scripts/lane/lane-launch-cli.ts",
|
|
1087
|
+
description: "Collapse a backlogrun lane-start event into one call: open the lane, pop and re-install on the first, dispatch the child",
|
|
1088
|
+
category: "AI tools",
|
|
1089
|
+
reference: ["<issue> [--stash <message>]", "automation", ["files", "git", "processes"]]
|
|
1090
|
+
},
|
|
1091
|
+
// `.env` is read for `JOSH_LANE_ROOT`, as every lane command reads it, so the lane is found.
|
|
1092
|
+
"lane:restart": {
|
|
1093
|
+
script: "scripts/lane/lane-restart-cli.ts",
|
|
1094
|
+
tsx_arguments: OPTIONAL_ENV_FILE_FLAGS,
|
|
1095
|
+
description: "Stop a running lane child, stash its uncommitted work and re-dispatch its issue in a fresh session \u2014 not counted as an outage or a failure",
|
|
1096
|
+
category: "AI tools",
|
|
1097
|
+
reference: ["<issue>", "developer", ["files", "git", "processes", "network"]]
|
|
1098
|
+
},
|
|
1099
|
+
// `.env` is read for `JOSH_LANE_ROOT`, as every lane command reads it, so the lane is found.
|
|
1100
|
+
"lane:withdraw": {
|
|
1101
|
+
script: "scripts/lane/lane-withdraw-cli.ts",
|
|
1102
|
+
tsx_arguments: OPTIONAL_ENV_FILE_FLAGS,
|
|
1103
|
+
description: "Stop a running lane child, stash its uncommitted work, close its issue as not planned and close the lane \u2014 booked withdrawn, not as an outage or a failure",
|
|
1104
|
+
category: "AI tools",
|
|
1105
|
+
reference: ["<issue> --reason <text>", "developer", ["files", "git", "processes", "network"]]
|
|
1106
|
+
},
|
|
1107
|
+
// A live run's lane limit: `.env` is read so the bare form reports the
|
|
1108
|
+
// `JOSH_LANE_LIMIT` a person set there when no override stands.
|
|
1109
|
+
"lane:limit": {
|
|
1110
|
+
script: "scripts/lane/lane-limit-cli.ts",
|
|
1111
|
+
tsx_arguments: OPTIONAL_ENV_FILE_FLAGS,
|
|
1112
|
+
description: "Change a live backlogrun\u2019s lane limit without stopping it, or print the limit, lanes in use and free lanes",
|
|
1113
|
+
category: "AI tools",
|
|
1114
|
+
reference: ["[<limit>] [--reset]", "developer", ["files"]]
|
|
1115
|
+
},
|
|
1116
|
+
// The lane-limit measurement: `.env` is read so `lane:stats` labels the row
|
|
1117
|
+
// with the `JOSH_LANE_LIMIT` a person set there.
|
|
1118
|
+
"lane:sample": {
|
|
1119
|
+
...MEASURE_ARGUMENTS,
|
|
1120
|
+
description: "Record the machine load (load average, swap, free memory, working lanes) to the lane ledger \u2014 a backlogrun samples itself, this is for a reading outside one",
|
|
1121
|
+
category: "AI tools",
|
|
1122
|
+
reference: ["[--every <seconds>]", "maintainer", ["files"]],
|
|
1123
|
+
default_script_arguments: ["sample"]
|
|
1124
|
+
},
|
|
1125
|
+
"lane:stats": {
|
|
1126
|
+
...MEASURE_ARGUMENTS,
|
|
1127
|
+
description: "Print one table row of lane throughput, gate duration and machine load over a period, the per-stage durations of a lane under it, the wait after each ship stop reason under those, and the duration and reuse count of each gate check last",
|
|
1128
|
+
category: "AI tools",
|
|
1129
|
+
reference: ["--period <days> [--limit <lane-limit>]", "maintainer", ["none"]],
|
|
1130
|
+
default_script_arguments: ["stats"]
|
|
1131
|
+
}
|
|
1132
|
+
};
|
|
1133
|
+
|
|
1134
|
+
// scripts/josh/josh-commands-run.ts
|
|
1135
|
+
var RUN_HOLD_SCRIPT = "scripts/run/hold/run-hold-cli.ts";
|
|
1136
|
+
var RUN_COMMANDS = {
|
|
1137
|
+
"run:hold": {
|
|
1138
|
+
script: RUN_HOLD_SCRIPT,
|
|
1139
|
+
description: "Claim this working tree for a run, or say which run already holds it",
|
|
1140
|
+
category: "AI tools",
|
|
1141
|
+
reference: ["[<issue> [--fullrun | --halfrun-stop | --prrun-stop]]", "automation", ["files"]]
|
|
1142
|
+
},
|
|
1143
|
+
"run:release": {
|
|
1144
|
+
script: RUN_HOLD_SCRIPT,
|
|
1145
|
+
description: "Release this working tree's run record",
|
|
1146
|
+
category: "AI tools",
|
|
1147
|
+
reference: ["[issue|--force]", "automation", ["files"]],
|
|
1148
|
+
default_script_arguments: ["--release"]
|
|
1149
|
+
},
|
|
1150
|
+
"run:tidy": {
|
|
1151
|
+
script: "scripts/run/tidy/run-tidy-cli.ts",
|
|
1152
|
+
description: "Close merged lanes and drop stashes whose issues are all merged",
|
|
1153
|
+
category: "AI tools",
|
|
1154
|
+
reference: ["", "automation", ["git", "network", "files"]]
|
|
1155
|
+
},
|
|
1156
|
+
"run:carry": {
|
|
1157
|
+
script: "scripts/run/carry/run-carry-cli.ts",
|
|
1158
|
+
// `--stopped` sends the stop confirmation, so the Telegram credentials come from `.env` as for
|
|
1159
|
+
// `run:wake` below.
|
|
1160
|
+
tsx_arguments: OPTIONAL_ENV_FILE_FLAGS,
|
|
1161
|
+
description: "Carry one invocation\u2019s budget across its own session cuts",
|
|
1162
|
+
category: "AI tools",
|
|
1163
|
+
reference: ["<operation> [arguments...]", "automation", ["files"]]
|
|
1164
|
+
},
|
|
1165
|
+
"run:add": {
|
|
1166
|
+
script: "scripts/run/add/run-add-cli.ts",
|
|
1167
|
+
description: "Add issues to a live backlogrun, ahead of the queue unless --no-priority",
|
|
1168
|
+
category: "AI tools",
|
|
1169
|
+
reference: ["<issue...> [--no-priority]", "automation", ["network", "files"]]
|
|
1170
|
+
},
|
|
1171
|
+
"run:wake": {
|
|
1172
|
+
script: "scripts/run/wake/run-wake-cli.ts",
|
|
1173
|
+
// `.env` rather than the ambient environment:
|
|
1174
|
+
// the failure warning needs the Telegram credentials, the same
|
|
1175
|
+
// reasons `notify` and `followup` carry these flags.
|
|
1176
|
+
tsx_arguments: OPTIONAL_ENV_FILE_FLAGS,
|
|
1177
|
+
description: "Wake the next session of a cut backlogrun from outside the conversation",
|
|
1178
|
+
category: "AI tools",
|
|
1179
|
+
reference: ["[options]", "automation", ["processes", "notifications"]]
|
|
1180
|
+
},
|
|
1181
|
+
"run:cut": {
|
|
1182
|
+
script: "scripts/run/cut/run-cut-cli.ts",
|
|
1183
|
+
description: "Cut a lane child before the gate and resume a fresh process",
|
|
1184
|
+
category: "AI tools",
|
|
1185
|
+
reference: ["[--resume] <issue> [--impl] [--handoff <path>]", "automation", ["files"]]
|
|
1186
|
+
},
|
|
1187
|
+
"run:liveness": {
|
|
1188
|
+
script: "scripts/run/run-liveness-cli.ts",
|
|
1189
|
+
description: "Say whether a delegated unit is still working, or stopped without reporting",
|
|
1190
|
+
category: "AI tools",
|
|
1191
|
+
reference: ["<issue> --output <path> [options]", "automation", ["processes"]]
|
|
1192
|
+
},
|
|
1193
|
+
"run:ending": {
|
|
1194
|
+
script: "scripts/run/run-ending-cli.ts",
|
|
1195
|
+
description: "Classify how a dispatched lane child ended (merged, cut, abandoned, unreadable)",
|
|
1196
|
+
category: "AI tools",
|
|
1197
|
+
reference: ["<issue> --output <path> [--repo <owner/repo>]", "automation", ["network"]]
|
|
1198
|
+
},
|
|
1199
|
+
"run:progress": {
|
|
1200
|
+
script: "scripts/run/progress/run-progress-cli.ts",
|
|
1201
|
+
description: "Report an unattended run\u2019s progress once it has gone quiet for an interval",
|
|
1202
|
+
category: "AI tools",
|
|
1203
|
+
reference: [
|
|
1204
|
+
"[--once | --wait] [--interval <minutes>] [--hours <hours>] [--repo <owner/repo>] [--output <path>] | --mark | --path",
|
|
1205
|
+
"automation",
|
|
1206
|
+
["files"]
|
|
1207
|
+
]
|
|
1208
|
+
},
|
|
1209
|
+
"run:board": {
|
|
1210
|
+
script: "scripts/run/board/run-board-cli.ts",
|
|
1211
|
+
// `--every` pushes its frame as a Telegram, so the credentials come from `.env` as for `run:carry`.
|
|
1212
|
+
tsx_arguments: OPTIONAL_ENV_FILE_FLAGS,
|
|
1213
|
+
description: "Draw a live board of the running backlogrun, redrawn every second",
|
|
1214
|
+
category: "AI tools",
|
|
1215
|
+
reference: ["[--once | --chat | --every <minutes>]", "automation", ["files", "network"]]
|
|
1216
|
+
},
|
|
1217
|
+
"run:prep": {
|
|
1218
|
+
script: "scripts/run/run-prep-cli.ts",
|
|
1219
|
+
description: "Bundle a run\u2019s pre-edit reads: body, comments, state, dependency scope",
|
|
1220
|
+
category: "AI tools",
|
|
1221
|
+
reference: ["<issue>", "automation", ["network"]]
|
|
1222
|
+
},
|
|
1223
|
+
// The entry sequence a lane opened on, folded into one call: claim the tree,
|
|
1224
|
+
// read the budget, gather the issue reads and decide the pre-implementation step. `run:hold`,
|
|
1225
|
+
// `cost --cut`, `run:prep` and `run:step` were four round trips re-billing a lane's full context each.
|
|
1226
|
+
"run:entry": {
|
|
1227
|
+
script: "scripts/run/entry/run-entry-cli.ts",
|
|
1228
|
+
// A `busy` hold or an `over` budget sends the stop confirmation.
|
|
1229
|
+
tsx_arguments: OPTIONAL_ENV_FILE_FLAGS,
|
|
1230
|
+
description: "Open a run in one call: claim the tree, read the budget, bundle the reads, decide the pre-implementation step",
|
|
1231
|
+
category: "AI tools",
|
|
1232
|
+
reference: ["<issue> [--to <command>]", "automation", ["git", "network", "files"]]
|
|
1233
|
+
},
|
|
1234
|
+
"run:status": {
|
|
1235
|
+
script: "scripts/run/run-status-cli.ts",
|
|
1236
|
+
description: "Bundle a run\u2019s read-only status: issue state, cost verdict, carry counters",
|
|
1237
|
+
category: "AI tools",
|
|
1238
|
+
reference: ["<issue> [--repo <owner/repo>]", "automation", ["network"]]
|
|
1239
|
+
},
|
|
1240
|
+
"run:next": {
|
|
1241
|
+
script: "scripts/run/run-next-cli.ts",
|
|
1242
|
+
description: "Print the next step a fullrun takes, computed from the run\u2019s state",
|
|
1243
|
+
category: "AI tools",
|
|
1244
|
+
reference: ["<issue>", "automation", ["network"]]
|
|
1245
|
+
},
|
|
1246
|
+
"run:step": {
|
|
1247
|
+
script: "scripts/run/run-step-cli.ts",
|
|
1248
|
+
description: "Print the run\u2019s next single action, computed from the event stream, carry record and issue state",
|
|
1249
|
+
category: "AI tools",
|
|
1250
|
+
reference: ["[<issue>]", "automation", ["network", "files"]]
|
|
1251
|
+
}
|
|
1252
|
+
};
|
|
1253
|
+
|
|
1254
|
+
// scripts/josh/josh-commands-split.ts
|
|
1255
|
+
var SPLIT_COMMANDS = {
|
|
1256
|
+
"split:assess": {
|
|
1257
|
+
script: "scripts/split/split-assess-cli.ts",
|
|
1258
|
+
description: "Measure the branch change size (tests excluded) and answer the split assessment size question: split | single",
|
|
1259
|
+
category: "AI tools",
|
|
1260
|
+
reference: ["[--json]", "automation", ["none"]]
|
|
1261
|
+
}
|
|
1262
|
+
};
|
|
1263
|
+
|
|
1264
|
+
// scripts/josh/josh-commands-ai.ts
|
|
1265
|
+
var ISSUE_WITH_OPTIONS2 = "<issue> [options]";
|
|
1266
|
+
var AI_COMMANDS = {
|
|
1267
|
+
...ISSUE_COMMANDS,
|
|
1268
|
+
"pkg:scout": {
|
|
1269
|
+
script: "scripts/package/package-scout-cli.ts",
|
|
1270
|
+
description: "Rank package candidates by measured metrics so the Package-First tier decision is read, not judged",
|
|
1271
|
+
category: "AI tools",
|
|
1272
|
+
reference: ["<keywords> [--size <n>]", "automation", ["network"]]
|
|
1273
|
+
},
|
|
1274
|
+
"stash:pop": {
|
|
1275
|
+
script: "scripts/git/stash/stash-pop-cli.ts",
|
|
1276
|
+
description: "Pop the stash matching this message, not whichever a shared stack has on top",
|
|
1277
|
+
category: "AI tools",
|
|
1278
|
+
reference: ["<message>", "automation", ["git"]]
|
|
1279
|
+
},
|
|
1280
|
+
epic: {
|
|
1281
|
+
script: "scripts/epic/epic.ts",
|
|
1282
|
+
description: "Create an epic issue from its child issue numbers",
|
|
1283
|
+
category: "AI tools",
|
|
1284
|
+
reference: ["<title> <issue...> [--ordered]", "automation", ["network"]]
|
|
1285
|
+
},
|
|
1286
|
+
"epic:next": {
|
|
1287
|
+
script: "scripts/epic/epic-next.ts",
|
|
1288
|
+
description: "List an epic's runnable children, bundled per repository",
|
|
1289
|
+
category: "AI tools",
|
|
1290
|
+
reference: ["<epic>... [--repo <owner/repo>] [--lanes]", "automation", ["network"]]
|
|
1291
|
+
},
|
|
1292
|
+
"epic:bundle": {
|
|
1293
|
+
script: "scripts/epic/epic-bundle-cli.ts",
|
|
1294
|
+
description: "Say whether a newly filed issue belongs with ones already in the backlog",
|
|
1295
|
+
category: "AI tools",
|
|
1296
|
+
reference: ["<issue>", "automation", ["network"]]
|
|
1297
|
+
},
|
|
1298
|
+
"epic:audit": {
|
|
1299
|
+
script: "scripts/epic/epic-audit-cli.ts",
|
|
1300
|
+
description: "Audit an epic's children against each other for contradictions",
|
|
1301
|
+
category: "AI tools",
|
|
1302
|
+
reference: ["<epic>", "automation", ["network"]]
|
|
1303
|
+
},
|
|
1304
|
+
"epic:check": {
|
|
1305
|
+
script: "scripts/epic/epic-check.ts",
|
|
1306
|
+
description: "Check an epic issue against the tracking requirements",
|
|
1307
|
+
category: "AI tools",
|
|
1308
|
+
reference: ["<epic>", "automation", ["network"]]
|
|
1309
|
+
},
|
|
1310
|
+
"auto-ok:next": {
|
|
1311
|
+
script: "scripts/auto-ok/auto-ok-cli.ts",
|
|
1312
|
+
description: "Print the next opted-in issue an unattended run may pick up outside an epic",
|
|
1313
|
+
category: "AI tools",
|
|
1314
|
+
reference: ["[--exclude <n>[,<n>...]] [--label <name>]", "automation", ["network"]]
|
|
1315
|
+
},
|
|
1316
|
+
"auto-ok:judge": {
|
|
1317
|
+
script: "scripts/auto-ok/auto-ok-judge-cli.ts",
|
|
1318
|
+
description: "List the open issues backlogrun owes an auto-ok judgement, or record one (--apply / --decline)",
|
|
1319
|
+
category: "AI tools",
|
|
1320
|
+
reference: [
|
|
1321
|
+
"[--apply <n> (--lane | --solo) | --decline <n> --reason <text>]",
|
|
1322
|
+
"automation",
|
|
1323
|
+
["network"]
|
|
1324
|
+
]
|
|
1325
|
+
},
|
|
1326
|
+
...BACKLOG_COMMANDS,
|
|
1327
|
+
cost: {
|
|
1328
|
+
script: "scripts/cost-runtime/cost-cli.ts",
|
|
1329
|
+
description: "Say whether the next turn of a run crosses the context-cut threshold (--cut) or a given tokens-per-request figure (--over)",
|
|
1330
|
+
category: "AI tools",
|
|
1331
|
+
reference: ["(--cut | --over <tokens-per-request>) [--path <dir>]", "automation", ["none"]]
|
|
1332
|
+
},
|
|
1333
|
+
"cost:audit": {
|
|
1334
|
+
script: "scripts/cost-audit/cost-audit-cli.ts",
|
|
1335
|
+
description: "Report AI cost over a period by session kind, tool category and guard, with cost per merge and regressions against the period before",
|
|
1336
|
+
category: "AI tools",
|
|
1337
|
+
reference: ["[--period <days>]", "maintainer", ["none"]],
|
|
1338
|
+
// Kit-only: it reads kit's own transcript store, like `josh retrospective`, which carries its
|
|
1339
|
+
// summary line.
|
|
1340
|
+
is_kit_only: true
|
|
1341
|
+
},
|
|
1342
|
+
...DOCUMENT_COMMANDS,
|
|
1343
|
+
time: {
|
|
1344
|
+
script: "scripts/time/time-cli.ts",
|
|
1345
|
+
description: "Report where a run's wall clock went: model wait, tool execution, human wait",
|
|
1346
|
+
category: "AI tools",
|
|
1347
|
+
reference: ["[options]", "maintainer", ["none"]],
|
|
1348
|
+
// Kit-only: it measures kit's own development runs and its report modules live under the
|
|
1349
|
+
// undistributed `scripts/time/`, so it means nothing in a consumer project and is dropped from a
|
|
1350
|
+
// consumer's help. The runtime analysis hooks and guards rely on stays
|
|
1351
|
+
// distributed under `scripts/time-runtime/`.
|
|
1352
|
+
is_kit_only: true
|
|
1353
|
+
},
|
|
1354
|
+
// The tool-calls-per-round-trip density the batching guard is measured on, aggregated across the
|
|
1355
|
+
// recent lane sessions. Kit-only for the same reason `time` is: it reads
|
|
1356
|
+
// kit's own lane transcripts and means nothing in a consumer project.
|
|
1357
|
+
"time:density": {
|
|
1358
|
+
script: "scripts/time/time-density-cli.ts",
|
|
1359
|
+
description: "Report tool calls per round trip across the recent lane sessions",
|
|
1360
|
+
category: "AI tools",
|
|
1361
|
+
reference: ["[--lanes <n>] [--path <dir>]", "maintainer", ["none"]],
|
|
1362
|
+
is_kit_only: true
|
|
1363
|
+
},
|
|
1364
|
+
retrospective: {
|
|
1365
|
+
script: "scripts/retrospective/retrospective-cli.ts",
|
|
1366
|
+
description: "Aggregate a finished run's cost, review findings, observation ledger and events into one digest",
|
|
1367
|
+
category: "AI tools",
|
|
1368
|
+
reference: ["", "maintainer", ["none"]],
|
|
1369
|
+
core_weight: core_budget.CORE_WEIGHTS.retrospective,
|
|
1370
|
+
memory_mb: core_budget.MEMORY_MB.retrospective,
|
|
1371
|
+
// Kit-only: it reads kit's own development run — the transcript store, the review ledger and the
|
|
1372
|
+
// run event stream — so it means nothing in a consumer project and is dropped from a consumer's
|
|
1373
|
+
// help, through the same declaration `josh time` uses for the same reason.
|
|
1374
|
+
is_kit_only: true
|
|
1375
|
+
},
|
|
1376
|
+
"review:brief": {
|
|
1377
|
+
script: "scripts/review/review-brief-cli.ts",
|
|
1378
|
+
description: "Print the whole /code-review invocation: level, what the gate already proved, target (--level-only for the level alone)",
|
|
1379
|
+
category: "AI tools",
|
|
1380
|
+
reference: ["[--round <1|2>] | --level-only [--staged] [--json]", "automation", ["files"]]
|
|
1381
|
+
},
|
|
1382
|
+
"review:attest": {
|
|
1383
|
+
script: "scripts/review/review-attest-cli.ts",
|
|
1384
|
+
description: "Record, or verify, which checkout a /code-review actually read",
|
|
1385
|
+
category: "AI tools",
|
|
1386
|
+
reference: ["<nonce> | --check", "automation", ["files"]]
|
|
1387
|
+
},
|
|
1388
|
+
"review:round2": {
|
|
1389
|
+
script: "scripts/review/review-round2-cli.ts",
|
|
1390
|
+
description: "Say whether the second /code-review round is due, or may be skipped entirely",
|
|
1391
|
+
category: "AI tools",
|
|
1392
|
+
reference: ["[--round-1-closed] [--json]", "automation", ["none"]]
|
|
1393
|
+
},
|
|
1394
|
+
delegate: {
|
|
1395
|
+
script: "scripts/delegation/delegation-cli.ts",
|
|
1396
|
+
description: "Say whether a run step may go to a cheaper execution tier",
|
|
1397
|
+
category: "AI tools",
|
|
1398
|
+
reference: ["<step> | --list", "automation", ["none"]]
|
|
1399
|
+
},
|
|
1400
|
+
fanout: {
|
|
1401
|
+
script: "scripts/delegation/fanout-cli.ts",
|
|
1402
|
+
description: "Say whether proposed implementation units are file-disjoint, so they may run in parallel",
|
|
1403
|
+
category: "AI tools",
|
|
1404
|
+
reference: ["<unit-files> <unit-files> [<unit-files> \u2026]", "automation", ["none"]]
|
|
1405
|
+
},
|
|
1406
|
+
...RUN_COMMANDS,
|
|
1407
|
+
"repo:party": {
|
|
1408
|
+
script: "scripts/discovery/repo-party-cli.ts",
|
|
1409
|
+
description: "Say whether a repository is first-party or third-party by owner equality (computed, not judged)",
|
|
1410
|
+
category: "AI tools",
|
|
1411
|
+
reference: ["[<owner/repo>]", "automation", ["none"]]
|
|
1412
|
+
},
|
|
1413
|
+
"run:merge": {
|
|
1414
|
+
script: "scripts/run/merge/run-merge-cli.ts",
|
|
1415
|
+
description: "Collapse a backlogrun merge event into one call: confirm the child, do the post-merge steps, offer the next child",
|
|
1416
|
+
category: "AI tools",
|
|
1417
|
+
reference: [ISSUE_WITH_OPTIONS2, "automation", ["git", "network"]]
|
|
1418
|
+
},
|
|
1419
|
+
"run:review": {
|
|
1420
|
+
script: "scripts/run/review/run-review-cli.ts",
|
|
1421
|
+
description: "Start the gate in the background and print the /code-review brief in one call so the two overlap (--join to join the gate and check its verdict)",
|
|
1422
|
+
category: "AI tools",
|
|
1423
|
+
reference: ["[--join]", "automation", ["processes", "files"]]
|
|
1424
|
+
},
|
|
1425
|
+
// The post-merge sequence a run closed on, folded into one call: commit the
|
|
1426
|
+
// observation ledger, read the completion citations and decide the release scope. `observations:flush`,
|
|
1427
|
+
// `issue:cite` and `release:scope` were three round trips re-billing the run's full context each.
|
|
1428
|
+
"run:tail": {
|
|
1429
|
+
script: "scripts/run/run-tail-cli.ts",
|
|
1430
|
+
description: "Close a run in one call: return to the default branch, commit the observation ledger, read the citations, decide the release scope",
|
|
1431
|
+
category: "AI tools",
|
|
1432
|
+
reference: ["[<issue> ...]", "automation", ["git", "network"]]
|
|
1433
|
+
},
|
|
1434
|
+
// The commit-to-report region a run ships a change on, folded into one call:
|
|
1435
|
+
// the gate, the commit/push/PR (`git -y`), the CI-wait merge (`followup`) and the report bookkeeping
|
|
1436
|
+
// (`run:tail`) were four round trips re-billing the run's full context each. It stops at the first
|
|
1437
|
+
// failed step and names it, so the run reads only the step to fix.
|
|
1438
|
+
ship: {
|
|
1439
|
+
script: "scripts/run/ship/run-ship-cli.ts",
|
|
1440
|
+
description: "Ship a change in one call: gate, commit/push/PR, the CI-wait merge and the report bookkeeping, stopping at the first failed step",
|
|
1441
|
+
category: "AI tools",
|
|
1442
|
+
reference: [
|
|
1443
|
+
'"<title> #<N>" [<follow-up-N> ...] [--cite <N>] [--review] [--detach] [--body-file <path>] [--notify-message <text> | --notify-message-file <path>] | --log <N>',
|
|
1444
|
+
"automation",
|
|
1445
|
+
["git", "network"]
|
|
1446
|
+
]
|
|
1447
|
+
},
|
|
1448
|
+
"run:event": {
|
|
1449
|
+
script: "scripts/run/event/run-event-cli.ts",
|
|
1450
|
+
description: "Append to, read or watch the run\u2019s append-only event stream (--append <kind> <text> | --from|--follow <n> | --watch [<n>] | --last)",
|
|
1451
|
+
category: "AI tools",
|
|
1452
|
+
reference: [
|
|
1453
|
+
"--append <kind> <text> | --from|--follow <n> | --watch [<n>] | --last",
|
|
1454
|
+
"automation",
|
|
1455
|
+
["files"]
|
|
1456
|
+
]
|
|
1457
|
+
},
|
|
1458
|
+
"run:report": {
|
|
1459
|
+
script: "scripts/run/run-report-cli.ts",
|
|
1460
|
+
description: "Generate the session-facing report for this invocation from the run\u2019s event stream (merges, parks, cuts) with the release tail \u2014 the same text josh notify sends",
|
|
1461
|
+
category: "AI tools",
|
|
1462
|
+
reference: ["", "automation", ["files", "network"]]
|
|
1463
|
+
},
|
|
1464
|
+
...LANE_COMMANDS,
|
|
1465
|
+
...GUARD_COMMANDS,
|
|
1466
|
+
"run:watcher:guard": {
|
|
1467
|
+
script: "scripts/run/run-watcher-guard-cli.ts",
|
|
1468
|
+
description: "Guard: exits non-zero when lane children are in-flight but the watcher has not pinged recently",
|
|
1469
|
+
category: "AI tools",
|
|
1470
|
+
reference: ["", "automation", ["none"]]
|
|
1471
|
+
},
|
|
1472
|
+
"run:stranded": {
|
|
1473
|
+
script: "scripts/run/run-stranded-cli.ts",
|
|
1474
|
+
// `.env` rather than the ambient environment: the strand notification needs the Telegram
|
|
1475
|
+
// credentials, the same reason `run:wake` carries these flags.
|
|
1476
|
+
tsx_arguments: OPTIONAL_ENV_FILE_FLAGS,
|
|
1477
|
+
description: "Report whether the run is stranded \u2014 owner gone, no successor holding the budget, and no supervisor watching",
|
|
1478
|
+
category: "AI tools",
|
|
1479
|
+
reference: ["", "automation", ["processes", "notifications"]]
|
|
1480
|
+
},
|
|
1481
|
+
...SPLIT_COMMANDS,
|
|
1482
|
+
"oracle:list": {
|
|
1483
|
+
script: "scripts/rules/oracle-list-cli.ts",
|
|
1484
|
+
description: "Print the decision oracles \u2014 commands that answer a rule question mechanically",
|
|
1485
|
+
category: "AI tools",
|
|
1486
|
+
reference: ["", "automation", ["none"]]
|
|
1487
|
+
},
|
|
1488
|
+
eval: {
|
|
1489
|
+
script: "scripts/eval/eval-run.ts",
|
|
1490
|
+
description: "Run the agent rule-compliance scenarios (real Claude sessions)",
|
|
1491
|
+
category: "AI tools",
|
|
1492
|
+
reference: ["[scenario...]", "maintainer", ["processes", "network"]],
|
|
1493
|
+
// Kit-only: it replays kit's own distributed rules against real Claude sessions, so it means
|
|
1494
|
+
// nothing in a consumer project and is dropped from a consumer's help.
|
|
1495
|
+
is_kit_only: true
|
|
1496
|
+
}
|
|
1497
|
+
};
|
|
1498
|
+
|
|
1499
|
+
// scripts/josh/josh-commands-clone.ts
|
|
1500
|
+
var CLONE_COMMANDS = {
|
|
1501
|
+
"clone:scan": {
|
|
1502
|
+
script: "scripts/clone/clone-scan-cli.ts",
|
|
1503
|
+
description: "Count code duplication across files and first-party repositories, printing each clone as file:line pairs",
|
|
1504
|
+
category: "AI tools",
|
|
1505
|
+
reference: ["", "automation", ["files"]]
|
|
1305
1506
|
}
|
|
1306
|
-
}
|
|
1307
|
-
async function with_command_reservation(weight, run, options = {}) {
|
|
1308
|
-
if (is_held() || unit_worker_share.is_nested_run()) return await run();
|
|
1309
|
-
return await with_core_reservation(weight, async () => await with_held_mark(run), options);
|
|
1310
|
-
}
|
|
1311
|
-
var core_budget = {
|
|
1312
|
-
CORE_WEIGHTS,
|
|
1313
|
-
HELD_KEY,
|
|
1314
|
-
HELD_VALUE,
|
|
1315
|
-
MEMORY_MB,
|
|
1316
|
-
RESERVED_PREFIX,
|
|
1317
|
-
admitted_keys: core_admission.admitted_keys,
|
|
1318
|
-
admitted_load: core_admission.admitted_load,
|
|
1319
|
-
describe_overflow: core_admission.describe_overflow,
|
|
1320
|
-
is_held,
|
|
1321
|
-
live_reservations,
|
|
1322
|
-
release,
|
|
1323
|
-
reserve,
|
|
1324
|
-
with_command_reservation,
|
|
1325
|
-
with_core_reservation,
|
|
1326
|
-
with_held_mark
|
|
1327
1507
|
};
|
|
1328
1508
|
|
|
1329
1509
|
// scripts/gate/gate-plan.ts
|
|
@@ -1389,6 +1569,7 @@ var TYPE_CHECK_LABEL = "check";
|
|
|
1389
1569
|
var UNIT_LABEL = "test:unit";
|
|
1390
1570
|
var BEHAVIOR_LABEL = "behavior";
|
|
1391
1571
|
var METRICS_LABEL = "metrics";
|
|
1572
|
+
var EXPORTS_LABEL = "exports";
|
|
1392
1573
|
var WARNING_CHECKER_LABELS = [LINT_LABEL, TYPE_CHECK_LABEL];
|
|
1393
1574
|
var STATIC_CHECKS = [
|
|
1394
1575
|
{
|
|
@@ -1414,7 +1595,7 @@ var STATIC_CHECKS = [
|
|
|
1414
1595
|
// program and walks it for about 5s, and it is over well before lint and the type check. A
|
|
1415
1596
|
// reservation would only take a place from the four-core CI runner's fan-out and a worker from the
|
|
1416
1597
|
// unit suite for the whole gate.
|
|
1417
|
-
{ label:
|
|
1598
|
+
{ label: EXPORTS_LABEL, target: "exports:unused", reserved_cores: 0 },
|
|
1418
1599
|
// **The metrics ratchet is kit's own**: it holds kit's repository-wide
|
|
1419
1600
|
// totals to the merge-base's. It is one in-process eslint pass with only `max-lines` enabled, over
|
|
1420
1601
|
// in a few seconds, so it reserves no core for the reason the unused-member check above does not.
|
|
@@ -1503,6 +1684,15 @@ function resolve_gate_plan(available_cores = availableParallelism4(), concurrent
|
|
|
1503
1684
|
unit_worker_cap: unit_cap_for(checks, available_cores, concurrent_runs)
|
|
1504
1685
|
};
|
|
1505
1686
|
}
|
|
1687
|
+
function bound_by_free(plan, available_cores, free_cores) {
|
|
1688
|
+
if (!has_unit_check(plan.checks)) return plan;
|
|
1689
|
+
const unit_worker_cap = unit_worker_share.cap_to_free(
|
|
1690
|
+
plan.unit_worker_cap,
|
|
1691
|
+
free_cores,
|
|
1692
|
+
available_cores
|
|
1693
|
+
);
|
|
1694
|
+
return { ...plan, unit_worker_cap };
|
|
1695
|
+
}
|
|
1506
1696
|
function format_machine(available_cores, concurrent_runs) {
|
|
1507
1697
|
const cores = `${String(available_cores)} cores`;
|
|
1508
1698
|
if (concurrent_runs <= unit_worker_share.SOLO_RUNS) return cores;
|
|
@@ -1521,6 +1711,7 @@ function format_gate_plan(plan, available_cores = availableParallelism4(), concu
|
|
|
1521
1711
|
return `${josh_verdict.GATE_OPENING_PREFIX}${width}, ${cap} (${machine})`;
|
|
1522
1712
|
}
|
|
1523
1713
|
var gate_plan = {
|
|
1714
|
+
EXPORTS_LABEL,
|
|
1524
1715
|
GATE_CHECKS,
|
|
1525
1716
|
LINT_LABEL,
|
|
1526
1717
|
MEASURED_CORES,
|
|
@@ -1530,6 +1721,7 @@ var gate_plan = {
|
|
|
1530
1721
|
TYPE_CHECK_LABEL,
|
|
1531
1722
|
UNIT_LABEL,
|
|
1532
1723
|
WARNING_CHECKER_LABELS,
|
|
1724
|
+
bound_by_free,
|
|
1533
1725
|
check_memory,
|
|
1534
1726
|
check_weight,
|
|
1535
1727
|
direct_unit_memory,
|
|
@@ -1662,7 +1854,7 @@ var DEV_COMMANDS = {
|
|
|
1662
1854
|
},
|
|
1663
1855
|
"stop:guard": {
|
|
1664
1856
|
script: "scripts/hooks/stop-guard.ts",
|
|
1665
|
-
description: "Claude Code Stop hook:
|
|
1857
|
+
description: "Claude Code Stop hook: send the hold notify, release the hold and print the issue citations itself; block on the rules that need the model (reads the Stop payload on stdin)",
|
|
1666
1858
|
category: "Development",
|
|
1667
1859
|
reference: ["", "automation", ["none"]]
|
|
1668
1860
|
// **No `tsx_arguments`, deliberately**, the same as `pretool:guard` above: this runs at every
|
|
@@ -1796,6 +1988,12 @@ var HOOKS_COMMANDS = {
|
|
|
1796
1988
|
category: "Git hooks",
|
|
1797
1989
|
reference: ["[paths...]", "automation", ["none"]]
|
|
1798
1990
|
},
|
|
1991
|
+
"pre-commit-lint": {
|
|
1992
|
+
script: "scripts/hooks/pre-commit-lint.ts",
|
|
1993
|
+
description: "Git hook: run cspell, prettier or eslint over staged files from a content cache",
|
|
1994
|
+
category: "Git hooks",
|
|
1995
|
+
reference: ["<cspell|prettier|eslint> [paths...]", "automation", ["files"]]
|
|
1996
|
+
},
|
|
1799
1997
|
"pre-push-unit": {
|
|
1800
1998
|
script: "scripts/hooks/pre-push-unit.ts",
|
|
1801
1999
|
description: "Git hook: run unit tests, reusing a green gate recorded on the pushed tree",
|
|
@@ -2201,6 +2399,11 @@ var WORKFLOW_COMMANDS = {
|
|
|
2201
2399
|
// A script rather than an `sh -c` chain, because the strategy has to be named rather than left to
|
|
2202
2400
|
// the caller's git configuration: a bare `git pull` aborts with `Need to specify how to reconcile
|
|
2203
2401
|
// divergent branches` on exactly the diverged branch the command exists for.
|
|
2402
|
+
//
|
|
2403
|
+
// **It declares no core weight, by measurement** (joshuafolkken/kit#3944): its own work is git, and
|
|
2404
|
+
// the catalog it may regenerate took 0.6s of CPU. The heavy processes seen beside it are commit
|
|
2405
|
+
// hooks — its regenerated catalog's commit runs them — which reserve themselves, and a weight here
|
|
2406
|
+
// would set the held mark they inherit, folding the parallel hook tools into this one place.
|
|
2204
2407
|
"main:merge": {
|
|
2205
2408
|
script: "scripts/git/main-merge.ts",
|
|
2206
2409
|
description: "Merge origin default branch into the current branch",
|
|
@@ -2267,6 +2470,7 @@ var ALIASES = {
|
|
|
2267
2470
|
tm: "time",
|
|
2268
2471
|
tmd: "time:density",
|
|
2269
2472
|
rtr: "retrospective",
|
|
2473
|
+
ca: "cost:audit",
|
|
2270
2474
|
rvf: "review:findings",
|
|
2271
2475
|
ruv: "rule:value",
|
|
2272
2476
|
ev: "eval",
|
|
@@ -2276,6 +2480,7 @@ var ALIASES = {
|
|
|
2276
2480
|
lst: "lane:stats",
|
|
2277
2481
|
lli: "lane:limit",
|
|
2278
2482
|
lrs: "lane:restart",
|
|
2483
|
+
lwd: "lane:withdraw",
|
|
2279
2484
|
blr: "backlogrun"
|
|
2280
2485
|
};
|
|
2281
2486
|
function canonical_command(name) {
|
|
@@ -2288,7 +2493,7 @@ var BASH_SEPARATOR = ": ";
|
|
|
2288
2493
|
var WHITESPACE_PATTERN = /\s+/u;
|
|
2289
2494
|
var SEGMENT_PATTERN = /&&|\|\||;|\|/u;
|
|
2290
2495
|
var PIPE_PATTERN = /(?<!\|)\|(?!\|)/u;
|
|
2291
|
-
var CHAIN_PATTERN =
|
|
2496
|
+
var CHAIN_PATTERN = /&&|\|\||;|\n/u;
|
|
2292
2497
|
var QUOTED_SPAN_PATTERN = /\\[\s\S]|'[^']*'|"(?:\\[\s\S]|[^"\\])*"/gu;
|
|
2293
2498
|
var HEREDOC_MARKER = /(?<!<)<<-?\s*(['"]?)(\w+)\1/u;
|
|
2294
2499
|
var HEREDOC_DELIMITER_GROUP = 2;
|
|
@@ -2386,13 +2591,24 @@ function drop_redirections(words) {
|
|
|
2386
2591
|
}
|
|
2387
2592
|
return kept;
|
|
2388
2593
|
}
|
|
2389
|
-
function
|
|
2390
|
-
const text = command_words(
|
|
2594
|
+
function josh_arguments_of_segment(segment) {
|
|
2595
|
+
const text = command_words(segment).join(" ");
|
|
2391
2596
|
const match = JOSH_PATTERN.exec(text);
|
|
2392
2597
|
if (match === null) return [];
|
|
2393
2598
|
const words = text.slice(match[0].length).split(WHITESPACE_PATTERN).filter((word) => word !== "");
|
|
2394
2599
|
return drop_redirections(words);
|
|
2395
2600
|
}
|
|
2601
|
+
function josh_arguments(command) {
|
|
2602
|
+
return josh_arguments_of_segment(command_segment(command));
|
|
2603
|
+
}
|
|
2604
|
+
function josh_signature_of_segment(segment) {
|
|
2605
|
+
const name = josh_command_of_segment(segment);
|
|
2606
|
+
if (name === "") return "";
|
|
2607
|
+
return [name, ...josh_arguments_of_segment(segment)].join(" ");
|
|
2608
|
+
}
|
|
2609
|
+
function josh_signatures_of(command) {
|
|
2610
|
+
return unquoted(command).split(SEGMENT_PATTERN).map((segment) => josh_signature_of_segment(segment)).filter((signature) => signature !== "");
|
|
2611
|
+
}
|
|
2396
2612
|
var time_shell = {
|
|
2397
2613
|
JOSH_PREFIX,
|
|
2398
2614
|
bash_command,
|
|
@@ -2403,6 +2619,7 @@ var time_shell = {
|
|
|
2403
2619
|
josh_arguments,
|
|
2404
2620
|
josh_command_of,
|
|
2405
2621
|
josh_commands_of,
|
|
2622
|
+
josh_signatures_of,
|
|
2406
2623
|
leading_word,
|
|
2407
2624
|
unquoted
|
|
2408
2625
|
};
|
|
@@ -2503,78 +2720,6 @@ var time_background = {
|
|
|
2503
2720
|
unread_phases
|
|
2504
2721
|
};
|
|
2505
2722
|
|
|
2506
|
-
// scripts/git/git-schemas.ts
|
|
2507
|
-
import { z as z3 } from "zod";
|
|
2508
|
-
var package_name_schema = z3.object({ name: z3.string().min(1) });
|
|
2509
|
-
var rollup_item_schema = z3.looseObject({
|
|
2510
|
-
// eslint-disable-next-line @typescript-eslint/naming-convention -- GraphQL field name from GitHub API
|
|
2511
|
-
__typename: z3.string().optional(),
|
|
2512
|
-
state: z3.string().optional(),
|
|
2513
|
-
status: z3.string().optional(),
|
|
2514
|
-
conclusion: z3.string().optional(),
|
|
2515
|
-
name: z3.string().optional(),
|
|
2516
|
-
context: z3.string().optional(),
|
|
2517
|
-
description: z3.string().nullish()
|
|
2518
|
-
});
|
|
2519
|
-
var pr_raw_schema = z3.object({
|
|
2520
|
-
statusCheckRollup: z3.array(rollup_item_schema).optional(),
|
|
2521
|
-
mergeStateStatus: z3.string().optional(),
|
|
2522
|
-
reviewDecision: z3.string().optional()
|
|
2523
|
-
});
|
|
2524
|
-
var pull_comment_schema = z3.object({
|
|
2525
|
-
body: z3.string().optional(),
|
|
2526
|
-
html_url: z3.string().optional(),
|
|
2527
|
-
user: z3.object({ login: z3.string().optional() }).optional()
|
|
2528
|
-
});
|
|
2529
|
-
var ai_review_pull_comment_schema = z3.object({
|
|
2530
|
-
body: z3.string().optional(),
|
|
2531
|
-
url: z3.string().optional(),
|
|
2532
|
-
author: z3.object({ login: z3.string().optional() }).optional()
|
|
2533
|
-
});
|
|
2534
|
-
var pr_info_schema = z3.object({
|
|
2535
|
-
mergeable: z3.union([z3.boolean(), z3.string()]).nullable().optional(),
|
|
2536
|
-
mergeStateStatus: z3.string().nullable().optional(),
|
|
2537
|
-
state: z3.string().optional()
|
|
2538
|
-
});
|
|
2539
|
-
var epic_issue_schema = z3.object({
|
|
2540
|
-
number: z3.number(),
|
|
2541
|
-
body: z3.string().optional()
|
|
2542
|
-
});
|
|
2543
|
-
var blocking_issue_schema = z3.object({
|
|
2544
|
-
number: z3.number(),
|
|
2545
|
-
state: z3.string().optional(),
|
|
2546
|
-
repository_url: z3.string().optional()
|
|
2547
|
-
});
|
|
2548
|
-
var blocked_by_schema = z3.object({
|
|
2549
|
-
nodes: z3.array(blocking_issue_schema).default([]),
|
|
2550
|
-
totalCount: z3.number().optional()
|
|
2551
|
-
}).optional();
|
|
2552
|
-
var epic_child_schema = z3.object({
|
|
2553
|
-
state: z3.string().optional(),
|
|
2554
|
-
blockedBy: blocked_by_schema
|
|
2555
|
-
});
|
|
2556
|
-
var rest_comment_schema = z3.looseObject({
|
|
2557
|
-
body: z3.string().nullish(),
|
|
2558
|
-
html_url: z3.string().optional(),
|
|
2559
|
-
user: z3.looseObject({ login: z3.string().optional() }).nullish()
|
|
2560
|
-
});
|
|
2561
|
-
var issue_label_schema = z3.object({ name: z3.string() });
|
|
2562
|
-
var epic_subject_schema = z3.object({
|
|
2563
|
-
number: z3.number(),
|
|
2564
|
-
state: z3.string().optional(),
|
|
2565
|
-
labels: z3.array(issue_label_schema).optional(),
|
|
2566
|
-
body: z3.string().optional()
|
|
2567
|
-
});
|
|
2568
|
-
var open_issue_schema = z3.object({
|
|
2569
|
-
number: z3.number(),
|
|
2570
|
-
title: z3.string(),
|
|
2571
|
-
labels: z3.array(issue_label_schema).optional(),
|
|
2572
|
-
createdAt: z3.string(),
|
|
2573
|
-
blockedBy: blocked_by_schema,
|
|
2574
|
-
// Present only on the pickup listing, which reads it for the paths each issue declares.
|
|
2575
|
-
body: z3.string().optional()
|
|
2576
|
-
});
|
|
2577
|
-
|
|
2578
2723
|
// scripts/gh/git-pr-checks-parse.ts
|
|
2579
2724
|
var CHECK_STATUS_PASS = "pass";
|
|
2580
2725
|
var CHECK_STATUS_PENDING = "pending";
|
|
@@ -2753,7 +2898,7 @@ var PrConflictError = class extends Error {
|
|
|
2753
2898
|
|
|
2754
2899
|
// scripts/lane/lane-ledger.ts
|
|
2755
2900
|
import { closeSync, constants, openSync, writeSync } from "node:fs";
|
|
2756
|
-
import { z as
|
|
2901
|
+
import { z as z3 } from "zod";
|
|
2757
2902
|
var LEDGER_PREFIX = "josh-lane-ledger-";
|
|
2758
2903
|
var LEDGER_SUFFIX = ".jsonl";
|
|
2759
2904
|
var LINE_SEPARATOR = "\n";
|
|
@@ -2767,63 +2912,83 @@ var KIND = {
|
|
|
2767
2912
|
LOAD: "load",
|
|
2768
2913
|
DISPATCH: "dispatch",
|
|
2769
2914
|
STAGE: "stage",
|
|
2770
|
-
STOP: "stop"
|
|
2915
|
+
STOP: "stop",
|
|
2916
|
+
OVERLAP: "overlap"
|
|
2771
2917
|
};
|
|
2772
|
-
var merge_schema =
|
|
2773
|
-
var
|
|
2774
|
-
|
|
2775
|
-
|
|
2776
|
-
|
|
2777
|
-
|
|
2918
|
+
var merge_schema = z3.object({ kind: z3.literal(KIND.MERGE), at: z3.string(), issue: z3.number() });
|
|
2919
|
+
var check_schema = z3.object({
|
|
2920
|
+
label: z3.string(),
|
|
2921
|
+
elapsed_ms: z3.number(),
|
|
2922
|
+
is_passed: z3.boolean(),
|
|
2923
|
+
is_reused: z3.boolean().optional()
|
|
2924
|
+
});
|
|
2925
|
+
var gate_schema = z3.object({
|
|
2926
|
+
kind: z3.literal(KIND.GATE),
|
|
2927
|
+
at: z3.string(),
|
|
2928
|
+
elapsed_ms: z3.number(),
|
|
2929
|
+
is_passed: z3.boolean(),
|
|
2778
2930
|
// The unit suite's own duration, present when this gate ran it — the one
|
|
2779
2931
|
// record of a full vitest run `josh metrics` reads, so the suite is never timed twice.
|
|
2780
|
-
unit_ms:
|
|
2932
|
+
unit_ms: z3.number().optional(),
|
|
2781
2933
|
// The whole cores busy outside this gate, beyond the machine's baseline, at its start or its end —
|
|
2782
2934
|
// whichever was higher. Absent when the machine could not be read.
|
|
2783
|
-
external_cores:
|
|
2935
|
+
external_cores: z3.number().optional(),
|
|
2936
|
+
// Each check's own duration and verdict, so `josh lane:stats` can name the check a slow gate waited
|
|
2937
|
+
// on. A reused check started no process: its row says so, and its zero is no timing.
|
|
2938
|
+
checks: z3.array(check_schema).optional()
|
|
2784
2939
|
});
|
|
2785
|
-
var process_kind_schema =
|
|
2786
|
-
kind:
|
|
2787
|
-
cpu_percent:
|
|
2788
|
-
rss_mb:
|
|
2789
|
-
count:
|
|
2940
|
+
var process_kind_schema = z3.object({
|
|
2941
|
+
kind: z3.string(),
|
|
2942
|
+
cpu_percent: z3.number(),
|
|
2943
|
+
rss_mb: z3.number(),
|
|
2944
|
+
count: z3.number()
|
|
2790
2945
|
});
|
|
2791
|
-
var load_schema =
|
|
2792
|
-
kind:
|
|
2793
|
-
at:
|
|
2794
|
-
load:
|
|
2795
|
-
available_mb:
|
|
2796
|
-
swapped_mb:
|
|
2797
|
-
lanes:
|
|
2946
|
+
var load_schema = z3.object({
|
|
2947
|
+
kind: z3.literal(KIND.LOAD),
|
|
2948
|
+
at: z3.string(),
|
|
2949
|
+
load: z3.number(),
|
|
2950
|
+
available_mb: z3.number().optional(),
|
|
2951
|
+
swapped_mb: z3.number().optional(),
|
|
2952
|
+
lanes: z3.number().optional(),
|
|
2798
2953
|
// The heaviest process kinds at the instant of the sample (`lane-process-load.ts`), so the load the
|
|
2799
2954
|
// core budget's ledger does not explain can be read back by what made it (joshuafolkken/kit#3874).
|
|
2800
|
-
processes:
|
|
2955
|
+
processes: z3.array(process_kind_schema).optional()
|
|
2801
2956
|
});
|
|
2802
|
-
var dispatch_schema =
|
|
2803
|
-
kind:
|
|
2804
|
-
at:
|
|
2805
|
-
issue:
|
|
2957
|
+
var dispatch_schema = z3.object({
|
|
2958
|
+
kind: z3.literal(KIND.DISPATCH),
|
|
2959
|
+
at: z3.string(),
|
|
2960
|
+
issue: z3.number()
|
|
2806
2961
|
});
|
|
2807
|
-
var stage_schema =
|
|
2808
|
-
kind:
|
|
2809
|
-
at:
|
|
2810
|
-
stage:
|
|
2811
|
-
elapsed_ms:
|
|
2812
|
-
issue:
|
|
2962
|
+
var stage_schema = z3.object({
|
|
2963
|
+
kind: z3.literal(KIND.STAGE),
|
|
2964
|
+
at: z3.string(),
|
|
2965
|
+
stage: z3.string(),
|
|
2966
|
+
elapsed_ms: z3.number(),
|
|
2967
|
+
issue: z3.number().optional()
|
|
2813
2968
|
});
|
|
2814
|
-
var stop_schema =
|
|
2815
|
-
kind:
|
|
2816
|
-
at:
|
|
2817
|
-
issue:
|
|
2818
|
-
reason:
|
|
2969
|
+
var stop_schema = z3.object({
|
|
2970
|
+
kind: z3.literal(KIND.STOP),
|
|
2971
|
+
at: z3.string(),
|
|
2972
|
+
issue: z3.number(),
|
|
2973
|
+
reason: z3.string()
|
|
2819
2974
|
});
|
|
2820
|
-
var
|
|
2975
|
+
var overlap_schema = z3.object({
|
|
2976
|
+
kind: z3.literal(KIND.OVERLAP),
|
|
2977
|
+
at: z3.string(),
|
|
2978
|
+
issue: z3.number(),
|
|
2979
|
+
lanes: z3.array(z3.number()),
|
|
2980
|
+
paths: z3.array(z3.string()),
|
|
2981
|
+
waited_ms: z3.number(),
|
|
2982
|
+
is_cleared: z3.boolean()
|
|
2983
|
+
});
|
|
2984
|
+
var entry_schema = z3.discriminatedUnion("kind", [
|
|
2821
2985
|
merge_schema,
|
|
2822
2986
|
gate_schema,
|
|
2823
2987
|
load_schema,
|
|
2824
2988
|
dispatch_schema,
|
|
2825
2989
|
stage_schema,
|
|
2826
|
-
stop_schema
|
|
2990
|
+
stop_schema,
|
|
2991
|
+
overlap_schema
|
|
2827
2992
|
]);
|
|
2828
2993
|
function target_of(repository) {
|
|
2829
2994
|
return stamp_file.stamp_path(LEDGER_PREFIX, repository, LEDGER_SUFFIX);
|
|
@@ -2837,19 +3002,19 @@ async function target() {
|
|
|
2837
3002
|
return void 0;
|
|
2838
3003
|
}
|
|
2839
3004
|
}
|
|
2840
|
-
function append(
|
|
2841
|
-
const descriptor = openSync(
|
|
3005
|
+
function append(path5, entry) {
|
|
3006
|
+
const descriptor = openSync(path5, APPEND_FLAGS, LEDGER_FILE_MODE);
|
|
2842
3007
|
try {
|
|
2843
|
-
if (stamp_file.is_own_regular_file(
|
|
3008
|
+
if (stamp_file.is_own_regular_file(path5)) {
|
|
2844
3009
|
writeSync(descriptor, `${JSON.stringify(entry)}${LINE_SEPARATOR}`);
|
|
2845
3010
|
}
|
|
2846
3011
|
} finally {
|
|
2847
3012
|
closeSync(descriptor);
|
|
2848
3013
|
}
|
|
2849
3014
|
}
|
|
2850
|
-
async function record(entry,
|
|
3015
|
+
async function record(entry, path5) {
|
|
2851
3016
|
try {
|
|
2852
|
-
const destination =
|
|
3017
|
+
const destination = path5 ?? await target();
|
|
2853
3018
|
if (destination !== void 0) append(destination, entry);
|
|
2854
3019
|
} catch {
|
|
2855
3020
|
}
|
|
@@ -2857,20 +3022,23 @@ async function record(entry, path4) {
|
|
|
2857
3022
|
function now_iso() {
|
|
2858
3023
|
return (/* @__PURE__ */ new Date()).toISOString();
|
|
2859
3024
|
}
|
|
2860
|
-
async function record_gate(
|
|
2861
|
-
await record({ kind: KIND.GATE, at: now_iso(), ...gate },
|
|
3025
|
+
async function record_gate(path5, gate) {
|
|
3026
|
+
await record({ kind: KIND.GATE, at: now_iso(), ...gate }, path5);
|
|
2862
3027
|
}
|
|
2863
3028
|
async function record_merge(issue) {
|
|
2864
3029
|
await record({ kind: KIND.MERGE, at: now_iso(), issue });
|
|
2865
3030
|
}
|
|
2866
|
-
async function record_dispatch(issue,
|
|
2867
|
-
await record({ kind: KIND.DISPATCH, at: now_iso(), issue },
|
|
3031
|
+
async function record_dispatch(issue, path5) {
|
|
3032
|
+
await record({ kind: KIND.DISPATCH, at: now_iso(), issue }, path5);
|
|
3033
|
+
}
|
|
3034
|
+
async function record_stage(stage, path5) {
|
|
3035
|
+
await record({ kind: KIND.STAGE, at: now_iso(), ...stage }, path5);
|
|
2868
3036
|
}
|
|
2869
|
-
async function
|
|
2870
|
-
await record({ kind: KIND.
|
|
3037
|
+
async function record_stop(stop, path5) {
|
|
3038
|
+
await record({ kind: KIND.STOP, at: now_iso(), ...stop }, path5);
|
|
2871
3039
|
}
|
|
2872
|
-
async function
|
|
2873
|
-
await record({ kind: KIND.
|
|
3040
|
+
async function record_overlap(overlap, path5) {
|
|
3041
|
+
await record({ kind: KIND.OVERLAP, at: now_iso(), ...overlap }, path5);
|
|
2874
3042
|
}
|
|
2875
3043
|
function parse_line2(line) {
|
|
2876
3044
|
return entry_schema.safeParse(json_value.parse_or_undefined(line)).data;
|
|
@@ -2878,8 +3046,8 @@ function parse_line2(line) {
|
|
|
2878
3046
|
function is_present(entry) {
|
|
2879
3047
|
return entry !== void 0;
|
|
2880
3048
|
}
|
|
2881
|
-
function read_entries(
|
|
2882
|
-
const raw = stamp_file.read_stamp_text(
|
|
3049
|
+
function read_entries(path5) {
|
|
3050
|
+
const raw = stamp_file.read_stamp_text(path5);
|
|
2883
3051
|
if (raw === void 0) return [];
|
|
2884
3052
|
return raw.split(LINE_SEPARATOR).map((line) => parse_line2(line.trim())).filter(is_present);
|
|
2885
3053
|
}
|
|
@@ -2892,6 +3060,7 @@ var lane_ledger = {
|
|
|
2892
3060
|
record_dispatch,
|
|
2893
3061
|
record_gate,
|
|
2894
3062
|
record_merge,
|
|
3063
|
+
record_overlap,
|
|
2895
3064
|
record_stage,
|
|
2896
3065
|
record_stop,
|
|
2897
3066
|
target,
|
|
@@ -3058,9 +3227,9 @@ function read_stages(text) {
|
|
|
3058
3227
|
var time_followup_stage = { NO_STAGES, read_stages };
|
|
3059
3228
|
|
|
3060
3229
|
// scripts/time-runtime/time-reported-failure.ts
|
|
3061
|
-
import { z as
|
|
3062
|
-
var TEXT_BLOCK_SCHEMA =
|
|
3063
|
-
var CONTENT_SCHEMA =
|
|
3230
|
+
import { z as z4 } from "zod";
|
|
3231
|
+
var TEXT_BLOCK_SCHEMA = z4.object({ text: z4.string().nullish() });
|
|
3232
|
+
var CONTENT_SCHEMA = z4.union([z4.string(), z4.array(z4.unknown())]);
|
|
3064
3233
|
var LINE_SEPARATOR3 = "\n";
|
|
3065
3234
|
function block_text(block) {
|
|
3066
3235
|
const parsed = TEXT_BLOCK_SCHEMA.safeParse(block);
|
|
@@ -3597,38 +3766,38 @@ function check_key(josh_command, command) {
|
|
|
3597
3766
|
var time_single_check = { NO_CHECK, check_key };
|
|
3598
3767
|
|
|
3599
3768
|
// scripts/time-runtime/time-transcript-line.ts
|
|
3600
|
-
import { z as
|
|
3601
|
-
var BLOCK_SCHEMA2 =
|
|
3602
|
-
type:
|
|
3603
|
-
name:
|
|
3604
|
-
id:
|
|
3605
|
-
tool_use_id:
|
|
3606
|
-
input:
|
|
3769
|
+
import { z as z5 } from "zod";
|
|
3770
|
+
var BLOCK_SCHEMA2 = z5.object({
|
|
3771
|
+
type: z5.string().nullish(),
|
|
3772
|
+
name: z5.string().nullish(),
|
|
3773
|
+
id: z5.string().nullish(),
|
|
3774
|
+
tool_use_id: z5.string().nullish(),
|
|
3775
|
+
input: z5.unknown().nullish(),
|
|
3607
3776
|
// Whether the harness wrote this result back as a failure. Present on a
|
|
3608
3777
|
// `tool_result` block and on nothing else, and absent even there for the tools that never report
|
|
3609
3778
|
// one — which is why it is read as three answers rather than as a boolean with a default.
|
|
3610
|
-
is_error:
|
|
3779
|
+
is_error: z5.boolean().nullish(),
|
|
3611
3780
|
// What the call printed. Read only to recover the outcome a pipeline threw away:
|
|
3612
3781
|
// it is kept as flattened text rather than as blocks, because that is
|
|
3613
3782
|
// the whole of what `time-reported-failure.ts` asks of it.
|
|
3614
|
-
content:
|
|
3783
|
+
content: z5.unknown().nullish()
|
|
3615
3784
|
});
|
|
3616
|
-
var CONTENT_SCHEMA2 =
|
|
3617
|
-
var MESSAGE_SCHEMA2 =
|
|
3618
|
-
var LINE_SCHEMA =
|
|
3619
|
-
type:
|
|
3620
|
-
timestamp:
|
|
3785
|
+
var CONTENT_SCHEMA2 = z5.union([z5.string(), z5.array(BLOCK_SCHEMA2)]);
|
|
3786
|
+
var MESSAGE_SCHEMA2 = z5.object({ id: z5.string().nullish(), content: CONTENT_SCHEMA2.nullish() });
|
|
3787
|
+
var LINE_SCHEMA = z5.object({
|
|
3788
|
+
type: z5.string().nullish(),
|
|
3789
|
+
timestamp: z5.string().nullish(),
|
|
3621
3790
|
// The branch the line was written on, which is where the issue number lives — `josh git` names a
|
|
3622
3791
|
// branch `<N>-<slug>`. Read here rather than re-parsed by a second reader so `josh time --issue`
|
|
3623
3792
|
// and `josh cost --issue` answer from the same field.
|
|
3624
|
-
gitBranch:
|
|
3793
|
+
gitBranch: z5.string().nullish(),
|
|
3625
3794
|
// The two fields outside `message` that the harness writes an end-of-task notice on.
|
|
3626
3795
|
// **Both are read as `unknown` rather than as strings**: a schema
|
|
3627
3796
|
// insisting on a string here would reject every line whose `content` is a list of blocks — which is
|
|
3628
3797
|
// most of them — and a rejected line is dropped from the timeline entirely, so the shares would stop
|
|
3629
3798
|
// reconstructing the elapsed time. The shape is checked at the read below instead.
|
|
3630
|
-
content:
|
|
3631
|
-
attachment:
|
|
3799
|
+
content: z5.unknown().nullish(),
|
|
3800
|
+
attachment: z5.unknown().nullish(),
|
|
3632
3801
|
message: MESSAGE_SCHEMA2.nullish()
|
|
3633
3802
|
});
|
|
3634
3803
|
var UNKNOWN_BRANCH = "";
|
|
@@ -3739,6 +3908,7 @@ var NO_CALL = {
|
|
|
3739
3908
|
label: "",
|
|
3740
3909
|
josh_command: "",
|
|
3741
3910
|
josh_commands: [],
|
|
3911
|
+
josh_signatures: [],
|
|
3742
3912
|
check_key: time_single_check.NO_CHECK,
|
|
3743
3913
|
marker: time_markers.NO_MARKER,
|
|
3744
3914
|
message_id: NO_MESSAGE_ID2,
|
|
@@ -3751,6 +3921,7 @@ var UNKNOWN_CALL = {
|
|
|
3751
3921
|
label: UNKNOWN_TOOL,
|
|
3752
3922
|
josh_command: "",
|
|
3753
3923
|
josh_commands: [],
|
|
3924
|
+
josh_signatures: [],
|
|
3754
3925
|
check_key: time_single_check.NO_CHECK,
|
|
3755
3926
|
marker: time_markers.NO_MARKER,
|
|
3756
3927
|
message_id: NO_MESSAGE_ID2,
|
|
@@ -3764,6 +3935,7 @@ function non_bash_call(name, input, message_id) {
|
|
|
3764
3935
|
label: name,
|
|
3765
3936
|
josh_command: "",
|
|
3766
3937
|
josh_commands: [],
|
|
3938
|
+
josh_signatures: [],
|
|
3767
3939
|
check_key: time_single_check.NO_CHECK,
|
|
3768
3940
|
marker: time_markers.tool_marker(name, input),
|
|
3769
3941
|
message_id,
|
|
@@ -3783,6 +3955,7 @@ function bash_call(input, message_id) {
|
|
|
3783
3955
|
label: time_shell.bash_label(command),
|
|
3784
3956
|
josh_command,
|
|
3785
3957
|
josh_commands: time_shell.josh_commands_of(command),
|
|
3958
|
+
josh_signatures: time_shell.josh_signatures_of(command),
|
|
3786
3959
|
check_key: time_single_check.check_key(josh_command, command),
|
|
3787
3960
|
marker: time_markers.bash_marker(command),
|
|
3788
3961
|
message_id,
|
|
@@ -3905,6 +4078,7 @@ function to_spans(events, finished) {
|
|
|
3905
4078
|
label: event.label,
|
|
3906
4079
|
josh_command: event.josh_command,
|
|
3907
4080
|
josh_commands: event.josh_commands,
|
|
4081
|
+
josh_signatures: event.josh_signatures,
|
|
3908
4082
|
check_key: event.check_key,
|
|
3909
4083
|
marker: event.marker,
|
|
3910
4084
|
is_bundleable: event.is_bundleable,
|
|
@@ -4425,7 +4599,7 @@ function describe_span(span) {
|
|
|
4425
4599
|
}
|
|
4426
4600
|
var FOLD_MINIMUM = 2;
|
|
4427
4601
|
function read_targets(sequence) {
|
|
4428
|
-
const paths = sequence.filter((span) => span.is_bundleable && !span.is_writing).map((span) => span.targets[0] ?? "").filter((
|
|
4602
|
+
const paths = sequence.filter((span) => span.is_bundleable && !span.is_writing).map((span) => span.targets[0] ?? "").filter((path5) => path5 !== "" && time_bundle_call.has_extension(path5));
|
|
4429
4603
|
return [...new Set(paths)];
|
|
4430
4604
|
}
|
|
4431
4605
|
function read_fold_directive(sequence) {
|
|
@@ -4434,7 +4608,7 @@ function read_fold_directive(sequence) {
|
|
|
4434
4608
|
return ` Fold them into one call: \`pnpm josh read:files ${paths.join(" ")}\`.`;
|
|
4435
4609
|
}
|
|
4436
4610
|
function write_targets(sequence) {
|
|
4437
|
-
const paths = sequence.filter((span) => span.label === EDIT_TOOL).map((span) => span.targets[0] ?? "").filter((
|
|
4611
|
+
const paths = sequence.filter((span) => span.label === EDIT_TOOL).map((span) => span.targets[0] ?? "").filter((path5) => path5 !== "" && time_bundle_call.has_extension(path5));
|
|
4438
4612
|
return [...new Set(paths)];
|
|
4439
4613
|
}
|
|
4440
4614
|
function write_fold_directive(sequence) {
|
|
@@ -4486,7 +4660,7 @@ var time_batch_guard = {
|
|
|
4486
4660
|
};
|
|
4487
4661
|
|
|
4488
4662
|
// scripts/time-runtime/time-hook-transcript.ts
|
|
4489
|
-
import
|
|
4663
|
+
import path4 from "node:path";
|
|
4490
4664
|
var { TRANSCRIPT_EXTENSION } = cost_transcript;
|
|
4491
4665
|
var AGENT_PREFIX = "agent-";
|
|
4492
4666
|
var AGENT_ID_PATTERN = /^[\w-]+$/u;
|
|
@@ -4494,10 +4668,10 @@ function is_agent_id(value) {
|
|
|
4494
4668
|
return typeof value === "string" && AGENT_ID_PATTERN.test(value);
|
|
4495
4669
|
}
|
|
4496
4670
|
function fork_path(transcript_path, agent_id) {
|
|
4497
|
-
const directory =
|
|
4498
|
-
const session_id =
|
|
4671
|
+
const directory = path4.dirname(transcript_path);
|
|
4672
|
+
const session_id = path4.basename(transcript_path, TRANSCRIPT_EXTENSION);
|
|
4499
4673
|
const file_name = `${AGENT_PREFIX}${agent_id}${TRANSCRIPT_EXTENSION}`;
|
|
4500
|
-
return
|
|
4674
|
+
return path4.join(cost_transcript.unit_directory(directory, session_id), file_name);
|
|
4501
4675
|
}
|
|
4502
4676
|
function transcript_of(transcript_path, agent_id) {
|
|
4503
4677
|
if (!is_agent_id(agent_id) || !transcript_path.endsWith(TRANSCRIPT_EXTENSION)) {
|
|
@@ -4512,7 +4686,7 @@ var time_hook_transcript = {
|
|
|
4512
4686
|
|
|
4513
4687
|
// scripts/time-runtime/time-density-hook.ts
|
|
4514
4688
|
import { closeSync as closeSync2, fstatSync, openSync as openSync2, readSync } from "node:fs";
|
|
4515
|
-
import { z as
|
|
4689
|
+
import { z as z6 } from "zod";
|
|
4516
4690
|
|
|
4517
4691
|
// scripts/time-runtime/time-density.ts
|
|
4518
4692
|
var MIN_ROUND_TRIPS = 10;
|
|
@@ -4568,15 +4742,15 @@ var TAIL_BYTES = 262144;
|
|
|
4568
4742
|
var FILE_START = 0;
|
|
4569
4743
|
var NOTICE_PREFIX = "josh-density-notice-";
|
|
4570
4744
|
var NEVER_MS = 0;
|
|
4571
|
-
var payload_schema =
|
|
4572
|
-
transcript_path:
|
|
4745
|
+
var payload_schema = z6.object({
|
|
4746
|
+
transcript_path: z6.string().min(1).optional(),
|
|
4573
4747
|
// **`nullish`, not `optional`.** A payload spelling the absent agent as `null` has to read as "no
|
|
4574
4748
|
// fork"; rejected, it would fail `safeParse` for the whole payload and silence the line for every
|
|
4575
4749
|
// edit of the main line — the opposite of this module's stated failure direction.
|
|
4576
|
-
agent_id:
|
|
4750
|
+
agent_id: z6.string().nullish()
|
|
4577
4751
|
});
|
|
4578
|
-
var notice_schema =
|
|
4579
|
-
var call_schema =
|
|
4752
|
+
var notice_schema = z6.object({ notified_at_ms: z6.number() });
|
|
4753
|
+
var call_schema = z6.object({ tool_name: z6.string().min(1), tool_input: z6.unknown() });
|
|
4580
4754
|
function parse_transcript_path(raw_payload) {
|
|
4581
4755
|
const parsed = payload_schema.safeParse(JSON.parse(raw_payload));
|
|
4582
4756
|
if (!parsed.success || parsed.data.transcript_path === void 0) return void 0;
|
|
@@ -4643,9 +4817,6 @@ export {
|
|
|
4643
4817
|
ESLINT_EDIT_CACHE_FLAGS,
|
|
4644
4818
|
time_shell,
|
|
4645
4819
|
time_bundle_call,
|
|
4646
|
-
blocking_issue_schema,
|
|
4647
|
-
rest_comment_schema,
|
|
4648
|
-
open_issue_schema,
|
|
4649
4820
|
CHECK_STATUS_PENDING,
|
|
4650
4821
|
CHECK_STATUS_FAIL,
|
|
4651
4822
|
read_string,
|