@outcomeeng/spx 0.6.18 → 0.6.19

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 (3) hide show
  1. package/dist/cli.js +1293 -742
  2. package/dist/cli.js.map +1 -1
  3. package/package.json +2 -2
package/dist/cli.js CHANGED
@@ -1,5 +1,5 @@
1
1
  // src/cli.ts
2
- import { createRequire as createRequire2 } from "module";
2
+ import { createRequire as createRequire3 } from "module";
3
3
 
4
4
  // src/interfaces/cli/program.ts
5
5
  import { Command } from "commander";
@@ -14647,6 +14647,9 @@ function formatMissingCitedDecisionError(citedPath, citingPath) {
14647
14647
  function specContextBootstrap(nodeCount) {
14648
14648
  return nodeCount === 0;
14649
14649
  }
14650
+ function compareSpecContextOrdinal(left, right) {
14651
+ return left < right ? -1 : left > right ? 1 : 0;
14652
+ }
14650
14653
  var SPEC_CONTEXT_DIGEST_ALGORITHM = "sha256";
14651
14654
  function specContextDigest(rawBytes) {
14652
14655
  return `${SPEC_CONTEXT_DIGEST_ALGORITHM}:${createHash4(SPEC_CONTEXT_DIGEST_ALGORITHM).update(rawBytes).digest("hex")}`;
@@ -14661,9 +14664,6 @@ function formatUnreadableContextDocumentError(path4) {
14661
14664
  return `Spec context document unreadable: ${path4}`;
14662
14665
  }
14663
14666
 
14664
- // src/lib/node-status/ci-projection.ts
14665
- import { parse as parseYaml3 } from "yaml";
14666
-
14667
14667
  // src/lib/node-status/classify.ts
14668
14668
  var NODE_STATUS_SCHEMA_VERSION = 1;
14669
14669
  var NODE_STATUS_JSON_INDENT = 2;
@@ -14739,9 +14739,11 @@ function verificationPassed(verification) {
14739
14739
  import { readFileSync } from "fs";
14740
14740
  import { join as join19 } from "path";
14741
14741
  var NODE_STATUS_EXCLUDE_FILENAME = "EXCLUDE";
14742
- var PATH_SEGMENT_SEPARATOR2 = "/";
14743
- var CURRENT_DIRECTORY_SEGMENT = ".";
14744
- var PARENT_DIRECTORY_SEGMENT = "..";
14742
+ var NODE_STATUS_EXCLUDE_PATH_GRAMMAR = {
14743
+ SEGMENT_SEPARATOR: "/",
14744
+ CURRENT_DIRECTORY_SEGMENT: ".",
14745
+ PARENT_DIRECTORY_SEGMENT: ".."
14746
+ };
14745
14747
  function isNodeError(err) {
14746
14748
  return err instanceof Error && "code" in err;
14747
14749
  }
@@ -14752,14 +14754,17 @@ function parseExcludeEntries(content) {
14752
14754
  return content.split("\n").map((line) => line.trim()).filter((line) => line.length > 0 && !line.startsWith("#")).map(validateExcludeEntry);
14753
14755
  }
14754
14756
  function validateExcludeEntry(entry) {
14755
- const segments = entry.split(PATH_SEGMENT_SEPARATOR2);
14756
- if (entry.startsWith(PATH_SEGMENT_SEPARATOR2) || segments.some(
14757
- (segment) => segment.length === 0 || segment === CURRENT_DIRECTORY_SEGMENT || segment === PARENT_DIRECTORY_SEGMENT
14757
+ const segments = entry.split(NODE_STATUS_EXCLUDE_PATH_GRAMMAR.SEGMENT_SEPARATOR);
14758
+ if (entry.startsWith(NODE_STATUS_EXCLUDE_PATH_GRAMMAR.SEGMENT_SEPARATOR) || segments.some(
14759
+ (segment) => segment.length === 0 || segment === NODE_STATUS_EXCLUDE_PATH_GRAMMAR.CURRENT_DIRECTORY_SEGMENT || segment === NODE_STATUS_EXCLUDE_PATH_GRAMMAR.PARENT_DIRECTORY_SEGMENT
14758
14760
  )) {
14759
- throw new Error(`Invalid ${SPEC_TREE_CONFIG.ROOT_DIRECTORY}/${NODE_STATUS_EXCLUDE_FILENAME} entry: ${entry}`);
14761
+ throw new Error(nodeStatusInvalidExcludeEntryMessage(entry));
14760
14762
  }
14761
14763
  return entry;
14762
14764
  }
14765
+ function nodeStatusInvalidExcludeEntryMessage(entry) {
14766
+ return `Invalid ${SPEC_TREE_CONFIG.ROOT_DIRECTORY}/${NODE_STATUS_EXCLUDE_FILENAME} entry: ${entry}`;
14767
+ }
14763
14768
  function createNodeStatusExcludeReader(productDir) {
14764
14769
  let content;
14765
14770
  try {
@@ -14961,12 +14966,14 @@ async function updateNodeStatus(options) {
14961
14966
  continue;
14962
14967
  }
14963
14968
  const evidence = evidenceByNode.get(node.id) ?? [];
14969
+ const statusPath = nodeStatusPath(productDir, node.id);
14970
+ const committedVerification = readNodeStatus(dirname11(statusPath))?.[NODE_STATUS_FIELD.VERIFICATION];
14964
14971
  const verification = await resolveVerification(node, {
14965
14972
  evidence,
14966
14973
  isExcluded: excludeReader.isExcluded(node),
14967
- resolveOutcome
14974
+ resolveOutcome,
14975
+ committedOutcomes: testOutcomes(committedVerification)
14968
14976
  });
14969
- const statusPath = nodeStatusPath(productDir, node.id);
14970
14977
  liveStatusPaths.add(statusPath);
14971
14978
  await writeNodeStatus(statusPath, verification);
14972
14979
  }
@@ -14977,7 +14984,8 @@ async function resolveVerification(node, input) {
14977
14984
  if (input.isExcluded) return createTestVerification(input.evidence, NODE_STATUS_EVIDENCE_OUTCOME.NOT_RUN);
14978
14985
  return createTestVerificationFromOutcomes(
14979
14986
  input.evidence,
14980
- await input.resolveOutcome(node.id, evidencePaths(input.evidence))
14987
+ await input.resolveOutcome(node.id, evidencePaths(input.evidence)),
14988
+ input.committedOutcomes
14981
14989
  );
14982
14990
  }
14983
14991
  function collectEvidenceByNode(snapshot) {
@@ -14993,16 +15001,23 @@ function collectEvidenceByNode(snapshot) {
14993
15001
  }
14994
15002
  function createTestVerification(evidence, outcome) {
14995
15003
  const outcomes = Object.fromEntries(evidence.map((entry) => [evidencePath(entry), outcome]));
14996
- return createTestVerificationFromOutcomes(evidence, outcomes);
15004
+ return createTestVerificationFromOutcomes(evidence, outcomes, {});
14997
15005
  }
14998
- function createTestVerificationFromOutcomes(evidence, resolvedOutcomes) {
15006
+ function createTestVerificationFromOutcomes(evidence, resolvedOutcomes, committedOutcomes) {
14999
15007
  const outcomes = {};
15000
15008
  for (const entry of evidence) {
15001
15009
  const path4 = evidencePath(entry);
15002
- outcomes[path4] = resolvedOutcomes[path4] ?? NODE_STATUS_EVIDENCE_OUTCOME.NOT_RUN;
15010
+ outcomes[path4] = resolvedOutcomes[path4] ?? committedOutcomes[path4] ?? NODE_STATUS_EVIDENCE_OUTCOME.NOT_RUN;
15003
15011
  }
15004
15012
  return { [NODE_STATUS_VERIFICATION_MECHANISM.TEST]: createNodeStatusMechanismRecord(outcomes) };
15005
15013
  }
15014
+ function testOutcomes(verification) {
15015
+ const record7 = verification?.[NODE_STATUS_VERIFICATION_MECHANISM.TEST];
15016
+ if (record7 === void 0) return {};
15017
+ return Object.fromEntries(
15018
+ Object.entries(record7).filter(([reference]) => reference !== NODE_STATUS_FIELD.OVERALL)
15019
+ );
15020
+ }
15006
15021
  function evidencePaths(evidence) {
15007
15022
  return evidence.map(evidencePath);
15008
15023
  }
@@ -15152,7 +15167,7 @@ function resolveSpecContextTarget(snapshot, input) {
15152
15167
  if (candidates.length > 1) {
15153
15168
  return {
15154
15169
  failure: {
15155
- candidates: candidates.map(nodeSegment).sort((left, right) => left.localeCompare(right)),
15170
+ candidates: candidates.map(nodeSegment).sort(compareSpecContextOrdinal),
15156
15171
  input,
15157
15172
  kind: SPEC_CONTEXT_TARGET_FAILURE_KIND.AMBIGUOUS_SEGMENT,
15158
15173
  segment
@@ -15201,7 +15216,7 @@ function refPath(ref) {
15201
15216
  return ref?.path;
15202
15217
  }
15203
15218
  function sortPaths(paths) {
15204
- return [...paths].sort((left, right) => left.localeCompare(right));
15219
+ return [...paths].sort(compareSpecContextOrdinal);
15205
15220
  }
15206
15221
  function fullSpecPath(path4) {
15207
15222
  return path4.startsWith(SPEC_TREE_ROOT_PREFIX) ? path4 : `${SPEC_TREE_ROOT_PREFIX}${path4}`;
@@ -15247,11 +15262,11 @@ function lowerIndexSiblingsForContextNodes(snapshot, contextNodes) {
15247
15262
  }
15248
15263
  }
15249
15264
  lowerSiblings.sort((left, right) => {
15250
- const parentComparison = (left.parentId ?? "").localeCompare(right.parentId ?? "");
15265
+ const parentComparison = compareSpecContextOrdinal(left.parentId ?? "", right.parentId ?? "");
15251
15266
  if (parentComparison !== 0) return parentComparison;
15252
15267
  const orderComparison = left.order - right.order;
15253
15268
  if (orderComparison !== 0) return orderComparison;
15254
- return left.id.localeCompare(right.id);
15269
+ return compareSpecContextOrdinal(left.id, right.id);
15255
15270
  });
15256
15271
  return lowerSiblings;
15257
15272
  }
@@ -15386,9 +15401,7 @@ async function citedDecisionDocuments(productDir, sources, knownPaths, includePa
15386
15401
  }
15387
15402
  async function localOverlayPaths(productDir, trackedPaths) {
15388
15403
  if (trackedPaths !== void 0) {
15389
- return [...trackedPaths].filter((path4) => isLocalOverlayPath(path4)).sort(
15390
- (left, right) => left.localeCompare(right)
15391
- );
15404
+ return [...trackedPaths].filter((path4) => isLocalOverlayPath(path4)).sort(compareSpecContextOrdinal);
15392
15405
  }
15393
15406
  let entries;
15394
15407
  try {
@@ -15396,7 +15409,7 @@ async function localOverlayPaths(productDir, trackedPaths) {
15396
15409
  } catch {
15397
15410
  return [];
15398
15411
  }
15399
- return entries.filter((name) => name.endsWith(SPEC_TREE_GRAMMAR.LOCAL_OVERLAYS.EXTENSION)).sort((left, right) => left.localeCompare(right)).map((name) => childSpecPath(SPEC_CONTEXT_LOCAL_OVERLAY_DIRECTORY, name));
15412
+ return entries.filter((name) => name.endsWith(SPEC_TREE_GRAMMAR.LOCAL_OVERLAYS.EXTENSION)).sort(compareSpecContextOrdinal).map((name) => childSpecPath(SPEC_CONTEXT_LOCAL_OVERLAY_DIRECTORY, name));
15400
15413
  }
15401
15414
  async function withDocumentContent(productDir, read, scannedDocuments) {
15402
15415
  const enriched = [];
@@ -15789,7 +15802,7 @@ function groupTestFiles(testFiles, languages) {
15789
15802
 
15790
15803
  // src/domains/test/targeting.ts
15791
15804
  var TESTS_DIRECTORY_NAME3 = SPEC_TREE_EVIDENCE_FILE.DIRECTORY_NAME;
15792
- var PATH_SEGMENT_SEPARATOR3 = "/";
15805
+ var PATH_SEGMENT_SEPARATOR2 = "/";
15793
15806
  function normalizeTargetOperand(operand) {
15794
15807
  return normalizePathPrefix(operand);
15795
15808
  }
@@ -15799,7 +15812,7 @@ function matchOperand(discovered, operand, recursive) {
15799
15812
  return applyPathFilter(discovered, { include: [normalized] });
15800
15813
  }
15801
15814
  return applyPathFilter(discovered, {
15802
- include: [`${normalized}${PATH_SEGMENT_SEPARATOR3}${TESTS_DIRECTORY_NAME3}`]
15815
+ include: [`${normalized}${PATH_SEGMENT_SEPARATOR2}${TESTS_DIRECTORY_NAME3}`]
15803
15816
  });
15804
15817
  }
15805
15818
  function resolveTargetedTestFiles(discovered, selection) {
@@ -15842,7 +15855,7 @@ async function runTests(options, deps) {
15842
15855
  for (const group of groups) {
15843
15856
  const invocation = await group.language.runTests(
15844
15857
  {
15845
- projectRoot: options.productDir,
15858
+ productDir: options.productDir,
15846
15859
  testPaths: group.testPaths,
15847
15860
  excludedNodePaths: runnerExcludedNodePaths
15848
15861
  },
@@ -16470,7 +16483,7 @@ async function relatedTestPaths(sourceFiles, options, baseRef, candidateTestPath
16470
16483
  if (language.relatedTestPaths === void 0) continue;
16471
16484
  const relatedDeps = deps.relatedDepsFor(language.name);
16472
16485
  const languageResolution = await language.relatedTestPaths(
16473
- { projectRoot: options.productDir, sourcePaths: sourceFiles, candidateTestPaths: candidateTestPaths2, baseRef },
16486
+ { productDir: options.productDir, sourcePaths: sourceFiles, candidateTestPaths: candidateTestPaths2, baseRef },
16474
16487
  options.staged === true ? { ...relatedDeps, readFile: (path4) => readStagedFile(options.productDir, path4, deps.git) } : relatedDeps
16475
16488
  );
16476
16489
  for (const sourceFile of languageResolution.resolvedSourcePaths) resolved.add(sourceFile);
@@ -16877,16 +16890,6 @@ async function runTestsCommand(options, deps) {
16877
16890
  );
16878
16891
  return { dispatch, runFile, recorded };
16879
16892
  }
16880
- async function runNodeCommand(options, deps) {
16881
- const recording = resolveRecordingDependencies(deps);
16882
- const runFile = await reserveRunFile(options.productDir, recording);
16883
- const dispatch = await runTests(
16884
- { productDir: options.productDir, registry: deps.registry, passingScope: { include: [options.nodePath] } },
16885
- { runnerDepsFor: deps.runnerDepsFor }
16886
- );
16887
- const recorded = await recordRun(runFile, options.productDir, dispatch, recording, deps.registry);
16888
- return { dispatch, runFile, recorded };
16889
- }
16890
16893
 
16891
16894
  // src/commands/spec/node-outcome-resolver.ts
16892
16895
  var PATH_SEPARATOR3 = "/";
@@ -16895,9 +16898,6 @@ function createNodeOutcomeResolver(deps) {
16895
16898
  let evidence;
16896
16899
  const currentInputsByCoveredPaths = /* @__PURE__ */ new Map();
16897
16900
  const sharedEvidence = () => evidence ??= loadResolverEvidence(deps);
16898
- const refreshEvidence = () => {
16899
- evidence = loadResolverEvidence(deps);
16900
- };
16901
16901
  const currentInputsFor = (coveredPaths) => {
16902
16902
  const cacheKey = coveredPathCollectionKey(coveredPaths);
16903
16903
  const cached = currentInputsByCoveredPaths.get(cacheKey);
@@ -16912,13 +16912,13 @@ function createNodeOutcomeResolver(deps) {
16912
16912
  const { discoveredTestPaths, terminalRuns } = await sharedEvidence();
16913
16913
  const nodePath = `${SPEC_TREE_CONFIG.ROOT_DIRECTORY}${PATH_SEPARATOR3}${nodeId}`;
16914
16914
  const nodeTestPaths = filterNodeTestPaths(discoveredTestPaths, nodePath, evidencePaths2);
16915
- const usable = await usableRecordedOutcome(discoveredTestPaths, terminalRuns, nodeTestPaths, currentInputsFor);
16916
- if (usable !== void 0) {
16917
- return usable;
16918
- }
16919
- const { dispatch } = await runNodeCommand({ productDir: deps.productDir, nodePath }, deps);
16920
- refreshEvidence();
16921
- return outcomesForPaths(dispatch.outcomes, nodeTestPaths);
16915
+ return recordedOutcomes(
16916
+ discoveredTestPaths,
16917
+ terminalRuns,
16918
+ evidencePaths2,
16919
+ nodeTestPaths,
16920
+ currentInputsFor
16921
+ );
16922
16922
  };
16923
16923
  }
16924
16924
  function coveredPathCollectionKey(coveredPaths) {
@@ -16934,24 +16934,27 @@ function filterNodeTestPaths(discoveredTestPaths, nodePath, evidencePaths2) {
16934
16934
  const discovered = new Set(discoveredTestPaths.filter((path4) => path4.startsWith(prefix)));
16935
16935
  return evidencePaths2.filter((path4) => discovered.has(path4));
16936
16936
  }
16937
- async function usableRecordedOutcome(discoveredTestPaths, terminalRuns, nodeTestPaths, currentInputsFor) {
16938
- const latest = selectLatestTerminalTestRunForNode(terminalRuns, nodeTestPaths);
16939
- if (latest === void 0) {
16940
- return void 0;
16941
- }
16942
- const runCoveredPaths = latest.state.runnerOutcomes.flatMap((outcome) => outcome.testPaths);
16937
+ async function recordedOutcomes(discoveredTestPaths, terminalRuns, evidencePaths2, nodeTestPaths, currentInputsFor) {
16938
+ const resolved = Object.fromEntries(
16939
+ evidencePaths2.map((path4) => [path4, NODE_STATUS_EVIDENCE_OUTCOME.NOT_RUN])
16940
+ );
16943
16941
  const presentTestPaths = new Set(discoveredTestPaths);
16944
- if (!runCoveredPaths.every((path4) => presentTestPaths.has(path4))) {
16945
- return void 0;
16942
+ for (const path4 of nodeTestPaths) {
16943
+ const latest = selectLatestTerminalTestRunForNode(terminalRuns, [path4]);
16944
+ if (latest === void 0) continue;
16945
+ const runCoveredPaths = latest.state.runnerOutcomes.flatMap((outcome) => outcome.testPaths);
16946
+ if (!runCoveredPaths.every((coveredPath) => presentTestPaths.has(coveredPath))) {
16947
+ delete resolved[path4];
16948
+ continue;
16949
+ }
16950
+ const current = await currentInputsFor(runCoveredPaths);
16951
+ if (!isStalenessMatch(extractStalenessInputs(latest.state), current)) {
16952
+ delete resolved[path4];
16953
+ continue;
16954
+ }
16955
+ resolved[path4] = outcomeForPath(latest.state.runnerOutcomes, path4);
16946
16956
  }
16947
- const current = await currentInputsFor(runCoveredPaths);
16948
- const fresh = isStalenessMatch(extractStalenessInputs(latest.state), current);
16949
- return fresh && latest.state.status === TEST_RUN_STATE_STATUS.PASSED ? outcomesForPaths(latest.state.runnerOutcomes, nodeTestPaths) : void 0;
16950
- }
16951
- function outcomesForPaths(outcomes, nodeTestPaths) {
16952
- return Object.fromEntries(
16953
- nodeTestPaths.map((path4) => [path4, outcomeForPath(outcomes, path4)])
16954
- );
16957
+ return resolved;
16955
16958
  }
16956
16959
  function outcomeForPath(outcomes, path4) {
16957
16960
  const covering = outcomes.filter((outcome) => outcome.testPaths.includes(path4));
@@ -17173,11 +17176,11 @@ function coveredProductInputPaths(coveredTestPaths2) {
17173
17176
  function excludeFlag(nodePath) {
17174
17177
  return `${PYTHON_PYTEST_IGNORE_FLAG_PREFIX}${nodePath}${PYTHON_PYTEST_IGNORE_FLAG_SUFFIX}`;
17175
17178
  }
17176
- function detect(projectRoot, deps) {
17177
- return deps?.isLanguagePresent?.(projectRoot) ?? detectPython(projectRoot).present;
17179
+ function detect(productDir, deps) {
17180
+ return deps?.isLanguagePresent?.(productDir) ?? detectPython(productDir).present;
17178
17181
  }
17179
17182
  async function runTests2(request, deps) {
17180
- if (!detect(request.projectRoot, deps)) {
17183
+ if (!detect(request.productDir, deps)) {
17181
17184
  return { invoked: false };
17182
17185
  }
17183
17186
  const args = [
@@ -17248,7 +17251,7 @@ function createJournalReporter(sink) {
17248
17251
  async function runTestsStreaming(request, deps) {
17249
17252
  const reporter = createJournalReporter(deps.sink);
17250
17253
  await deps.starter.start({
17251
- projectRoot: request.projectRoot,
17254
+ productDir: request.productDir,
17252
17255
  testPaths: request.testPaths,
17253
17256
  reporters: [reporter]
17254
17257
  });
@@ -17261,7 +17264,7 @@ function createVitestRunStarter() {
17261
17264
  const priorExitCode = process.exitCode;
17262
17265
  try {
17263
17266
  const vitest = await startVitest("test", [...options.testPaths], {
17264
- root: options.projectRoot,
17267
+ root: options.productDir,
17265
17268
  watch: false,
17266
17269
  reporters: [...options.reporters]
17267
17270
  });
@@ -17310,17 +17313,17 @@ function matchesTestFile2(filePath) {
17310
17313
  function excludeFlag2(nodePath) {
17311
17314
  return `${TYPESCRIPT_VITEST_EXCLUDE_FLAG_PREFIX}${nodePath}${TYPESCRIPT_VITEST_EXCLUDE_FLAG_SUFFIX}`;
17312
17315
  }
17313
- function detect2(projectRoot, deps) {
17314
- return deps?.isLanguagePresent?.(projectRoot) ?? detectTypeScript(projectRoot).present;
17316
+ function detect2(productDir, deps) {
17317
+ return deps?.isLanguagePresent?.(productDir) ?? detectTypeScript(productDir).present;
17315
17318
  }
17316
17319
  async function runTests3(request, deps) {
17317
- if (!detect2(request.projectRoot, deps)) {
17320
+ if (!detect2(request.productDir, deps)) {
17318
17321
  return { invoked: false };
17319
17322
  }
17320
17323
  const args = [
17321
17324
  ...VITEST_INVOKE_ARGS,
17322
17325
  VITEST_ROOT_FLAG,
17323
- request.projectRoot,
17326
+ request.productDir,
17324
17327
  ...request.testPaths,
17325
17328
  ...request.excludedNodePaths.map(excludeFlag2)
17326
17329
  ];
@@ -17515,7 +17518,7 @@ async function readCandidateTest(path4, deps, moduleTextCache) {
17515
17518
  return loaded;
17516
17519
  }
17517
17520
  async function relatedTestPaths2(request, deps) {
17518
- if (!detect2(request.projectRoot, deps)) {
17521
+ if (!detect2(request.productDir, deps)) {
17519
17522
  return { testPaths: [], resolvedSourcePaths: [] };
17520
17523
  }
17521
17524
  const sourcePaths = new Set(request.sourcePaths);
@@ -17545,7 +17548,7 @@ async function relatedTestPaths2(request, deps) {
17545
17548
  return { testPaths, resolvedSourcePaths: [...resolvedSourcePaths] };
17546
17549
  }
17547
17550
  async function runTestsStreaming2(request, deps) {
17548
- if (!detect2(request.projectRoot, deps)) {
17551
+ if (!detect2(request.productDir, deps)) {
17549
17552
  return { invoked: false };
17550
17553
  }
17551
17554
  const terminalStatus = await runTestsStreaming(request, {
@@ -17571,141 +17574,13 @@ var testingRegistry = {
17571
17574
  languages: [typescriptTestingLanguage, pythonTestingLanguage]
17572
17575
  };
17573
17576
 
17574
- // src/interfaces/cli/test-runner-deps.ts
17575
- import { createWriteStream } from "fs";
17576
- import { mkdtemp as mkdtemp3, readFile as readFile12 } from "fs/promises";
17577
- import { tmpdir as tmpdir3 } from "os";
17578
- import { join as join28 } from "path";
17579
- import { finished } from "stream/promises";
17580
- var PROCESS_FAILURE_EXIT_CODE = 1;
17581
- var AGENT_ARTIFACT_DIR_PREFIX = "spx-test-agent-";
17582
- var STDOUT_FILE_SUFFIX = "stdout.log";
17583
- var STDERR_FILE_SUFFIX = "stderr.log";
17584
- var ARTIFACT_INDEX_WIDTH = 3;
17585
- var ARTIFACT_INDEX_RADIX = 10;
17586
- var ARTIFACT_FILE_FLAGS = "wx";
17587
- var EMPTY_RUNNER_ARGS = [];
17588
- function createCommandRunner(productDir, outStream) {
17589
- return (command, args) => new Promise((resolveResult) => {
17590
- const child = spawnManagedSubprocess(lifecycleProcessRunner, command, args, {
17591
- cwd: productDir
17592
- });
17593
- child.stdout?.pipe(outStream);
17594
- child.stderr?.pipe(process.stderr);
17595
- child.on("close", (code) => resolveResult({ exitCode: code ?? PROCESS_FAILURE_EXIT_CODE }));
17596
- child.on("error", () => resolveResult({ exitCode: PROCESS_FAILURE_EXIT_CODE }));
17597
- });
17598
- }
17599
- function createRelatedCommandRunner(productDir) {
17600
- return (command, args) => new Promise((resolveResult) => {
17601
- const stdout = [];
17602
- const stderr = [];
17603
- const child = spawnManagedSubprocess(lifecycleProcessRunner, command, args, {
17604
- cwd: productDir
17605
- });
17606
- child.stdout?.on("data", (chunk) => stdout.push(String(chunk)));
17607
- child.stderr?.on("data", (chunk) => stderr.push(String(chunk)));
17608
- child.on("close", (code) => resolveResult({
17609
- exitCode: code ?? PROCESS_FAILURE_EXIT_CODE,
17610
- stdout: stdout.join(""),
17611
- stderr: stderr.join("")
17612
- }));
17613
- child.on("error", (error) => resolveResult({
17614
- exitCode: PROCESS_FAILURE_EXIT_CODE,
17615
- stdout: stdout.join(""),
17616
- stderr: error.message
17617
- }));
17618
- });
17619
- }
17620
- function createRunnerDepsFor(productDir, outStream = process.stdout) {
17621
- const runCommand = createCommandRunner(productDir, outStream);
17622
- return () => ({ runCommand });
17623
- }
17624
- function createRelatedDepsFor(productDir) {
17625
- const runCommand = createRelatedCommandRunner(productDir);
17626
- return () => ({ runCommand, readFile: (path4) => readFile12(join28(productDir, path4), "utf8") });
17627
- }
17628
- function artifactFileName(index, suffix) {
17629
- return `${index.toString(ARTIFACT_INDEX_RADIX).padStart(ARTIFACT_INDEX_WIDTH, "0")}-${suffix}`;
17630
- }
17631
- function createArtifactWriters(root, index, createArtifactWriteStream) {
17632
- const stdoutPath = join28(root, artifactFileName(index, STDOUT_FILE_SUFFIX));
17633
- const stderrPath = join28(root, artifactFileName(index, STDERR_FILE_SUFFIX));
17634
- const stdoutFile = createArtifactWriteStream(stdoutPath);
17635
- const stderrFile = createArtifactWriteStream(stderrPath);
17636
- return {
17637
- stdoutPath,
17638
- stderrPath,
17639
- stdoutFile,
17640
- stderrFile,
17641
- completion: Promise.all([finished(stdoutFile), finished(stderrFile)])
17642
- };
17643
- }
17644
- async function finishArtifactWriters(writers) {
17645
- writers.stdoutFile.end();
17646
- writers.stderrFile.end();
17647
- await writers.completion;
17648
- return {
17649
- stdoutPath: writers.stdoutPath,
17650
- stderrPath: writers.stderrPath
17651
- };
17652
- }
17653
- function runCapturedCommand(request) {
17654
- return new Promise((resolveResult) => {
17655
- let settled = false;
17656
- const resolveOnce = (result) => {
17657
- if (settled) return;
17658
- settled = true;
17659
- resolveResult(result);
17660
- };
17661
- const child = spawnManagedSubprocess(request.processRunner, request.command, request.args, {
17662
- cwd: request.productDir,
17663
- env: request.inheritedEnv
17664
- });
17665
- child.stdout?.pipe(request.writers.stdoutFile);
17666
- child.stderr?.pipe(request.writers.stderrFile);
17667
- const resolveWithExitCode = (exitCode) => {
17668
- finishArtifactWriters(request.writers).then((output) => resolveOnce({ exitCode, output })).catch(() => resolveOnce({ exitCode: PROCESS_FAILURE_EXIT_CODE }));
17669
- };
17670
- request.writers.completion.catch(() => {
17671
- child.kill();
17672
- resolveOnce({ exitCode: PROCESS_FAILURE_EXIT_CODE });
17673
- });
17674
- child.on("close", (code) => resolveWithExitCode(code ?? PROCESS_FAILURE_EXIT_CODE));
17675
- child.on("error", () => resolveWithExitCode(PROCESS_FAILURE_EXIT_CODE));
17676
- });
17677
- }
17678
- function createAgentOutputCommandRunner(productDir, options = {}) {
17679
- let artifactRoot;
17680
- const processRunner = options.processRunner ?? lifecycleProcessRunner;
17681
- const inheritedEnv = options.env ?? process.env;
17682
- const createArtifactWriteStream = options.createArtifactWriteStream ?? ((path4) => createWriteStream(path4, { flags: ARTIFACT_FILE_FLAGS }));
17683
- let nextArtifactIndex = 0;
17684
- return async (command, args = EMPTY_RUNNER_ARGS) => {
17685
- nextArtifactIndex += 1;
17686
- artifactRoot ??= mkdtemp3(join28(options.tmpDir ?? tmpdir3(), AGENT_ARTIFACT_DIR_PREFIX));
17687
- const root = await artifactRoot;
17688
- return runCapturedCommand({
17689
- productDir,
17690
- command,
17691
- args,
17692
- processRunner,
17693
- inheritedEnv,
17694
- writers: createArtifactWriters(root, nextArtifactIndex, createArtifactWriteStream)
17695
- });
17696
- };
17697
- }
17698
- function createAgentRunnerDepsFor(productDir, options = {}) {
17699
- const runCommand = createAgentOutputCommandRunner(productDir, options);
17700
- return () => ({ runCommand });
17701
- }
17702
-
17703
17577
  // src/interfaces/cli/spec.ts
17704
17578
  var SPEC_DOMAIN_CLI = {
17705
17579
  COMMAND: "spec",
17706
17580
  STATUS_COMMAND: "status",
17707
17581
  NEXT_COMMAND: "next",
17708
17582
  CONTEXT_COMMAND: "context",
17583
+ RETIRED_APPLY_COMMAND: "apply",
17709
17584
  JSON_OPTION: "--json",
17710
17585
  CONTENT_OPTION: "--content",
17711
17586
  FORMAT_OPTION_FLAG: "--format",
@@ -17826,8 +17701,7 @@ function registerSpecCommands(specCmd, invocation) {
17826
17701
  update: true,
17827
17702
  resolveOutcomeFor: (productDir2) => createNodeOutcomeResolver({
17828
17703
  productDir: productDir2,
17829
- registry: testingRegistry,
17830
- runnerDepsFor: createRunnerDepsFor(productDir2, process.stderr)
17704
+ registry: testingRegistry
17831
17705
  })
17832
17706
  }) : await statusCommand({ cwd: productDir(), format: format2, onWarning });
17833
17707
  writeOutput3(invocation.io, output);
@@ -17951,68 +17825,197 @@ function formatAgentTestOutput(run) {
17951
17825
  return `${lines.join(NEWLINE2)}${NEWLINE2}`;
17952
17826
  }
17953
17827
 
17954
- // src/interfaces/cli/test.ts
17955
- var TESTING_CLI = {
17956
- commandName: "test",
17957
- description: "Run spec-tree tests across product languages",
17958
- agentOption: "--agent",
17959
- agentDescription: "Capture raw runner output and print a compact agent summary",
17960
- recursiveShortFlag: "-r",
17961
- recursiveLongFlag: "--recursive",
17962
- recursiveDescription: "Extend a node-path operand to its descendant nodes' tests",
17963
- changedLongFlag: "--changed",
17964
- changedDescription: "Run only tests affected by changes against the selected base",
17965
- stagedLongFlag: "--staged",
17966
- stagedDescription: "With --changed, diff the staged snapshot instead of the worktree",
17967
- baseLongFlag: "--base",
17968
- baseOperand: "<ref>",
17969
- baseDescription: "Base ref for --changed; defaults to origin of the default branch",
17970
- targetsArgument: "[targets...]",
17971
- targetsDescription: "Node paths or test-file paths to run; omit to run the full discovered suite",
17972
- passingSubcommand: "passing",
17973
- passingDescription: "Run only the tests within the configured passing scope"
17974
- };
17975
- var UNMATCHED_TEST_FILES_WARNING = "Skipped test files with no registered runner";
17976
- var GATED_TEST_RUNNERS_WARNING = "Skipped test files because their registered runner was gated out";
17977
- var UNRESOLVED_TARGETS_WARNING = "No tests matched these operands";
17978
- var UNRESOLVED_CHANGED_SOURCE_WARNING = "No related-test capability resolved these changed source files";
17979
- var TESTING_PRODUCT_DIR_WARNING = {
17980
- NOT_GIT_REPOSITORY: `Warning: Not in a git repository. Reading ${SPEC_TREE_CONFIG.ROOT_DIRECTORY} tests relative to the current working directory.`
17981
- };
17982
- async function resolveTestProductDir(cwd, writeWarning) {
17983
- const { productDir, isGitRepo } = await detectWorktreeProductRoot(cwd);
17984
- writeWarning(isGitRepo ? void 0 : TESTING_PRODUCT_DIR_WARNING.NOT_GIT_REPOSITORY);
17985
- return productDir;
17828
+ // src/interfaces/cli/test-runner-deps.ts
17829
+ import { createWriteStream } from "fs";
17830
+ import { mkdtemp as mkdtemp3, readFile as readFile12 } from "fs/promises";
17831
+ import { tmpdir as tmpdir3 } from "os";
17832
+ import { join as join28 } from "path";
17833
+ import { finished } from "stream/promises";
17834
+ var PROCESS_FAILURE_EXIT_CODE = 1;
17835
+ var AGENT_ARTIFACT_DIR_PREFIX = "spx-test-agent-";
17836
+ var STDOUT_FILE_SUFFIX = "stdout.log";
17837
+ var STDERR_FILE_SUFFIX = "stderr.log";
17838
+ var ARTIFACT_INDEX_WIDTH = 3;
17839
+ var ARTIFACT_INDEX_RADIX = 10;
17840
+ var ARTIFACT_FILE_FLAGS = "wx";
17841
+ var EMPTY_RUNNER_ARGS = [];
17842
+ function createCommandRunner(productDir, outStream, errStream, processRunner) {
17843
+ return (command, args) => new Promise((resolveResult) => {
17844
+ const child = spawnManagedSubprocess(processRunner, command, args, {
17845
+ cwd: productDir
17846
+ });
17847
+ child.stdout?.pipe(outStream);
17848
+ child.stderr?.pipe(errStream);
17849
+ child.on("close", (code) => resolveResult({ exitCode: code ?? PROCESS_FAILURE_EXIT_CODE }));
17850
+ child.on("error", () => resolveResult({ exitCode: PROCESS_FAILURE_EXIT_CODE }));
17851
+ });
17986
17852
  }
17987
- async function runTestsThroughCommand(productDir, passing, io, targets, changed) {
17988
- try {
17989
- return await runTestsCommand(
17990
- { productDir, passing, targets, changed },
17991
- {
17992
- registry: testingRegistry,
17993
- runnerDepsFor: createRunnerDepsFor(productDir),
17994
- relatedDepsFor: createRelatedDepsFor(productDir)
17995
- }
17996
- );
17997
- } catch (error) {
17998
- io.writeStderr(`${error instanceof Error ? error.message : String(error)}
17999
- `);
18000
- io.exit(PROCESS_FAILURE_EXIT_CODE);
18001
- }
17853
+ function createRelatedCommandRunner(productDir, processRunner) {
17854
+ return (command, args) => new Promise((resolveResult) => {
17855
+ const stdout = [];
17856
+ const stderr = [];
17857
+ const child = spawnManagedSubprocess(processRunner, command, args, {
17858
+ cwd: productDir
17859
+ });
17860
+ child.stdout?.on("data", (chunk) => stdout.push(String(chunk)));
17861
+ child.stderr?.on("data", (chunk) => stderr.push(String(chunk)));
17862
+ child.on("close", (code) => resolveResult({
17863
+ exitCode: code ?? PROCESS_FAILURE_EXIT_CODE,
17864
+ stdout: stdout.join(""),
17865
+ stderr: stderr.join("")
17866
+ }));
17867
+ child.on("error", (error) => resolveResult({
17868
+ exitCode: PROCESS_FAILURE_EXIT_CODE,
17869
+ stdout: stdout.join(""),
17870
+ stderr: error.message
17871
+ }));
17872
+ });
18002
17873
  }
18003
- async function runAgentTestsThroughCommand(productDir, passing, io, targets, changed) {
18004
- try {
18005
- return await runTestsCommand(
18006
- { productDir, passing, targets, changed },
18007
- {
18008
- registry: testingRegistry,
18009
- runnerDepsFor: createAgentRunnerDepsFor(productDir),
18010
- relatedDepsFor: createRelatedDepsFor(productDir)
18011
- }
18012
- );
18013
- } catch (error) {
18014
- io.writeStderr(`${error instanceof Error ? error.message : String(error)}
18015
- `);
17874
+ function createRunnerDepsFor(productDir, outStream = process.stdout, processRunner = lifecycleProcessRunner, errStream = process.stderr) {
17875
+ const runCommand = createCommandRunner(productDir, outStream, errStream, processRunner);
17876
+ return () => ({ runCommand });
17877
+ }
17878
+ function createRelatedDepsFor(productDir, processRunner = lifecycleProcessRunner) {
17879
+ const runCommand = createRelatedCommandRunner(productDir, processRunner);
17880
+ return () => ({ runCommand, readFile: (path4) => readFile12(join28(productDir, path4), "utf8") });
17881
+ }
17882
+ function artifactFileName(index, suffix) {
17883
+ return `${index.toString(ARTIFACT_INDEX_RADIX).padStart(ARTIFACT_INDEX_WIDTH, "0")}-${suffix}`;
17884
+ }
17885
+ function createArtifactWriters(root, index, createArtifactWriteStream) {
17886
+ const stdoutPath = join28(root, artifactFileName(index, STDOUT_FILE_SUFFIX));
17887
+ const stderrPath = join28(root, artifactFileName(index, STDERR_FILE_SUFFIX));
17888
+ const stdoutFile = createArtifactWriteStream(stdoutPath);
17889
+ const stderrFile = createArtifactWriteStream(stderrPath);
17890
+ return {
17891
+ stdoutPath,
17892
+ stderrPath,
17893
+ stdoutFile,
17894
+ stderrFile,
17895
+ completion: Promise.all([finished(stdoutFile), finished(stderrFile)])
17896
+ };
17897
+ }
17898
+ async function finishArtifactWriters(writers) {
17899
+ writers.stdoutFile.end();
17900
+ writers.stderrFile.end();
17901
+ await writers.completion;
17902
+ return {
17903
+ stdoutPath: writers.stdoutPath,
17904
+ stderrPath: writers.stderrPath
17905
+ };
17906
+ }
17907
+ function runCapturedCommand(request) {
17908
+ return new Promise((resolveResult) => {
17909
+ let settled = false;
17910
+ const resolveOnce = (result) => {
17911
+ if (settled) return;
17912
+ settled = true;
17913
+ resolveResult(result);
17914
+ };
17915
+ const child = spawnManagedSubprocess(request.processRunner, request.command, request.args, {
17916
+ cwd: request.productDir,
17917
+ env: request.inheritedEnv
17918
+ });
17919
+ child.stdout?.pipe(request.writers.stdoutFile);
17920
+ child.stderr?.pipe(request.writers.stderrFile);
17921
+ const resolveWithExitCode = (exitCode) => {
17922
+ finishArtifactWriters(request.writers).then((output) => resolveOnce({ exitCode, output })).catch(() => resolveOnce({ exitCode: PROCESS_FAILURE_EXIT_CODE }));
17923
+ };
17924
+ request.writers.completion.catch(() => {
17925
+ child.kill();
17926
+ resolveOnce({ exitCode: PROCESS_FAILURE_EXIT_CODE });
17927
+ });
17928
+ child.on("close", (code) => resolveWithExitCode(code ?? PROCESS_FAILURE_EXIT_CODE));
17929
+ child.on("error", () => resolveWithExitCode(PROCESS_FAILURE_EXIT_CODE));
17930
+ });
17931
+ }
17932
+ function createAgentOutputCommandRunner(productDir, options = {}) {
17933
+ let artifactRoot;
17934
+ const processRunner = options.processRunner ?? lifecycleProcessRunner;
17935
+ const inheritedEnv = options.env ?? process.env;
17936
+ const createArtifactWriteStream = options.createArtifactWriteStream ?? ((path4) => createWriteStream(path4, { flags: ARTIFACT_FILE_FLAGS }));
17937
+ let nextArtifactIndex = 0;
17938
+ return async (command, args = EMPTY_RUNNER_ARGS) => {
17939
+ nextArtifactIndex += 1;
17940
+ artifactRoot ??= mkdtemp3(join28(options.tmpDir ?? tmpdir3(), AGENT_ARTIFACT_DIR_PREFIX));
17941
+ const root = await artifactRoot;
17942
+ return runCapturedCommand({
17943
+ productDir,
17944
+ command,
17945
+ args,
17946
+ processRunner,
17947
+ inheritedEnv,
17948
+ writers: createArtifactWriters(root, nextArtifactIndex, createArtifactWriteStream)
17949
+ });
17950
+ };
17951
+ }
17952
+ function createAgentRunnerDepsFor(productDir, options = {}) {
17953
+ const runCommand = createAgentOutputCommandRunner(productDir, options);
17954
+ return () => ({ runCommand });
17955
+ }
17956
+
17957
+ // src/interfaces/cli/test.ts
17958
+ var TESTING_CLI = {
17959
+ commandName: "test",
17960
+ description: "Run spec-tree tests across product languages",
17961
+ agentOption: "--agent",
17962
+ agentDescription: "Capture raw runner output and print a compact agent summary",
17963
+ recursiveShortFlag: "-r",
17964
+ recursiveLongFlag: "--recursive",
17965
+ recursiveDescription: "Extend a node-path operand to its descendant nodes' tests",
17966
+ changedLongFlag: "--changed",
17967
+ changedDescription: "Run only tests affected by changes against the selected base",
17968
+ stagedLongFlag: "--staged",
17969
+ stagedDescription: "With --changed, diff the staged snapshot instead of the worktree",
17970
+ baseLongFlag: "--base",
17971
+ baseOperand: "<ref>",
17972
+ baseDescription: "Base ref for --changed; defaults to origin of the default branch",
17973
+ targetsArgument: "[targets...]",
17974
+ targetsDescription: "Node paths or test-file paths to run; omit to run the full discovered suite",
17975
+ passingSubcommand: "passing",
17976
+ passingDescription: "Run only the tests within the configured passing scope"
17977
+ };
17978
+ var UNMATCHED_TEST_FILES_WARNING = "Skipped test files with no registered runner";
17979
+ var GATED_TEST_RUNNERS_WARNING = "Skipped test files because their registered runner was gated out";
17980
+ var UNRESOLVED_TARGETS_WARNING = "No tests matched these operands";
17981
+ var UNRESOLVED_CHANGED_SOURCE_WARNING = "No related-test capability resolved these changed source files";
17982
+ var TESTING_PRODUCT_DIR_WARNING = {
17983
+ NOT_GIT_REPOSITORY: `Warning: Not in a git repository. Reading ${SPEC_TREE_CONFIG.ROOT_DIRECTORY} tests relative to the current working directory.`
17984
+ };
17985
+ async function resolveTestProductDir(cwd, writeWarning) {
17986
+ const { productDir, isGitRepo } = await detectWorktreeProductRoot(cwd);
17987
+ writeWarning(isGitRepo ? void 0 : TESTING_PRODUCT_DIR_WARNING.NOT_GIT_REPOSITORY);
17988
+ return productDir;
17989
+ }
17990
+ async function runTestsThroughCommand(productDir, passing, io, targets, changed) {
17991
+ try {
17992
+ return await runTestsCommand(
17993
+ { productDir, passing, targets, changed },
17994
+ {
17995
+ registry: testingRegistry,
17996
+ runnerDepsFor: createRunnerDepsFor(productDir),
17997
+ relatedDepsFor: createRelatedDepsFor(productDir)
17998
+ }
17999
+ );
18000
+ } catch (error) {
18001
+ io.writeStderr(`${error instanceof Error ? error.message : String(error)}
18002
+ `);
18003
+ io.exit(PROCESS_FAILURE_EXIT_CODE);
18004
+ }
18005
+ }
18006
+ async function runAgentTestsThroughCommand(productDir, passing, io, targets, changed) {
18007
+ try {
18008
+ return await runTestsCommand(
18009
+ { productDir, passing, targets, changed },
18010
+ {
18011
+ registry: testingRegistry,
18012
+ runnerDepsFor: createAgentRunnerDepsFor(productDir),
18013
+ relatedDepsFor: createRelatedDepsFor(productDir)
18014
+ }
18015
+ );
18016
+ } catch (error) {
18017
+ io.writeStderr(`${error instanceof Error ? error.message : String(error)}
18018
+ `);
18016
18019
  io.exit(PROCESS_FAILURE_EXIT_CODE);
18017
18020
  }
18018
18021
  }
@@ -18138,6 +18141,12 @@ var testingDomain = createTestingDomain();
18138
18141
  import { existsSync as existsSync9, realpathSync } from "fs";
18139
18142
  import { relative as relative11, resolve as resolve16 } from "path";
18140
18143
 
18144
+ // src/validation/languages/types.ts
18145
+ var VALIDATION_STAGE_PARTICIPATION = {
18146
+ RUN: "run",
18147
+ SKIP: "skip"
18148
+ };
18149
+
18141
18150
  // src/commands/validation/formatting.ts
18142
18151
  import { existsSync, statSync } from "fs";
18143
18152
  import { isAbsolute as isAbsolute6, join as join29, relative as relative6 } from "path";
@@ -18153,8 +18162,8 @@ function unique(values) {
18153
18162
  function nonEmpty(values) {
18154
18163
  return values?.filter((value) => value.length > 0) ?? [];
18155
18164
  }
18156
- function toProjectRelativeValidationPath(projectRoot, path4) {
18157
- return isAbsolute5(path4) ? relative5(projectRoot, path4) : path4;
18165
+ function toProductRelativeValidationPath(productDir, path4) {
18166
+ return isAbsolute5(path4) ? relative5(productDir, path4) : path4;
18158
18167
  }
18159
18168
  function intersectIncludes(baseInclude, toolInclude) {
18160
18169
  const base = nonEmpty(baseInclude);
@@ -18259,6 +18268,9 @@ function applyValidationPathFilterToScope(scopeConfig, filter) {
18259
18268
  };
18260
18269
  }
18261
18270
 
18271
+ // src/validation/steps/formatting.ts
18272
+ import { createRequire } from "module";
18273
+
18262
18274
  // src/validation/steps/subprocess-output.ts
18263
18275
  var VALIDATION_SUBPROCESS_EVENTS = {
18264
18276
  CLOSE: "close",
@@ -18270,6 +18282,13 @@ var defaultValidationSubprocessOutputStreams = {
18270
18282
  stdout: process.stdout,
18271
18283
  stderr: process.stderr
18272
18284
  };
18285
+ var discardValidationSubprocessOutputStream = {
18286
+ write: () => true
18287
+ };
18288
+ var discardValidationSubprocessOutputStreams = {
18289
+ stdout: discardValidationSubprocessOutputStream,
18290
+ stderr: discardValidationSubprocessOutputStream
18291
+ };
18273
18292
  function forwardValidationSubprocessOutput(child, streams = defaultValidationSubprocessOutputStreams) {
18274
18293
  child.stdout?.on(VALIDATION_SUBPROCESS_EVENTS.DATA, (chunk) => {
18275
18294
  forwardChunkWithBackpressure(child.stdout, streams.stdout, chunk);
@@ -18290,7 +18309,12 @@ function forwardChunkWithBackpressure(source, stream, chunk) {
18290
18309
  }
18291
18310
 
18292
18311
  // src/validation/steps/formatting.ts
18293
- var DPRINT_COMMAND = "dprint";
18312
+ var DPRINT_EXECUTABLE_SPECIFIER = "dprint/bin.cjs";
18313
+ var cachedDprintCommand;
18314
+ function resolveDprintCommand() {
18315
+ cachedDprintCommand ??= createRequire(import.meta.url).resolve(DPRINT_EXECUTABLE_SPECIFIER);
18316
+ return cachedDprintCommand;
18317
+ }
18294
18318
  var DPRINT_CHECK_SUBCOMMAND = "check";
18295
18319
  var DPRINT_EXCLUDES_OPTION = "--excludes";
18296
18320
  var DPRINT_OPTIONS_TERMINATOR = "--";
@@ -18306,16 +18330,17 @@ function buildDprintCheckArgs(options) {
18306
18330
  ...files
18307
18331
  ];
18308
18332
  }
18309
- async function validateFormatting(context, runner = defaultFormattingProcessRunner) {
18333
+ async function validateFormatting(context, runner = defaultFormattingProcessRunner, outputStreams) {
18310
18334
  const args = buildDprintCheckArgs({ files: context.files, excludes: context.excludes });
18311
18335
  return new Promise((resolve17) => {
18312
- const child = spawnManagedSubprocess(runner, DPRINT_COMMAND, args, { cwd: context.projectRoot });
18336
+ const child = spawnManagedSubprocess(runner, resolveDprintCommand(), args, { cwd: context.productDir });
18313
18337
  const chunks = [];
18314
18338
  const capture = (chunk) => {
18315
18339
  chunks.push(typeof chunk === "string" ? chunk : Buffer.from(chunk).toString());
18316
18340
  };
18317
18341
  child.stdout?.on(VALIDATION_SUBPROCESS_EVENTS.DATA, capture);
18318
18342
  child.stderr?.on(VALIDATION_SUBPROCESS_EVENTS.DATA, capture);
18343
+ forwardValidationSubprocessOutput(child, outputStreams);
18319
18344
  child.on(VALIDATION_SUBPROCESS_EVENTS.CLOSE, (code) => {
18320
18345
  resolve17({ success: code === 0, output: chunks.join("") });
18321
18346
  });
@@ -18337,59 +18362,140 @@ var VALIDATION_STAGE_DISPLAY_NAMES = {
18337
18362
  };
18338
18363
  var VALIDATION_SKIP_LABELS = {
18339
18364
  VERB: "Skipping",
18340
- CIRCULAR_REASON: "skip-circular",
18341
- LITERAL_REASON: "skip-literal",
18342
18365
  DISABLED_BY_PREFIX: "disabled by",
18343
- TYPESCRIPT_ABSENT_REASON: "TypeScript not detected in project",
18366
+ TYPESCRIPT_ABSENT_REASON: "TypeScript not detected in product",
18344
18367
  VALIDATION_PATHS_NO_TARGETS_REASON: "validation paths matched no files",
18368
+ EXPLICIT_PATHS_NO_TARGETS_REASON: "explicit paths matched no files in tool scope",
18345
18369
  MARKDOWN_NO_SCOPE_REASON: "no markdown files in explicit path scope",
18346
18370
  MARKDOWN_NO_DEFAULT_DIRECTORIES_REASON: "no spx/ or docs/ directories found"
18347
18371
  };
18372
+ var VALIDATION_STREAMED_STAGE_RESULT = "output streamed";
18373
+ var VALIDATION_PROBLEM_TERMS = {
18374
+ SINGULAR: "problem",
18375
+ PLURAL: "problems"
18376
+ };
18377
+ function formatValidationNoProblemsMessage(stageName) {
18378
+ return `${stageName}: \u2713 No ${VALIDATION_PROBLEM_TERMS.PLURAL}`;
18379
+ }
18380
+ function formatValidationProblemsFoundMessage(stageName, options = {}) {
18381
+ const countPrefix = options.count === void 0 ? "" : `${options.count} `;
18382
+ const term = options.count === 1 ? VALIDATION_PROBLEM_TERMS.SINGULAR : VALIDATION_PROBLEM_TERMS.PLURAL;
18383
+ const detailSuffix = options.detail === void 0 ? "" : ` (${options.detail})`;
18384
+ return `${stageName}: ${countPrefix}${term} found${detailSuffix}`;
18385
+ }
18386
+ function formatValidationConfigProblemMessage(stageName, detail) {
18387
+ return formatValidationProblemsFoundMessage(stageName, { count: 1, detail });
18388
+ }
18389
+ var VALIDATION_STAGE_PROBLEM_MESSAGES = {
18390
+ [VALIDATION_STAGE_DISPLAY_NAMES.CIRCULAR]: {
18391
+ clear: formatValidationNoProblemsMessage(VALIDATION_STAGE_DISPLAY_NAMES.CIRCULAR),
18392
+ attention: formatValidationProblemsFoundMessage(VALIDATION_STAGE_DISPLAY_NAMES.CIRCULAR, {
18393
+ detail: "circular dependencies"
18394
+ })
18395
+ },
18396
+ [VALIDATION_STAGE_DISPLAY_NAMES.KNIP]: {
18397
+ clear: formatValidationNoProblemsMessage(VALIDATION_STAGE_DISPLAY_NAMES.KNIP),
18398
+ attention: formatValidationProblemsFoundMessage(VALIDATION_STAGE_DISPLAY_NAMES.KNIP, {
18399
+ detail: "unused code"
18400
+ })
18401
+ },
18402
+ [VALIDATION_STAGE_DISPLAY_NAMES.ESLINT]: {
18403
+ clear: formatValidationNoProblemsMessage(VALIDATION_STAGE_DISPLAY_NAMES.ESLINT),
18404
+ attention: formatValidationProblemsFoundMessage(VALIDATION_STAGE_DISPLAY_NAMES.ESLINT)
18405
+ },
18406
+ [VALIDATION_STAGE_DISPLAY_NAMES.TYPESCRIPT]: {
18407
+ clear: formatValidationNoProblemsMessage(VALIDATION_STAGE_DISPLAY_NAMES.TYPESCRIPT),
18408
+ attention: formatValidationProblemsFoundMessage(VALIDATION_STAGE_DISPLAY_NAMES.TYPESCRIPT)
18409
+ },
18410
+ [VALIDATION_STAGE_DISPLAY_NAMES.MARKDOWN]: {
18411
+ clear: formatValidationNoProblemsMessage(VALIDATION_STAGE_DISPLAY_NAMES.MARKDOWN),
18412
+ attention: formatValidationProblemsFoundMessage(VALIDATION_STAGE_DISPLAY_NAMES.MARKDOWN)
18413
+ },
18414
+ [VALIDATION_STAGE_DISPLAY_NAMES.LITERAL]: {
18415
+ clear: formatValidationNoProblemsMessage(VALIDATION_STAGE_DISPLAY_NAMES.LITERAL),
18416
+ attention: formatValidationProblemsFoundMessage(VALIDATION_STAGE_DISPLAY_NAMES.LITERAL)
18417
+ },
18418
+ [VALIDATION_STAGE_DISPLAY_NAMES.FORMATTING]: {
18419
+ clear: formatValidationNoProblemsMessage(VALIDATION_STAGE_DISPLAY_NAMES.FORMATTING),
18420
+ attention: formatValidationProblemsFoundMessage(VALIDATION_STAGE_DISPLAY_NAMES.FORMATTING, {
18421
+ detail: "unformatted files"
18422
+ })
18423
+ }
18424
+ };
18348
18425
  var VALIDATION_COMMAND_OUTPUT = {
18349
- CIRCULAR_FOUND: `${VALIDATION_STAGE_DISPLAY_NAMES.CIRCULAR}: circular dependencies found`,
18350
- CIRCULAR_NONE_FOUND: `${VALIDATION_STAGE_DISPLAY_NAMES.CIRCULAR}: \u2713 No circular dependencies found`,
18351
- KNIP_CONFIG_ERROR: `${VALIDATION_STAGE_DISPLAY_NAMES.KNIP}: \u2717 config error`,
18426
+ CIRCULAR_FOUND: VALIDATION_STAGE_PROBLEM_MESSAGES[VALIDATION_STAGE_DISPLAY_NAMES.CIRCULAR].attention,
18427
+ CIRCULAR_NONE_FOUND: VALIDATION_STAGE_PROBLEM_MESSAGES[VALIDATION_STAGE_DISPLAY_NAMES.CIRCULAR].clear,
18428
+ KNIP_CONFIG_ERROR: formatValidationConfigProblemMessage(
18429
+ VALIDATION_STAGE_DISPLAY_NAMES.KNIP,
18430
+ "configuration error"
18431
+ ),
18352
18432
  KNIP_DISABLED: `${VALIDATION_STAGE_DISPLAY_NAMES.KNIP}: skipped (${VALIDATION_SKIP_LABELS.DISABLED_BY_PREFIX} validation.knip.enabled)`,
18353
- KNIP_SUCCESS: `${VALIDATION_STAGE_DISPLAY_NAMES.KNIP}: \u2713 No unused code found`,
18354
- KNIP_FAILURE: "Unused code found",
18355
- ESLINT_SUCCESS: `${VALIDATION_STAGE_DISPLAY_NAMES.ESLINT}: \u2713 No errors found`,
18356
- ESLINT_FAILURE: `${VALIDATION_STAGE_DISPLAY_NAMES.ESLINT} validation failed`,
18357
- ESLINT_MISSING_CONFIG: "ESLint config not found: project has tsconfig.json but no eslint.config.{ts,js,mjs,cjs}",
18358
- TYPESCRIPT_SUCCESS: `${VALIDATION_STAGE_DISPLAY_NAMES.TYPESCRIPT}: \u2713 No type errors`,
18359
- TYPESCRIPT_FAILURE: `${VALIDATION_STAGE_DISPLAY_NAMES.TYPESCRIPT} validation failed`,
18360
- MARKDOWN_NO_ISSUES: `${VALIDATION_STAGE_DISPLAY_NAMES.MARKDOWN}: No issues found`,
18361
- MARKDOWN_ERROR_SUMMARY_SUFFIX: "error(s) found",
18362
- FORMATTING_NO_ISSUES: `${VALIDATION_STAGE_DISPLAY_NAMES.FORMATTING}: \u2713 No formatting issues found`,
18363
- FORMATTING_FAILURE_SUMMARY: `${VALIDATION_STAGE_DISPLAY_NAMES.FORMATTING}: unformatted files found`
18364
- };
18365
- var CIRCULAR_SKIP_OUTPUT = `${VALIDATION_STAGE_DISPLAY_NAMES.CIRCULAR}: skipped (--${VALIDATION_SKIP_LABELS.CIRCULAR_REASON})`;
18366
- var CIRCULAR_SKIP_JSON_OUTPUT = JSON.stringify({
18367
- skipped: true,
18368
- reason: VALIDATION_SKIP_LABELS.CIRCULAR_REASON
18369
- });
18370
- var LITERAL_SKIP_OUTPUT = `${VALIDATION_STAGE_DISPLAY_NAMES.LITERAL}: skipped (--${VALIDATION_SKIP_LABELS.LITERAL_REASON})`;
18371
- var LITERAL_SKIP_JSON_OUTPUT = JSON.stringify({
18372
- skipped: true,
18373
- reason: VALIDATION_SKIP_LABELS.LITERAL_REASON
18374
- });
18433
+ KNIP_SUCCESS: VALIDATION_STAGE_PROBLEM_MESSAGES[VALIDATION_STAGE_DISPLAY_NAMES.KNIP].clear,
18434
+ KNIP_FAILURE: VALIDATION_STAGE_PROBLEM_MESSAGES[VALIDATION_STAGE_DISPLAY_NAMES.KNIP].attention,
18435
+ ESLINT_SUCCESS: VALIDATION_STAGE_PROBLEM_MESSAGES[VALIDATION_STAGE_DISPLAY_NAMES.ESLINT].clear,
18436
+ ESLINT_FAILURE: VALIDATION_STAGE_PROBLEM_MESSAGES[VALIDATION_STAGE_DISPLAY_NAMES.ESLINT].attention,
18437
+ ESLINT_MISSING_CONFIG: formatValidationConfigProblemMessage(
18438
+ VALIDATION_STAGE_DISPLAY_NAMES.ESLINT,
18439
+ "configuration missing: product has tsconfig.json but no eslint.config.{ts,js,mjs,cjs}"
18440
+ ),
18441
+ TYPESCRIPT_SUCCESS: VALIDATION_STAGE_PROBLEM_MESSAGES[VALIDATION_STAGE_DISPLAY_NAMES.TYPESCRIPT].clear,
18442
+ TYPESCRIPT_FAILURE: VALIDATION_STAGE_PROBLEM_MESSAGES[VALIDATION_STAGE_DISPLAY_NAMES.TYPESCRIPT].attention,
18443
+ MARKDOWN_NO_ISSUES: VALIDATION_STAGE_PROBLEM_MESSAGES[VALIDATION_STAGE_DISPLAY_NAMES.MARKDOWN].clear,
18444
+ FORMATTING_NO_ISSUES: VALIDATION_STAGE_PROBLEM_MESSAGES[VALIDATION_STAGE_DISPLAY_NAMES.FORMATTING].clear,
18445
+ FORMATTING_FAILURE_SUMMARY: VALIDATION_STAGE_PROBLEM_MESSAGES[VALIDATION_STAGE_DISPLAY_NAMES.FORMATTING].attention
18446
+ };
18447
+ function formatValidationStageSkipOutput(stageName, reason) {
18448
+ return `${stageName}: skipped (${reason})`;
18449
+ }
18450
+ function formatValidationStageSkipJsonOutput(reason, durationMs) {
18451
+ return JSON.stringify({ skipped: true, reason, durationMs });
18452
+ }
18375
18453
  function formatTypeScriptAbsentSkipMessage(stageName) {
18376
18454
  return `\u23ED ${VALIDATION_SKIP_LABELS.VERB} ${stageName} (${VALIDATION_SKIP_LABELS.TYPESCRIPT_ABSENT_REASON})`;
18377
18455
  }
18378
18456
  function formatValidationPathsNoTargetsSkipMessage(stageName) {
18379
18457
  return `\u23ED ${VALIDATION_SKIP_LABELS.VERB} ${stageName} (${VALIDATION_SKIP_LABELS.VALIDATION_PATHS_NO_TARGETS_REASON})`;
18380
18458
  }
18459
+ function formatExplicitPathsNoTargetsSkipMessage(stageName) {
18460
+ return `\u23ED ${VALIDATION_SKIP_LABELS.VERB} ${stageName} (${VALIDATION_SKIP_LABELS.EXPLICIT_PATHS_NO_TARGETS_REASON})`;
18461
+ }
18462
+ function formatValidationScopeNoTargetsSkipMessage(stageName, metadata) {
18463
+ if (metadata.explicitPathNoMatches) {
18464
+ return formatExplicitPathsNoTargetsSkipMessage(stageName);
18465
+ }
18466
+ if (metadata.filteredByValidationPathNoMatches) {
18467
+ return formatValidationPathsNoTargetsSkipMessage(stageName);
18468
+ }
18469
+ return void 0;
18470
+ }
18471
+
18472
+ // src/commands/validation/types.ts
18473
+ var VALIDATION_OUTPUT_TARGET = {
18474
+ STDOUT: "stdout",
18475
+ STDERR: "stderr"
18476
+ };
18477
+ var VALIDATION_STREAMED_TERMINAL_OUTPUT = "";
18478
+ function streamedValidationTerminalOutput(subprocessOutput, json, streamedPipelineOutput) {
18479
+ return json !== true && streamedPipelineOutput === true && subprocessOutput !== void 0 && subprocessOutput.length > 0 ? VALIDATION_STREAMED_TERMINAL_OUTPUT : void 0;
18480
+ }
18381
18481
 
18382
18482
  // src/commands/validation/formatting.ts
18483
+ var defaultFormattingCommandDependencies = {
18484
+ validateFormatting
18485
+ };
18383
18486
  var FORMATTING_COMMAND_OUTPUT = {
18384
18487
  NO_ISSUES: VALIDATION_COMMAND_OUTPUT.FORMATTING_NO_ISSUES,
18385
18488
  FAILURE_SUMMARY: VALIDATION_COMMAND_OUTPUT.FORMATTING_FAILURE_SUMMARY,
18386
18489
  EMPTY_SCOPE_REASON: "no files in scope",
18387
18490
  NO_CONFIG_SKIP_REASON: `no ${DPRINT_CONFIG_FILENAME} at product root`
18388
18491
  };
18389
- var FORMATTING_CONFIG_ERROR_MESSAGE = `${VALIDATION_STAGE_DISPLAY_NAMES.FORMATTING}: \u2717 config error`;
18492
+ var FORMATTING_CONFIG_ERROR_MESSAGE = formatValidationConfigProblemMessage(
18493
+ VALIDATION_STAGE_DISPLAY_NAMES.FORMATTING,
18494
+ "configuration error"
18495
+ );
18390
18496
  var DPRINT_RECURSIVE_DIRECTORY_GLOB_SUFFIX = "/**/*";
18391
- async function formattingCommand(options) {
18392
- const { cwd, files, quiet } = options;
18497
+ async function formattingCommand(options, dependencies = defaultFormattingCommandDependencies) {
18498
+ const { cwd, files, json, outputStreams, quiet, streamedPipelineOutput } = options;
18393
18499
  const startTime = Date.now();
18394
18500
  const loaded = await resolveConfig(cwd, [validationConfigDescriptor]);
18395
18501
  if (!loaded.ok) {
@@ -18409,32 +18515,52 @@ async function formattingCommand(options) {
18409
18515
  VALIDATION_PATH_TOOL_SUBSECTIONS.FORMATTING
18410
18516
  );
18411
18517
  const hasExplicitScope = files !== void 0 && files.length > 0;
18412
- const scopedFiles = hasExplicitScope ? files.flatMap(
18413
- (filePath) => formattingPathOperandsForValidationPathFilter(
18414
- cwd,
18415
- isAbsolute6(filePath) ? relative6(cwd, filePath) : filePath,
18416
- pathFilter
18417
- )
18418
- ) : void 0;
18419
- const scopedExcludes = hasExplicitScope && files.some(
18420
- (filePath) => isFormattingFileOperand(cwd, isAbsolute6(filePath) ? relative6(cwd, filePath) : filePath)
18421
- ) ? [] : validationPathFilterExcludes(pathFilter);
18422
- if (hasExplicitScope && (scopedFiles === void 0 || scopedFiles.length === 0)) {
18518
+ const contexts = formattingValidationContexts(cwd, files, pathFilter);
18519
+ const scopedFiles = contexts.flatMap((context) => context.files ?? []);
18520
+ if (hasExplicitScope && scopedFiles.length === 0 || contexts.length === 0) {
18423
18521
  const output2 = quiet ? "" : `${VALIDATION_STAGE_DISPLAY_NAMES.FORMATTING}: skipped (${FORMATTING_COMMAND_OUTPUT.EMPTY_SCOPE_REASON})`;
18424
18522
  return { exitCode: 0, output: output2, durationMs: Date.now() - startTime };
18425
18523
  }
18426
- const result = await validateFormatting({
18427
- projectRoot: cwd,
18428
- files: scopedFiles,
18429
- excludes: scopedExcludes
18430
- });
18524
+ const results = [];
18525
+ const subprocessOutputStreams = outputStreams ?? discardValidationSubprocessOutputStreams;
18526
+ for (const context of contexts) {
18527
+ results.push(await dependencies.validateFormatting(context, void 0, subprocessOutputStreams));
18528
+ }
18431
18529
  const durationMs = Date.now() - startTime;
18432
- if (result.success) {
18530
+ if (results.every((result) => result.success)) {
18433
18531
  return { exitCode: 0, output: quiet ? "" : FORMATTING_COMMAND_OUTPUT.NO_ISSUES, durationMs };
18434
18532
  }
18435
- const detail = result.error ?? result.output;
18533
+ const detail = results.filter((result) => !result.success).flatMap((result) => [result.output, result.error]).filter((output2) => output2 !== void 0).filter((output2) => output2.length > 0).join("\n");
18436
18534
  const output = [FORMATTING_COMMAND_OUTPUT.FAILURE_SUMMARY, detail].filter((line) => line.length > 0).join("\n");
18437
- return { exitCode: 1, output, durationMs };
18535
+ const terminalOutput = formattingTerminalOutput(results, outputStreams, json, streamedPipelineOutput);
18536
+ return { exitCode: 1, output, terminalOutput, durationMs };
18537
+ }
18538
+ function formattingValidationContexts(productDir, files, pathFilter) {
18539
+ if (files === void 0 || files.length === 0) {
18540
+ if (validationPathFilterHasNoMatchingIncludes(pathFilter)) return [];
18541
+ const automaticFiles = pathFilter.include?.map((path4) => normalizeFormattingPathOperand(productDir, path4));
18542
+ return [{
18543
+ productDir,
18544
+ files: automaticFiles !== void 0 && automaticFiles.length > 0 ? automaticFiles : void 0,
18545
+ excludes: validationPathFilterExcludes(pathFilter)
18546
+ }];
18547
+ }
18548
+ const relativeFiles = files.map((filePath) => isAbsolute6(filePath) ? relative6(productDir, filePath) : filePath);
18549
+ return [{
18550
+ productDir,
18551
+ files: relativeFiles.map((filePath) => normalizeFormattingPathOperand(productDir, filePath)),
18552
+ excludes: []
18553
+ }];
18554
+ }
18555
+ function formattingTerminalOutput(results, outputStreams, json, streamedPipelineOutput) {
18556
+ if (json === true || outputStreams === void 0) {
18557
+ return void 0;
18558
+ }
18559
+ const errors = results.flatMap((result) => result.error === void 0 ? [] : [result.error]);
18560
+ if (errors.length > 0) {
18561
+ return [FORMATTING_COMMAND_OUTPUT.FAILURE_SUMMARY, ...errors].join("\n");
18562
+ }
18563
+ return streamedPipelineOutput === true ? VALIDATION_STREAMED_TERMINAL_OUTPUT : FORMATTING_COMMAND_OUTPUT.FAILURE_SUMMARY;
18438
18564
  }
18439
18565
  function normalizeFormattingPathOperand(productDir, relativePath) {
18440
18566
  if (isFormattingFileOperand(productDir, relativePath)) {
@@ -18450,22 +18576,35 @@ function isFormattingFileOperand(productDir, relativePath) {
18450
18576
  const absolutePath = join29(productDir, relativePath);
18451
18577
  return !existsSync(absolutePath) || !statSync(absolutePath).isDirectory();
18452
18578
  }
18453
- function formattingPathOperandsForValidationPathFilter(productDir, relativePath, pathFilter) {
18454
- if (isFormattingFileOperand(productDir, relativePath)) {
18455
- return [relativePath];
18456
- }
18457
- return validationPathFilterIntersections(relativePath, pathFilter).map((path4) => normalizeFormattingPathOperand(productDir, path4));
18458
- }
18459
18579
 
18460
18580
  // src/validation/languages/formatting.ts
18461
18581
  var FORMATTING_LANGUAGE_NAME = "formatting";
18582
+ var SKIP_FORMATTING_REASON = "skip-formatting";
18583
+ var FORMATTING_VALIDATION_STAGE_PARTICIPATION = {
18584
+ [VALIDATION_STAGE_DISPLAY_NAMES.FORMATTING]: {
18585
+ default: VALIDATION_STAGE_PARTICIPATION.RUN,
18586
+ skipReason: SKIP_FORMATTING_REASON,
18587
+ override: {
18588
+ flag: "--skip-formatting",
18589
+ description: "Skip formatting validation for this validation all run"
18590
+ }
18591
+ }
18592
+ };
18462
18593
  var formattingValidationLanguage = {
18463
18594
  name: FORMATTING_LANGUAGE_NAME,
18464
18595
  stages: [
18465
18596
  {
18466
18597
  name: VALIDATION_STAGE_DISPLAY_NAMES.FORMATTING,
18467
18598
  failsPipeline: true,
18468
- run: (context) => formattingCommand({ cwd: context.cwd, files: context.files, quiet: context.quiet })
18599
+ participation: FORMATTING_VALIDATION_STAGE_PARTICIPATION[VALIDATION_STAGE_DISPLAY_NAMES.FORMATTING],
18600
+ run: (context) => formattingCommand({
18601
+ cwd: context.cwd,
18602
+ files: context.files,
18603
+ quiet: context.quiet,
18604
+ json: context.json,
18605
+ streamedPipelineOutput: true,
18606
+ outputStreams: context.outputStreams
18607
+ })
18469
18608
  }
18470
18609
  ]
18471
18610
  };
@@ -18506,6 +18645,11 @@ var MARKDOWN_VALIDATION_TARGET_DIAGNOSTICS = {
18506
18645
  var defaultMarkdownValidationTargetDeps = {
18507
18646
  statSync: statSync2
18508
18647
  };
18648
+ var defaultMarkdownValidationDeps = {
18649
+ runMarkdownlint: async (options) => {
18650
+ await markdownlintMain(options);
18651
+ }
18652
+ };
18509
18653
  function buildMarkdownlintConfig(directoryName) {
18510
18654
  const md024Disabled = MD024_DISABLED_DIRECTORIES.includes(
18511
18655
  directoryName
@@ -18517,8 +18661,8 @@ function buildMarkdownlintConfig(directoryName) {
18517
18661
  [MARKDOWN_CONFIG_CONTROL_KEYS.CUSTOM_RULES]: [relativeLinksRule]
18518
18662
  };
18519
18663
  }
18520
- function getDefaultDirectories(projectRoot) {
18521
- return MARKDOWN_DEFAULT_DIRECTORY_NAMES.map((name) => join30(projectRoot, name)).filter((dir) => existsSync2(dir));
18664
+ function getDefaultDirectories(productDir) {
18665
+ return MARKDOWN_DEFAULT_DIRECTORY_NAMES.map((name) => join30(productDir, name)).filter((dir) => existsSync2(dir));
18522
18666
  }
18523
18667
  function resolveMarkdownValidationTarget(path4, deps = defaultMarkdownValidationTargetDeps) {
18524
18668
  if (isExistingDirectory(path4, deps)) {
@@ -18534,10 +18678,10 @@ function resolveMarkdownValidationTarget(path4, deps = defaultMarkdownValidation
18534
18678
  }
18535
18679
  };
18536
18680
  }
18537
- function getExcludeGlobsForTarget(target, projectRoot, entries) {
18538
- if (projectRoot === void 0 || entries.length === 0) return [];
18681
+ function getExcludeGlobsForTarget(target, productDir, entries) {
18682
+ if (productDir === void 0 || entries.length === 0) return [];
18539
18683
  const directory = targetDirectory(target);
18540
- const specTreeRoot = join30(projectRoot, SPEC_TREE_CONFIG.ROOT_DIRECTORY);
18684
+ const specTreeRoot = join30(productDir, SPEC_TREE_CONFIG.ROOT_DIRECTORY);
18541
18685
  const targetPath = normalizePathPrefix(pathRelative(specTreeRoot, directory));
18542
18686
  return entries.flatMap((entry) => {
18543
18687
  const excludedPath = normalizePathPrefix(entry);
@@ -18615,24 +18759,24 @@ function isAsciiDigit(value) {
18615
18759
  function isAsciiWhitespace(value) {
18616
18760
  return value === " " || value === " ";
18617
18761
  }
18618
- async function validateMarkdown(options) {
18762
+ async function validateMarkdown(options, deps = defaultMarkdownValidationDeps) {
18619
18763
  const {
18620
18764
  targets,
18621
- projectRoot,
18765
+ productDir,
18622
18766
  applyNodeStatusExcludes = true,
18623
18767
  validationPathExcludes = []
18624
18768
  } = options;
18625
18769
  const errors = [];
18626
- const specTreeExcludeEntries = applyNodeStatusExcludes ? getExcludeEntries(projectRoot) : [];
18770
+ const specTreeExcludeEntries = applyNodeStatusExcludes ? getExcludeEntries(productDir) : [];
18627
18771
  for (const target of targets) {
18628
18772
  const directory = targetDirectory(target);
18629
- const dirName = markdownlintConfigDirectoryName(directory, projectRoot);
18773
+ const dirName = markdownlintConfigDirectoryName(directory, productDir);
18630
18774
  const config = buildMarkdownlintConfig(dirName);
18631
18775
  const excludeGlobs = [
18632
- ...getExcludeGlobsForTarget(target, projectRoot, specTreeExcludeEntries),
18633
- ...validationPathExcludeGlobsForTarget(target, projectRoot, validationPathExcludes)
18776
+ ...getExcludeGlobsForTarget(target, productDir, specTreeExcludeEntries),
18777
+ ...validationPathExcludeGlobsForTarget(target, productDir, validationPathExcludes)
18634
18778
  ];
18635
- const dirErrors = await validateTarget(target, config, projectRoot, excludeGlobs);
18779
+ const dirErrors = await validateTarget(target, config, deps, productDir, excludeGlobs);
18636
18780
  errors.push(...dirErrors);
18637
18781
  }
18638
18782
  return {
@@ -18640,11 +18784,11 @@ async function validateMarkdown(options) {
18640
18784
  errors
18641
18785
  };
18642
18786
  }
18643
- function getExcludeEntries(projectRoot) {
18644
- if (projectRoot === void 0) return [];
18645
- return createNodeStatusExcludeReader(projectRoot).entries();
18787
+ function getExcludeEntries(productDir) {
18788
+ if (productDir === void 0) return [];
18789
+ return createNodeStatusExcludeReader(productDir).entries();
18646
18790
  }
18647
- async function validateTarget(target, config, projectRoot, ignoreGlobs = []) {
18791
+ async function validateTarget(target, config, deps, productDir, ignoreGlobs = []) {
18648
18792
  const errors = [];
18649
18793
  const directory = targetDirectory(target);
18650
18794
  const argv = target.kind === MARKDOWN_VALIDATION_TARGET_KIND.FILE ? [basename7(target.path)] : [MARKDOWN_DIRECTORY_GLOB];
@@ -18652,14 +18796,14 @@ async function validateTarget(target, config, projectRoot, ignoreGlobs = []) {
18652
18796
  const optionsOverride = {
18653
18797
  config: {
18654
18798
  ...markdownlintConfig,
18655
- "relative-links": projectRoot ? { root_path: projectRoot } : true
18799
+ "relative-links": productDir ? { root_path: productDir } : true
18656
18800
  },
18657
18801
  customRules,
18658
18802
  noProgress: true,
18659
18803
  noBanner: true,
18660
18804
  ...ignoreGlobs.length > 0 ? { ignores: ignoreGlobs } : {}
18661
18805
  };
18662
- await markdownlintMain({
18806
+ await deps.runMarkdownlint({
18663
18807
  directory,
18664
18808
  argv,
18665
18809
  optionsOverride,
@@ -18681,10 +18825,10 @@ async function validateTarget(target, config, projectRoot, ignoreGlobs = []) {
18681
18825
  function targetDirectory(target) {
18682
18826
  return target.kind === MARKDOWN_VALIDATION_TARGET_KIND.FILE ? dirname13(target.path) : target.path;
18683
18827
  }
18684
- function validationPathExcludeGlobsForTarget(target, projectRoot, excludes) {
18685
- if (projectRoot === void 0 || excludes.length === 0 || target.kind === MARKDOWN_VALIDATION_TARGET_KIND.FILE) return [];
18828
+ function validationPathExcludeGlobsForTarget(target, productDir, excludes) {
18829
+ if (productDir === void 0 || excludes.length === 0 || target.kind === MARKDOWN_VALIDATION_TARGET_KIND.FILE) return [];
18686
18830
  const directory = targetDirectory(target);
18687
- const targetPath = normalizePathPrefix(pathRelative(projectRoot, directory));
18831
+ const targetPath = normalizePathPrefix(pathRelative(productDir, directory));
18688
18832
  return excludes.flatMap((exclude) => {
18689
18833
  const excludedPath = normalizePathPrefix(exclude);
18690
18834
  if (targetPath === excludedPath) {
@@ -18693,11 +18837,11 @@ function validationPathExcludeGlobsForTarget(target, projectRoot, excludes) {
18693
18837
  if (!pathContainsValidationPath(targetPath, excludedPath)) {
18694
18838
  return [];
18695
18839
  }
18696
- const relativeExclude = normalizePathPrefix(pathRelative(directory, join30(projectRoot, excludedPath)));
18840
+ const relativeExclude = normalizePathPrefix(pathRelative(directory, join30(productDir, excludedPath)));
18697
18841
  if (relativeExclude.length === 0) {
18698
18842
  return [MARKDOWN_DIRECTORY_GLOB];
18699
18843
  }
18700
- const absoluteExclude = join30(projectRoot, excludedPath);
18844
+ const absoluteExclude = join30(productDir, excludedPath);
18701
18845
  return [
18702
18846
  isExistingFile(absoluteExclude, defaultMarkdownValidationTargetDeps) ? relativeExclude : `${relativeExclude}/**`
18703
18847
  ];
@@ -18726,9 +18870,9 @@ function isExistingFile(path4, deps) {
18726
18870
  return false;
18727
18871
  }
18728
18872
  }
18729
- function markdownlintConfigDirectoryName(directory, projectRoot) {
18730
- if (projectRoot !== void 0) {
18731
- const [rootSegment] = pathRelative(projectRoot, directory).split(/[\\/]/);
18873
+ function markdownlintConfigDirectoryName(directory, productDir) {
18874
+ if (productDir !== void 0) {
18875
+ const [rootSegment] = pathRelative(productDir, directory).split(/[\\/]/);
18732
18876
  if (MD024_DISABLED_DIRECTORIES.includes(rootSegment)) {
18733
18877
  return rootSegment;
18734
18878
  }
@@ -18738,11 +18882,14 @@ function markdownlintConfigDirectoryName(directory, projectRoot) {
18738
18882
 
18739
18883
  // src/commands/validation/markdown.ts
18740
18884
  var MARKDOWN_COMMAND_OUTPUT = {
18741
- ERROR_SUMMARY_SUFFIX: VALIDATION_COMMAND_OUTPUT.MARKDOWN_ERROR_SUMMARY_SUFFIX,
18742
18885
  NO_ISSUES: VALIDATION_COMMAND_OUTPUT.MARKDOWN_NO_ISSUES,
18886
+ PROBLEM_TERM: VALIDATION_PROBLEM_TERMS.SINGULAR,
18743
18887
  SKIPPED_FILE_SCOPE_PREFIX: "Markdown skipped file scope"
18744
18888
  };
18745
- var MARKDOWN_CONFIG_ERROR_MESSAGE = `${VALIDATION_STAGE_DISPLAY_NAMES.MARKDOWN}: \u2717 config error`;
18889
+ var MARKDOWN_CONFIG_ERROR_MESSAGE = formatValidationConfigProblemMessage(
18890
+ VALIDATION_STAGE_DISPLAY_NAMES.MARKDOWN,
18891
+ "configuration error"
18892
+ );
18746
18893
  async function markdownCommand(options) {
18747
18894
  const { cwd, files, quiet } = options;
18748
18895
  const startTime = Date.now();
@@ -18775,8 +18922,8 @@ async function markdownCommand(options) {
18775
18922
  }
18776
18923
  const result = await validateMarkdown({
18777
18924
  targets,
18778
- projectRoot: cwd,
18779
- validationPathExcludes: validationPathFilterExcludes(pathFilter)
18925
+ productDir: cwd,
18926
+ validationPathExcludes: targetResolutions === void 0 ? validationPathFilterExcludes(pathFilter) : []
18780
18927
  });
18781
18928
  const durationMs = Date.now() - startTime;
18782
18929
  return formatMarkdownResult(result, skippedOutput, quiet, durationMs);
@@ -18786,7 +18933,7 @@ function markdownValidationOperandPath(productDir, filePath) {
18786
18933
  }
18787
18934
  function defaultMarkdownTargets(productDir, pathFilter) {
18788
18935
  return getDefaultDirectories(productDir).flatMap(
18789
- (directory) => validationPathFilterIntersections(toProjectRelativeValidationPath(productDir, directory), pathFilter).map((intersection) => resolveMarkdownValidationTarget(join31(productDir, intersection)).target).filter((target) => target !== void 0)
18936
+ (directory) => validationPathFilterIntersections(toProductRelativeValidationPath(productDir, directory), pathFilter).map((intersection) => resolveMarkdownValidationTarget(join31(productDir, intersection)).target).filter((target) => target !== void 0)
18790
18937
  );
18791
18938
  }
18792
18939
  function formatSkippedFileScope(target) {
@@ -18802,7 +18949,9 @@ function formatMarkdownResult(result, skippedOutput, quiet, durationMs) {
18802
18949
  );
18803
18950
  const output = [
18804
18951
  ...skippedOutput,
18805
- `Markdown: ${result.errors.length} ${MARKDOWN_COMMAND_OUTPUT.ERROR_SUMMARY_SUFFIX}`,
18952
+ formatValidationProblemsFoundMessage(VALIDATION_STAGE_DISPLAY_NAMES.MARKDOWN, {
18953
+ count: result.errors.length
18954
+ }),
18806
18955
  ...errorLines
18807
18956
  ].join("\n");
18808
18957
  return { exitCode: 1, output, durationMs };
@@ -18810,12 +18959,24 @@ function formatMarkdownResult(result, skippedOutput, quiet, durationMs) {
18810
18959
 
18811
18960
  // src/validation/languages/markdown.ts
18812
18961
  var MARKDOWN_LANGUAGE_NAME = "markdown";
18962
+ var SKIP_MARKDOWN_REASON = "skip-markdown";
18963
+ var MARKDOWN_VALIDATION_STAGE_PARTICIPATION = {
18964
+ [VALIDATION_STAGE_DISPLAY_NAMES.MARKDOWN]: {
18965
+ default: VALIDATION_STAGE_PARTICIPATION.RUN,
18966
+ skipReason: SKIP_MARKDOWN_REASON,
18967
+ override: {
18968
+ flag: "--skip-markdown",
18969
+ description: "Skip Markdown validation for this validation all run"
18970
+ }
18971
+ }
18972
+ };
18813
18973
  var markdownValidationLanguage = {
18814
18974
  name: MARKDOWN_LANGUAGE_NAME,
18815
18975
  stages: [
18816
18976
  {
18817
18977
  name: VALIDATION_STAGE_DISPLAY_NAMES.MARKDOWN,
18818
18978
  failsPipeline: true,
18979
+ participation: MARKDOWN_VALIDATION_STAGE_PARTICIPATION[VALIDATION_STAGE_DISPLAY_NAMES.MARKDOWN],
18819
18980
  run: (context) => markdownCommand({ cwd: context.cwd, files: context.files, quiet: context.quiet })
18820
18981
  }
18821
18982
  ]
@@ -19687,7 +19848,7 @@ var TSCONFIG_FILES = {
19687
19848
  full: "tsconfig.json",
19688
19849
  production: "tsconfig.production.json"
19689
19850
  };
19690
- var PATH_SEGMENT_SEPARATOR4 = "/";
19851
+ var PATH_SEGMENT_SEPARATOR3 = "/";
19691
19852
  var GLOB_MARKER = "*";
19692
19853
  var RECURSIVE_GLOB_SEGMENT = "**";
19693
19854
  var SINGLE_CHARACTER_GLOB_MARKER = "?";
@@ -19718,8 +19879,8 @@ var EXPLICIT_TYPESCRIPT_SCOPE_TARGET_KIND = {
19718
19879
  DIRECTORY: "directory",
19719
19880
  FILE: "file"
19720
19881
  };
19721
- function resolveProjectPath(projectRoot, path4) {
19722
- return isAbsolute8(path4) ? path4 : join32(projectRoot, path4);
19882
+ function resolveProductPath(productDir, path4) {
19883
+ return isAbsolute8(path4) ? path4 : join32(productDir, path4);
19723
19884
  }
19724
19885
  function parseTypeScriptConfig(configPath, deps = defaultScopeDeps) {
19725
19886
  try {
@@ -19733,11 +19894,11 @@ function parseTypeScriptConfig(configPath, deps = defaultScopeDeps) {
19733
19894
  };
19734
19895
  }
19735
19896
  }
19736
- function resolveTypeScriptConfig(scope2, projectRoot, deps = defaultScopeDeps) {
19897
+ function resolveTypeScriptConfig(scope2, productDir, deps = defaultScopeDeps) {
19737
19898
  const configFile = TSCONFIG_FILES[scope2];
19738
- const config = parseTypeScriptConfig(resolveProjectPath(projectRoot, configFile), deps);
19899
+ const config = parseTypeScriptConfig(resolveProductPath(productDir, configFile), deps);
19739
19900
  if (config.extends) {
19740
- const baseConfigs = normalizeExtends(config.extends).map((extendedConfig) => parseTypeScriptConfig(resolveProjectPath(projectRoot, extendedConfig), deps));
19901
+ const baseConfigs = normalizeExtends(config.extends).map((extendedConfig) => parseTypeScriptConfig(resolveProductPath(productDir, extendedConfig), deps));
19741
19902
  const inheritedInclude = [...baseConfigs].reverse().find((baseConfig) => baseConfig.include !== void 0)?.include ?? [];
19742
19903
  const inheritedExclude = [...baseConfigs].reverse().find((baseConfig) => baseConfig.exclude !== void 0)?.exclude ?? [];
19743
19904
  return {
@@ -19773,48 +19934,48 @@ function hasTypeScriptFilesRecursive(dirPath, maxDepth = 2, deps = defaultScopeD
19773
19934
  }
19774
19935
  }
19775
19936
  function pathPassesTypeScriptFileDiscoveryExcludes(path4, options) {
19776
- const { excludePatterns = [], projectRoot } = options;
19777
- if (projectRoot === void 0) {
19937
+ const { excludePatterns = [], productDir } = options;
19938
+ if (productDir === void 0) {
19778
19939
  return true;
19779
19940
  }
19780
- const projectRelativePath = toProjectRelativeTypeScriptScopePath(projectRoot, path4);
19941
+ const projectRelativePath = toProductRelativeTypeScriptScopePath(productDir, path4);
19781
19942
  return !excludePatterns.some((pattern) => pathMatchesTypeScriptPattern(projectRelativePath, pattern));
19782
19943
  }
19783
- function getTopLevelDirectoriesWithTypeScript(config, projectRoot, deps = defaultScopeDeps) {
19944
+ function getTopLevelDirectoriesWithTypeScript(config, productDir, deps = defaultScopeDeps) {
19784
19945
  const directories = /* @__PURE__ */ new Set();
19785
- for (const dir of listTopLevelDirectories(projectRoot, deps)) {
19786
- if (directoryContributesTypeScriptScope(dir, config, projectRoot, deps)) {
19946
+ for (const dir of listTopLevelDirectories(productDir, deps)) {
19947
+ if (directoryContributesTypeScriptScope(dir, config, productDir, deps)) {
19787
19948
  directories.add(dir);
19788
19949
  }
19789
19950
  }
19790
- for (const dir of explicitIncludeTopLevelDirectories(config, projectRoot, deps)) {
19951
+ for (const dir of explicitIncludeTopLevelDirectories(config, productDir, deps)) {
19791
19952
  directories.add(dir);
19792
19953
  }
19793
19954
  return Array.from(directories).sort(compareAsciiStrings);
19794
19955
  }
19795
- function listTopLevelDirectories(projectRoot, deps) {
19796
- return deps.readdirSync(projectRoot, { withFileTypes: true }).filter((item) => item.isDirectory()).map((item) => item.name).filter((name) => !name.startsWith("."));
19956
+ function listTopLevelDirectories(productDir, deps) {
19957
+ return deps.readdirSync(productDir, { withFileTypes: true }).filter((item) => item.isDirectory()).map((item) => item.name).filter((name) => !name.startsWith("."));
19797
19958
  }
19798
- function directoryContributesTypeScriptScope(dir, config, projectRoot, deps) {
19799
- if (!directoryPassesIncludePatterns(dir, config.include ?? [], projectRoot, deps)) {
19959
+ function directoryContributesTypeScriptScope(dir, config, productDir, deps) {
19960
+ if (!directoryPassesIncludePatterns(dir, config.include ?? [], productDir, deps)) {
19800
19961
  return false;
19801
19962
  }
19802
19963
  try {
19803
- return hasTypeScriptFilesRecursive(join32(projectRoot, dir), 2, deps, {
19964
+ return hasTypeScriptFilesRecursive(join32(productDir, dir), 2, deps, {
19804
19965
  excludePatterns: config.exclude,
19805
- projectRoot
19966
+ productDir
19806
19967
  });
19807
19968
  } catch {
19808
19969
  return false;
19809
19970
  }
19810
19971
  }
19811
- function explicitIncludeTopLevelDirectories(config, projectRoot, deps) {
19972
+ function explicitIncludeTopLevelDirectories(config, productDir, deps) {
19812
19973
  if (!config.include) {
19813
19974
  return [];
19814
19975
  }
19815
19976
  const directories = [];
19816
19977
  for (const pattern of config.include) {
19817
- if (includePatternTargetsTypeScriptScope(pattern, projectRoot, deps) && pattern.includes(PATH_SEGMENT_SEPARATOR4)) {
19978
+ if (includePatternTargetsTypeScriptScope(pattern, productDir, deps) && pattern.includes(PATH_SEGMENT_SEPARATOR3)) {
19818
19979
  const topLevelDir = getLiteralTopLevelPatternDirectory(pattern);
19819
19980
  if (topLevelDir) {
19820
19981
  directories.push(topLevelDir);
@@ -19824,39 +19985,39 @@ function explicitIncludeTopLevelDirectories(config, projectRoot, deps) {
19824
19985
  return directories;
19825
19986
  }
19826
19987
  function getLiteralTopLevelPatternDirectory(pattern) {
19827
- const topLevelDir = pattern.split(PATH_SEGMENT_SEPARATOR4)[0];
19988
+ const topLevelDir = pattern.split(PATH_SEGMENT_SEPARATOR3)[0];
19828
19989
  if (!topLevelDir || typeScriptScopePatternHasGlob(topLevelDir) || topLevelDir.startsWith(HIDDEN_PATH_PREFIX)) {
19829
19990
  return null;
19830
19991
  }
19831
19992
  return topLevelDir;
19832
19993
  }
19833
- function directoryPassesIncludePatterns(directory, patterns, projectRoot, deps) {
19994
+ function directoryPassesIncludePatterns(directory, patterns, productDir, deps) {
19834
19995
  return patterns.length === 0 || patterns.some(
19835
- (pattern) => includePatternTargetsTypeScriptScope(pattern, projectRoot, deps) && typeScriptScopePatternIntersectsDirectory(pattern, directory)
19996
+ (pattern) => includePatternTargetsTypeScriptScope(pattern, productDir, deps) && typeScriptScopePatternIntersectsDirectory(pattern, directory)
19836
19997
  );
19837
19998
  }
19838
19999
  function stripTrailingPathSeparators2(value) {
19839
20000
  let end = value.length;
19840
- while (end > 0 && value.charAt(end - 1) === PATH_SEGMENT_SEPARATOR4) {
20001
+ while (end > 0 && value.charAt(end - 1) === PATH_SEGMENT_SEPARATOR3) {
19841
20002
  end -= 1;
19842
20003
  }
19843
20004
  return value.slice(0, end);
19844
20005
  }
19845
20006
  function normalizeTypeScriptScopePath(path4) {
19846
- const joined = path4.split(/[\\/]/gu).join(PATH_SEGMENT_SEPARATOR4).replace(/^\.\//u, "");
20007
+ const joined = path4.split(/[\\/]/gu).join(PATH_SEGMENT_SEPARATOR3).replace(/^\.\//u, "");
19847
20008
  return stripTrailingPathSeparators2(joined);
19848
20009
  }
19849
20010
  function pathMatchesLiteralPrefix(path4, prefix) {
19850
20011
  const normalizedPath = normalizeTypeScriptScopePath(path4);
19851
20012
  const normalizedPrefix = normalizeTypeScriptScopePath(prefix);
19852
- return normalizedPath === normalizedPrefix || normalizedPath.startsWith(`${normalizedPrefix}${PATH_SEGMENT_SEPARATOR4}`);
20013
+ return normalizedPath === normalizedPrefix || normalizedPath.startsWith(`${normalizedPrefix}${PATH_SEGMENT_SEPARATOR3}`);
19853
20014
  }
19854
20015
  function splitTypeScriptScopePathSegments(path4) {
19855
20016
  const normalizedPath = normalizeTypeScriptScopePath(path4);
19856
20017
  if (normalizedPath.length === 0 || normalizedPath === ".") {
19857
20018
  return [];
19858
20019
  }
19859
- return normalizedPath.split(PATH_SEGMENT_SEPARATOR4);
20020
+ return normalizedPath.split(PATH_SEGMENT_SEPARATOR3);
19860
20021
  }
19861
20022
  function globLiteralPrefix(pattern) {
19862
20023
  const normalizedPattern = normalizeTypeScriptScopePath(pattern);
@@ -19930,16 +20091,16 @@ function typeScriptScopeGlobPatternToRegExp(pattern) {
19930
20091
  const character = normalizedPattern[index];
19931
20092
  const nextCharacter = normalizedPattern[index + 1];
19932
20093
  const followingCharacter = normalizedPattern[index + 2];
19933
- if (character === GLOB_MARKER && nextCharacter === GLOB_MARKER && followingCharacter === PATH_SEGMENT_SEPARATOR4) {
19934
- source += `(?:.*${PATH_SEGMENT_SEPARATOR4})?`;
20094
+ if (character === GLOB_MARKER && nextCharacter === GLOB_MARKER && followingCharacter === PATH_SEGMENT_SEPARATOR3) {
20095
+ source += `(?:.*${PATH_SEGMENT_SEPARATOR3})?`;
19935
20096
  index += 2;
19936
20097
  } else if (character === GLOB_MARKER && nextCharacter === GLOB_MARKER) {
19937
20098
  source += ".*";
19938
20099
  index += 1;
19939
20100
  } else if (character === GLOB_MARKER) {
19940
- source += `[^${PATH_SEGMENT_SEPARATOR4}]*`;
20101
+ source += `[^${PATH_SEGMENT_SEPARATOR3}]*`;
19941
20102
  } else if (character === SINGLE_CHARACTER_GLOB_MARKER) {
19942
- source += `[^${PATH_SEGMENT_SEPARATOR4}]`;
20103
+ source += `[^${PATH_SEGMENT_SEPARATOR3}]`;
19943
20104
  } else {
19944
20105
  source += character.replaceAll(GLOB_REGEX_SPECIAL_CHARACTER_PATTERN, REGEX_ESCAPE_REPLACEMENT);
19945
20106
  }
@@ -20007,12 +20168,12 @@ function typeScriptScopePatternTargetsTypeScriptSource(pattern) {
20007
20168
  const terminalSegment = splitTypeScriptScopePathSegments(normalizedPattern).at(-1) ?? normalizedPattern;
20008
20169
  return typeScriptScopePatternHasGlob(terminalSegment) && !TERMINAL_EXTENSION_PATTERN.test(terminalSegment);
20009
20170
  }
20010
- function includePatternTargetsTypeScriptScope(pattern, projectRoot, deps) {
20011
- return includePatternIsLiteralDirectory(pattern, projectRoot, deps) || typeScriptScopePatternTargetsTypeScriptSource(pattern);
20171
+ function includePatternTargetsTypeScriptScope(pattern, productDir, deps) {
20172
+ return includePatternIsLiteralDirectory(pattern, productDir, deps) || typeScriptScopePatternTargetsTypeScriptSource(pattern);
20012
20173
  }
20013
- function includePatternIsLiteralDirectory(pattern, projectRoot, deps) {
20174
+ function includePatternIsLiteralDirectory(pattern, productDir, deps) {
20014
20175
  const normalizedPattern = normalizeTypeScriptScopePath(pattern);
20015
- return !typeScriptScopePatternHasGlob(normalizedPattern) && pathIsDirectory(resolveProjectPath(projectRoot, normalizedPattern), deps);
20176
+ return !typeScriptScopePatternHasGlob(normalizedPattern) && pathIsDirectory(resolveProductPath(productDir, normalizedPattern), deps);
20016
20177
  }
20017
20178
  function pathIsDirectory(path4, deps) {
20018
20179
  try {
@@ -20022,28 +20183,28 @@ function pathIsDirectory(path4, deps) {
20022
20183
  return false;
20023
20184
  }
20024
20185
  }
20025
- function normalizeActiveIncludePattern(pattern, projectRoot, deps) {
20186
+ function normalizeActiveIncludePattern(pattern, productDir, deps) {
20026
20187
  const normalizedPattern = normalizeTypeScriptScopePath(pattern);
20027
- return includePatternIsLiteralDirectory(normalizedPattern, projectRoot, deps) ? `${normalizedPattern}${TYPESCRIPT_SCOPE_DIRECTORY_PATTERN_SUFFIX}` : pattern;
20188
+ return includePatternIsLiteralDirectory(normalizedPattern, productDir, deps) ? `${normalizedPattern}${TYPESCRIPT_SCOPE_DIRECTORY_PATTERN_SUFFIX}` : pattern;
20028
20189
  }
20029
- function filterActiveIncludePatterns(patterns, excludePatterns, projectRoot, deps) {
20030
- return patterns.map((pattern) => normalizeActiveIncludePattern(pattern, projectRoot, deps)).filter((pattern) => typeScriptScopePatternTargetsTypeScriptSource(pattern)).filter((pattern) => {
20190
+ function filterActiveIncludePatterns(patterns, excludePatterns, productDir, deps) {
20191
+ return patterns.map((pattern) => normalizeActiveIncludePattern(pattern, productDir, deps)).filter((pattern) => typeScriptScopePatternTargetsTypeScriptSource(pattern)).filter((pattern) => {
20031
20192
  const topLevelDir = getLiteralTopLevelPatternDirectory(pattern);
20032
- return topLevelDir === null || deps.existsSync(join32(projectRoot, topLevelDir));
20193
+ return topLevelDir === null || deps.existsSync(join32(productDir, topLevelDir));
20033
20194
  }).filter((pattern) => pathPassesValidationFilter(pattern, { exclude: excludePatterns }));
20034
20195
  }
20035
- function getValidationDirectories(scope2, projectRoot, deps = defaultScopeDeps) {
20036
- const config = resolveTypeScriptConfig(scope2, projectRoot, deps);
20037
- const configDirectories = getTopLevelDirectoriesWithTypeScript(config, projectRoot, deps);
20038
- const existingDirectories = configDirectories.filter((dir) => deps.existsSync(join32(projectRoot, dir)));
20196
+ function getValidationDirectories(scope2, productDir, deps = defaultScopeDeps) {
20197
+ const config = resolveTypeScriptConfig(scope2, productDir, deps);
20198
+ const configDirectories = getTopLevelDirectoriesWithTypeScript(config, productDir, deps);
20199
+ const existingDirectories = configDirectories.filter((dir) => deps.existsSync(join32(productDir, dir)));
20039
20200
  return existingDirectories;
20040
20201
  }
20041
- function getTypeScriptScope(scope2, projectRoot, deps = defaultScopeDeps) {
20042
- const directories = getValidationDirectories(scope2, projectRoot, deps);
20043
- const config = resolveTypeScriptConfig(scope2, projectRoot, deps);
20202
+ function getTypeScriptScope(scope2, productDir, deps = defaultScopeDeps) {
20203
+ const directories = getValidationDirectories(scope2, productDir, deps);
20204
+ const config = resolveTypeScriptConfig(scope2, productDir, deps);
20044
20205
  return {
20045
20206
  directories,
20046
- filePatterns: filterActiveIncludePatterns(config.include ?? [], config.exclude ?? [], projectRoot, deps),
20207
+ filePatterns: filterActiveIncludePatterns(config.include ?? [], config.exclude ?? [], productDir, deps),
20047
20208
  excludePatterns: config.exclude ?? []
20048
20209
  };
20049
20210
  }
@@ -20055,31 +20216,31 @@ function pathPassesTypeScriptScope(path4, scopeConfig) {
20055
20216
  const excluded = scopeConfig.excludePatterns.some((pattern) => pathMatchesTypeScriptPattern(path4, pattern));
20056
20217
  return included && !excluded;
20057
20218
  }
20058
- function pathStaysInsideTypeScriptScopeRoot(projectRoot, path4) {
20059
- const resolvedPath = isAbsolute8(path4) ? resolve14(path4) : resolve14(projectRoot, path4);
20060
- const relativePath = relative7(projectRoot, resolvedPath);
20061
- const segments = normalizeTypeScriptScopePath(relativePath).split(PATH_SEGMENT_SEPARATOR4);
20219
+ function pathStaysInsideTypeScriptScopeRoot(productDir, path4) {
20220
+ const resolvedPath = isAbsolute8(path4) ? resolve14(path4) : resolve14(productDir, path4);
20221
+ const relativePath = relative7(productDir, resolvedPath);
20222
+ const segments = normalizeTypeScriptScopePath(relativePath).split(PATH_SEGMENT_SEPARATOR3);
20062
20223
  return relativePath.length === 0 || !segments.includes("..") && !isAbsolute8(relativePath);
20063
20224
  }
20064
- function toProjectRelativeTypeScriptScopePath(projectRoot, path4) {
20065
- const resolvedPath = isAbsolute8(path4) ? resolve14(path4) : resolve14(projectRoot, path4);
20066
- const relativePath = relative7(projectRoot, resolvedPath);
20225
+ function toProductRelativeTypeScriptScopePath(productDir, path4) {
20226
+ const resolvedPath = isAbsolute8(path4) ? resolve14(path4) : resolve14(productDir, path4);
20227
+ const relativePath = relative7(productDir, resolvedPath);
20067
20228
  return relativePath.length === 0 ? TYPESCRIPT_SCOPE_PROJECT_ROOT : normalizeTypeScriptScopePath(relativePath);
20068
20229
  }
20069
- function toExplicitTypeScriptScopeTarget(projectRoot, originalPath, deps = defaultScopeDeps) {
20070
- const path4 = toProjectRelativeTypeScriptScopePath(projectRoot, originalPath);
20230
+ function toExplicitTypeScriptScopeTarget(productDir, originalPath, deps = defaultScopeDeps) {
20231
+ const path4 = toProductRelativeTypeScriptScopePath(productDir, originalPath);
20071
20232
  return {
20072
- kind: pathIsDirectory(resolveProjectPath(projectRoot, path4), deps) ? EXPLICIT_TYPESCRIPT_SCOPE_TARGET_KIND.DIRECTORY : EXPLICIT_TYPESCRIPT_SCOPE_TARGET_KIND.FILE,
20233
+ kind: pathIsDirectory(resolveProductPath(productDir, path4), deps) ? EXPLICIT_TYPESCRIPT_SCOPE_TARGET_KIND.DIRECTORY : EXPLICIT_TYPESCRIPT_SCOPE_TARGET_KIND.FILE,
20073
20234
  path: path4
20074
20235
  };
20075
20236
  }
20076
- function explicitTypeScriptScopeTargetExists(projectRoot, target, deps) {
20077
- return deps.existsSync(resolveProjectPath(projectRoot, target.path));
20237
+ function explicitTypeScriptScopeTargetExists(productDir, target, deps) {
20238
+ return deps.existsSync(resolveProductPath(productDir, target.path));
20078
20239
  }
20079
20240
  function filterExplicitTypeScriptScopeTargets(filter, deps = defaultScopeDeps) {
20080
20241
  const {
20081
20242
  paths,
20082
- projectRoot,
20243
+ productDir,
20083
20244
  requireExistingPaths = true,
20084
20245
  scopeConfig,
20085
20246
  validationPathFilter,
@@ -20088,7 +20249,7 @@ function filterExplicitTypeScriptScopeTargets(filter, deps = defaultScopeDeps) {
20088
20249
  if (paths === void 0) {
20089
20250
  return void 0;
20090
20251
  }
20091
- return paths.filter((path4) => pathStaysInsideTypeScriptScopeRoot(projectRoot, path4)).map((path4) => toExplicitTypeScriptScopeTarget(projectRoot, path4, deps)).filter((target) => !requireExistingPaths || explicitTypeScriptScopeTargetExists(projectRoot, target, deps)).filter((target) => explicitTypeScriptScopeTargetPassesSourceKind(target)).filter(
20252
+ return paths.filter((path4) => pathStaysInsideTypeScriptScopeRoot(productDir, path4)).map((path4) => toExplicitTypeScriptScopeTarget(productDir, path4, deps)).filter((target) => !requireExistingPaths || explicitTypeScriptScopeTargetExists(productDir, target, deps)).filter((target) => explicitTypeScriptScopeTargetPassesSourceKind(target)).filter(
20092
20253
  (target) => bypassValidationPathFilter || explicitTypeScriptScopeTargetIntersectsValidationPathFilter(target, validationPathFilter)
20093
20254
  ).filter((target) => explicitTypeScriptScopeTargetPassesScope(target, scopeConfig));
20094
20255
  }
@@ -20157,7 +20318,7 @@ function constrainTypeScriptScopeToExplicitTargets(scopeConfig, targets) {
20157
20318
  const retainedDirectoryOperandPatterns = scopeConfig.filePatterns.length === 0 ? retainedDirectories.map(typeScriptLiteralDirectoryPattern) : [];
20158
20319
  const explicitFileTargets = targets.filter((target) => target.kind === EXPLICIT_TYPESCRIPT_SCOPE_TARGET_KIND.FILE).map((target) => target.path).filter(
20159
20320
  (path4) => !retainedDirectories.some(
20160
- (directory) => path4 === directory || path4.startsWith(`${directory}${PATH_SEGMENT_SEPARATOR4}`)
20321
+ (directory) => path4 === directory || path4.startsWith(`${directory}${PATH_SEGMENT_SEPARATOR3}`)
20161
20322
  )
20162
20323
  );
20163
20324
  const uncoveredExplicitFileTargets = explicitFileTargets.filter(
@@ -20182,28 +20343,28 @@ function typeScriptLiteralDirectoryPattern(pattern) {
20182
20343
  return pattern === TYPESCRIPT_SCOPE_PROJECT_ROOT ? TYPESCRIPT_SCOPE_DIRECTORY_PATTERN_SUFFIX.slice(1) : `${pattern}${TYPESCRIPT_SCOPE_DIRECTORY_PATTERN_SUFFIX}`;
20183
20344
  }
20184
20345
  function resolveTypeScriptValidationScope(filter, deps = defaultScopeDeps) {
20185
- const baseScopeConfig = getTypeScriptScope(filter.scope, filter.projectRoot, deps);
20346
+ const baseScopeConfig = getTypeScriptScope(filter.scope, filter.productDir, deps);
20186
20347
  const scopeConfig = applyValidationPathFilterToScope(baseScopeConfig, filter.validationPathFilter);
20187
- const explicitTargetScopeConfig = filter.bypassExplicitPathValidationFilter === true ? baseScopeConfig : scopeConfig;
20188
20348
  const explicitTargets = filterExplicitTypeScriptScopeTargets({
20189
20349
  paths: filter.paths,
20190
- projectRoot: filter.projectRoot,
20350
+ productDir: filter.productDir,
20191
20351
  validationPathFilter: filter.validationPathFilter,
20192
- scopeConfig: explicitTargetScopeConfig,
20193
- bypassValidationPathFilter: filter.bypassExplicitPathValidationFilter
20352
+ scopeConfig: baseScopeConfig,
20353
+ bypassValidationPathFilter: true
20194
20354
  }, deps);
20195
20355
  if (filter.paths !== void 0 && filter.paths.length > 0 && explicitTargets?.length === 0) {
20196
20356
  return {
20197
20357
  ...scopeConfig,
20198
20358
  directories: [],
20199
20359
  filePatterns: [],
20200
- filteredByValidationPaths: true,
20201
- filteredByValidationPathIncludes: true,
20202
- filteredByValidationPathNoMatches: true
20360
+ explicitPathNoMatches: true,
20361
+ filteredByValidationPaths: void 0,
20362
+ filteredByValidationPathIncludes: void 0,
20363
+ filteredByValidationPathNoMatches: void 0
20203
20364
  };
20204
20365
  }
20205
20366
  if (explicitTargets !== void 0 && explicitTargets.length > 0) {
20206
- const explicitScopeConfig = constrainTypeScriptScopeToExplicitTargets(explicitTargetScopeConfig, explicitTargets);
20367
+ const explicitScopeConfig = constrainTypeScriptScopeToExplicitTargets(baseScopeConfig, explicitTargets);
20207
20368
  return filter.markExplicitPathsAsValidationFilter === true ? {
20208
20369
  ...explicitScopeConfig,
20209
20370
  filteredByValidationPaths: true,
@@ -20216,7 +20377,7 @@ function resolveTypeScriptValidationScope(filter, deps = defaultScopeDeps) {
20216
20377
  function constrainTypeScriptPatternToDirectory(pattern, directory) {
20217
20378
  const normalizedPattern = normalizeTypeScriptScopePath(pattern);
20218
20379
  const normalizedDirectory = normalizeTypeScriptScopePath(directory);
20219
- if (normalizedPattern === normalizedDirectory || normalizedPattern.startsWith(`${normalizedDirectory}${PATH_SEGMENT_SEPARATOR4}`)) {
20380
+ if (normalizedPattern === normalizedDirectory || normalizedPattern.startsWith(`${normalizedDirectory}${PATH_SEGMENT_SEPARATOR3}`)) {
20220
20381
  return normalizedPattern;
20221
20382
  }
20222
20383
  const patternSegments = splitTypeScriptScopePathSegments(normalizedPattern);
@@ -20229,7 +20390,7 @@ function constrainTypeScriptPatternToDirectory(pattern, directory) {
20229
20390
  const suffixSegments = patternSegments.slice(patternIndex);
20230
20391
  const constrainedSuffixSegments = directoryAdvance.recursiveGlobConsumedDirectory && suffixSegments.length > 0 ? [RECURSIVE_GLOB_SEGMENT, ...suffixSegments] : suffixSegments;
20231
20392
  if (constrainedSuffixSegments.length > 0) {
20232
- return [normalizedDirectory, ...constrainedSuffixSegments].join(PATH_SEGMENT_SEPARATOR4);
20393
+ return [normalizedDirectory, ...constrainedSuffixSegments].join(PATH_SEGMENT_SEPARATOR3);
20233
20394
  }
20234
20395
  return typeScriptScopePatternHasGlob(normalizedPattern) && typeScriptScopePatternCoversDirectorySourceSet(normalizedPattern, normalizedDirectory) ? `${normalizedDirectory}${TYPESCRIPT_SCOPE_DIRECTORY_PATTERN_SUFFIX}` : normalizedDirectory;
20235
20396
  }
@@ -20263,7 +20424,7 @@ function patternSegmentMatchesDirectorySegment(patternSegment, directorySegment)
20263
20424
 
20264
20425
  // src/validation/discovery/tool-finder.ts
20265
20426
  import fs3 from "fs";
20266
- import { createRequire } from "module";
20427
+ import { createRequire as createRequire2 } from "module";
20267
20428
  import path3 from "path";
20268
20429
  import { fileURLToPath } from "url";
20269
20430
 
@@ -20298,7 +20459,11 @@ var TOOL_DISCOVERY = {
20298
20459
  };
20299
20460
 
20300
20461
  // src/validation/discovery/tool-finder.ts
20301
- var require2 = createRequire(import.meta.url);
20462
+ var TOOL_DISCOVERY_PRIORITY = {
20463
+ BUNDLED_FIRST: "bundled-first",
20464
+ PRODUCT_FIRST: "product-first"
20465
+ };
20466
+ var require2 = createRequire2(import.meta.url);
20302
20467
  var FILE_URL_PROTOCOL = new URL(import.meta.url).protocol;
20303
20468
  var PACKAGE_MANIFEST_FILENAME = "package.json";
20304
20469
  var defaultToolDiscoveryDeps = {
@@ -20345,30 +20510,44 @@ function bundledToolPath(resolvedPath, existsSync10) {
20345
20510
  return nearestPackageRoot(bundledFilePath, existsSync10) ?? path3.dirname(bundledFilePath);
20346
20511
  }
20347
20512
  async function discoverTool(tool, options = {}) {
20348
- const { projectRoot = CONFIG_PROCESS_CWD.read(), deps = defaultToolDiscoveryDeps } = options;
20349
- const bundledPath = deps.resolveModule(`${tool}/package.json`) ?? deps.resolveImport?.(tool);
20513
+ const {
20514
+ productDir = CONFIG_PROCESS_CWD.read(),
20515
+ executableName = tool,
20516
+ bundledExecutable,
20517
+ includeBundled = true,
20518
+ priority = TOOL_DISCOVERY_PRIORITY.BUNDLED_FIRST,
20519
+ deps = defaultToolDiscoveryDeps
20520
+ } = options;
20521
+ const productBinPath = path3.join(productDir, "node_modules", ".bin", executableName);
20522
+ const productLocation = () => deps.existsSync(productBinPath) ? {
20523
+ found: true,
20524
+ location: {
20525
+ tool,
20526
+ path: productBinPath,
20527
+ source: TOOL_DISCOVERY.SOURCES.PROJECT
20528
+ }
20529
+ } : null;
20530
+ if (priority === TOOL_DISCOVERY_PRIORITY.PRODUCT_FIRST) {
20531
+ const productResult = productLocation();
20532
+ if (productResult !== null) return productResult;
20533
+ }
20534
+ const bundledSpecifier = bundledExecutable ?? `${tool}/package.json`;
20535
+ const bundledPath = includeBundled ? deps.resolveModule(bundledSpecifier) ?? deps.resolveImport?.(bundledExecutable ?? tool) : null;
20350
20536
  if (bundledPath) {
20351
20537
  return {
20352
20538
  found: true,
20353
20539
  location: {
20354
20540
  tool,
20355
- path: bundledToolPath(bundledPath, deps.existsSync),
20541
+ path: bundledExecutable === void 0 ? bundledToolPath(bundledPath, deps.existsSync) : resolvedModulePath(bundledPath),
20356
20542
  source: TOOL_DISCOVERY.SOURCES.BUNDLED
20357
20543
  }
20358
20544
  };
20359
20545
  }
20360
- const projectBinPath = path3.join(projectRoot, "node_modules", ".bin", tool);
20361
- if (deps.existsSync(projectBinPath)) {
20362
- return {
20363
- found: true,
20364
- location: {
20365
- tool,
20366
- path: projectBinPath,
20367
- source: TOOL_DISCOVERY.SOURCES.PROJECT
20368
- }
20369
- };
20546
+ if (priority === TOOL_DISCOVERY_PRIORITY.BUNDLED_FIRST) {
20547
+ const productResult = productLocation();
20548
+ if (productResult !== null) return productResult;
20370
20549
  }
20371
- const globalPath = deps.whichSync(tool);
20550
+ const globalPath = deps.whichSync(executableName);
20372
20551
  if (globalPath) {
20373
20552
  return {
20374
20553
  found: true,
@@ -20519,13 +20698,13 @@ function dependencyCruiserGlobSegmentToRegExpSource(segment) {
20519
20698
  }
20520
20699
  return source;
20521
20700
  }
20522
- function buildDependencyCruiserOptions(typescriptScope, projectRoot, tsConfigFile) {
20701
+ function buildDependencyCruiserOptions(typescriptScope, productDir, tsConfigFile) {
20523
20702
  const excludePatterns = [
20524
20703
  DEPENDENCY_CRUISER_PACKAGE_EXCLUDE_PATTERN,
20525
20704
  ...toDependencyCruiserExcludePatterns(typescriptScope.excludePatterns)
20526
20705
  ];
20527
20706
  return {
20528
- baseDir: projectRoot,
20707
+ baseDir: productDir,
20529
20708
  enhancedResolveOptions: { extensions: [...DEPENDENCY_CRUISER_TYPESCRIPT_RESOLVE_EXTENSIONS] },
20530
20709
  exclude: { path: excludePatterns },
20531
20710
  includeOnly: { path: DEPENDENCY_CRUISER_TYPESCRIPT_SOURCE_PATTERN },
@@ -20633,16 +20812,16 @@ function circularDependencyCycles(result) {
20633
20812
  );
20634
20813
  return uniqueCycles(cycles);
20635
20814
  }
20636
- async function validateCircularDependencies(scope2, typescriptScope, projectRoot, deps = defaultCircularDeps) {
20815
+ async function validateCircularDependencies(scope2, typescriptScope, productDir, deps = defaultCircularDeps) {
20637
20816
  try {
20638
20817
  const analyzeSourcePatterns = toDependencyCruiserSourcePatterns(typescriptScope);
20639
20818
  if (analyzeSourcePatterns.length === 0) {
20640
20819
  return { success: true };
20641
20820
  }
20642
- const tsConfigFile = join33(projectRoot, TSCONFIG_FILES[scope2]);
20821
+ const tsConfigFile = join33(productDir, TSCONFIG_FILES[scope2]);
20643
20822
  const result = await deps.dependencyCruiser(
20644
20823
  analyzeSourcePatterns,
20645
- buildDependencyCruiserOptions(typescriptScope, projectRoot, tsConfigFile),
20824
+ buildDependencyCruiserOptions(typescriptScope, productDir, tsConfigFile),
20646
20825
  void 0,
20647
20826
  { tsConfig: deps.extractTypeScriptConfig(tsConfigFile) }
20648
20827
  );
@@ -20681,9 +20860,9 @@ var EXECUTION_MODES = {
20681
20860
 
20682
20861
  // src/commands/validation/circular.ts
20683
20862
  var TYPESCRIPT_ABSENT_MESSAGE = formatTypeScriptAbsentSkipMessage(VALIDATION_STAGE_DISPLAY_NAMES.CIRCULAR);
20684
- var CIRCULAR_CONFIG_ERROR_MESSAGE = `${VALIDATION_STAGE_DISPLAY_NAMES.CIRCULAR}: \u2717 config error`;
20685
- var CIRCULAR_VALIDATION_PATHS_NO_TARGETS_MESSAGE = formatValidationPathsNoTargetsSkipMessage(
20686
- VALIDATION_STAGE_DISPLAY_NAMES.CIRCULAR
20863
+ var CIRCULAR_CONFIG_ERROR_MESSAGE = formatValidationConfigProblemMessage(
20864
+ VALIDATION_STAGE_DISPLAY_NAMES.CIRCULAR,
20865
+ "configuration error"
20687
20866
  );
20688
20867
  var CIRCULAR_DEPENDENCY_OUTPUT = {
20689
20868
  FOUND: VALIDATION_COMMAND_OUTPUT.CIRCULAR_FOUND
@@ -20691,7 +20870,6 @@ var CIRCULAR_DEPENDENCY_OUTPUT = {
20691
20870
  var defaultCircularCommandDeps = {
20692
20871
  validateCircularDependencies
20693
20872
  };
20694
- var DEPENDENCY_CRUISER_PACKAGE_NAME = "dependency-cruiser";
20695
20873
  function formatCircularValidationResult(result, quiet) {
20696
20874
  if (result.success) {
20697
20875
  return {
@@ -20723,11 +20901,6 @@ async function circularCommand(options, deps = defaultCircularCommandDeps) {
20723
20901
  durationMs: Date.now() - startTime
20724
20902
  };
20725
20903
  }
20726
- const toolResult = await discoverTool(DEPENDENCY_CRUISER_PACKAGE_NAME, { projectRoot: cwd });
20727
- if (!toolResult.found) {
20728
- const skipMessage = formatSkipMessage(VALIDATION_STAGE_DISPLAY_NAMES.CIRCULAR, toolResult);
20729
- return { exitCode: 0, output: skipMessage, durationMs: Date.now() - startTime };
20730
- }
20731
20904
  const loaded = await resolveConfig(cwd, [validationConfigDescriptor]);
20732
20905
  if (!loaded.ok) {
20733
20906
  return {
@@ -20738,19 +20911,22 @@ async function circularCommand(options, deps = defaultCircularCommandDeps) {
20738
20911
  }
20739
20912
  const validationConfig = loaded.value[validationConfigDescriptor.section];
20740
20913
  const effectiveScopeConfig = resolveTypeScriptValidationScope({
20741
- projectRoot: cwd,
20914
+ productDir: cwd,
20742
20915
  scope: scope2,
20743
20916
  paths: files,
20744
20917
  validationPathFilter: validationPathFilterForTool(
20745
20918
  validationConfig.paths,
20746
20919
  VALIDATION_PATH_TOOL_SUBSECTIONS.CIRCULAR
20747
- ),
20748
- bypassExplicitPathValidationFilter: true
20920
+ )
20749
20921
  });
20750
- if (effectiveScopeConfig.filteredByValidationPathNoMatches) {
20922
+ const noTargetsMessage = formatValidationScopeNoTargetsSkipMessage(
20923
+ VALIDATION_STAGE_DISPLAY_NAMES.CIRCULAR,
20924
+ effectiveScopeConfig
20925
+ );
20926
+ if (noTargetsMessage !== void 0) {
20751
20927
  return {
20752
20928
  exitCode: 0,
20753
- output: quiet ? "" : CIRCULAR_VALIDATION_PATHS_NO_TARGETS_MESSAGE,
20929
+ output: quiet ? "" : noTargetsMessage,
20754
20930
  durationMs: Date.now() - startTime
20755
20931
  };
20756
20932
  }
@@ -20770,6 +20946,7 @@ var KNIP_COMMAND_TOKENS = {
20770
20946
  TSCONFIG_FLAG: "--tsConfig",
20771
20947
  USE_TSCONFIG_FILES_FLAG: "--use-tsconfig-files"
20772
20948
  };
20949
+ var KNIP_LOCAL_BIN_SEGMENTS = ["node_modules", ".bin", KNIP_COMMAND_TOKENS.COMMAND];
20773
20950
  var defaultKnipDeps = {
20774
20951
  existsSync: existsSync4,
20775
20952
  mkdir: mkdir7,
@@ -20777,9 +20954,9 @@ var defaultKnipDeps = {
20777
20954
  rm: rm4,
20778
20955
  writeFile: writeFile5
20779
20956
  };
20780
- async function validateKnip2(context, runner = defaultKnipProcessRunner, deps = defaultKnipDeps) {
20957
+ async function validateKnip2(context, runner = defaultKnipProcessRunner, deps = defaultKnipDeps, outputStreams = defaultValidationSubprocessOutputStreams) {
20781
20958
  try {
20782
- const { projectRoot, typescriptScope } = context;
20959
+ const { productDir, typescriptScope, toolPath } = context;
20783
20960
  const analyzeTargets = [
20784
20961
  ...typescriptScope.directories,
20785
20962
  ...typescriptScope.filePatterns
@@ -20787,16 +20964,16 @@ async function validateKnip2(context, runner = defaultKnipProcessRunner, deps =
20787
20964
  if (analyzeTargets.length === 0) {
20788
20965
  return { success: true };
20789
20966
  }
20790
- return await runKnipSubprocess(projectRoot, typescriptScope, runner, deps);
20967
+ return await runKnipSubprocess(productDir, typescriptScope, runner, deps, outputStreams, toolPath);
20791
20968
  } catch (error) {
20792
20969
  const errorMessage2 = error instanceof Error ? error.message : String(error);
20793
20970
  return { success: false, error: errorMessage2 };
20794
20971
  }
20795
20972
  }
20796
- async function runKnipSubprocess(projectRoot, typescriptScope, runner, deps) {
20797
- const scopedTsconfig = typescriptScope.filteredByValidationPaths ? await createScopedKnipTsconfig(projectRoot, typescriptScope, deps) : void 0;
20798
- const localBin = join34(projectRoot, "node_modules", ".bin", "knip");
20799
- const binary = deps.existsSync(localBin) ? localBin : KNIP_COMMAND_TOKENS.NPX_COMMAND;
20973
+ async function runKnipSubprocess(productDir, typescriptScope, runner, deps, outputStreams, toolPath) {
20974
+ const scopedTsconfig = typescriptScope.filteredByValidationPaths ? await createScopedKnipTsconfig(productDir, typescriptScope, deps) : void 0;
20975
+ const localBin = join34(productDir, ...KNIP_LOCAL_BIN_SEGMENTS);
20976
+ const binary = toolPath ?? (deps.existsSync(localBin) ? localBin : KNIP_COMMAND_TOKENS.NPX_COMMAND);
20800
20977
  const baseArgs = scopedTsconfig === void 0 ? [] : [
20801
20978
  KNIP_COMMAND_TOKENS.USE_TSCONFIG_FILES_FLAG,
20802
20979
  KNIP_COMMAND_TOKENS.TSCONFIG_FLAG,
@@ -20804,8 +20981,9 @@ async function runKnipSubprocess(projectRoot, typescriptScope, runner, deps) {
20804
20981
  ];
20805
20982
  const args = binary === KNIP_COMMAND_TOKENS.NPX_COMMAND ? [KNIP_COMMAND_TOKENS.COMMAND, ...baseArgs] : baseArgs;
20806
20983
  const knipProcess = spawnManagedSubprocess(runner, binary, args, {
20807
- cwd: projectRoot
20984
+ cwd: productDir
20808
20985
  });
20986
+ forwardValidationSubprocessOutput(knipProcess, outputStreams);
20809
20987
  const cleanup = scopedTsconfig?.cleanup ?? (async () => {
20810
20988
  });
20811
20989
  let cleanupStarted = false;
@@ -20835,7 +21013,7 @@ async function runKnipSubprocess(projectRoot, typescriptScope, runner, deps) {
20835
21013
  };
20836
21014
  knipProcess.on("close", (code) => {
20837
21015
  if (code === 0) {
20838
- resolveAfterCleanup({ success: true });
21016
+ resolveAfterCleanup({ success: true, output: knipOutput });
20839
21017
  } else {
20840
21018
  const errorOutput = knipOutput || knipError || "Unused code detected";
20841
21019
  resolveAfterCleanup({
@@ -20849,12 +21027,12 @@ async function runKnipSubprocess(projectRoot, typescriptScope, runner, deps) {
20849
21027
  });
20850
21028
  });
20851
21029
  }
20852
- async function createScopedKnipTsconfig(projectRoot, typescriptScope, deps) {
20853
- const tempParentDir = join34(projectRoot, ...TEMPORARY_TSCONFIG_PARENT_SEGMENTS);
21030
+ async function createScopedKnipTsconfig(productDir, typescriptScope, deps) {
21031
+ const tempParentDir = join34(productDir, ...TEMPORARY_TSCONFIG_PARENT_SEGMENTS);
20854
21032
  await deps.mkdir(tempParentDir, { recursive: true });
20855
21033
  const tempDir = await deps.mkdtemp(join34(tempParentDir, "validate-knip-"));
20856
21034
  const configPath = join34(tempDir, TSCONFIG_FILES.full);
20857
- const toProjectPathPattern = (pattern) => isAbsolute9(pattern) ? pattern : join34(projectRoot, pattern);
21035
+ const toProjectPathPattern = (pattern) => isAbsolute9(pattern) ? pattern : join34(productDir, pattern);
20858
21036
  const project = [
20859
21037
  ...typescriptScope.directories.flatMap(
20860
21038
  (directory) => TYPESCRIPT_FALLBACK_INCLUDE_PATTERNS.map((pattern) => `${directory}/${pattern}`)
@@ -20862,7 +21040,7 @@ async function createScopedKnipTsconfig(projectRoot, typescriptScope, deps) {
20862
21040
  ...typescriptScope.filePatterns
20863
21041
  ];
20864
21042
  const config = {
20865
- extends: join34(projectRoot, TSCONFIG_FILES.full),
21043
+ extends: join34(productDir, TSCONFIG_FILES.full),
20866
21044
  include: project.map(toProjectPathPattern),
20867
21045
  exclude: typescriptScope.excludePatterns.map(toProjectPathPattern)
20868
21046
  };
@@ -20879,17 +21057,25 @@ var defaultKnipCommandDeps = {
20879
21057
  discoverTool,
20880
21058
  validateKnip: validateKnip2
20881
21059
  };
20882
- var TYPESCRIPT_ABSENT_MESSAGE2 = formatTypeScriptAbsentSkipMessage(VALIDATION_STAGE_DISPLAY_NAMES.KNIP);
20883
- var KNIP_VALIDATION_PATHS_NO_TARGETS_MESSAGE = formatValidationPathsNoTargetsSkipMessage(
21060
+ var KNIP_VALIDATION_STEP_NAME = "unused code detection";
21061
+ var KNIP_TYPESCRIPT_ABSENT_MESSAGE = formatTypeScriptAbsentSkipMessage(
20884
21062
  VALIDATION_STAGE_DISPLAY_NAMES.KNIP
20885
21063
  );
20886
21064
  async function knipCommand(options, deps = defaultKnipCommandDeps) {
20887
- const { cwd, files, quiet, scope: scope2 = VALIDATION_SCOPES.FULL } = options;
21065
+ const {
21066
+ cwd,
21067
+ files,
21068
+ json,
21069
+ outputStreams,
21070
+ quiet,
21071
+ scope: scope2 = VALIDATION_SCOPES.FULL,
21072
+ streamedPipelineOutput
21073
+ } = options;
20888
21074
  const startTime = Date.now();
20889
21075
  if (!deps.detectTypeScript(cwd).present) {
20890
21076
  return {
20891
21077
  exitCode: 0,
20892
- output: quiet ? "" : TYPESCRIPT_ABSENT_MESSAGE2,
21078
+ output: quiet ? "" : KNIP_TYPESCRIPT_ABSENT_MESSAGE,
20893
21079
  durationMs: Date.now() - startTime
20894
21080
  };
20895
21081
  }
@@ -20906,34 +21092,51 @@ async function knipCommand(options, deps = defaultKnipCommandDeps) {
20906
21092
  const output = quiet ? "" : VALIDATION_COMMAND_OUTPUT.KNIP_DISABLED;
20907
21093
  return { exitCode: 0, output, durationMs: Date.now() - startTime };
20908
21094
  }
20909
- const toolResult = await deps.discoverTool("knip", { projectRoot: cwd });
21095
+ const toolResult = await deps.discoverTool(KNIP_COMMAND_TOKENS.COMMAND, {
21096
+ productDir: cwd,
21097
+ includeBundled: false
21098
+ });
20910
21099
  if (!toolResult.found) {
20911
- const skipMessage = formatSkipMessage("unused code detection", toolResult);
21100
+ const skipMessage = formatSkipMessage(KNIP_VALIDATION_STEP_NAME, toolResult);
20912
21101
  return { exitCode: 0, output: skipMessage, durationMs: Date.now() - startTime };
20913
21102
  }
20914
21103
  const scopeConfig = resolveTypeScriptValidationScope({
20915
- projectRoot: cwd,
21104
+ productDir: cwd,
20916
21105
  scope: scope2,
20917
21106
  paths: files,
20918
21107
  validationPathFilter: validationPathFilterForTool(validationConfig.paths, VALIDATION_PATH_TOOL_SUBSECTIONS.KNIP),
20919
- markExplicitPathsAsValidationFilter: true,
20920
- bypassExplicitPathValidationFilter: true
21108
+ markExplicitPathsAsValidationFilter: true
20921
21109
  });
20922
- if (scopeConfig.filteredByValidationPathNoMatches) {
21110
+ const noTargetsMessage = formatValidationScopeNoTargetsSkipMessage(
21111
+ VALIDATION_STAGE_DISPLAY_NAMES.KNIP,
21112
+ scopeConfig
21113
+ );
21114
+ if (noTargetsMessage !== void 0) {
20923
21115
  return {
20924
21116
  exitCode: 0,
20925
- output: quiet ? "" : KNIP_VALIDATION_PATHS_NO_TARGETS_MESSAGE,
21117
+ output: quiet ? "" : noTargetsMessage,
20926
21118
  durationMs: Date.now() - startTime
20927
21119
  };
20928
21120
  }
20929
- const result = await deps.validateKnip({ projectRoot: cwd, typescriptScope: scopeConfig });
21121
+ const result = await deps.validateKnip(
21122
+ {
21123
+ productDir: cwd,
21124
+ typescriptScope: scopeConfig,
21125
+ toolPath: toolResult.location.path
21126
+ },
21127
+ void 0,
21128
+ void 0,
21129
+ outputStreams ?? discardValidationSubprocessOutputStreams
21130
+ );
20930
21131
  const durationMs = Date.now() - startTime;
20931
21132
  if (result.success) {
20932
- const output = quiet ? "" : VALIDATION_COMMAND_OUTPUT.KNIP_SUCCESS;
20933
- return { exitCode: 0, output, durationMs };
21133
+ const output = quiet ? "" : [VALIDATION_COMMAND_OUTPUT.KNIP_SUCCESS, result.output].filter((line) => line !== void 0 && line.length > 0).join("\n");
21134
+ const terminalOutput = streamedValidationTerminalOutput(result.output, json, streamedPipelineOutput);
21135
+ return { exitCode: 0, output, terminalOutput, durationMs };
20934
21136
  } else {
20935
21137
  const output = result.error ?? VALIDATION_COMMAND_OUTPUT.KNIP_FAILURE;
20936
- return { exitCode: 1, output, durationMs };
21138
+ const terminalOutput = streamedValidationTerminalOutput(result.error, json, streamedPipelineOutput);
21139
+ return { exitCode: 1, output, terminalOutput, durationMs };
20937
21140
  }
20938
21141
  }
20939
21142
 
@@ -21193,8 +21396,7 @@ function validateLintPolicy(productDir) {
21193
21396
  }
21194
21397
  }
21195
21398
 
21196
- // src/validation/steps/eslint.ts
21197
- var defaultEslintProcessRunner = lifecycleProcessRunner;
21399
+ // src/validation/steps/eslint-contract.ts
21198
21400
  var DEFAULT_ESLINT_CONFIG_FILE = "eslint.config.ts";
21199
21401
  var ESLINT_COMMAND_TOKENS = {
21200
21402
  COMMAND: "eslint",
@@ -21205,6 +21407,9 @@ var ESLINT_COMMAND_TOKENS = {
21205
21407
  IGNORE_PATTERN_FLAG: "--ignore-pattern"
21206
21408
  };
21207
21409
  var ESLINT_LOCAL_BIN_SEGMENTS = ["node_modules", ".bin", ESLINT_COMMAND_TOKENS.COMMAND];
21410
+
21411
+ // src/validation/steps/eslint.ts
21412
+ var defaultEslintProcessRunner = lifecycleProcessRunner;
21208
21413
  function buildEslintArgs(context) {
21209
21414
  const {
21210
21415
  validatedFiles,
@@ -21252,8 +21457,8 @@ function buildIgnorePatternArgs(patterns) {
21252
21457
  return patterns.flatMap((pattern) => [ESLINT_COMMAND_TOKENS.IGNORE_PATTERN_FLAG, pattern]);
21253
21458
  }
21254
21459
  async function validateESLint(context, runner = defaultEslintProcessRunner, outputStreams = defaultValidationSubprocessOutputStreams) {
21255
- const { projectRoot, scope: scope2, validatedFiles, mode, eslintConfigFile } = context;
21256
- const lintPolicy = validateLintPolicy(projectRoot);
21460
+ const { productDir, scope: scope2, validatedFiles, mode, eslintConfigFile, toolPath } = context;
21461
+ const lintPolicy = validateLintPolicy(productDir);
21257
21462
  if (!lintPolicy.ok) {
21258
21463
  return { success: false, error: lintPolicy.error };
21259
21464
  }
@@ -21269,47 +21474,57 @@ async function validateESLint(context, runner = defaultEslintProcessRunner, outp
21269
21474
  scopeConfig: context.scopeConfig
21270
21475
  });
21271
21476
  return new Promise((resolve17) => {
21272
- const localBin = join36(projectRoot, ...ESLINT_LOCAL_BIN_SEGMENTS);
21273
- const binary = existsSync6(localBin) ? localBin : "npx";
21477
+ const localBin = join36(productDir, ...ESLINT_LOCAL_BIN_SEGMENTS);
21478
+ const binary = toolPath ?? (existsSync6(localBin) ? localBin : "npx");
21274
21479
  const spawnArgs = binary === "npx" ? eslintArgs : eslintArgs.slice(1);
21275
21480
  const eslintProcess = spawnManagedSubprocess(runner, binary, spawnArgs, {
21276
- cwd: projectRoot
21481
+ cwd: productDir
21277
21482
  });
21483
+ const chunks = [];
21484
+ const capture = (chunk) => {
21485
+ chunks.push(typeof chunk === "string" ? chunk : Buffer.from(chunk).toString());
21486
+ };
21487
+ eslintProcess.stdout?.on(VALIDATION_SUBPROCESS_EVENTS.DATA, capture);
21488
+ eslintProcess.stderr?.on(VALIDATION_SUBPROCESS_EVENTS.DATA, capture);
21278
21489
  forwardValidationSubprocessOutput(eslintProcess, outputStreams);
21279
21490
  eslintProcess.on(VALIDATION_SUBPROCESS_EVENTS.CLOSE, (code) => {
21491
+ const output = chunks.join("");
21280
21492
  if (code === 0) {
21281
- resolve17({ success: true });
21493
+ resolve17({ success: true, output });
21282
21494
  } else {
21283
- resolve17({ success: false, error: `ESLint exited with code ${code}` });
21495
+ resolve17({ success: false, output, error: `ESLint exited with code ${code}` });
21284
21496
  }
21285
21497
  });
21286
21498
  eslintProcess.on(VALIDATION_SUBPROCESS_EVENTS.ERROR, (error) => {
21287
- resolve17({ success: false, error: error.message });
21499
+ resolve17({ success: false, output: chunks.join(""), error: error.message });
21288
21500
  });
21289
21501
  });
21290
21502
  }
21291
21503
 
21292
21504
  // src/commands/validation/lint.ts
21293
- var TYPESCRIPT_ABSENT_MESSAGE3 = formatTypeScriptAbsentSkipMessage(VALIDATION_STAGE_DISPLAY_NAMES.ESLINT);
21294
- var MISSING_CONFIG_MESSAGE = VALIDATION_COMMAND_OUTPUT.ESLINT_MISSING_CONFIG;
21295
- var ESLINT_CONFIG_ERROR_MESSAGE = `${VALIDATION_STAGE_DISPLAY_NAMES.ESLINT}: \u2717 config error`;
21296
- var VALIDATION_PATHS_NO_TARGETS_MESSAGE = formatValidationPathsNoTargetsSkipMessage(
21297
- VALIDATION_STAGE_DISPLAY_NAMES.ESLINT
21298
- );
21299
- var defaultLintCommandDependencies = {
21505
+ var defaultLintCommandDeps = {
21300
21506
  detectTypeScript,
21301
21507
  discoverTool,
21302
21508
  resolveConfig,
21303
21509
  validateESLint
21304
21510
  };
21305
- async function lintCommand(options, deps = defaultLintCommandDependencies) {
21306
- const { cwd, scope: scope2 = "full", files, fix, outputStreams, quiet } = options;
21511
+ var TYPESCRIPT_ABSENT_MESSAGE2 = formatTypeScriptAbsentSkipMessage(VALIDATION_STAGE_DISPLAY_NAMES.ESLINT);
21512
+ var MISSING_CONFIG_MESSAGE = VALIDATION_COMMAND_OUTPUT.ESLINT_MISSING_CONFIG;
21513
+ var ESLINT_CONFIG_ERROR_MESSAGE = formatValidationConfigProblemMessage(
21514
+ VALIDATION_STAGE_DISPLAY_NAMES.ESLINT,
21515
+ "configuration error"
21516
+ );
21517
+ var VALIDATION_PATHS_NO_TARGETS_MESSAGE = formatValidationPathsNoTargetsSkipMessage(
21518
+ VALIDATION_STAGE_DISPLAY_NAMES.ESLINT
21519
+ );
21520
+ async function lintCommand(options, deps = defaultLintCommandDeps) {
21521
+ const { cwd, scope: scope2 = "full", files, fix, json, outputStreams, quiet, streamedPipelineOutput } = options;
21307
21522
  const startTime = Date.now();
21308
21523
  const tsDetection = deps.detectTypeScript(cwd);
21309
21524
  if (!tsDetection.present) {
21310
21525
  return {
21311
21526
  exitCode: 0,
21312
- output: quiet ? "" : TYPESCRIPT_ABSENT_MESSAGE3,
21527
+ output: quiet ? "" : TYPESCRIPT_ABSENT_MESSAGE2,
21313
21528
  durationMs: Date.now() - startTime
21314
21529
  };
21315
21530
  }
@@ -21336,35 +21551,38 @@ async function lintCommand(options, deps = defaultLintCommandDependencies) {
21336
21551
  );
21337
21552
  const explicitMode = files !== void 0 && files.length > 0;
21338
21553
  const scopeConfig = resolveTypeScriptValidationScope({
21339
- projectRoot: cwd,
21554
+ productDir: cwd,
21340
21555
  scope: scope2,
21341
21556
  paths: files,
21342
21557
  validationPathFilter,
21343
- markExplicitPathsAsValidationFilter: explicitMode,
21344
- bypassExplicitPathValidationFilter: true
21558
+ markExplicitPathsAsValidationFilter: explicitMode
21345
21559
  });
21346
21560
  const explicitTargets = explicitMode ? filterExplicitTypeScriptScopeTargets({
21347
21561
  paths: files,
21348
- projectRoot: cwd,
21562
+ productDir: cwd,
21349
21563
  validationPathFilter,
21350
21564
  scopeConfig: getTypeScriptScope(scope2, cwd),
21351
21565
  bypassValidationPathFilter: true
21352
21566
  }) : void 0;
21353
- const validatedFiles = explicitTargets?.every((target) => target.kind === EXPLICIT_TYPESCRIPT_SCOPE_TARGET_KIND.FILE) ? explicitTargets.map((target) => formatLintValidationOperand(toProjectRelativeValidationPath(cwd, target.path))) : void 0;
21354
- if (scopeConfig.filteredByValidationPathNoMatches) {
21567
+ const validatedFiles = explicitTargets?.every((target) => target.kind === EXPLICIT_TYPESCRIPT_SCOPE_TARGET_KIND.FILE) ? explicitTargets.map((target) => formatLintValidationOperand(toProductRelativeValidationPath(cwd, target.path))) : void 0;
21568
+ const noTargetsMessage = formatValidationScopeNoTargetsSkipMessage(
21569
+ VALIDATION_STAGE_DISPLAY_NAMES.ESLINT,
21570
+ scopeConfig
21571
+ );
21572
+ if (noTargetsMessage !== void 0) {
21355
21573
  return {
21356
21574
  exitCode: 0,
21357
- output: quiet ? "" : VALIDATION_PATHS_NO_TARGETS_MESSAGE,
21575
+ output: quiet ? "" : noTargetsMessage,
21358
21576
  durationMs: Date.now() - startTime
21359
21577
  };
21360
21578
  }
21361
- const toolResult = await deps.discoverTool("eslint", { projectRoot: cwd });
21579
+ const toolResult = await deps.discoverTool("eslint", { productDir: cwd, includeBundled: false });
21362
21580
  if (!toolResult.found) {
21363
21581
  const skipMessage = formatSkipMessage(VALIDATION_STAGE_DISPLAY_NAMES.ESLINT, toolResult);
21364
21582
  return { exitCode: 0, output: skipMessage, durationMs: Date.now() - startTime };
21365
21583
  }
21366
21584
  const context = {
21367
- projectRoot: cwd,
21585
+ productDir: cwd,
21368
21586
  scope: scope2,
21369
21587
  scopeConfig,
21370
21588
  mode: fix ? "write" : "read",
@@ -21372,26 +21590,35 @@ async function lintCommand(options, deps = defaultLintCommandDependencies) {
21372
21590
  validatedFiles,
21373
21591
  validatedFileIgnorePatterns: void 0,
21374
21592
  isFileSpecificMode: Boolean(validatedFiles && validatedFiles.length > 0),
21375
- eslintConfigFile
21593
+ eslintConfigFile,
21594
+ toolPath: toolResult.location.path
21376
21595
  };
21377
- const result = await deps.validateESLint(context, void 0, outputStreams);
21596
+ const result = await deps.validateESLint(
21597
+ context,
21598
+ void 0,
21599
+ outputStreams ?? discardValidationSubprocessOutputStreams
21600
+ );
21378
21601
  const durationMs = Date.now() - startTime;
21379
- return formatLintResult(result, quiet, durationMs);
21602
+ return formatLintResult(result, quiet, durationMs, json, streamedPipelineOutput);
21380
21603
  }
21381
21604
  function formatLintValidationOperand(path4) {
21382
21605
  return path4.length === 0 ? "." : path4;
21383
21606
  }
21384
- function formatLintResult(result, quiet, durationMs) {
21607
+ function formatLintResult(result, quiet, durationMs, json, streamedPipelineOutput) {
21385
21608
  if (result.skipped) {
21386
21609
  const output2 = quiet ? "" : VALIDATION_PATHS_NO_TARGETS_MESSAGE;
21387
21610
  return { exitCode: 0, output: output2, durationMs };
21388
21611
  }
21389
21612
  if (result.success) {
21390
- const output2 = quiet ? "" : VALIDATION_COMMAND_OUTPUT.ESLINT_SUCCESS;
21391
- return { exitCode: 0, output: output2, durationMs };
21613
+ const output2 = quiet ? "" : [VALIDATION_COMMAND_OUTPUT.ESLINT_SUCCESS, result.output].filter(
21614
+ (line) => line !== void 0 && line.length > 0
21615
+ ).join("\n");
21616
+ const terminalOutput2 = streamedValidationTerminalOutput(result.output, json, streamedPipelineOutput);
21617
+ return { exitCode: 0, output: output2, terminalOutput: terminalOutput2, durationMs };
21392
21618
  }
21393
- const output = result.error ?? VALIDATION_COMMAND_OUTPUT.ESLINT_FAILURE;
21394
- return { exitCode: 1, output, durationMs };
21619
+ const output = [result.output, result.error ?? VALIDATION_COMMAND_OUTPUT.ESLINT_FAILURE].filter((line) => line !== void 0 && line.length > 0).join("\n");
21620
+ const terminalOutput = streamedValidationTerminalOutput(result.output, json, streamedPipelineOutput);
21621
+ return { exitCode: 1, output, terminalOutput, durationMs };
21395
21622
  }
21396
21623
 
21397
21624
  // src/domains/validation/literal-problem-kind.ts
@@ -21443,7 +21670,7 @@ var GIT_DEFAULT_GLOBAL_IGNORE_PATH = {
21443
21670
  GIT_DIRECTORY: "git",
21444
21671
  IGNORE_FILE: "ignore"
21445
21672
  };
21446
- var PATH_SEGMENT_SEPARATOR5 = "/";
21673
+ var PATH_SEGMENT_SEPARATOR4 = "/";
21447
21674
  var CURRENT_DIRECTORY_PREFIX2 = ".";
21448
21675
  var GIT_SCOPE_FAILURE_MESSAGE = "failed to read git scope";
21449
21676
  var GIT_MISSING_CONTEXT_MESSAGE = "missing git working tree";
@@ -21576,11 +21803,11 @@ function readGlobalExcludesPath(productDir) {
21576
21803
  }
21577
21804
  function parentPrefixes(path4) {
21578
21805
  const prefixes = [];
21579
- let index = path4.lastIndexOf(PATH_SEGMENT_SEPARATOR5);
21806
+ let index = path4.lastIndexOf(PATH_SEGMENT_SEPARATOR4);
21580
21807
  while (index > 0) {
21581
21808
  const parent = path4.slice(0, index);
21582
21809
  prefixes.push(parent);
21583
- index = parent.lastIndexOf(PATH_SEGMENT_SEPARATOR5);
21810
+ index = parent.lastIndexOf(PATH_SEGMENT_SEPARATOR4);
21584
21811
  }
21585
21812
  return prefixes;
21586
21813
  }
@@ -21635,7 +21862,7 @@ function createIgnoreSourceReader(productDir, config) {
21635
21862
  },
21636
21863
  hasIncludedDescendant(relativePath) {
21637
21864
  return descendantParents.has(
21638
- relativePath.endsWith(PATH_SEGMENT_SEPARATOR5) ? relativePath.slice(0, -1) : relativePath
21865
+ relativePath.endsWith(PATH_SEGMENT_SEPARATOR4) ? relativePath.slice(0, -1) : relativePath
21639
21866
  );
21640
21867
  },
21641
21868
  appliedOverrides() {
@@ -22340,7 +22567,8 @@ async function validateLiteralReuse(input) {
22340
22567
  return {
22341
22568
  findings,
22342
22569
  indexedOccurrencesByFile,
22343
- filteredByValidationPathNoMatches: input.scopeConfig?.filteredByValidationPathNoMatches
22570
+ filteredByValidationPathNoMatches: input.scopeConfig?.filteredByValidationPathNoMatches,
22571
+ explicitPathNoMatches: input.scopeConfig?.explicitPathNoMatches
22344
22572
  };
22345
22573
  }
22346
22574
  async function readSafe(path4) {
@@ -22388,29 +22616,29 @@ var OUTPUT_MODE_NAME = {
22388
22616
  JSON: "json"
22389
22617
  };
22390
22618
  var OUTPUT_MODE_NAMES = Object.values(OUTPUT_MODE_NAME);
22619
+ var defaultLiteralCommandDeps = {
22620
+ validateLiteralReuse
22621
+ };
22391
22622
  var LITERAL_EXIT_CODES = {
22392
22623
  OK: 0,
22393
22624
  FINDINGS: 1,
22394
22625
  CONFIG_ERROR: 2
22395
22626
  };
22396
- var TYPESCRIPT_ABSENT_MESSAGE4 = formatTypeScriptAbsentSkipMessage(
22397
- VALIDATION_STAGE_DISPLAY_NAMES.LITERAL
22398
- );
22399
- var VALIDATION_PATHS_NO_TARGETS_MESSAGE2 = formatValidationPathsNoTargetsSkipMessage(
22627
+ var TYPESCRIPT_ABSENT_MESSAGE3 = formatTypeScriptAbsentSkipMessage(
22400
22628
  VALIDATION_STAGE_DISPLAY_NAMES.LITERAL
22401
22629
  );
22402
22630
  var LITERAL_DISABLED_MESSAGE = `\u23ED ${VALIDATION_SKIP_LABELS.VERB} ${VALIDATION_STAGE_DISPLAY_NAMES.LITERAL} (${VALIDATION_SKIP_LABELS.DISABLED_BY_PREFIX} validation.literal.enabled)`;
22403
- var NO_PROBLEMS_MESSAGE = "Literal: \u2713 No problems";
22631
+ var NO_PROBLEMS_MESSAGE = formatValidationNoProblemsMessage(VALIDATION_STAGE_DISPLAY_NAMES.LITERAL);
22404
22632
  function formatNoProblemsOfKind(kind) {
22405
22633
  return `Literal: No problems of type ${kind}`;
22406
22634
  }
22407
- async function literalCommand(options) {
22635
+ async function literalCommand(options, deps = defaultLiteralCommandDeps) {
22408
22636
  const start = Date.now();
22409
22637
  const tsDetection = detectTypeScript(options.cwd);
22410
22638
  if (!tsDetection.present) {
22411
22639
  return {
22412
22640
  exitCode: LITERAL_EXIT_CODES.OK,
22413
- output: options.quiet ? "" : TYPESCRIPT_ABSENT_MESSAGE4,
22641
+ output: options.quiet ? "" : TYPESCRIPT_ABSENT_MESSAGE3,
22414
22642
  durationMs: Date.now() - start
22415
22643
  };
22416
22644
  }
@@ -22418,7 +22646,10 @@ async function literalCommand(options) {
22418
22646
  if (typeof resolved === "string") {
22419
22647
  return {
22420
22648
  exitCode: LITERAL_EXIT_CODES.CONFIG_ERROR,
22421
- output: `Literal: \u2717 config error \u2014 ${resolved}`,
22649
+ output: `${formatValidationConfigProblemMessage(
22650
+ VALIDATION_STAGE_DISPLAY_NAMES.LITERAL,
22651
+ "configuration error"
22652
+ )} \u2014 ${resolved}`,
22422
22653
  durationMs: Date.now() - start
22423
22654
  };
22424
22655
  }
@@ -22429,17 +22660,18 @@ async function literalCommand(options) {
22429
22660
  durationMs: Date.now() - start
22430
22661
  };
22431
22662
  }
22432
- const result = await validateLiteralReuse({
22663
+ const result = await deps.validateLiteralReuse({
22433
22664
  productDir: options.cwd,
22434
22665
  explicitFiles: explicitLiteralPaths(options.files),
22435
22666
  config: resolved.literalConfig,
22436
22667
  pathConfig: resolved.pathConfig,
22437
22668
  scopeConfig: resolveExplicitLiteralTypeScriptScope(options, resolved.pathConfig)
22438
22669
  });
22439
- if (options.files !== void 0 && options.files.length > 0 && result.filteredByValidationPathNoMatches) {
22670
+ const noTargetsMessage = explicitLiteralNoTargetsSkipMessage(options, result);
22671
+ if (noTargetsMessage !== void 0) {
22440
22672
  return {
22441
22673
  exitCode: LITERAL_EXIT_CODES.OK,
22442
- output: options.quiet ? "" : VALIDATION_PATHS_NO_TARGETS_MESSAGE2,
22674
+ output: options.quiet ? "" : noTargetsMessage,
22443
22675
  durationMs: Date.now() - start
22444
22676
  };
22445
22677
  }
@@ -22454,7 +22686,18 @@ async function literalCommand(options) {
22454
22686
  } else {
22455
22687
  output = formatLiteralCommandOutput(filteredFindings, options);
22456
22688
  }
22457
- return { exitCode, output, durationMs: Date.now() - start };
22689
+ return {
22690
+ exitCode,
22691
+ output,
22692
+ durationMs: Date.now() - start,
22693
+ outputTarget: VALIDATION_OUTPUT_TARGET.STDOUT
22694
+ };
22695
+ }
22696
+ function explicitLiteralNoTargetsSkipMessage(options, result) {
22697
+ if (options.files === void 0 || options.files.length === 0) {
22698
+ return void 0;
22699
+ }
22700
+ return formatValidationScopeNoTargetsSkipMessage(VALIDATION_STAGE_DISPLAY_NAMES.LITERAL, result);
22458
22701
  }
22459
22702
  async function resolveLiteralCommandConfig(options) {
22460
22703
  if (options.config !== void 0) {
@@ -22481,12 +22724,11 @@ function resolveExplicitLiteralTypeScriptScope(options, pathConfig) {
22481
22724
  return void 0;
22482
22725
  }
22483
22726
  return resolveTypeScriptValidationScope({
22484
- projectRoot: options.cwd,
22727
+ productDir: options.cwd,
22485
22728
  scope: options.scope ?? VALIDATION_SCOPES.FULL,
22486
22729
  paths: options.files,
22487
22730
  validationPathFilter: pathConfig,
22488
- markExplicitPathsAsValidationFilter: true,
22489
- bypassExplicitPathValidationFilter: true
22731
+ markExplicitPathsAsValidationFilter: true
22490
22732
  });
22491
22733
  }
22492
22734
  function explicitLiteralPaths(files) {
@@ -22630,8 +22872,8 @@ function formatTypeScriptExitCodeError(code) {
22630
22872
  }
22631
22873
  var TEMPORARY_TSCONFIG_COMPILER_OPTIONS = { noEmit: true };
22632
22874
  var TEMPORARY_TSCONFIG_DIR_PREFIX = "validate-ts-";
22633
- async function createTemporaryTsconfigDir(projectRoot, deps) {
22634
- const parent = join39(projectRoot, ...TEMPORARY_TSCONFIG_PARENT_SEGMENTS);
22875
+ async function createTemporaryTsconfigDir(productDir, deps) {
22876
+ const parent = join39(productDir, ...TEMPORARY_TSCONFIG_PARENT_SEGMENTS);
22635
22877
  deps.mkdirSync(parent, { recursive: true });
22636
22878
  return deps.mkdtemp(join39(parent, TEMPORARY_TSCONFIG_DIR_PREFIX));
22637
22879
  }
@@ -22639,13 +22881,13 @@ function buildTypeScriptArgs(context) {
22639
22881
  const { scope: scope2, configFile } = context;
22640
22882
  return scope2 === VALIDATION_SCOPES.FULL ? ["tsc", "--noEmit"] : ["tsc", "--project", configFile];
22641
22883
  }
22642
- async function createFileSpecificTsconfig(scope2, files, projectRoot, deps = defaultTypeScriptDeps) {
22643
- const tempDir = await createTemporaryTsconfigDir(projectRoot, deps);
22884
+ async function createFileSpecificTsconfig(scope2, files, productDir, deps = defaultTypeScriptDeps) {
22885
+ const tempDir = await createTemporaryTsconfigDir(productDir, deps);
22644
22886
  const configPath = join39(tempDir, "tsconfig.json");
22645
22887
  const baseConfigFile = TSCONFIG_FILES[scope2];
22646
- const absoluteFiles = files.map((file) => isAbsolute12(file) ? file : join39(projectRoot, file));
22888
+ const absoluteFiles = files.map((file) => isAbsolute12(file) ? file : join39(productDir, file));
22647
22889
  const tempConfig = {
22648
- extends: join39(projectRoot, baseConfigFile),
22890
+ extends: join39(productDir, baseConfigFile),
22649
22891
  files: absoluteFiles,
22650
22892
  include: [],
22651
22893
  exclude: [],
@@ -22655,16 +22897,16 @@ async function createFileSpecificTsconfig(scope2, files, projectRoot, deps = def
22655
22897
  const cleanup = createTemporaryTsconfigCleanup(tempDir, deps);
22656
22898
  return { configPath, tempDir, cleanup };
22657
22899
  }
22658
- async function createScopeFilteredTsconfig(scope2, projectRoot, scopeConfig, deps = defaultTypeScriptDeps) {
22659
- const tempDir = await createTemporaryTsconfigDir(projectRoot, deps);
22900
+ async function createScopeFilteredTsconfig(scope2, productDir, scopeConfig, deps = defaultTypeScriptDeps) {
22901
+ const tempDir = await createTemporaryTsconfigDir(productDir, deps);
22660
22902
  const configPath = join39(tempDir, "tsconfig.json");
22661
22903
  const baseConfigFile = TSCONFIG_FILES[scope2];
22662
22904
  const toTemporaryConfigPathPattern = (pattern) => {
22663
- const absolutePattern = isAbsolute12(pattern) ? pattern : join39(projectRoot, pattern);
22905
+ const absolutePattern = isAbsolute12(pattern) ? pattern : join39(productDir, pattern);
22664
22906
  return relative10(tempDir, absolutePattern);
22665
22907
  };
22666
22908
  const tempConfig = {
22667
- extends: join39(projectRoot, baseConfigFile),
22909
+ extends: join39(productDir, baseConfigFile),
22668
22910
  include: scopeConfigToTemporaryIncludes(scopeConfig).map(toTemporaryConfigPathPattern),
22669
22911
  exclude: scopeConfig.excludePatterns.map(toTemporaryConfigPathPattern),
22670
22912
  compilerOptions: TEMPORARY_TSCONFIG_COMPILER_OPTIONS
@@ -22693,19 +22935,20 @@ function createTemporaryTsconfigCleanup(tempDir, deps) {
22693
22935
  };
22694
22936
  }
22695
22937
  async function validateTypeScript(context, options = {}) {
22696
- const { scope: scope2, projectRoot, files, scopeConfig } = context;
22938
+ const { scope: scope2, productDir, files, scopeConfig } = context;
22697
22939
  const {
22698
22940
  runner = defaultTypeScriptProcessRunner,
22699
22941
  deps = defaultTypeScriptDeps,
22942
+ toolPath,
22700
22943
  outputStreams = defaultValidationSubprocessOutputStreams
22701
22944
  } = options;
22702
22945
  const configFile = TSCONFIG_FILES[scope2];
22703
22946
  if (files && files.length > 0) {
22704
- const { configPath, cleanup } = await createFileSpecificTsconfig(scope2, files, projectRoot, deps);
22947
+ const { configPath, cleanup } = await createFileSpecificTsconfig(scope2, files, productDir, deps);
22705
22948
  try {
22706
22949
  return await runTypeScriptInvocation(
22707
- projectRoot,
22708
- resolveProjectTscInvocation(projectRoot, deps, ["--project", configPath]),
22950
+ productDir,
22951
+ resolveTscInvocation(productDir, deps, ["--project", configPath], toolPath),
22709
22952
  runner,
22710
22953
  outputStreams,
22711
22954
  cleanup
@@ -22722,64 +22965,89 @@ async function validateTypeScript(context, options = {}) {
22722
22965
  if (scopeConfig.filePatterns.length === 0 && scopeConfig.directories.length === 0) {
22723
22966
  return { success: true, skipped: true };
22724
22967
  }
22725
- const { configPath, cleanup } = await createScopeFilteredTsconfig(scope2, projectRoot, scopeConfig, deps);
22968
+ const { configPath, cleanup } = await createScopeFilteredTsconfig(scope2, productDir, scopeConfig, deps);
22726
22969
  return runTypeScriptInvocation(
22727
- projectRoot,
22728
- resolveProjectTscInvocation(projectRoot, deps, ["--project", configPath]),
22970
+ productDir,
22971
+ resolveTscInvocation(productDir, deps, ["--project", configPath], toolPath),
22729
22972
  runner,
22730
22973
  outputStreams,
22731
22974
  cleanup
22732
22975
  );
22733
22976
  }
22734
22977
  return runTypeScriptInvocation(
22735
- projectRoot,
22736
- resolveProjectTscInvocation(projectRoot, deps, buildTypeScriptArgs({ scope: scope2, configFile }).slice(1)),
22978
+ productDir,
22979
+ resolveTscInvocation(productDir, deps, buildTypeScriptArgs({ scope: scope2, configFile }).slice(1), toolPath),
22737
22980
  runner,
22738
22981
  outputStreams
22739
22982
  );
22740
22983
  }
22741
- function resolveProjectTscInvocation(projectRoot, deps, tscArgs) {
22742
- const tscBin = join39(projectRoot, "node_modules", ".bin", "tsc");
22984
+ function resolveTscInvocation(productDir, deps, tscArgs, toolPath) {
22985
+ if (toolPath !== void 0) {
22986
+ return { tool: toolPath, args: tscArgs };
22987
+ }
22988
+ const tscBin = join39(productDir, "node_modules", ".bin", "tsc");
22743
22989
  const tool = deps.existsSync(tscBin) ? tscBin : "npx";
22744
22990
  return {
22745
22991
  tool,
22746
22992
  args: tool === "npx" ? ["tsc", ...tscArgs] : tscArgs
22747
22993
  };
22748
22994
  }
22749
- function runTypeScriptInvocation(projectRoot, invocation, runner, outputStreams, cleanup = () => {
22995
+ function runTypeScriptInvocation(productDir, invocation, runner, outputStreams, cleanup = () => {
22750
22996
  }) {
22751
22997
  return new Promise((resolve17) => {
22752
22998
  const tscProcess = spawnManagedSubprocess(runner, invocation.tool, invocation.args, {
22753
- cwd: projectRoot
22999
+ cwd: productDir
22754
23000
  });
23001
+ const chunks = [];
23002
+ const capture = (chunk) => {
23003
+ chunks.push(typeof chunk === "string" ? chunk : Buffer.from(chunk).toString());
23004
+ };
23005
+ tscProcess.stdout?.on(VALIDATION_SUBPROCESS_EVENTS.DATA, capture);
23006
+ tscProcess.stderr?.on(VALIDATION_SUBPROCESS_EVENTS.DATA, capture);
22755
23007
  forwardValidationSubprocessOutput(tscProcess, outputStreams);
22756
23008
  tscProcess.on(VALIDATION_SUBPROCESS_EVENTS.CLOSE, (code) => {
22757
23009
  cleanup();
23010
+ const output = chunks.join("");
22758
23011
  if (code === 0) {
22759
- resolve17({ success: true, skipped: false });
23012
+ resolve17({ success: true, skipped: false, output });
22760
23013
  } else {
22761
- resolve17({ success: false, error: formatTypeScriptExitCodeError(code) });
23014
+ resolve17({ success: false, output, error: formatTypeScriptExitCodeError(code) });
22762
23015
  }
22763
23016
  });
22764
23017
  tscProcess.on(VALIDATION_SUBPROCESS_EVENTS.ERROR, (error) => {
22765
23018
  cleanup();
22766
- resolve17({ success: false, error: error.message });
23019
+ resolve17({ success: false, output: chunks.join(""), error: error.message });
22767
23020
  });
22768
23021
  });
22769
23022
  }
22770
23023
 
22771
23024
  // src/commands/validation/typescript.ts
23025
+ var defaultTypeScriptCommandDeps = {
23026
+ detectTypeScript,
23027
+ discoverTool,
23028
+ validateTypeScript
23029
+ };
22772
23030
  var TYPESCRIPT_VALIDATION_MESSAGES = {
22773
23031
  ABSENT: formatTypeScriptAbsentSkipMessage(VALIDATION_STAGE_DISPLAY_NAMES.TYPESCRIPT),
22774
- CONFIG_ERROR: `${VALIDATION_STAGE_DISPLAY_NAMES.TYPESCRIPT}: \u2717 config error`,
23032
+ CONFIG_ERROR: formatValidationConfigProblemMessage(
23033
+ VALIDATION_STAGE_DISPLAY_NAMES.TYPESCRIPT,
23034
+ "configuration error"
23035
+ ),
22775
23036
  NO_VALIDATION_PATH_TARGETS: formatValidationPathsNoTargetsSkipMessage(VALIDATION_STAGE_DISPLAY_NAMES.TYPESCRIPT),
23037
+ NO_EXPLICIT_PATH_TARGETS: formatExplicitPathsNoTargetsSkipMessage(VALIDATION_STAGE_DISPLAY_NAMES.TYPESCRIPT),
22776
23038
  SUCCESS: VALIDATION_COMMAND_OUTPUT.TYPESCRIPT_SUCCESS,
22777
23039
  TOOL_LABEL: VALIDATION_STAGE_DISPLAY_NAMES.TYPESCRIPT
22778
23040
  };
22779
- async function typescriptCommand(options) {
22780
- const { cwd, scope: scope2 = "full", files, outputStreams, quiet } = options;
23041
+ var TYPESCRIPT_TOOL_DISCOVERY = {
23042
+ TOOL: "typescript",
23043
+ EXECUTABLE_NAME: "tsc",
23044
+ BUNDLED_EXECUTABLE: "typescript/bin/tsc",
23045
+ PRODUCT_EXECUTABLE_SEGMENTS: ["node_modules", ".bin", "tsc"]
23046
+ };
23047
+ async function typescriptCommand(options, deps = defaultTypeScriptCommandDeps) {
23048
+ const { cwd, scope: scope2 = "full", files, json, outputStreams, quiet, streamedPipelineOutput } = options;
22781
23049
  const startTime = Date.now();
22782
- const tsDetection = detectTypeScript(cwd);
23050
+ const tsDetection = deps.detectTypeScript(cwd);
22783
23051
  if (!tsDetection.present) {
22784
23052
  return {
22785
23053
  exitCode: 0,
@@ -22797,53 +23065,69 @@ async function typescriptCommand(options) {
22797
23065
  }
22798
23066
  const validationConfig = loaded.value[validationConfigDescriptor.section];
22799
23067
  const scopeConfig = resolveTypeScriptValidationScope({
22800
- projectRoot: cwd,
23068
+ productDir: cwd,
22801
23069
  scope: scope2,
22802
23070
  paths: files,
22803
23071
  validationPathFilter: validationPathFilterForTool(
22804
23072
  validationConfig.paths,
22805
23073
  VALIDATION_PATH_TOOL_SUBSECTIONS.TYPESCRIPT
22806
23074
  ),
22807
- markExplicitPathsAsValidationFilter: true,
22808
- bypassExplicitPathValidationFilter: true
23075
+ markExplicitPathsAsValidationFilter: true
22809
23076
  });
22810
- if (scopeConfig.filteredByValidationPathNoMatches) {
23077
+ const noTargetsMessage = formatValidationScopeNoTargetsSkipMessage(
23078
+ VALIDATION_STAGE_DISPLAY_NAMES.TYPESCRIPT,
23079
+ scopeConfig
23080
+ );
23081
+ if (noTargetsMessage !== void 0) {
22811
23082
  return {
22812
23083
  exitCode: 0,
22813
- output: quiet ? "" : TYPESCRIPT_VALIDATION_MESSAGES.NO_VALIDATION_PATH_TARGETS,
23084
+ output: quiet ? "" : noTargetsMessage,
22814
23085
  durationMs: Date.now() - startTime
22815
23086
  };
22816
23087
  }
22817
- const toolResult = await discoverTool("typescript", { projectRoot: cwd });
23088
+ const toolResult = await deps.discoverTool(TYPESCRIPT_TOOL_DISCOVERY.TOOL, {
23089
+ productDir: cwd,
23090
+ executableName: TYPESCRIPT_TOOL_DISCOVERY.EXECUTABLE_NAME,
23091
+ bundledExecutable: TYPESCRIPT_TOOL_DISCOVERY.BUNDLED_EXECUTABLE,
23092
+ priority: TOOL_DISCOVERY_PRIORITY.PRODUCT_FIRST
23093
+ });
22818
23094
  if (!toolResult.found) {
22819
23095
  const skipMessage = formatSkipMessage(VALIDATION_STAGE_DISPLAY_NAMES.TYPESCRIPT, toolResult);
22820
23096
  return { exitCode: 0, output: skipMessage, durationMs: Date.now() - startTime };
22821
23097
  }
22822
- const result = await validateTypeScript({
23098
+ const result = await deps.validateTypeScript({
22823
23099
  scope: scope2,
22824
- projectRoot: cwd,
23100
+ productDir: cwd,
22825
23101
  scopeConfig
22826
23102
  }, {
22827
- outputStreams
23103
+ toolPath: toolResult.location.path,
23104
+ outputStreams: outputStreams ?? discardValidationSubprocessOutputStreams
22828
23105
  });
22829
23106
  const durationMs = Date.now() - startTime;
22830
- return formatTypeScriptResult(result, quiet, durationMs);
23107
+ return formatTypeScriptResult(result, quiet, durationMs, json, streamedPipelineOutput);
22831
23108
  }
22832
- function formatTypeScriptResult(result, quiet, durationMs) {
23109
+ function formatTypeScriptResult(result, quiet, durationMs, json, streamedPipelineOutput) {
22833
23110
  if (result.skipped) {
22834
23111
  const output2 = quiet ? "" : TYPESCRIPT_VALIDATION_MESSAGES.NO_VALIDATION_PATH_TARGETS;
22835
23112
  return { exitCode: 0, output: output2, durationMs };
22836
23113
  }
22837
23114
  if (result.success) {
22838
- const output2 = quiet ? "" : TYPESCRIPT_VALIDATION_MESSAGES.SUCCESS;
22839
- return { exitCode: 0, output: output2, durationMs };
23115
+ const output2 = quiet ? "" : [TYPESCRIPT_VALIDATION_MESSAGES.SUCCESS, result.output].filter((line) => line !== void 0 && line.length > 0).join("\n");
23116
+ const terminalOutput2 = streamedValidationTerminalOutput(result.output, json, streamedPipelineOutput);
23117
+ return { exitCode: 0, output: output2, terminalOutput: terminalOutput2, durationMs };
22840
23118
  }
22841
- const output = result.error ?? VALIDATION_COMMAND_OUTPUT.TYPESCRIPT_FAILURE;
22842
- return { exitCode: 1, output, durationMs };
23119
+ const output = [result.output, result.error ?? VALIDATION_COMMAND_OUTPUT.TYPESCRIPT_FAILURE].filter((line) => line !== void 0 && line.length > 0).join("\n");
23120
+ const terminalOutput = streamedValidationTerminalOutput(result.output, json, streamedPipelineOutput);
23121
+ return { exitCode: 1, output, terminalOutput, durationMs };
22843
23122
  }
22844
23123
 
22845
23124
  // src/validation/languages/typescript.ts
22846
23125
  var TYPESCRIPT_LANGUAGE_NAME = "typescript";
23126
+ var SKIP_CIRCULAR_REASON = "skip-circular";
23127
+ var SKIP_KNIP_REASON = "skip-knip";
23128
+ var SKIP_LINT_REASON = "skip-lint";
23129
+ var SKIP_TYPESCRIPT_REASON = "skip-typescript";
23130
+ var SKIP_LITERAL_REASON = "skip-literal";
22847
23131
  var TYPESCRIPT_VALIDATION_CONCERN = {
22848
23132
  LINT: "lint",
22849
23133
  TYPE_CHECK: "type-check",
@@ -22852,14 +23136,60 @@ var TYPESCRIPT_VALIDATION_CONCERN = {
22852
23136
  LITERAL_REUSE: "literal-reuse",
22853
23137
  UNUSED_CODE: "unused-code"
22854
23138
  };
23139
+ var TYPESCRIPT_VALIDATION_STAGE_BY_CONCERN = {
23140
+ [TYPESCRIPT_VALIDATION_CONCERN.LINT]: VALIDATION_STAGE_DISPLAY_NAMES.ESLINT,
23141
+ [TYPESCRIPT_VALIDATION_CONCERN.TYPE_CHECK]: VALIDATION_STAGE_DISPLAY_NAMES.TYPESCRIPT,
23142
+ [TYPESCRIPT_VALIDATION_CONCERN.AST_ENFORCEMENT]: VALIDATION_STAGE_DISPLAY_NAMES.ESLINT,
23143
+ [TYPESCRIPT_VALIDATION_CONCERN.CIRCULAR_DEPS]: VALIDATION_STAGE_DISPLAY_NAMES.CIRCULAR,
23144
+ [TYPESCRIPT_VALIDATION_CONCERN.LITERAL_REUSE]: VALIDATION_STAGE_DISPLAY_NAMES.LITERAL,
23145
+ [TYPESCRIPT_VALIDATION_CONCERN.UNUSED_CODE]: VALIDATION_STAGE_DISPLAY_NAMES.KNIP
23146
+ };
23147
+ var TYPESCRIPT_VALIDATION_STAGE_PARTICIPATION = {
23148
+ [VALIDATION_STAGE_DISPLAY_NAMES.CIRCULAR]: {
23149
+ default: VALIDATION_STAGE_PARTICIPATION.RUN,
23150
+ skipReason: SKIP_CIRCULAR_REASON,
23151
+ override: {
23152
+ flag: "--skip-circular",
23153
+ description: "Skip circular dependency detection for this validation all run"
23154
+ }
23155
+ },
23156
+ [VALIDATION_STAGE_DISPLAY_NAMES.KNIP]: {
23157
+ default: VALIDATION_STAGE_PARTICIPATION.RUN,
23158
+ skipReason: SKIP_KNIP_REASON,
23159
+ override: {
23160
+ flag: "--skip-knip",
23161
+ description: "Skip unused-code detection for this validation all run"
23162
+ }
23163
+ },
23164
+ [VALIDATION_STAGE_DISPLAY_NAMES.ESLINT]: {
23165
+ default: VALIDATION_STAGE_PARTICIPATION.RUN,
23166
+ skipReason: SKIP_LINT_REASON,
23167
+ override: {
23168
+ flag: "--skip-lint",
23169
+ description: "Skip ESLint validation for this validation all run"
23170
+ }
23171
+ },
23172
+ [VALIDATION_STAGE_DISPLAY_NAMES.TYPESCRIPT]: {
23173
+ default: VALIDATION_STAGE_PARTICIPATION.RUN,
23174
+ skipReason: SKIP_TYPESCRIPT_REASON,
23175
+ override: {
23176
+ flag: "--skip-typescript",
23177
+ description: "Skip TypeScript validation for this validation all run"
23178
+ }
23179
+ },
23180
+ [VALIDATION_STAGE_DISPLAY_NAMES.LITERAL]: {
23181
+ default: VALIDATION_STAGE_PARTICIPATION.RUN,
23182
+ skipReason: SKIP_LITERAL_REASON,
23183
+ override: {
23184
+ flag: "--skip-literal",
23185
+ description: "Skip literal reuse detection for this validation all run"
23186
+ }
23187
+ }
23188
+ };
22855
23189
  var defaultKnipStageDeps = {
22856
23190
  knipCommand
22857
23191
  };
22858
23192
  async function runCircularStage(context) {
22859
- if (context.skipCircular) {
22860
- const skipOutput = context.json ? CIRCULAR_SKIP_JSON_OUTPUT : CIRCULAR_SKIP_OUTPUT;
22861
- return { exitCode: 0, output: context.quiet ? "" : skipOutput };
22862
- }
22863
23193
  return circularCommand({
22864
23194
  cwd: context.cwd,
22865
23195
  scope: context.scope,
@@ -22869,10 +23199,6 @@ async function runCircularStage(context) {
22869
23199
  });
22870
23200
  }
22871
23201
  async function runLiteralStage(context) {
22872
- if (context.skipLiteral) {
22873
- const skipOutput = context.json ? LITERAL_SKIP_JSON_OUTPUT : LITERAL_SKIP_OUTPUT;
22874
- return { exitCode: 0, output: context.quiet ? "" : skipOutput };
22875
- }
22876
23202
  return literalCommand({
22877
23203
  cwd: context.cwd,
22878
23204
  scope: context.scope,
@@ -22887,27 +23213,32 @@ async function runKnipStage(context, deps = defaultKnipStageDeps) {
22887
23213
  scope: context.scope,
22888
23214
  files: context.files,
22889
23215
  quiet: context.quiet,
22890
- json: context.json
23216
+ json: context.json,
23217
+ streamedPipelineOutput: true,
23218
+ outputStreams: context.outputStreams
22891
23219
  });
22892
23220
  }
22893
23221
  var typescriptValidationLanguage = {
22894
23222
  name: TYPESCRIPT_LANGUAGE_NAME,
22895
23223
  concerns: Object.values(TYPESCRIPT_VALIDATION_CONCERN),
23224
+ stageByConcern: TYPESCRIPT_VALIDATION_STAGE_BY_CONCERN,
22896
23225
  stages: [
22897
23226
  {
22898
23227
  name: VALIDATION_STAGE_DISPLAY_NAMES.CIRCULAR,
22899
23228
  failsPipeline: true,
23229
+ participation: TYPESCRIPT_VALIDATION_STAGE_PARTICIPATION[VALIDATION_STAGE_DISPLAY_NAMES.CIRCULAR],
22900
23230
  run: runCircularStage
22901
23231
  },
22902
23232
  {
22903
23233
  name: VALIDATION_STAGE_DISPLAY_NAMES.KNIP,
22904
- // Knip is informational: unused-code findings never fail the pipeline.
22905
- failsPipeline: false,
23234
+ failsPipeline: true,
23235
+ participation: TYPESCRIPT_VALIDATION_STAGE_PARTICIPATION[VALIDATION_STAGE_DISPLAY_NAMES.KNIP],
22906
23236
  run: runKnipStage
22907
23237
  },
22908
23238
  {
22909
23239
  name: VALIDATION_STAGE_DISPLAY_NAMES.ESLINT,
22910
23240
  failsPipeline: true,
23241
+ participation: TYPESCRIPT_VALIDATION_STAGE_PARTICIPATION[VALIDATION_STAGE_DISPLAY_NAMES.ESLINT],
22911
23242
  run: (context) => lintCommand({
22912
23243
  cwd: context.cwd,
22913
23244
  scope: context.scope,
@@ -22915,32 +23246,46 @@ var typescriptValidationLanguage = {
22915
23246
  fix: context.fix,
22916
23247
  quiet: context.quiet,
22917
23248
  json: context.json,
23249
+ streamedPipelineOutput: true,
22918
23250
  outputStreams: context.outputStreams
22919
23251
  })
22920
23252
  },
22921
23253
  {
22922
23254
  name: VALIDATION_STAGE_DISPLAY_NAMES.TYPESCRIPT,
22923
23255
  failsPipeline: true,
23256
+ participation: TYPESCRIPT_VALIDATION_STAGE_PARTICIPATION[VALIDATION_STAGE_DISPLAY_NAMES.TYPESCRIPT],
22924
23257
  run: (context) => typescriptCommand({
22925
23258
  cwd: context.cwd,
22926
23259
  scope: context.scope,
22927
23260
  files: context.files,
22928
23261
  quiet: context.quiet,
22929
23262
  json: context.json,
23263
+ streamedPipelineOutput: true,
22930
23264
  outputStreams: context.outputStreams
22931
23265
  })
22932
23266
  },
22933
23267
  {
22934
23268
  name: VALIDATION_STAGE_DISPLAY_NAMES.LITERAL,
22935
23269
  failsPipeline: true,
23270
+ participation: TYPESCRIPT_VALIDATION_STAGE_PARTICIPATION[VALIDATION_STAGE_DISPLAY_NAMES.LITERAL],
22936
23271
  run: runLiteralStage
22937
23272
  }
22938
23273
  ]
22939
23274
  };
22940
23275
 
22941
23276
  // src/validation/registry.ts
23277
+ var VALIDATION_STAGE_PARTICIPATION_POLICIES = {
23278
+ ...TYPESCRIPT_VALIDATION_STAGE_PARTICIPATION,
23279
+ ...MARKDOWN_VALIDATION_STAGE_PARTICIPATION,
23280
+ ...FORMATTING_VALIDATION_STAGE_PARTICIPATION
23281
+ };
23282
+ var VALIDATION_REGISTRY_LANGUAGES = [
23283
+ typescriptValidationLanguage,
23284
+ markdownValidationLanguage,
23285
+ formattingValidationLanguage
23286
+ ];
22942
23287
  var validationRegistry = {
22943
- languages: [typescriptValidationLanguage, markdownValidationLanguage, formattingValidationLanguage]
23288
+ languages: VALIDATION_REGISTRY_LANGUAGES
22944
23289
  };
22945
23290
  function composeValidationPipelineStages(languages) {
22946
23291
  return languages.flatMap((language) => language.stages);
@@ -22974,10 +23319,17 @@ function formatSummary(options) {
22974
23319
  }
22975
23320
 
22976
23321
  // src/commands/validation/all.ts
22977
- function formatStepWithTiming(stepNumber, totalSteps, result, quiet) {
22978
- if (quiet || !result.output) return "";
23322
+ function formatStepWithTiming(stepNumber, totalSteps, stageName, result, quiet) {
23323
+ const output = result.terminalOutput ?? result.output;
23324
+ if (quiet) return "";
23325
+ if (result.terminalOutput === VALIDATION_STREAMED_TERMINAL_OUTPUT) {
23326
+ const verdict = result.exitCode === 0 ? VALIDATION_SYMBOLS.SUCCESS : VALIDATION_SYMBOLS.FAILURE;
23327
+ const timing2 = result.durationMs === void 0 ? "" : ` (${formatDuration(result.durationMs)})`;
23328
+ return `[${stepNumber}/${totalSteps}] ${stageName}: ${verdict} ${VALIDATION_STREAMED_STAGE_RESULT}${timing2}`;
23329
+ }
23330
+ if (!output) return "";
22979
23331
  const timing = result.durationMs === void 0 ? "" : ` (${formatDuration(result.durationMs)})`;
22980
- return `[${stepNumber}/${totalSteps}] ${result.output}${timing}`;
23332
+ return `[${stepNumber}/${totalSteps}] ${output}${timing}`;
22981
23333
  }
22982
23334
  function parseStageOutput(output) {
22983
23335
  if (output.length === 0) return null;
@@ -23011,12 +23363,39 @@ function recordStageResult(options) {
23011
23363
  });
23012
23364
  return;
23013
23365
  }
23014
- const stepOutput = formatStepWithTiming(stepNumber, totalSteps, result, quiet);
23366
+ const stepOutput = formatStepWithTiming(stepNumber, totalSteps, stage.name, result, quiet);
23015
23367
  if (stepOutput) {
23016
23368
  outputs.push(stepOutput);
23017
23369
  writeOutput5?.(stepOutput);
23018
23370
  }
23019
23371
  }
23372
+ function resolveStageParticipation(stage, participationOverrides) {
23373
+ const override = stage.participation.override;
23374
+ if (participationOverrides.has(override.flag)) {
23375
+ const participation = stage.participation.default === VALIDATION_STAGE_PARTICIPATION.RUN ? VALIDATION_STAGE_PARTICIPATION.SKIP : VALIDATION_STAGE_PARTICIPATION.RUN;
23376
+ return {
23377
+ participation,
23378
+ reason: participation === VALIDATION_STAGE_PARTICIPATION.SKIP ? stage.participation.skipReason : void 0,
23379
+ flag: override.flag
23380
+ };
23381
+ }
23382
+ return {
23383
+ participation: stage.participation.default,
23384
+ reason: stage.participation.default === VALIDATION_STAGE_PARTICIPATION.SKIP ? stage.participation.skipReason : void 0
23385
+ };
23386
+ }
23387
+ function skippedStageResult(stage, participation, json, durationMs) {
23388
+ const reason = participation.reason;
23389
+ if (reason === void 0) {
23390
+ throw new Error(`validation stage ${stage.name} skipped without a configured reason`);
23391
+ }
23392
+ return {
23393
+ exitCode: 0,
23394
+ output: json ? formatValidationStageSkipJsonOutput(reason, durationMs) : formatValidationStageSkipOutput(stage.name, participation.flag ?? reason),
23395
+ structuredOutput: json,
23396
+ durationMs
23397
+ };
23398
+ }
23020
23399
  function createCapturedSubprocessOutput() {
23021
23400
  const stdout = [];
23022
23401
  const stderr = [];
@@ -23037,14 +23416,72 @@ function createCaptureStream(chunks) {
23037
23416
  }
23038
23417
  };
23039
23418
  }
23419
+ function resolveFullPipelineStages(validationStages, dependencyStages) {
23420
+ return validationStages ?? dependencyStages ?? validationPipelineStages;
23421
+ }
23422
+ async function executeValidationStages(options) {
23423
+ let hasFailure = false;
23424
+ for (const [index, stage] of options.stages.entries()) {
23425
+ const stepNumber = index + 1;
23426
+ const execution = await executeValidationStage(stage, options);
23427
+ recordStageResult({
23428
+ json: options.json,
23429
+ stepNumber,
23430
+ totalSteps: options.stages.length,
23431
+ stage,
23432
+ result: execution.result,
23433
+ quiet: options.quiet,
23434
+ outputs: options.outputs,
23435
+ jsonSteps: options.jsonSteps,
23436
+ subprocessOutput: execution.subprocessOutput,
23437
+ writeOutput: options.writeOutput
23438
+ });
23439
+ notifyStageCompletion(stage, execution.result, stepNumber, options);
23440
+ hasFailure ||= stage.failsPipeline && execution.result.exitCode !== 0;
23441
+ }
23442
+ return hasFailure;
23443
+ }
23444
+ async function executeValidationStage(stage, options) {
23445
+ const subprocessOutput = options.json ? createCapturedSubprocessOutput() : void 0;
23446
+ const stageStartTime = options.now();
23447
+ const participation = resolveStageParticipation(stage, options.participationOverrides);
23448
+ const stageResult = participation.participation === VALIDATION_STAGE_PARTICIPATION.RUN ? await stage.run({
23449
+ ...options.context,
23450
+ outputStreams: subprocessOutput?.streams ?? options.outputStreams
23451
+ }) : skippedStageResult(stage, participation, options.json, options.now() - stageStartTime);
23452
+ const result = stageResult.durationMs === void 0 ? { ...stageResult, durationMs: options.now() - stageStartTime } : stageResult;
23453
+ return { result, subprocessOutput };
23454
+ }
23455
+ function notifyStageCompletion(stage, result, stepNumber, options) {
23456
+ if (options.json || options.onStageComplete === void 0) return;
23457
+ const output = formatStepWithTiming(stepNumber, options.stages.length, stage.name, result, options.quiet);
23458
+ if (output.length === 0) return;
23459
+ options.onStageComplete({
23460
+ stepNumber,
23461
+ totalSteps: options.stages.length,
23462
+ stageName: stage.name,
23463
+ result,
23464
+ output
23465
+ });
23466
+ }
23040
23467
  async function allCommand(options, deps = {}) {
23041
- const { cwd, scope: scope2, files, fix, quiet = false, json, skipCircular = false, skipLiteral = false } = options;
23042
- const stages = deps.stages ?? validationPipelineStages;
23468
+ const {
23469
+ cwd,
23470
+ scope: scope2,
23471
+ files,
23472
+ fix,
23473
+ quiet = false,
23474
+ json,
23475
+ validationStages,
23476
+ participationOverrides = [],
23477
+ onStageComplete,
23478
+ outputStreams
23479
+ } = options;
23480
+ const stages = resolveFullPipelineStages(validationStages, deps.stages);
23043
23481
  const now = deps.now ?? Date.now;
23044
23482
  const startTime = now();
23045
23483
  const outputs = [];
23046
23484
  const jsonSteps = [];
23047
- let hasFailure = false;
23048
23485
  const context = {
23049
23486
  cwd,
23050
23487
  scope: scope2,
@@ -23052,28 +23489,21 @@ async function allCommand(options, deps = {}) {
23052
23489
  fix,
23053
23490
  quiet: json === true ? false : quiet,
23054
23491
  json,
23055
- skipCircular,
23056
- skipLiteral
23492
+ outputStreams
23057
23493
  };
23058
- let stepNumber = 0;
23059
- for (const stage of stages) {
23060
- stepNumber += 1;
23061
- const subprocessOutput = json === true ? createCapturedSubprocessOutput() : void 0;
23062
- const result = await stage.run({ ...context, outputStreams: subprocessOutput?.streams });
23063
- recordStageResult({
23064
- json: json === true,
23065
- stepNumber,
23066
- totalSteps: stages.length,
23067
- stage,
23068
- result,
23069
- quiet,
23070
- outputs,
23071
- jsonSteps,
23072
- subprocessOutput,
23073
- writeOutput: deps.writeOutput
23074
- });
23075
- if (stage.failsPipeline && result.exitCode !== 0) hasFailure = true;
23076
- }
23494
+ const hasFailure = await executeValidationStages({
23495
+ stages,
23496
+ context,
23497
+ participationOverrides: new Set(participationOverrides),
23498
+ json: json === true,
23499
+ quiet,
23500
+ outputs,
23501
+ jsonSteps,
23502
+ onStageComplete,
23503
+ outputStreams,
23504
+ writeOutput: deps.writeOutput,
23505
+ now
23506
+ });
23077
23507
  const totalDurationMs = now() - startTime;
23078
23508
  if (json === true) {
23079
23509
  const jsonOutput = {
@@ -23086,18 +23516,26 @@ async function allCommand(options, deps = {}) {
23086
23516
  return {
23087
23517
  exitCode: hasFailure ? 1 : 0,
23088
23518
  output,
23519
+ outputTarget: VALIDATION_OUTPUT_TARGET.STDOUT,
23089
23520
  durationMs: totalDurationMs
23090
23521
  };
23091
23522
  }
23523
+ let terminalOutput;
23092
23524
  if (!quiet) {
23093
23525
  const summary = formatSummary({ success: !hasFailure, totalDurationMs });
23094
- outputs.push("", summary);
23095
23526
  deps.writeOutput?.(`
23096
23527
  ${summary}`);
23528
+ if (onStageComplete !== void 0) {
23529
+ terminalOutput = `
23530
+ ${summary}`;
23531
+ } else {
23532
+ outputs.push("", summary);
23533
+ }
23097
23534
  }
23098
23535
  return {
23099
23536
  exitCode: hasFailure ? 1 : 0,
23100
23537
  output: outputs.join("\n"),
23538
+ terminalOutput,
23101
23539
  durationMs: totalDurationMs
23102
23540
  };
23103
23541
  }
@@ -23117,36 +23555,44 @@ var validationCliDefinition = {
23117
23555
  typescript: {
23118
23556
  commandName: "typescript",
23119
23557
  alias: "ts",
23120
- description: "Run TypeScript type checking"
23558
+ description: "Run TypeScript type checking",
23559
+ options: { scope: true, json: false }
23121
23560
  },
23122
23561
  lint: {
23123
23562
  commandName: "lint",
23124
- description: "Run ESLint"
23563
+ description: "Run ESLint",
23564
+ options: { scope: true, json: false }
23125
23565
  },
23126
23566
  circular: {
23127
23567
  commandName: "circular",
23128
- description: "Check for circular dependencies"
23568
+ description: "Check for circular dependencies",
23569
+ options: { scope: true, json: false }
23129
23570
  },
23130
23571
  knip: {
23131
23572
  commandName: "knip",
23132
- description: "Detect unused code"
23573
+ description: "Detect unused code",
23574
+ options: { scope: true, json: false }
23133
23575
  },
23134
23576
  literal: {
23135
23577
  commandName: "literal",
23136
- description: "Detect cross-file literal reuse between source and tests"
23578
+ description: "Detect cross-file literal reuse between source and tests",
23579
+ options: { scope: true, json: true }
23137
23580
  },
23138
23581
  markdown: {
23139
23582
  commandName: "markdown",
23140
23583
  alias: "md",
23141
- description: "Validate markdown link integrity and structure"
23584
+ description: "Validate markdown link integrity and structure",
23585
+ options: { scope: false, json: false }
23142
23586
  },
23143
23587
  format: {
23144
23588
  commandName: "format",
23145
- description: "Check code formatting with dprint"
23589
+ description: "Check code formatting with dprint",
23590
+ options: { scope: false, json: false }
23146
23591
  },
23147
23592
  all: {
23148
23593
  commandName: "all",
23149
- description: "Run all validations"
23594
+ description: "Run all validations",
23595
+ options: { scope: true, json: true }
23150
23596
  }
23151
23597
  },
23152
23598
  commanderHelpOperands: {
@@ -23196,16 +23642,25 @@ var literalValidationCliOptions = {
23196
23642
  description: "Print grouped problem details"
23197
23643
  }
23198
23644
  };
23199
- var allValidationCliOptions = {
23200
- skipCircular: {
23201
- flag: "--skip-circular",
23202
- description: "Skip circular dependency detection for this validation all run"
23645
+ var validationCommonCliOptions = {
23646
+ scope: {
23647
+ flag: "--scope"
23203
23648
  },
23204
- skipLiteral: {
23205
- flag: "--skip-literal",
23206
- description: "Skip literal reuse detection for this validation all run"
23649
+ quiet: {
23650
+ flag: "--quiet"
23651
+ },
23652
+ json: {
23653
+ flag: "--json"
23207
23654
  }
23208
23655
  };
23656
+ var validationAllBuiltInCliOptions = {
23657
+ fix: {
23658
+ flag: "--fix"
23659
+ }
23660
+ };
23661
+ function isValidationLiteralProblemKind(value) {
23662
+ return validationLiteralProblemKinds.some((kind) => kind === value);
23663
+ }
23209
23664
  var validationSubcommandOperands = Object.values(validationCliDefinition.subcommands).flatMap(
23210
23665
  (subcommand) => {
23211
23666
  const operands = [subcommand.commandName];
@@ -23218,6 +23673,7 @@ var validationKnownOperands = /* @__PURE__ */ new Set([
23218
23673
  ...Object.values(validationCliDefinition.commanderHelpOperands)
23219
23674
  ]);
23220
23675
  var validationOptionPrefix = validationCliDefinition.commanderHelpOperands.longFlag.slice(0, 2);
23676
+ var validationShortOptionPrefix = validationCliDefinition.commanderHelpOperands.shortFlag.slice(0, 1);
23221
23677
 
23222
23678
  // src/validation/literal/allowlist-existing.ts
23223
23679
  import { randomBytes as randomBytes3 } from "crypto";
@@ -23304,27 +23760,86 @@ function serializeWithUpdatedInclude(target, include) {
23304
23760
  }
23305
23761
 
23306
23762
  // src/interfaces/cli/validation.ts
23307
- var defaultValidationCliDependencies = {
23308
- allCommand,
23309
- allowlistExisting,
23310
- circularCommand,
23311
- formattingCommand,
23312
- knipCommand,
23313
- lintCommand,
23314
- literalCommand,
23315
- markdownCommand,
23316
- typescriptCommand
23763
+ var LONG_OPTION_PREFIX = "--";
23764
+ var OPTION_PROPERTY_WORD_SEPARATOR_PATTERN = /-([a-z0-9])/g;
23765
+ var VALIDATION_ALL_OVERRIDE_FLAG_PATTERN = /^--(?!no-)[a-z][a-z0-9]*(?:-[a-z0-9]+)*$/u;
23766
+ var validationAllReservedOverrideFlags = /* @__PURE__ */ new Set([
23767
+ ...Object.values(validationCommonCliOptions).map((option) => option.flag),
23768
+ ...Object.values(validationAllBuiltInCliOptions).map((option) => option.flag),
23769
+ validationCliDefinition.commanderHelpOperands.longFlag
23770
+ ]);
23771
+ function validationOptionPropertyName(flag) {
23772
+ return flag.slice(LONG_OPTION_PREFIX.length).replace(OPTION_PROPERTY_WORD_SEPARATOR_PATTERN, (_match, character) => character.toUpperCase());
23773
+ }
23774
+ function deriveValidationAllOverrideCliOptions(stages) {
23775
+ const optionPropertyNames = /* @__PURE__ */ new Set();
23776
+ return stages.flatMap((stage) => {
23777
+ validateStageParticipationMetadata(stage);
23778
+ const override = stage.participation.override;
23779
+ const optionPropertyName = validationOptionPropertyName(override.flag);
23780
+ if (optionPropertyNames.has(optionPropertyName)) {
23781
+ throw new Error(`duplicate validation all override option property: ${optionPropertyName}`);
23782
+ }
23783
+ optionPropertyNames.add(optionPropertyName);
23784
+ return [{
23785
+ stageName: stage.name,
23786
+ flag: override.flag,
23787
+ description: override.description,
23788
+ reason: stage.participation.skipReason,
23789
+ optionPropertyName
23790
+ }];
23791
+ });
23792
+ }
23793
+ function validateStageParticipationMetadata(stage) {
23794
+ if (stage.participation.skipReason.length === 0) {
23795
+ throw new Error(`validation stage ${stage.name} skip participation requires a reason`);
23796
+ }
23797
+ const override = stage.participation.override;
23798
+ if (!VALIDATION_ALL_OVERRIDE_FLAG_PATTERN.test(override.flag)) {
23799
+ throw new Error(`validation stage ${stage.name} override flag must be a bare long kebab-case boolean flag`);
23800
+ }
23801
+ if (validationAllReservedOverrideFlags.has(override.flag)) {
23802
+ throw new Error(`validation stage ${stage.name} override flag collides with a validation all built-in option`);
23803
+ }
23804
+ if (override.description.length === 0) {
23805
+ throw new Error(`validation stage ${stage.name} override flag requires a description`);
23806
+ }
23807
+ }
23808
+ var validationAllOverrideCliOptions = deriveValidationAllOverrideCliOptions(validationPipelineStages);
23809
+ var defaultValidationCommandHandlers = {
23810
+ typescript: typescriptCommand,
23811
+ lint: lintCommand,
23812
+ circular: circularCommand,
23813
+ knip: knipCommand,
23814
+ literal: literalCommand,
23815
+ markdown: markdownCommand,
23816
+ format: formattingCommand,
23817
+ all: allCommand
23317
23818
  };
23318
23819
  function emitValidationResult(result, io) {
23319
- if (result.output.length > 0) {
23320
- io.writeStdout(`${result.output}
23820
+ const output = result.terminalOutput ?? result.output;
23821
+ if (output.length > 0) {
23822
+ const outputTarget = result.outputTarget ?? (result.exitCode === 0 ? VALIDATION_OUTPUT_TARGET.STDOUT : VALIDATION_OUTPUT_TARGET.STDERR);
23823
+ const writeOutput5 = outputTarget === VALIDATION_OUTPUT_TARGET.STDOUT ? io.writeStdout : io.writeStderr;
23824
+ writeOutput5(`${output}
23321
23825
  `);
23322
23826
  }
23323
23827
  return io.exit(result.exitCode);
23324
23828
  }
23325
- function addCommonOptions(cmd) {
23829
+ function addCommonOptions(cmd, definition) {
23326
23830
  const { pathOperands } = validationCliDefinition;
23327
- return cmd.argument(pathOperands.optionalVariadic, pathOperands.description).option("--scope <scope>", "Validation scope (full|production)", "full").option("--quiet", "Suppress progress output").option("--json", "Output results as JSON");
23831
+ let configured = cmd.argument(pathOperands.optionalVariadic, pathOperands.description).option(validationCommonCliOptions.quiet.flag, "Suppress progress output");
23832
+ if (definition.options.scope) {
23833
+ configured = configured.option(
23834
+ `${validationCommonCliOptions.scope.flag} <scope>`,
23835
+ "Validation scope (full|production)",
23836
+ "full"
23837
+ );
23838
+ }
23839
+ if (definition.options.json) {
23840
+ configured = configured.option(validationCommonCliOptions.json.flag, "Output results as JSON");
23841
+ }
23842
+ return configured;
23328
23843
  }
23329
23844
  async function normalizeProductPathOperand(productDir, effectiveInvocationDir, operand) {
23330
23845
  const resolvedProductDir = await canonicalPathThroughExistingAncestor(resolve16(productDir));
@@ -23384,57 +23899,65 @@ function addValidationSubcommand(validationCmd, definition) {
23384
23899
  }
23385
23900
  return subcommand;
23386
23901
  }
23387
- function registerValidationCommands(validationCmd, invocation, deps) {
23902
+ function selectedValidationAllOverrides(options, allOverrideCliOptions = validationAllOverrideCliOptions) {
23903
+ return allOverrideCliOptions.filter((option) => options[option.optionPropertyName] === true).map((option) => option.flag);
23904
+ }
23905
+ function registerValidationCommands(validationCmd, invocation, options = {}) {
23388
23906
  const { subcommands } = validationCliDefinition;
23389
- const tsCmd = addValidationSubcommand(validationCmd, subcommands.typescript).action(async (pathOperands, options) => {
23907
+ const commandHandlers = {
23908
+ ...defaultValidationCommandHandlers,
23909
+ ...options.commandHandlers
23910
+ };
23911
+ const runAllowlistExisting = options.allowlistExisting ?? allowlistExisting;
23912
+ const validationStages = options.validationStages ?? validationPipelineStages;
23913
+ const allOverrideCliOptions = deriveValidationAllOverrideCliOptions(
23914
+ validationStages
23915
+ );
23916
+ const tsCmd = addValidationSubcommand(validationCmd, subcommands.typescript).action(async (pathOperands, options2) => {
23390
23917
  const paths = await resolveValidationPaths(invocation, pathOperands);
23391
- const result = await deps.typescriptCommand({
23918
+ const result = await commandHandlers.typescript({
23392
23919
  cwd: paths.productDir,
23393
- scope: options.scope,
23920
+ scope: options2.scope,
23394
23921
  files: paths.files,
23395
- quiet: options.quiet,
23396
- json: options.json
23922
+ quiet: options2.quiet
23397
23923
  });
23398
23924
  emitValidationResult(result, invocation.io);
23399
23925
  });
23400
- addCommonOptions(tsCmd);
23401
- const lintCmd = addValidationSubcommand(validationCmd, subcommands.lint).option("--fix", "Auto-fix issues").action(async (pathOperands, options) => {
23926
+ addCommonOptions(tsCmd, subcommands.typescript);
23927
+ const lintCmd = addValidationSubcommand(validationCmd, subcommands.lint).option("--fix", "Auto-fix issues").action(async (pathOperands, options2) => {
23402
23928
  const paths = await resolveValidationPaths(invocation, pathOperands);
23403
- const result = await deps.lintCommand({
23929
+ const result = await commandHandlers.lint({
23404
23930
  cwd: paths.productDir,
23405
- scope: options.scope,
23931
+ scope: options2.scope,
23406
23932
  files: paths.files,
23407
- fix: options.fix,
23408
- quiet: options.quiet,
23409
- json: options.json
23933
+ fix: options2.fix,
23934
+ quiet: options2.quiet
23410
23935
  });
23411
23936
  emitValidationResult(result, invocation.io);
23412
23937
  });
23413
- addCommonOptions(lintCmd);
23414
- const circularCmd = addValidationSubcommand(validationCmd, subcommands.circular).action(async (pathOperands, options) => {
23938
+ addCommonOptions(lintCmd, subcommands.lint);
23939
+ const circularCmd = addValidationSubcommand(validationCmd, subcommands.circular).action(async (pathOperands, options2) => {
23415
23940
  const paths = await resolveValidationPaths(invocation, pathOperands);
23416
- const result = await deps.circularCommand({
23941
+ const result = await commandHandlers.circular({
23417
23942
  cwd: paths.productDir,
23418
- scope: options.scope,
23943
+ scope: options2.scope,
23419
23944
  files: paths.files,
23420
- quiet: options.quiet,
23421
- json: options.json
23945
+ quiet: options2.quiet
23422
23946
  });
23423
23947
  emitValidationResult(result, invocation.io);
23424
23948
  });
23425
- addCommonOptions(circularCmd);
23426
- const knipCmd = addValidationSubcommand(validationCmd, subcommands.knip).action(async (pathOperands, options) => {
23949
+ addCommonOptions(circularCmd, subcommands.circular);
23950
+ const knipCmd = addValidationSubcommand(validationCmd, subcommands.knip).action(async (pathOperands, options2) => {
23427
23951
  const paths = await resolveValidationPaths(invocation, pathOperands);
23428
- const result = await deps.knipCommand({
23952
+ const result = await commandHandlers.knip({
23429
23953
  cwd: paths.productDir,
23430
- scope: options.scope,
23954
+ scope: options2.scope,
23431
23955
  files: paths.files,
23432
- quiet: options.quiet,
23433
- json: options.json
23956
+ quiet: options2.quiet
23434
23957
  });
23435
23958
  emitValidationResult(result, invocation.io);
23436
23959
  });
23437
- addCommonOptions(knipCmd);
23960
+ addCommonOptions(knipCmd, subcommands.knip);
23438
23961
  const literalCmd = addValidationSubcommand(validationCmd, subcommands.literal).option(
23439
23962
  literalValidationCliOptions.allowlistExisting.flag,
23440
23963
  literalValidationCliOptions.allowlistExisting.description
@@ -23444,106 +23967,127 @@ function registerValidationCommands(validationCmd, invocation, deps) {
23444
23967
  ).option(literalValidationCliOptions.literals.flag, literalValidationCliOptions.literals.description).option(literalValidationCliOptions.verbose.flag, literalValidationCliOptions.verbose.description).addHelpText(
23445
23968
  "after",
23446
23969
  "\nEnabled for TypeScript projects by default. Set validation.literal.enabled=false\nin spx.config.* to skip during migration."
23447
- ).action(async (pathOperands, options) => {
23970
+ ).action(async (pathOperands, options2) => {
23448
23971
  const paths = await resolveValidationPaths(invocation, pathOperands);
23449
- if (options.allowlistExisting) {
23450
- const result2 = await deps.allowlistExisting({ productDir: paths.productDir });
23972
+ if (options2.allowlistExisting) {
23973
+ const result2 = await runAllowlistExisting({ productDir: paths.productDir });
23451
23974
  emitValidationResult(result2, invocation.io);
23452
23975
  }
23453
23976
  let kind;
23454
- if (options.kind !== void 0) {
23455
- kind = parseLiteralProblemKind(options.kind);
23977
+ if (options2.kind !== void 0) {
23978
+ kind = parseLiteralProblemKind(options2.kind);
23456
23979
  if (kind === void 0) {
23457
23980
  const { unknownLiteralProblemKind } = validationCliDefinition.diagnostics;
23458
23981
  invocation.io.writeStderr(
23459
- `spx validation literal: ${unknownLiteralProblemKind.messageLabel}: ${sanitizeCliArgument(options.kind)}
23982
+ `spx validation literal: ${unknownLiteralProblemKind.messageLabel}: ${sanitizeCliArgument(options2.kind)}
23460
23983
  `
23461
23984
  );
23462
23985
  invocation.io.exit(unknownLiteralProblemKind.exitCode);
23463
23986
  }
23464
23987
  }
23465
- const result = await deps.literalCommand({
23988
+ const result = await commandHandlers.literal({
23466
23989
  cwd: paths.productDir,
23467
- scope: options.scope,
23990
+ scope: options2.scope,
23468
23991
  files: paths.files,
23469
23992
  kind,
23470
- filesWithProblems: options.filesWithProblems,
23471
- literals: options.literals,
23472
- verbose: options.verbose,
23473
- quiet: options.quiet,
23474
- json: options.json
23993
+ filesWithProblems: options2.filesWithProblems,
23994
+ literals: options2.literals,
23995
+ verbose: options2.verbose,
23996
+ quiet: options2.quiet,
23997
+ json: options2.json
23475
23998
  });
23476
23999
  emitValidationResult(result, invocation.io);
23477
24000
  });
23478
- addCommonOptions(literalCmd);
24001
+ addCommonOptions(literalCmd, subcommands.literal);
23479
24002
  const markdownCmd = addValidationSubcommand(validationCmd, subcommands.markdown).addHelpText(
23480
24003
  "after",
23481
24004
  "\nValidates spx/ and docs/ by default. For nodes listed in spx/EXCLUDE,\nonly direct markdown files in that node directory are skipped;\nchild-node markdown remains in scope."
23482
- ).action(async (pathOperands, options) => {
24005
+ ).action(async (pathOperands, options2) => {
23483
24006
  const paths = await resolveValidationPaths(invocation, pathOperands);
23484
- const result = await deps.markdownCommand({
24007
+ const result = await commandHandlers.markdown({
23485
24008
  cwd: paths.productDir,
23486
24009
  files: paths.files,
23487
- quiet: options.quiet
24010
+ quiet: options2.quiet
23488
24011
  });
23489
24012
  emitValidationResult(result, invocation.io);
23490
24013
  });
23491
- addCommonOptions(markdownCmd);
23492
- const formatCmd = addValidationSubcommand(validationCmd, subcommands.format).action(async (pathOperands, options) => {
24014
+ addCommonOptions(markdownCmd, subcommands.markdown);
24015
+ const formatCmd = addValidationSubcommand(validationCmd, subcommands.format).action(async (pathOperands, options2) => {
23493
24016
  const paths = await resolveValidationPaths(invocation, pathOperands);
23494
- const result = await deps.formattingCommand({
24017
+ const result = await commandHandlers.format({
23495
24018
  cwd: paths.productDir,
23496
24019
  files: paths.files,
23497
- quiet: options.quiet
24020
+ quiet: options2.quiet
23498
24021
  });
23499
24022
  emitValidationResult(result, invocation.io);
23500
24023
  });
23501
- addCommonOptions(formatCmd);
23502
- 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 (pathOperands, options) => {
24024
+ addCommonOptions(formatCmd, subcommands.format);
24025
+ let allCmd = addValidationSubcommand(validationCmd, subcommands.all).option(validationAllBuiltInCliOptions.fix.flag, "Auto-fix ESLint issues");
24026
+ for (const option of allOverrideCliOptions) {
24027
+ allCmd = allCmd.option(option.flag, option.description);
24028
+ }
24029
+ allCmd = allCmd.action(async (pathOperands, options2) => {
23503
24030
  const paths = await resolveValidationPaths(invocation, pathOperands);
23504
- const result = await deps.allCommand({
24031
+ const result = await commandHandlers.all({
23505
24032
  cwd: paths.productDir,
23506
- scope: options.scope,
24033
+ scope: options2.scope,
23507
24034
  files: paths.files,
23508
- fix: options.fix,
23509
- skipCircular: options.skipCircular,
23510
- skipLiteral: options.skipLiteral,
23511
- quiet: options.quiet,
23512
- json: options.json
23513
- }, {
23514
- writeOutput: (output) => invocation.io.writeStdout(`${output}
23515
- `)
24035
+ fix: options2.fix,
24036
+ validationStages,
24037
+ participationOverrides: selectedValidationAllOverrides(options2, allOverrideCliOptions),
24038
+ quiet: options2.quiet,
24039
+ json: options2.json,
24040
+ onStageComplete: ({ output }) => invocation.io.writeStdout(`${output}
24041
+ `),
24042
+ outputStreams: validationSubprocessOutputStreams(invocation.io, options2.json)
23516
24043
  });
23517
- return invocation.io.exit(result.exitCode);
24044
+ emitValidationResult(result, invocation.io);
23518
24045
  });
23519
- addCommonOptions(allCmd);
24046
+ addCommonOptions(allCmd, subcommands.all);
24047
+ }
24048
+ function validationSubprocessOutputStreams(io, json) {
24049
+ if (json === true) return discardValidationSubprocessOutputStreams;
24050
+ return {
24051
+ stdout: {
24052
+ write: (chunk) => {
24053
+ io.writeStdout(Buffer.from(chunk).toString());
24054
+ return true;
24055
+ }
24056
+ },
24057
+ stderr: {
24058
+ write: (chunk) => {
24059
+ io.writeStderr(Buffer.from(chunk).toString());
24060
+ return true;
24061
+ }
24062
+ }
24063
+ };
23520
24064
  }
23521
24065
  function parseLiteralProblemKind(value) {
23522
- return validationLiteralProblemKinds.find((problemKind) => problemKind === value);
24066
+ if (isValidationLiteralProblemKind(value)) {
24067
+ return value;
24068
+ }
24069
+ return void 0;
23523
24070
  }
23524
24071
  function handleUnknownSubcommand(operands, io) {
23525
24072
  const [first] = operands;
23526
24073
  const sanitized = sanitizeCliArgument(first);
23527
24074
  const { domain, diagnostics } = validationCliDefinition;
23528
24075
  const { unknownSubcommand } = diagnostics;
23529
- io.writeStderr(
23530
- `${SPX_PROGRAM_NAME} ${domain.commandName}: ${unknownSubcommand.messageLabel}: ${sanitized}
23531
- `
23532
- );
24076
+ io.writeStderr(`${SPX_PROGRAM_NAME} ${domain.commandName}: ${unknownSubcommand.messageLabel}: ${sanitized}
24077
+ `);
23533
24078
  return io.exit(unknownSubcommand.exitCode);
23534
24079
  }
23535
- function createValidationDomain(overrides = {}) {
23536
- const deps = { ...defaultValidationCliDependencies, ...overrides };
24080
+ function createValidationDomain(options = {}) {
23537
24081
  return {
23538
24082
  name: validationCliDefinition.domain.commandName,
23539
24083
  description: validationCliDefinition.domain.description,
23540
24084
  register: (program, invocation) => {
23541
24085
  const { domain } = validationCliDefinition;
23542
- const validationCmd = program.command(domain.commandName).alias(domain.alias).description(domain.description);
24086
+ const validationCmd = program.command(domain.commandName).alias(domain.alias).description(domain.description).addHelpCommand(false);
23543
24087
  validationCmd.on("command:*", (operands) => {
23544
24088
  handleUnknownSubcommand(operands, invocation.io);
23545
24089
  });
23546
- registerValidationCommands(validationCmd, invocation, deps);
24090
+ registerValidationCommands(validationCmd, invocation, options);
23547
24091
  }
23548
24092
  };
23549
24093
  }
@@ -23850,6 +24394,10 @@ var REVIEW_TERMINAL_STATE = {
23850
24394
  CHANGES_REQUESTED: "changes_requested",
23851
24395
  COMMENTED: "commented"
23852
24396
  };
24397
+ var REVIEW_TERMINAL_STATUSES = /* @__PURE__ */ new Set([
24398
+ JOURNAL_RUN_STATE_STATUS.APPROVED,
24399
+ JOURNAL_RUN_STATE_STATUS.REJECTED
24400
+ ]);
23853
24401
  var AUDIT_CLASS = {
23854
24402
  IMPLEMENTATION: "implementation",
23855
24403
  INSTRUCTIONS: "instructions",
@@ -24154,6 +24702,9 @@ function validateReviewTerminal(input) {
24154
24702
  if (input.metadata !== void 0 && validated === void 0) {
24155
24703
  return { ok: false, error: TERMINAL_METADATA_VALIDATION_ERROR.METADATA_INVALID };
24156
24704
  }
24705
+ if (!REVIEW_TERMINAL_STATUSES.has(input.terminalStatus)) {
24706
+ return { ok: false, error: TERMINAL_METADATA_VALIDATION_ERROR.STATUS_CONFLICT };
24707
+ }
24157
24708
  const evidenceStatus = expectedReviewEvidenceTerminalStatus(input.events);
24158
24709
  const metadataStatus = expectedReviewMetadataTerminalStatus(validated);
24159
24710
  if (evidenceStatus !== void 0 && metadataStatus !== void 0 && evidenceStatus !== metadataStatus) {
@@ -25685,7 +26236,7 @@ function createCliProgram(options = {}) {
25685
26236
 
25686
26237
  // src/cli.ts
25687
26238
  installLifecycle();
25688
- var require3 = createRequire2(import.meta.url);
26239
+ var require3 = createRequire3(import.meta.url);
25689
26240
  var { version } = require3("../package.json");
25690
26241
  createCliProgram({ version }).parse();
25691
26242
  //# sourceMappingURL=cli.js.map