@joshuafolkken/kit 1.953.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.
Files changed (100) hide show
  1. package/.claude/skills/workflow-commands/background-commands.md +3 -0
  2. package/.claude/skills/workflow-commands/backlogrun-child.md +75 -120
  3. package/.claude/skills/workflow-commands/backlogrun-lanes.md +78 -115
  4. package/.claude/skills/workflow-commands/backlogrun-progress.md +82 -146
  5. package/.claude/skills/workflow-commands/backlogrun-steps.md +173 -229
  6. package/.claude/skills/workflow-commands/backlogrun.md +3 -1
  7. package/.claude/skills/workflow-commands/followup.md +3 -0
  8. package/.claude/skills/workflow-commands/fullrun.md +3 -4
  9. package/.claude/skills/workflow-commands/observation-filing.md +31 -25
  10. package/.claude/skills/workflow-commands/pre-gate-cut.md +96 -161
  11. package/.github/workflows/pr-classification.yml +12 -1
  12. package/CLAUDE.md +3 -3
  13. package/README.md +5 -3
  14. package/dist/CLAUDE.md +3 -3
  15. package/dist/hooks/batch-guard.js +4 -4
  16. package/dist/hooks/{chunk-OM5VDM6F.js → chunk-2K52BRWV.js} +2 -2
  17. package/dist/hooks/{chunk-Z5TVZ4AP.js → chunk-BNWIOJFU.js} +29 -16
  18. package/dist/hooks/{chunk-NKVLBB65.js → chunk-BVMSPG3M.js} +3 -3
  19. package/dist/hooks/{chunk-IA7WC4NS.js → chunk-CRGJTD6E.js} +1 -1
  20. package/dist/hooks/{chunk-M4U4YNRE.js → chunk-F6OM6SIG.js} +3 -3
  21. package/dist/hooks/{chunk-O3PEDF3M.js → chunk-KQG4776P.js} +1 -1
  22. package/dist/hooks/{chunk-V7RH56HU.js → chunk-NXL3SBXY.js} +2 -2
  23. package/dist/hooks/{chunk-452OKRQX.js → chunk-WWTMHHZU.js} +120 -18
  24. package/dist/hooks/{chunk-EYFJ74MJ.js → chunk-ZPLXLJ7O.js} +21 -7
  25. package/dist/hooks/{chunk-IBFC25IX.js → chunk-ZYYSBNR6.js} +4 -4
  26. package/dist/hooks/codex-hook-adapter.js +10 -10
  27. package/dist/hooks/duplicate-read-guard.js +5 -5
  28. package/dist/hooks/format-edited.js +2 -2
  29. package/dist/hooks/investigation-guard.js +5 -5
  30. package/dist/hooks/pretool-guard.js +9 -9
  31. package/dist/hooks/stop-guard.js +206 -176
  32. package/dist/josh.js +14 -6
  33. package/package.json +3 -3
  34. package/prompts/collaboration-workflow/wip-cap.md +1 -1
  35. package/scripts/backlog/backlog-arrival.ts +11 -1
  36. package/scripts/backlog/backlog-budget.ts +25 -8
  37. package/scripts/backlog/backlog-drive-cli.ts +5 -1
  38. package/scripts/backlog/backlog-drive-retrospective.ts +20 -0
  39. package/scripts/backlog/backlog-drive.ts +7 -3
  40. package/scripts/backlog/backlog-next.ts +45 -2
  41. package/scripts/backlog/backlog-offer.ts +1 -0
  42. package/scripts/backlog/backlog-plan-cli.ts +66 -27
  43. package/scripts/backlog/backlog-plan.ts +30 -2
  44. package/scripts/backlog/backlog-stalled.ts +9 -0
  45. package/scripts/backlog/backlog-waves.ts +169 -0
  46. package/scripts/document/document-byte-budget.ts +1 -1
  47. package/scripts/document/entry-read-budget.ts +7 -5
  48. package/scripts/epic/epic-lane-offer.ts +69 -4
  49. package/scripts/epic/epic-next.ts +2 -2
  50. package/scripts/epic/epic-solo.ts +147 -0
  51. package/scripts/epic/epic-triage.ts +47 -0
  52. package/scripts/gate/hook-gate-reuse.ts +7 -8
  53. package/scripts/git/git-closes-keyword.ts +15 -0
  54. package/scripts/git/git-followup-flush.ts +5 -6
  55. package/scripts/git/git-followup-issue-close.ts +75 -0
  56. package/scripts/git/git-pr-followup-wrapup.ts +36 -6
  57. package/scripts/git/git-pr-followup.ts +9 -16
  58. package/scripts/git/git-staging.ts +33 -20
  59. package/scripts/git/issue-labels.ts +42 -0
  60. package/scripts/hooks/stop-guard.ts +1 -0
  61. package/scripts/init/init-install.ts +64 -0
  62. package/scripts/init/init.ts +38 -11
  63. package/scripts/init/project-profile.ts +9 -5
  64. package/scripts/init/start-plan.ts +145 -0
  65. package/scripts/init/start-prompt.ts +55 -0
  66. package/scripts/init/start-steps.ts +167 -0
  67. package/scripts/init/start.ts +92 -16
  68. package/scripts/issue/issue-closing-pr.ts +93 -0
  69. package/scripts/josh/__snapshots__/josh-command-map.test.ts.snap +4 -2
  70. package/scripts/josh/josh-commands-project.ts +11 -3
  71. package/scripts/josh/josh-commands-workflow.ts +3 -3
  72. package/scripts/observations/observation-ledger-prepare.ts +31 -0
  73. package/scripts/observations/observation-ledger.ts +5 -5
  74. package/scripts/observations/observations-flush.ts +11 -30
  75. package/scripts/rules/implementation-cut.ts +49 -16
  76. package/scripts/rules/lane-background.ts +25 -5
  77. package/scripts/rules/stop-rules.ts +16 -4
  78. package/scripts/run/run-carry-cli.ts +21 -0
  79. package/scripts/run/run-cut-cli.ts +92 -110
  80. package/scripts/run/run-cut-report.ts +167 -0
  81. package/scripts/run/run-cut.ts +23 -7
  82. package/scripts/run/run-entry-cli.ts +36 -2
  83. package/scripts/run/run-headless.ts +1 -0
  84. package/scripts/run/run-hold-cli.ts +36 -15
  85. package/scripts/run/run-hold.ts +15 -4
  86. package/scripts/run/run-merge-cli.ts +28 -14
  87. package/scripts/run/run-merge-steps.ts +38 -2
  88. package/scripts/run/run-merge.ts +41 -3
  89. package/scripts/run/run-prep-cli.ts +8 -2
  90. package/scripts/run/run-prep-locate.ts +165 -0
  91. package/scripts/run/run-prep.ts +12 -1
  92. package/scripts/run/run-retrospective.ts +34 -0
  93. package/scripts/run/run-step-cli.ts +19 -5
  94. package/scripts/run/run-step.ts +31 -19
  95. package/scripts/run/run-tail-cli.ts +4 -2
  96. package/scripts/run/run-wake-work.ts +4 -0
  97. package/scripts/safe-chain/ci-installer-pin.ts +8 -4
  98. package/scripts/time-runtime/time-background.ts +11 -0
  99. package/scripts/time-runtime/time-transcript-line.ts +4 -0
  100. package/templates/pnpm-workspace.static.yaml +2 -0
@@ -1,25 +1,25 @@
1
1
  #!/usr/bin/env tsx
2
2
  import {
3
3
  pretool_guard
4
- } from "./chunk-Z5TVZ4AP.js";
5
- import "./chunk-452OKRQX.js";
4
+ } from "./chunk-BNWIOJFU.js";
5
+ import "./chunk-WWTMHHZU.js";
6
6
  import {
7
7
  build_envelope,
8
8
  compose_context,
9
9
  format_edited_payload
10
- } from "./chunk-IA7WC4NS.js";
10
+ } from "./chunk-CRGJTD6E.js";
11
11
  import "./chunk-3V37IID6.js";
12
- import "./chunk-V7RH56HU.js";
13
- import "./chunk-NKVLBB65.js";
14
- import "./chunk-IBFC25IX.js";
15
- import "./chunk-M4U4YNRE.js";
16
- import "./chunk-OM5VDM6F.js";
12
+ import "./chunk-NXL3SBXY.js";
13
+ import "./chunk-BVMSPG3M.js";
14
+ import "./chunk-ZYYSBNR6.js";
15
+ import "./chunk-F6OM6SIG.js";
16
+ import "./chunk-2K52BRWV.js";
17
17
  import {
18
18
  hook_decision
19
- } from "./chunk-O3PEDF3M.js";
19
+ } from "./chunk-KQG4776P.js";
20
20
  import {
21
21
  time_density_hook
22
- } from "./chunk-EYFJ74MJ.js";
22
+ } from "./chunk-ZPLXLJ7O.js";
23
23
  import "./chunk-WPALEIRX.js";
24
24
  import "./chunk-RKUAD3I6.js";
25
25
 
@@ -9,11 +9,11 @@ import {
9
9
  is_enabled,
10
10
  notice_refusal_path,
11
11
  refusal_path
12
- } from "./chunk-IBFC25IX.js";
13
- import "./chunk-M4U4YNRE.js";
14
- import "./chunk-OM5VDM6F.js";
15
- import "./chunk-O3PEDF3M.js";
16
- import "./chunk-EYFJ74MJ.js";
12
+ } from "./chunk-ZYYSBNR6.js";
13
+ import "./chunk-F6OM6SIG.js";
14
+ import "./chunk-2K52BRWV.js";
15
+ import "./chunk-KQG4776P.js";
16
+ import "./chunk-ZPLXLJ7O.js";
17
17
  import "./chunk-WPALEIRX.js";
18
18
  import "./chunk-RKUAD3I6.js";
19
19
  export {
@@ -15,9 +15,9 @@ import {
15
15
  relative_to_root,
16
16
  resolve_invocations,
17
17
  select_invocation
18
- } from "./chunk-IA7WC4NS.js";
18
+ } from "./chunk-CRGJTD6E.js";
19
19
  import "./chunk-3V37IID6.js";
20
- import "./chunk-EYFJ74MJ.js";
20
+ import "./chunk-ZPLXLJ7O.js";
21
21
  import "./chunk-RKUAD3I6.js";
22
22
  export {
23
23
  ESLINT_DAEMON,
@@ -10,11 +10,11 @@ import {
10
10
  is_enabled,
11
11
  load_environment_file,
12
12
  refusal_path
13
- } from "./chunk-NKVLBB65.js";
14
- import "./chunk-M4U4YNRE.js";
15
- import "./chunk-OM5VDM6F.js";
16
- import "./chunk-O3PEDF3M.js";
17
- import "./chunk-EYFJ74MJ.js";
13
+ } from "./chunk-BVMSPG3M.js";
14
+ import "./chunk-F6OM6SIG.js";
15
+ import "./chunk-2K52BRWV.js";
16
+ import "./chunk-KQG4776P.js";
17
+ import "./chunk-ZPLXLJ7O.js";
18
18
  import "./chunk-WPALEIRX.js";
19
19
  import "./chunk-RKUAD3I6.js";
20
20
  export {
@@ -3,16 +3,16 @@ import {
3
3
  pretool_guard,
4
4
  pretool_outcome,
5
5
  pretool_outcome_async
6
- } from "./chunk-Z5TVZ4AP.js";
7
- import "./chunk-452OKRQX.js";
6
+ } from "./chunk-BNWIOJFU.js";
7
+ import "./chunk-WWTMHHZU.js";
8
8
  import "./chunk-3V37IID6.js";
9
- import "./chunk-V7RH56HU.js";
10
- import "./chunk-NKVLBB65.js";
11
- import "./chunk-IBFC25IX.js";
12
- import "./chunk-M4U4YNRE.js";
13
- import "./chunk-OM5VDM6F.js";
14
- import "./chunk-O3PEDF3M.js";
15
- import "./chunk-EYFJ74MJ.js";
9
+ import "./chunk-NXL3SBXY.js";
10
+ import "./chunk-BVMSPG3M.js";
11
+ import "./chunk-ZYYSBNR6.js";
12
+ import "./chunk-F6OM6SIG.js";
13
+ import "./chunk-2K52BRWV.js";
14
+ import "./chunk-KQG4776P.js";
15
+ import "./chunk-ZPLXLJ7O.js";
16
16
  import "./chunk-WPALEIRX.js";
17
17
  import "./chunk-RKUAD3I6.js";
18
18
  export {
@@ -1,10 +1,13 @@
1
1
  #!/usr/bin/env tsx
2
2
  import {
3
+ RUN_LANE_LABEL,
4
+ RUN_SOLO_LABEL,
3
5
  agent_role_profile,
4
6
  backlog_budget,
5
7
  filing_cap,
6
8
  git_gh_api_path,
7
9
  git_gh_exec,
10
+ has_label_name,
8
11
  lane_background,
9
12
  lane_child_invocation,
10
13
  lane_park,
@@ -16,20 +19,20 @@ import {
16
19
  run_hold,
17
20
  run_invocation,
18
21
  run_watcher_guard
19
- } from "./chunk-452OKRQX.js";
22
+ } from "./chunk-WWTMHHZU.js";
20
23
  import {
21
24
  session_language
22
25
  } from "./chunk-6V5OPIUU.js";
23
26
  import {
24
27
  hook_decision,
25
28
  lane_child_marker
26
- } from "./chunk-O3PEDF3M.js";
29
+ } from "./chunk-KQG4776P.js";
27
30
  import {
28
31
  PROJECT_ROOT,
29
32
  stamp_file,
30
33
  time_density_hook,
31
34
  time_hook_transcript
32
- } from "./chunk-EYFJ74MJ.js";
35
+ } from "./chunk-ZPLXLJ7O.js";
33
36
  import "./chunk-WPALEIRX.js";
34
37
  import "./chunk-RKUAD3I6.js";
35
38
 
@@ -208,9 +211,181 @@ var run_headless = {
208
211
  environment,
209
212
  is_backlog_parent,
210
213
  is_headless,
214
+ is_owned_here,
211
215
  must_keep_waiting
212
216
  };
213
217
 
218
+ // scripts/issue/issue-cite.ts
219
+ var GITHUB_URL_PREFIX = "https://github.com/";
220
+ var ISSUE_PATH = "/issues/";
221
+ var SUMMARY_SEPARATOR = " \u2014 ";
222
+ var REPO_QUALIFIED = /^([^\s/#]+\/[^\s/#]+)#(\d+)$/u;
223
+ var BARE_NUMBER = /^#?(\d+)$/u;
224
+ function issue_url(slug, number) {
225
+ return `${GITHUB_URL_PREFIX}${slug}${ISSUE_PATH}${number}`;
226
+ }
227
+ function citation_line(slug, number, summary) {
228
+ return `[#${number}](${issue_url(slug, number)})${SUMMARY_SEPARATOR}${summary}`;
229
+ }
230
+ function qualified_target(token) {
231
+ const match = REPO_QUALIFIED.exec(token);
232
+ if (match === null) return void 0;
233
+ const [, repo = "", number = ""] = match;
234
+ return { number, repo };
235
+ }
236
+ function bare_target(token, default_repo) {
237
+ const match = BARE_NUMBER.exec(token);
238
+ if (match === null) return void 0;
239
+ const [, number = ""] = match;
240
+ return { number, repo: default_repo };
241
+ }
242
+ function parse_target(token, default_repo) {
243
+ return qualified_target(token) ?? bare_target(token, default_repo);
244
+ }
245
+ function label(target) {
246
+ return target.repo === void 0 ? `#${target.number}` : `${target.repo}#${target.number}`;
247
+ }
248
+ function missing_line(target) {
249
+ return `\u2716 ${label(target)}: issue does not resolve \u2014 check the number and the repository`;
250
+ }
251
+ function unreadable_line(target) {
252
+ return `\u2716 ${label(target)}: could not be read \u2014 a rate limit, expired auth, or a dropped connection`;
253
+ }
254
+ function no_repo_line(target) {
255
+ return `\u2716 ${label(target)}: could not read this repository \u2014 check gh auth and the origin remote`;
256
+ }
257
+ var issue_cite = {
258
+ SUMMARY_SEPARATOR,
259
+ citation_line,
260
+ issue_url,
261
+ label,
262
+ missing_line,
263
+ no_repo_line,
264
+ parse_target,
265
+ unreadable_line
266
+ };
267
+
268
+ // scripts/rules/issue-citation.ts
269
+ var ISSUE_NUMBER = /#\d+/gu;
270
+ var REFERENCE_BOUNDARY = /[\s#()[\]<>|]/u;
271
+ var OWNER_REPO = /^[^\s/#]+\/[^\s/#]+$/u;
272
+ var MARKDOWN_LINK = /\[([^[\]]*)\]\([^()]*\)/gu;
273
+ var FENCE_MARKER = /^\s*(?:`{3,}|~{3,})/u;
274
+ var QUOTE_LINE = /^\s*>/u;
275
+ var PR_PREFIX = /\b(?:pr|pull request)\s*$/iu;
276
+ var BACKTICK_PAIR = 2;
277
+ function text_span_covers(link, index) {
278
+ const start = (link.index ?? 0) + 1;
279
+ const end = start + (link[1] ?? "").length;
280
+ return index >= start && index < end;
281
+ }
282
+ function is_linked_at(line, index) {
283
+ for (const link of line.matchAll(MARKDOWN_LINK)) {
284
+ if (text_span_covers(link, index)) return true;
285
+ }
286
+ return false;
287
+ }
288
+ function repo_prefix(message2, hash_index) {
289
+ let start = hash_index;
290
+ while (start > 0 && !REFERENCE_BOUNDARY.test(message2.charAt(start - 1))) start -= 1;
291
+ const token = message2.slice(start, hash_index);
292
+ return OWNER_REPO.test(token) ? token : "";
293
+ }
294
+ function is_inline_code_at(line, index) {
295
+ const backticks = line.slice(0, index).split("`").length - 1;
296
+ return backticks % BACKTICK_PAIR === 1;
297
+ }
298
+ function is_bare_at(line, index) {
299
+ if (is_linked_at(line, index) || is_inline_code_at(line, index)) return false;
300
+ return !PR_PREFIX.test(line.slice(0, index));
301
+ }
302
+ function line_references(line) {
303
+ const references = [];
304
+ for (const match of line.matchAll(ISSUE_NUMBER)) {
305
+ const { index } = match;
306
+ if (is_bare_at(line, index)) {
307
+ references.push({ index, prefix: repo_prefix(line, index), text: match[0] });
308
+ }
309
+ }
310
+ return references;
311
+ }
312
+ function collect_line_references(line, found) {
313
+ for (const reference of line_references(line)) found.push(`${reference.prefix}${reference.text}`);
314
+ }
315
+ function lines_outside_fences(message2) {
316
+ const lines = [];
317
+ let is_in_fence = false;
318
+ for (const line of message2.split("\n")) {
319
+ if (FENCE_MARKER.test(line)) is_in_fence = !is_in_fence;
320
+ else if (!is_in_fence) lines.push(line);
321
+ }
322
+ return lines;
323
+ }
324
+ function prose_lines(message2) {
325
+ return lines_outside_fences(message2).filter((line) => !QUOTE_LINE.test(line));
326
+ }
327
+ function bare_references(message2) {
328
+ const found = [];
329
+ for (const line of prose_lines(message2)) collect_line_references(line, found);
330
+ return [...new Set(found)];
331
+ }
332
+ function has_bare_reference(message2) {
333
+ return bare_references(message2).length > 0;
334
+ }
335
+ function cite_argument(reference) {
336
+ return reference.startsWith("#") ? reference.slice(1) : reference;
337
+ }
338
+ function cite_arguments(references) {
339
+ return references.map((reference) => cite_argument(reference));
340
+ }
341
+ function reference_link(reference, default_slug) {
342
+ const slug = reference.prefix === "" ? default_slug : reference.prefix;
343
+ const number = reference.text.slice(1);
344
+ return `[${reference.prefix}${reference.text}](${issue_cite.issue_url(slug, number)})`;
345
+ }
346
+ function linkify_line(line, slug) {
347
+ let result = "";
348
+ let cursor = 0;
349
+ for (const reference of line_references(line)) {
350
+ const start = reference.index - reference.prefix.length;
351
+ result += line.slice(cursor, start) + reference_link(reference, slug);
352
+ cursor = reference.index + reference.text.length;
353
+ }
354
+ return result + line.slice(cursor);
355
+ }
356
+ function rewrite_line(line, slug, is_in_fence) {
357
+ if (is_in_fence || FENCE_MARKER.test(line) || QUOTE_LINE.test(line)) return line;
358
+ return linkify_line(line, slug);
359
+ }
360
+ function linkify(message2, slug) {
361
+ const out = [];
362
+ let is_in_fence = false;
363
+ for (const line of message2.split("\n")) {
364
+ if (FENCE_MARKER.test(line)) is_in_fence = !is_in_fence;
365
+ out.push(rewrite_line(line, slug, is_in_fence));
366
+ }
367
+ return out.join("\n");
368
+ }
369
+ var issue_citation = { bare_references, cite_arguments, has_bare_reference, linkify, prose_lines };
370
+
371
+ // scripts/epic/epic-triage.ts
372
+ var TRIAGE_VERDICT = "triage";
373
+ function is_triaged(child) {
374
+ const { labels } = child;
375
+ return has_label_name(labels, RUN_SOLO_LABEL) || has_label_name(labels, RUN_LANE_LABEL);
376
+ }
377
+ function untriaged(children) {
378
+ return children.filter((child) => !is_triaged(child));
379
+ }
380
+ function message(children, repo) {
381
+ const numbers = children.map((child) => `#${String(child.number)}`).join(", ");
382
+ return issue_citation.linkify(
383
+ `Untriaged: ${numbers} carry neither \`${RUN_SOLO_LABEL}\` nor \`${RUN_LANE_LABEL}\` in ${repo}, so nothing is offered until each is judged. Read each one, record its blocked-by order and one of the two labels with a comment saying why, then ask again.`,
384
+ repo
385
+ );
386
+ }
387
+ var epic_triage = { TRIAGE_VERDICT, is_triaged, message, untriaged };
388
+
214
389
  // scripts/run/run-event-stream.ts
215
390
  import { z } from "zod";
216
391
  var EVENT_PREFIX = "josh-run-events-";
@@ -392,6 +567,9 @@ function ready_tokens(out) {
392
567
  function count_ready_tokens(out) {
393
568
  return ready_tokens(out).length;
394
569
  }
570
+ function needs_triage(out) {
571
+ return out.split("\n").some((token) => token.trim() === epic_triage.TRIAGE_VERDICT);
572
+ }
395
573
  var backlog_stalled = {
396
574
  STALLED,
397
575
  OK,
@@ -402,6 +580,7 @@ var backlog_stalled = {
402
580
  describe,
403
581
  dispatch_age_ms,
404
582
  is_stalled,
583
+ needs_triage,
405
584
  ready_tokens
406
585
  };
407
586
 
@@ -862,159 +1041,6 @@ var backlog_stalled_detect = { DEFAULT_PORTS: DEFAULT_PORTS2, detect_and_report,
862
1041
  // scripts/rules/stop-rules.ts
863
1042
  import { z as z3 } from "zod";
864
1043
 
865
- // scripts/issue/issue-cite.ts
866
- var GITHUB_URL_PREFIX = "https://github.com/";
867
- var ISSUE_PATH = "/issues/";
868
- var SUMMARY_SEPARATOR = " \u2014 ";
869
- var REPO_QUALIFIED = /^([^\s/#]+\/[^\s/#]+)#(\d+)$/u;
870
- var BARE_NUMBER = /^#?(\d+)$/u;
871
- function issue_url(slug, number) {
872
- return `${GITHUB_URL_PREFIX}${slug}${ISSUE_PATH}${number}`;
873
- }
874
- function citation_line(slug, number, summary) {
875
- return `[#${number}](${issue_url(slug, number)})${SUMMARY_SEPARATOR}${summary}`;
876
- }
877
- function qualified_target(token) {
878
- const match = REPO_QUALIFIED.exec(token);
879
- if (match === null) return void 0;
880
- const [, repo = "", number = ""] = match;
881
- return { number, repo };
882
- }
883
- function bare_target(token, default_repo) {
884
- const match = BARE_NUMBER.exec(token);
885
- if (match === null) return void 0;
886
- const [, number = ""] = match;
887
- return { number, repo: default_repo };
888
- }
889
- function parse_target(token, default_repo) {
890
- return qualified_target(token) ?? bare_target(token, default_repo);
891
- }
892
- function label(target) {
893
- return target.repo === void 0 ? `#${target.number}` : `${target.repo}#${target.number}`;
894
- }
895
- function missing_line(target) {
896
- return `\u2716 ${label(target)}: issue does not resolve \u2014 check the number and the repository`;
897
- }
898
- function unreadable_line(target) {
899
- return `\u2716 ${label(target)}: could not be read \u2014 a rate limit, expired auth, or a dropped connection`;
900
- }
901
- function no_repo_line(target) {
902
- return `\u2716 ${label(target)}: could not read this repository \u2014 check gh auth and the origin remote`;
903
- }
904
- var issue_cite = {
905
- SUMMARY_SEPARATOR,
906
- citation_line,
907
- issue_url,
908
- label,
909
- missing_line,
910
- no_repo_line,
911
- parse_target,
912
- unreadable_line
913
- };
914
-
915
- // scripts/rules/issue-citation.ts
916
- var ISSUE_NUMBER = /#\d+/gu;
917
- var REFERENCE_BOUNDARY = /[\s#()[\]<>|]/u;
918
- var OWNER_REPO = /^[^\s/#]+\/[^\s/#]+$/u;
919
- var MARKDOWN_LINK = /\[([^[\]]*)\]\([^()]*\)/gu;
920
- var FENCE_MARKER = /^\s*(?:`{3,}|~{3,})/u;
921
- var QUOTE_LINE = /^\s*>/u;
922
- var PR_PREFIX = /\b(?:pr|pull request)\s*$/iu;
923
- var BACKTICK_PAIR = 2;
924
- function text_span_covers(link, index) {
925
- const start = (link.index ?? 0) + 1;
926
- const end = start + (link[1] ?? "").length;
927
- return index >= start && index < end;
928
- }
929
- function is_linked_at(line, index) {
930
- for (const link of line.matchAll(MARKDOWN_LINK)) {
931
- if (text_span_covers(link, index)) return true;
932
- }
933
- return false;
934
- }
935
- function repo_prefix(message, hash_index) {
936
- let start = hash_index;
937
- while (start > 0 && !REFERENCE_BOUNDARY.test(message.charAt(start - 1))) start -= 1;
938
- const token = message.slice(start, hash_index);
939
- return OWNER_REPO.test(token) ? token : "";
940
- }
941
- function is_inline_code_at(line, index) {
942
- const backticks = line.slice(0, index).split("`").length - 1;
943
- return backticks % BACKTICK_PAIR === 1;
944
- }
945
- function is_bare_at(line, index) {
946
- if (is_linked_at(line, index) || is_inline_code_at(line, index)) return false;
947
- return !PR_PREFIX.test(line.slice(0, index));
948
- }
949
- function line_references(line) {
950
- const references = [];
951
- for (const match of line.matchAll(ISSUE_NUMBER)) {
952
- const { index } = match;
953
- if (is_bare_at(line, index)) {
954
- references.push({ index, prefix: repo_prefix(line, index), text: match[0] });
955
- }
956
- }
957
- return references;
958
- }
959
- function collect_line_references(line, found) {
960
- for (const reference of line_references(line)) found.push(`${reference.prefix}${reference.text}`);
961
- }
962
- function lines_outside_fences(message) {
963
- const lines = [];
964
- let is_in_fence = false;
965
- for (const line of message.split("\n")) {
966
- if (FENCE_MARKER.test(line)) is_in_fence = !is_in_fence;
967
- else if (!is_in_fence) lines.push(line);
968
- }
969
- return lines;
970
- }
971
- function prose_lines(message) {
972
- return lines_outside_fences(message).filter((line) => !QUOTE_LINE.test(line));
973
- }
974
- function bare_references(message) {
975
- const found = [];
976
- for (const line of prose_lines(message)) collect_line_references(line, found);
977
- return [...new Set(found)];
978
- }
979
- function has_bare_reference(message) {
980
- return bare_references(message).length > 0;
981
- }
982
- function cite_argument(reference) {
983
- return reference.startsWith("#") ? reference.slice(1) : reference;
984
- }
985
- function cite_arguments(references) {
986
- return references.map((reference) => cite_argument(reference));
987
- }
988
- function reference_link(reference, default_slug) {
989
- const slug = reference.prefix === "" ? default_slug : reference.prefix;
990
- const number = reference.text.slice(1);
991
- return `[${reference.prefix}${reference.text}](${issue_cite.issue_url(slug, number)})`;
992
- }
993
- function linkify_line(line, slug) {
994
- let result = "";
995
- let cursor = 0;
996
- for (const reference of line_references(line)) {
997
- const start = reference.index - reference.prefix.length;
998
- result += line.slice(cursor, start) + reference_link(reference, slug);
999
- cursor = reference.index + reference.text.length;
1000
- }
1001
- return result + line.slice(cursor);
1002
- }
1003
- function rewrite_line(line, slug, is_in_fence) {
1004
- if (is_in_fence || FENCE_MARKER.test(line) || QUOTE_LINE.test(line)) return line;
1005
- return linkify_line(line, slug);
1006
- }
1007
- function linkify(message, slug) {
1008
- const out = [];
1009
- let is_in_fence = false;
1010
- for (const line of message.split("\n")) {
1011
- if (FENCE_MARKER.test(line)) is_in_fence = !is_in_fence;
1012
- out.push(rewrite_line(line, slug, is_in_fence));
1013
- }
1014
- return out.join("\n");
1015
- }
1016
- var issue_citation = { bare_references, cite_arguments, has_bare_reference, linkify, prose_lines };
1017
-
1018
1044
  // scripts/rules/filing-offer.ts
1019
1045
  var OFFER_PATTERNS = [
1020
1046
  /起票(?:して)?(?:も)?(?:よければ|よろしければ|よいですか|良いですか|よろしいですか|いいですか)/u,
@@ -1029,21 +1055,21 @@ var OWNER_SEPARATOR = "/";
1029
1055
  function is_offer_line(line) {
1030
1056
  return OFFER_PATTERNS.some((pattern) => pattern.test(line));
1031
1057
  }
1032
- function offers_filing(message) {
1033
- return issue_citation.prose_lines(message).some((line) => is_offer_line(line));
1058
+ function offers_filing(message2) {
1059
+ return issue_citation.prose_lines(message2).some((line) => is_offer_line(line));
1034
1060
  }
1035
- function url_owners(message) {
1036
- return [...message.matchAll(GITHUB_URL_OWNER)].map((match) => match[1] ?? "");
1061
+ function url_owners(message2) {
1062
+ return [...message2.matchAll(GITHUB_URL_OWNER)].map((match) => match[1] ?? "");
1037
1063
  }
1038
- function qualified_owners(message) {
1039
- return issue_citation.bare_references(message).filter((reference) => reference.includes(OWNER_SEPARATOR)).map((reference) => reference.split(OWNER_SEPARATOR)[0] ?? "");
1064
+ function qualified_owners(message2) {
1065
+ return issue_citation.bare_references(message2).filter((reference) => reference.includes(OWNER_SEPARATOR)).map((reference) => reference.split(OWNER_SEPARATOR)[0] ?? "");
1040
1066
  }
1041
- function mentioned_owners(message) {
1042
- return [...url_owners(message), ...qualified_owners(message)];
1067
+ function mentioned_owners(message2) {
1068
+ return [...url_owners(message2), ...qualified_owners(message2)];
1043
1069
  }
1044
- function is_first_party_target(message, session_owner) {
1070
+ function is_first_party_target(message2, session_owner) {
1045
1071
  if (session_owner === void 0) return false;
1046
- return mentioned_owners(message).every(
1072
+ return mentioned_owners(message2).every(
1047
1073
  (owner) => repo_party.classify(session_owner, owner) === repo_party.FIRST_PARTY
1048
1074
  );
1049
1075
  }
@@ -1085,8 +1111,8 @@ function letter_pattern(script) {
1085
1111
  return void 0;
1086
1112
  }
1087
1113
  }
1088
- function prose_of(message) {
1089
- let text2 = message;
1114
+ function prose_of(message2) {
1115
+ let text2 = message2;
1090
1116
  for (const pattern of CODE_PATTERNS) text2 = text2.replaceAll(pattern, " ");
1091
1117
  return text2.replaceAll(ASCII_RUN_PATTERN, (run) => run.includes("/") ? " " : run);
1092
1118
  }
@@ -1107,12 +1133,12 @@ function matches_share(prose, script, pattern) {
1107
1133
  const matched = lines.filter((line) => is_line_in_script(line, script, pattern));
1108
1134
  return matched.length / lines.length;
1109
1135
  }
1110
- function is_mismatch(message, lang) {
1136
+ function is_mismatch(message2, lang) {
1111
1137
  const script = script_of(lang);
1112
1138
  if (script === void 0) return false;
1113
1139
  const pattern = letter_pattern(script);
1114
1140
  if (pattern === void 0) return false;
1115
- const prose = prose_of(message);
1141
+ const prose = prose_of(message2);
1116
1142
  if (letter_count(prose) < MIN_PROSE_LETTERS) return false;
1117
1143
  return matches_share(prose, script, pattern) < MATCH_SHARE_FLOOR;
1118
1144
  }
@@ -1144,8 +1170,8 @@ var HEADLESS_WAIT_REASON = `${HEADLESS_WAIT_MARKER}${HEADLESS_WAIT_BODY}`;
1144
1170
  var INDEX_COMMAND_PATTERN = /git (?:add|stage|rm --cached|restore --staged|restore -S)\b/u;
1145
1171
  var PERMISSION_PATTERN = /許可|承認|permission|approve|approval|authorization/iu;
1146
1172
  var LANE_INDEX_REASON = '\u26D4 lane index permission: this is a dispatched lane child (`JOSH_LANE_CHILD`) and your reply asks a person for permission to change the git index. A lane is a per-run work tree with no person\'s staging to protect, and the sanctioned commit flow is already authorized: remove any conflict markers, then run `pnpm josh git -y`, which stages and commits them \u2014 a conflicted merge included. This is Tier A (`.claude/skills/workflow-commands/chain-rule.md` \u2192 "origin/main is merged in before the gate"). Do it and continue the run \u2014 do not repeat your previous reply.';
1147
- function asks_index_permission(message) {
1148
- return INDEX_COMMAND_PATTERN.test(message) && PERMISSION_PATTERN.test(message);
1173
+ function asks_index_permission(message2) {
1174
+ return INDEX_COMMAND_PATTERN.test(message2) && PERMISSION_PATTERN.test(message2);
1149
1175
  }
1150
1176
  function needs_index_route(context) {
1151
1177
  return context.lane_child && asks_index_permission(context.message);
@@ -1164,14 +1190,17 @@ function needs_filing(context) {
1164
1190
  if (context.filed || !filing_offer.offers_filing(context.message)) return false;
1165
1191
  return filing_offer.is_first_party_target(context.message, context.session_owner);
1166
1192
  }
1193
+ function is_held_idle(context) {
1194
+ return context.hold_present && !context.agent_pending;
1195
+ }
1167
1196
  function needs_notify(context) {
1168
- return context.hold_present && !context.notified;
1197
+ return is_held_idle(context) && !context.notified;
1169
1198
  }
1170
1199
  function needs_release(context) {
1171
- return context.hold_present && context.tree_clean;
1200
+ return is_held_idle(context) && context.tree_clean;
1172
1201
  }
1173
- function citation_reason(message) {
1174
- const references = issue_citation.bare_references(message);
1202
+ function citation_reason(message2) {
1203
+ const references = issue_citation.bare_references(message2);
1175
1204
  if (references.length === 0) return void 0;
1176
1205
  return build_citation_reason(references);
1177
1206
  }
@@ -1564,6 +1593,7 @@ async function build_context(transcript_path, payload_tail) {
1564
1593
  headless_refusals: stop_rules.count_headless_refusals(tail),
1565
1594
  lane_child: lane_child_marker.is_child_of(process.cwd()),
1566
1595
  background_pending: lane_background.pending_background_ids(tail).length > 0,
1596
+ agent_pending: lane_background.pending_agent_ids(tail).length > 0,
1567
1597
  session_lang: session_lang()
1568
1598
  };
1569
1599
  }
package/dist/josh.js CHANGED
@@ -1173,15 +1173,23 @@ var PROJECT_COMMANDS = {
1173
1173
  },
1174
1174
  start: {
1175
1175
  script: "scripts/init/start.ts",
1176
- description: "Initialize a project for the GitHub Issue workflow",
1176
+ description: "Create a new project for the GitHub Issue workflow, from git init to labels",
1177
1177
  category: "Project",
1178
- reference: [PROFILE_ARGUMENTS, "developer", ["files", "git", "network"]]
1178
+ reference: [
1179
+ `${PROFILE_ARGUMENTS} [--yes] [--github] [--public]`,
1180
+ "developer",
1181
+ ["files", "git", "network", "processes"]
1182
+ ]
1179
1183
  },
1180
1184
  init: {
1181
1185
  script: "scripts/init/init.ts",
1182
1186
  description: "Initialize config in a new project",
1183
1187
  category: "Project",
1184
- reference: [PROFILE_ARGUMENTS, "developer", ["files", "processes"]]
1188
+ reference: [
1189
+ `${PROFILE_ARGUMENTS} [--no-install]`,
1190
+ "developer",
1191
+ ["files", "network", "processes"]
1192
+ ]
1185
1193
  },
1186
1194
  sync: {
1187
1195
  script: "scripts/sync/sync.ts",
@@ -1322,9 +1330,9 @@ var WORKFLOW_COMMANDS = {
1322
1330
  reference: ["--task-type <type> --body <text>", "automation", ["notifications"]],
1323
1331
  tsx_arguments: OPTIONAL_ENV_FILE_FLAGS
1324
1332
  },
1325
- // The observation ledger's only commit path (joshuafolkken/kit#1756). `pnpm josh git` excludes the
1326
- // ledger from what it stages, so without this command a parent's appended line has no route to the
1327
- // default branch at all — and the recurrence count the promotion rule reads is a count of main.
1333
+ // The observation ledger's commit path for the lines no run's own commit carried
1334
+ // (joshuafolkken/kit#1756, joshuafolkken/kit#2763): a lane's lines sit in the primary checkout, which
1335
+ // its commit cannot see, and the recurrence count the promotion rule reads is a count of main.
1328
1336
  "observations:flush": {
1329
1337
  script: "scripts/observations/observations-flush-cli.ts",
1330
1338
  description: "Commit the observation ledger as a pull request of its own, and merge it",
package/package.json CHANGED
@@ -1,5 +1,5 @@
1
1
  {
2
- "version": "1.953.0",
2
+ "version": "1.972.0",
3
3
  "name": "@joshuafolkken/kit",
4
4
  "description": "Development toolkit for AI-assisted projects: formatting, AI assistant rules and a Git/GitHub Issue workflow for any repository, plus lint, type check, tests and hooks for Node projects",
5
5
  "keywords": [
@@ -200,7 +200,7 @@
200
200
  "eslint-import-resolver-typescript": "^4.4.5",
201
201
  "eslint-plugin-import-x": "^4.17.1",
202
202
  "eslint-plugin-promise": "^7.3.0",
203
- "eslint-plugin-sonarjs": "^4.2.1",
203
+ "eslint-plugin-sonarjs": "^4.2.2",
204
204
  "eslint-plugin-unicorn": "^76.0.0",
205
205
  "eslint_d": "^15.0.3",
206
206
  "globals": "^17.12.0",
@@ -212,7 +212,7 @@
212
212
  "rollup-plugin-dts": "^6.5.1",
213
213
  "secretlint": "^13.0.6",
214
214
  "typescript": "^6.0.3",
215
- "typescript-eslint": "^8.70.1",
215
+ "typescript-eslint": "^8.71.0",
216
216
  "vitest": "^5.0.2"
217
217
  },
218
218
  "dependencies": {
@@ -92,7 +92,7 @@ gh api "repos/{owner}/{repo}/issues?state=open&per_page=100" --paginate \
92
92
  - **push 時のフック**
93
93
  - **マージ時のチェック**
94
94
 
95
- **いずれかに当たれば単独実行。** 当たらなければ(機能追加、文書修正など)並列バッチの一員として走らせてよい。**「重大だと感じるか」は判定条件ではない** — 3 条件と同じ理由で、深刻さの自己申告を条件に加えた瞬間、この区分も抜け道になる。
95
+ **いずれかに当たれば単独実行。** その Issue に `run:solo` を付け、強制はそれを読む `backlog:next` と `epic:next --lanes` が行う(joshuafolkken/kit#2776)。当たらなければ(機能追加、文書修正など)`run:lane` を付け、並列バッチの一員として走らせてよい。どちらもなければ `triage` が返り何も始まらない(joshuafolkken/kit#2779)ので、run が起票する Issue(割り込み・分割・前提)は起票時にどちらかを付ける。**「重大だと感じるか」は判定条件ではない** — 3 条件と同じ理由で、深刻さの自己申告を条件に加えた瞬間、この区分も抜け道になる。
96
96
 
97
97
  **理由は 2 つあり、どちらか一方だけでも単独実行の根拠として十分である。**
98
98