@outcomeeng/spx 0.6.1 → 0.6.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/cli.js CHANGED
@@ -1598,6 +1598,18 @@ import { join as join5 } from "path";
1598
1598
  import { parse as parseToml, stringify as stringifyToml } from "smol-toml";
1599
1599
  import { parse as parseYaml, parseDocument as parseYamlDocument, stringify as stringifyYaml } from "yaml";
1600
1600
 
1601
+ // src/lib/error-message.ts
1602
+ function toMessage(error) {
1603
+ if (error instanceof Error) return error.message;
1604
+ if (typeof error === "string") return error;
1605
+ const fallback = `[${typeof error}]`;
1606
+ try {
1607
+ return JSON.stringify(error) ?? fallback;
1608
+ } catch {
1609
+ return fallback;
1610
+ }
1611
+ }
1612
+
1601
1613
  // src/domains/agent-environment/config.ts
1602
1614
  var AGENT_ENVIRONMENT_SECTION = "agentEnvironment";
1603
1615
  var AGENT_RUNTIME = {
@@ -1794,15 +1806,15 @@ function validateRuntimes(raw) {
1794
1806
  }
1795
1807
  return { ok: true, value: runtimes };
1796
1808
  }
1797
- function validateRuntimeConfig(path7, raw, defaults4) {
1809
+ function validateRuntimeConfig(path7, raw, defaults5) {
1798
1810
  if (!isRecord(raw)) return { ok: false, error: `${path7} must be an object` };
1799
1811
  const unknown = rejectUnknownFields(path7, raw, AGENT_ENVIRONMENT_RUNTIME_CONFIG_ALLOWED_FIELDS);
1800
1812
  if (!unknown.ok) return unknown;
1801
1813
  const enabledRaw = raw[AGENT_ENVIRONMENT_CONFIG_FIELDS.ENABLED];
1802
- if (enabledRaw === void 0) return { ok: true, value: defaults4 };
1814
+ if (enabledRaw === void 0) return { ok: true, value: defaults5 };
1803
1815
  const enabled = validateBoolean(`${path7}.${AGENT_ENVIRONMENT_CONFIG_FIELDS.ENABLED}`, enabledRaw);
1804
1816
  if (!enabled.ok) return enabled;
1805
- return { ok: true, value: { ...defaults4, enabled: enabled.value } };
1817
+ return { ok: true, value: { ...defaults5, enabled: enabled.value } };
1806
1818
  }
1807
1819
  function validatePluginBootstrap(raw) {
1808
1820
  const sectionPath = `${AGENT_ENVIRONMENT_SECTION}.${AGENT_ENVIRONMENT_CONFIG_FIELDS.PLUGIN_BOOTSTRAP}`;
@@ -1866,8 +1878,8 @@ function validatePluginBootstrap(raw) {
1866
1878
  }
1867
1879
  };
1868
1880
  }
1869
- function validateEntryArray(path7, raw, validateEntry2, defaults4) {
1870
- if (raw === void 0) return { ok: true, value: defaults4 };
1881
+ function validateEntryArray(path7, raw, validateEntry2, defaults5) {
1882
+ if (raw === void 0) return { ok: true, value: defaults5 };
1871
1883
  if (!Array.isArray(raw)) return { ok: false, error: `${path7} must be an array` };
1872
1884
  const entries = [];
1873
1885
  for (const [index, entry] of raw.entries()) {
@@ -2050,6 +2062,82 @@ var agentEnvironmentConfigDescriptor = {
2050
2062
  validate
2051
2063
  };
2052
2064
 
2065
+ // src/domains/diagnose/facts.ts
2066
+ function isRecord2(value) {
2067
+ return typeof value === "object" && value !== null && !Array.isArray(value);
2068
+ }
2069
+ function isNonEmptyString(value) {
2070
+ return typeof value === "string" && value.length > 0;
2071
+ }
2072
+ function isNonEmptyStringArray(value) {
2073
+ return Array.isArray(value) && value.length > 0 && value.every(isNonEmptyString);
2074
+ }
2075
+ function validateMarketplaceIdentity(value, field) {
2076
+ if (!isRecord2(value) || !isNonEmptyString(value.name) || !isNonEmptyString(value.source)) {
2077
+ return { ok: false, error: `${field} must carry a non-empty \`name\` and \`source\`` };
2078
+ }
2079
+ return { ok: true, value: { name: value.name, source: value.source } };
2080
+ }
2081
+
2082
+ // src/domains/diagnose/config.ts
2083
+ var DIAGNOSE_SECTION = "diagnose";
2084
+ var DIAGNOSE_CONFIG_FIELDS = {
2085
+ SPX_FLOOR: "spxFloor",
2086
+ MARKETPLACE: "marketplace",
2087
+ EXPECTED_PLUGINS: "expectedPlugins",
2088
+ CHECKS: "checks"
2089
+ };
2090
+ var defaults = {};
2091
+ function validate2(value) {
2092
+ if (typeof value !== "object" || value === null || Array.isArray(value)) {
2093
+ return { ok: false, error: `${DIAGNOSE_SECTION} section must be an object` };
2094
+ }
2095
+ const candidate = value;
2096
+ const resolved = {};
2097
+ const spxFloor = candidate[DIAGNOSE_CONFIG_FIELDS.SPX_FLOOR];
2098
+ if (spxFloor !== void 0) {
2099
+ if (!isNonEmptyString(spxFloor)) {
2100
+ return { ok: false, error: `${DIAGNOSE_SECTION}.${DIAGNOSE_CONFIG_FIELDS.SPX_FLOOR} must be a non-empty string` };
2101
+ }
2102
+ resolved.spxFloor = spxFloor;
2103
+ }
2104
+ const marketplace = candidate[DIAGNOSE_CONFIG_FIELDS.MARKETPLACE];
2105
+ if (marketplace !== void 0) {
2106
+ const result = validateMarketplaceIdentity(
2107
+ marketplace,
2108
+ `${DIAGNOSE_SECTION}.${DIAGNOSE_CONFIG_FIELDS.MARKETPLACE}`
2109
+ );
2110
+ if (!result.ok) return result;
2111
+ resolved.marketplace = result.value;
2112
+ }
2113
+ const expectedPlugins = candidate[DIAGNOSE_CONFIG_FIELDS.EXPECTED_PLUGINS];
2114
+ if (expectedPlugins !== void 0) {
2115
+ if (!isNonEmptyStringArray(expectedPlugins)) {
2116
+ return {
2117
+ ok: false,
2118
+ error: `${DIAGNOSE_SECTION}.${DIAGNOSE_CONFIG_FIELDS.EXPECTED_PLUGINS} must be a non-empty array of non-empty strings`
2119
+ };
2120
+ }
2121
+ resolved.expectedPlugins = expectedPlugins;
2122
+ }
2123
+ const checks = candidate[DIAGNOSE_CONFIG_FIELDS.CHECKS];
2124
+ if (checks !== void 0) {
2125
+ if (!isNonEmptyStringArray(checks)) {
2126
+ return {
2127
+ ok: false,
2128
+ error: `${DIAGNOSE_SECTION}.${DIAGNOSE_CONFIG_FIELDS.CHECKS} must be a non-empty array of non-empty strings`
2129
+ };
2130
+ }
2131
+ resolved.checks = checks;
2132
+ }
2133
+ return { ok: true, value: resolved };
2134
+ }
2135
+ var diagnoseConfigDescriptor = {
2136
+ section: DIAGNOSE_SECTION,
2137
+ defaults,
2138
+ validate: validate2
2139
+ };
2140
+
2053
2141
  // src/lib/spec-tree/config.ts
2054
2142
  var SPEC_TREE_KIND_CATEGORY_VALUES = {
2055
2143
  NODE: "node",
@@ -2229,7 +2317,7 @@ var SPEC_TREE_NODE_STATE = {
2229
2317
  };
2230
2318
  var SPEC_TREE_SECTION = SPEC_TREE_CONFIG.SECTION;
2231
2319
  function isKind(value) {
2232
- return Object.prototype.hasOwnProperty.call(KIND_REGISTRY, value);
2320
+ return Object.hasOwn(KIND_REGISTRY, value);
2233
2321
  }
2234
2322
  function buildDefaults() {
2235
2323
  return { kinds: { ...KIND_REGISTRY } };
@@ -2238,7 +2326,7 @@ function buildConfigFromKindNames(kindNames) {
2238
2326
  const entries = kindNames.map((kind) => [kind, KIND_REGISTRY[kind]]);
2239
2327
  return { kinds: Object.fromEntries(entries) };
2240
2328
  }
2241
- function validate2(value) {
2329
+ function validate3(value) {
2242
2330
  if (typeof value !== "object" || value === null) {
2243
2331
  return { ok: false, error: `${SPEC_TREE_SECTION} section must be an object` };
2244
2332
  }
@@ -2337,7 +2425,7 @@ function validateKindDefinitionMap(kindEntries) {
2337
2425
  var specTreeConfigDescriptor = {
2338
2426
  section: SPEC_TREE_SECTION,
2339
2427
  defaults: buildDefaults(),
2340
- validate: validate2
2428
+ validate: validate3
2341
2429
  };
2342
2430
 
2343
2431
  // src/lib/file-inclusion/adapters/eslint.ts
@@ -2513,11 +2601,11 @@ var DEFAULT_SCOPE_CONFIG = {
2513
2601
  var DEFAULT_TOOLS_CONFIG = Object.fromEntries(
2514
2602
  Object.entries(TOOL_DEFAULT_FLAGS).map(([name, flag]) => [name, { ignoreFlag: flag }])
2515
2603
  );
2516
- var defaults = {
2604
+ var defaults2 = {
2517
2605
  scope: DEFAULT_SCOPE_CONFIG,
2518
2606
  tools: DEFAULT_TOOLS_CONFIG
2519
2607
  };
2520
- function isRecord2(value) {
2608
+ function isRecord3(value) {
2521
2609
  return typeof value === "object" && value !== null && !Array.isArray(value);
2522
2610
  }
2523
2611
  function validateStringField(section, field, value) {
@@ -2540,7 +2628,7 @@ function rejectUnknownFields2(section, value, allowed) {
2540
2628
  return { ok: true, value: void 0 };
2541
2629
  }
2542
2630
  function validateScope(raw) {
2543
- if (!isRecord2(raw)) {
2631
+ if (!isRecord3(raw)) {
2544
2632
  return {
2545
2633
  ok: false,
2546
2634
  error: `${FILE_INCLUSION_SECTION}.${FILE_INCLUSION_CONFIG_FIELDS.SCOPE} must be an object`
@@ -2597,7 +2685,7 @@ function validateScope(raw) {
2597
2685
  };
2598
2686
  }
2599
2687
  function validateTools(raw) {
2600
- if (!isRecord2(raw)) {
2688
+ if (!isRecord3(raw)) {
2601
2689
  return {
2602
2690
  ok: false,
2603
2691
  error: `${FILE_INCLUSION_SECTION}.${FILE_INCLUSION_CONFIG_FIELDS.TOOLS} must be an object`
@@ -2612,7 +2700,7 @@ function validateTools(raw) {
2612
2700
  error: `${FILE_INCLUSION_SECTION}.${FILE_INCLUSION_CONFIG_FIELDS.TOOLS}.${toolName} is not a recognized tool`
2613
2701
  };
2614
2702
  }
2615
- if (!isRecord2(adapterRaw)) {
2703
+ if (!isRecord3(adapterRaw)) {
2616
2704
  return {
2617
2705
  ok: false,
2618
2706
  error: `${FILE_INCLUSION_SECTION}.${FILE_INCLUSION_CONFIG_FIELDS.TOOLS}.${toolName} must be an object`
@@ -2632,7 +2720,7 @@ function validateTools(raw) {
2632
2720
  }
2633
2721
  return { ok: true, value: tools };
2634
2722
  }
2635
- function validate3(value) {
2723
+ function validate4(value) {
2636
2724
  if (typeof value !== "object" || value === null || Array.isArray(value)) {
2637
2725
  return { ok: false, error: `${FILE_INCLUSION_SECTION} section must be an object` };
2638
2726
  }
@@ -2659,8 +2747,8 @@ function validate3(value) {
2659
2747
  }
2660
2748
  var fileInclusionConfigDescriptor = {
2661
2749
  section: FILE_INCLUSION_SECTION,
2662
- defaults,
2663
- validate: validate3
2750
+ defaults: defaults2,
2751
+ validate: validate4
2664
2752
  };
2665
2753
 
2666
2754
  // src/lib/precommit/config.ts
@@ -2669,7 +2757,7 @@ var PRECOMMIT_DEFAULTS = {
2669
2757
  sourceDirs: ["src/"],
2670
2758
  testPattern: ".test.ts"
2671
2759
  };
2672
- function validate4(value) {
2760
+ function validate5(value) {
2673
2761
  if (typeof value !== "object" || value === null || Array.isArray(value)) {
2674
2762
  return { ok: false, error: `${PRECOMMIT_SECTION} section must be an object` };
2675
2763
  }
@@ -2699,7 +2787,7 @@ function validate4(value) {
2699
2787
  var precommitConfigDescriptor = {
2700
2788
  section: PRECOMMIT_SECTION,
2701
2789
  defaults: PRECOMMIT_DEFAULTS,
2702
- validate: validate4
2790
+ validate: validate5
2703
2791
  };
2704
2792
 
2705
2793
  // src/config/primitives/path-filter.ts
@@ -2736,7 +2824,11 @@ function validatePathFilterConfig(raw, path7) {
2736
2824
  }
2737
2825
  var PATH_SEGMENT_SEPARATOR = "/";
2738
2826
  function normalizePathPrefix(value) {
2739
- return value.replace(/\/+$/, "");
2827
+ let end = value.length;
2828
+ while (end > 0 && value[end - 1] === PATH_SEGMENT_SEPARATOR) {
2829
+ end--;
2830
+ }
2831
+ return value.slice(0, end);
2740
2832
  }
2741
2833
  function pathMatchesPrefix(path7, prefix) {
2742
2834
  const normalizedPath = normalizePathPrefix(path7);
@@ -2760,7 +2852,7 @@ var TESTING_CONFIG_FIELDS = {
2760
2852
  PASSING_SCOPE: "passingScope"
2761
2853
  };
2762
2854
  var DEFAULT_PASSING_SCOPE = resolveDefaultPassingScope();
2763
- var defaults2 = {
2855
+ var defaults3 = {
2764
2856
  passingScope: DEFAULT_PASSING_SCOPE
2765
2857
  };
2766
2858
  function resolveDefaultPassingScope() {
@@ -2773,14 +2865,14 @@ function resolveDefaultPassingScope() {
2773
2865
  }
2774
2866
  return result.value;
2775
2867
  }
2776
- function validate5(value) {
2868
+ function validate6(value) {
2777
2869
  if (typeof value !== "object" || value === null || Array.isArray(value)) {
2778
2870
  return { ok: false, error: `${TESTING_SECTION} section must be an object` };
2779
2871
  }
2780
2872
  const candidate = value;
2781
2873
  const passingScopeRaw = candidate[TESTING_CONFIG_FIELDS.PASSING_SCOPE];
2782
2874
  if (passingScopeRaw === void 0) {
2783
- return { ok: true, value: defaults2 };
2875
+ return { ok: true, value: defaults3 };
2784
2876
  }
2785
2877
  const passingScopeResult = validatePathFilterConfig(
2786
2878
  passingScopeRaw,
@@ -2796,8 +2888,8 @@ function validate5(value) {
2796
2888
  }
2797
2889
  var testingConfigDescriptor = {
2798
2890
  section: TESTING_SECTION,
2799
- defaults: defaults2,
2800
- validate: validate5
2891
+ defaults: defaults3,
2892
+ validate: validate6
2801
2893
  };
2802
2894
 
2803
2895
  // src/validation/literal/config.ts
@@ -2854,7 +2946,13 @@ var LITERAL_DEFAULTS = {
2854
2946
  minStringLength: DEFAULT_MIN_STRING_LENGTH,
2855
2947
  minNumberDigits: DEFAULT_MIN_NUMBER_DIGITS
2856
2948
  };
2857
- function validate6(value) {
2949
+ function isStringArray(value) {
2950
+ return Array.isArray(value) && value.every((item) => typeof item === "string");
2951
+ }
2952
+ function isNonNegativeInteger(value) {
2953
+ return typeof value === "number" && Number.isInteger(value) && value >= 0;
2954
+ }
2955
+ function validate7(value) {
2858
2956
  if (typeof value !== "object" || value === null || Array.isArray(value)) {
2859
2957
  return { ok: false, error: `${LITERAL_SECTION} section must be an object` };
2860
2958
  }
@@ -2864,32 +2962,31 @@ function validate6(value) {
2864
2962
  }
2865
2963
  const presets = candidate["presets"];
2866
2964
  if (presets !== void 0) {
2867
- if (!Array.isArray(presets) || !presets.every((x) => typeof x === "string")) {
2965
+ if (!isStringArray(presets)) {
2868
2966
  return { ok: false, error: `${LITERAL_SECTION}.presets must be an array of strings` };
2869
2967
  }
2870
- for (const id of presets) {
2871
- if (!PRESET_REGISTRY.has(id)) {
2872
- return { ok: false, error: `${LITERAL_SECTION}.presets: unrecognized preset "${id}"` };
2873
- }
2968
+ const unknownPreset = presets.find((id) => !PRESET_REGISTRY.has(id));
2969
+ if (unknownPreset !== void 0) {
2970
+ return { ok: false, error: `${LITERAL_SECTION}.presets: unrecognized preset "${unknownPreset}"` };
2874
2971
  }
2875
2972
  }
2876
2973
  const include = candidate["include"];
2877
- if (include !== void 0 && (!Array.isArray(include) || !include.every((x) => typeof x === "string"))) {
2974
+ if (include !== void 0 && !isStringArray(include)) {
2878
2975
  return { ok: false, error: `${LITERAL_SECTION}.include must be an array of strings` };
2879
2976
  }
2880
2977
  const exclude = candidate["exclude"];
2881
- if (exclude !== void 0 && (!Array.isArray(exclude) || !exclude.every((x) => typeof x === "string"))) {
2978
+ if (exclude !== void 0 && !isStringArray(exclude)) {
2882
2979
  return { ok: false, error: `${LITERAL_SECTION}.exclude must be an array of strings` };
2883
2980
  }
2884
2981
  const minStringLength = candidate["minStringLength"] ?? LITERAL_DEFAULTS.minStringLength;
2885
- if (typeof minStringLength !== "number" || !Number.isInteger(minStringLength) || minStringLength < 0) {
2982
+ if (!isNonNegativeInteger(minStringLength)) {
2886
2983
  return {
2887
2984
  ok: false,
2888
2985
  error: `${LITERAL_SECTION}.minStringLength must be a non-negative integer`
2889
2986
  };
2890
2987
  }
2891
2988
  const minNumberDigits = candidate["minNumberDigits"] ?? LITERAL_DEFAULTS.minNumberDigits;
2892
- if (typeof minNumberDigits !== "number" || !Number.isInteger(minNumberDigits) || minNumberDigits < 0) {
2989
+ if (!isNonNegativeInteger(minNumberDigits)) {
2893
2990
  return {
2894
2991
  ok: false,
2895
2992
  error: `${LITERAL_SECTION}.minNumberDigits must be a non-negative integer`
@@ -2909,7 +3006,7 @@ function validate6(value) {
2909
3006
  var literalConfigDescriptor = {
2910
3007
  section: LITERAL_SECTION,
2911
3008
  defaults: LITERAL_DEFAULTS,
2912
- validate: validate6
3009
+ validate: validate7
2913
3010
  };
2914
3011
 
2915
3012
  // src/validation/config/descriptor.ts
@@ -2925,9 +3022,10 @@ var VALIDATION_PATH_TOOL_SUBSECTIONS = {
2925
3022
  CIRCULAR: "circular",
2926
3023
  KNIP: "knip",
2927
3024
  MARKDOWN: "markdown",
2928
- LITERAL: "literal"
3025
+ LITERAL: "literal",
3026
+ FORMATTING: "formatting"
2929
3027
  };
2930
- var defaults3 = {
3028
+ var defaults4 = {
2931
3029
  paths: {},
2932
3030
  literal: {
2933
3031
  enabled: true,
@@ -2967,7 +3065,7 @@ function validateLiteral(raw) {
2967
3065
  };
2968
3066
  }
2969
3067
  const candidate = raw;
2970
- const enabledRaw = candidate[VALIDATION_ENABLED_FIELD] ?? defaults3.literal.enabled;
3068
+ const enabledRaw = candidate[VALIDATION_ENABLED_FIELD] ?? defaults4.literal.enabled;
2971
3069
  if (typeof enabledRaw !== "boolean") {
2972
3070
  return {
2973
3071
  ok: false,
@@ -2992,7 +3090,7 @@ function validateKnip(raw) {
2992
3090
  };
2993
3091
  }
2994
3092
  const candidate = raw;
2995
- const enabledRaw = candidate[VALIDATION_ENABLED_FIELD] ?? defaults3.knip.enabled;
3093
+ const enabledRaw = candidate[VALIDATION_ENABLED_FIELD] ?? defaults4.knip.enabled;
2996
3094
  if (typeof enabledRaw !== "boolean") {
2997
3095
  return {
2998
3096
  ok: false,
@@ -3001,7 +3099,7 @@ function validateKnip(raw) {
3001
3099
  }
3002
3100
  return { ok: true, value: { enabled: enabledRaw } };
3003
3101
  }
3004
- function validate7(value) {
3102
+ function validate8(value) {
3005
3103
  if (typeof value !== "object" || value === null || Array.isArray(value)) {
3006
3104
  return { ok: false, error: `${VALIDATION_SECTION} section must be an object` };
3007
3105
  }
@@ -3019,8 +3117,8 @@ function validate7(value) {
3019
3117
  }
3020
3118
  var validationConfigDescriptor = {
3021
3119
  section: VALIDATION_SECTION,
3022
- defaults: defaults3,
3023
- validate: validate7
3120
+ defaults: defaults4,
3121
+ validate: validate8
3024
3122
  };
3025
3123
 
3026
3124
  // src/config/registry.ts
@@ -3030,7 +3128,8 @@ var productionRegistry = [
3030
3128
  testingConfigDescriptor,
3031
3129
  fileInclusionConfigDescriptor,
3032
3130
  precommitConfigDescriptor,
3033
- agentEnvironmentConfigDescriptor
3131
+ agentEnvironmentConfigDescriptor,
3132
+ diagnoseConfigDescriptor
3034
3133
  ];
3035
3134
 
3036
3135
  // src/config/descriptor-digest.ts
@@ -3328,7 +3427,7 @@ function setNested(target, path7, value) {
3328
3427
  cursor = fresh;
3329
3428
  }
3330
3429
  }
3331
- cursor[path7[path7.length - 1]] = value;
3430
+ cursor[path7.at(-1)] = value;
3332
3431
  }
3333
3432
  function validateParsedSections(filename, parsed) {
3334
3433
  if (typeof parsed !== "object" || parsed === null || Array.isArray(parsed)) {
@@ -3339,9 +3438,6 @@ function validateParsedSections(filename, parsed) {
3339
3438
  function isFileNotFound(error) {
3340
3439
  return error instanceof Error && "code" in error && error.code === "ENOENT";
3341
3440
  }
3342
- function toMessage(error) {
3343
- return error instanceof Error ? error.message : String(error);
3344
- }
3345
3441
 
3346
3442
  // src/commands/config/defaults.ts
3347
3443
  function defaultsCommand(options, deps) {
@@ -3576,12 +3672,6 @@ var CHECK_NAME = {
3576
3672
  SESSION_STORE: "session-store",
3577
3673
  MARKETPLACE_INSTALL: "marketplace-install"
3578
3674
  };
3579
- function isRecord3(value) {
3580
- return typeof value === "object" && value !== null && !Array.isArray(value);
3581
- }
3582
- function isNonEmptyString(value) {
3583
- return typeof value === "string" && value.length > 0;
3584
- }
3585
3675
  function validateChecks(raw, available) {
3586
3676
  if (!Array.isArray(raw) || raw.length === 0) {
3587
3677
  return { ok: false, error: "manifest `checks` must be a non-empty array of check names" };
@@ -3595,12 +3685,6 @@ function validateChecks(raw, available) {
3595
3685
  }
3596
3686
  return { ok: true, value: raw };
3597
3687
  }
3598
- function validateMarketplace2(raw) {
3599
- if (!isRecord3(raw) || !isNonEmptyString(raw.name) || !isNonEmptyString(raw.source)) {
3600
- return { ok: false, error: "manifest `marketplace` must carry a non-empty `name` and `source`" };
3601
- }
3602
- return { ok: true, value: { name: raw.name, source: raw.source } };
3603
- }
3604
3688
  function parseManifest(rawJson, availableChecks) {
3605
3689
  let parsed;
3606
3690
  try {
@@ -3608,7 +3692,7 @@ function parseManifest(rawJson, availableChecks) {
3608
3692
  } catch (error) {
3609
3693
  return { ok: false, error: `manifest is not valid JSON: ${error.message}` };
3610
3694
  }
3611
- if (!isRecord3(parsed)) {
3695
+ if (!isRecord2(parsed)) {
3612
3696
  return { ok: false, error: "manifest must be a JSON object" };
3613
3697
  }
3614
3698
  const checks = validateChecks(parsed.checks, new Set(availableChecks));
@@ -3621,9 +3705,9 @@ function parseManifest(rawJson, availableChecks) {
3621
3705
  manifest.spxFloor = parsed.spx_floor;
3622
3706
  }
3623
3707
  if (checks.value.includes(CHECK_NAME.MARKETPLACE_INSTALL)) {
3624
- const marketplace = validateMarketplace2(parsed.marketplace);
3708
+ const marketplace = validateMarketplaceIdentity(parsed.marketplace, "manifest `marketplace`");
3625
3709
  if (!marketplace.ok) return marketplace;
3626
- if (!Array.isArray(parsed.expected_plugins) || !parsed.expected_plugins.every(isNonEmptyString)) {
3710
+ if (!isNonEmptyStringArray(parsed.expected_plugins)) {
3627
3711
  return { ok: false, error: "manifest selects `marketplace-install` but carries no `expected_plugins`" };
3628
3712
  }
3629
3713
  manifest.marketplace = marketplace.value;
@@ -3738,21 +3822,70 @@ function renderReport(report2, format2, options) {
3738
3822
  return format2 === DIAGNOSE_FORMAT.JSON ? renderReportJson(report2) : renderReportText(report2, options);
3739
3823
  }
3740
3824
 
3825
+ // src/domains/diagnose/resolve.ts
3826
+ var CHECK_NAMES = new Set(Object.values(CHECK_NAME));
3827
+ function isCheckName(value) {
3828
+ return CHECK_NAMES.has(value);
3829
+ }
3830
+ function resolveDiagnoseFacts(options) {
3831
+ if (options.manifest !== void 0) {
3832
+ return { ok: true, value: options.manifest };
3833
+ }
3834
+ const { config, availableChecks } = options;
3835
+ const configuredChecks = config.checks;
3836
+ let checks;
3837
+ if (configuredChecks === void 0) {
3838
+ checks = availableChecks;
3839
+ } else {
3840
+ const available = new Set(availableChecks);
3841
+ const unavailable = configuredChecks.filter((name) => !isCheckName(name) || !available.has(name));
3842
+ if (unavailable.length > 0) {
3843
+ return {
3844
+ ok: false,
3845
+ error: `diagnose config \`checks\` names checks not available in this build: ${unavailable.join(", ")}`
3846
+ };
3847
+ }
3848
+ checks = configuredChecks.filter(isCheckName);
3849
+ }
3850
+ return {
3851
+ ok: true,
3852
+ value: {
3853
+ checks,
3854
+ spxFloor: config.spxFloor,
3855
+ marketplace: config.marketplace,
3856
+ expectedPlugins: config.expectedPlugins
3857
+ }
3858
+ };
3859
+ }
3860
+
3741
3861
  // src/commands/diagnose/index.ts
3742
- async function diagnoseCommand(options) {
3862
+ async function resolveDiagnoseConfig(productDir) {
3863
+ const loaded = await resolveConfig(productDir, [diagnoseConfigDescriptor]);
3864
+ if (!loaded.ok) return loaded;
3865
+ return { ok: true, value: loaded.value[diagnoseConfigDescriptor.section] };
3866
+ }
3867
+ async function readManifest(fs8, manifestPath, availableChecks) {
3743
3868
  let raw;
3744
3869
  try {
3745
- raw = await options.fs.readFile(options.manifestPath);
3870
+ raw = await fs8.readFile(manifestPath);
3746
3871
  } catch (error) {
3747
- return {
3748
- ok: false,
3749
- error: `cannot read diagnose manifest at ${options.manifestPath}: ${error.message}`
3750
- };
3872
+ return { ok: false, error: `cannot read diagnose manifest at ${manifestPath}: ${error.message}` };
3751
3873
  }
3874
+ return parseManifest(raw, availableChecks);
3875
+ }
3876
+ async function diagnoseCommand(options) {
3752
3877
  const availableChecks = Object.keys(options.registry);
3753
- const manifest = parseManifest(raw, availableChecks);
3754
- if (!manifest.ok) return manifest;
3755
- const report2 = await runDiagnose(manifest.value, options.registry);
3878
+ const manifest = options.manifestPath === void 0 ? void 0 : await readManifest(options.fs, options.manifestPath, availableChecks);
3879
+ if (manifest !== void 0 && !manifest.ok) return manifest;
3880
+ const config = manifest === void 0 ? await resolveDiagnoseConfig(options.productDir) : { ok: true, value: {} };
3881
+ if (!config.ok) return config;
3882
+ const resolved = resolveDiagnoseFacts({
3883
+ manifest: manifest?.value,
3884
+ config: config.value,
3885
+ availableChecks
3886
+ });
3887
+ if (!resolved.ok) return resolved;
3888
+ const report2 = await runDiagnose(resolved.value, options.registry);
3756
3889
  if (!report2.ok) return report2;
3757
3890
  return {
3758
3891
  ok: true,
@@ -4458,6 +4591,7 @@ function sessionStoreRunner(probe) {
4458
4591
  // src/domains/diagnose/checks/spx-reachability.ts
4459
4592
  var SPX_REACHABILITY_VERDICT = {
4460
4593
  REACHABLE: "reachable",
4594
+ PRESENT: "present",
4461
4595
  BELOW_FLOOR: "below-floor",
4462
4596
  UNREACHABLE: "unreachable",
4463
4597
  UNKNOWN: "unknown"
@@ -4508,9 +4642,10 @@ function meetsFloor(version2, floor) {
4508
4642
  }
4509
4643
  var REMEDIATION4 = {
4510
4644
  [SPX_REACHABILITY_VERDICT.REACHABLE]: "spx is on PATH at or above the required floor; no action needed.",
4645
+ [SPX_REACHABILITY_VERDICT.PRESENT]: "spx is on PATH; no version floor is configured to compare against, so only presence is reported.",
4511
4646
  [SPX_REACHABILITY_VERDICT.BELOW_FLOOR]: "Update spx to at least the required floor (pnpm add -g @outcomeeng/spx).",
4512
4647
  [SPX_REACHABILITY_VERDICT.UNREACHABLE]: "Install spx and ensure it resolves on PATH (pnpm add -g @outcomeeng/spx).",
4513
- [SPX_REACHABILITY_VERDICT.UNKNOWN]: "Re-run diagnose; if it persists, verify the manifest carries a valid spx_floor, that spx is on PATH, and that spx --version reports a semver version."
4648
+ [SPX_REACHABILITY_VERDICT.UNKNOWN]: "Re-run diagnose; if it persists, verify the configured or manifest-supplied spx_floor is a valid semver, that spx is on PATH, and that spx --version reports a semver version."
4514
4649
  };
4515
4650
  function record4(verdict, bucket, reading, floor) {
4516
4651
  return {
@@ -4526,12 +4661,15 @@ function record4(verdict, bucket, reading, floor) {
4526
4661
  };
4527
4662
  }
4528
4663
  function classifySpxReachability(reading, floor) {
4529
- if (reading.errored || floor === void 0) {
4664
+ if (reading.errored) {
4530
4665
  return record4(SPX_REACHABILITY_VERDICT.UNKNOWN, VERDICT_BUCKET.UNKNOWN, reading, floor);
4531
4666
  }
4532
4667
  if (reading.resolvedPath === null) {
4533
4668
  return record4(SPX_REACHABILITY_VERDICT.UNREACHABLE, VERDICT_BUCKET.BROKEN, reading, floor);
4534
4669
  }
4670
+ if (floor === void 0) {
4671
+ return record4(SPX_REACHABILITY_VERDICT.PRESENT, VERDICT_BUCKET.HEALTHY, reading, floor);
4672
+ }
4535
4673
  if (reading.version === null) {
4536
4674
  return record4(SPX_REACHABILITY_VERDICT.UNKNOWN, VERDICT_BUCKET.UNKNOWN, reading, floor);
4537
4675
  }
@@ -4639,7 +4777,7 @@ var DIAGNOSE_CLI = {
4639
4777
  COLOR_FLAG: "--color",
4640
4778
  NO_COLOR_FLAG: "--no-color"
4641
4779
  };
4642
- var DIAGNOSE_DOMAIN_DESCRIPTION = "Run deterministic environment-diagnostics checks from a declarative manifest";
4780
+ var DIAGNOSE_DOMAIN_DESCRIPTION = "Run deterministic environment-diagnostics checks, resolving facts from spx.config or a --manifest";
4643
4781
  var DEFAULT_REGISTRY = {
4644
4782
  [CHECK_NAME.SPX_REACHABILITY]: spxReachabilityRunner(defaultSpxReachabilityProbe),
4645
4783
  [CHECK_NAME.SESSION_ENVIRONMENT]: sessionEnvironmentRunner(defaultSessionEnvironmentProbe),
@@ -4655,11 +4793,15 @@ var diagnoseDomain = {
4655
4793
  name: DIAGNOSE_CLI.COMMAND,
4656
4794
  description: DIAGNOSE_DOMAIN_DESCRIPTION,
4657
4795
  register: (program2) => {
4658
- program2.command(DIAGNOSE_CLI.COMMAND).description(DIAGNOSE_DOMAIN_DESCRIPTION).requiredOption(`${DIAGNOSE_CLI.MANIFEST_FLAG} <path>`, "Path to the declarative diagnose manifest").addOption(
4796
+ program2.command(DIAGNOSE_CLI.COMMAND).description(DIAGNOSE_DOMAIN_DESCRIPTION).option(
4797
+ `${DIAGNOSE_CLI.MANIFEST_FLAG} <path>`,
4798
+ "Path to a declarative diagnose manifest that fully instruments the diagnosis"
4799
+ ).addOption(
4659
4800
  new Option(`${DIAGNOSE_CLI.FORMAT_FLAG} <format>`, "Output format").choices([DIAGNOSE_FORMAT.TEXT, DIAGNOSE_FORMAT.JSON]).default(DIAGNOSE_FORMAT.TEXT)
4660
4801
  ).addOption(new Option(`${DIAGNOSE_CLI.COLOR_FLAG}`, "Force colored output")).addOption(new Option(`${DIAGNOSE_CLI.NO_COLOR_FLAG}`, "Disable colored output")).action(async (options) => {
4661
4802
  const result = await diagnoseCommand({
4662
4803
  manifestPath: options.manifest,
4804
+ productDir: process.cwd(),
4663
4805
  format: options.format,
4664
4806
  color: resolveColorChoice({
4665
4807
  flag: options.color,
@@ -5159,6 +5301,84 @@ function createJournal(backend, identity) {
5159
5301
  };
5160
5302
  }
5161
5303
 
5304
+ // src/lib/github-snapshot-sink/gh-api-runner.ts
5305
+ import { execa as execa5 } from "execa";
5306
+ var GH_API = {
5307
+ executable: "gh"
5308
+ };
5309
+ var runGhApi = async (args, options) => {
5310
+ const result = await execa5(GH_API.executable, [...args], {
5311
+ ...options?.input === void 0 ? {} : { input: options.input }
5312
+ });
5313
+ return { stdout: typeof result.stdout === "string" ? result.stdout : "" };
5314
+ };
5315
+
5316
+ // src/lib/github-snapshot-sink/pull-request-comment-client.ts
5317
+ var GITHUB_API_ERROR = {
5318
+ SURFACE_UNSUPPORTED: "the pull-request comment client supports only the pull-request comment surface"
5319
+ };
5320
+ var GITHUB_API = {
5321
+ apiCommand: "api",
5322
+ fieldFlag: "-f",
5323
+ methodFlag: "-X",
5324
+ paginateFlag: "--paginate",
5325
+ slurpFlag: "--slurp",
5326
+ postMethod: "POST",
5327
+ patchMethod: "PATCH",
5328
+ bodyField: "body"
5329
+ };
5330
+ function githubCommentMarkerTag(marker) {
5331
+ return `<!-- ${marker} -->`;
5332
+ }
5333
+ function issueCommentsPath(repository, pullNumber) {
5334
+ return `repos/${repository}/issues/${pullNumber}/comments`;
5335
+ }
5336
+ function issueCommentPath(repository, commentId) {
5337
+ return `repos/${repository}/issues/comments/${commentId}`;
5338
+ }
5339
+ function createGithubPullRequestCommentClient(options) {
5340
+ const { run } = options;
5341
+ return {
5342
+ async upsertPullRequestComment({ pullNumber, marker, body }) {
5343
+ const markedBody = `${body}
5344
+ ${githubCommentMarkerTag(marker)}`;
5345
+ const listed = await run([
5346
+ GITHUB_API.apiCommand,
5347
+ issueCommentsPath(options.repository, pullNumber),
5348
+ GITHUB_API.paginateFlag,
5349
+ GITHUB_API.slurpFlag
5350
+ ]);
5351
+ const comments = JSON.parse(listed.stdout).flat();
5352
+ const existing = comments.find((comment) => comment.body.includes(githubCommentMarkerTag(marker)));
5353
+ if (existing === void 0) {
5354
+ await run([
5355
+ GITHUB_API.apiCommand,
5356
+ issueCommentsPath(options.repository, pullNumber),
5357
+ GITHUB_API.methodFlag,
5358
+ GITHUB_API.postMethod,
5359
+ GITHUB_API.fieldFlag,
5360
+ `${GITHUB_API.bodyField}=${markedBody}`
5361
+ ]);
5362
+ return;
5363
+ }
5364
+ await run([
5365
+ GITHUB_API.apiCommand,
5366
+ issueCommentPath(options.repository, existing.id),
5367
+ GITHUB_API.methodFlag,
5368
+ GITHUB_API.patchMethod,
5369
+ GITHUB_API.fieldFlag,
5370
+ `${GITHUB_API.bodyField}=${markedBody}`
5371
+ ]);
5372
+ },
5373
+ uploadActionsArtifact() {
5374
+ return Promise.reject(new Error(GITHUB_API_ERROR.SURFACE_UNSUPPORTED));
5375
+ },
5376
+ saveActionsCache() {
5377
+ return Promise.reject(new Error(GITHUB_API_ERROR.SURFACE_UNSUPPORTED));
5378
+ }
5379
+ };
5380
+ }
5381
+
5162
5382
  // src/lib/github-snapshot-sink/index.ts
5163
5383
  var SNAPSHOT_SURFACE_KIND = {
5164
5384
  PULL_REQUEST_COMMENT: "pull-request-comment",
@@ -5369,7 +5589,7 @@ async function appendJournalEvent(ref, input, sink, options = {}) {
5369
5589
  try {
5370
5590
  event = await bound.value.journal.append(input);
5371
5591
  } catch (error) {
5372
- return { ok: false, error: `${JOURNAL_RUNTIME_ERROR.APPEND_FAILED}: ${toMessage2(error)}` };
5592
+ return { ok: false, error: `${JOURNAL_RUNTIME_ERROR.APPEND_FAILED}: ${toMessage(error)}` };
5373
5593
  }
5374
5594
  try {
5375
5595
  await sink.emit(event);
@@ -5383,7 +5603,7 @@ async function readJournalEvents(ref, fromCursor, options = {}) {
5383
5603
  try {
5384
5604
  return { ok: true, value: await bound.value.journal.read(fromCursor) };
5385
5605
  } catch (error) {
5386
- return { ok: false, error: `${JOURNAL_RUNTIME_ERROR.READ_FAILED}: ${toMessage2(error)}` };
5606
+ return { ok: false, error: `${JOURNAL_RUNTIME_ERROR.READ_FAILED}: ${toMessage(error)}` };
5387
5607
  }
5388
5608
  }
5389
5609
  async function sealJournalRun(ref, options = {}) {
@@ -5393,7 +5613,7 @@ async function sealJournalRun(ref, options = {}) {
5393
5613
  await bound.value.journal.seal();
5394
5614
  return { ok: true, value: void 0 };
5395
5615
  } catch (error) {
5396
- return { ok: false, error: `${JOURNAL_RUNTIME_ERROR.SEAL_FAILED}: ${toMessage2(error)}` };
5616
+ return { ok: false, error: `${JOURNAL_RUNTIME_ERROR.SEAL_FAILED}: ${toMessage(error)}` };
5397
5617
  }
5398
5618
  }
5399
5619
  async function renderJournalRun(ref, projection, options = {}) {
@@ -5402,12 +5622,9 @@ async function renderJournalRun(ref, projection, options = {}) {
5402
5622
  try {
5403
5623
  return { ok: true, value: await bound.value.journal.render(projection) };
5404
5624
  } catch (error) {
5405
- return { ok: false, error: `${JOURNAL_RUNTIME_ERROR.RENDER_FAILED}: ${toMessage2(error)}` };
5625
+ return { ok: false, error: `${JOURNAL_RUNTIME_ERROR.RENDER_FAILED}: ${toMessage(error)}` };
5406
5626
  }
5407
5627
  }
5408
- function toMessage2(error) {
5409
- return error instanceof Error ? error.message : String(error);
5410
- }
5411
5628
 
5412
5629
  // src/commands/journal/cli.ts
5413
5630
  var JOURNAL_CLI_EXIT_CODE = {
@@ -5600,80 +5817,6 @@ async function journalRenderCommand(scope2, deps = {}) {
5600
5817
  return okResult(JSON.stringify(rendered.value));
5601
5818
  }
5602
5819
 
5603
- // src/commands/journal/github-client.ts
5604
- import { execa as execa5 } from "execa";
5605
- var GITHUB_CLI_ERROR = {
5606
- SURFACE_UNSUPPORTED: "the pull-request comment client supports only the pull-request comment surface"
5607
- };
5608
- var GITHUB_CLI = {
5609
- executable: "gh",
5610
- apiCommand: "api",
5611
- fieldFlag: "-f",
5612
- methodFlag: "-X",
5613
- paginateFlag: "--paginate",
5614
- slurpFlag: "--slurp",
5615
- postMethod: "POST",
5616
- patchMethod: "PATCH",
5617
- bodyField: "body"
5618
- };
5619
- var defaultGhRunner = async (args, options) => {
5620
- const result = await execa5(GITHUB_CLI.executable, [...args], {
5621
- ...options?.input === void 0 ? {} : { input: options.input }
5622
- });
5623
- return { stdout: typeof result.stdout === "string" ? result.stdout : "" };
5624
- };
5625
- function githubCommentMarkerTag(marker) {
5626
- return `<!-- ${marker} -->`;
5627
- }
5628
- function issueCommentsPath(repository, pullNumber) {
5629
- return `repos/${repository}/issues/${pullNumber}/comments`;
5630
- }
5631
- function issueCommentPath(repository, commentId) {
5632
- return `repos/${repository}/issues/comments/${commentId}`;
5633
- }
5634
- function createGithubPrCommentClient(options) {
5635
- const run = options.run ?? defaultGhRunner;
5636
- return {
5637
- async upsertPullRequestComment({ pullNumber, marker, body }) {
5638
- const markedBody = `${body}
5639
- ${githubCommentMarkerTag(marker)}`;
5640
- const listed = await run([
5641
- GITHUB_CLI.apiCommand,
5642
- issueCommentsPath(options.repository, pullNumber),
5643
- GITHUB_CLI.paginateFlag,
5644
- GITHUB_CLI.slurpFlag
5645
- ]);
5646
- const comments = JSON.parse(listed.stdout).flat();
5647
- const existing = comments.find((comment) => comment.body.includes(githubCommentMarkerTag(marker)));
5648
- if (existing === void 0) {
5649
- await run([
5650
- GITHUB_CLI.apiCommand,
5651
- issueCommentsPath(options.repository, pullNumber),
5652
- GITHUB_CLI.methodFlag,
5653
- GITHUB_CLI.postMethod,
5654
- GITHUB_CLI.fieldFlag,
5655
- `${GITHUB_CLI.bodyField}=${markedBody}`
5656
- ]);
5657
- return;
5658
- }
5659
- await run([
5660
- GITHUB_CLI.apiCommand,
5661
- issueCommentPath(options.repository, existing.id),
5662
- GITHUB_CLI.methodFlag,
5663
- GITHUB_CLI.patchMethod,
5664
- GITHUB_CLI.fieldFlag,
5665
- `${GITHUB_CLI.bodyField}=${markedBody}`
5666
- ]);
5667
- },
5668
- uploadActionsArtifact() {
5669
- return Promise.reject(new Error(GITHUB_CLI_ERROR.SURFACE_UNSUPPORTED));
5670
- },
5671
- saveActionsCache() {
5672
- return Promise.reject(new Error(GITHUB_CLI_ERROR.SURFACE_UNSUPPORTED));
5673
- }
5674
- };
5675
- }
5676
-
5677
5820
  // src/lib/process-lifecycle/exit-codes.ts
5678
5821
  var SIGINT_EXIT_CODE = 130;
5679
5822
  var SIGTERM_EXIT_CODE = 143;
@@ -5913,7 +6056,7 @@ function streamBinding() {
5913
6056
  const repository = process.env[JOURNAL_CLI_ENV.GITHUB_REPOSITORY] ?? "";
5914
6057
  return {
5915
6058
  localSink: stdoutStreamSink(),
5916
- githubClient: createGithubPrCommentClient({ repository }),
6059
+ githubClient: createGithubPullRequestCommentClient({ repository, run: runGhApi }),
5917
6060
  githubRepository: repository
5918
6061
  };
5919
6062
  }
@@ -9954,8 +10097,9 @@ function createTestingDomain(deps = defaultTestingCliDependencies) {
9954
10097
  }
9955
10098
  var testingDomain = createTestingDomain();
9956
10099
 
9957
- // src/commands/validation/markdown.ts
9958
- import { relative as relative3 } from "path";
10100
+ // src/commands/validation/formatting.ts
10101
+ import { existsSync } from "fs";
10102
+ import { isAbsolute as isAbsolute3, join as join24, relative as relative3 } from "path";
9959
10103
 
9960
10104
  // src/validation/config/path-filter.ts
9961
10105
  import { isAbsolute as isAbsolute2, relative as relative2 } from "path";
@@ -10052,9 +10196,180 @@ function applyValidationPathFilterToScope(scopeConfig, filter) {
10052
10196
  };
10053
10197
  }
10054
10198
 
10199
+ // src/validation/steps/subprocess-output.ts
10200
+ var VALIDATION_SUBPROCESS_EVENTS = {
10201
+ CLOSE: "close",
10202
+ DATA: "data",
10203
+ DRAIN: "drain",
10204
+ ERROR: "error"
10205
+ };
10206
+ var defaultValidationSubprocessOutputStreams = {
10207
+ stdout: process.stdout,
10208
+ stderr: process.stderr
10209
+ };
10210
+ function forwardValidationSubprocessOutput(child, streams = defaultValidationSubprocessOutputStreams) {
10211
+ child.stdout?.on(VALIDATION_SUBPROCESS_EVENTS.DATA, (chunk) => {
10212
+ forwardChunkWithBackpressure(child.stdout, streams.stdout, chunk);
10213
+ });
10214
+ child.stderr?.on(VALIDATION_SUBPROCESS_EVENTS.DATA, (chunk) => {
10215
+ forwardChunkWithBackpressure(child.stderr, streams.stderr, chunk);
10216
+ });
10217
+ }
10218
+ function forwardChunkWithBackpressure(source, stream, chunk) {
10219
+ const ready = stream.write(chunk);
10220
+ if (ready || stream.once === void 0 || source === null || source === void 0) {
10221
+ return;
10222
+ }
10223
+ source.pause();
10224
+ stream.once(VALIDATION_SUBPROCESS_EVENTS.DRAIN, () => {
10225
+ source.resume();
10226
+ });
10227
+ }
10228
+
10229
+ // src/validation/steps/formatting.ts
10230
+ var DPRINT_COMMAND = "dprint";
10231
+ var DPRINT_CHECK_SUBCOMMAND = "check";
10232
+ var DPRINT_CONFIG_FILENAME = "dprint.jsonc";
10233
+ var defaultFormattingProcessRunner = lifecycleProcessRunner;
10234
+ function buildDprintCheckArgs(options) {
10235
+ return [DPRINT_CHECK_SUBCOMMAND, ...options.files ?? []];
10236
+ }
10237
+ async function validateFormatting(context, runner = defaultFormattingProcessRunner) {
10238
+ const args = buildDprintCheckArgs({ files: context.files });
10239
+ return new Promise((resolve8) => {
10240
+ const child = spawnManagedSubprocess(runner, DPRINT_COMMAND, args, { cwd: context.projectRoot });
10241
+ const chunks = [];
10242
+ const capture = (chunk) => {
10243
+ chunks.push(typeof chunk === "string" ? chunk : Buffer.from(chunk).toString());
10244
+ };
10245
+ child.stdout?.on(VALIDATION_SUBPROCESS_EVENTS.DATA, capture);
10246
+ child.stderr?.on(VALIDATION_SUBPROCESS_EVENTS.DATA, capture);
10247
+ child.on(VALIDATION_SUBPROCESS_EVENTS.CLOSE, (code) => {
10248
+ resolve8({ success: code === 0, output: chunks.join("") });
10249
+ });
10250
+ child.on(VALIDATION_SUBPROCESS_EVENTS.ERROR, (error) => {
10251
+ resolve8({ success: false, output: chunks.join(""), error: error.message });
10252
+ });
10253
+ });
10254
+ }
10255
+
10256
+ // src/commands/validation/messages.ts
10257
+ var VALIDATION_STAGE_DISPLAY_NAMES = {
10258
+ CIRCULAR: "dependency-cruiser",
10259
+ KNIP: "Knip",
10260
+ ESLINT: "ESLint",
10261
+ TYPESCRIPT: "TypeScript",
10262
+ MARKDOWN: "Markdown",
10263
+ LITERAL: "Literal",
10264
+ FORMATTING: "Formatting"
10265
+ };
10266
+ var VALIDATION_SKIP_LABELS = {
10267
+ VERB: "Skipping",
10268
+ CIRCULAR_REASON: "skip-circular",
10269
+ LITERAL_REASON: "skip-literal",
10270
+ DISABLED_BY_PREFIX: "disabled by",
10271
+ TYPESCRIPT_ABSENT_REASON: "TypeScript not detected in project",
10272
+ VALIDATION_PATHS_NO_TARGETS_REASON: "validation paths matched no files",
10273
+ MARKDOWN_NO_SCOPE_REASON: "no markdown files in --files scope",
10274
+ MARKDOWN_NO_DEFAULT_DIRECTORIES_REASON: "no spx/ or docs/ directories found"
10275
+ };
10276
+ var VALIDATION_COMMAND_OUTPUT = {
10277
+ CIRCULAR_FOUND: `${VALIDATION_STAGE_DISPLAY_NAMES.CIRCULAR}: circular dependencies found`,
10278
+ CIRCULAR_NONE_FOUND: `${VALIDATION_STAGE_DISPLAY_NAMES.CIRCULAR}: \u2713 No circular dependencies found`,
10279
+ KNIP_CONFIG_ERROR: `${VALIDATION_STAGE_DISPLAY_NAMES.KNIP}: \u2717 config error`,
10280
+ KNIP_DISABLED: `${VALIDATION_STAGE_DISPLAY_NAMES.KNIP}: skipped (${VALIDATION_SKIP_LABELS.DISABLED_BY_PREFIX} validation.knip.enabled)`,
10281
+ KNIP_SUCCESS: `${VALIDATION_STAGE_DISPLAY_NAMES.KNIP}: \u2713 No unused code found`,
10282
+ KNIP_FAILURE: "Unused code found",
10283
+ ESLINT_SUCCESS: `${VALIDATION_STAGE_DISPLAY_NAMES.ESLINT}: \u2713 No errors found`,
10284
+ ESLINT_FAILURE: `${VALIDATION_STAGE_DISPLAY_NAMES.ESLINT} validation failed`,
10285
+ ESLINT_MISSING_CONFIG: "ESLint config not found: project has tsconfig.json but no eslint.config.{ts,js,mjs,cjs}",
10286
+ TYPESCRIPT_SUCCESS: `${VALIDATION_STAGE_DISPLAY_NAMES.TYPESCRIPT}: \u2713 No type errors`,
10287
+ TYPESCRIPT_FAILURE: `${VALIDATION_STAGE_DISPLAY_NAMES.TYPESCRIPT} validation failed`,
10288
+ MARKDOWN_NO_ISSUES: `${VALIDATION_STAGE_DISPLAY_NAMES.MARKDOWN}: No issues found`,
10289
+ MARKDOWN_ERROR_SUMMARY_SUFFIX: "error(s) found",
10290
+ FORMATTING_NO_ISSUES: `${VALIDATION_STAGE_DISPLAY_NAMES.FORMATTING}: \u2713 No formatting issues found`,
10291
+ FORMATTING_FAILURE_SUMMARY: `${VALIDATION_STAGE_DISPLAY_NAMES.FORMATTING}: unformatted files found`
10292
+ };
10293
+ var CIRCULAR_SKIP_OUTPUT = `${VALIDATION_STAGE_DISPLAY_NAMES.CIRCULAR}: skipped (--${VALIDATION_SKIP_LABELS.CIRCULAR_REASON})`;
10294
+ var CIRCULAR_SKIP_JSON_OUTPUT = JSON.stringify({
10295
+ skipped: true,
10296
+ reason: VALIDATION_SKIP_LABELS.CIRCULAR_REASON
10297
+ });
10298
+ var LITERAL_SKIP_OUTPUT = `${VALIDATION_STAGE_DISPLAY_NAMES.LITERAL}: skipped (--${VALIDATION_SKIP_LABELS.LITERAL_REASON})`;
10299
+ var LITERAL_SKIP_JSON_OUTPUT = JSON.stringify({
10300
+ skipped: true,
10301
+ reason: VALIDATION_SKIP_LABELS.LITERAL_REASON
10302
+ });
10303
+ function formatTypeScriptAbsentSkipMessage(stageName) {
10304
+ return `\u23ED ${VALIDATION_SKIP_LABELS.VERB} ${stageName} (${VALIDATION_SKIP_LABELS.TYPESCRIPT_ABSENT_REASON})`;
10305
+ }
10306
+ function formatValidationPathsNoTargetsSkipMessage(stageName) {
10307
+ return `\u23ED ${VALIDATION_SKIP_LABELS.VERB} ${stageName} (${VALIDATION_SKIP_LABELS.VALIDATION_PATHS_NO_TARGETS_REASON})`;
10308
+ }
10309
+
10310
+ // src/commands/validation/formatting.ts
10311
+ var FORMATTING_COMMAND_OUTPUT = {
10312
+ NO_ISSUES: VALIDATION_COMMAND_OUTPUT.FORMATTING_NO_ISSUES,
10313
+ FAILURE_SUMMARY: VALIDATION_COMMAND_OUTPUT.FORMATTING_FAILURE_SUMMARY,
10314
+ EMPTY_SCOPE_REASON: "no files in scope",
10315
+ NO_CONFIG_SKIP_REASON: `no ${DPRINT_CONFIG_FILENAME} at product root`
10316
+ };
10317
+ var FORMATTING_CONFIG_ERROR_MESSAGE = `${VALIDATION_STAGE_DISPLAY_NAMES.FORMATTING}: \u2717 config error`;
10318
+ async function formattingCommand(options) {
10319
+ const { cwd, files, quiet } = options;
10320
+ const startTime = Date.now();
10321
+ const loaded = await resolveConfig(cwd, [validationConfigDescriptor]);
10322
+ if (!loaded.ok) {
10323
+ return {
10324
+ exitCode: 1,
10325
+ output: `${FORMATTING_CONFIG_ERROR_MESSAGE} \u2014 ${loaded.error}`,
10326
+ durationMs: Date.now() - startTime
10327
+ };
10328
+ }
10329
+ const validationConfig = loaded.value[validationConfigDescriptor.section];
10330
+ if (!existsSync(join24(cwd, DPRINT_CONFIG_FILENAME))) {
10331
+ const output2 = quiet ? "" : `${VALIDATION_STAGE_DISPLAY_NAMES.FORMATTING}: skipped (${FORMATTING_COMMAND_OUTPUT.NO_CONFIG_SKIP_REASON})`;
10332
+ return { exitCode: 0, output: output2, durationMs: Date.now() - startTime };
10333
+ }
10334
+ const pathFilter = validationPathFilterForTool(
10335
+ validationConfig.paths,
10336
+ VALIDATION_PATH_TOOL_SUBSECTIONS.FORMATTING
10337
+ );
10338
+ const hasExplicitScope = files !== void 0 && files.length > 0;
10339
+ const scopedFiles = hasExplicitScope ? files.map((filePath) => isAbsolute3(filePath) ? relative3(cwd, filePath) : filePath).filter((relativePath) => pathPassesValidationFilter(relativePath, pathFilter)) : void 0;
10340
+ if (hasExplicitScope && (scopedFiles === void 0 || scopedFiles.length === 0)) {
10341
+ const output2 = quiet ? "" : `${VALIDATION_STAGE_DISPLAY_NAMES.FORMATTING}: skipped (${FORMATTING_COMMAND_OUTPUT.EMPTY_SCOPE_REASON})`;
10342
+ return { exitCode: 0, output: output2, durationMs: Date.now() - startTime };
10343
+ }
10344
+ const result = await validateFormatting({ projectRoot: cwd, files: scopedFiles });
10345
+ const durationMs = Date.now() - startTime;
10346
+ if (result.success) {
10347
+ return { exitCode: 0, output: quiet ? "" : FORMATTING_COMMAND_OUTPUT.NO_ISSUES, durationMs };
10348
+ }
10349
+ const detail = result.error ?? result.output;
10350
+ const output = [FORMATTING_COMMAND_OUTPUT.FAILURE_SUMMARY, detail].filter((line) => line.length > 0).join("\n");
10351
+ return { exitCode: 1, output, durationMs };
10352
+ }
10353
+
10354
+ // src/validation/languages/formatting.ts
10355
+ var FORMATTING_LANGUAGE_NAME = "formatting";
10356
+ var formattingValidationLanguage = {
10357
+ name: FORMATTING_LANGUAGE_NAME,
10358
+ stages: [
10359
+ {
10360
+ name: VALIDATION_STAGE_DISPLAY_NAMES.FORMATTING,
10361
+ failsPipeline: true,
10362
+ run: (context) => formattingCommand({ cwd: context.cwd, files: context.files, quiet: context.quiet })
10363
+ }
10364
+ ]
10365
+ };
10366
+
10367
+ // src/commands/validation/markdown.ts
10368
+ import { relative as relative4 } from "path";
10369
+
10055
10370
  // src/validation/steps/markdown.ts
10056
- import { existsSync, statSync } from "fs";
10057
- import { basename as basename4, dirname as dirname8, join as join24, relative as pathRelative } from "path";
10371
+ import { existsSync as existsSync2, statSync } from "fs";
10372
+ import { basename as basename4, dirname as dirname8, join as join25, relative as pathRelative } from "path";
10058
10373
  import { main as markdownlintMain } from "markdownlint-cli2";
10059
10374
  import relativeLinksRule from "markdownlint-rule-relative-links";
10060
10375
  var MARKDOWN_DEFAULT_DIRECTORY_NAMES = ["spx", "docs"];
@@ -10093,7 +10408,7 @@ function buildMarkdownlintConfig(directoryName) {
10093
10408
  };
10094
10409
  }
10095
10410
  function getDefaultDirectories(projectRoot) {
10096
- return MARKDOWN_DEFAULT_DIRECTORY_NAMES.map((name) => join24(projectRoot, name)).filter((dir) => existsSync(dir));
10411
+ return MARKDOWN_DEFAULT_DIRECTORY_NAMES.map((name) => join25(projectRoot, name)).filter((dir) => existsSync2(dir));
10097
10412
  }
10098
10413
  function resolveMarkdownValidationTarget(path7, deps = defaultMarkdownValidationTargetDeps) {
10099
10414
  if (isExistingDirectory(path7, deps)) {
@@ -10176,7 +10491,7 @@ async function validateTarget(target, config, projectRoot, ignoreGlobs = []) {
10176
10491
  if (parsed) {
10177
10492
  errors.push({
10178
10493
  ...parsed,
10179
- file: join24(directory, parsed.file)
10494
+ file: join25(directory, parsed.file)
10180
10495
  });
10181
10496
  }
10182
10497
  }
@@ -10215,57 +10530,6 @@ function markdownlintConfigDirectoryName(directory, projectRoot) {
10215
10530
  return basename4(directory);
10216
10531
  }
10217
10532
 
10218
- // src/commands/validation/messages.ts
10219
- var VALIDATION_STAGE_DISPLAY_NAMES = {
10220
- CIRCULAR: "dependency-cruiser",
10221
- KNIP: "Knip",
10222
- ESLINT: "ESLint",
10223
- TYPESCRIPT: "TypeScript",
10224
- MARKDOWN: "Markdown",
10225
- LITERAL: "Literal"
10226
- };
10227
- var VALIDATION_SKIP_LABELS = {
10228
- VERB: "Skipping",
10229
- CIRCULAR_REASON: "skip-circular",
10230
- LITERAL_REASON: "skip-literal",
10231
- DISABLED_BY_PREFIX: "disabled by",
10232
- TYPESCRIPT_ABSENT_REASON: "TypeScript not detected in project",
10233
- VALIDATION_PATHS_NO_TARGETS_REASON: "validation paths matched no files",
10234
- MARKDOWN_NO_SCOPE_REASON: "no markdown files in --files scope",
10235
- MARKDOWN_NO_DEFAULT_DIRECTORIES_REASON: "no spx/ or docs/ directories found"
10236
- };
10237
- var VALIDATION_COMMAND_OUTPUT = {
10238
- CIRCULAR_FOUND: `${VALIDATION_STAGE_DISPLAY_NAMES.CIRCULAR}: circular dependencies found`,
10239
- CIRCULAR_NONE_FOUND: `${VALIDATION_STAGE_DISPLAY_NAMES.CIRCULAR}: \u2713 No circular dependencies found`,
10240
- KNIP_CONFIG_ERROR: `${VALIDATION_STAGE_DISPLAY_NAMES.KNIP}: \u2717 config error`,
10241
- KNIP_DISABLED: `${VALIDATION_STAGE_DISPLAY_NAMES.KNIP}: skipped (${VALIDATION_SKIP_LABELS.DISABLED_BY_PREFIX} validation.knip.enabled)`,
10242
- KNIP_SUCCESS: `${VALIDATION_STAGE_DISPLAY_NAMES.KNIP}: \u2713 No unused code found`,
10243
- KNIP_FAILURE: "Unused code found",
10244
- ESLINT_SUCCESS: `${VALIDATION_STAGE_DISPLAY_NAMES.ESLINT}: \u2713 No errors found`,
10245
- ESLINT_FAILURE: `${VALIDATION_STAGE_DISPLAY_NAMES.ESLINT} validation failed`,
10246
- ESLINT_MISSING_CONFIG: "ESLint config not found: project has tsconfig.json but no eslint.config.{ts,js,mjs,cjs}",
10247
- TYPESCRIPT_SUCCESS: `${VALIDATION_STAGE_DISPLAY_NAMES.TYPESCRIPT}: \u2713 No type errors`,
10248
- TYPESCRIPT_FAILURE: `${VALIDATION_STAGE_DISPLAY_NAMES.TYPESCRIPT} validation failed`,
10249
- MARKDOWN_NO_ISSUES: `${VALIDATION_STAGE_DISPLAY_NAMES.MARKDOWN}: No issues found`,
10250
- MARKDOWN_ERROR_SUMMARY_SUFFIX: "error(s) found"
10251
- };
10252
- var CIRCULAR_SKIP_OUTPUT = `${VALIDATION_STAGE_DISPLAY_NAMES.CIRCULAR}: skipped (--${VALIDATION_SKIP_LABELS.CIRCULAR_REASON})`;
10253
- var CIRCULAR_SKIP_JSON_OUTPUT = JSON.stringify({
10254
- skipped: true,
10255
- reason: VALIDATION_SKIP_LABELS.CIRCULAR_REASON
10256
- });
10257
- var LITERAL_SKIP_OUTPUT = `${VALIDATION_STAGE_DISPLAY_NAMES.LITERAL}: skipped (--${VALIDATION_SKIP_LABELS.LITERAL_REASON})`;
10258
- var LITERAL_SKIP_JSON_OUTPUT = JSON.stringify({
10259
- skipped: true,
10260
- reason: VALIDATION_SKIP_LABELS.LITERAL_REASON
10261
- });
10262
- function formatTypeScriptAbsentSkipMessage(stageName) {
10263
- return `\u23ED ${VALIDATION_SKIP_LABELS.VERB} ${stageName} (${VALIDATION_SKIP_LABELS.TYPESCRIPT_ABSENT_REASON})`;
10264
- }
10265
- function formatValidationPathsNoTargetsSkipMessage(stageName) {
10266
- return `\u23ED ${VALIDATION_SKIP_LABELS.VERB} ${stageName} (${VALIDATION_SKIP_LABELS.VALIDATION_PATHS_NO_TARGETS_REASON})`;
10267
- }
10268
-
10269
10533
  // src/commands/validation/markdown.ts
10270
10534
  var MARKDOWN_COMMAND_OUTPUT = {
10271
10535
  ERROR_SUMMARY_SUFFIX: VALIDATION_COMMAND_OUTPUT.MARKDOWN_ERROR_SUMMARY_SUFFIX,
@@ -10295,7 +10559,7 @@ async function markdownCommand(options) {
10295
10559
  path: path7
10296
10560
  }));
10297
10561
  const targets = unfilteredTargets.filter(
10298
- (target) => pathPassesValidationFilter(relative3(cwd, target.path), pathFilter)
10562
+ (target) => pathPassesValidationFilter(relative4(cwd, target.path), pathFilter)
10299
10563
  );
10300
10564
  const skippedTargets = targetResolutions === void 0 ? [] : targetResolutions.map((resolution) => resolution.skipped).filter((skipped) => skipped !== void 0);
10301
10565
  const skippedOutput = quiet ? [] : skippedTargets.map(formatSkippedFileScope);
@@ -11204,8 +11468,8 @@ var ParseErrorCode;
11204
11468
  })(ParseErrorCode || (ParseErrorCode = {}));
11205
11469
 
11206
11470
  // src/validation/config/scope.ts
11207
- import { existsSync as existsSync2, readdirSync, readFileSync as readFileSync3 } from "fs";
11208
- import { isAbsolute as isAbsolute3, join as join25, relative as relative4, resolve as resolve6 } from "path";
11471
+ import { existsSync as existsSync3, readdirSync, readFileSync as readFileSync3 } from "fs";
11472
+ import { isAbsolute as isAbsolute4, join as join26, relative as relative5, resolve as resolve6 } from "path";
11209
11473
  var TSCONFIG_FILES = {
11210
11474
  full: "tsconfig.json",
11211
11475
  production: "tsconfig.production.json"
@@ -11234,7 +11498,7 @@ var TYPESCRIPT_FALLBACK_INCLUDE_PATTERNS = [
11234
11498
  var TEMPORARY_TSCONFIG_PARENT_SEGMENTS = ["node_modules", ".cache", "spx"];
11235
11499
  var defaultScopeDeps = {
11236
11500
  readFileSync: readFileSync3,
11237
- existsSync: existsSync2,
11501
+ existsSync: existsSync3,
11238
11502
  readdirSync
11239
11503
  };
11240
11504
  var EXPLICIT_TYPESCRIPT_SCOPE_TARGET_KIND = {
@@ -11242,7 +11506,7 @@ var EXPLICIT_TYPESCRIPT_SCOPE_TARGET_KIND = {
11242
11506
  FILE: "file"
11243
11507
  };
11244
11508
  function resolveProjectPath(projectRoot, path7) {
11245
- return isAbsolute3(path7) ? path7 : join25(projectRoot, path7);
11509
+ return isAbsolute4(path7) ? path7 : join26(projectRoot, path7);
11246
11510
  }
11247
11511
  function parseTypeScriptConfig(configPath, deps = defaultScopeDeps) {
11248
11512
  try {
@@ -11281,12 +11545,12 @@ function hasTypeScriptFilesRecursive(dirPath, maxDepth = 2, deps = defaultScopeD
11281
11545
  try {
11282
11546
  const items = deps.readdirSync(dirPath, { withFileTypes: true });
11283
11547
  const hasDirectTsFiles = items.some(
11284
- (item) => item.isFile() && pathHasTypeScriptSourceExtension(item.name) && pathPassesTypeScriptFileDiscoveryExcludes(join25(dirPath, item.name), options)
11548
+ (item) => item.isFile() && pathHasTypeScriptSourceExtension(item.name) && pathPassesTypeScriptFileDiscoveryExcludes(join26(dirPath, item.name), options)
11285
11549
  );
11286
11550
  if (hasDirectTsFiles) return true;
11287
11551
  const subdirs = items.filter((item) => item.isDirectory() && !item.name.startsWith("."));
11288
11552
  for (const subdir of subdirs.slice(0, 5)) {
11289
- if (hasTypeScriptFilesRecursive(join25(dirPath, subdir.name), maxDepth - 1, deps, options)) {
11553
+ if (hasTypeScriptFilesRecursive(join26(dirPath, subdir.name), maxDepth - 1, deps, options)) {
11290
11554
  return true;
11291
11555
  }
11292
11556
  }
@@ -11323,7 +11587,7 @@ function directoryContributesTypeScriptScope(dir, config, projectRoot, deps) {
11323
11587
  return false;
11324
11588
  }
11325
11589
  try {
11326
- return hasTypeScriptFilesRecursive(join25(projectRoot, dir), 2, deps, {
11590
+ return hasTypeScriptFilesRecursive(join26(projectRoot, dir), 2, deps, {
11327
11591
  excludePatterns: config.exclude,
11328
11592
  projectRoot
11329
11593
  });
@@ -11552,13 +11816,13 @@ function normalizeActiveIncludePattern(pattern, projectRoot, deps) {
11552
11816
  function filterActiveIncludePatterns(patterns, excludePatterns, projectRoot, deps) {
11553
11817
  return patterns.map((pattern) => normalizeActiveIncludePattern(pattern, projectRoot, deps)).filter((pattern) => typeScriptScopePatternTargetsTypeScriptSource(pattern)).filter((pattern) => {
11554
11818
  const topLevelDir = getLiteralTopLevelPatternDirectory(pattern);
11555
- return topLevelDir === null || deps.existsSync(join25(projectRoot, topLevelDir));
11819
+ return topLevelDir === null || deps.existsSync(join26(projectRoot, topLevelDir));
11556
11820
  }).filter((pattern) => pathPassesValidationFilter(pattern, { exclude: excludePatterns }));
11557
11821
  }
11558
11822
  function getValidationDirectories(scope2, projectRoot, deps = defaultScopeDeps) {
11559
11823
  const config = resolveTypeScriptConfig(scope2, projectRoot, deps);
11560
11824
  const configDirectories = getTopLevelDirectoriesWithTypeScript(config, projectRoot, deps);
11561
- const existingDirectories = configDirectories.filter((dir) => deps.existsSync(join25(projectRoot, dir)));
11825
+ const existingDirectories = configDirectories.filter((dir) => deps.existsSync(join26(projectRoot, dir)));
11562
11826
  return existingDirectories;
11563
11827
  }
11564
11828
  function getTypeScriptScope(scope2, projectRoot, deps = defaultScopeDeps) {
@@ -11579,14 +11843,14 @@ function pathPassesTypeScriptScope(path7, scopeConfig) {
11579
11843
  return included && !excluded;
11580
11844
  }
11581
11845
  function pathStaysInsideTypeScriptScopeRoot(projectRoot, path7) {
11582
- const resolvedPath = isAbsolute3(path7) ? resolve6(path7) : resolve6(projectRoot, path7);
11583
- const relativePath = relative4(projectRoot, resolvedPath);
11846
+ const resolvedPath = isAbsolute4(path7) ? resolve6(path7) : resolve6(projectRoot, path7);
11847
+ const relativePath = relative5(projectRoot, resolvedPath);
11584
11848
  const segments = normalizeTypeScriptScopePath(relativePath).split(PATH_SEGMENT_SEPARATOR3);
11585
- return relativePath.length === 0 || !segments.includes("..") && !isAbsolute3(relativePath);
11849
+ return relativePath.length === 0 || !segments.includes("..") && !isAbsolute4(relativePath);
11586
11850
  }
11587
11851
  function toProjectRelativeTypeScriptScopePath(projectRoot, path7) {
11588
- const resolvedPath = isAbsolute3(path7) ? resolve6(path7) : resolve6(projectRoot, path7);
11589
- const relativePath = relative4(projectRoot, resolvedPath);
11852
+ const resolvedPath = isAbsolute4(path7) ? resolve6(path7) : resolve6(projectRoot, path7);
11853
+ const relativePath = relative5(projectRoot, resolvedPath);
11590
11854
  return relativePath.length === 0 ? TYPESCRIPT_SCOPE_PROJECT_ROOT : normalizeTypeScriptScopePath(relativePath);
11591
11855
  }
11592
11856
  function toExplicitTypeScriptScopeTarget(projectRoot, originalPath, deps = defaultScopeDeps) {
@@ -11625,10 +11889,10 @@ function explicitTypeScriptScopeTargetPassesScope(target, scopeConfig) {
11625
11889
  if (typeScriptSourcePatterns.length > 0) {
11626
11890
  return typeScriptSourcePatterns.some((pattern) => typeScriptScopePatternIntersectsDirectory(pattern, target.path));
11627
11891
  }
11628
- return pathPassesTypeScriptScope(join25(target.path, TYPESCRIPT_SCOPE_DIRECTORY_PROBE_FILENAME), scopeConfig);
11892
+ return pathPassesTypeScriptScope(join26(target.path, TYPESCRIPT_SCOPE_DIRECTORY_PROBE_FILENAME), scopeConfig);
11629
11893
  }
11630
11894
  function directoryPassesTypeScriptExcludes(directory, scopeConfig) {
11631
- const probePath = join25(directory, TYPESCRIPT_SCOPE_DIRECTORY_PROBE_FILENAME);
11895
+ const probePath = join26(directory, TYPESCRIPT_SCOPE_DIRECTORY_PROBE_FILENAME);
11632
11896
  return !scopeConfig.excludePatterns.some(
11633
11897
  (pattern) => typeScriptScopePatternCoversDirectorySourceSet(pattern, directory) || pathMatchesTypeScriptPattern(probePath, pattern)
11634
11898
  );
@@ -11819,10 +12083,10 @@ function resolvedModulePath(resolvedPath) {
11819
12083
  return resolvedPath;
11820
12084
  }
11821
12085
  }
11822
- function nearestPackageRoot(filePath, existsSync7) {
12086
+ function nearestPackageRoot(filePath, existsSync8) {
11823
12087
  let currentDirectory = path6.dirname(filePath);
11824
12088
  while (true) {
11825
- if (existsSync7(path6.join(currentDirectory, PACKAGE_MANIFEST_FILENAME))) {
12089
+ if (existsSync8(path6.join(currentDirectory, PACKAGE_MANIFEST_FILENAME))) {
11826
12090
  return currentDirectory;
11827
12091
  }
11828
12092
  const parentDirectory = path6.dirname(currentDirectory);
@@ -11832,9 +12096,9 @@ function nearestPackageRoot(filePath, existsSync7) {
11832
12096
  currentDirectory = parentDirectory;
11833
12097
  }
11834
12098
  }
11835
- function bundledToolPath(resolvedPath, existsSync7) {
12099
+ function bundledToolPath(resolvedPath, existsSync8) {
11836
12100
  const bundledFilePath = resolvedModulePath(resolvedPath);
11837
- return nearestPackageRoot(bundledFilePath, existsSync7) ?? path6.dirname(bundledFilePath);
12101
+ return nearestPackageRoot(bundledFilePath, existsSync8) ?? path6.dirname(bundledFilePath);
11838
12102
  }
11839
12103
  async function discoverTool(tool, options = {}) {
11840
12104
  const { projectRoot = process.cwd(), deps = defaultToolDiscoveryDeps } = options;
@@ -11891,7 +12155,7 @@ import {
11891
12155
  cruise as dependencyCruiser
11892
12156
  } from "dependency-cruiser";
11893
12157
  import extractTypeScriptConfig from "dependency-cruiser/config-utl/extract-ts-config";
11894
- import { join as join26 } from "path";
12158
+ import { join as join27 } from "path";
11895
12159
  var DEPENDENCY_CRUISER_MODULE_SYSTEMS = ["es6", "cjs"];
11896
12160
  var DEPENDENCY_CRUISER_TYPESCRIPT_SOURCE_GLOB_SUFFIXES = [...TYPESCRIPT_FALLBACK_INCLUDE_PATTERNS];
11897
12161
  var DEPENDENCY_CRUISER_TYPESCRIPT_SOURCE_PATTERN = String.raw`(?<!\.d)\.(?:[cm]?ts|tsx)$`;
@@ -12131,7 +12395,7 @@ async function validateCircularDependencies(scope2, typescriptScope, projectRoot
12131
12395
  if (analyzeSourcePatterns.length === 0) {
12132
12396
  return { success: true };
12133
12397
  }
12134
- const tsConfigFile = join26(projectRoot, TSCONFIG_FILES[scope2]);
12398
+ const tsConfigFile = join27(projectRoot, TSCONFIG_FILES[scope2]);
12135
12399
  const result = await deps.dependencyCruiser(
12136
12400
  analyzeSourcePatterns,
12137
12401
  buildDependencyCruiserOptions(typescriptScope, projectRoot, tsConfigFile),
@@ -12251,9 +12515,9 @@ async function circularCommand(options, deps = defaultCircularCommandDeps) {
12251
12515
  }
12252
12516
 
12253
12517
  // src/validation/steps/knip.ts
12254
- import { existsSync as existsSync3 } from "fs";
12518
+ import { existsSync as existsSync4 } from "fs";
12255
12519
  import { mkdir as mkdir5, mkdtemp as mkdtemp2, rm, writeFile as writeFile3 } from "fs/promises";
12256
- import { isAbsolute as isAbsolute4, join as join27 } from "path";
12520
+ import { isAbsolute as isAbsolute5, join as join28 } from "path";
12257
12521
  var defaultKnipProcessRunner = lifecycleProcessRunner;
12258
12522
  var KNIP_COMMAND_TOKENS = {
12259
12523
  COMMAND: "knip",
@@ -12262,7 +12526,7 @@ var KNIP_COMMAND_TOKENS = {
12262
12526
  USE_TSCONFIG_FILES_FLAG: "--use-tsconfig-files"
12263
12527
  };
12264
12528
  var defaultKnipDeps = {
12265
- existsSync: existsSync3,
12529
+ existsSync: existsSync4,
12266
12530
  mkdir: mkdir5,
12267
12531
  mkdtemp: mkdtemp2,
12268
12532
  rm,
@@ -12286,7 +12550,7 @@ async function validateKnip2(context, runner = defaultKnipProcessRunner, deps =
12286
12550
  }
12287
12551
  async function runKnipSubprocess(projectRoot, typescriptScope, runner, deps) {
12288
12552
  const scopedTsconfig = typescriptScope.filteredByValidationPaths ? await createScopedKnipTsconfig(projectRoot, typescriptScope, deps) : void 0;
12289
- const localBin = join27(projectRoot, "node_modules", ".bin", "knip");
12553
+ const localBin = join28(projectRoot, "node_modules", ".bin", "knip");
12290
12554
  const binary = deps.existsSync(localBin) ? localBin : KNIP_COMMAND_TOKENS.NPX_COMMAND;
12291
12555
  const baseArgs = scopedTsconfig === void 0 ? [] : [
12292
12556
  KNIP_COMMAND_TOKENS.USE_TSCONFIG_FILES_FLAG,
@@ -12341,11 +12605,11 @@ async function runKnipSubprocess(projectRoot, typescriptScope, runner, deps) {
12341
12605
  });
12342
12606
  }
12343
12607
  async function createScopedKnipTsconfig(projectRoot, typescriptScope, deps) {
12344
- const tempParentDir = join27(projectRoot, ...TEMPORARY_TSCONFIG_PARENT_SEGMENTS);
12608
+ const tempParentDir = join28(projectRoot, ...TEMPORARY_TSCONFIG_PARENT_SEGMENTS);
12345
12609
  await deps.mkdir(tempParentDir, { recursive: true });
12346
- const tempDir = await deps.mkdtemp(join27(tempParentDir, "validate-knip-"));
12347
- const configPath = join27(tempDir, TSCONFIG_FILES.full);
12348
- const toProjectPathPattern = (pattern) => isAbsolute4(pattern) ? pattern : join27(projectRoot, pattern);
12610
+ const tempDir = await deps.mkdtemp(join28(tempParentDir, "validate-knip-"));
12611
+ const configPath = join28(tempDir, TSCONFIG_FILES.full);
12612
+ const toProjectPathPattern = (pattern) => isAbsolute5(pattern) ? pattern : join28(projectRoot, pattern);
12349
12613
  const project = [
12350
12614
  ...typescriptScope.directories.flatMap(
12351
12615
  (directory) => TYPESCRIPT_FALLBACK_INCLUDE_PATTERNS.map((pattern) => `${directory}/${pattern}`)
@@ -12353,7 +12617,7 @@ async function createScopedKnipTsconfig(projectRoot, typescriptScope, deps) {
12353
12617
  ...typescriptScope.filePatterns
12354
12618
  ];
12355
12619
  const config = {
12356
- extends: join27(projectRoot, TSCONFIG_FILES.full),
12620
+ extends: join28(projectRoot, TSCONFIG_FILES.full),
12357
12621
  include: project.map(toProjectPathPattern),
12358
12622
  exclude: typescriptScope.excludePatterns.map(toProjectPathPattern)
12359
12623
  };
@@ -12419,13 +12683,13 @@ async function knipCommand(options, deps = defaultKnipCommandDeps) {
12419
12683
  }
12420
12684
 
12421
12685
  // src/validation/steps/eslint.ts
12422
- import { existsSync as existsSync5 } from "fs";
12423
- import { join as join29 } from "path";
12686
+ import { existsSync as existsSync6 } from "fs";
12687
+ import { join as join30 } from "path";
12424
12688
 
12425
12689
  // src/validation/lint-policy.ts
12426
12690
  import { execFileSync } from "child_process";
12427
- import { existsSync as existsSync4, readdirSync as readdirSync2, readFileSync as readFileSync4, statSync as statSync2 } from "fs";
12428
- import { join as join28 } from "path";
12691
+ import { existsSync as existsSync5, readdirSync as readdirSync2, readFileSync as readFileSync4, statSync as statSync2 } from "fs";
12692
+ import { join as join29 } from "path";
12429
12693
 
12430
12694
  // src/validation/lint-policy-constants.ts
12431
12695
  var LINT_POLICY_MANIFESTS = {
@@ -12471,19 +12735,19 @@ var BASE_BRANCH_REFS = [LINT_POLICY_BASE_REFS.REMOTE_MAIN, LINT_POLICY_BASE_REFS
12471
12735
  function isSpecTreeNodePath(entry) {
12472
12736
  return NODE_SUFFIXES.some((suffix) => entry.endsWith(suffix));
12473
12737
  }
12474
- function readManifest(productDir, file, key) {
12738
+ function readManifest2(productDir, file, key) {
12475
12739
  return parseLintPolicyManifest(
12476
- readFileSync4(join28(productDir, file), "utf-8"),
12740
+ readFileSync4(join29(productDir, file), "utf-8"),
12477
12741
  file,
12478
12742
  key
12479
12743
  );
12480
12744
  }
12481
12745
  function manifestExists(productDir, file) {
12482
- return existsSync4(join28(productDir, file));
12746
+ return existsSync5(join29(productDir, file));
12483
12747
  }
12484
12748
  function findDeprecatedSpecNodePath(productDir) {
12485
12749
  function visit2(relativeDirectory) {
12486
- const absoluteDirectory = join28(productDir, relativeDirectory);
12750
+ const absoluteDirectory = join29(productDir, relativeDirectory);
12487
12751
  for (const entry of readdirSync2(absoluteDirectory, { withFileTypes: true })) {
12488
12752
  if (!entry.isDirectory()) {
12489
12753
  continue;
@@ -12499,8 +12763,8 @@ function findDeprecatedSpecNodePath(productDir) {
12499
12763
  }
12500
12764
  return void 0;
12501
12765
  }
12502
- const specTreeRootPath = join28(productDir, SPEC_TREE_ROOT);
12503
- if (!existsSync4(specTreeRootPath)) {
12766
+ const specTreeRootPath = join29(productDir, SPEC_TREE_ROOT);
12767
+ if (!existsSync5(specTreeRootPath)) {
12504
12768
  return void 0;
12505
12769
  }
12506
12770
  return visit2(SPEC_TREE_ROOT);
@@ -12525,8 +12789,8 @@ function assertManifestEntries(productDir, file, entries, suffixPredicate, suffi
12525
12789
  if (!suffixPredicate(entry)) {
12526
12790
  throw new Error(`${file} entry must ${suffixDescription}: ${entry}`);
12527
12791
  }
12528
- const absoluteEntry = join28(productDir, entry);
12529
- if (!existsSync4(absoluteEntry) || !statSync2(absoluteEntry).isDirectory()) {
12792
+ const absoluteEntry = join29(productDir, entry);
12793
+ if (!existsSync5(absoluteEntry) || !statSync2(absoluteEntry).isDirectory()) {
12530
12794
  throw new Error(`${file} entry does not exist as a directory: ${entry}`);
12531
12795
  }
12532
12796
  }
@@ -12658,11 +12922,11 @@ function validateLintPolicy(productDir) {
12658
12922
  }
12659
12923
  validateTestLintDebtNodeManifest(
12660
12924
  productDir,
12661
- readManifest(productDir, TEST_LINT_DEBT_NODE_MANIFEST_FILE, TEST_LINT_DEBT_NODE_MANIFEST_KEY)
12925
+ readManifest2(productDir, TEST_LINT_DEBT_NODE_MANIFEST_FILE, TEST_LINT_DEBT_NODE_MANIFEST_KEY)
12662
12926
  );
12663
12927
  validateTestOwnedConstantDebtNodeManifest(
12664
12928
  productDir,
12665
- readManifest(
12929
+ readManifest2(
12666
12930
  productDir,
12667
12931
  TEST_OWNED_CONSTANT_DEBT_NODE_MANIFEST_FILE,
12668
12932
  TEST_OWNED_CONSTANT_DEBT_NODE_MANIFEST_KEY
@@ -12674,36 +12938,6 @@ function validateLintPolicy(productDir) {
12674
12938
  }
12675
12939
  }
12676
12940
 
12677
- // src/validation/steps/subprocess-output.ts
12678
- var VALIDATION_SUBPROCESS_EVENTS = {
12679
- CLOSE: "close",
12680
- DATA: "data",
12681
- DRAIN: "drain",
12682
- ERROR: "error"
12683
- };
12684
- var defaultValidationSubprocessOutputStreams = {
12685
- stdout: process.stdout,
12686
- stderr: process.stderr
12687
- };
12688
- function forwardValidationSubprocessOutput(child, streams = defaultValidationSubprocessOutputStreams) {
12689
- child.stdout?.on(VALIDATION_SUBPROCESS_EVENTS.DATA, (chunk) => {
12690
- forwardChunkWithBackpressure(child.stdout, streams.stdout, chunk);
12691
- });
12692
- child.stderr?.on(VALIDATION_SUBPROCESS_EVENTS.DATA, (chunk) => {
12693
- forwardChunkWithBackpressure(child.stderr, streams.stderr, chunk);
12694
- });
12695
- }
12696
- function forwardChunkWithBackpressure(source, stream, chunk) {
12697
- const ready = stream.write(chunk);
12698
- if (ready || stream.once === void 0 || source === null || source === void 0) {
12699
- return;
12700
- }
12701
- source.pause();
12702
- stream.once(VALIDATION_SUBPROCESS_EVENTS.DRAIN, () => {
12703
- source.resume();
12704
- });
12705
- }
12706
-
12707
12941
  // src/validation/steps/eslint.ts
12708
12942
  var defaultEslintProcessRunner = lifecycleProcessRunner;
12709
12943
  var DEFAULT_ESLINT_CONFIG_FILE = "eslint.config.ts";
@@ -12771,8 +13005,8 @@ async function validateESLint(context, runner = defaultEslintProcessRunner, outp
12771
13005
  scopeConfig: context.scopeConfig
12772
13006
  });
12773
13007
  return new Promise((resolve8) => {
12774
- const localBin = join29(projectRoot, ...ESLINT_LOCAL_BIN_SEGMENTS);
12775
- const binary = existsSync5(localBin) ? localBin : "npx";
13008
+ const localBin = join30(projectRoot, ...ESLINT_LOCAL_BIN_SEGMENTS);
13009
+ const binary = existsSync6(localBin) ? localBin : "npx";
12776
13010
  const spawnArgs = binary === "npx" ? eslintArgs : eslintArgs.slice(1);
12777
13011
  const eslintProcess = spawnManagedSubprocess(runner, binary, spawnArgs, {
12778
13012
  cwd: projectRoot
@@ -12873,7 +13107,7 @@ async function lintCommand(options) {
12873
13107
 
12874
13108
  // src/validation/literal/index.ts
12875
13109
  import { readFile as readFile9 } from "fs/promises";
12876
- import { isAbsolute as isAbsolute5, relative as relative6, resolve as resolve7 } from "path";
13110
+ import { isAbsolute as isAbsolute6, relative as relative7, resolve as resolve7 } from "path";
12877
13111
 
12878
13112
  // src/lib/file-inclusion/predicates/ignore-source.ts
12879
13113
  var IGNORE_SOURCE_LAYER = "ignore-source";
@@ -12908,7 +13142,7 @@ var ignoreSourceLayer = makeLayer(
12908
13142
 
12909
13143
  // src/lib/file-inclusion/pipeline.ts
12910
13144
  import { readdir as readdir7 } from "fs/promises";
12911
- import { join as join30, relative as relative5, sep as sep2 } from "path";
13145
+ import { join as join31, relative as relative6, sep as sep2 } from "path";
12912
13146
  var EXPLICIT_OVERRIDE_LAYER = "explicit-override";
12913
13147
  function isNodeError3(err) {
12914
13148
  return err instanceof Error && "code" in err;
@@ -12926,11 +13160,11 @@ async function collectPaths(absoluteDir, projectRoot, result, artifactDirs) {
12926
13160
  for (const entry of dirEntries) {
12927
13161
  if (entry.isDirectory()) {
12928
13162
  if (artifactDirs.has(entry.name)) continue;
12929
- const absolutePath = join30(absoluteDir, entry.name);
13163
+ const absolutePath = join31(absoluteDir, entry.name);
12930
13164
  await collectPaths(absolutePath, projectRoot, result, artifactDirs);
12931
13165
  } else if (entry.isFile()) {
12932
- const absolutePath = join30(absoluteDir, entry.name);
12933
- const rel = relative5(projectRoot, absolutePath);
13166
+ const absolutePath = join31(absoluteDir, entry.name);
13167
+ const rel = relative6(projectRoot, absolutePath);
12934
13168
  result.push(sep2 === "/" ? rel : rel.split(sep2).join("/"));
12935
13169
  }
12936
13170
  }
@@ -13336,8 +13570,8 @@ async function validateLiteralReuse(input) {
13336
13570
  const config = input.config ?? literalConfigDescriptor.defaults;
13337
13571
  const request = input.files ? {
13338
13572
  explicit: input.files.map((f) => {
13339
- const abs = isAbsolute5(f) ? f : resolve7(input.productDir, f);
13340
- return relative6(input.productDir, abs).split(/[\\/]/g).join("/");
13573
+ const abs = isAbsolute6(f) ? f : resolve7(input.productDir, f);
13574
+ return relative7(input.productDir, abs).split(/[\\/]/g).join("/");
13341
13575
  })
13342
13576
  } : { walkRoot: input.productDir };
13343
13577
  const scope2 = await runPipeline(
@@ -13358,7 +13592,7 @@ async function validateLiteralReuse(input) {
13358
13592
  const testOccurrencesByFile = /* @__PURE__ */ new Map();
13359
13593
  const indexedOccurrencesByFile = /* @__PURE__ */ new Map();
13360
13594
  for (const abs of candidateFiles) {
13361
- const rel = relative6(input.productDir, abs).split(/[\\/]/g).join("/");
13595
+ const rel = relative7(input.productDir, abs).split(/[\\/]/g).join("/");
13362
13596
  const content = await readSafe(abs);
13363
13597
  if (content === null) continue;
13364
13598
  const occurrences = collectLiterals(content, rel, collectOptions);
@@ -13596,23 +13830,23 @@ function formatLoc(loc) {
13596
13830
  }
13597
13831
 
13598
13832
  // src/validation/steps/typescript.ts
13599
- import { existsSync as existsSync6, mkdirSync, rmSync, writeFileSync } from "fs";
13833
+ import { existsSync as existsSync7, mkdirSync, rmSync, writeFileSync } from "fs";
13600
13834
  import { mkdtemp as mkdtemp3 } from "fs/promises";
13601
- import { isAbsolute as isAbsolute6, join as join31 } from "path";
13835
+ import { isAbsolute as isAbsolute7, join as join32 } from "path";
13602
13836
  var defaultTypeScriptProcessRunner = lifecycleProcessRunner;
13603
13837
  var defaultTypeScriptDeps = {
13604
13838
  mkdtemp: mkdtemp3,
13605
13839
  mkdirSync,
13606
13840
  writeFileSync,
13607
13841
  rmSync,
13608
- existsSync: existsSync6
13842
+ existsSync: existsSync7
13609
13843
  };
13610
13844
  var TEMPORARY_TSCONFIG_COMPILER_OPTIONS = { noEmit: true };
13611
13845
  var TEMPORARY_TSCONFIG_DIR_PREFIX = "validate-ts-";
13612
13846
  async function createTemporaryTsconfigDir(projectRoot, deps) {
13613
- const parent = join31(projectRoot, ...TEMPORARY_TSCONFIG_PARENT_SEGMENTS);
13847
+ const parent = join32(projectRoot, ...TEMPORARY_TSCONFIG_PARENT_SEGMENTS);
13614
13848
  deps.mkdirSync(parent, { recursive: true });
13615
- return deps.mkdtemp(join31(parent, TEMPORARY_TSCONFIG_DIR_PREFIX));
13849
+ return deps.mkdtemp(join32(parent, TEMPORARY_TSCONFIG_DIR_PREFIX));
13616
13850
  }
13617
13851
  function buildTypeScriptArgs(context) {
13618
13852
  const { scope: scope2, configFile } = context;
@@ -13620,11 +13854,11 @@ function buildTypeScriptArgs(context) {
13620
13854
  }
13621
13855
  async function createFileSpecificTsconfig(scope2, files, projectRoot, deps = defaultTypeScriptDeps) {
13622
13856
  const tempDir = await createTemporaryTsconfigDir(projectRoot, deps);
13623
- const configPath = join31(tempDir, "tsconfig.json");
13857
+ const configPath = join32(tempDir, "tsconfig.json");
13624
13858
  const baseConfigFile = TSCONFIG_FILES[scope2];
13625
- const absoluteFiles = files.map((file) => isAbsolute6(file) ? file : join31(projectRoot, file));
13859
+ const absoluteFiles = files.map((file) => isAbsolute7(file) ? file : join32(projectRoot, file));
13626
13860
  const tempConfig = {
13627
- extends: join31(projectRoot, baseConfigFile),
13861
+ extends: join32(projectRoot, baseConfigFile),
13628
13862
  files: absoluteFiles,
13629
13863
  include: [],
13630
13864
  exclude: [],
@@ -13641,11 +13875,11 @@ async function createFileSpecificTsconfig(scope2, files, projectRoot, deps = def
13641
13875
  }
13642
13876
  async function createScopeFilteredTsconfig(scope2, projectRoot, scopeConfig, deps = defaultTypeScriptDeps) {
13643
13877
  const tempDir = await createTemporaryTsconfigDir(projectRoot, deps);
13644
- const configPath = join31(tempDir, "tsconfig.json");
13878
+ const configPath = join32(tempDir, "tsconfig.json");
13645
13879
  const baseConfigFile = TSCONFIG_FILES[scope2];
13646
- const toProjectPathPattern = (pattern) => isAbsolute6(pattern) ? pattern : join31(projectRoot, pattern);
13880
+ const toProjectPathPattern = (pattern) => isAbsolute7(pattern) ? pattern : join32(projectRoot, pattern);
13647
13881
  const tempConfig = {
13648
- extends: join31(projectRoot, baseConfigFile),
13882
+ extends: join32(projectRoot, baseConfigFile),
13649
13883
  include: scopeConfig.filePatterns.map(toProjectPathPattern),
13650
13884
  exclude: scopeConfig.excludePatterns.map(toProjectPathPattern),
13651
13885
  compilerOptions: TEMPORARY_TSCONFIG_COMPILER_OPTIONS
@@ -13673,7 +13907,7 @@ async function validateTypeScript(context, options = {}) {
13673
13907
  const { configPath, cleanup } = await createFileSpecificTsconfig(scope2, files, projectRoot, deps);
13674
13908
  try {
13675
13909
  return await new Promise((resolve8) => {
13676
- const tscBin = join31(projectRoot, "node_modules", ".bin", "tsc");
13910
+ const tscBin = join32(projectRoot, "node_modules", ".bin", "tsc");
13677
13911
  const tscBinary = deps.existsSync(tscBin) ? tscBin : "npx";
13678
13912
  const tscArgs2 = tscBinary === "npx" ? ["tsc", "--project", configPath] : ["--project", configPath];
13679
13913
  const tscProcess = spawnManagedSubprocess(runner, tscBinary, tscArgs2, {
@@ -13706,7 +13940,7 @@ async function validateTypeScript(context, options = {}) {
13706
13940
  return { success: true, skipped: true };
13707
13941
  }
13708
13942
  const { configPath, cleanup } = await createScopeFilteredTsconfig(scope2, projectRoot, scopeConfig, deps);
13709
- const tscBin = join31(projectRoot, "node_modules", ".bin", "tsc");
13943
+ const tscBin = join32(projectRoot, "node_modules", ".bin", "tsc");
13710
13944
  tool = deps.existsSync(tscBin) ? tscBin : "npx";
13711
13945
  tscArgs = tool === "npx" ? ["tsc", "--project", configPath] : ["--project", configPath];
13712
13946
  return new Promise((resolve8) => {
@@ -13728,7 +13962,7 @@ async function validateTypeScript(context, options = {}) {
13728
13962
  });
13729
13963
  });
13730
13964
  } else {
13731
- const tscBin = join31(projectRoot, "node_modules", ".bin", "tsc");
13965
+ const tscBin = join32(projectRoot, "node_modules", ".bin", "tsc");
13732
13966
  tool = deps.existsSync(tscBin) ? tscBin : "npx";
13733
13967
  const rawArgs = buildTypeScriptArgs({ scope: scope2, configFile });
13734
13968
  tscArgs = tool === "npx" ? rawArgs : rawArgs.slice(1);
@@ -13905,11 +14139,12 @@ var typescriptValidationLanguage = {
13905
14139
 
13906
14140
  // src/validation/registry.ts
13907
14141
  var validationRegistry = {
13908
- languages: [typescriptValidationLanguage, markdownValidationLanguage]
14142
+ languages: [typescriptValidationLanguage, markdownValidationLanguage, formattingValidationLanguage]
13909
14143
  };
13910
- var validationPipelineStages = validationRegistry.languages.flatMap(
13911
- (language) => language.stages
13912
- );
14144
+ function composeValidationPipelineStages(languages) {
14145
+ return languages.flatMap((language) => language.stages);
14146
+ }
14147
+ var validationPipelineStages = composeValidationPipelineStages(validationRegistry.languages);
13913
14148
  var VALIDATION_PIPELINE_TOTAL_STEPS = validationPipelineStages.length;
13914
14149
 
13915
14150
  // src/commands/validation/format.ts
@@ -13972,7 +14207,7 @@ async function allCommand(options) {
13972
14207
  // src/validation/literal/allowlist-existing.ts
13973
14208
  import { randomBytes } from "crypto";
13974
14209
  import { rename as rename4, writeFile as writeFile4 } from "fs/promises";
13975
- import { dirname as dirname9, join as join32 } from "path";
14210
+ import { dirname as dirname9, join as join33 } from "path";
13976
14211
  var EXIT_OK = 0;
13977
14212
  var EXIT_ERROR = 1;
13978
14213
  var INCLUDE_FIELD = "include";
@@ -13992,7 +14227,7 @@ var productionWriter = {
13992
14227
  async write(filePath, content) {
13993
14228
  const dir = dirname9(filePath);
13994
14229
  const random = randomBytes(RANDOM_TOKEN_BYTES).toString("hex");
13995
- const tmpPath = join32(dir, `${TEMP_FILE_PREFIX}${random}${TEMP_FILE_SUFFIX}`);
14230
+ const tmpPath = join33(dir, `${TEMP_FILE_PREFIX}${random}${TEMP_FILE_SUFFIX}`);
13996
14231
  await writeFile4(tmpPath, content, "utf8");
13997
14232
  await rename4(tmpPath, filePath);
13998
14233
  }
@@ -14098,6 +14333,10 @@ var validationCliDefinition = {
14098
14333
  alias: "md",
14099
14334
  description: "Validate markdown link integrity and structure"
14100
14335
  },
14336
+ format: {
14337
+ commandName: "format",
14338
+ description: "Check code formatting with dprint"
14339
+ },
14101
14340
  all: {
14102
14341
  commandName: "all",
14103
14342
  description: "Run all validations"
@@ -14278,6 +14517,16 @@ function registerValidationCommands(validationCmd) {
14278
14517
  process.exit(result.exitCode);
14279
14518
  });
14280
14519
  addCommonOptions(markdownCmd);
14520
+ const formatCmd = addValidationSubcommand(validationCmd, subcommands.format).action(async (options) => {
14521
+ const result = await formattingCommand({
14522
+ cwd: process.cwd(),
14523
+ files: options.files,
14524
+ quiet: options.quiet
14525
+ });
14526
+ if (result.output) console.log(result.output);
14527
+ process.exit(result.exitCode);
14528
+ });
14529
+ addCommonOptions(formatCmd);
14281
14530
  const allCmd = addValidationSubcommand(validationCmd, subcommands.all).option("--fix", "Auto-fix ESLint issues").option(allValidationCliOptions.skipCircular.flag, allValidationCliOptions.skipCircular.description).option(allValidationCliOptions.skipLiteral.flag, allValidationCliOptions.skipLiteral.description).action(async (options) => {
14282
14531
  const result = await allCommand({
14283
14532
  cwd: process.cwd(),