@joshuafolkken/kit 1.983.0 → 1.998.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 (94) hide show
  1. package/.claude/skills/workflow-commands/backlogrun-child.md +1 -1
  2. package/.claude/skills/workflow-commands/backlogrun-steps.md +1 -1
  3. package/.github/workflows/sonar-qube.yml +12 -2
  4. package/CLAUDE.md +2 -2
  5. package/README.md +30 -21
  6. package/dist/CLAUDE.md +2 -2
  7. package/dist/hooks/batch-guard.js +4 -4
  8. package/dist/hooks/{chunk-ZT65LR3U.js → chunk-4GDR2YTV.js} +4 -4
  9. package/dist/hooks/{chunk-ASUZGZFT.js → chunk-56LGMJJV.js} +2 -2
  10. package/dist/hooks/{chunk-LBXQHXMC.js → chunk-762BDXDU.js} +220 -68
  11. package/dist/hooks/{chunk-IM3KM25Y.js → chunk-7WAOP42A.js} +5 -3
  12. package/dist/hooks/{chunk-P3UX7SFN.js → chunk-A2H5I2VD.js} +2 -2
  13. package/dist/hooks/{chunk-ERXMJJOV.js → chunk-AWTFKKFW.js} +1 -1
  14. package/dist/hooks/{chunk-APVOACVT.js → chunk-GBJUERYK.js} +1 -1
  15. package/dist/hooks/{chunk-5JX6HXDX.js → chunk-KPZN42RT.js} +3 -3
  16. package/dist/hooks/{chunk-47SPMGOD.js → chunk-PAXMLGSS.js} +26 -7
  17. package/dist/hooks/{chunk-RFPHKU7C.js → chunk-WMIR5SNX.js} +3 -3
  18. package/dist/hooks/codex-hook-adapter.js +10 -10
  19. package/dist/hooks/duplicate-read-guard.js +5 -5
  20. package/dist/hooks/format-edited.js +2 -2
  21. package/dist/hooks/investigation-guard.js +5 -5
  22. package/dist/hooks/pretool-guard.js +9 -9
  23. package/dist/hooks/stop-guard.js +98 -26
  24. package/dist/josh.js +5 -5
  25. package/package.json +5 -1
  26. package/prettier/basic.d.ts +3 -0
  27. package/prettier/basic.js +10 -0
  28. package/prettier/index.js +2 -2
  29. package/prettier/static.js +3 -10
  30. package/prompts/sonar-hotspot-handling.md +6 -0
  31. package/prompts/testing-guide.md +1 -1
  32. package/scripts/agent/agent-diagnostics.ts +1 -0
  33. package/scripts/agent/agent-role-profile.ts +1 -1
  34. package/scripts/behavior/behavior-rules.ts +8 -1
  35. package/scripts/build/build-claude-md.ts +13 -4
  36. package/scripts/document/document-byte-budget.ts +1 -1
  37. package/scripts/gate/gate-steps.ts +1 -1
  38. package/scripts/gate/project-checks.ts +12 -11
  39. package/scripts/gate/type-check-command.ts +1 -1
  40. package/scripts/git/git-branch.ts +14 -3
  41. package/scripts/git/git-issue.ts +13 -5
  42. package/scripts/git/git-pr.ts +16 -10
  43. package/scripts/git/git-preflight.ts +138 -0
  44. package/scripts/git/telegram-notify.ts +21 -0
  45. package/scripts/hooks/stop-guard.ts +2 -0
  46. package/scripts/init/basic-path-migration.ts +24 -0
  47. package/scripts/init/distributed-paths.ts +22 -0
  48. package/scripts/init/init-actions.ts +27 -19
  49. package/scripts/init/init-ai-copy.ts +86 -28
  50. package/scripts/init/{init-static.ts → init-basic.ts} +6 -6
  51. package/scripts/init/init-bootstrap.ts +4 -3
  52. package/scripts/init/init-install.ts +1 -1
  53. package/scripts/init/init-logic-templates.ts +1 -1
  54. package/scripts/init/init-logic.ts +1 -1
  55. package/scripts/init/init.ts +41 -19
  56. package/scripts/init/kit-setup-state.ts +30 -0
  57. package/scripts/init/kit-written-paths.ts +33 -0
  58. package/scripts/init/project-profile-cli.ts +15 -1
  59. package/scripts/init/project-profile.ts +41 -12
  60. package/scripts/init/session-language-environment.ts +83 -0
  61. package/scripts/init/start-exec.ts +28 -0
  62. package/scripts/init/start-plan.ts +35 -13
  63. package/scripts/init/start-prompt.ts +22 -8
  64. package/scripts/init/start-setup-pr.ts +138 -0
  65. package/scripts/init/start-steps.ts +30 -30
  66. package/scripts/init/start.ts +7 -3
  67. package/scripts/josh/__snapshots__/josh-command-map.test.ts.snap +3 -3
  68. package/scripts/josh/josh-commands-development.ts +3 -3
  69. package/scripts/josh/josh-commands-project.ts +2 -2
  70. package/scripts/lint/cspell-cached.ts +3 -3
  71. package/scripts/lint/format.ts +1 -1
  72. package/scripts/lint/lint-parallel.ts +2 -2
  73. package/scripts/rules/filing-cap.ts +1 -0
  74. package/scripts/rules/issue-citation.ts +30 -1
  75. package/scripts/rules/last-prompt.ts +79 -0
  76. package/scripts/rules/stop-rules.ts +5 -3
  77. package/scripts/safe-chain/ci-installer-pin.ts +19 -12
  78. package/scripts/safe-chain/project-config.ts +1 -1
  79. package/scripts/sync/sync-ai-files.ts +204 -0
  80. package/scripts/sync/sync.ts +48 -184
  81. package/scripts/test/josh-harness-environment.ts +1 -1
  82. package/scripts/test/pilot-files.ts +0 -1
  83. package/scripts/test/test-declared-changed.ts +1 -1
  84. package/scripts/test/test-declared-logic.ts +55 -14
  85. package/scripts/test/test-declared.ts +23 -11
  86. package/scripts/test/test-unit-guard.ts +6 -6
  87. package/scripts/time-runtime/time-shell.ts +36 -2
  88. package/scripts-ai/git-workflow.ts +10 -0
  89. package/templates/CLAUDE.basic.md +8 -0
  90. package/templates/workflows/ci.yml +27 -0
  91. /package/{templates/CLAUDE.static.md → dist/CLAUDE.basic.md} +0 -0
  92. /package/templates/{cursorrules.static → cursorrules.basic} +0 -0
  93. /package/templates/{pnpm-workspace.static.yaml → pnpm-workspace.basic.yaml} +0 -0
  94. /package/templates/{prettierignore.static → prettierignore.basic} +0 -0
@@ -712,7 +712,7 @@ var DEV_COMMANDS = {
712
712
  },
713
713
  lint: {
714
714
  script: "scripts/lint/lint-parallel.ts",
715
- description: "Check code with prettier and eslint (skips a tool a static project lacks)",
715
+ description: "Check code with prettier and eslint (skips a tool a basic project lacks)",
716
716
  category: "Development",
717
717
  reference: ["", "developer", ["processes"]]
718
718
  },
@@ -748,7 +748,7 @@ var DEV_COMMANDS = {
748
748
  },
749
749
  format: {
750
750
  script: "scripts/lint/format.ts",
751
- description: "Format code with prettier and eslint (skips a tool a static project lacks)",
751
+ description: "Format code with prettier and eslint (skips a tool a basic project lacks)",
752
752
  category: "Development",
753
753
  reference: ["", "developer", ["files", "processes"]]
754
754
  },
@@ -861,7 +861,7 @@ var DEV_COMMANDS = {
861
861
  },
862
862
  check: {
863
863
  script: "scripts/gate/type-check-command.ts",
864
- description: "Type-check with tsc (skips a static project with no TypeScript to check)",
864
+ description: "Type-check with tsc (skips a basic project with no TypeScript to check)",
865
865
  category: "Development",
866
866
  reference: ["[arguments...]", "developer", ["processes"]]
867
867
  },
@@ -1016,7 +1016,7 @@ var MAINTENANCE_COMMANDS = {
1016
1016
  };
1017
1017
 
1018
1018
  // scripts/josh/josh-commands-project.ts
1019
- var PROFILE_ARGUMENTS = "[--profile static|node]";
1019
+ var PROFILE_ARGUMENTS = "[--profile basic|full]";
1020
1020
  var PROJECT_COMMANDS = {
1021
1021
  profile: {
1022
1022
  script: "scripts/init/project-profile-cli.ts",
@@ -1026,7 +1026,7 @@ var PROJECT_COMMANDS = {
1026
1026
  },
1027
1027
  start: {
1028
1028
  script: "scripts/init/start.ts",
1029
- description: "Create a new project for the GitHub Issue workflow, from git init to labels",
1029
+ description: "Set a project up for the GitHub Issue workflow, from git init to the setup PR",
1030
1030
  category: "Project",
1031
1031
  reference: [
1032
1032
  `${PROFILE_ARGUMENTS} [--yes] [--github] [--public]`,
@@ -1417,7 +1417,10 @@ var WHITESPACE_PATTERN = /\s+/u;
1417
1417
  var SEGMENT_PATTERN = /&&|\|\||;|\|/u;
1418
1418
  var PIPE_PATTERN = /(?<!\|)\|(?!\|)/u;
1419
1419
  var CHAIN_PATTERN = /&&|\|\||;/u;
1420
- var QUOTED_SPAN_PATTERN = /'[^']*'|"[^"]*"/gu;
1420
+ var QUOTED_SPAN_PATTERN = /\\[\s\S]|'[^']*'|"(?:\\[\s\S]|[^"\\])*"/gu;
1421
+ var HEREDOC_MARKER = /(?<!<)<<-?\s*(['"]?)(\w+)\1/u;
1422
+ var HEREDOC_DELIMITER_GROUP = 2;
1423
+ var LINE_BREAK = "\n";
1421
1424
  var PIPEFAIL_PATTERN = /\bset\s+-[a-z]*o\s+pipefail\b/u;
1422
1425
  var COMMAND_WORD_PATTERN = /^[\w./@:+][\w./@:+-]*$/u;
1423
1426
  var ASSIGNMENT_PATTERN = /^\w+=/u;
@@ -1453,8 +1456,23 @@ function command_segment(command) {
1453
1456
  function leading_word(command) {
1454
1457
  return segment_command(command_segment(command));
1455
1458
  }
1459
+ function open_heredoc(line, kept) {
1460
+ kept.push(line.replace(HEREDOC_MARKER, " "));
1461
+ return HEREDOC_MARKER.exec(line)?.[HEREDOC_DELIMITER_GROUP];
1462
+ }
1463
+ function close_heredoc(line, delimiter) {
1464
+ return line.trim() === delimiter ? void 0 : delimiter;
1465
+ }
1466
+ function without_heredoc_bodies(command) {
1467
+ const kept = [];
1468
+ const body = {};
1469
+ for (const line of command.split(LINE_BREAK)) {
1470
+ body.delimiter = body.delimiter === void 0 ? open_heredoc(line, kept) : close_heredoc(line, body.delimiter);
1471
+ }
1472
+ return kept.join(LINE_BREAK);
1473
+ }
1456
1474
  function unquoted(command) {
1457
- return command.replaceAll(QUOTED_SPAN_PATTERN, " ");
1475
+ return without_heredoc_bodies(command).replaceAll(QUOTED_SPAN_PATTERN, " ");
1458
1476
  }
1459
1477
  function discarded_commands(command) {
1460
1478
  const plain = unquoted(command);
@@ -3505,6 +3523,7 @@ export {
3505
3523
  time_bundle_call,
3506
3524
  time_format,
3507
3525
  cost_attribute,
3526
+ time_reported_failure,
3508
3527
  time_transcript_line,
3509
3528
  time_spans,
3510
3529
  time_round_trips,
@@ -1,16 +1,16 @@
1
1
  import {
2
2
  time_batch_guard
3
- } from "./chunk-ASUZGZFT.js";
3
+ } from "./chunk-56LGMJJV.js";
4
4
  import {
5
5
  hook_decision
6
- } from "./chunk-APVOACVT.js";
6
+ } from "./chunk-GBJUERYK.js";
7
7
  import {
8
8
  cost_blocks,
9
9
  cost_transcript,
10
10
  time_bundle_call,
11
11
  time_shell,
12
12
  time_spans
13
- } from "./chunk-47SPMGOD.js";
13
+ } from "./chunk-PAXMLGSS.js";
14
14
 
15
15
  // scripts/delegation/investigation-reads.ts
16
16
  import path from "node:path";
@@ -1,25 +1,25 @@
1
1
  #!/usr/bin/env tsx
2
2
  import {
3
3
  pretool_guard
4
- } from "./chunk-LBXQHXMC.js";
5
- import "./chunk-IM3KM25Y.js";
4
+ } from "./chunk-762BDXDU.js";
5
+ import "./chunk-7WAOP42A.js";
6
6
  import {
7
7
  build_envelope,
8
8
  compose_context,
9
9
  format_edited_payload
10
- } from "./chunk-ERXMJJOV.js";
10
+ } from "./chunk-AWTFKKFW.js";
11
11
  import "./chunk-3V37IID6.js";
12
- import "./chunk-P3UX7SFN.js";
13
- import "./chunk-5JX6HXDX.js";
14
- import "./chunk-ZT65LR3U.js";
15
- import "./chunk-RFPHKU7C.js";
16
- import "./chunk-ASUZGZFT.js";
12
+ import "./chunk-A2H5I2VD.js";
13
+ import "./chunk-KPZN42RT.js";
14
+ import "./chunk-4GDR2YTV.js";
15
+ import "./chunk-WMIR5SNX.js";
16
+ import "./chunk-56LGMJJV.js";
17
17
  import {
18
18
  hook_decision
19
- } from "./chunk-APVOACVT.js";
19
+ } from "./chunk-GBJUERYK.js";
20
20
  import {
21
21
  time_density_hook
22
- } from "./chunk-47SPMGOD.js";
22
+ } from "./chunk-PAXMLGSS.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-ZT65LR3U.js";
13
- import "./chunk-RFPHKU7C.js";
14
- import "./chunk-ASUZGZFT.js";
15
- import "./chunk-APVOACVT.js";
16
- import "./chunk-47SPMGOD.js";
12
+ } from "./chunk-4GDR2YTV.js";
13
+ import "./chunk-WMIR5SNX.js";
14
+ import "./chunk-56LGMJJV.js";
15
+ import "./chunk-GBJUERYK.js";
16
+ import "./chunk-PAXMLGSS.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-ERXMJJOV.js";
18
+ } from "./chunk-AWTFKKFW.js";
19
19
  import "./chunk-3V37IID6.js";
20
- import "./chunk-47SPMGOD.js";
20
+ import "./chunk-PAXMLGSS.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-5JX6HXDX.js";
14
- import "./chunk-RFPHKU7C.js";
15
- import "./chunk-ASUZGZFT.js";
16
- import "./chunk-APVOACVT.js";
17
- import "./chunk-47SPMGOD.js";
13
+ } from "./chunk-KPZN42RT.js";
14
+ import "./chunk-WMIR5SNX.js";
15
+ import "./chunk-56LGMJJV.js";
16
+ import "./chunk-GBJUERYK.js";
17
+ import "./chunk-PAXMLGSS.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-LBXQHXMC.js";
7
- import "./chunk-IM3KM25Y.js";
6
+ } from "./chunk-762BDXDU.js";
7
+ import "./chunk-7WAOP42A.js";
8
8
  import "./chunk-3V37IID6.js";
9
- import "./chunk-P3UX7SFN.js";
10
- import "./chunk-5JX6HXDX.js";
11
- import "./chunk-ZT65LR3U.js";
12
- import "./chunk-RFPHKU7C.js";
13
- import "./chunk-ASUZGZFT.js";
14
- import "./chunk-APVOACVT.js";
15
- import "./chunk-47SPMGOD.js";
9
+ import "./chunk-A2H5I2VD.js";
10
+ import "./chunk-KPZN42RT.js";
11
+ import "./chunk-4GDR2YTV.js";
12
+ import "./chunk-WMIR5SNX.js";
13
+ import "./chunk-56LGMJJV.js";
14
+ import "./chunk-GBJUERYK.js";
15
+ import "./chunk-PAXMLGSS.js";
16
16
  import "./chunk-WPALEIRX.js";
17
17
  import "./chunk-RKUAD3I6.js";
18
18
  export {
@@ -19,20 +19,22 @@ import {
19
19
  run_hold,
20
20
  run_invocation,
21
21
  run_watcher_guard
22
- } from "./chunk-IM3KM25Y.js";
22
+ } from "./chunk-7WAOP42A.js";
23
23
  import {
24
24
  session_language
25
25
  } from "./chunk-6V5OPIUU.js";
26
26
  import {
27
27
  hook_decision,
28
28
  lane_child_marker
29
- } from "./chunk-APVOACVT.js";
29
+ } from "./chunk-GBJUERYK.js";
30
30
  import {
31
31
  PROJECT_ROOT,
32
+ json_value,
32
33
  stamp_file,
33
34
  time_density_hook,
34
- time_hook_transcript
35
- } from "./chunk-47SPMGOD.js";
35
+ time_hook_transcript,
36
+ time_reported_failure
37
+ } from "./chunk-PAXMLGSS.js";
36
38
  import "./chunk-WPALEIRX.js";
37
39
  import "./chunk-RKUAD3I6.js";
38
40
 
@@ -329,6 +331,17 @@ function bare_references(message2) {
329
331
  for (const line of prose_lines(message2)) collect_line_references(line, found);
330
332
  return [...new Set(found)];
331
333
  }
334
+ function prompt_references(prompt) {
335
+ const found = /* @__PURE__ */ new Set();
336
+ for (const match of prompt.matchAll(ISSUE_NUMBER)) {
337
+ found.add(repo_prefix(prompt, match.index) + match[0]);
338
+ }
339
+ return found;
340
+ }
341
+ function unquoted_references(message2, prompt) {
342
+ const quoted = prompt_references(prompt);
343
+ return bare_references(message2).filter((reference) => !quoted.has(reference));
344
+ }
332
345
  function has_bare_reference(message2) {
333
346
  return bare_references(message2).length > 0;
334
347
  }
@@ -366,7 +379,14 @@ function linkify(message2, slug) {
366
379
  }
367
380
  return out.join("\n");
368
381
  }
369
- var issue_citation = { bare_references, cite_arguments, has_bare_reference, linkify, prose_lines };
382
+ var issue_citation = {
383
+ bare_references,
384
+ cite_arguments,
385
+ has_bare_reference,
386
+ linkify,
387
+ prose_lines,
388
+ unquoted_references
389
+ };
370
390
 
371
391
  // scripts/epic/epic-triage.ts
372
392
  var TRIAGE_VERDICT = "triage";
@@ -684,6 +704,8 @@ var TASK_DEFINITIONS = {
684
704
  stalled: { icon: "\u23F3", label: "Ready work is sitting undispatched" },
685
705
  stranded: { icon: "\u{1F6A8}", label: "Run stranded \u2014 nobody is driving it" }
686
706
  };
707
+ var NOTIFY_SWITCH_KEY = "JOSH_NOTIFY";
708
+ var NOTIFY_OFF_VALUE = "off";
687
709
  var NOT_CONFIGURED_PREFIX = "Telegram is not configured";
688
710
  var SEND_FAILED_PREFIX = "Telegram notification failed";
689
711
  var REDACTED = "<redacted>";
@@ -720,6 +742,9 @@ function load_config() {
720
742
  }
721
743
  return { bot_token: result.data.telegram_bot_token, chat_id: result.data.telegram_chat_id };
722
744
  }
745
+ function is_notify_disabled() {
746
+ return parse_environment_string(process.env[NOTIFY_SWITCH_KEY]).toLowerCase() === NOTIFY_OFF_VALUE;
747
+ }
723
748
  function build_header(task_type, repo_name) {
724
749
  const { icon, label: label2 } = TASK_DEFINITIONS[task_type];
725
750
  if (repo_name === void 0 || repo_name.length === 0) return `${icon} ${label2}`;
@@ -767,6 +792,12 @@ function build_send_failure(error, config) {
767
792
  return new Error(`${SEND_FAILED_PREFIX}: ${reason}`, { cause: new Error(reason) });
768
793
  }
769
794
  async function send(input) {
795
+ if (is_notify_disabled()) {
796
+ console.info(
797
+ `\u{1F515} Telegram notifications are disabled (${NOTIFY_SWITCH_KEY}=${NOTIFY_OFF_VALUE}).`
798
+ );
799
+ return;
800
+ }
770
801
  const config = load_config();
771
802
  const text2 = build_text(input);
772
803
  try {
@@ -1038,8 +1069,48 @@ async function run_stall_check(ready = backlog_ready.DEFAULT_PORTS) {
1038
1069
  }
1039
1070
  var backlog_stalled_detect = { DEFAULT_PORTS: DEFAULT_PORTS2, detect_and_report, gather, report, run_stall_check };
1040
1071
 
1041
- // scripts/rules/stop-rules.ts
1072
+ // scripts/rules/last-prompt.ts
1042
1073
  import { z as z3 } from "zod";
1074
+ var WORKFLOW_KEYWORDS = /* @__PURE__ */ new Set([
1075
+ "kickoff",
1076
+ "fullrun",
1077
+ "halfrun",
1078
+ "backlogrun"
1079
+ ]);
1080
+ var NO_PROMPT = "";
1081
+ var HUMAN_ORIGIN = "human";
1082
+ var KEYWORD_BEFORE_ARGUMENT = -2;
1083
+ var PROMPT_LINE_SCHEMA = z3.object({
1084
+ isMeta: z3.boolean().nullish(),
1085
+ origin: z3.object({ kind: z3.string().nullish() }).nullish(),
1086
+ message: z3.object({ content: z3.unknown() })
1087
+ });
1088
+ function is_person(line) {
1089
+ if (line.isMeta === true) return false;
1090
+ return line.origin === null || line.origin === void 0 || line.origin.kind === HUMAN_ORIGIN;
1091
+ }
1092
+ function person_text(line) {
1093
+ const parsed = PROMPT_LINE_SCHEMA.safeParse(json_value.parse_or_undefined(line));
1094
+ if (!parsed.success || !is_person(parsed.data)) return void 0;
1095
+ return time_reported_failure.result_text(parsed.data.message.content);
1096
+ }
1097
+ function is_invocation(prompt) {
1098
+ const words = prompt.trim().split(/\s+/u);
1099
+ return [words.at(0), words.at(KEYWORD_BEFORE_ARGUMENT)].some(
1100
+ (word) => WORKFLOW_KEYWORDS.has(word ?? NO_PROMPT)
1101
+ );
1102
+ }
1103
+ function prompt_line_text(line) {
1104
+ return filing_cap.is_prompt_line(line) ? person_text(line) : void 0;
1105
+ }
1106
+ function prompt_text(tail) {
1107
+ const text2 = tail.split("\n").map((line) => prompt_line_text(line)).findLast((one) => one !== void 0);
1108
+ return text2 === void 0 || is_invocation(text2) ? NO_PROMPT : text2;
1109
+ }
1110
+ var last_prompt = { prompt_text };
1111
+
1112
+ // scripts/rules/stop-rules.ts
1113
+ import { z as z4 } from "zod";
1043
1114
 
1044
1115
  // scripts/rules/filing-offer.ts
1045
1116
  var OFFER_PATTERNS = [
@@ -1147,10 +1218,10 @@ var reply_language = { MIN_PROSE_LETTERS, is_mismatch, script_of };
1147
1218
  // scripts/rules/stop-rules.ts
1148
1219
  var SWITCH_ENV_KEY = "JOSH_STOP_GUARD";
1149
1220
  var BLOCK_DECISION = "block";
1150
- var stop_payload_schema = z3.object({
1151
- transcript_path: z3.string().min(1),
1152
- stop_hook_active: z3.boolean().nullish(),
1153
- last_assistant_message: z3.string().nullish()
1221
+ var stop_payload_schema = z4.object({
1222
+ transcript_path: z4.string().min(1),
1223
+ stop_hook_active: z4.boolean().nullish(),
1224
+ last_assistant_message: z4.string().nullish()
1154
1225
  });
1155
1226
  var NO_OUTCOME = { reason: void 0 };
1156
1227
  var STOP_NOTIFY_REASON = '\u26D4 mid-workflow stop notification: this working tree is still held by a run and no `confirmation` Telegram was sent this turn, so a person is being left to wait off-screen without knowing the run paused. `CLAUDE.md` \u2192 "Mid-workflow stop notification" requires `pnpm josh notify --task-type confirmation --issue-url "<url>" --body=$\'<reason>\'` before any mid-workflow pause. Send it, then end with a one-line confirmation that it was sent \u2014 do not repeat your previous reply. This fires only while the hold is held and no notify is on the transcript, and `stop_hook_active` lets a second stop through so a run is never wedged.';
@@ -1199,14 +1270,14 @@ function needs_notify(context) {
1199
1270
  function needs_release(context) {
1200
1271
  return is_held_idle(context) && context.tree_clean;
1201
1272
  }
1202
- function citation_reason(message2) {
1203
- const references = issue_citation.bare_references(message2);
1273
+ function citation_reason(context) {
1274
+ const references = issue_citation.unquoted_references(context.message, context.prompt);
1204
1275
  if (references.length === 0) return void 0;
1205
1276
  return build_citation_reason(references);
1206
1277
  }
1207
1278
  function reply_reason(context) {
1208
1279
  if (needs_filing(context)) return FILING_OFFER_REASON;
1209
- return citation_reason(context.message) ?? language_reason(context);
1280
+ return citation_reason(context) ?? language_reason(context);
1210
1281
  }
1211
1282
  function is_headless_held(context) {
1212
1283
  if (!context.headless_waiting) return false;
@@ -1302,7 +1373,7 @@ var run_stranded = {
1302
1373
  };
1303
1374
 
1304
1375
  // scripts/run/run-wake.ts
1305
- import { z as z4 } from "zod";
1376
+ import { z as z5 } from "zod";
1306
1377
  var WAKE_PREFIX = "josh-run-wake-";
1307
1378
  var WAKE_LOG_PREFIX = "josh-run-wake-log-";
1308
1379
  var WAKE_LOG_SUFFIX = ".log";
@@ -1322,21 +1393,21 @@ var STOP_REASONS = {
1322
1393
  expired: "expired",
1323
1394
  unreadable: "unreadable"
1324
1395
  };
1325
- var run_wake_schema = z4.object({
1326
- invocation: z4.string(),
1327
- started_at: z4.string(),
1328
- pid: z4.number(),
1329
- process_start: z4.string().optional(),
1330
- woke: z4.number(),
1331
- woke_at: z4.string().optional(),
1396
+ var run_wake_schema = z5.object({
1397
+ invocation: z5.string(),
1398
+ started_at: z5.string(),
1399
+ pid: z5.number(),
1400
+ process_start: z5.string().optional(),
1401
+ woke: z5.number(),
1402
+ woke_at: z5.string().optional(),
1332
1403
  // **Every optional field of `RunWake` has to be listed here.** `z.object` strips what it does not
1333
1404
  // declare, so a field added to the interface alone round-trips through disk as `undefined` — which
1334
1405
  // reads as "no attempt has been made yet" and would restart the retry count on every pass.
1335
- attempts: z4.number().optional(),
1336
- woke_pid: z4.number().optional(),
1337
- held_at: z4.string().optional(),
1338
- spawned: z4.array(z4.string()).optional(),
1339
- idle_since: z4.string().optional(),
1406
+ attempts: z5.number().optional(),
1407
+ woke_pid: z5.number().optional(),
1408
+ held_at: z5.string().optional(),
1409
+ spawned: z5.array(z5.string()).optional(),
1410
+ idle_since: z5.string().optional(),
1340
1411
  profile: agent_role_profile.PROFILE_SCHEMA.optional()
1341
1412
  });
1342
1413
  function is_overdue(marked_at, now, limit_ms = WAKE_GRACE_MS) {
@@ -1585,6 +1656,7 @@ async function build_context(transcript_path, payload_tail) {
1585
1656
  tree_clean: !await run_hold.is_tree_dirty(),
1586
1657
  notified: lane_park.mentions_confirmation_notify(tail),
1587
1658
  message: payload_tail.message,
1659
+ prompt: last_prompt.prompt_text(tail),
1588
1660
  stop_hook_active: payload_tail.stop_hook_active,
1589
1661
  cut_pending: run_cut.carried_cut_sync() !== void 0,
1590
1662
  filed: was_filed(tail),
package/dist/josh.js CHANGED
@@ -869,7 +869,7 @@ var DEV_COMMANDS = {
869
869
  },
870
870
  lint: {
871
871
  script: "scripts/lint/lint-parallel.ts",
872
- description: "Check code with prettier and eslint (skips a tool a static project lacks)",
872
+ description: "Check code with prettier and eslint (skips a tool a basic project lacks)",
873
873
  category: "Development",
874
874
  reference: ["", "developer", ["processes"]]
875
875
  },
@@ -905,7 +905,7 @@ var DEV_COMMANDS = {
905
905
  },
906
906
  format: {
907
907
  script: "scripts/lint/format.ts",
908
- description: "Format code with prettier and eslint (skips a tool a static project lacks)",
908
+ description: "Format code with prettier and eslint (skips a tool a basic project lacks)",
909
909
  category: "Development",
910
910
  reference: ["", "developer", ["files", "processes"]]
911
911
  },
@@ -1018,7 +1018,7 @@ var DEV_COMMANDS = {
1018
1018
  },
1019
1019
  check: {
1020
1020
  script: "scripts/gate/type-check-command.ts",
1021
- description: "Type-check with tsc (skips a static project with no TypeScript to check)",
1021
+ description: "Type-check with tsc (skips a basic project with no TypeScript to check)",
1022
1022
  category: "Development",
1023
1023
  reference: ["[arguments...]", "developer", ["processes"]]
1024
1024
  },
@@ -1173,7 +1173,7 @@ var MAINTENANCE_COMMANDS = {
1173
1173
  };
1174
1174
 
1175
1175
  // scripts/josh/josh-commands-project.ts
1176
- var PROFILE_ARGUMENTS = "[--profile static|node]";
1176
+ var PROFILE_ARGUMENTS = "[--profile basic|full]";
1177
1177
  var PROJECT_COMMANDS = {
1178
1178
  profile: {
1179
1179
  script: "scripts/init/project-profile-cli.ts",
@@ -1183,7 +1183,7 @@ var PROJECT_COMMANDS = {
1183
1183
  },
1184
1184
  start: {
1185
1185
  script: "scripts/init/start.ts",
1186
- description: "Create a new project for the GitHub Issue workflow, from git init to labels",
1186
+ description: "Set a project up for the GitHub Issue workflow, from git init to the setup PR",
1187
1187
  category: "Project",
1188
1188
  reference: [
1189
1189
  `${PROFILE_ARGUMENTS} [--yes] [--github] [--public]`,
package/package.json CHANGED
@@ -1,5 +1,5 @@
1
1
  {
2
- "version": "1.983.0",
2
+ "version": "1.998.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": [
@@ -97,6 +97,10 @@
97
97
  "types": "./prettier/index.d.ts",
98
98
  "default": "./prettier/index.js"
99
99
  },
100
+ "./prettier/basic": {
101
+ "types": "./prettier/basic.d.ts",
102
+ "default": "./prettier/basic.js"
103
+ },
100
104
  "./prettier/static": {
101
105
  "types": "./prettier/static.d.ts",
102
106
  "default": "./prettier/static.js"
@@ -0,0 +1,3 @@
1
+ import type { Config } from 'prettier'
2
+
3
+ export const config: Config
@@ -0,0 +1,10 @@
1
+ export const config = {
2
+ useTabs: true,
3
+ singleQuote: true,
4
+ trailingComma: 'all',
5
+ printWidth: 100,
6
+ semi: false,
7
+ arrowParens: 'always',
8
+ bracketSpacing: true,
9
+ endOfLine: 'lf',
10
+ }
package/prettier/index.js CHANGED
@@ -1,7 +1,7 @@
1
- import { config as static_config } from './static.js'
1
+ import { config as basic_config } from './basic.js'
2
2
 
3
3
  export const config = {
4
- ...static_config,
4
+ ...basic_config,
5
5
  plugins: [
6
6
  '@ianvs/prettier-plugin-sort-imports',
7
7
  'prettier-plugin-svelte',
@@ -1,10 +1,3 @@
1
- export const config = {
2
- useTabs: true,
3
- singleQuote: true,
4
- trailingComma: 'all',
5
- printWidth: 100,
6
- semi: false,
7
- arrowParens: 'always',
8
- bracketSpacing: true,
9
- endOfLine: 'lf',
10
- }
1
+ // The basic profile's preset under the name it had before joshuafolkken/kit#2829. A project
2
+ // initialized earlier imports this path from its `prettier.config.mjs`, so it stays exported.
3
+ export { config } from './basic.js'
@@ -17,6 +17,12 @@ comments), but it **does** wait on the required `SonarQube` CI check, so a red g
17
17
  `followup` merge. Consumers pick the workflow up verbatim via `josh sync`; do not re-implement the
18
18
  gate per consumer.
19
19
 
20
+ **A run that cannot see `SONAR_TOKEN` skips the scan and passes**, printing one line that names the
21
+ missing token (joshuafolkken/kit#2815) — a consumer that does not use SonarCloud is never blocked by
22
+ it. The same holds for any run GitHub withholds secrets from (a fork pull request, or a Dependabot
23
+ run without the secret in the Dependabot store), so a repository that relies on this gate keeps
24
+ `SONAR_TOKEN` in both secret stores.
25
+
20
26
  ### New Code vs Overall Code semantics
21
27
 
22
28
  SonarCloud's default **Sonar way** Quality Gate evaluates **New Code only** — conditions
@@ -23,7 +23,7 @@ Req N: ...
23
23
 
24
24
  - **Every user-facing behavior change** (UI interaction, keyboard shortcut, visible state change) must have a corresponding E2E test.
25
25
  - **Every logic/utility change** (pure functions, filters, transforms) must have a unit test.
26
- - **static の HTML/CSS だけの変更:** `package.json` に `josh.profile: static` が記録されていれば自動テストは不要。ブラウザーで対象ページを開き、想定する画面幅でレイアウト・リンク・HTML 内の操作を確認して結果を記録する。同じ変更に JavaScript または TypeScript のファイルが含まれれば自動テストを行う。
26
+ - **basic で kit がテストできない言語だけの変更:** `package.json` に `josh.profile: basic`(改名前の `static` も同じ)が記録され、変更が HTML/CSS や Lua などのソースだけなら自動テストは不要。HTML/CSS はブラウザーで対象ページを開き、想定する画面幅でレイアウト・リンク・HTML 内の操作を確認する。そのほかのソースは変更した処理を実際に動かす。どちらも結果を記録する。同じ変更に JavaScript または TypeScript のファイルが含まれれば自動テストを行う。
27
27
  - If a test is genuinely infeasible (e.g., native OS date-picker popup cannot be driven by Playwright), write a comment in the test file explaining why and test the closest observable behavior instead (e.g., that the editor remains open after the `change` event fires).
28
28
  - Do **not** report a requirement as done if its test is missing.
29
29
 
@@ -37,6 +37,7 @@ const PROVIDER_CLI_TABLE: Readonly<Record<AgentProvider, ProviderCli>> = {
37
37
  const MODEL_CLI_FLOORS: ReadonlyMap<string, string> = new Map([
38
38
  ['claude-opus-5-5', '2.1.280'],
39
39
  ['gpt-6-sol', '0.155.0'],
40
+ ['gpt-6.1-sol', '0.159.1'],
40
41
  ])
41
42
 
42
43
  const default_ports: DiagnosticPorts = {
@@ -44,7 +44,7 @@ const OPENAI_PROVIDER: AgentProvider = 'openai'
44
44
  // whenever the CLI moves it, so a run log could not say which model produced it and a model migration
45
45
  // could not be measured apart from everything else. A new lane records the id it resolved; a lane
46
46
  // created before a migration keeps the model it recorded (`with_phase_effort` leaves it untouched).
47
- const OPENAI_MODEL = 'gpt-6-sol'
47
+ const OPENAI_MODEL = 'gpt-6.1-sol'
48
48
  const ANTHROPIC_MODEL = 'claude-opus-5-5'
49
49
  const CODEX_SESSION_KEY = 'CODEX_THREAD_ID'
50
50
 
@@ -1,4 +1,5 @@
1
1
  import { json_value } from '#scripts/lib/json-value'
2
+ import { time_shell } from '#scripts/time-runtime/time-shell'
2
3
  import type { Block, TranscriptLine } from '#scripts/time-runtime/time-transcript-line'
3
4
  import type { Assertion, Finding } from './behavior-assertion'
4
5
 
@@ -57,8 +58,14 @@ function command_of(block: Block): string | undefined {
57
58
  // Whether any segment of a command directly mutates the index: it begins with a staging or commit
58
59
  // subcommand and is not a dry run. Each `;`/`&&`/`|`-separated segment is judged on its own, so a
59
60
  // mutation anywhere in a chain is caught while a mention inside another command's argument is not.
61
+ //
62
+ // Quoted spans are removed before the split (joshuafolkken/kit#2841): a `|` inside a quoted pattern —
63
+ // `grep -E "commit|git add" file` — is not a separator, and cutting there synthesized a segment that
64
+ // began with `git add` out of a read-only search. `unquoted` honors escapes and heredoc bodies, so a
65
+ // stray `it's` cannot pair with a later quote and erase the real `git add` between them.
60
66
  function is_index_mutation(command: string): boolean {
61
- return command
67
+ return time_shell
68
+ .unquoted(command)
62
69
  .split(SEGMENT_SEPARATOR)
63
70
  .map((segment) => segment.trim())
64
71
  .some((segment) => INDEX_MUTATION_HEAD.test(segment) && !is_dry_run(segment))
@@ -7,11 +7,17 @@ import { init_logic } from '#scripts/init/init-logic'
7
7
  const SCRIPT_DIR = path.dirname(fileURLToPath(import.meta.url))
8
8
  const REPO_ROOT = path.resolve(SCRIPT_DIR, '..', '..')
9
9
  const DIST_ROOT = path.join(REPO_ROOT, 'dist')
10
- const STATIC_CLAUDE_FILENAME = 'CLAUDE.static.md'
10
+ const BASIC_CLAUDE_FILENAME = 'CLAUDE.basic.md'
11
11
  const SOURCE_CLAUDE_MD = path.join(REPO_ROOT, 'CLAUDE.md')
12
12
  const DIST_CLAUDE_MD = path.join(DIST_ROOT, 'CLAUDE.md')
13
- const SOURCE_STATIC_CLAUDE_MD = path.join(REPO_ROOT, 'templates', STATIC_CLAUDE_FILENAME)
14
- const DIST_STATIC_CLAUDE_MD = path.join(DIST_ROOT, STATIC_CLAUDE_FILENAME)
13
+ const SOURCE_BASIC_CLAUDE_MD = path.join(REPO_ROOT, 'templates', BASIC_CLAUDE_FILENAME)
14
+ // A basic project's `CLAUDE.md` imports the rules from dist/. One initialized before
15
+ // joshuafolkken/kit#2829 imports them under the profile's old name until `josh init` is re-run
16
+ // and migrates that import, so the same rules ship under both names.
17
+ const DIST_BASIC_CLAUDE_MDS: ReadonlyArray<string> = [
18
+ path.join(DIST_ROOT, BASIC_CLAUDE_FILENAME),
19
+ path.join(DIST_ROOT, 'CLAUDE.static.md'),
20
+ ]
15
21
 
16
22
  // Read kit's own CLAUDE.md and apply the distribution path transform. kit's source keeps relative
17
23
  // paths so it resolves inside the kit repository; the published copy rewrites them so every backtick
@@ -27,7 +33,9 @@ function generate_distributed_claude_md(): string {
27
33
  function build_claude_md(): string {
28
34
  mkdirSync(path.dirname(DIST_CLAUDE_MD), { recursive: true })
29
35
  writeFileSync(DIST_CLAUDE_MD, generate_distributed_claude_md())
30
- writeFileSync(DIST_STATIC_CLAUDE_MD, readFileSync(SOURCE_STATIC_CLAUDE_MD, 'utf8'))
36
+ const basic_rules = readFileSync(SOURCE_BASIC_CLAUDE_MD, 'utf8')
37
+
38
+ for (const destination of DIST_BASIC_CLAUDE_MDS) writeFileSync(destination, basic_rules)
31
39
 
32
40
  return DIST_CLAUDE_MD
33
41
  }
@@ -40,6 +48,7 @@ if (process.argv[1] === fileURLToPath(import.meta.url)) main()
40
48
 
41
49
  export {
42
50
  build_claude_md,
51
+ DIST_BASIC_CLAUDE_MDS,
43
52
  generate_distributed_claude_md,
44
53
  SOURCE_CLAUDE_MD,
45
54
  DIST_CLAUDE_MD,