@outcomeeng/spx 0.6.14 → 0.6.15

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/cli.js CHANGED
@@ -4,11 +4,6 @@ import { createRequire as createRequire2 } from "module";
4
4
  // src/interfaces/cli/program.ts
5
5
  import { Command } from "commander";
6
6
 
7
- // src/domains/config/cwd.ts
8
- var CONFIG_PROCESS_CWD = {
9
- read: () => process.cwd()
10
- };
11
-
12
7
  // src/domains/config/root.ts
13
8
  import { execFileSync } from "child_process";
14
9
  import { resolve } from "path";
@@ -41,6 +36,11 @@ var DEFAULT_PRODUCT_DIR_RESOLVER_DEPS = {
41
36
  }
42
37
  };
43
38
 
39
+ // src/lib/config/cwd.ts
40
+ var CONFIG_PROCESS_CWD = {
41
+ read: () => process.cwd()
42
+ };
43
+
44
44
  // src/interfaces/cli/product-context.ts
45
45
  import { resolve as resolve2 } from "path";
46
46
  var SPX_GLOBAL_OPTIONS = {
@@ -1720,11 +1720,11 @@ function compareSearchResults(left, right) {
1720
1720
  return `${left.agent}:${left.sessionId}`.localeCompare(`${right.agent}:${right.sessionId}`);
1721
1721
  }
1722
1722
 
1723
- // src/git/root.ts
1723
+ // src/lib/git/root.ts
1724
1724
  import { execa } from "execa";
1725
1725
  import { basename, dirname, isAbsolute as isAbsolute2, join, resolve as resolve5 } from "path";
1726
1726
 
1727
- // src/git/environment.ts
1727
+ // src/lib/git/environment.ts
1728
1728
  function withoutGitEnvironment(env) {
1729
1729
  const cleaned = { ...env };
1730
1730
  for (const key of Object.keys(cleaned)) {
@@ -1735,7 +1735,7 @@ function withoutGitEnvironment(env) {
1735
1735
  return cleaned;
1736
1736
  }
1737
1737
 
1738
- // src/git/root.ts
1738
+ // src/lib/git/root.ts
1739
1739
  var defaultGitDependencies = {
1740
1740
  execa: async (command, args, options) => {
1741
1741
  const result = await execa(command, args, {
@@ -6543,11 +6543,17 @@ function spxReachabilityRunner(probe) {
6543
6543
  var WORKTREE_POOL_VERDICT = {
6544
6544
  COMPLIANT: "compliant",
6545
6545
  NON_COMPLIANT: "non-compliant",
6546
+ MAIN_CHECKOUT_MISSING: "main-checkout-missing",
6547
+ MAIN_CHECKOUT_DETACHED: "main-checkout-detached",
6548
+ MAIN_CHECKOUT_WRONG_BRANCH: "main-checkout-wrong-branch",
6546
6549
  UNKNOWN: "unknown"
6547
6550
  };
6548
6551
  var REMEDIATION5 = {
6549
6552
  [WORKTREE_POOL_VERDICT.COMPLIANT]: "Worktree layout is compliant; no action needed.",
6550
6553
  [WORKTREE_POOL_VERDICT.NON_COMPLIANT]: "Linked worktrees require a bare-repository pool; convert the layout or remove the linked worktrees.",
6554
+ [WORKTREE_POOL_VERDICT.MAIN_CHECKOUT_MISSING]: "Create the repository-named main checkout beside the bare repository and attach it to the default branch.",
6555
+ [WORKTREE_POOL_VERDICT.MAIN_CHECKOUT_DETACHED]: "Attach the designated main checkout to the repository default branch.",
6556
+ [WORKTREE_POOL_VERDICT.MAIN_CHECKOUT_WRONG_BRANCH]: "Switch the designated main checkout to the repository default branch.",
6551
6557
  [WORKTREE_POOL_VERDICT.UNKNOWN]: "Re-run diagnose; if it persists, inspect git worktree list and occupancy claims."
6552
6558
  };
6553
6559
  function record6(verdict, bucket, reading2) {
@@ -6558,6 +6564,10 @@ function record6(verdict, bucket, reading2) {
6558
6564
  readings: {
6559
6565
  bare: String(reading2.bareRepository),
6560
6566
  linked: String(reading2.linkedWorktrees),
6567
+ mainCheckoutPath: reading2.mainCheckoutPath ?? "",
6568
+ defaultBranch: reading2.defaultBranch ?? "",
6569
+ mainCheckoutBranch: reading2.mainCheckoutBranch ?? "",
6570
+ mainCheckoutBranchRead: String(reading2.mainCheckoutBranchRead),
6561
6571
  running: String(reading2.running),
6562
6572
  free: String(reading2.free)
6563
6573
  },
@@ -6571,6 +6581,20 @@ function classifyWorktreePool(reading2) {
6571
6581
  if (!reading2.bareRepository && reading2.linkedWorktrees) {
6572
6582
  return record6(WORKTREE_POOL_VERDICT.NON_COMPLIANT, VERDICT_BUCKET.BROKEN, reading2);
6573
6583
  }
6584
+ if (reading2.bareRepository) {
6585
+ if (reading2.mainCheckoutPath === null) {
6586
+ return record6(WORKTREE_POOL_VERDICT.MAIN_CHECKOUT_MISSING, VERDICT_BUCKET.BROKEN, reading2);
6587
+ }
6588
+ if (reading2.defaultBranch === null || !reading2.mainCheckoutBranchRead) {
6589
+ return record6(WORKTREE_POOL_VERDICT.UNKNOWN, VERDICT_BUCKET.UNKNOWN, reading2);
6590
+ }
6591
+ if (reading2.mainCheckoutBranch === null) {
6592
+ return record6(WORKTREE_POOL_VERDICT.MAIN_CHECKOUT_DETACHED, VERDICT_BUCKET.BROKEN, reading2);
6593
+ }
6594
+ if (reading2.mainCheckoutBranch !== reading2.defaultBranch) {
6595
+ return record6(WORKTREE_POOL_VERDICT.MAIN_CHECKOUT_WRONG_BRANCH, VERDICT_BUCKET.BROKEN, reading2);
6596
+ }
6597
+ }
6574
6598
  return record6(WORKTREE_POOL_VERDICT.COMPLIANT, VERDICT_BUCKET.HEALTHY, reading2);
6575
6599
  }
6576
6600
  function worktreePoolRunner(probe) {
@@ -6638,6 +6662,26 @@ function resolveColorChoice(choice) {
6638
6662
  return choice.isTty;
6639
6663
  }
6640
6664
 
6665
+ // src/domains/diagnose/report-contract.ts
6666
+ var BUCKET_SEVERITY = {
6667
+ [VERDICT_BUCKET.HEALTHY]: SEVERITY.OK,
6668
+ [VERDICT_BUCKET.DEGRADED]: SEVERITY.WARN,
6669
+ [VERDICT_BUCKET.UNKNOWN]: SEVERITY.UNKNOWN,
6670
+ [VERDICT_BUCKET.BROKEN]: SEVERITY.ERROR,
6671
+ [VERDICT_BUCKET.NOT_APPLICABLE]: SEVERITY.MUTED
6672
+ };
6673
+ var OVERALL_SEVERITY = {
6674
+ [OVERALL_VERDICT.HEALTHY]: SEVERITY.OK,
6675
+ [OVERALL_VERDICT.DEGRADED]: SEVERITY.WARN,
6676
+ [OVERALL_VERDICT.UNKNOWN]: SEVERITY.UNKNOWN,
6677
+ [OVERALL_VERDICT.BROKEN]: SEVERITY.ERROR
6678
+ };
6679
+ var CANONICAL_CHECKOUT_PROBLEM = {
6680
+ [WORKTREE_POOL_VERDICT.MAIN_CHECKOUT_DETACHED]: "The designated main checkout is not attached to any branch.",
6681
+ [WORKTREE_POOL_VERDICT.MAIN_CHECKOUT_MISSING]: "The designated main checkout could not be found.",
6682
+ [WORKTREE_POOL_VERDICT.MAIN_CHECKOUT_WRONG_BRANCH]: "The designated main checkout is attached to the wrong branch."
6683
+ };
6684
+
6641
6685
  // src/domains/diagnose/report.ts
6642
6686
  var DIAGNOSE_FORMAT = {
6643
6687
  JSON: "json",
@@ -6707,19 +6751,6 @@ var DIAGNOSE_TEXT_DETAIL = {
6707
6751
  UNREGISTERED_MARKETPLACE_FIX: "Register the configured plugin marketplace.",
6708
6752
  WORKTREE_POOL_VALID: "Layout is valid for shared session work."
6709
6753
  };
6710
- var BUCKET_SEVERITY = {
6711
- [VERDICT_BUCKET.HEALTHY]: SEVERITY.OK,
6712
- [VERDICT_BUCKET.DEGRADED]: SEVERITY.WARN,
6713
- [VERDICT_BUCKET.UNKNOWN]: SEVERITY.UNKNOWN,
6714
- [VERDICT_BUCKET.BROKEN]: SEVERITY.ERROR,
6715
- [VERDICT_BUCKET.NOT_APPLICABLE]: SEVERITY.MUTED
6716
- };
6717
- var OVERALL_SEVERITY = {
6718
- [OVERALL_VERDICT.HEALTHY]: SEVERITY.OK,
6719
- [OVERALL_VERDICT.DEGRADED]: SEVERITY.WARN,
6720
- [OVERALL_VERDICT.UNKNOWN]: SEVERITY.UNKNOWN,
6721
- [OVERALL_VERDICT.BROKEN]: SEVERITY.ERROR
6722
- };
6723
6754
  function renderReportJson(report2) {
6724
6755
  return JSON.stringify(
6725
6756
  {
@@ -6885,6 +6916,30 @@ function worktreePoolText(check) {
6885
6916
  `${DIAGNOSE_TEXT_LABEL.FIX}: convert to a bare-repository worktree pool or remove the linked worktrees.`
6886
6917
  ]
6887
6918
  };
6919
+ case WORKTREE_POOL_VERDICT.MAIN_CHECKOUT_MISSING:
6920
+ return {
6921
+ header: DIAGNOSE_TEXT_HEADER.WORKTREE_POOL_INVALID,
6922
+ details: [
6923
+ `${DIAGNOSE_TEXT_LABEL.PROBLEM}: ${CANONICAL_CHECKOUT_PROBLEM[WORKTREE_POOL_VERDICT.MAIN_CHECKOUT_MISSING]}`,
6924
+ `${DIAGNOSE_TEXT_LABEL.FIX}: ${check.remediation}`
6925
+ ]
6926
+ };
6927
+ case WORKTREE_POOL_VERDICT.MAIN_CHECKOUT_DETACHED:
6928
+ return {
6929
+ header: DIAGNOSE_TEXT_HEADER.WORKTREE_POOL_INVALID,
6930
+ details: [
6931
+ `${DIAGNOSE_TEXT_LABEL.PROBLEM}: ${CANONICAL_CHECKOUT_PROBLEM[WORKTREE_POOL_VERDICT.MAIN_CHECKOUT_DETACHED]}`,
6932
+ `${DIAGNOSE_TEXT_LABEL.FIX}: ${check.remediation}`
6933
+ ]
6934
+ };
6935
+ case WORKTREE_POOL_VERDICT.MAIN_CHECKOUT_WRONG_BRANCH:
6936
+ return {
6937
+ header: DIAGNOSE_TEXT_HEADER.WORKTREE_POOL_INVALID,
6938
+ details: [
6939
+ `${DIAGNOSE_TEXT_LABEL.PROBLEM}: ${CANONICAL_CHECKOUT_PROBLEM[WORKTREE_POOL_VERDICT.MAIN_CHECKOUT_WRONG_BRANCH]}`,
6940
+ `${DIAGNOSE_TEXT_LABEL.FIX}: ${check.remediation}`
6941
+ ]
6942
+ };
6888
6943
  case WORKTREE_POOL_VERDICT.UNKNOWN:
6889
6944
  return {
6890
6945
  header: DIAGNOSE_TEXT_HEADER.WORKTREE_POOL_UNKNOWN,
@@ -7670,9 +7725,31 @@ var DIAGNOSE_DOING_SESSION_ARGS = ["session", "list", "--status", "doing", "--js
7670
7725
  var PLUGIN_CACHE_SEGMENTS = ["plugins", "cache"];
7671
7726
  var NOT_FOUND_ERROR_CODE = "ENOENT";
7672
7727
  var VERSION_DIRECTORY_PATTERN = /^\d+(?:\.\d+)*$/;
7728
+ var MAIN_CHECKOUT_SYMBOLIC_REF_ARGS = [
7729
+ GIT_ROOT_COMMAND.SYMBOLIC_REF,
7730
+ GIT_ROOT_COMMAND.QUIET,
7731
+ GIT_ROOT_COMMAND.SHORT,
7732
+ GIT_ROOT_COMMAND.HEAD
7733
+ ];
7734
+ async function readMainCheckoutBranch(path7) {
7735
+ try {
7736
+ const result = await defaultGitDependencies.execa(
7737
+ GIT_ROOT_COMMAND.EXECUTABLE,
7738
+ [...MAIN_CHECKOUT_SYMBOLIC_REF_ARGS],
7739
+ { cwd: path7, reject: false }
7740
+ );
7741
+ if (result.exitCode === 0) return { read: true, branch: result.stdout.trim() };
7742
+ if (result.exitCode === 1) return { read: true, branch: null };
7743
+ return { read: false, branch: null };
7744
+ } catch {
7745
+ return { read: false, branch: null };
7746
+ }
7747
+ }
7673
7748
  var defaultWorktreePoolSnapshotDependencies = {
7674
7749
  env: process.env,
7675
7750
  gatherGitFacts,
7751
+ resolveDefaultBranch,
7752
+ readMainCheckoutBranch,
7676
7753
  fs: defaultOccupancyFileSystem,
7677
7754
  processTable: defaultProcessTable
7678
7755
  };
@@ -7695,6 +7772,10 @@ function erroredWorktreePoolSnapshot() {
7695
7772
  errored: true,
7696
7773
  bareRepository: false,
7697
7774
  linkedWorktrees: false,
7775
+ mainCheckoutPath: null,
7776
+ defaultBranch: null,
7777
+ mainCheckoutBranch: null,
7778
+ mainCheckoutBranchRead: false,
7698
7779
  worktrees: [],
7699
7780
  currentWorktreeRoot: null,
7700
7781
  liveClaimSessionIds: /* @__PURE__ */ new Set()
@@ -7708,9 +7789,29 @@ function parseDoingSessions(stdout) {
7708
7789
  return null;
7709
7790
  }
7710
7791
  }
7792
+ async function gatherMainCheckoutStanding(facts, deps) {
7793
+ const designatedMainCheckout = mainCheckoutPath(facts);
7794
+ if (!facts.commonDirIsBare) {
7795
+ return {
7796
+ mainCheckoutPath: designatedMainCheckout,
7797
+ defaultBranch: null,
7798
+ mainCheckoutBranch: null,
7799
+ mainCheckoutBranchRead: true
7800
+ };
7801
+ }
7802
+ const defaultBranch = await (deps.resolveDefaultBranch ?? resolveDefaultBranch)();
7803
+ const branchReading = designatedMainCheckout === null ? { read: true, branch: null } : await (deps.readMainCheckoutBranch ?? readMainCheckoutBranch)(designatedMainCheckout);
7804
+ return {
7805
+ mainCheckoutPath: designatedMainCheckout,
7806
+ defaultBranch,
7807
+ mainCheckoutBranch: branchReading.branch,
7808
+ mainCheckoutBranchRead: branchReading.read
7809
+ };
7810
+ }
7711
7811
  async function gatherWorktreePoolSnapshot(deps = defaultWorktreePoolSnapshotDependencies) {
7712
7812
  const facts = await deps.gatherGitFacts();
7713
7813
  if (!facts?.worktreeListRead) return erroredWorktreePoolSnapshot();
7814
+ const mainCheckoutStanding = await gatherMainCheckoutStanding(facts, deps);
7714
7815
  const worktreesDir = worktreesScopeDir(dirname3(facts.commonDir));
7715
7816
  const worktrees = [];
7716
7817
  const liveClaimSessionIds = /* @__PURE__ */ new Set();
@@ -7739,6 +7840,7 @@ async function gatherWorktreePoolSnapshot(deps = defaultWorktreePoolSnapshotDepe
7739
7840
  errored: false,
7740
7841
  bareRepository: facts.commonDirIsBare,
7741
7842
  linkedWorktrees: !facts.commonDirIsBare && facts.worktreeRoots.length > 1,
7843
+ ...mainCheckoutStanding,
7742
7844
  worktrees,
7743
7845
  currentWorktreeRoot: facts.worktreeRoot,
7744
7846
  liveClaimSessionIds
@@ -7750,6 +7852,10 @@ function worktreePoolReadingFromSnapshot(snapshot) {
7750
7852
  errored: true,
7751
7853
  bareRepository: false,
7752
7854
  linkedWorktrees: false,
7855
+ mainCheckoutPath: null,
7856
+ defaultBranch: null,
7857
+ mainCheckoutBranch: null,
7858
+ mainCheckoutBranchRead: false,
7753
7859
  running: 0,
7754
7860
  free: 0
7755
7861
  };
@@ -7764,6 +7870,10 @@ function worktreePoolReadingFromSnapshot(snapshot) {
7764
7870
  errored: false,
7765
7871
  bareRepository: snapshot.bareRepository,
7766
7872
  linkedWorktrees: snapshot.linkedWorktrees,
7873
+ mainCheckoutPath: snapshot.mainCheckoutPath,
7874
+ defaultBranch: snapshot.defaultBranch,
7875
+ mainCheckoutBranch: snapshot.mainCheckoutBranch,
7876
+ mainCheckoutBranchRead: snapshot.mainCheckoutBranchRead,
7767
7877
  running,
7768
7878
  free
7769
7879
  };
@@ -12326,7 +12436,7 @@ var sessionDomain = {
12326
12436
  import { access } from "fs/promises";
12327
12437
  import { join as join16 } from "path";
12328
12438
 
12329
- // src/git/tracked-paths.ts
12439
+ // src/lib/git/tracked-paths.ts
12330
12440
  var GIT_LS_FILES_SUBCOMMAND = "ls-files";
12331
12441
  var GIT_NUL_TERMINATED_FLAG = "-z";
12332
12442
  var TRACKED_PATH_NUL_SEPARATOR = "\0";
@@ -12953,13 +13063,14 @@ function lowerIndexSiblingsForContextNodes(snapshot, contextNodes) {
12953
13063
  return lowerSiblings;
12954
13064
  }
12955
13065
  function decisionsFor(snapshot, contextNodes) {
12956
- const contextByParentId = /* @__PURE__ */ new Map();
12957
- for (const node of contextNodes) {
12958
- contextByParentId.set(node.parentId, node.order);
12959
- }
13066
+ const constrainingOrderByParentId = new Map(
13067
+ contextNodes.map((node) => [node.parentId, node.order])
13068
+ );
13069
+ const targetId = contextNodes.at(-1)?.id;
12960
13070
  return snapshot.decisions.filter((decision) => {
12961
- const constrainedOrder = contextByParentId.get(decision.parentId);
12962
- return constrainedOrder !== void 0 && decision.order < constrainedOrder;
13071
+ if (decision.parentId === targetId) return true;
13072
+ const constrainingOrder = constrainingOrderByParentId.get(decision.parentId);
13073
+ return constrainingOrder !== void 0 && decision.order < constrainingOrder;
12963
13074
  });
12964
13075
  }
12965
13076
  function evidenceFor(snapshot, target) {
@@ -13788,16 +13899,100 @@ function pathsFromNulDelimited(stdout) {
13788
13899
  return nulDelimitedFields(stdout).sort(compareAsciiStrings);
13789
13900
  }
13790
13901
 
13902
+ // src/lib/git/changed-paths.ts
13903
+ var GIT_DIFF_CACHED_FLAG = "--cached";
13904
+ var GIT_LS_FILES_COMMAND = "ls-files";
13905
+ var GIT_LS_FILES_OTHERS_FLAG = "--others";
13906
+ var GIT_LS_FILES_EXCLUDE_STANDARD_FLAG = "--exclude-standard";
13907
+ function uniqueSortedPaths(paths) {
13908
+ return [...new Set(paths)].sort(compareAsciiStrings);
13909
+ }
13910
+ async function gitStdout(git, productDir, args, errorMessage) {
13911
+ const result = await git.execa(GIT_ROOT_COMMAND.EXECUTABLE, [...args], { cwd: productDir, reject: false });
13912
+ if (result.exitCode !== 0) {
13913
+ throw new Error(`${errorMessage}: ${result.stderr}`);
13914
+ }
13915
+ return result.stdout;
13916
+ }
13917
+ async function changedPathsForCommittedRange(options) {
13918
+ const stdout = await gitStdout(
13919
+ options.git,
13920
+ options.productDir,
13921
+ changesetNameStatusArgs(options.base, options.head),
13922
+ "failed to diff committed changed paths"
13923
+ );
13924
+ return pathsFromNameStatus(stdout);
13925
+ }
13926
+ async function changedPathsForStagedComparison(options) {
13927
+ const stdout = await gitStdout(
13928
+ options.git,
13929
+ options.productDir,
13930
+ [
13931
+ GIT_DIFF_COMMAND,
13932
+ GIT_DIFF_CACHED_FLAG,
13933
+ GIT_NAME_STATUS_FLAG,
13934
+ GIT_NULL_DELIMITED_FLAG,
13935
+ options.base
13936
+ ],
13937
+ "failed to diff staged changed paths"
13938
+ );
13939
+ return pathsFromNameStatus(stdout);
13940
+ }
13941
+ async function changedPathsForWorktreeComparison(options) {
13942
+ const trackedStdout = await gitStdout(
13943
+ options.git,
13944
+ options.productDir,
13945
+ [
13946
+ GIT_DIFF_COMMAND,
13947
+ GIT_NAME_STATUS_FLAG,
13948
+ GIT_NULL_DELIMITED_FLAG,
13949
+ options.base
13950
+ ],
13951
+ "failed to diff worktree changed paths"
13952
+ );
13953
+ const untrackedPaths = await untrackedProductPaths(options);
13954
+ return uniqueSortedPaths([
13955
+ ...pathsFromNameStatus(trackedStdout),
13956
+ ...untrackedPaths
13957
+ ]);
13958
+ }
13959
+ async function changedPathsForDirtyWorktree(options) {
13960
+ const trackedStdout = await gitStdout(
13961
+ options.git,
13962
+ options.productDir,
13963
+ [
13964
+ GIT_DIFF_COMMAND,
13965
+ GIT_NAME_STATUS_FLAG,
13966
+ GIT_NULL_DELIMITED_FLAG
13967
+ ],
13968
+ "failed to diff dirty worktree paths"
13969
+ );
13970
+ const untrackedPaths = await untrackedProductPaths(options);
13971
+ return uniqueSortedPaths([
13972
+ ...pathsFromNameStatus(trackedStdout),
13973
+ ...untrackedPaths
13974
+ ]);
13975
+ }
13976
+ async function untrackedProductPaths(options) {
13977
+ const stdout = await gitStdout(
13978
+ options.git,
13979
+ options.productDir,
13980
+ [
13981
+ GIT_LS_FILES_COMMAND,
13982
+ GIT_LS_FILES_OTHERS_FLAG,
13983
+ GIT_LS_FILES_EXCLUDE_STANDARD_FLAG,
13984
+ GIT_NULL_DELIMITED_FLAG
13985
+ ],
13986
+ "failed to list untracked paths"
13987
+ );
13988
+ return pathsFromNulDelimited(stdout);
13989
+ }
13990
+
13791
13991
  // src/commands/test/changed-set-planning.ts
13792
- var CHANGED_TEST_DIFF_COMMAND = "diff";
13793
- var CHANGED_TEST_DIFF_CACHED_FLAG = "--cached";
13794
- var CHANGED_TEST_LS_FILES_COMMAND = "ls-files";
13992
+ var CHANGED_TEST_LS_FILES_COMMAND = GIT_LS_FILES_COMMAND;
13795
13993
  var CHANGED_TEST_SHOW_COMMAND = "show";
13796
13994
  var CHANGED_TEST_INDEX_PATH_PREFIX = ":";
13797
- var CHANGED_TEST_DIFF_NAME_STATUS_FLAG = GIT_NAME_STATUS_FLAG;
13798
13995
  var CHANGED_TEST_NULL_DELIMITED_FLAG = GIT_NULL_DELIMITED_FLAG;
13799
- var CHANGED_TEST_LS_FILES_OTHERS_FLAG = "--others";
13800
- var CHANGED_TEST_LS_FILES_EXCLUDE_STANDARD_FLAG = "--exclude-standard";
13801
13996
  var CHANGED_TEST_LS_FILES_CACHED_FLAG = "--cached";
13802
13997
  var HEAD_REF = "HEAD";
13803
13998
  var ORIGIN_REMOTE = "origin";
@@ -13819,7 +14014,6 @@ var CHANGED_TEST_PRODUCT_INPUT_PATHS = [
13819
14014
  "src/config/source-roots.ts"
13820
14015
  ];
13821
14016
  var EMPTY_TREE_SHA = "4b825dc642cb6eb9a060e54bf8d69288fbee4904";
13822
- var changedPathsFromNameStatus = pathsFromNameStatus;
13823
14017
  function stagedSnapshotReadError(path7, stderr) {
13824
14018
  const error = new Error(
13825
14019
  `failed to read staged test candidate for changed test planning: ${path7}: ${stderr}`
@@ -13849,48 +14043,13 @@ async function requiredRefSha(ref, productDir, git) {
13849
14043
  return sha;
13850
14044
  }
13851
14045
  async function changedPaths(productDir, baseSha, staged, git) {
13852
- const runner = git?.execa;
13853
- if (runner === void 0) {
14046
+ if (git === void 0) {
13854
14047
  throw new Error("changed test planning requires injected git access");
13855
14048
  }
13856
- const result = await runner(
13857
- GIT_ROOT_COMMAND.EXECUTABLE,
13858
- [
13859
- CHANGED_TEST_DIFF_COMMAND,
13860
- ...staged ? [CHANGED_TEST_DIFF_CACHED_FLAG, CHANGED_TEST_DIFF_NAME_STATUS_FLAG, CHANGED_TEST_NULL_DELIMITED_FLAG] : [CHANGED_TEST_DIFF_NAME_STATUS_FLAG, CHANGED_TEST_NULL_DELIMITED_FLAG],
13861
- baseSha
13862
- ],
13863
- { cwd: productDir, reject: false }
13864
- );
13865
- if (result.exitCode !== 0) {
13866
- throw new Error(`failed to diff changed paths for test planning: ${result.stderr}`);
13867
- }
13868
- const diffPaths = changedPathsFromNameStatus(result.stdout);
13869
14049
  if (staged) {
13870
- return diffPaths;
13871
- }
13872
- const untrackedPaths = await untrackedWorktreePaths(productDir, git);
13873
- return [.../* @__PURE__ */ new Set([...diffPaths, ...untrackedPaths])].sort(compareAsciiStrings);
13874
- }
13875
- async function untrackedWorktreePaths(productDir, git) {
13876
- const runner = git?.execa;
13877
- if (runner === void 0) {
13878
- throw new Error("changed test planning requires injected git access");
13879
- }
13880
- const result = await runner(
13881
- GIT_ROOT_COMMAND.EXECUTABLE,
13882
- [
13883
- CHANGED_TEST_LS_FILES_COMMAND,
13884
- CHANGED_TEST_LS_FILES_OTHERS_FLAG,
13885
- CHANGED_TEST_LS_FILES_EXCLUDE_STANDARD_FLAG,
13886
- CHANGED_TEST_NULL_DELIMITED_FLAG
13887
- ],
13888
- { cwd: productDir, reject: false }
13889
- );
13890
- if (result.exitCode !== 0) {
13891
- throw new Error(`failed to list untracked paths for changed test planning: ${result.stderr}`);
14050
+ return await changedPathsForStagedComparison({ productDir, base: baseSha, git });
13892
14051
  }
13893
- return pathsFromNulDelimited(result.stdout);
14052
+ return await changedPathsForWorktreeComparison({ productDir, base: baseSha, git });
13894
14053
  }
13895
14054
  function isSpecTestPath(path7) {
13896
14055
  return path7.startsWith(SPEC_ROOT_PREFIX) && path7.includes(SPEC_TESTS_PATH_SEGMENT);
@@ -14153,33 +14312,7 @@ function stagedSnapshotFileReader(productDir, git) {
14153
14312
  };
14154
14313
  }
14155
14314
  async function dirtyWorktreePaths(productDir, git) {
14156
- const tracked = await git.execa(
14157
- GIT_ROOT_COMMAND.EXECUTABLE,
14158
- [CHANGED_TEST_DIFF_COMMAND, CHANGED_TEST_DIFF_NAME_STATUS_FLAG, CHANGED_TEST_NULL_DELIMITED_FLAG],
14159
- { cwd: productDir, reject: false }
14160
- );
14161
- if (tracked.exitCode !== 0) {
14162
- throw new Error(`failed to diff worktree paths for staged test planning: ${tracked.stderr}`);
14163
- }
14164
- const untracked = await git.execa(
14165
- GIT_ROOT_COMMAND.EXECUTABLE,
14166
- [
14167
- CHANGED_TEST_LS_FILES_COMMAND,
14168
- CHANGED_TEST_LS_FILES_OTHERS_FLAG,
14169
- CHANGED_TEST_LS_FILES_EXCLUDE_STANDARD_FLAG,
14170
- CHANGED_TEST_NULL_DELIMITED_FLAG
14171
- ],
14172
- { cwd: productDir, reject: false }
14173
- );
14174
- if (untracked.exitCode !== 0) {
14175
- throw new Error(`failed to list untracked paths for staged test planning: ${untracked.stderr}`);
14176
- }
14177
- return [
14178
- .../* @__PURE__ */ new Set([
14179
- ...pathsFromNameStatus(tracked.stdout),
14180
- ...pathsFromNulDelimited(untracked.stdout)
14181
- ])
14182
- ].sort(compareAsciiStrings);
14315
+ return await changedPathsForDirtyWorktree({ productDir, git });
14183
14316
  }
14184
14317
  function isSpecTreeTestPath(path7) {
14185
14318
  return path7.startsWith(`${SPEC_TREE_ROOT_OPERAND}${PATH_SEPARATOR2}`) && path7.includes(SPEC_TREE_TESTS_PATH_SEGMENT);
@@ -15438,6 +15571,10 @@ var SPEC_DOMAIN_CLI = {
15438
15571
  var SPEC_STATUS_FORMAT_MESSAGE = {
15439
15572
  INVALID_PREFIX: "Invalid format"
15440
15573
  };
15574
+ var SPEC_CONTEXT_OUTPUT_FORMAT = {
15575
+ TEXT: "text",
15576
+ JSON: "json"
15577
+ };
15441
15578
  var VALID_STATUS_FORMATS = [
15442
15579
  OUTPUT_FORMAT.TEXT,
15443
15580
  OUTPUT_FORMAT.JSON,
@@ -15472,6 +15609,9 @@ function handleCommandError(io, error) {
15472
15609
  `);
15473
15610
  return io.exit(1);
15474
15611
  }
15612
+ async function contextOutputForFormat(format2, options) {
15613
+ return format2 === SPEC_CONTEXT_OUTPUT_FORMAT.JSON ? contextCommand(options) : contextTextCommand(options);
15614
+ }
15475
15615
  function resolveStatusFormat(options) {
15476
15616
  if (options.json === true) {
15477
15617
  return "json";
@@ -15491,7 +15631,8 @@ function registerSpecCommands(specCmd, invocation) {
15491
15631
  const onWarning = (warning) => writeInvocationWarning3(invocation.io, warning);
15492
15632
  specCmd.command(SPEC_DOMAIN_CLI.CONTEXT_COMMAND).description("Load deterministic context for a spec-tree node").argument("<target>", "Spec-tree node path").option(SPEC_DOMAIN_CLI.JSON_OPTION, "Output as JSON").action(async (target, options) => {
15493
15633
  try {
15494
- const output = options.json === true ? await contextCommand({ target, cwd: productDir(), onWarning }) : await contextTextCommand({ target, cwd: productDir(), onWarning });
15634
+ const format2 = options.json === true ? SPEC_CONTEXT_OUTPUT_FORMAT.JSON : SPEC_CONTEXT_OUTPUT_FORMAT.TEXT;
15635
+ const output = await contextOutputForFormat(format2, { target, cwd: productDir(), onWarning });
15495
15636
  writeOutput4(invocation.io, output);
15496
15637
  } catch (error) {
15497
15638
  handleCommandError(invocation.io, error);
@@ -22049,22 +22190,27 @@ async function resolveVerifyScope(deps) {
22049
22190
  ok: true,
22050
22191
  value: {
22051
22192
  productDir: product.productDir,
22193
+ worktreeRoot: product.worktreeRoot,
22052
22194
  branchSlug: slugBranchIdentity(branchIdentity),
22053
22195
  backendIdentity: backend.value
22054
22196
  }
22055
22197
  };
22056
22198
  }
22057
- async function resolveChangedScope(scope2, deps) {
22058
- const cwd = deps.cwd ?? CONFIG_PROCESS_CWD.read();
22199
+ async function resolveChangedScope(scope2, productDir, deps) {
22059
22200
  const git = deps.git ?? defaultGitDependencies;
22060
- const diff = await git.execa(GIT_ROOT_COMMAND.EXECUTABLE, [...changesetNameStatusArgs(scope2.base, scope2.head)], {
22061
- cwd,
22062
- reject: false
22063
- });
22064
- if (diff.exitCode !== VERIFY_CLI_EXIT_CODE.OK) {
22065
- return { ok: false, error: `${VERIFY_CLI_ERROR.CHANGED_SCOPE_FAILED}: ${diff.stderr}` };
22201
+ try {
22202
+ return {
22203
+ ok: true,
22204
+ value: await changedPathsForCommittedRange({
22205
+ productDir,
22206
+ base: scope2.base,
22207
+ head: scope2.head,
22208
+ git
22209
+ })
22210
+ };
22211
+ } catch (error) {
22212
+ return { ok: false, error: `${VERIFY_CLI_ERROR.CHANGED_SCOPE_FAILED}: ${error.message}` };
22066
22213
  }
22067
- return { ok: true, value: pathsFromNameStatus(diff.stdout) };
22068
22214
  }
22069
22215
  async function persistInputRecord(runScope2, record7, deps) {
22070
22216
  const path7 = verifyInputRecordPath(runScope2);
@@ -22231,7 +22377,7 @@ async function verifyStartCommand(options, deps) {
22231
22377
  if (!inputContent.ok) return errorResult3(inputContent.error);
22232
22378
  const inputDigest = digestRunInput(options.input, inputContent.value);
22233
22379
  if (!inputDigest.ok) return errorResult3(inputDigest.error);
22234
- const changedScope = await resolveChangedScope(scope2.value, deps);
22380
+ const changedScope = await resolveChangedScope(scope2.value, resolved.value.worktreeRoot, deps);
22235
22381
  if (!changedScope.ok) return errorResult3(changedScope.error);
22236
22382
  const context = await verificationContextCreateCommand(
22237
22383
  {