@joshuafolkken/kit 1.1379.0 → 1.1387.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.claude/skills/workflow-commands/backlogrun-progress.md +2 -3
- package/.claude/skills/workflow-commands/backlogrun-steps.md +2 -4
- package/.claude/skills/workflow-commands/retrospective.md +1 -2
- package/dist/hooks/batch-guard.js +6 -6
- package/dist/hooks/{chunk-RW4XMF47.js → chunk-2YSW3CXH.js} +133 -234
- package/dist/hooks/{chunk-4RWRP3FF.js → chunk-DCY2GWHC.js} +5 -5
- package/dist/hooks/{chunk-DHSKBAQ2.js → chunk-EAQEGME7.js} +1 -1
- package/dist/hooks/{chunk-E7LZL5AN.js → chunk-EBSHN25W.js} +3 -3
- package/dist/hooks/{chunk-HGA4EG5S.js → chunk-GJ7JPIHN.js} +15 -15
- package/dist/hooks/{chunk-TG2AB3W6.js → chunk-GNDJ7NJ7.js} +88 -501
- package/dist/hooks/{chunk-546X4PX3.js → chunk-IYVOLN6A.js} +3 -3
- package/dist/hooks/{chunk-NBEAGPS4.js → chunk-J3KSS7II.js} +1 -1
- package/dist/hooks/{chunk-7ZM464OW.js → chunk-LMWBRJIP.js} +3 -3
- package/dist/hooks/{chunk-TORRSNDJ.js → chunk-SL4GQ2XP.js} +3 -3
- package/dist/hooks/{chunk-PV3FWDSQ.js → chunk-STDSEPWU.js} +2 -2
- package/dist/hooks/{chunk-YSDAH73S.js → chunk-U32VSRNG.js} +420 -4
- package/dist/hooks/{chunk-QARPP36N.js → chunk-WNFJLBXI.js} +3 -3
- package/dist/hooks/codex-hook-adapter.js +13 -13
- package/dist/hooks/duplicate-read-guard.js +7 -7
- package/dist/hooks/format-edited.js +3 -3
- package/dist/hooks/inputs.json +213 -212
- package/dist/hooks/investigation-guard.js +7 -7
- package/dist/hooks/pretool-guard.js +12 -12
- package/dist/hooks/session-lang.js +65 -9
- package/dist/hooks/stop-guard.js +104 -32
- package/dist/josh.js +2 -2
- package/package.json +1 -1
- package/prompts/collaboration-workflow/upstream-interrupt.md +1 -1
- package/scripts/backlog/backlog-budget-cli.ts +2 -20
- package/scripts/backlog/backlog-budget.ts +72 -109
- package/scripts/backlog/backlog-drive-cli.ts +12 -7
- package/scripts/backlog/backlog-drive-launch.ts +2 -0
- package/scripts/backlog/backlog-drive-owner.ts +72 -5
- package/scripts/backlog/backlog-drive.ts +5 -2
- package/scripts/backlog/backlog-offer-cli.ts +1 -15
- package/scripts/backlog/backlogrun-launch.ts +56 -5
- package/scripts/document/document-byte-budget.ts +1 -1
- package/scripts/epic/epic-candidate-confirm.ts +1 -1
- package/scripts/epic/epic-cross-repo.ts +4 -4
- package/scripts/epic/epic-next.ts +1 -1
- package/scripts/hooks/stop-guard.ts +20 -9
- package/scripts/init/start-plan.ts +15 -4
- package/scripts/init/start-steps.ts +31 -3
- package/scripts/init/start.ts +20 -13
- package/scripts/josh/josh-commands-ai.ts +1 -1
- package/scripts/josh/josh-commands-project.ts +1 -1
- package/scripts/lane/lane-cli.ts +1 -3
- package/scripts/run/board/run-board-header.ts +137 -88
- package/scripts/run/board/run-board-labels.ts +18 -17
- package/scripts/run/board/run-board-machine.ts +21 -8
- package/scripts/run/board/run-board-read.ts +9 -2
- package/scripts/run/board/run-board-render.ts +1 -5
- package/scripts/run/board/run-board-setup.ts +73 -0
- package/scripts/run/board/run-board-span.ts +49 -8
- package/scripts/run/board/run-board-status.ts +10 -23
- package/scripts/run/board/run-board-tick.ts +11 -2
- package/scripts/run/carry/run-carry-added.ts +1 -1
- package/scripts/run/carry/run-carry-change.ts +1 -1
- package/scripts/run/carry/run-carry-cli.ts +36 -60
- package/scripts/run/carry/run-carry-ended.ts +10 -2
- package/scripts/run/carry/run-carry.ts +38 -68
- package/scripts/run/cut/run-cut.ts +4 -4
- package/scripts/run/event/run-event-render.ts +1 -1
- package/scripts/run/event/run-event-scope.ts +4 -8
- package/scripts/run/event/run-event-setup.ts +47 -0
- package/scripts/run/event/run-event-stream-emit.ts +0 -25
- package/scripts/run/event/run-event-stream.ts +41 -28
- package/scripts/run/merge/run-merge-collect.ts +1 -1
- package/scripts/run/merge/run-merge-steps.ts +1 -1
- package/scripts/run/progress/run-progress-read.ts +1 -1
- package/scripts/run/run-headless.ts +2 -2
- package/scripts/run/run-status-cli.ts +1 -5
- package/scripts/run/run-step.ts +2 -9
- package/scripts/run/run-stop-notify.ts +1 -1
- package/scripts/run/run-stranded-detect.ts +61 -15
- package/scripts/run/run-stranded.ts +25 -17
- package/scripts/run/wake/run-wake-cli.ts +58 -15
- package/scripts/run/wake/run-wake-describe.ts +5 -5
- package/scripts/run/wake/run-wake-driver.ts +13 -4
- package/scripts/run/wake/run-wake-handoff.ts +1 -1
- package/scripts/run/wake/run-wake-loop.ts +4 -21
- package/scripts/run/wake/run-wake-restart.ts +1 -1
- package/scripts/run/wake/run-wake.ts +6 -7
- package/scripts/backlog/backlog-idle.ts +0 -88
|
@@ -74,7 +74,7 @@ const DOCUMENT_BYTE_BUDGET: ReadonlyArray<DocumentBudget> = [
|
|
|
74
74
|
{ path: '.claude/skills/workflow-commands/target-repository.md', bytes: 4096 },
|
|
75
75
|
{ path: '.claude/skills/workflow-commands/working-tree-hold.md', bytes: 8192 },
|
|
76
76
|
{ path: 'docs/josh-commands-automation.md', bytes: 53_248 },
|
|
77
|
-
{ path: 'docs/josh-commands-run.md', bytes:
|
|
77
|
+
{ path: 'docs/josh-commands-run.md', bytes: 69_632 },
|
|
78
78
|
{ path: 'docs/josh-commands-backlog.md', bytes: 57_344 },
|
|
79
79
|
{ path: 'docs/josh-commands.md', bytes: 49_152 },
|
|
80
80
|
{ path: 'docs/labels-and-run-states.md', bytes: 8192 },
|
|
@@ -111,7 +111,7 @@ function is_still_runnable(
|
|
|
111
111
|
// A read that failed withholds the candidate rather than confirming it. "Could not tell" is not
|
|
112
112
|
// "nothing blocks it", and this is the one direction the guard may not fail in, because that answer
|
|
113
113
|
// *starts* work — the same reason `epic-busy.ts` answers `wait` for a listing it could not read. The
|
|
114
|
-
// caller then offers the next candidate or waits, and the
|
|
114
|
+
// caller then offers the next candidate or waits, and the idle wait bounds the wait.
|
|
115
115
|
async function read_or_withhold(
|
|
116
116
|
candidate: EpicChild,
|
|
117
117
|
read_blockers: BlockersReader,
|
|
@@ -80,7 +80,7 @@ const NOT_FOUND_STATUS = 404
|
|
|
80
80
|
//
|
|
81
81
|
// **`absent` is not `unreadable`.** A repository with no
|
|
82
82
|
// manifest, or one whose manifest says `private`, publishes nothing — so a closed blocker there has
|
|
83
|
-
// no release anyone could wait for, and waiting means waiting until the run's
|
|
83
|
+
// no release anyone could wait for, and waiting means waiting until the run's idle wait runs out
|
|
84
84
|
// with nothing an operator can edit to clear it. A read that merely failed says none of that.
|
|
85
85
|
//
|
|
86
86
|
// **It is read from the repository rather than from the registry, and that is the whole design.** The
|
|
@@ -123,7 +123,7 @@ type WorkspaceCheck = 'workspace' | 'single' | 'unreadable'
|
|
|
123
123
|
//
|
|
124
124
|
// **The file's existence proves nothing.** `josh sync` distributes it to every consumer regardless of
|
|
125
125
|
// layout — it carries `allowBuilds`, `overrides` and the like — so probing for its presence would read
|
|
126
|
-
// every private first-party repository as a workspace and wait the
|
|
126
|
+
// every private first-party repository as a workspace and wait the whole idle wait on each. What
|
|
127
127
|
// makes a repository a workspace is a non-empty `packages`
|
|
128
128
|
// key, so that is what is read.
|
|
129
129
|
function declares_packages(yaml: string): boolean {
|
|
@@ -271,7 +271,7 @@ function read_default_branch_version(repo: string): string | undefined {
|
|
|
271
271
|
// would start a dependent before its blocker's release existed. What is asked is only whether the
|
|
272
272
|
// repository *is* a workspace — the members are not enumerated and nothing looks for which of them
|
|
273
273
|
// publishes, so a workspace whose members are all private waits when it need not. That is the safe
|
|
274
|
-
// direction: waiting ends
|
|
274
|
+
// direction: waiting ends when the run's idle wait runs out, resolving early does not end at all.
|
|
275
275
|
function publishes_nothing(repo: string): boolean {
|
|
276
276
|
const answer = read_manifest(repo)
|
|
277
277
|
|
|
@@ -285,7 +285,7 @@ function publishes_nothing(repo: string): boolean {
|
|
|
285
285
|
//
|
|
286
286
|
// **"Ships nothing" is asked first, and it is a different question.** Waiting is right only where
|
|
287
287
|
// something could still arrive; a closed blocker in a repository that publishes no package has no
|
|
288
|
-
// release for `is_published` to ever find, so it would wait until the run's
|
|
288
|
+
// release for `is_published` to ever find, so it would wait until the run's idle wait runs out
|
|
289
289
|
// with nothing an operator could edit to clear it. It is answered from that repository's own manifest and never from
|
|
290
290
|
// the registry, for the reason `ManifestAnswer` records: a registry 404 also means "this token may
|
|
291
291
|
// not see it", and resolving on one would start a consumer child before its blocker's release
|
|
@@ -194,7 +194,7 @@ function refuse(reason: string): number {
|
|
|
194
194
|
|
|
195
195
|
// The verdict as it applies to *this* repository. `run` never reaches a caller here: it means some
|
|
196
196
|
// other repository has work, which for this session is something to wait on rather than a state its
|
|
197
|
-
// loop has a branch for. The
|
|
197
|
+
// loop has a branch for. The idle wait is what bounds that wait.
|
|
198
198
|
function repo_verdict(verdict: LaneOffer['verdict']): LaneOffer['verdict'] {
|
|
199
199
|
return verdict === 'run' ? 'wait' : verdict
|
|
200
200
|
}
|
|
@@ -126,20 +126,22 @@ async function outcome_of(raw_payload: string): Promise<StopOutcome> {
|
|
|
126
126
|
|
|
127
127
|
// A live carry record is in place — `carried` alone reads as a run. Both report checks below are about
|
|
128
128
|
// a carried run: the strand judge reads this very record and returns on anything but `carried`, and a
|
|
129
|
-
// stall needs a backlog driver, which begins one before its plan.
|
|
130
|
-
// record a crashed run left behind is no run either
|
|
131
|
-
//
|
|
129
|
+
// stall needs a backlog driver, which begins one before its plan. An `unreadable`
|
|
130
|
+
// record a crashed run left behind is no run either.
|
|
131
|
+
// The answer is whether that run's owner is still live, and `undefined` where there is no run at all.
|
|
132
132
|
// A failed git read — outside a repository, `git rev-parse` exits non-zero — reads as no run, since this
|
|
133
133
|
// gate sits ahead of the checks' own swallowing and must not throw into the stop decision either.
|
|
134
|
-
async function
|
|
134
|
+
async function carried_owner_live(): Promise<boolean | undefined> {
|
|
135
135
|
try {
|
|
136
136
|
const directory = await run_carry.repository_directory()
|
|
137
137
|
|
|
138
|
-
if (directory === undefined) return
|
|
138
|
+
if (directory === undefined) return undefined
|
|
139
139
|
|
|
140
|
-
|
|
140
|
+
const read = run_carry.read_carry(run_carry.carry_path(directory))
|
|
141
|
+
|
|
142
|
+
return read.kind === 'carried' ? run_carry.is_owner_live(read.carry) : undefined
|
|
141
143
|
} catch {
|
|
142
|
-
return
|
|
144
|
+
return undefined
|
|
143
145
|
}
|
|
144
146
|
}
|
|
145
147
|
|
|
@@ -149,10 +151,19 @@ async function has_run_record(): Promise<boolean> {
|
|
|
149
151
|
// — the budget handed off, the owner dead, no supervisor watching. Both only report and swallow their
|
|
150
152
|
// own failures, so neither can change the stop decision. An ordinary stop with no run record skips
|
|
151
153
|
// both, so it pays one directory read rather than their reads and a stale stream's backlog probe.
|
|
154
|
+
//
|
|
155
|
+
// **A stall is a live driver not dispatching, so a record whose owner is gone is not asked.** A record
|
|
156
|
+
// stands until a person carries or discards it, so a crashed run's would otherwise re-arm the stall's
|
|
157
|
+
// `git worktree list` and network backlog probe on every stop in the repository, for a run nobody
|
|
158
|
+
// drives. A gone driver is the strand check's question — handed off or not (`run-stranded.ts` →
|
|
159
|
+
// `had_driver`) — and that one reads local files only.
|
|
152
160
|
async function run_report_checks(): Promise<void> {
|
|
153
|
-
|
|
161
|
+
const is_owner_live = await carried_owner_live()
|
|
162
|
+
|
|
163
|
+
if (is_owner_live === undefined) return
|
|
164
|
+
|
|
165
|
+
if (is_owner_live) await backlog_stalled_detect.run_stall_check(backlog_ready.DEFAULT_PORTS)
|
|
154
166
|
|
|
155
|
-
await backlog_stalled_detect.run_stall_check(backlog_ready.DEFAULT_PORTS)
|
|
156
167
|
await run_stranded_detect.run_stranded_check()
|
|
157
168
|
}
|
|
158
169
|
|
|
@@ -9,6 +9,7 @@ interface StartOptions {
|
|
|
9
9
|
is_github: boolean
|
|
10
10
|
visibility: Visibility
|
|
11
11
|
init_command: string | undefined
|
|
12
|
+
push_preflight_command: string | undefined
|
|
12
13
|
}
|
|
13
14
|
|
|
14
15
|
type StartStep = 'git_init' | 'initialize' | 'commit' | 'repository' | 'labels' | 'setup_pr'
|
|
@@ -53,7 +54,8 @@ const STEP_LABELS: Readonly<Record<StartStep, string>> = {
|
|
|
53
54
|
setup_pr: 'Open an Issue and a pull request that add the kit setup to main',
|
|
54
55
|
}
|
|
55
56
|
|
|
56
|
-
// The GitHub write steps `--yes` alone never consents to.
|
|
57
|
+
// The GitHub write steps `--yes` alone never consents to. Each one pushes — main, or the setup branch —
|
|
58
|
+
// so they are also the steps a caller's push preflight guards (#3702).
|
|
57
59
|
const GITHUB_WRITE_STEPS: ReadonlySet<StartStep> = new Set(['repository', 'setup_pr'])
|
|
58
60
|
|
|
59
61
|
function refused(refusal: string): StepPlan {
|
|
@@ -107,6 +109,10 @@ function plan_steps(state: GitState): StepPlan {
|
|
|
107
109
|
return { steps: FULL_STEPS, refusal: undefined }
|
|
108
110
|
}
|
|
109
111
|
|
|
112
|
+
function has_push(steps: ReadonlyArray<StartStep>): boolean {
|
|
113
|
+
return steps.some((step) => GITHUB_WRITE_STEPS.has(step))
|
|
114
|
+
}
|
|
115
|
+
|
|
110
116
|
function interaction_of(options: StartOptions, is_tty: boolean): Verdict<Interaction> {
|
|
111
117
|
if (options.is_yes) return { value: 'unattended', refusal: undefined }
|
|
112
118
|
if (is_tty) return { value: 'interactive', refusal: undefined }
|
|
@@ -126,9 +132,7 @@ function github_consent(
|
|
|
126
132
|
options: StartOptions,
|
|
127
133
|
interaction: Interaction,
|
|
128
134
|
): Verdict<Consent> {
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
if (!is_github_write || options.is_github) return { value: 'granted', refusal: undefined }
|
|
135
|
+
if (!has_push(steps) || options.is_github) return { value: 'granted', refusal: undefined }
|
|
132
136
|
if (interaction === 'interactive') return { value: 'ask', refusal: undefined }
|
|
133
137
|
|
|
134
138
|
return {
|
|
@@ -158,6 +162,11 @@ function progress_report(
|
|
|
158
162
|
return `josh start stopped at: ${labels[failed]}\nCompleted: ${done}\nCause: ${cause}`
|
|
159
163
|
}
|
|
160
164
|
|
|
165
|
+
// What a run prints when the caller's push preflight fails: no step has run yet, so nothing changed.
|
|
166
|
+
function preflight_report(command: string, cause: string): string {
|
|
167
|
+
return `josh start stopped before its first step: the push preflight (${command}) failed. Nothing was changed.\nCause: ${cause}`
|
|
168
|
+
}
|
|
169
|
+
|
|
161
170
|
function plan_summary(
|
|
162
171
|
steps: ReadonlyArray<StartStep>,
|
|
163
172
|
labels: Readonly<Record<StartStep, string>> = STEP_LABELS,
|
|
@@ -169,9 +178,11 @@ function plan_summary(
|
|
|
169
178
|
|
|
170
179
|
const start_plan = {
|
|
171
180
|
plan_steps,
|
|
181
|
+
has_push,
|
|
172
182
|
interaction_of,
|
|
173
183
|
github_consent,
|
|
174
184
|
progress_report,
|
|
185
|
+
preflight_report,
|
|
175
186
|
plan_summary,
|
|
176
187
|
step_labels,
|
|
177
188
|
STEP_LABELS,
|
|
@@ -18,6 +18,7 @@ interface StepContext {
|
|
|
18
18
|
profile: ProjectProfile
|
|
19
19
|
visibility: Visibility
|
|
20
20
|
init_command: string | undefined
|
|
21
|
+
push_preflight_command: string | undefined
|
|
21
22
|
}
|
|
22
23
|
|
|
23
24
|
interface RunContext extends StepContext {
|
|
@@ -69,6 +70,13 @@ function self_run_refusal(root: string): string | undefined {
|
|
|
69
70
|
return self_sync_guard.self_sync_refusal(PACKAGE_DIR, root)
|
|
70
71
|
}
|
|
71
72
|
|
|
73
|
+
// A caller's command line runs without a shell, split the way execa splits one.
|
|
74
|
+
function run_command_line(command_line: string, extra: ReadonlyArray<string>, root: string): void {
|
|
75
|
+
const [command = '', ...args] = parseCommandString(command_line)
|
|
76
|
+
|
|
77
|
+
run_local(command, [...args, ...extra], root)
|
|
78
|
+
}
|
|
79
|
+
|
|
72
80
|
function create_git_repository(context: StepContext): void {
|
|
73
81
|
initial_commit.create_repository(context.root)
|
|
74
82
|
}
|
|
@@ -84,9 +92,7 @@ function initialize(context: StepContext): void {
|
|
|
84
92
|
return
|
|
85
93
|
}
|
|
86
94
|
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
run_local(command, [...args, '--profile', context.profile], context.root)
|
|
95
|
+
run_command_line(context.init_command, ['--profile', context.profile], context.root)
|
|
90
96
|
}
|
|
91
97
|
|
|
92
98
|
function commit_all(context: StepContext): void {
|
|
@@ -122,6 +128,26 @@ function baseline_of(
|
|
|
122
128
|
return start_setup_pr.changed_paths(context.root)
|
|
123
129
|
}
|
|
124
130
|
|
|
131
|
+
// A toolkit's own pre-push hook can need something kit knows nothing about (app-kit's needs a Docker
|
|
132
|
+
// daemon), and a push it refuses lands after `gh repo create` has made the repository. So the caller
|
|
133
|
+
// names a check that runs only when the plan pushes, before the first step: a run that does not push
|
|
134
|
+
// is never stopped by it, and one it stops has written nothing (#3702).
|
|
135
|
+
function run_push_preflight(steps: ReadonlyArray<StartStep>, context: StepContext): void {
|
|
136
|
+
const command_line = context.push_preflight_command
|
|
137
|
+
|
|
138
|
+
if (command_line === undefined || !start_plan.has_push(steps)) return
|
|
139
|
+
|
|
140
|
+
console.info(`\nPush preflight: ${command_line}`)
|
|
141
|
+
|
|
142
|
+
try {
|
|
143
|
+
run_command_line(command_line, [], context.root)
|
|
144
|
+
} catch (error) {
|
|
145
|
+
const cause = error_text.message_of(error)
|
|
146
|
+
|
|
147
|
+
throw new Error(start_plan.preflight_report(command_line, cause), { cause: error })
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
|
|
125
151
|
type StepAction = (context: RunContext) => void | Promise<void>
|
|
126
152
|
|
|
127
153
|
const STEP_ACTIONS: Readonly<Record<StartStep, StepAction>> = {
|
|
@@ -150,6 +176,8 @@ async function run_step(
|
|
|
150
176
|
|
|
151
177
|
async function run_steps(steps: ReadonlyArray<StartStep>, context: StepContext): Promise<void> {
|
|
152
178
|
const labels = start_plan.step_labels(context.init_command)
|
|
179
|
+
|
|
180
|
+
run_push_preflight(steps, context)
|
|
153
181
|
const run_context: RunContext = { ...context, baseline: baseline_of(steps, context) }
|
|
154
182
|
|
|
155
183
|
for (const [index, step] of steps.entries()) {
|
package/scripts/init/start.ts
CHANGED
|
@@ -15,8 +15,9 @@ import { start_steps } from './start-steps'
|
|
|
15
15
|
const ARGUMENT_START_INDEX = 2
|
|
16
16
|
const READY_MESSAGE = 'GitHub workflow ready. Use kickoff new in your assistant to plan an Issue.'
|
|
17
17
|
const START_USAGE =
|
|
18
|
-
'josh start [--profile basic|full] [--yes] [--github] [--public] [--init-command <command>]'
|
|
18
|
+
'josh start [--profile basic|full] [--yes] [--github] [--public] [--init-command <command>] [--push-preflight-command <command>]'
|
|
19
19
|
const INIT_COMMAND_FLAG = '--init-command'
|
|
20
|
+
const PUSH_PREFLIGHT_COMMAND_FLAG = '--push-preflight-command'
|
|
20
21
|
const YES_FLAG = '--yes'
|
|
21
22
|
const GITHUB_FLAG = '--github'
|
|
22
23
|
const PUBLIC_FLAG = '--public'
|
|
@@ -28,34 +29,38 @@ interface Prepared {
|
|
|
28
29
|
choices: Choices
|
|
29
30
|
}
|
|
30
31
|
|
|
31
|
-
interface
|
|
32
|
-
|
|
32
|
+
interface CommandSplit {
|
|
33
|
+
command: string | undefined
|
|
33
34
|
rest: ReadonlyArray<string>
|
|
34
35
|
}
|
|
35
36
|
|
|
36
37
|
// A value that is blank or another flag means the command itself was left out, which would otherwise
|
|
37
|
-
// swallow the next switch or hand
|
|
38
|
+
// swallow the next switch or hand its step nothing to run.
|
|
38
39
|
function is_command_value(value: string | undefined): value is string {
|
|
39
40
|
return value !== undefined && value.trim() !== '' && !value.startsWith('-')
|
|
40
41
|
}
|
|
41
42
|
|
|
42
43
|
// The command arrives as the one argument after its flag and is removed together with it.
|
|
43
|
-
function
|
|
44
|
-
const index = args.indexOf(
|
|
44
|
+
function split_command(args: ReadonlyArray<string>, flag: string): CommandSplit {
|
|
45
|
+
const index = args.indexOf(flag)
|
|
45
46
|
|
|
46
|
-
if (index === -1) return {
|
|
47
|
+
if (index === -1) return { command: undefined, rest: args }
|
|
47
48
|
|
|
48
49
|
const value = args[index + 1]
|
|
49
50
|
|
|
50
51
|
if (!is_command_value(value)) throw new Error(`Usage: ${START_USAGE}`)
|
|
51
52
|
|
|
52
|
-
return {
|
|
53
|
+
return { command: value, rest: args.filter((_, at) => at !== index && at !== index + 1) }
|
|
53
54
|
}
|
|
54
55
|
|
|
55
|
-
// The switches and the
|
|
56
|
+
// The switches and the caller's commands are peeled off first, so what remains is exactly the
|
|
56
57
|
// `--profile` pair `josh init` accepts — one parser for the profile in both commands.
|
|
57
58
|
function parse_start_options(args: ReadonlyArray<string>): StartOptions {
|
|
58
|
-
const
|
|
59
|
+
const init = split_command(args, INIT_COMMAND_FLAG)
|
|
60
|
+
const { command: push_preflight_command, rest } = split_command(
|
|
61
|
+
init.rest,
|
|
62
|
+
PUSH_PREFLIGHT_COMMAND_FLAG,
|
|
63
|
+
)
|
|
59
64
|
const profile_args = rest.filter((argument) => !SWITCHES.has(argument))
|
|
60
65
|
|
|
61
66
|
return {
|
|
@@ -63,7 +68,8 @@ function parse_start_options(args: ReadonlyArray<string>): StartOptions {
|
|
|
63
68
|
is_yes: rest.includes(YES_FLAG),
|
|
64
69
|
is_github: rest.includes(GITHUB_FLAG),
|
|
65
70
|
visibility: rest.includes(PUBLIC_FLAG) ? 'public' : 'private',
|
|
66
|
-
init_command,
|
|
71
|
+
init_command: init.command,
|
|
72
|
+
push_preflight_command,
|
|
67
73
|
}
|
|
68
74
|
}
|
|
69
75
|
|
|
@@ -112,12 +118,13 @@ async function main(args: ReadonlyArray<string>, is_tty: boolean): Promise<void>
|
|
|
112
118
|
const { steps, interaction, choices } = prepare(args, is_tty, process.cwd())
|
|
113
119
|
|
|
114
120
|
const { root, options } = choices
|
|
115
|
-
const { visibility, init_command } = options
|
|
121
|
+
const { visibility, init_command, push_preflight_command } = options
|
|
116
122
|
|
|
117
123
|
console.info(start_plan.plan_summary(steps, start_plan.step_labels(init_command)))
|
|
118
124
|
const profile = await resolve_profile(choices, interaction)
|
|
125
|
+
const context = { root, profile, visibility, init_command, push_preflight_command }
|
|
119
126
|
|
|
120
|
-
await start_steps.run_steps(steps,
|
|
127
|
+
await start_steps.run_steps(steps, context)
|
|
121
128
|
console.info(`\n${READY_MESSAGE}`)
|
|
122
129
|
}
|
|
123
130
|
|
|
@@ -214,7 +214,7 @@ const AI_COMMANDS: Record<string, CommandEntry> = {
|
|
|
214
214
|
// credentials, the same reason `run:wake` carries these flags.
|
|
215
215
|
tsx_arguments: OPTIONAL_ENV_FILE_FLAGS,
|
|
216
216
|
description:
|
|
217
|
-
'Report whether the run is stranded —
|
|
217
|
+
'Report whether the run is stranded — owner gone, no successor holding the budget, and no supervisor watching',
|
|
218
218
|
category: 'AI tools',
|
|
219
219
|
reference: ['', 'automation', ['processes', 'notifications']],
|
|
220
220
|
},
|
|
@@ -17,7 +17,7 @@ const PROJECT_COMMANDS: Record<string, CommandEntry> = {
|
|
|
17
17
|
description: 'Set a project up for the GitHub Issue workflow, from git init to the setup PR',
|
|
18
18
|
category: 'Project',
|
|
19
19
|
reference: [
|
|
20
|
-
`${PROFILE_ARGUMENTS} [--yes] [--github] [--public] [--init-command <command>]`,
|
|
20
|
+
`${PROFILE_ARGUMENTS} [--yes] [--github] [--public] [--init-command <command>] [--push-preflight-command <command>]`,
|
|
21
21
|
'developer',
|
|
22
22
|
['files', 'git', 'network', 'processes'],
|
|
23
23
|
],
|
package/scripts/lane/lane-cli.ts
CHANGED
|
@@ -62,9 +62,7 @@ async function guard_cut_session(): Promise<string | undefined> {
|
|
|
62
62
|
|
|
63
63
|
const read = run_carry.read_carry(run_carry.carry_path(directory))
|
|
64
64
|
|
|
65
|
-
if (
|
|
66
|
-
return undefined
|
|
67
|
-
}
|
|
65
|
+
if (read.kind !== 'carried' || read.carry.is_handed_off !== true) return undefined
|
|
68
66
|
|
|
69
67
|
return run_carry.count_refused_message(read.carry)
|
|
70
68
|
}
|