@joshuafolkken/kit 1.1435.0 → 1.1455.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-handoff-report.md +1 -1
- package/.claude/skills/workflow-commands/backlogrun-park.md +2 -0
- package/.claude/skills/workflow-commands/backlogrun-steps.md +9 -7
- package/.claude/skills/workflow-commands/backlogrun.md +2 -2
- package/.claude/skills/workflow-commands/fullrun-steps.md +1 -1
- package/.claude/skills/workflow-commands/halfrun.md +3 -2
- package/.claude/skills/workflow-commands/kickoff.md +12 -13
- package/.claude/skills/workflow-commands/observation-filing.md +1 -1
- package/.claude/skills/workflow-commands/prerequisite.md +6 -0
- package/.claude/skills/workflow-commands/prrun.md +2 -1
- package/.claude/skills/workflow-commands/retrospective.md +5 -5
- package/.claude/skills/workflow-commands/split-assessment.md +2 -2
- package/CLAUDE.md +1 -1
- package/README.md +10 -19
- package/dist/CLAUDE.md +1 -1
- package/dist/hooks/batch-guard.js +6 -6
- package/dist/hooks/{chunk-3LE6VLSR.js → chunk-3C4XRYG6.js} +18 -5
- package/dist/hooks/{chunk-7KMN5QB5.js → chunk-5H4SSDSD.js} +3 -3
- package/dist/hooks/{chunk-YZXYOAZ3.js → chunk-7WQBPLUN.js} +11 -5
- package/dist/hooks/{chunk-2G5F2YCD.js → chunk-D5QBTOCZ.js} +3 -3
- package/dist/hooks/{chunk-MU4LJFIV.js → chunk-GPLGZ6IC.js} +3 -3
- package/dist/hooks/{chunk-MSYSUSY6.js → chunk-HFGWYGUV.js} +5 -5
- package/dist/hooks/{chunk-JXQBK323.js → chunk-HHI6FO3R.js} +132 -105
- package/dist/hooks/{chunk-XER6SSIB.js → chunk-LIAFCXL7.js} +17 -7
- package/dist/hooks/{chunk-4BHUPFKF.js → chunk-OLMO72ON.js} +1 -1
- package/dist/hooks/{chunk-EMB55BDF.js → chunk-TBJFTSUY.js} +2 -2
- package/dist/hooks/{chunk-5SJSSCJG.js → chunk-UT7YC3N2.js} +1 -1
- package/dist/hooks/{chunk-QL6QTPWN.js → chunk-WNLVGT7E.js} +3 -3
- package/dist/hooks/{chunk-YW452ZAX.js → chunk-Z4XI7CZS.js} +2 -2
- package/dist/hooks/codex-hook-adapter.js +13 -13
- package/dist/hooks/duplicate-read-guard.js +7 -7
- package/dist/hooks/format-edited.js +3 -3
- package/dist/hooks/inputs.json +229 -226
- package/dist/hooks/investigation-guard.js +7 -7
- package/dist/hooks/pretool-guard.js +12 -12
- package/dist/hooks/session-lang.js +3 -3
- package/dist/hooks/stop-guard.js +64 -11
- package/dist/josh.js +34 -11
- package/package.json +1 -1
- package/prompts/collaboration-workflow/issue-template.md +10 -8
- package/prompts/collaboration-workflow/shell-body.md +1 -1
- package/prompts/review.md +8 -4
- package/scripts/auto-ok/auto-ok-judge-cli.ts +125 -0
- package/scripts/auto-ok/auto-ok-judge-read.ts +95 -0
- package/scripts/auto-ok/auto-ok-judge-write.ts +109 -0
- package/scripts/auto-ok/auto-ok-judge.ts +67 -0
- package/scripts/backlog/backlog-drive-cli.ts +14 -0
- package/scripts/backlog/backlog-drive.ts +22 -3
- package/scripts/document/document-byte-budget.ts +1 -1
- package/scripts/fresh-machine/fresh-machine-patterns.ts +60 -15
- package/scripts/gate/project-checks.ts +5 -2
- package/scripts/gh/git-gh-issue-rest.ts +1 -0
- package/scripts/gh/git-gh-issue.ts +9 -0
- package/scripts/gh/git-pr-ai-review.ts +12 -1
- package/scripts/gh/git-pr-confirmation.ts +11 -7
- package/scripts/init/init-actions.ts +3 -3
- package/scripts/init/init-ai-copy.ts +1 -1
- package/scripts/init/init-basic.ts +1 -1
- package/scripts/init/init-bootstrap.ts +6 -2
- package/scripts/init/init-dry-run-report.ts +150 -0
- package/scripts/init/init-dry-run.ts +189 -0
- package/scripts/init/init-install.ts +8 -1
- package/scripts/init/init.ts +57 -19
- package/scripts/init/kit-setup-state.ts +11 -17
- package/scripts/init/own-formatter.ts +21 -0
- package/scripts/init/project-profile.ts +25 -6
- package/scripts/init/start-dry-run.ts +70 -0
- package/scripts/init/start-plan.ts +8 -3
- package/scripts/init/start-setup-pr.ts +13 -24
- package/scripts/init/start-steps.ts +16 -1
- package/scripts/init/start.ts +38 -18
- package/scripts/init/working-tree.ts +116 -0
- package/scripts/issue/issue-auto-ok.ts +68 -99
- package/scripts/issue/issue-file-cli.ts +17 -17
- package/scripts/issue/issue-file.ts +1 -6
- package/scripts/issue/issue-labels.ts +6 -2
- package/scripts/issue/issue-release-cli.ts +69 -23
- package/scripts/issue/issue-release.ts +3 -1
- package/scripts/josh/josh-commands-ai.ts +11 -0
- package/scripts/josh/josh-commands-issue.ts +3 -3
- package/scripts/josh/josh-commands-project.ts +2 -2
- package/scripts/josh/josh-commands-workflow.ts +1 -1
- package/scripts/josh/process-identity.ts +32 -6
- package/scripts/lane/lane-broker-server.ts +69 -0
- package/scripts/lane/lane-session-broker.ts +37 -14
- package/scripts/notify/confirmation-options.ts +59 -0
- package/scripts/notify/notify-opt-in.ts +33 -0
- package/scripts/notify/telegram-notify.ts +25 -8
- package/scripts/notify/telegram-test-logic.ts +33 -1
- package/scripts/notify/telegram-test.ts +4 -2
- package/scripts/observations/observation-ledger-migrate.ts +3 -15
- package/scripts/release/release-issue-close.ts +133 -0
- package/scripts/release/release-publish.ts +12 -1
- package/scripts/repo/repository-labels.ts +11 -1
- package/scripts/review/comment-ranges.ts +199 -0
- package/scripts/review/review-brief-cli.ts +22 -5
- package/scripts/review/review-code-tree.ts +119 -0
- package/scripts/review/review-round2-cli.ts +16 -5
- package/scripts/review/review-round2.ts +30 -10
- package/scripts/review/review-stamps.ts +22 -1
- package/scripts/rules/shell-body-trigger.ts +3 -2
- package/scripts/rules/stop-rules.ts +2 -1
- package/scripts/run/entry/run-entry-stop.ts +6 -3
- package/scripts/run/run-stop-notify.ts +2 -0
- package/scripts/uninstall/distributed-files.ts +1 -1
|
@@ -9,14 +9,14 @@ import { review_level } from './review-level'
|
|
|
9
9
|
//
|
|
10
10
|
// **The two arms are the two states in which round 2's reason does not arise.** Round 2 exists
|
|
11
11
|
// because round 1's *fix code* is unreviewed, and that is structural. Arm A applies where there is no fix code — the findings closed without an edit — so
|
|
12
|
-
// the premise is absent rather than overridden. Arm B applies where the fix
|
|
13
|
-
//
|
|
14
|
-
//
|
|
12
|
+
// the premise is absent rather than overridden. Arm B applies where the fix leaves what executes
|
|
13
|
+
// unchanged: `review-level.ts`'s inert set, documentation under `docs/` and the root `README.md`,
|
|
14
|
+
// `prompts/`, and source files whose only change is in comments (`review-code-tree.ts`).
|
|
15
15
|
//
|
|
16
|
-
// **
|
|
17
|
-
//
|
|
18
|
-
//
|
|
19
|
-
//
|
|
16
|
+
// **Arm B is wider than the review level's inert set, on purpose** (joshuafolkken/kit#3787). Round 1
|
|
17
|
+
// still reviews a documentation or prompt change in full, and the fix to its findings is still made;
|
|
18
|
+
// what goes unread is only that fix. `.claude/**`, `CLAUDE.md`, tests and configuration stay outside:
|
|
19
|
+
// a test fix can weaken the verification it belongs to, and nothing reads that but the review.
|
|
20
20
|
|
|
21
21
|
type RoundTwoVerdict = 'required' | 'skip'
|
|
22
22
|
|
|
@@ -33,7 +33,11 @@ const NO_SNAPSHOT_REASON =
|
|
|
33
33
|
const EMPTY_DELTA_REASON_PREFIX =
|
|
34
34
|
'the fix delta is empty: round 1 wrote no fix code, so there is no unreviewed fix for a verification pass to read'
|
|
35
35
|
const INERT_DELTA_REASON_PREFIX =
|
|
36
|
-
'every path round 1 fixed
|
|
36
|
+
'every path round 1 fixed leaves what executes unchanged — inert, documentation, a prompt, or a comment-only edit'
|
|
37
|
+
const README_PATH = 'README.md'
|
|
38
|
+
const DOCUMENTATION_PREFIX = 'docs/'
|
|
39
|
+
const MARKDOWN_EXTENSION = '.md'
|
|
40
|
+
const PROMPTS_PREFIX = 'prompts/'
|
|
37
41
|
const BASE_MOVED_REASON =
|
|
38
42
|
'the change base moved since round 1 was recorded, so the two file maps do not cover the same set of paths and their difference is not the fix delta — the round runs rather than reading that difference as one'
|
|
39
43
|
|
|
@@ -51,6 +55,9 @@ interface RoundTwoInput {
|
|
|
51
55
|
is_round_one_closed: boolean
|
|
52
56
|
snapshot: FileMapStamp | undefined
|
|
53
57
|
tree: Record<string, string>
|
|
58
|
+
// The paths whose code round 1's fixes left as it was (`review_code_tree.comment_only`). Absent is
|
|
59
|
+
// none, which is what a run without a code record pays.
|
|
60
|
+
comment_only?: ReadonlyArray<string>
|
|
54
61
|
}
|
|
55
62
|
|
|
56
63
|
interface RoundTwoDecision {
|
|
@@ -86,13 +93,26 @@ function live_reason(deciding: ReadonlyArray<string>): string {
|
|
|
86
93
|
return `round 1's fixes touched paths that execute, instruct or ship: ${path_decision.format_path_list(deciding)}`
|
|
87
94
|
}
|
|
88
95
|
|
|
96
|
+
function is_documentation(relative: string): boolean {
|
|
97
|
+
if (relative === README_PATH || relative.startsWith(PROMPTS_PREFIX)) return true
|
|
98
|
+
|
|
99
|
+
return relative.startsWith(DOCUMENTATION_PREFIX) && relative.endsWith(MARKDOWN_EXTENSION)
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
function is_unexecuted(relative: string, comment_only: ReadonlyArray<string>): boolean {
|
|
103
|
+
if (review_level.is_inert(relative) || is_documentation(relative)) return true
|
|
104
|
+
|
|
105
|
+
return comment_only.includes(relative)
|
|
106
|
+
}
|
|
107
|
+
|
|
89
108
|
function verdict_for_delta(
|
|
90
109
|
delta: ReadonlyArray<string>,
|
|
91
110
|
taken_at: string,
|
|
111
|
+
comment_only: ReadonlyArray<string> = [],
|
|
92
112
|
): { verdict: RoundTwoVerdict; reason: string } {
|
|
93
113
|
if (delta.length === 0) return { verdict: SKIPPED_VERDICT, reason: empty_reason(taken_at) }
|
|
94
114
|
|
|
95
|
-
const deciding = delta.filter((relative) => !
|
|
115
|
+
const deciding = delta.filter((relative) => !is_unexecuted(relative, comment_only))
|
|
96
116
|
|
|
97
117
|
if (deciding.length === 0) return { verdict: SKIPPED_VERDICT, reason: inert_reason(delta) }
|
|
98
118
|
|
|
@@ -110,7 +130,7 @@ function decide(input: RoundTwoInput): RoundTwoDecision {
|
|
|
110
130
|
|
|
111
131
|
const delta = file_map_stamp.changed_since(input.snapshot, input.tree)
|
|
112
132
|
|
|
113
|
-
return { ...verdict_for_delta(delta, input.snapshot.taken_at), delta }
|
|
133
|
+
return { ...verdict_for_delta(delta, input.snapshot.taken_at, input.comment_only), delta }
|
|
114
134
|
}
|
|
115
135
|
|
|
116
136
|
const review_round2 = {
|
|
@@ -13,11 +13,17 @@ const TEST_RELATED_PREFIX = 'josh-test-related-stamp-'
|
|
|
13
13
|
const GATE_PREFIX = 'josh-gate-stamp-'
|
|
14
14
|
const IN_FLIGHT_PREFIX = 'josh-gate-running-'
|
|
15
15
|
const ROUND_ONE_PREFIX = 'josh-review-round1-'
|
|
16
|
+
const ROUND_ONE_CODE_PREFIX = 'josh-review-round1-code-'
|
|
16
17
|
const BRIEFED_PREFIX = 'josh-review-briefed-'
|
|
17
18
|
|
|
18
19
|
const gate_stamp: FileMapStampAccess = file_map_stamp.create(GATE_PREFIX, PROJECT_ROOT)
|
|
19
20
|
const in_flight_stamp: FileMapStampAccess = file_map_stamp.create(IN_FLIGHT_PREFIX, PROJECT_ROOT)
|
|
20
21
|
const round_one_stamp: FileMapStampAccess = file_map_stamp.create(ROUND_ONE_PREFIX, PROJECT_ROOT)
|
|
22
|
+
// Written beside the round-1 snapshot: the comment-free digest of each source file (`review-code-tree.ts`).
|
|
23
|
+
const round_one_code_stamp: FileMapStampAccess = file_map_stamp.create(
|
|
24
|
+
ROUND_ONE_CODE_PREFIX,
|
|
25
|
+
PROJECT_ROOT,
|
|
26
|
+
)
|
|
21
27
|
// Retaken on every brief: the round-1 snapshot is not the tree a later review read.
|
|
22
28
|
const briefed_stamp: FileMapStampAccess = file_map_stamp.create(BRIEFED_PREFIX, PROJECT_ROOT)
|
|
23
29
|
const lint_related_stamp: FileMapStampAccess = file_map_stamp.create(
|
|
@@ -29,24 +35,39 @@ const test_related_stamp: FileMapStampAccess = file_map_stamp.create(
|
|
|
29
35
|
PROJECT_ROOT,
|
|
30
36
|
)
|
|
31
37
|
|
|
38
|
+
// Swallowed: a code record left behind only ever sits beside an older snapshot, which the next
|
|
39
|
+
// round-1 brief replaces together with it.
|
|
40
|
+
function clear_round_one_code(target?: string): void {
|
|
41
|
+
try {
|
|
42
|
+
round_one_code_stamp.remove(target)
|
|
43
|
+
} catch {
|
|
44
|
+
/* the next round-1 brief overwrites it */
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
|
|
32
48
|
// Called only from the end of a merged run: a leftover widens the next delta (safe), while an early
|
|
33
49
|
// removal lets round 1 re-snapshot an already-fixed tree (unsafe). Swallowed — the PR already merged.
|
|
34
|
-
function clear_round_one(target?: string): void {
|
|
50
|
+
function clear_round_one(target?: string, code_target?: string): void {
|
|
35
51
|
try {
|
|
36
52
|
round_one_stamp.remove(target)
|
|
37
53
|
} catch {
|
|
38
54
|
/* a record left behind widens the next round rather than narrowing it */
|
|
39
55
|
}
|
|
56
|
+
|
|
57
|
+
clear_round_one_code(code_target)
|
|
40
58
|
}
|
|
41
59
|
|
|
42
60
|
const review_stamps = {
|
|
43
61
|
briefed_stamp,
|
|
44
62
|
clear_round_one,
|
|
63
|
+
clear_round_one_code,
|
|
45
64
|
GATE_PREFIX,
|
|
46
65
|
gate_stamp,
|
|
47
66
|
IN_FLIGHT_PREFIX,
|
|
48
67
|
in_flight_stamp,
|
|
49
68
|
lint_related_stamp,
|
|
69
|
+
ROUND_ONE_CODE_PREFIX,
|
|
70
|
+
round_one_code_stamp,
|
|
50
71
|
ROUND_ONE_PREFIX,
|
|
51
72
|
round_one_stamp,
|
|
52
73
|
test_related_stamp,
|
|
@@ -6,7 +6,8 @@
|
|
|
6
6
|
|
|
7
7
|
// A body handed to a command as an inline double-quoted argument, in the spellings a run reaches for:
|
|
8
8
|
// `gh`'s field flags (`-f` / `-F` / `--field` / `--raw-field` with `body=`), `gh`'s own `--body`, and
|
|
9
|
-
// `josh`'s `--body` and `--notify-message
|
|
9
|
+
// `josh`'s `--body` and `--notify-message`, and `josh notify`'s `--recommended` / `--alternative` —
|
|
10
|
+
// free text every confirmation carries, naming the very commands a backtick would run. The value is captured so the decision can be made on what
|
|
10
11
|
// the body actually contains rather than on the flag alone.
|
|
11
12
|
//
|
|
12
13
|
// **`-b` is deliberately absent.** It is `gh`'s short `--body`, but it is also `git checkout -b`, and
|
|
@@ -14,7 +15,7 @@
|
|
|
14
15
|
// spellings end in `-` where this pattern needs whitespace or `=`, so `--body-file <path>` and
|
|
15
16
|
// `--notify-message-file <path>` cannot match it.
|
|
16
17
|
const FIELD_FLAGS = '(?:-f|-F|--field|--raw-field)'
|
|
17
|
-
const MESSAGE_FLAGS = '(?:--body|--notify-message)'
|
|
18
|
+
const MESSAGE_FLAGS = '(?:--body|--notify-message|--recommended|--alternative)'
|
|
18
19
|
|
|
19
20
|
// **The opening quote sits on either side of `body=`, and both spellings are the same command.** zsh
|
|
20
21
|
// strips the quotes before `gh` is started, so `-f body="… \`pnpm josh ms\` …"` and
|
|
@@ -103,7 +103,8 @@ const STOP_NOTIFY_REASON =
|
|
|
103
103
|
'⛔ mid-workflow stop notification: this working tree is still held by a run and no `confirmation` ' +
|
|
104
104
|
'Telegram was sent this turn, so a person is being left to wait off-screen without knowing the run ' +
|
|
105
105
|
'paused. `CLAUDE.md` → "Mid-workflow stop notification" requires ' +
|
|
106
|
-
'`pnpm josh notify --task-type confirmation --issue-url "<url>" --body=$\'<reason>\'
|
|
106
|
+
'`pnpm josh notify --task-type confirmation --issue-url "<url>" --body=$\'<reason>\' ' +
|
|
107
|
+
'--recommended "<choice>" --alternative "<choice>"` before any ' +
|
|
107
108
|
'mid-workflow pause. Send it, then end with a one-line confirmation that it was sent — do not repeat ' +
|
|
108
109
|
'your previous reply. This fires only while the hold is held and no notify is on the transcript, and ' +
|
|
109
110
|
'`stop_hook_active` lets a second stop through so a run is never wedged.'
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { issue_cite } from '#scripts/issue/issue-cite'
|
|
2
2
|
import { josh_command } from '#scripts/josh/josh-run'
|
|
3
|
-
import { telegram_notify } from '#scripts/notify/telegram-notify'
|
|
3
|
+
import { telegram_notify, type ConfirmInput } from '#scripts/notify/telegram-notify'
|
|
4
4
|
import { lane_park } from '#scripts/rules/lane-park'
|
|
5
5
|
|
|
6
6
|
// What `run:entry` does on the way out of a stop it decided itself.
|
|
@@ -26,14 +26,15 @@ interface StopNotice {
|
|
|
26
26
|
}
|
|
27
27
|
|
|
28
28
|
interface StopPorts {
|
|
29
|
-
notify: (input:
|
|
29
|
+
notify: (input: ConfirmInput) => Promise<boolean>
|
|
30
30
|
release: (issue_number: string) => Promise<boolean>
|
|
31
31
|
is_parking_child: () => boolean
|
|
32
32
|
}
|
|
33
33
|
|
|
34
34
|
const RECOVERY = 'If this did not arrive, the run:entry output in the session names the stop.'
|
|
35
|
+
const LATER_ALTERNATIVE = 'Leave the Issue for a later session — nothing was changed'
|
|
35
36
|
const NOTIFY_FAILED_NOTE =
|
|
36
|
-
'(the confirmation Telegram could not be sent — send it with `pnpm josh notify --task-type confirmation` before stopping)'
|
|
37
|
+
'(the confirmation Telegram could not be sent — send it with `pnpm josh notify --task-type confirmation --recommended "<choice>"` before stopping)'
|
|
37
38
|
const RELEASED_NOTE = '(hold released by run:entry — the tree is free for the resume)'
|
|
38
39
|
const RELEASE_FAILED_NOTE =
|
|
39
40
|
'(the hold could not be released — run `pnpm josh run:release` before stopping)'
|
|
@@ -78,6 +79,8 @@ async function notify_stop(notice: StopNotice, ports: StopPorts): Promise<void>
|
|
|
78
79
|
issue_title: `${notice.command} ${issue_cite.plain(notice.issue_number)} stopped`,
|
|
79
80
|
body: body_of(notice),
|
|
80
81
|
recovery: RECOVERY,
|
|
82
|
+
recommended: `Run \`${notice.command} ${issue_cite.plain(notice.issue_number)}\` again in a fresh session`,
|
|
83
|
+
alternatives: [LATER_ALTERNATIVE],
|
|
81
84
|
})
|
|
82
85
|
|
|
83
86
|
console.info(is_sent ? lane_park.COMMAND_NOTIFY_MARKER : NOTIFY_FAILED_NOTE)
|
|
@@ -38,6 +38,8 @@ async function announce(notice: StopNotice): Promise<boolean> {
|
|
|
38
38
|
issue_title: STOP_TITLE,
|
|
39
39
|
body: notice.reason,
|
|
40
40
|
recovery: STOP_RECOVERY,
|
|
41
|
+
recommended: 'Resolve the reason above, then run `backlogrun` again',
|
|
42
|
+
alternatives: ['Leave the backlog stopped until a later session'],
|
|
41
43
|
})
|
|
42
44
|
}
|
|
43
45
|
|