@joshuafolkken/kit 1.983.0 → 1.1003.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 (102) hide show
  1. package/.claude/skills/workflow-commands/backlogrun-child.md +4 -5
  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-6PSTVOLH.js} +263 -74
  11. package/dist/hooks/{chunk-P3UX7SFN.js → chunk-A2H5I2VD.js} +2 -2
  12. package/dist/hooks/{chunk-ERXMJJOV.js → chunk-AWTFKKFW.js} +1 -1
  13. package/dist/hooks/{chunk-IM3KM25Y.js → chunk-EZLHXKJ6.js} +10 -7
  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/ci/pr-classification.ts +11 -2
  37. package/scripts/document/document-byte-budget.ts +1 -1
  38. package/scripts/gate/gate-steps.ts +1 -1
  39. package/scripts/gate/project-checks.ts +12 -11
  40. package/scripts/gate/type-check-command.ts +1 -1
  41. package/scripts/git/git-branch.ts +14 -3
  42. package/scripts/git/git-command.ts +5 -2
  43. package/scripts/git/git-issue.ts +13 -5
  44. package/scripts/git/git-pr.ts +16 -10
  45. package/scripts/git/git-preflight.ts +138 -0
  46. package/scripts/git/telegram-notify.ts +21 -0
  47. package/scripts/hooks/stop-guard.ts +2 -0
  48. package/scripts/init/basic-path-migration.ts +24 -0
  49. package/scripts/init/distributed-paths.ts +22 -0
  50. package/scripts/init/init-actions.ts +27 -19
  51. package/scripts/init/init-ai-copy.ts +86 -28
  52. package/scripts/init/{init-static.ts → init-basic.ts} +6 -6
  53. package/scripts/init/init-bootstrap.ts +4 -3
  54. package/scripts/init/init-install.ts +1 -1
  55. package/scripts/init/init-logic-templates.ts +1 -1
  56. package/scripts/init/init-logic.ts +1 -1
  57. package/scripts/init/init.ts +41 -19
  58. package/scripts/init/kit-setup-state.ts +30 -0
  59. package/scripts/init/kit-written-paths.ts +33 -0
  60. package/scripts/init/project-profile-cli.ts +15 -1
  61. package/scripts/init/project-profile.ts +41 -12
  62. package/scripts/init/session-language-environment.ts +83 -0
  63. package/scripts/init/start-exec.ts +28 -0
  64. package/scripts/init/start-plan.ts +35 -13
  65. package/scripts/init/start-prompt.ts +22 -8
  66. package/scripts/init/start-setup-pr.ts +138 -0
  67. package/scripts/init/start-steps.ts +30 -30
  68. package/scripts/init/start.ts +7 -3
  69. package/scripts/josh/__snapshots__/josh-command-map.test.ts.snap +3 -3
  70. package/scripts/josh/josh-commands-development.ts +3 -3
  71. package/scripts/josh/josh-commands-project.ts +2 -2
  72. package/scripts/lane/lane-leftover.ts +143 -0
  73. package/scripts/lane/lane-open.ts +14 -1
  74. package/scripts/lint/cspell-cached.ts +3 -3
  75. package/scripts/lint/format.ts +1 -1
  76. package/scripts/lint/lint-parallel.ts +2 -2
  77. package/scripts/propagate/propagate-steps.ts +14 -1
  78. package/scripts/rules/filing-cap.ts +1 -0
  79. package/scripts/rules/issue-citation.ts +30 -1
  80. package/scripts/rules/last-prompt.ts +79 -0
  81. package/scripts/rules/stop-rules.ts +5 -3
  82. package/scripts/run/run-hold.ts +3 -3
  83. package/scripts/run/run-preflight.ts +75 -5
  84. package/scripts/run/run-progress-cli.ts +14 -2
  85. package/scripts/safe-chain/ci-installer-pin.ts +19 -12
  86. package/scripts/safe-chain/project-config.ts +1 -1
  87. package/scripts/sync/sync-ai-files.ts +204 -0
  88. package/scripts/sync/sync.ts +48 -184
  89. package/scripts/test/josh-harness-environment.ts +1 -1
  90. package/scripts/test/pilot-files.ts +0 -1
  91. package/scripts/test/test-declared-changed.ts +1 -1
  92. package/scripts/test/test-declared-logic.ts +55 -14
  93. package/scripts/test/test-declared.ts +23 -11
  94. package/scripts/test/test-unit-guard.ts +6 -6
  95. package/scripts/time-runtime/time-shell.ts +36 -2
  96. package/scripts-ai/git-workflow.ts +10 -0
  97. package/templates/CLAUDE.basic.md +8 -0
  98. package/templates/workflows/ci.yml +27 -0
  99. /package/{templates/CLAUDE.static.md → dist/CLAUDE.basic.md} +0 -0
  100. /package/templates/{cursorrules.static → cursorrules.basic} +0 -0
  101. /package/templates/{pnpm-workspace.static.yaml → pnpm-workspace.basic.yaml} +0 -0
  102. /package/templates/{prettierignore.static → prettierignore.basic} +0 -0
@@ -26,6 +26,7 @@ import {
26
26
  parse_json_array_or_undefined,
27
27
  parse_json_object_safe,
28
28
  read_json_listing,
29
+ repo_origin,
29
30
  repo_party,
30
31
  run_carry,
31
32
  run_cut,
@@ -35,31 +36,31 @@ import {
35
36
  run_watcher_guard,
36
37
  shell_segments,
37
38
  tail_commands
38
- } from "./chunk-IM3KM25Y.js";
39
+ } from "./chunk-EZLHXKJ6.js";
39
40
  import {
40
41
  find_local_bin_upwards
41
42
  } from "./chunk-3V37IID6.js";
42
43
  import {
43
44
  batch_outcome
44
- } from "./chunk-P3UX7SFN.js";
45
+ } from "./chunk-A2H5I2VD.js";
45
46
  import {
46
47
  investigation_refusal
47
- } from "./chunk-5JX6HXDX.js";
48
+ } from "./chunk-KPZN42RT.js";
48
49
  import {
49
50
  duplicate_read_outcome
50
- } from "./chunk-ZT65LR3U.js";
51
+ } from "./chunk-4GDR2YTV.js";
51
52
  import {
52
53
  investigation_reads
53
- } from "./chunk-RFPHKU7C.js";
54
+ } from "./chunk-WMIR5SNX.js";
54
55
  import {
55
56
  lane_guard_policy,
56
57
  time_batch_guard
57
- } from "./chunk-ASUZGZFT.js";
58
+ } from "./chunk-56LGMJJV.js";
58
59
  import {
59
60
  hook_decision,
60
61
  lane_child_marker,
61
62
  run_issue_number
62
- } from "./chunk-APVOACVT.js";
63
+ } from "./chunk-GBJUERYK.js";
63
64
  import {
64
65
  PLATFORM_TEMP_ROOT,
65
66
  cost_attribute,
@@ -70,7 +71,7 @@ import {
70
71
  lane_paths,
71
72
  stamp_file,
72
73
  time_shell
73
- } from "./chunk-47SPMGOD.js";
74
+ } from "./chunk-PAXMLGSS.js";
74
75
 
75
76
  // scripts/hooks/pretool-guard.ts
76
77
  import { text } from "node:stream/consumers";
@@ -827,8 +828,8 @@ function selected_threshold(values) {
827
828
  function threshold_option(over) {
828
829
  return over === void 0 ? {} : { over };
829
830
  }
830
- function path_option(path6) {
831
- return path6 === void 0 ? {} : { path: path6 };
831
+ function path_option(path7) {
832
+ return path7 === void 0 ? {} : { path: path7 };
832
833
  }
833
834
  function to_options(values) {
834
835
  if (values.cut === true && values.over !== void 0) return void 0;
@@ -2009,9 +2010,133 @@ var shell_body_trigger = {
2009
2010
  import { execFileSync } from "node:child_process";
2010
2011
 
2011
2012
  // scripts/gate/project-checks.ts
2012
- import { existsSync as existsSync3, globSync as globSync2, readFileSync as readFileSync4 } from "node:fs";
2013
+ import { existsSync as existsSync4, globSync as globSync2, readFileSync as readFileSync5 } from "node:fs";
2014
+ import path5 from "node:path";
2015
+
2016
+ // scripts/init/project-profile.ts
2017
+ import { existsSync as existsSync3, readdirSync, readFileSync as readFileSync4 } from "node:fs";
2013
2018
  import path4 from "node:path";
2014
2019
 
2020
+ // scripts/version/kit-descriptor.ts
2021
+ var KIT_PACKAGE_NAME = "@joshuafolkken/kit";
2022
+
2023
+ // scripts/init/project-profile.ts
2024
+ import { execaSync } from "execa";
2025
+ var IGNORED_DIRECTORIES = /* @__PURE__ */ new Set([".git", "node_modules", "dist", "build", ".svelte-kit"]);
2026
+ var WEB_EXTENSIONS = /* @__PURE__ */ new Set([".html", ".css", ".js", ".jsx", ".mjs", ".cjs"]);
2027
+ var TYPESCRIPT_EXTENSIONS = /* @__PURE__ */ new Set([".ts", ".tsx"]);
2028
+ var PROFILE_ARG_LENGTH = 2;
2029
+ var PROFILE_NAMES = {
2030
+ basic: "basic",
2031
+ full: "full",
2032
+ static: "basic",
2033
+ node: "full"
2034
+ };
2035
+ function parse_profile(value) {
2036
+ return typeof value === "string" && Object.hasOwn(PROFILE_NAMES, value) ? PROFILE_NAMES[value] : void 0;
2037
+ }
2038
+ var LEGACY_NAMES = { basic: "static", full: "node" };
2039
+ function with_legacy_profile_names(args) {
2040
+ return args.map((argument, index) => {
2041
+ const profile = args[index - 1] === "--profile" ? parse_profile(argument) : void 0;
2042
+ return profile === void 0 ? argument : LEGACY_NAMES[profile];
2043
+ });
2044
+ }
2045
+ function is_profile_pair(args) {
2046
+ return args.length === PROFILE_ARG_LENGTH && args[0] === "--profile";
2047
+ }
2048
+ function requested_profile(args, usage = "josh init [--profile basic|full]") {
2049
+ if (args.length === 0) return void 0;
2050
+ if (!is_profile_pair(args)) throw new Error(`Usage: ${usage}`);
2051
+ const profile = parse_profile(args[1]);
2052
+ if (profile !== void 0) return profile;
2053
+ throw new Error("Profile must be basic or full");
2054
+ }
2055
+ function read_manifest(root) {
2056
+ const filename = path4.join(root, "package.json");
2057
+ if (!existsSync3(filename)) return void 0;
2058
+ const parsed = JSON.parse(readFileSync4(filename, "utf8"));
2059
+ if (typeof parsed !== "object" || parsed === null || Array.isArray(parsed)) {
2060
+ throw new Error("package.json must contain an object");
2061
+ }
2062
+ return parsed;
2063
+ }
2064
+ function is_record(value) {
2065
+ return typeof value === "object" && value !== null && !Array.isArray(value);
2066
+ }
2067
+ function recorded_profile(manifest) {
2068
+ const { josh } = manifest;
2069
+ return is_record(josh) ? parse_profile(josh["profile"]) : void 0;
2070
+ }
2071
+ function has_build_script(value) {
2072
+ return is_record(value) && ("build" in value || "dev" in value);
2073
+ }
2074
+ function has_project_dependencies(value) {
2075
+ if (!is_record(value)) return false;
2076
+ return Object.keys(value).some((name) => name !== KIT_PACKAGE_NAME);
2077
+ }
2078
+ function inferred_profile(manifest) {
2079
+ if (has_project_dependencies(manifest["dependencies"]) || has_project_dependencies(manifest["devDependencies"])) {
2080
+ return { profile: "full", reason: "package dependencies" };
2081
+ }
2082
+ if (has_build_script(manifest["scripts"])) {
2083
+ return { profile: "full", reason: "build or dev script" };
2084
+ }
2085
+ return { profile: "basic", reason: "metadata-only package.json" };
2086
+ }
2087
+ function resolve_profile(root, requested) {
2088
+ if (requested !== void 0) return { profile: requested, reason: "explicit --profile" };
2089
+ const manifest = read_manifest(root);
2090
+ if (manifest === void 0) return { profile: "basic", reason: "no package.json" };
2091
+ const profile = recorded_profile(manifest);
2092
+ if (profile !== void 0) return { profile, reason: "package.json josh.profile" };
2093
+ return inferred_profile(manifest);
2094
+ }
2095
+ function should_visit(entry) {
2096
+ return entry.isDirectory() && !IGNORED_DIRECTORIES.has(entry.name);
2097
+ }
2098
+ function scan_directory(directory, pending) {
2099
+ const entries = readdirSync(directory, { withFileTypes: true });
2100
+ pending.push(
2101
+ ...entries.filter((entry) => should_visit(entry)).map((entry) => path4.join(directory, entry.name))
2102
+ );
2103
+ return entries.filter((entry) => entry.isFile()).map((entry) => path4.extname(entry.name));
2104
+ }
2105
+ function file_extensions(root) {
2106
+ const pending = [root];
2107
+ const extensions = [];
2108
+ for (const directory of pending) {
2109
+ extensions.push(...scan_directory(directory, pending));
2110
+ }
2111
+ return extensions;
2112
+ }
2113
+ function scan_files(root) {
2114
+ const extensions = file_extensions(root);
2115
+ const has_web = extensions.some((extension) => WEB_EXTENSIONS.has(extension));
2116
+ const has_typescript = extensions.some((extension) => TYPESCRIPT_EXTENSIONS.has(extension));
2117
+ return { has_web, has_typescript };
2118
+ }
2119
+ function has_github_remote(root) {
2120
+ const result = execaSync("git", ["config", "--get", "remote.origin.url"], {
2121
+ cwd: root,
2122
+ reject: false
2123
+ });
2124
+ return result.exitCode === 0 && repo_origin.parse_origin_url(result.stdout) !== void 0;
2125
+ }
2126
+ function inspect_project(root, requested) {
2127
+ const profile = resolve_profile(root, requested);
2128
+ const files = scan_files(root);
2129
+ const has_git = existsSync3(path4.join(root, ".git"));
2130
+ return { ...profile, ...files, has_git, has_github: has_git && has_github_remote(root) };
2131
+ }
2132
+ var project_profile = {
2133
+ parse_profile,
2134
+ resolve_profile,
2135
+ inspect_project,
2136
+ requested_profile,
2137
+ with_legacy_profile_names
2138
+ };
2139
+
2015
2140
  // scripts/test/skip-marker.ts
2016
2141
  var SKIP_MARKER = "\u2014 skipping";
2017
2142
 
@@ -2054,8 +2179,8 @@ var DEPENDENCY_FIELDS = ["dependencies", "devDependencies"];
2054
2179
  var object_schema = z5.record(z5.string(), z5.unknown());
2055
2180
  function project_root(directory) {
2056
2181
  let current = directory;
2057
- while (!existsSync3(path4.join(current, PACKAGE_JSON))) {
2058
- const parent = path4.dirname(current);
2182
+ while (!existsSync4(path5.join(current, PACKAGE_JSON))) {
2183
+ const parent = path5.dirname(current);
2059
2184
  if (parent === current) return directory;
2060
2185
  current = parent;
2061
2186
  }
@@ -2068,7 +2193,7 @@ function has_files(directory, pattern) {
2068
2193
  return globSync2(pattern, { cwd: project_root(directory), exclude: is_ignored }).length > 0;
2069
2194
  }
2070
2195
  function has_config(directory, names) {
2071
- return names.some((name) => existsSync3(path4.join(project_root(directory), name)));
2196
+ return names.some((name) => existsSync4(path5.join(project_root(directory), name)));
2072
2197
  }
2073
2198
  function has_bin(directory, bin_name) {
2074
2199
  return find_local_bin_upwards(directory, bin_name) !== void 0;
@@ -2077,24 +2202,24 @@ function josh_settings(manifest) {
2077
2202
  if (typeof manifest !== "object" || manifest === null || !("josh" in manifest)) return void 0;
2078
2203
  return manifest.josh;
2079
2204
  }
2080
- function is_static_manifest(manifest) {
2205
+ function is_basic_manifest(manifest) {
2081
2206
  const josh = josh_settings(manifest);
2082
2207
  if (typeof josh !== "object" || josh === null || !("profile" in josh)) return false;
2083
- return josh.profile === "static";
2208
+ return project_profile.parse_profile(josh.profile) === "basic";
2084
2209
  }
2085
- function read_manifest(directory) {
2210
+ function read_manifest2(directory) {
2086
2211
  try {
2087
- const manifest_path = path4.join(project_root(directory), PACKAGE_JSON);
2088
- return JSON.parse(readFileSync4(manifest_path, "utf8"));
2212
+ const manifest_path = path5.join(project_root(directory), PACKAGE_JSON);
2213
+ return JSON.parse(readFileSync5(manifest_path, "utf8"));
2089
2214
  } catch {
2090
2215
  return void 0;
2091
2216
  }
2092
2217
  }
2093
- function is_static(directory) {
2094
- return is_static_manifest(read_manifest(directory));
2218
+ function is_basic(directory) {
2219
+ return is_basic_manifest(read_manifest2(directory));
2095
2220
  }
2096
2221
  function is_declared(directory, package_name) {
2097
- const manifest = object_schema.safeParse(read_manifest(directory));
2222
+ const manifest = object_schema.safeParse(read_manifest2(directory));
2098
2223
  if (!manifest.success) return false;
2099
2224
  return DEPENDENCY_FIELDS.some((field) => {
2100
2225
  const dependencies = object_schema.safeParse(manifest.data[field]);
@@ -2109,18 +2234,18 @@ function skip_notice(check3, reason) {
2109
2234
  return `josh ${check3}: ${reason} ${SKIP_MARKER} ${check3}.`;
2110
2235
  }
2111
2236
  function type_check_skip_reason(directory) {
2112
- if (!is_static(directory)) return void 0;
2237
+ if (!is_basic(directory)) return void 0;
2113
2238
  if (!has_files(directory, TYPE_FILES)) return "no TypeScript files were found";
2114
2239
  if (!has_config(directory, TYPE_CONFIGS)) return "no TypeScript configuration was found";
2115
2240
  return missing_tool_reason(directory, "typescript", "tsc");
2116
2241
  }
2117
2242
  function prettier_skip_reason(directory) {
2118
- if (!is_static(directory)) return void 0;
2243
+ if (!is_basic(directory)) return void 0;
2119
2244
  if (!has_files(directory, WEB_FILES)) return "no HTML, CSS or JavaScript files were found";
2120
2245
  return missing_tool_reason(directory, "prettier", "prettier");
2121
2246
  }
2122
2247
  function eslint_skip_reason(directory) {
2123
- if (!is_static(directory)) return void 0;
2248
+ if (!is_basic(directory)) return void 0;
2124
2249
  if (!has_files(directory, SCRIPT_FILES)) return "no JavaScript or TypeScript files were found";
2125
2250
  if (!has_config(directory, ESLINT_CONFIGS)) return "no ESLint configuration was found";
2126
2251
  return missing_tool_reason(directory, "eslint", "eslint");
@@ -2136,7 +2261,7 @@ var project_checks = {
2136
2261
  has_bin,
2137
2262
  has_config,
2138
2263
  has_files,
2139
- is_static,
2264
+ is_basic,
2140
2265
  prettier_skip_reason,
2141
2266
  project_root,
2142
2267
  skip_notice,
@@ -2148,34 +2273,59 @@ var TEST_SUFFIXES = [".e2e.ts", ".test.ts"];
2148
2273
  var EXEMPT_PATHS = [".editorconfig"];
2149
2274
  var EXEMPT_PREFIXES = [".idea/", ".vscode/", "prompts/"];
2150
2275
  var EXEMPT_SUFFIXES = [".md"];
2151
- var STATIC_VISUAL_SUFFIXES = [".html", ".css"];
2276
+ var BASIC_VISUAL_SUFFIXES = [".html", ".css"];
2277
+ var BASIC_REQUIRED_SUFFIXES = [
2278
+ ".ts",
2279
+ ".tsx",
2280
+ ".mts",
2281
+ ".cts",
2282
+ ".js",
2283
+ ".jsx",
2284
+ ".mjs",
2285
+ ".cjs",
2286
+ ".svelte",
2287
+ ".json",
2288
+ ".jsonc",
2289
+ ".json5",
2290
+ ".yaml",
2291
+ ".yml",
2292
+ ".toml"
2293
+ ];
2152
2294
  function normalize(paths) {
2153
- return paths.map((path6) => path6.trim()).filter((path6) => path6 !== "");
2295
+ return paths.map((path7) => path7.trim()).filter((path7) => path7 !== "");
2296
+ }
2297
+ function is_test_file(path7) {
2298
+ return TEST_SUFFIXES.some((suffix) => path7.endsWith(suffix));
2154
2299
  }
2155
- function is_test_file(path6) {
2156
- return TEST_SUFFIXES.some((suffix) => path6.endsWith(suffix));
2300
+ function is_basic_manual(path7, is_basic2) {
2301
+ const lower = path7.toLowerCase();
2302
+ return is_basic2 && BASIC_REQUIRED_SUFFIXES.every((suffix) => !lower.endsWith(suffix));
2157
2303
  }
2158
- function is_static_visual(path6, is_static2) {
2159
- return is_static2 && STATIC_VISUAL_SUFFIXES.some((suffix) => path6.endsWith(suffix));
2304
+ function is_basic_visual(path7) {
2305
+ const lower = path7.toLowerCase();
2306
+ return BASIC_VISUAL_SUFFIXES.some((suffix) => lower.endsWith(suffix));
2160
2307
  }
2161
- function is_exempt(path6, is_static2 = false) {
2162
- if (EXEMPT_PATHS.includes(path6) || is_static_visual(path6, is_static2)) return true;
2163
- return EXEMPT_SUFFIXES.some((suffix) => path6.endsWith(suffix)) || EXEMPT_PREFIXES.some((prefix) => path6.startsWith(prefix));
2308
+ function is_exempt(path7, is_basic2 = false) {
2309
+ if (EXEMPT_PATHS.includes(path7) || is_basic_manual(path7, is_basic2)) return true;
2310
+ return EXEMPT_SUFFIXES.some((suffix) => path7.endsWith(suffix)) || EXEMPT_PREFIXES.some((prefix) => path7.startsWith(prefix));
2164
2311
  }
2165
- function is_runtime(path6, is_static2 = false) {
2166
- return !is_test_file(path6) && !is_exempt(path6, is_static2);
2312
+ function is_runtime(path7, is_basic2 = false) {
2313
+ return !is_test_file(path7) && !is_exempt(path7, is_basic2);
2167
2314
  }
2168
- function verdict_for(paths, is_static2 = false) {
2315
+ function verdict_for(paths, is_basic2 = false) {
2169
2316
  const changed = normalize(paths);
2170
- if (changed.some((path6) => is_test_file(path6))) return "satisfied";
2171
- if (changed.some((path6) => is_runtime(path6, is_static2))) return "required";
2317
+ if (changed.some((path7) => is_test_file(path7))) return "satisfied";
2318
+ if (changed.some((path7) => is_runtime(path7, is_basic2))) return "required";
2172
2319
  return "exempt";
2173
2320
  }
2174
- function runtime_files(paths, is_static2 = false) {
2175
- return normalize(paths).filter((path6) => is_runtime(path6, is_static2));
2321
+ function runtime_files(paths, is_basic2 = false) {
2322
+ return normalize(paths).filter((path7) => is_runtime(path7, is_basic2));
2176
2323
  }
2177
- function exempt_files(paths, is_static2 = false) {
2178
- return normalize(paths).filter((path6) => is_exempt(path6, is_static2));
2324
+ function exempt_files(paths, is_basic2 = false) {
2325
+ return normalize(paths).filter((path7) => is_exempt(path7, is_basic2));
2326
+ }
2327
+ function manual_check_files(paths, is_basic2) {
2328
+ return normalize(paths).filter((path7) => is_exempt(path7, is_basic2) && !is_exempt(path7));
2179
2329
  }
2180
2330
  var test_declared_logic = {
2181
2331
  EXEMPT_PATHS,
@@ -2183,9 +2333,11 @@ var test_declared_logic = {
2183
2333
  EXEMPT_SUFFIXES,
2184
2334
  TEST_SUFFIXES,
2185
2335
  exempt_files,
2336
+ is_basic_visual,
2186
2337
  is_exempt,
2187
2338
  is_runtime,
2188
2339
  is_test_file,
2340
+ manual_check_files,
2189
2341
  runtime_files,
2190
2342
  verdict_for
2191
2343
  };
@@ -2214,7 +2366,7 @@ function source_paths() {
2214
2366
  return injected.paths ?? read_changed_paths_sync();
2215
2367
  }
2216
2368
  function current_verdict(paths = source_paths()) {
2217
- return test_declared_logic.verdict_for(paths, project_checks.is_static(process.cwd()));
2369
+ return test_declared_logic.verdict_for(paths, project_checks.is_basic(process.cwd()));
2218
2370
  }
2219
2371
  function restore_injection(previous) {
2220
2372
  if (previous === void 0) {
@@ -3056,9 +3208,9 @@ async function issue_get_labels_and_body(issue_number) {
3056
3208
  return await issue_view_json(issue_number, "number,state,labels,body");
3057
3209
  }
3058
3210
  async function issue_list_comments(issue_number) {
3059
- const path6 = `${git_gh_api_path.issue_comments_api_path(issue_number)}${git_gh_api_path.FULL_PAGE_QUERY}`;
3211
+ const path7 = `${git_gh_api_path.issue_comments_api_path(issue_number)}${git_gh_api_path.FULL_PAGE_QUERY}`;
3060
3212
  try {
3061
- return await git_gh_exec.exec_gh_api({ path: path6, should_paginate: true });
3213
+ return await git_gh_exec.exec_gh_api({ path: path7, should_paginate: true });
3062
3214
  } catch {
3063
3215
  return void 0;
3064
3216
  }
@@ -3133,8 +3285,8 @@ function is_selected(rest, request) {
3133
3285
  return !git_gh_issue_rest.is_pull_request(rest) && matches_body(rest, request.body_term);
3134
3286
  }
3135
3287
  async function fetch_page(request, page) {
3136
- const path6 = listing_path(request, page);
3137
- const rows = git_gh_issue_rest.parse_rest_issues(await git_gh_exec.exec_gh_api({ path: path6 }));
3288
+ const path7 = listing_path(request, page);
3289
+ const rows = git_gh_issue_rest.parse_rest_issues(await git_gh_exec.exec_gh_api({ path: path7 }));
3138
3290
  return {
3139
3291
  selected: rows.filter((row) => is_selected(row, request)),
3140
3292
  is_full: rows.length >= PER_PAGE
@@ -3375,13 +3527,14 @@ var git_gh_issue = {
3375
3527
  };
3376
3528
 
3377
3529
  // scripts/ci/pr-classification.ts
3378
- import { readFileSync as readFileSync5 } from "node:fs";
3530
+ import { readFileSync as readFileSync6 } from "node:fs";
3379
3531
  import { fileURLToPath as fileURLToPath2 } from "node:url";
3380
- import { execaSync } from "execa";
3532
+ import { execaSync as execaSync2 } from "execa";
3381
3533
  import { z as z8 } from "zod";
3382
3534
  var CLASSIFICATION_SET = new Set(RELEASE_CLASSIFICATION_NAMES);
3383
3535
  var ISSUE_BUG_LABEL = "bug";
3384
- var DECLARATION = /^- リリース分類: (.+)$/gmu;
3536
+ var DECLARATION_PREFIX = "- \u30EA\u30EA\u30FC\u30B9\u5206\u985E: ";
3537
+ var DECLARATION = new RegExp(`^${DECLARATION_PREFIX}(.+)$`, "gmu");
3385
3538
  var LEGACY_LABELS = /* @__PURE__ */ new Set(["semver-major", "semver-minor"]);
3386
3539
  var LABEL = z8.object({ name: z8.string() });
3387
3540
  var USER = z8.object({ login: z8.string() });
@@ -3400,6 +3553,9 @@ function classification_error(labels, author) {
3400
3553
  if (selected.length === 1 || author.endsWith("[bot]")) return void 0;
3401
3554
  return `Choose one release classification: ${RELEASE_CLASSIFICATION_NAMES.join(", ")}`;
3402
3555
  }
3556
+ function declaration(label) {
3557
+ return `${DECLARATION_PREFIX}${label}`;
3558
+ }
3403
3559
  function is_classification(value) {
3404
3560
  return CLASSIFICATION_SET.has(value);
3405
3561
  }
@@ -3425,13 +3581,13 @@ function select_issue_classification(issue_json) {
3425
3581
  const [label] = selected;
3426
3582
  if (label === void 0 || selected.length > 1) {
3427
3583
  throw new Error(
3428
- `Choose one release classification before opening the PR: ${RELEASE_CLASSIFICATION_NAMES.join(", ")}; found: ${selected.join(", ") || "none"}. Add "- \u30EA\u30EA\u30FC\u30B9\u5206\u985E: <label>" to the Issue body.`
3584
+ `Choose one release classification before opening the PR: ${RELEASE_CLASSIFICATION_NAMES.join(", ")}; found: ${selected.join(", ") || "none"}. Add "${DECLARATION_PREFIX}<label>" to the Issue body.`
3429
3585
  );
3430
3586
  }
3431
3587
  return label;
3432
3588
  }
3433
3589
  function fetch_current_labels(repository, pull_number) {
3434
- const result = execaSync(
3590
+ const result = execaSync2(
3435
3591
  "gh",
3436
3592
  ["api", `repos/${repository}/issues/${String(pull_number)}/labels`, "--jq", ".[].name"],
3437
3593
  { reject: false }
@@ -3442,7 +3598,7 @@ function fetch_current_labels(repository, pull_number) {
3442
3598
  return result.stdout.split("\n").filter((name) => name !== "");
3443
3599
  }
3444
3600
  function check_event(event_path, read_labels = fetch_current_labels) {
3445
- const event = EVENT.parse(JSON.parse(readFileSync5(event_path, "utf8")));
3601
+ const event = EVENT.parse(JSON.parse(readFileSync6(event_path, "utf8")));
3446
3602
  const labels = read_labels(event.repository.full_name, event.pull_request.number);
3447
3603
  return classification_error(labels, event.pull_request.user.login);
3448
3604
  }
@@ -3458,6 +3614,7 @@ if (process.argv[1] === fileURLToPath2(import.meta.url)) {
3458
3614
  var pr_classification = {
3459
3615
  classification_error,
3460
3616
  check_event,
3617
+ declaration,
3461
3618
  is_classification,
3462
3619
  select_issue_classification
3463
3620
  };
@@ -3675,9 +3832,9 @@ async function pr_head_reference(pr_number) {
3675
3832
  async function read_comments(branch_name, to_path, to_json) {
3676
3833
  const pr_number = await resolve_pr_number(branch_name);
3677
3834
  if (pr_number === void 0) return void 0;
3678
- const path6 = `${to_path(String(pr_number))}${COMMENTS_QUERY}`;
3835
+ const path7 = `${to_path(String(pr_number))}${COMMENTS_QUERY}`;
3679
3836
  try {
3680
- return to_json(await git_gh_exec.exec_gh_api({ path: path6, should_paginate: true }));
3837
+ return to_json(await git_gh_exec.exec_gh_api({ path: path7, should_paginate: true }));
3681
3838
  } catch {
3682
3839
  return void 0;
3683
3840
  }
@@ -3825,16 +3982,16 @@ var git_gh_pr_rollup = {
3825
3982
 
3826
3983
  // scripts/git/git-gh-pr-snapshot.ts
3827
3984
  var NO_HEAD_SHA_MESSAGE = "gh api answered a pull request with no head commit";
3828
- async function read_object_pages(path6) {
3985
+ async function read_object_pages(path7) {
3829
3986
  return await git_gh_exec.exec_gh_api({
3830
- path: `${path6}${FULL_PAGE_QUERY}`,
3987
+ path: `${path7}${FULL_PAGE_QUERY}`,
3831
3988
  should_paginate: true,
3832
3989
  should_slurp: true
3833
3990
  });
3834
3991
  }
3835
- async function read_array_pages(path6) {
3992
+ async function read_array_pages(path7) {
3836
3993
  return await git_gh_exec.exec_gh_api({
3837
- path: `${path6}${FULL_PAGE_QUERY}`,
3994
+ path: `${path7}${FULL_PAGE_QUERY}`,
3838
3995
  should_paginate: true
3839
3996
  });
3840
3997
  }
@@ -3851,8 +4008,8 @@ async function read_status_check_rollup(commit_sha) {
3851
4008
  return git_gh_pr_rollup.to_status_check_rollup({ check_runs_json, status_json });
3852
4009
  }
3853
4010
  async function pr_get_review_decision(pr_number) {
3854
- const path6 = git_gh_api_path.pull_reviews_api_path(String(pr_number));
3855
- return git_gh_pr_review.to_review_decision(await read_array_pages(path6));
4011
+ const path7 = git_gh_api_path.pull_reviews_api_path(String(pr_number));
4012
+ return git_gh_pr_review.to_review_decision(await read_array_pages(path7));
3856
4013
  }
3857
4014
  async function build_checks_fields(pull) {
3858
4015
  return {
@@ -4477,9 +4634,9 @@ var epic_busy = {
4477
4634
  };
4478
4635
 
4479
4636
  // scripts/run/run-liveness.ts
4480
- import { readFileSync as readFileSync6, realpathSync, statSync } from "node:fs";
4637
+ import { readFileSync as readFileSync7, realpathSync, statSync } from "node:fs";
4481
4638
  import { homedir, tmpdir } from "node:os";
4482
- import path5 from "node:path";
4639
+ import path6 from "node:path";
4483
4640
  import { setTimeout as sleep2 } from "node:timers/promises";
4484
4641
 
4485
4642
  // scripts/agent/agent-event.ts
@@ -4794,12 +4951,12 @@ var ALLOWED_ROOTS = [
4794
4951
  )
4795
4952
  ];
4796
4953
  function is_within(candidate, root) {
4797
- const relative = path5.relative(root, candidate);
4798
- return relative !== "" && !relative.startsWith("..") && !path5.isAbsolute(relative);
4954
+ const relative = path6.relative(root, candidate);
4955
+ return relative !== "" && !relative.startsWith("..") && !path6.isAbsolute(relative);
4799
4956
  }
4800
4957
  function to_safe_path(output_path) {
4801
- const normalized2 = path5.normalize(output_path);
4802
- if (!path5.isAbsolute(normalized2)) return void 0;
4958
+ const normalized2 = path6.normalize(output_path);
4959
+ if (!path6.isAbsolute(normalized2)) return void 0;
4803
4960
  return ALLOWED_ROOTS.some((root) => is_within(normalized2, root)) ? normalized2 : void 0;
4804
4961
  }
4805
4962
  function sample_output(output_path) {
@@ -4813,7 +4970,7 @@ function read_agent_state(output_path) {
4813
4970
  const safe_path = to_safe_path(output_path);
4814
4971
  if (safe_path === void 0) return void 0;
4815
4972
  try {
4816
- const state = agent_event.read(readFileSync6(safe_path, "utf8"));
4973
+ const state = agent_event.read(readFileSync7(safe_path, "utf8"));
4817
4974
  return state.provider === void 0 ? void 0 : state;
4818
4975
  } catch {
4819
4976
  return void 0;
@@ -4961,7 +5118,7 @@ var GH_PR_STATES = /* @__PURE__ */ new Map([
4961
5118
  ["MERGED", MERGED_PR],
4962
5119
  ["OPEN", OPEN_PR]
4963
5120
  ]);
4964
- var CLEAN_REASON = "Nothing was left behind: the tree is clean, HEAD is on the default branch, and this issue has no branch or pull request.";
5121
+ var CLEAN_REASON = "Nothing was left behind: the tree is clean, HEAD is on the default branch, and this issue has no pull request and no branch holding work.";
4965
5122
  var CLEAN_ADVICE = "Start the child.";
4966
5123
  var RESUME_ADVICE = "Reuse the branch and run the whole verification gate from the start \u2014 refactor, `pnpm josh gate`, `/code-review` \u2014 never only the part the interrupted run had not reached. Nobody verified what it already committed.";
4967
5124
  var PARK_ADVICE = "Park the child: add `needs-decision` and comment what was found. Do not delete the branch, do not reopen the pull request, and do not commit on top of it.";
@@ -5076,19 +5233,51 @@ function to_head_branch(name) {
5076
5233
  const separator_index = name.indexOf(REMOTE_SEPARATOR);
5077
5234
  return separator_index === NOT_FOUND_INDEX ? name : name.slice(separator_index + 1);
5078
5235
  }
5236
+ function group_by_head(pairs) {
5237
+ const groups = /* @__PURE__ */ new Map();
5238
+ for (const [name, reference] of pairs) groups.set(name, [...groups.get(name) ?? [], reference]);
5239
+ return groups;
5240
+ }
5079
5241
  async function read_branch_candidates(issue) {
5080
5242
  const pattern = `${issue}${ISSUE_BRANCH_SUFFIX}`;
5081
5243
  const [local, remote] = await Promise.all([
5082
5244
  git_command.branch_names(pattern),
5083
5245
  git_command.branch_names_remote(`${ANY_REMOTE_PREFIX}${pattern}`)
5084
5246
  ]);
5085
- return [.../* @__PURE__ */ new Set([...local, ...remote.map((name) => to_head_branch(name))])];
5247
+ return group_by_head([
5248
+ ...local.map((name) => [name, name]),
5249
+ ...remote.map((reference) => [to_head_branch(reference), reference])
5250
+ ]);
5251
+ }
5252
+ async function has_commits_beyond(base, references) {
5253
+ const counts = await Promise.all(
5254
+ references.map(async (reference) => await git_command.commit_count_beyond(base, reference))
5255
+ );
5256
+ return counts.some((count) => count > 0);
5257
+ }
5258
+ async function is_held_dirty(name, lane) {
5259
+ if (lane?.branch !== name || lane.is_stranded) return false;
5260
+ return await run_hold.is_tree_dirty(lane.directory);
5261
+ }
5262
+ async function read_candidate(name, references, context) {
5263
+ const state = await read_pr_of_branch(name);
5264
+ if (state.pr_state !== NO_PR) return state;
5265
+ if (await has_commits_beyond(context.base, references)) return state;
5266
+ return await is_held_dirty(name, context.lane) ? state : void 0;
5086
5267
  }
5087
5268
  async function read_child_state(issue) {
5088
5269
  const candidates = await read_branch_candidates(issue);
5089
- return pick_child_state(
5090
- await Promise.all(candidates.map(async (name) => await read_pr_of_branch(name)))
5270
+ if (candidates.size === 0) return NO_CHILD_WORK;
5271
+ const [base, lane] = await Promise.all([
5272
+ git_command.default_branch_reference(),
5273
+ lane_registry.find_open_lane(issue)
5274
+ ]);
5275
+ const reads = await Promise.all(
5276
+ [...candidates].map(
5277
+ async ([name, references]) => await read_candidate(name, references, { base, lane })
5278
+ )
5091
5279
  );
5280
+ return pick_child_state(reads.filter((read2) => read2 !== void 0));
5092
5281
  }
5093
5282
  async function check2(issue) {
5094
5283
  require_issue_number(issue);
@@ -1,10 +1,10 @@
1
1
  import {
2
2
  lane_guard_policy,
3
3
  time_batch_guard
4
- } from "./chunk-ASUZGZFT.js";
4
+ } from "./chunk-56LGMJJV.js";
5
5
  import {
6
6
  hook_decision
7
- } from "./chunk-APVOACVT.js";
7
+ } from "./chunk-GBJUERYK.js";
8
8
 
9
9
  // scripts/hooks/batch-guard.ts
10
10
  import { text } from "node:stream/consumers";
@@ -5,7 +5,7 @@ import {
5
5
  import {
6
6
  ESLINT_EDIT_CACHE_FLAGS,
7
7
  time_density_hook
8
- } from "./chunk-47SPMGOD.js";
8
+ } from "./chunk-PAXMLGSS.js";
9
9
 
10
10
  // scripts/hooks/format-edited-file.ts
11
11
  import { existsSync as existsSync2 } from "node:fs";