@joshuafolkken/kit 1.957.0 → 1.972.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/background-commands.md +3 -0
- package/.claude/skills/workflow-commands/backlogrun-child.md +75 -120
- package/.claude/skills/workflow-commands/backlogrun-lanes.md +78 -115
- package/.claude/skills/workflow-commands/backlogrun-progress.md +82 -146
- package/.claude/skills/workflow-commands/backlogrun-steps.md +173 -229
- package/.claude/skills/workflow-commands/backlogrun.md +3 -1
- package/.claude/skills/workflow-commands/followup.md +3 -0
- package/.claude/skills/workflow-commands/fullrun.md +3 -4
- package/.claude/skills/workflow-commands/observation-filing.md +31 -25
- package/.claude/skills/workflow-commands/pre-gate-cut.md +96 -161
- package/.github/workflows/pr-classification.yml +12 -1
- package/CLAUDE.md +3 -3
- package/README.md +3 -3
- package/dist/CLAUDE.md +3 -3
- package/dist/hooks/batch-guard.js +4 -4
- package/dist/hooks/{chunk-XSL4SRMA.js → chunk-2K52BRWV.js} +2 -2
- package/dist/hooks/{chunk-M6K7EQDO.js → chunk-BNWIOJFU.js} +29 -16
- package/dist/hooks/{chunk-KOCK5FXQ.js → chunk-BVMSPG3M.js} +3 -3
- package/dist/hooks/{chunk-3ON7HOYB.js → chunk-CRGJTD6E.js} +1 -1
- package/dist/hooks/{chunk-HJXQZ7V4.js → chunk-F6OM6SIG.js} +3 -3
- package/dist/hooks/{chunk-LDQDZYHX.js → chunk-KQG4776P.js} +1 -1
- package/dist/hooks/{chunk-244WYOEO.js → chunk-NXL3SBXY.js} +2 -2
- package/dist/hooks/{chunk-IJPBTDTB.js → chunk-WWTMHHZU.js} +59 -18
- package/dist/hooks/{chunk-BELI5XSS.js → chunk-ZPLXLJ7O.js} +15 -5
- package/dist/hooks/{chunk-GNOITLRR.js → chunk-ZYYSBNR6.js} +4 -4
- package/dist/hooks/codex-hook-adapter.js +10 -10
- package/dist/hooks/duplicate-read-guard.js +5 -5
- package/dist/hooks/format-edited.js +2 -2
- package/dist/hooks/investigation-guard.js +5 -5
- package/dist/hooks/pretool-guard.js +9 -9
- package/dist/hooks/stop-guard.js +206 -176
- package/dist/josh.js +8 -4
- package/package.json +3 -3
- package/prompts/collaboration-workflow/wip-cap.md +1 -1
- package/scripts/backlog/backlog-arrival.ts +11 -1
- package/scripts/backlog/backlog-budget.ts +25 -8
- package/scripts/backlog/backlog-next.ts +45 -2
- package/scripts/backlog/backlog-offer.ts +1 -0
- package/scripts/backlog/backlog-plan-cli.ts +66 -27
- package/scripts/backlog/backlog-plan.ts +30 -2
- package/scripts/backlog/backlog-stalled.ts +9 -0
- package/scripts/backlog/backlog-waves.ts +169 -0
- package/scripts/document/document-byte-budget.ts +1 -1
- package/scripts/document/entry-read-budget.ts +7 -5
- package/scripts/epic/epic-lane-offer.ts +69 -4
- package/scripts/epic/epic-next.ts +2 -2
- package/scripts/epic/epic-solo.ts +147 -0
- package/scripts/epic/epic-triage.ts +47 -0
- package/scripts/gate/hook-gate-reuse.ts +7 -8
- package/scripts/git/git-closes-keyword.ts +15 -0
- package/scripts/git/git-followup-flush.ts +5 -6
- package/scripts/git/git-followup-issue-close.ts +75 -0
- package/scripts/git/git-pr-followup-wrapup.ts +36 -6
- package/scripts/git/git-pr-followup.ts +9 -16
- package/scripts/git/git-staging.ts +33 -20
- package/scripts/git/issue-labels.ts +23 -0
- package/scripts/hooks/stop-guard.ts +1 -0
- package/scripts/init/init-install.ts +64 -0
- package/scripts/init/init.ts +38 -11
- package/scripts/issue/issue-closing-pr.ts +93 -0
- package/scripts/josh/__snapshots__/josh-command-map.test.ts.snap +2 -1
- package/scripts/josh/josh-commands-project.ts +5 -1
- package/scripts/josh/josh-commands-workflow.ts +3 -3
- package/scripts/observations/observation-ledger-prepare.ts +31 -0
- package/scripts/observations/observation-ledger.ts +5 -5
- package/scripts/observations/observations-flush.ts +11 -30
- package/scripts/rules/implementation-cut.ts +49 -16
- package/scripts/rules/lane-background.ts +25 -5
- package/scripts/rules/stop-rules.ts +16 -4
- package/scripts/run/run-carry-cli.ts +21 -0
- package/scripts/run/run-cut-cli.ts +92 -110
- package/scripts/run/run-cut-report.ts +167 -0
- package/scripts/run/run-cut.ts +23 -7
- package/scripts/run/run-entry-cli.ts +36 -2
- package/scripts/run/run-headless.ts +1 -0
- package/scripts/run/run-hold-cli.ts +36 -15
- package/scripts/run/run-hold.ts +15 -4
- package/scripts/run/run-merge-cli.ts +28 -14
- package/scripts/run/run-merge-steps.ts +38 -2
- package/scripts/run/run-merge.ts +41 -3
- package/scripts/run/run-prep-cli.ts +8 -2
- package/scripts/run/run-prep-locate.ts +165 -0
- package/scripts/run/run-prep.ts +12 -1
- package/scripts/run/run-step-cli.ts +16 -0
- package/scripts/run/run-step.ts +28 -4
- package/scripts/run/run-tail-cli.ts +4 -2
- package/scripts/run/run-wake-work.ts +4 -0
- package/scripts/safe-chain/ci-installer-pin.ts +8 -4
- package/scripts/time-runtime/time-background.ts +11 -0
- package/scripts/time-runtime/time-transcript-line.ts +4 -0
- package/templates/pnpm-workspace.static.yaml +2 -0
|
@@ -5,9 +5,11 @@ import { doctor_consumer } from '#scripts/doctor/doctor-consumer'
|
|
|
5
5
|
import { hook_decision } from '#scripts/josh/hook-decision'
|
|
6
6
|
import { find_package_directory } from '#scripts/josh/josh-logic'
|
|
7
7
|
import { lane_child_marker } from '#scripts/lane/lane-child-marker'
|
|
8
|
+
import { lane_reap } from '#scripts/lane/lane-reap'
|
|
8
9
|
import { run_carry, type CarryRead } from './run-carry'
|
|
9
10
|
import { run_event_scope } from './run-event-scope'
|
|
10
11
|
import { run_event_stream } from './run-event-stream'
|
|
12
|
+
import { run_headless } from './run-headless'
|
|
11
13
|
import { run_prep_cli } from './run-prep-cli'
|
|
12
14
|
import { run_retrospective } from './run-retrospective'
|
|
13
15
|
import { run_step, type StepInput } from './run-step'
|
|
@@ -28,9 +30,20 @@ interface RunReads {
|
|
|
28
30
|
carry_kind: CarryRead['kind']
|
|
29
31
|
is_retrospective_done: boolean
|
|
30
32
|
is_at_cut_cap: boolean
|
|
33
|
+
is_handed_off: boolean
|
|
31
34
|
last_event: string | undefined
|
|
32
35
|
}
|
|
33
36
|
|
|
37
|
+
// **A hand-off stops only the session that declared the record** (joshuafolkken/kit#2760). The record is
|
|
38
|
+
// one per repository, so a `fullrun #M` a person starts in a fresh conversation beside a `backlogrun`
|
|
39
|
+
// waiting for its successor reads the same handed-off mark — and would be told to stop on every step.
|
|
40
|
+
// The owner is `--owner "$PPID"`, an ancestor of this process only in the session that took the cut.
|
|
41
|
+
function is_handed_off_here(carry: CarryRead): boolean {
|
|
42
|
+
if (carry.kind !== 'carried' || carry.carry.is_handed_off !== true) return false
|
|
43
|
+
|
|
44
|
+
return run_headless.is_owned_here(carry.carry, lane_reap.own_ancestry)
|
|
45
|
+
}
|
|
46
|
+
|
|
34
47
|
// The run-level reads, keyed on the common git directory both the carry and the event stream share. A
|
|
35
48
|
// directory that cannot be read leaves the position unknowable, which `next_action` answers `unknown`.
|
|
36
49
|
function read_run(directory: string | undefined, issue_number: string): RunReads {
|
|
@@ -39,6 +52,7 @@ function read_run(directory: string | undefined, issue_number: string): RunReads
|
|
|
39
52
|
carry_kind: 'unreadable',
|
|
40
53
|
is_retrospective_done: false,
|
|
41
54
|
is_at_cut_cap: false,
|
|
55
|
+
is_handed_off: false,
|
|
42
56
|
last_event: undefined,
|
|
43
57
|
}
|
|
44
58
|
}
|
|
@@ -60,6 +74,7 @@ function read_run(directory: string | undefined, issue_number: string): RunReads
|
|
|
60
74
|
carry_kind: carry.kind,
|
|
61
75
|
is_retrospective_done: run_carry.retrospective_done_of(carry),
|
|
62
76
|
is_at_cut_cap: carry.kind === 'carried' && run_carry.is_at_cut_cap(carry.carry),
|
|
77
|
+
is_handed_off: is_handed_off_here(carry),
|
|
63
78
|
last_event: last?.kind,
|
|
64
79
|
}
|
|
65
80
|
}
|
|
@@ -78,6 +93,7 @@ async function gather(issue_number: string): Promise<StepInput> {
|
|
|
78
93
|
carry_kind: run_reads.carry_kind,
|
|
79
94
|
is_retrospective_done: run_reads.is_retrospective_done,
|
|
80
95
|
is_at_cut_cap: run_reads.is_at_cut_cap,
|
|
96
|
+
is_handed_off: run_reads.is_handed_off,
|
|
81
97
|
is_lane_child: lane_child_marker.is_child_of(process.cwd()),
|
|
82
98
|
is_consumer: doctor_consumer.is_kit_consumer(find_package_directory(process.cwd())),
|
|
83
99
|
// Read here rather than in `run-step.ts` so the position logic stays a pure function of its input.
|
package/scripts/run/run-step.ts
CHANGED
|
@@ -126,6 +126,11 @@ interface StepInput extends PreInput {
|
|
|
126
126
|
// Whether the carried invocation has already taken its maximum cuts, read off the carry record by the
|
|
127
127
|
// CLI (joshuafolkken/kit#2346). At the cap `run:carry --cut` refuses, so the hand-off is not offered.
|
|
128
128
|
is_at_cut_cap: boolean
|
|
129
|
+
// Whether the carry record has been handed off by a cut and no successor has adopted it yet, read off
|
|
130
|
+
// the record by the CLI (joshuafolkken/kit#2760) and set only in the session that declared the record.
|
|
131
|
+
// The session that took the cut is the only one that can read this — a successor spends the mark when
|
|
132
|
+
// it begins — so it is what tells that session to stop.
|
|
133
|
+
is_handed_off: boolean
|
|
129
134
|
}
|
|
130
135
|
|
|
131
136
|
interface StepAction {
|
|
@@ -258,12 +263,31 @@ function closed_action(input: StepInput): StepAction {
|
|
|
258
263
|
}
|
|
259
264
|
}
|
|
260
265
|
|
|
261
|
-
//
|
|
262
|
-
//
|
|
263
|
-
//
|
|
264
|
-
|
|
266
|
+
// **A cut hands the run on, so the session that took it stops** (joshuafolkken/kit#2760). The driver had
|
|
267
|
+
// no handed-off state, so after `run:carry --cut` it went on answering `wait` or the next offer — a
|
|
268
|
+
// `backlogrun` parent measured at 233k kept launching children in the same conversation up to 294k. A
|
|
269
|
+
// lane child never holds the carry record, so its own position is not decided here.
|
|
270
|
+
function is_handed_off_parent(input: StepInput): boolean {
|
|
271
|
+
return input.is_handed_off && input.carry_kind === 'carried' && !input.is_lane_child
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
// The carry record's terminal answers: an unreadable record leaves the position unknowable, a handed-off
|
|
275
|
+
// run is its successor's, and a spent budget is the person's call.
|
|
276
|
+
function carry_terminal_action(input: StepInput): StepAction | undefined {
|
|
265
277
|
if (input.carry_kind === 'unreadable') return verdict(UNKNOWN)
|
|
278
|
+
if (is_handed_off_parent(input)) return verdict(STOP)
|
|
266
279
|
if (input.carry_kind === 'expired') return { kind: 'decide', line: EXPIRED_DECISION }
|
|
280
|
+
|
|
281
|
+
return undefined
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
// The terminal answers read before the run's position matters: the carry record's first, then the
|
|
285
|
+
// issue's own state, which can end the run whatever the events say. `undefined` when none applies and
|
|
286
|
+
// the position decides.
|
|
287
|
+
function terminal_action(input: StepInput): StepAction | undefined {
|
|
288
|
+
const carry_action = carry_terminal_action(input)
|
|
289
|
+
|
|
290
|
+
if (carry_action !== undefined) return carry_action
|
|
267
291
|
if (input.state === undefined) return verdict(UNKNOWN)
|
|
268
292
|
if (input.state === CLOSED) return closed_action(input)
|
|
269
293
|
|
|
@@ -33,8 +33,10 @@ interface Step {
|
|
|
33
33
|
argv: (issues: ReadonlyArray<string>) => ReadonlyArray<string>
|
|
34
34
|
}
|
|
35
35
|
|
|
36
|
-
// The steps in the order a run closes on:
|
|
37
|
-
//
|
|
36
|
+
// The steps in the order a run closes on: any ledger line the run's own commit did not carry
|
|
37
|
+
// (joshuafolkken/kit#2763) is committed first so the recurrence count is on main — on most runs the
|
|
38
|
+
// flush answers `clean` — the citations are read for the completion report, and the release scope is
|
|
39
|
+
// decided last.
|
|
38
40
|
const FLUSH_STEP: Step = {
|
|
39
41
|
header: run_tail.OBSERVATIONS_HEADER,
|
|
40
42
|
argv: () => ['observations:flush'],
|
|
@@ -29,6 +29,7 @@ import { run_invocation } from './run-invocation'
|
|
|
29
29
|
const NO_FREE = 0
|
|
30
30
|
const NO_READY = 0
|
|
31
31
|
const SUCCESS_EXIT_CODE = 0
|
|
32
|
+
const TRIAGE_WORK = 1
|
|
32
33
|
|
|
33
34
|
interface WorkPorts {
|
|
34
35
|
// The free-lane count, or `undefined` where the lane limit could not be read.
|
|
@@ -41,6 +42,9 @@ async function real_ready_count(): Promise<number | undefined> {
|
|
|
41
42
|
const result = await josh_command.josh_run(['backlog:next'], true)
|
|
42
43
|
|
|
43
44
|
if (result.code !== SUCCESS_EXIT_CODE) return undefined
|
|
45
|
+
// A `triage` answer offers no number, but the untriaged issues are a session's to judge
|
|
46
|
+
// (joshuafolkken/kit#2779) — counted as one piece of work, or the whole pool would idle with nobody judging it.
|
|
47
|
+
if (backlog_stalled.needs_triage(result.out)) return TRIAGE_WORK
|
|
44
48
|
|
|
45
49
|
return backlog_stalled.count_ready_tokens(result.out)
|
|
46
50
|
}
|
|
@@ -2,11 +2,15 @@ import { createHash } from 'node:crypto'
|
|
|
2
2
|
import { existsSync, readFileSync, writeFileSync } from 'node:fs'
|
|
3
3
|
import { execaSync } from 'execa'
|
|
4
4
|
|
|
5
|
-
// The workflows that install safe-chain on the runner.
|
|
5
|
+
// The workflows that install safe-chain on the runner. Each carries the release and the installer's
|
|
6
6
|
// SHA-256 as workflow-level env, so one pin per file moves every "Setup safe-chain" step at once
|
|
7
|
-
// (joshuafolkken/kit#2711). A consumer has
|
|
8
|
-
//
|
|
9
|
-
const WORKFLOW_PATHS = [
|
|
7
|
+
// (joshuafolkken/kit#2711, joshuafolkken/kit#2765). A consumer has the first and the last, and
|
|
8
|
+
// `josh sync` rewrites both from kit, so moving them anywhere but kit is harmless and short-lived.
|
|
9
|
+
const WORKFLOW_PATHS = [
|
|
10
|
+
'.github/workflows/ci.yml',
|
|
11
|
+
'templates/workflows/ci.yml',
|
|
12
|
+
'.github/workflows/pr-classification.yml',
|
|
13
|
+
]
|
|
10
14
|
// `[ \t]` rather than `\s`: under the `m` flag `\s*` would run across line ends and swallow the
|
|
11
15
|
// blank line that follows the env block.
|
|
12
16
|
const VERSION_RE = /^(?<key>[ \t]*SAFE_CHAIN_INSTALLER_VERSION:[ \t]*)(?<value>\S+)[ \t]*$/mu
|
|
@@ -50,6 +50,8 @@ const NO_FINISH = 0
|
|
|
50
50
|
// The launch's own result body, which is the only place the harness writes the id it assigns. Read
|
|
51
51
|
// as a derived fact and the body discarded, exactly as `has_failure_line` and `followup_stages` are.
|
|
52
52
|
const LAUNCH_PATTERN = /running in background with ID:\s*([\w-]+)/iu
|
|
53
|
+
// A backgrounded subagent's launch result, which names its id on an `agentId:` line of its own.
|
|
54
|
+
const AGENT_LAUNCH_PATTERN = /^Async agent launched[\s\S]*?\bagentId:\s*([\w-]+)/u
|
|
53
55
|
// The notice the harness writes when a task it took into the background ends
|
|
54
56
|
// (joshuafolkken/kit#1696). It is anchored at the start because the whole content of that line *is*
|
|
55
57
|
// the notice — a body merely quoting one is text, the same hazard `read_id` unquotes for — and it
|
|
@@ -96,6 +98,14 @@ function launch_id(text: string): string {
|
|
|
96
98
|
return LAUNCH_PATTERN.exec(text)?.[1] ?? NO_BACKGROUND
|
|
97
99
|
}
|
|
98
100
|
|
|
101
|
+
// The id of a subagent the harness took into the background (joshuafolkken/kit#2774). Its end is the
|
|
102
|
+
// same `<task-id>` notice a command's is, so `finished_id` pairs it unchanged. It is kept apart from
|
|
103
|
+
// `launch_id` on purpose: a subagent is not a command, and folding it in would open a timeline window
|
|
104
|
+
// that re-stamps the spans a backgrounded gate running beside the review is owed.
|
|
105
|
+
function agent_launch_id(text: string): string {
|
|
106
|
+
return AGENT_LAUNCH_PATTERN.exec(text)?.[1] ?? NO_BACKGROUND
|
|
107
|
+
}
|
|
108
|
+
|
|
99
109
|
// The background run a task-notification line reports the end of, or nothing for every other line —
|
|
100
110
|
// which is nearly all of them, since only one line kind in a transcript carries this notice.
|
|
101
111
|
function finished_id(text: string): string {
|
|
@@ -288,6 +298,7 @@ const time_background = {
|
|
|
288
298
|
NO_BACKGROUND,
|
|
289
299
|
NO_FINISH,
|
|
290
300
|
launch_id,
|
|
301
|
+
agent_launch_id,
|
|
291
302
|
finished_id,
|
|
292
303
|
finished_at,
|
|
293
304
|
read_id,
|
|
@@ -117,6 +117,9 @@ interface Block {
|
|
|
117
117
|
// it is what pairs a launch with the call that later reads its output, so the minutes the command
|
|
118
118
|
// actually ran can be placed on the timeline instead of only the seconds its launch call took.
|
|
119
119
|
background_id: string
|
|
120
|
+
// The id of a subagent this block launched into the background, and `''` for every other block
|
|
121
|
+
// (joshuafolkken/kit#2774) — apart from `background_id` so the timeline windows stay commands only.
|
|
122
|
+
agent_id: string
|
|
120
123
|
}
|
|
121
124
|
|
|
122
125
|
interface TranscriptLine {
|
|
@@ -183,6 +186,7 @@ function to_block(raw: z.infer<typeof BLOCK_SCHEMA>): Block {
|
|
|
183
186
|
error_text: is_error === true ? text.slice(0, ERROR_TEXT_LIMIT) : '',
|
|
184
187
|
refusal_guard: is_error === true ? guard_from_refusal(text) : '',
|
|
185
188
|
background_id: time_background.launch_id(text),
|
|
189
|
+
agent_id: time_background.agent_launch_id(text),
|
|
186
190
|
}
|
|
187
191
|
}
|
|
188
192
|
|
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
# Written by `josh init` for a static project (joshuafolkken/kit#2693). kit's CLI runs on tsx, which
|
|
2
2
|
# depends on esbuild, and pnpm fails an install whose dependencies carry an unapproved build script.
|
|
3
|
+
# unrs-resolver arrives through kit's optional ESLint import peers (joshuafolkken/kit#2785).
|
|
3
4
|
allowBuilds:
|
|
4
5
|
esbuild: true
|
|
6
|
+
unrs-resolver: true
|