@outcomeeng/spx 0.6.13 → 0.6.14

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
@@ -93,10 +93,14 @@ var DEFAULT_CLI_IO = {
93
93
 
94
94
  // src/interfaces/cli/agent.ts
95
95
  import { inspect } from "util";
96
+ import parseDuration from "parse-duration-ms";
96
97
 
97
98
  // src/commands/agent/resume.ts
98
99
  import { open, readdir, stat } from "fs/promises";
100
+
101
+ // src/domains/agent/home.ts
99
102
  import { homedir } from "os";
103
+ import { resolve as resolve3 } from "path";
100
104
 
101
105
  // src/domains/agent/protocol.ts
102
106
  var AGENT_SESSION_KIND = {
@@ -148,10 +152,31 @@ var AGENT_RESUME_TEXT = {
148
152
  INTERACTIVE_REQUIRED: "agent resume requires an interactive terminal.",
149
153
  MODE_CONFLICT: "Choose only one resume mode"
150
154
  };
155
+ var AGENT_SEARCH_DEFAULT_LIMIT = 20;
156
+ var AGENT_SEARCH_MATCH_REASON = {
157
+ ALL: "all",
158
+ PICKUP_ID: "pickup-id",
159
+ CONTAINS: "contains",
160
+ SESSION_ID: "session-id",
161
+ AGENT: "agent",
162
+ BRANCH: "branch"
163
+ };
151
164
  var AGENT_SESSION_JSON_FIELDS = {
152
165
  TIMESTAMP: "timestamp",
153
166
  TYPE: "type",
154
167
  CWD: "cwd",
168
+ COMMAND: "command",
169
+ CMD: "cmd",
170
+ ARGS: "args",
171
+ ARGUMENTS: "arguments",
172
+ NAME: "name",
173
+ CALL_ID: "call_id",
174
+ OUTPUT: "output",
175
+ MESSAGE: "message",
176
+ CONTENT: "content",
177
+ INPUT: "input",
178
+ TOOL_USE_ID: "tool_use_id",
179
+ IS_ERROR: "is_error",
155
180
  ORIGINATOR: "originator",
156
181
  SOURCE: "source",
157
182
  SESSION_ID: "session_id",
@@ -163,8 +188,96 @@ var AGENT_SESSION_JSON_FIELDS = {
163
188
  GIT_BRANCH: "gitBranch",
164
189
  THREAD_SOURCE: "thread_source"
165
190
  };
191
+ var AGENT_TRANSCRIPT_GIT_COMMAND = {
192
+ EXECUTABLE: "git",
193
+ SWITCH: "switch",
194
+ CHECKOUT: "checkout",
195
+ WORKTREE: "worktree",
196
+ ADD: "add",
197
+ TRACK: "--track",
198
+ TRACK_SHORT: "-t",
199
+ NO_TRACK: "--no-track",
200
+ TRACK_DIRECT: "--track=direct",
201
+ TRACK_INHERIT: "--track=inherit",
202
+ CHANGE_DIRECTORY: "-C",
203
+ CONFIG: "-c",
204
+ GIT_DIR: "--git-dir",
205
+ WORK_TREE: "--work-tree",
206
+ NAMESPACE: "--namespace",
207
+ CONFIG_ENV: "--config-env",
208
+ EXEC_PATH: "--exec-path",
209
+ HTML_PATH: "--html-path",
210
+ MAN_PATH: "--man-path",
211
+ INFO_PATH: "--info-path",
212
+ PAGINATE_SHORT: "-p",
213
+ PAGINATE: "--paginate",
214
+ NO_PAGER: "--no-pager",
215
+ NO_REPLACE_OBJECTS: "--no-replace-objects",
216
+ NO_LAZY_FETCH: "--no-lazy-fetch",
217
+ NO_OPTIONAL_LOCKS: "--no-optional-locks",
218
+ NO_ADVICE: "--no-advice",
219
+ BARE: "--bare",
220
+ FORCE: "--force",
221
+ FORCE_SHORT: "-f",
222
+ QUIET: "--quiet",
223
+ QUIET_SHORT: "-q",
224
+ GUESS: "--guess",
225
+ NO_GUESS: "--no-guess",
226
+ CHECKOUT_WORKTREE: "--checkout",
227
+ NO_CHECKOUT_WORKTREE: "--no-checkout",
228
+ LOCK: "--lock",
229
+ NO_LOCK: "--no-lock",
230
+ GUESS_REMOTE: "--guess-remote",
231
+ NO_GUESS_REMOTE: "--no-guess-remote",
232
+ RELATIVE_PATHS: "--relative-paths",
233
+ NO_RELATIVE_PATHS: "--no-relative-paths",
234
+ IGNORE_OTHER_WORKTREES: "--ignore-other-worktrees",
235
+ NO_IGNORE_OTHER_WORKTREES: "--no-ignore-other-worktrees",
236
+ DISCARD_CHANGES: "--discard-changes",
237
+ NO_DISCARD_CHANGES: "--no-discard-changes",
238
+ MERGE: "--merge",
239
+ MERGE_SHORT: "-m",
240
+ OVERLAY: "--overlay",
241
+ NO_OVERLAY: "--no-overlay",
242
+ OVERWRITE_IGNORE: "--overwrite-ignore",
243
+ NO_OVERWRITE_IGNORE: "--no-overwrite-ignore",
244
+ PROGRESS: "--progress",
245
+ NO_PROGRESS: "--no-progress",
246
+ REASON: "--reason",
247
+ RECURSE_SUBMODULES: "--recurse-submodules",
248
+ NO_RECURSE_SUBMODULES: "--no-recurse-submodules",
249
+ CONFLICT: "--conflict",
250
+ PATHSPEC_SEPARATOR: "--",
251
+ CREATE_BRANCH_SHORT: "-b",
252
+ CREATE_REFLOG_SHORT: "-l",
253
+ CREATE_BRANCH_LONG: "-c",
254
+ CREATE_BRANCH_RESET_SHORT: "-B",
255
+ CREATE_BRANCH_SWITCH_RESET_SHORT: "-C",
256
+ CREATE_BRANCH_SWITCH_LONG: "--create",
257
+ CREATE_BRANCH_SWITCH_RESET_LONG: "--force-create",
258
+ DETACH: "--detach",
259
+ ORPHAN: "--orphan"
260
+ };
166
261
  var AGENT_SESSION_ROW_TYPE = {
167
- CODEX_SESSION_META: "session_meta"
262
+ CODEX_SESSION_META: "session_meta",
263
+ CODEX_RESPONSE_ITEM: "response_item",
264
+ CLAUDE_ASSISTANT: "assistant",
265
+ CLAUDE_USER: "user"
266
+ };
267
+ var AGENT_TRANSCRIPT_PAYLOAD_TYPE = {
268
+ FUNCTION_CALL: "function_call",
269
+ FUNCTION_CALL_OUTPUT: "function_call_output"
270
+ };
271
+ var AGENT_TRANSCRIPT_TOOL_NAME = {
272
+ CODEX_EXEC_COMMAND: "exec_command",
273
+ CLAUDE_BASH: "Bash"
274
+ };
275
+ var AGENT_TRANSCRIPT_CONTENT_TYPE = {
276
+ TOOL_USE: "tool_use",
277
+ TOOL_RESULT: "tool_result"
278
+ };
279
+ var AGENT_TRANSCRIPT_CODEX_OUTPUT = {
280
+ PROCESS_EXITED_WITH_CODE: "Process exited with code"
168
281
  };
169
282
  var CODEX_SESSION_ORIGINATOR = {
170
283
  TUI: "codex-tui",
@@ -178,8 +291,68 @@ var CODEX_SESSION_THREAD_SOURCE = {
178
291
  };
179
292
  var AGENT_RESUME_RECENT_WINDOW_MS = AGENT_RESUME_LIMITS.RECENT_DAYS * AGENT_RESUME_LIMITS.HOURS_PER_DAY * AGENT_RESUME_LIMITS.MINUTES_PER_HOUR * AGENT_RESUME_LIMITS.SECONDS_PER_MINUTE * AGENT_RESUME_LIMITS.MILLISECONDS_PER_SECOND;
180
293
 
294
+ // src/domains/agent/home.ts
295
+ var AGENT_HOME_ENV = {
296
+ CODEX: "CODEX_HOME",
297
+ CLAUDE: "CLAUDE_CONFIG_DIR"
298
+ };
299
+ var defaultAgentHomeResolutionDeps = {
300
+ homeDir: homedir
301
+ };
302
+ function agentHomeDirsFromHomeDir(homeDir) {
303
+ return {
304
+ codex: resolve3(homeDir, AGENT_SESSION_STORE.CODEX_DIR),
305
+ claudeCode: resolve3(homeDir, AGENT_SESSION_STORE.CLAUDE_DIR)
306
+ };
307
+ }
308
+ function resolveAgentHomeDirs(env = process.env, deps = defaultAgentHomeResolutionDeps) {
309
+ const defaults6 = agentHomeDirsFromHomeDir(deps.homeDir());
310
+ return {
311
+ codex: env[AGENT_HOME_ENV.CODEX] ?? defaults6.codex,
312
+ claudeCode: env[AGENT_HOME_ENV.CLAUDE] ?? defaults6.claudeCode
313
+ };
314
+ }
315
+
316
+ // src/domains/agent/resume.ts
317
+ import { isAbsolute, relative, resolve as resolve4, sep } from "path";
318
+
319
+ // src/domains/agent/transcript-json.ts
320
+ function parseJsonObject(line) {
321
+ const trimmed = line.trim();
322
+ if (trimmed.length === 0) {
323
+ return null;
324
+ }
325
+ try {
326
+ const parsed = JSON.parse(trimmed);
327
+ return isRecord(parsed) ? parsed : null;
328
+ } catch {
329
+ return null;
330
+ }
331
+ }
332
+ function firstString(row, paths) {
333
+ for (const path7 of paths) {
334
+ const value = valueAtPath(row, path7);
335
+ if (typeof value === "string" && value.length > 0) {
336
+ return value;
337
+ }
338
+ }
339
+ return null;
340
+ }
341
+ function valueAtPath(row, path7) {
342
+ let current = row;
343
+ for (const segment of path7) {
344
+ if (!isRecord(current)) {
345
+ return void 0;
346
+ }
347
+ current = current[segment];
348
+ }
349
+ return current;
350
+ }
351
+ function isRecord(value) {
352
+ return typeof value === "object" && value !== null && !Array.isArray(value);
353
+ }
354
+
181
355
  // src/domains/agent/resume.ts
182
- import { isAbsolute, relative, resolve as resolve3, sep } from "path";
183
356
  function worktreeResumeScope() {
184
357
  return { kind: AGENT_RESUME_SCOPE.WORKTREE };
185
358
  }
@@ -243,11 +416,11 @@ function moveAgentResumePickerSelection(state, delta, candidateCount) {
243
416
  const nextIndex = Math.min(Math.max(state.selectedIndex + delta, FIRST_PICKER_INDEX), lastIndex);
244
417
  return { selectedIndex: nextIndex };
245
418
  }
246
- function codexSessionStoreDir(homeDir) {
247
- return resolve3(homeDir, AGENT_SESSION_STORE.CODEX_DIR, AGENT_SESSION_STORE.CODEX_SESSIONS_DIR);
419
+ function codexSessionStoreDir(codexHomeDir) {
420
+ return resolve4(codexHomeDir, AGENT_SESSION_STORE.CODEX_SESSIONS_DIR);
248
421
  }
249
- function claudeCodeSessionStoreDir(homeDir) {
250
- return resolve3(homeDir, AGENT_SESSION_STORE.CLAUDE_DIR, AGENT_SESSION_STORE.CLAUDE_PROJECTS_DIR);
422
+ function claudeCodeSessionStoreDir(claudeCodeHomeDir) {
423
+ return resolve4(claudeCodeHomeDir, AGENT_SESSION_STORE.CLAUDE_PROJECTS_DIR);
251
424
  }
252
425
  var CLAUDE_PROJECT_PATH_SEPARATORS = /[/\\]/g;
253
426
  var CLAUDE_PROJECT_ENCODED_SEPARATOR = "-";
@@ -260,30 +433,41 @@ async function discoverAgentResumeCandidates(options) {
260
433
  return [];
261
434
  }
262
435
  const cap = AGENT_RESUME_LIMITS.PER_AGENT_DISPLAYED_CANDIDATES;
436
+ const recentWindowMs = options.sinceMs ?? AGENT_RESUME_RECENT_WINDOW_MS;
263
437
  const [codex, claude] = await Promise.all([
264
438
  collectAgentCandidates(
265
439
  AGENT_SESSION_KIND.CODEX,
266
440
  await recentStoreFiles(
267
- await collectJsonlFiles(codexSessionStoreDir(options.homeDir), options.fs),
441
+ await collectJsonlFiles(codexSessionStoreDir(options.agentHomeDirs.codex), options.fs),
268
442
  options.fs,
269
- options.nowMs
443
+ options.nowMs,
444
+ recentWindowMs
270
445
  ),
271
446
  options.fs,
272
447
  cap,
273
448
  scope2.match,
274
- parseCodexHead
449
+ parseCodexHead,
450
+ options.nowMs,
451
+ options.sinceMs
275
452
  ),
276
453
  collectAgentCandidates(
277
454
  AGENT_SESSION_KIND.CLAUDE_CODE,
278
455
  await recentStoreFiles(
279
- await claudeTranscriptFiles(claudeCodeSessionStoreDir(options.homeDir), options.fs, scope2.claudeDirAccepts),
456
+ await claudeTranscriptFiles(
457
+ claudeCodeSessionStoreDir(options.agentHomeDirs.claudeCode),
458
+ options.fs,
459
+ scope2.claudeDirAccepts
460
+ ),
280
461
  options.fs,
281
- options.nowMs
462
+ options.nowMs,
463
+ recentWindowMs
282
464
  ),
283
465
  options.fs,
284
466
  cap,
285
467
  scope2.match,
286
- parseClaudeHead
468
+ parseClaudeHead,
469
+ options.nowMs,
470
+ options.sinceMs
287
471
  )
288
472
  ]);
289
473
  return [...codex, ...claude].sort(compareCandidates);
@@ -326,10 +510,10 @@ function renderAgentResumeList(candidates) {
326
510
  function renderAgentResumeJson(candidates) {
327
511
  return JSON.stringify(candidates, null, 2);
328
512
  }
329
- async function recentStoreFiles(paths, fs8, nowMs) {
513
+ async function recentStoreFiles(paths, fs8, nowMs, recentWindowMs) {
330
514
  const stats = await mapWithConcurrency(paths, AGENT_RESUME_LIMITS.READ_CONCURRENCY, async (path7) => {
331
515
  const stat9 = await fs8.stat(path7).catch(() => null);
332
- if (stat9 === null || !isRecentAgentSessionMtime(stat9.mtimeMs, nowMs)) {
516
+ if (stat9 === null || !isRecentAgentSessionMtime(stat9.mtimeMs, nowMs, recentWindowMs)) {
333
517
  return null;
334
518
  }
335
519
  return { path: path7, modifiedAtMs: stat9.mtimeMs };
@@ -337,14 +521,14 @@ async function recentStoreFiles(paths, fs8, nowMs) {
337
521
  return stats.filter((file) => file !== null).sort((left, right) => right.modifiedAtMs - left.modifiedAtMs || left.path.localeCompare(right.path));
338
522
  }
339
523
  async function claudeTranscriptFiles(root, fs8, dirAccepts) {
340
- const projectDirs = (await fs8.readDir(root).catch(() => [])).filter((entry) => entry.isDirectory && dirAccepts(entry.name)).map((entry) => resolve3(root, entry.name));
524
+ const projectDirs = (await fs8.readDir(root).catch(() => [])).filter((entry) => entry.isDirectory && dirAccepts(entry.name)).map((entry) => resolve4(root, entry.name));
341
525
  const perDir = await mapWithConcurrency(projectDirs, AGENT_RESUME_LIMITS.READ_CONCURRENCY, async (dir) => {
342
526
  const entries = await fs8.readDir(dir).catch(() => []);
343
- return entries.filter((entry) => entry.isFile && entry.name.endsWith(AGENT_SESSION_STORE.JSONL_EXTENSION)).map((entry) => resolve3(dir, entry.name));
527
+ return entries.filter((entry) => entry.isFile && entry.name.endsWith(AGENT_SESSION_STORE.JSONL_EXTENSION)).map((entry) => resolve4(dir, entry.name));
344
528
  });
345
529
  return perDir.flat();
346
530
  }
347
- async function collectAgentCandidates(agent, files, fs8, cap, match, parseHead) {
531
+ async function collectAgentCandidates(agent, files, fs8, cap, match, parseHead, nowMs, sinceMs) {
348
532
  const candidates = await mapWithConcurrency(files, AGENT_RESUME_LIMITS.READ_CONCURRENCY, async (file) => {
349
533
  const head = await fs8.readHead(file.path, AGENT_RESUME_LIMITS.METADATA_HEAD_BYTES).catch(() => null);
350
534
  if (head === null) {
@@ -358,13 +542,17 @@ async function collectAgentCandidates(agent, files, fs8, cap, match, parseHead)
358
542
  if (tail === null) {
359
543
  return null;
360
544
  }
545
+ const lastActivityAtMs = latestTranscriptTimestampMs(tail);
546
+ if (sinceMs !== void 0 && !isAgentSessionActivityWithinWindow(lastActivityAtMs, nowMs, sinceMs)) {
547
+ return null;
548
+ }
361
549
  return {
362
550
  agent,
363
551
  sessionId: core.sessionId,
364
552
  cwd: core.cwd,
365
553
  sourcePath: file.path,
366
554
  modifiedAtMs: file.modifiedAtMs,
367
- lastActivityAtMs: latestTranscriptTimestampMs(tail),
555
+ lastActivityAtMs,
368
556
  updatedAt: core.updatedAt,
369
557
  branch: core.branch
370
558
  };
@@ -385,7 +573,7 @@ async function collectJsonlFiles(root, fs8) {
385
573
  const entries = await fs8.readDir(root).catch(() => []);
386
574
  const files = [];
387
575
  for (const entry of entries) {
388
- const child = resolve3(root, entry.name);
576
+ const child = resolve4(root, entry.name);
389
577
  if (entry.isDirectory) {
390
578
  files.push(...await collectJsonlFiles(child, fs8));
391
579
  } else if (entry.isFile && entry.name.endsWith(AGENT_SESSION_STORE.JSONL_EXTENSION)) {
@@ -394,8 +582,11 @@ async function collectJsonlFiles(root, fs8) {
394
582
  }
395
583
  return files;
396
584
  }
397
- function isRecentAgentSessionMtime(modifiedAtMs, nowMs) {
398
- return modifiedAtMs <= nowMs && nowMs - modifiedAtMs <= AGENT_RESUME_RECENT_WINDOW_MS;
585
+ function isRecentAgentSessionMtime(modifiedAtMs, nowMs, recentWindowMs = AGENT_RESUME_RECENT_WINDOW_MS) {
586
+ return modifiedAtMs <= nowMs && nowMs - modifiedAtMs <= recentWindowMs;
587
+ }
588
+ function isAgentSessionActivityWithinWindow(lastActivityAtMs, nowMs, sinceMs) {
589
+ return lastActivityAtMs !== null && lastActivityAtMs <= nowMs && nowMs - lastActivityAtMs <= sinceMs;
399
590
  }
400
591
  function parseCodexHead(head) {
401
592
  for (const line of head.split("\n")) {
@@ -427,6 +618,7 @@ function parseCodexHead(head) {
427
618
  [AGENT_SESSION_JSON_FIELDS.THREAD_SOURCE],
428
619
  [AGENT_SESSION_JSON_FIELDS.PAYLOAD, AGENT_SESSION_JSON_FIELDS.THREAD_SOURCE]
429
620
  ]);
621
+ const subagent = threadSource === CODEX_SESSION_THREAD_SOURCE.SUBAGENT;
430
622
  return {
431
623
  sessionId,
432
624
  cwd,
@@ -435,7 +627,8 @@ function parseCodexHead(head) {
435
627
  [AGENT_SESSION_JSON_FIELDS.GIT, AGENT_SESSION_JSON_FIELDS.BRANCH]
436
628
  ]),
437
629
  updatedAt: firstString(row, [[AGENT_SESSION_JSON_FIELDS.TIMESTAMP]]),
438
- interactive: isCodexInteractive(originator, threadSource)
630
+ interactive: isCodexInteractive(originator, threadSource),
631
+ subagent
439
632
  };
440
633
  }
441
634
  return null;
@@ -475,19 +668,7 @@ function parseClaudeHead(head) {
475
668
  if (sessionId === null || cwd === null) {
476
669
  return null;
477
670
  }
478
- return { sessionId, cwd, branch, updatedAt, interactive: true };
479
- }
480
- function parseJsonObject(line) {
481
- const trimmed = line.trim();
482
- if (trimmed.length === 0) {
483
- return null;
484
- }
485
- try {
486
- const parsed = JSON.parse(trimmed);
487
- return isRecord(parsed) ? parsed : null;
488
- } catch {
489
- return null;
490
- }
671
+ return { sessionId, cwd, branch, updatedAt, interactive: true, subagent: false };
491
672
  }
492
673
  function latestTranscriptTimestampMs(transcriptSlice) {
493
674
  let latest = null;
@@ -510,28 +691,6 @@ function parseTimestampMs(timestamp) {
510
691
  const timestampMs = Date.parse(timestamp);
511
692
  return Number.isNaN(timestampMs) ? null : timestampMs;
512
693
  }
513
- function firstString(row, paths) {
514
- for (const path7 of paths) {
515
- const value = valueAtPath(row, path7);
516
- if (typeof value === "string" && value.length > 0) {
517
- return value;
518
- }
519
- }
520
- return null;
521
- }
522
- function valueAtPath(row, path7) {
523
- let current = row;
524
- for (const segment of path7) {
525
- if (!isRecord(current)) {
526
- return void 0;
527
- }
528
- current = current[segment];
529
- }
530
- return current;
531
- }
532
- function isRecord(value) {
533
- return typeof value === "object" && value !== null && !Array.isArray(value);
534
- }
535
694
  function compareCandidates(left, right) {
536
695
  if (left.lastActivityAtMs !== null && right.lastActivityAtMs !== null) {
537
696
  const activityDiff = right.lastActivityAtMs - left.lastActivityAtMs;
@@ -559,7 +718,7 @@ function compareCodeUnits(left, right) {
559
718
  return 0;
560
719
  }
561
720
  function isPathInsideOrEqual(parent, child) {
562
- const rel = relative(resolve3(parent), resolve3(child));
721
+ const rel = relative(resolve4(parent), resolve4(child));
563
722
  return rel.length === 0 || !isAbsolute(rel) && rel !== ".." && !rel.startsWith(`..${sep}`);
564
723
  }
565
724
  async function mapWithConcurrency(items, concurrency, mapper) {
@@ -618,39 +777,763 @@ var SESSION_FRONT_MATTER = {
618
777
  FILES: "files"
619
778
  };
620
779
 
621
- // src/domains/agent/search.ts
622
- var AGENT_SEARCH_DEFAULT_LIMIT = 20;
623
- var AGENT_SEARCH_MATCH_REASON = {
624
- ALL: "all",
625
- PICKUP_ID: "pickup-id",
626
- CONTAINS: "contains",
627
- SESSION_ID: "session-id",
628
- AGENT: "agent",
629
- BRANCH: "branch"
630
- };
780
+ // src/domains/agent/search/query.ts
631
781
  function pickupIdSearchLiteral(pickupId) {
632
782
  return formatSessionOutputMarker(SESSION_OUTPUT_MARKER.PICKUP_ID, pickupId);
633
783
  }
634
- function agentSearchQueryFromOptions(options) {
635
- const contentNeedles = [];
636
- if (options.pickupId !== void 0) {
637
- contentNeedles.push({
638
- reason: AGENT_SEARCH_MATCH_REASON.PICKUP_ID,
639
- value: pickupIdSearchLiteral(options.pickupId)
640
- });
784
+ function agentSearchQueryFromOptions(options) {
785
+ const contentNeedles = [];
786
+ if (options.pickupId !== void 0) {
787
+ contentNeedles.push({
788
+ reason: AGENT_SEARCH_MATCH_REASON.PICKUP_ID,
789
+ value: pickupIdSearchLiteral(options.pickupId)
790
+ });
791
+ }
792
+ if (options.contains !== void 0) {
793
+ contentNeedles.push({ reason: AGENT_SEARCH_MATCH_REASON.CONTAINS, value: options.contains });
794
+ }
795
+ return {
796
+ contentNeedles,
797
+ sessionId: options.sessionId ?? null,
798
+ branch: options.branch ?? null,
799
+ agent: options.agent ?? null,
800
+ includeAll: options.all === true,
801
+ limit: options.limit ?? AGENT_SEARCH_DEFAULT_LIMIT
802
+ };
803
+ }
804
+ function hasSearchSelector(query) {
805
+ return query.contentNeedles.length > 0 || query.sessionId !== null || query.branch !== null || query.agent !== null;
806
+ }
807
+
808
+ // src/domains/agent/search/render.ts
809
+ function renderAgentSearchJson(results) {
810
+ return JSON.stringify(results, null, 2);
811
+ }
812
+ function renderAgentSearchList(results) {
813
+ if (results.length === 0) {
814
+ return "No matching agent sessions found.";
815
+ }
816
+ return results.map((result) => {
817
+ const updatedAt = result.updatedAt ?? new Date(result.modifiedAtMs).toISOString();
818
+ return `${updatedAt} ${AGENT_SESSION_LABEL[result.agent]} ${result.sessionId} ${result.cwd}`;
819
+ }).join("\n");
820
+ }
821
+
822
+ // src/domains/agent/search/shell-command.ts
823
+ var SHELL_COMMAND_SEPARATOR = {
824
+ SEQUENCE: ";"
825
+ };
826
+ var SHELL_OPERATOR_AND = "&&";
827
+ var SHELL_OPERATOR_OR = "||";
828
+ var SHELL_OPERATOR_AMPERSAND = "&";
829
+ var SHELL_OPERATOR_PIPE = "|";
830
+ var SHELL_BOURNE_COMMAND = "sh";
831
+ var SHELL_BASH_COMMAND = "bash";
832
+ var SHELL_COMMAND_STRING_FLAG = "-c";
833
+ var SHELL_LOGIN_COMMAND_STRING_FLAG = "-lc";
834
+ var SHELL_REDIRECTION_PATTERN = /^\d*(?:>>?|<<<?|>&|<&|&>|&>>)$/u;
835
+ var SHELL_DUPLICATED_DESCRIPTOR_PATTERN = /^\d*(?:>>?|<<<?|>&|<&|&>|&>>)&?\d+$/u;
836
+ var SHELL_WORD_PATTERN = /"([^"]*)"|'([^']*)'|(\d*(?:>>?|<<<?|>&|<&|&>|&>>)&?\d*)|(&&|\|\||[;&|])|([^\s;&|<>]+)/gu;
837
+ function shellWords(command) {
838
+ return [...command.matchAll(SHELL_WORD_PATTERN)].map(
839
+ (match) => match.at(1) ?? match.at(2) ?? match.at(3) ?? match.at(4) ?? match[0]
840
+ );
841
+ }
842
+ function shellSuccessProvingCommandSegments(words) {
843
+ const segments = [[]];
844
+ for (const word of words) {
845
+ if (isShellUnsafeSuccessSeparator(word)) {
846
+ return [];
847
+ }
848
+ if (word === SHELL_OPERATOR_AND) {
849
+ segments.push([]);
850
+ continue;
851
+ }
852
+ segments[segments.length - 1].push(word);
853
+ }
854
+ const populatedSegments = segments.filter((segment) => segment.length > 0);
855
+ const reachableSegments = [];
856
+ for (const segment of populatedSegments) {
857
+ reachableSegments.push(segment);
858
+ if (isShellKnownFailingSegment(segment)) {
859
+ break;
860
+ }
861
+ }
862
+ return reachableSegments;
863
+ }
864
+ function stripShellRedirections(words) {
865
+ const command = [];
866
+ for (let index = 0; index < words.length; index += 1) {
867
+ const word = words[index];
868
+ if (SHELL_DUPLICATED_DESCRIPTOR_PATTERN.test(word)) {
869
+ continue;
870
+ }
871
+ if (SHELL_REDIRECTION_PATTERN.test(word)) {
872
+ index += 1;
873
+ continue;
874
+ }
875
+ command.push(word);
876
+ }
877
+ return command;
878
+ }
879
+ function shellCommandWrapperWords(words) {
880
+ const executable = words[0];
881
+ if (executable !== SHELL_BOURNE_COMMAND && executable !== SHELL_BASH_COMMAND) {
882
+ return null;
883
+ }
884
+ const commandIndex = words.findIndex(
885
+ (word) => word === SHELL_COMMAND_STRING_FLAG || word === SHELL_LOGIN_COMMAND_STRING_FLAG
886
+ );
887
+ const command = commandIndex === -1 ? void 0 : words.at(commandIndex + 1);
888
+ return command === void 0 ? null : shellWords(command);
889
+ }
890
+ function isShellKnownFailingSegment(words) {
891
+ return stripShellRedirections(words)[0] === "false";
892
+ }
893
+ function isShellUnsafeSuccessSeparator(word) {
894
+ return word === SHELL_OPERATOR_OR || word === SHELL_OPERATOR_AMPERSAND || word === SHELL_OPERATOR_PIPE || word === SHELL_COMMAND_SEPARATOR.SEQUENCE;
895
+ }
896
+
897
+ // src/domains/agent/search/git-branch-evidence.ts
898
+ function gitCommandAssociatesBranch(words, branch) {
899
+ const segments = shellSuccessProvingCommandSegments(words);
900
+ let contextStable = true;
901
+ for (const segment of segments) {
902
+ if (isContextChangingShellSegment(segment)) {
903
+ contextStable = false;
904
+ continue;
905
+ }
906
+ if (contextStable && shellSegmentAssociatesBranch(segment, branch)) {
907
+ return true;
908
+ }
909
+ }
910
+ return false;
911
+ }
912
+ function shellSegmentAssociatesBranch(words, branch) {
913
+ const gitCommands = normalizeGitCommandSegments(words);
914
+ return gitCommands.some((gitCommand) => {
915
+ const args = stripGitGlobalOptions(gitCommand);
916
+ return args !== null && (gitSwitchCommandAssociatesBranch(args, branch) || gitCheckoutCommandAssociatesBranch(args, branch) || gitWorktreeAddCommandAssociatesBranch(args, branch));
917
+ });
918
+ }
919
+ function stripGitGlobalOptions(words) {
920
+ if (words[0] !== AGENT_TRANSCRIPT_GIT_COMMAND.EXECUTABLE) {
921
+ return null;
922
+ }
923
+ let index = 1;
924
+ while (index < words.length) {
925
+ const optionConsumption = gitOptionConsumption(words, index, GIT_GLOBAL_OPTIONS);
926
+ if (optionConsumption === GIT_OPTION_CONSUMPTION.INVALID) {
927
+ return null;
928
+ }
929
+ if (optionConsumption === GIT_OPTION_CONSUMPTION.NOT_ALLOWED) {
930
+ break;
931
+ }
932
+ index += optionConsumption + 1;
933
+ }
934
+ return words.slice(index);
935
+ }
936
+ function gitSwitchCommandAssociatesBranch(args, branch) {
937
+ if (args[0] !== AGENT_TRANSCRIPT_GIT_COMMAND.SWITCH) {
938
+ return false;
939
+ }
940
+ const parsed = parseGitBranchArgs(args.slice(1), SWITCH_CREATE_FLAGS, SWITCH_ALLOWED_OPTIONS);
941
+ if (parsed.invalid) {
942
+ return false;
943
+ }
944
+ if (parsed.createdBranch !== null) {
945
+ return parsed.createdBranch === branch && parsed.positionals.length <= 1;
946
+ }
947
+ return parsed.positionals.length === 1 && positionalBranchMatches(parsed.positionals[0], parsed, branch);
948
+ }
949
+ function gitCheckoutCommandAssociatesBranch(args, branch) {
950
+ if (args[0] !== AGENT_TRANSCRIPT_GIT_COMMAND.CHECKOUT) {
951
+ return false;
952
+ }
953
+ const parsed = parseGitBranchArgs(args.slice(1), CHECKOUT_CREATE_FLAGS, CHECKOUT_ALLOWED_OPTIONS);
954
+ if (parsed.invalid) {
955
+ return false;
956
+ }
957
+ if (parsed.createdBranch !== null) {
958
+ return parsed.createdBranch === branch && parsed.positionals.length <= 1;
959
+ }
960
+ return parsed.usesTrack && parsed.positionals.length === 1 && remoteTrackingBranchLocalName(parsed.positionals[0]) === branch;
961
+ }
962
+ function gitWorktreeAddCommandAssociatesBranch(args, branch) {
963
+ if (args[0] !== AGENT_TRANSCRIPT_GIT_COMMAND.WORKTREE || args[1] !== AGENT_TRANSCRIPT_GIT_COMMAND.ADD) {
964
+ return false;
965
+ }
966
+ const parsed = parseGitBranchArgs(args.slice(2), WORKTREE_CREATE_FLAGS, WORKTREE_ALLOWED_OPTIONS);
967
+ if (parsed.invalid) {
968
+ return false;
969
+ }
970
+ if (parsed.createdBranch !== null) {
971
+ return parsed.createdBranch === branch && parsed.positionals.length >= 1 && parsed.positionals.length <= 2;
972
+ }
973
+ if (parsed.positionals.length === 2) {
974
+ return parsed.positionals[1] === branch;
975
+ }
976
+ return false;
977
+ }
978
+ var GIT_OPTION_CONSUMPTION = {
979
+ INVALID: "invalid",
980
+ NOT_ALLOWED: "not-allowed"
981
+ };
982
+ var SWITCH_CREATE_FLAGS = [
983
+ AGENT_TRANSCRIPT_GIT_COMMAND.CREATE_BRANCH_LONG,
984
+ AGENT_TRANSCRIPT_GIT_COMMAND.CREATE_BRANCH_SWITCH_RESET_SHORT,
985
+ AGENT_TRANSCRIPT_GIT_COMMAND.CREATE_BRANCH_SWITCH_LONG,
986
+ AGENT_TRANSCRIPT_GIT_COMMAND.CREATE_BRANCH_SWITCH_RESET_LONG,
987
+ AGENT_TRANSCRIPT_GIT_COMMAND.ORPHAN
988
+ ];
989
+ var CHECKOUT_CREATE_FLAGS = [
990
+ AGENT_TRANSCRIPT_GIT_COMMAND.CREATE_BRANCH_SHORT,
991
+ AGENT_TRANSCRIPT_GIT_COMMAND.CREATE_BRANCH_RESET_SHORT,
992
+ AGENT_TRANSCRIPT_GIT_COMMAND.ORPHAN
993
+ ];
994
+ var WORKTREE_CREATE_FLAGS = [
995
+ AGENT_TRANSCRIPT_GIT_COMMAND.CREATE_BRANCH_SHORT,
996
+ AGENT_TRANSCRIPT_GIT_COMMAND.CREATE_BRANCH_RESET_SHORT
997
+ ];
998
+ var CHECKOUT_ALLOWED_FLAGS = [
999
+ AGENT_TRANSCRIPT_GIT_COMMAND.TRACK,
1000
+ AGENT_TRANSCRIPT_GIT_COMMAND.TRACK_SHORT,
1001
+ AGENT_TRANSCRIPT_GIT_COMMAND.NO_TRACK,
1002
+ AGENT_TRANSCRIPT_GIT_COMMAND.TRACK_DIRECT,
1003
+ AGENT_TRANSCRIPT_GIT_COMMAND.TRACK_INHERIT,
1004
+ AGENT_TRANSCRIPT_GIT_COMMAND.FORCE,
1005
+ AGENT_TRANSCRIPT_GIT_COMMAND.FORCE_SHORT,
1006
+ AGENT_TRANSCRIPT_GIT_COMMAND.QUIET,
1007
+ AGENT_TRANSCRIPT_GIT_COMMAND.QUIET_SHORT,
1008
+ AGENT_TRANSCRIPT_GIT_COMMAND.GUESS,
1009
+ AGENT_TRANSCRIPT_GIT_COMMAND.NO_GUESS,
1010
+ AGENT_TRANSCRIPT_GIT_COMMAND.OVERLAY,
1011
+ AGENT_TRANSCRIPT_GIT_COMMAND.NO_OVERLAY,
1012
+ AGENT_TRANSCRIPT_GIT_COMMAND.PROGRESS,
1013
+ AGENT_TRANSCRIPT_GIT_COMMAND.NO_PROGRESS,
1014
+ AGENT_TRANSCRIPT_GIT_COMMAND.MERGE,
1015
+ AGENT_TRANSCRIPT_GIT_COMMAND.MERGE_SHORT,
1016
+ AGENT_TRANSCRIPT_GIT_COMMAND.CREATE_REFLOG_SHORT,
1017
+ AGENT_TRANSCRIPT_GIT_COMMAND.OVERWRITE_IGNORE,
1018
+ AGENT_TRANSCRIPT_GIT_COMMAND.NO_OVERWRITE_IGNORE,
1019
+ AGENT_TRANSCRIPT_GIT_COMMAND.IGNORE_OTHER_WORKTREES,
1020
+ AGENT_TRANSCRIPT_GIT_COMMAND.NO_IGNORE_OTHER_WORKTREES,
1021
+ AGENT_TRANSCRIPT_GIT_COMMAND.NO_RECURSE_SUBMODULES
1022
+ ];
1023
+ var CHECKOUT_ALLOWED_OPTIONS = {
1024
+ flags: CHECKOUT_ALLOWED_FLAGS,
1025
+ valueFlags: [AGENT_TRANSCRIPT_GIT_COMMAND.CONFLICT],
1026
+ optionalValueFlags: [AGENT_TRANSCRIPT_GIT_COMMAND.RECURSE_SUBMODULES]
1027
+ };
1028
+ var SWITCH_ALLOWED_FLAGS = [
1029
+ AGENT_TRANSCRIPT_GIT_COMMAND.TRACK,
1030
+ AGENT_TRANSCRIPT_GIT_COMMAND.TRACK_SHORT,
1031
+ AGENT_TRANSCRIPT_GIT_COMMAND.NO_TRACK,
1032
+ AGENT_TRANSCRIPT_GIT_COMMAND.TRACK_DIRECT,
1033
+ AGENT_TRANSCRIPT_GIT_COMMAND.TRACK_INHERIT,
1034
+ AGENT_TRANSCRIPT_GIT_COMMAND.FORCE,
1035
+ AGENT_TRANSCRIPT_GIT_COMMAND.FORCE_SHORT,
1036
+ AGENT_TRANSCRIPT_GIT_COMMAND.QUIET,
1037
+ AGENT_TRANSCRIPT_GIT_COMMAND.QUIET_SHORT,
1038
+ AGENT_TRANSCRIPT_GIT_COMMAND.GUESS,
1039
+ AGENT_TRANSCRIPT_GIT_COMMAND.NO_GUESS,
1040
+ AGENT_TRANSCRIPT_GIT_COMMAND.DISCARD_CHANGES,
1041
+ AGENT_TRANSCRIPT_GIT_COMMAND.NO_DISCARD_CHANGES,
1042
+ AGENT_TRANSCRIPT_GIT_COMMAND.PROGRESS,
1043
+ AGENT_TRANSCRIPT_GIT_COMMAND.NO_PROGRESS,
1044
+ AGENT_TRANSCRIPT_GIT_COMMAND.MERGE,
1045
+ AGENT_TRANSCRIPT_GIT_COMMAND.MERGE_SHORT,
1046
+ AGENT_TRANSCRIPT_GIT_COMMAND.OVERWRITE_IGNORE,
1047
+ AGENT_TRANSCRIPT_GIT_COMMAND.NO_OVERWRITE_IGNORE,
1048
+ AGENT_TRANSCRIPT_GIT_COMMAND.IGNORE_OTHER_WORKTREES,
1049
+ AGENT_TRANSCRIPT_GIT_COMMAND.NO_IGNORE_OTHER_WORKTREES,
1050
+ AGENT_TRANSCRIPT_GIT_COMMAND.NO_RECURSE_SUBMODULES
1051
+ ];
1052
+ var SWITCH_ALLOWED_OPTIONS = {
1053
+ flags: SWITCH_ALLOWED_FLAGS,
1054
+ valueFlags: [AGENT_TRANSCRIPT_GIT_COMMAND.CONFLICT],
1055
+ optionalValueFlags: [AGENT_TRANSCRIPT_GIT_COMMAND.RECURSE_SUBMODULES]
1056
+ };
1057
+ var WORKTREE_ALLOWED_FLAGS = [
1058
+ AGENT_TRANSCRIPT_GIT_COMMAND.FORCE,
1059
+ AGENT_TRANSCRIPT_GIT_COMMAND.FORCE_SHORT,
1060
+ AGENT_TRANSCRIPT_GIT_COMMAND.TRACK,
1061
+ AGENT_TRANSCRIPT_GIT_COMMAND.NO_TRACK,
1062
+ AGENT_TRANSCRIPT_GIT_COMMAND.QUIET,
1063
+ AGENT_TRANSCRIPT_GIT_COMMAND.QUIET_SHORT,
1064
+ AGENT_TRANSCRIPT_GIT_COMMAND.CHECKOUT_WORKTREE,
1065
+ AGENT_TRANSCRIPT_GIT_COMMAND.NO_CHECKOUT_WORKTREE,
1066
+ AGENT_TRANSCRIPT_GIT_COMMAND.LOCK,
1067
+ AGENT_TRANSCRIPT_GIT_COMMAND.NO_LOCK,
1068
+ AGENT_TRANSCRIPT_GIT_COMMAND.GUESS_REMOTE,
1069
+ AGENT_TRANSCRIPT_GIT_COMMAND.NO_GUESS_REMOTE,
1070
+ AGENT_TRANSCRIPT_GIT_COMMAND.RELATIVE_PATHS,
1071
+ AGENT_TRANSCRIPT_GIT_COMMAND.NO_RELATIVE_PATHS
1072
+ ];
1073
+ var WORKTREE_ALLOWED_OPTIONS = {
1074
+ flags: WORKTREE_ALLOWED_FLAGS,
1075
+ valueFlags: [AGENT_TRANSCRIPT_GIT_COMMAND.REASON],
1076
+ optionalValueFlags: []
1077
+ };
1078
+ var GIT_GLOBAL_FLAGS = [
1079
+ AGENT_TRANSCRIPT_GIT_COMMAND.HTML_PATH,
1080
+ AGENT_TRANSCRIPT_GIT_COMMAND.MAN_PATH,
1081
+ AGENT_TRANSCRIPT_GIT_COMMAND.INFO_PATH,
1082
+ AGENT_TRANSCRIPT_GIT_COMMAND.PAGINATE_SHORT,
1083
+ AGENT_TRANSCRIPT_GIT_COMMAND.PAGINATE,
1084
+ AGENT_TRANSCRIPT_GIT_COMMAND.NO_PAGER,
1085
+ AGENT_TRANSCRIPT_GIT_COMMAND.NO_REPLACE_OBJECTS,
1086
+ AGENT_TRANSCRIPT_GIT_COMMAND.NO_LAZY_FETCH,
1087
+ AGENT_TRANSCRIPT_GIT_COMMAND.NO_OPTIONAL_LOCKS,
1088
+ AGENT_TRANSCRIPT_GIT_COMMAND.NO_ADVICE,
1089
+ AGENT_TRANSCRIPT_GIT_COMMAND.BARE
1090
+ ];
1091
+ var GIT_GLOBAL_OPTIONS = {
1092
+ flags: GIT_GLOBAL_FLAGS,
1093
+ valueFlags: [
1094
+ AGENT_TRANSCRIPT_GIT_COMMAND.CONFIG,
1095
+ AGENT_TRANSCRIPT_GIT_COMMAND.NAMESPACE,
1096
+ AGENT_TRANSCRIPT_GIT_COMMAND.CONFIG_ENV
1097
+ ],
1098
+ optionalValueFlags: [AGENT_TRANSCRIPT_GIT_COMMAND.EXEC_PATH]
1099
+ };
1100
+ var DISALLOWED_BRANCH_ASSOCIATION_FLAGS = [
1101
+ AGENT_TRANSCRIPT_GIT_COMMAND.DETACH,
1102
+ AGENT_TRANSCRIPT_GIT_COMMAND.PATHSPEC_SEPARATOR
1103
+ ];
1104
+ var SHELL_ENV_COMMAND = "env";
1105
+ var SHELL_COMMAND_WRAPPER_COMMAND = "command";
1106
+ var SHELL_SUDO_COMMAND = "sudo";
1107
+ var SHELL_CHANGE_DIRECTORY_COMMAND = "cd";
1108
+ var SHELL_ENV_ASSIGNMENT_PATTERN = /^[A-Za-z_][A-Za-z0-9_]*=.*$/u;
1109
+ var CONTEXT_CHANGING_GIT_ENV = /* @__PURE__ */ new Set(["GIT_DIR", "GIT_WORK_TREE"]);
1110
+ function isContextChangingShellSegment(words) {
1111
+ const command = stripShellRedirections(words);
1112
+ return command[0] === SHELL_CHANGE_DIRECTORY_COMMAND;
1113
+ }
1114
+ function normalizeGitCommandSegments(words) {
1115
+ let index = 0;
1116
+ while (index < words.length) {
1117
+ if (words[index] === SHELL_ENV_COMMAND) {
1118
+ index += 1;
1119
+ continue;
1120
+ }
1121
+ if (SHELL_ENV_ASSIGNMENT_PATTERN.test(words[index])) {
1122
+ if (isShellContextChangingEnvAssignment(words[index])) {
1123
+ return [];
1124
+ }
1125
+ index += 1;
1126
+ continue;
1127
+ }
1128
+ break;
1129
+ }
1130
+ if (words[index] === SHELL_COMMAND_WRAPPER_COMMAND || words[index] === SHELL_SUDO_COMMAND) {
1131
+ return normalizeGitCommandSegments(words.slice(index + 1));
1132
+ }
1133
+ const shellCommandWords = shellCommandWrapperWords(words.slice(index));
1134
+ if (shellCommandWords !== null) {
1135
+ return scopedGitCommandSegments(shellCommandWords);
1136
+ }
1137
+ const command = stripShellRedirections(words.slice(index));
1138
+ return command[0] === AGENT_TRANSCRIPT_GIT_COMMAND.EXECUTABLE ? [command] : [];
1139
+ }
1140
+ function scopedGitCommandSegments(words) {
1141
+ const commands = [];
1142
+ let contextStable = true;
1143
+ for (const segment of shellSuccessProvingCommandSegments(words)) {
1144
+ if (isContextChangingShellSegment(segment)) {
1145
+ contextStable = false;
1146
+ continue;
1147
+ }
1148
+ if (contextStable) {
1149
+ commands.push(...normalizeGitCommandSegments(segment));
1150
+ }
1151
+ }
1152
+ return commands;
1153
+ }
1154
+ function isShellContextChangingEnvAssignment(word) {
1155
+ const variableName = word.slice(0, word.indexOf("="));
1156
+ return CONTEXT_CHANGING_GIT_ENV.has(variableName);
1157
+ }
1158
+ function parseGitBranchArgs(args, createFlags, allowedOptions) {
1159
+ const positionals = [];
1160
+ let createdBranch = null;
1161
+ let usesTrack = false;
1162
+ for (let index = 0; index < args.length; index += 1) {
1163
+ const arg = args[index];
1164
+ if (tupleIncludes(DISALLOWED_BRANCH_ASSOCIATION_FLAGS, arg)) {
1165
+ return { createdBranch, positionals, usesTrack, invalid: true };
1166
+ }
1167
+ const createBranch = gitCreateBranchParse(args, index, createFlags);
1168
+ if (createBranch === GIT_OPTION_CONSUMPTION.INVALID) {
1169
+ return { createdBranch, positionals, usesTrack, invalid: true };
1170
+ }
1171
+ if (createBranch !== null) {
1172
+ createdBranch = createBranch.branch;
1173
+ index += createBranch.consumed;
1174
+ continue;
1175
+ }
1176
+ const optionConsumption = gitOptionConsumption(args, index, allowedOptions);
1177
+ if (optionConsumption === GIT_OPTION_CONSUMPTION.INVALID) {
1178
+ return { createdBranch, positionals, usesTrack, invalid: true };
1179
+ }
1180
+ if (optionConsumption !== GIT_OPTION_CONSUMPTION.NOT_ALLOWED) {
1181
+ usesTrack ||= isTrackOption(arg);
1182
+ index += optionConsumption;
1183
+ continue;
1184
+ }
1185
+ if (arg.startsWith("-")) {
1186
+ return { createdBranch, positionals, usesTrack, invalid: true };
1187
+ }
1188
+ positionals.push(arg);
1189
+ }
1190
+ return { createdBranch, positionals, usesTrack, invalid: false };
1191
+ }
1192
+ function positionalBranchMatches(positional, parsed, branch) {
1193
+ return positional === branch || parsed.usesTrack && remoteTrackingBranchLocalName(positional) === branch;
1194
+ }
1195
+ function remoteTrackingBranchLocalName(ref) {
1196
+ const firstSlash = ref.indexOf("/");
1197
+ return firstSlash > 0 && firstSlash < ref.length - 1 ? ref.slice(firstSlash + 1) : null;
1198
+ }
1199
+ function gitCreateBranchParse(args, index, createFlags) {
1200
+ const arg = args[index];
1201
+ for (const flag of createFlags) {
1202
+ if (arg === flag) {
1203
+ const branch = args.at(index + 1);
1204
+ return parseCreatedBranch(branch, 1);
1205
+ }
1206
+ if (isInlineValueFlag([flag], arg)) {
1207
+ return parseCreatedBranch(arg.slice(flag.length + 1), 0);
1208
+ }
1209
+ if (isShortFlagWithAttachedValue(flag, arg)) {
1210
+ return parseCreatedBranch(arg.slice(flag.length), 0);
1211
+ }
1212
+ }
1213
+ return null;
1214
+ }
1215
+ function parseCreatedBranch(branch, consumed) {
1216
+ if (branch === void 0 || branch.length === 0 || branch.startsWith("-")) {
1217
+ return GIT_OPTION_CONSUMPTION.INVALID;
1218
+ }
1219
+ return {
1220
+ branch,
1221
+ consumed
1222
+ };
1223
+ }
1224
+ function isShortFlagWithAttachedValue(flag, arg) {
1225
+ return flag.length === 2 && arg.startsWith(flag) && arg.length > flag.length;
1226
+ }
1227
+ function gitOptionConsumption(args, index, allowedOptions) {
1228
+ const arg = args[index];
1229
+ if (isInlineValueFlag(allowedOptions.valueFlags, arg)) {
1230
+ return 0;
1231
+ }
1232
+ if (tupleIncludes(allowedOptions.valueFlags, arg)) {
1233
+ const value = args.at(index + 1);
1234
+ if (value === void 0 || value.startsWith("-")) {
1235
+ return GIT_OPTION_CONSUMPTION.INVALID;
1236
+ }
1237
+ return 1;
1238
+ }
1239
+ if (isInlineValueFlag(allowedOptions.optionalValueFlags, arg) || tupleIncludes(allowedOptions.flags, arg)) {
1240
+ return 0;
1241
+ }
1242
+ if (tupleIncludes(allowedOptions.optionalValueFlags, arg)) {
1243
+ return 0;
1244
+ }
1245
+ return GIT_OPTION_CONSUMPTION.NOT_ALLOWED;
1246
+ }
1247
+ function tupleIncludes(values, value) {
1248
+ return values.includes(value);
1249
+ }
1250
+ function isInlineValueFlag(flags, value) {
1251
+ return flags.some((flag) => value.startsWith(`${flag}=`) && value.length > flag.length + 1);
1252
+ }
1253
+ function isTrackOption(value) {
1254
+ return value === AGENT_TRANSCRIPT_GIT_COMMAND.TRACK || value === AGENT_TRANSCRIPT_GIT_COMMAND.TRACK_SHORT || value.startsWith(`${AGENT_TRANSCRIPT_GIT_COMMAND.TRACK}=`);
1255
+ }
1256
+
1257
+ // src/domains/agent/search/transcript-command-evidence.ts
1258
+ function transcriptHasAcceptedBranchCommand(content, branch) {
1259
+ return transcriptBranchCommandEvidence(content).some(
1260
+ (evidence) => evidence.completed && evidence.succeeded && gitCommandAssociatesBranch(evidence.words, branch)
1261
+ );
1262
+ }
1263
+ function transcriptBranchCommandEvidence(content) {
1264
+ const evidence = [];
1265
+ const codexCalls = /* @__PURE__ */ new Map();
1266
+ const claudeToolUses = /* @__PURE__ */ new Map();
1267
+ const completedCodexCallIds = /* @__PURE__ */ new Set();
1268
+ const completedClaudeToolUseIds = /* @__PURE__ */ new Set();
1269
+ const succeededCodexCallIds = /* @__PURE__ */ new Set();
1270
+ const succeededClaudeToolUseIds = /* @__PURE__ */ new Set();
1271
+ for (const line of content.split("\n")) {
1272
+ const row = parseJsonObject(line);
1273
+ if (row === null) {
1274
+ continue;
1275
+ }
1276
+ collectCodexCommandEvidence(row, evidence, codexCalls, completedCodexCallIds, succeededCodexCallIds);
1277
+ collectClaudeCommandEvidence(row, evidence, claudeToolUses, completedClaudeToolUseIds, succeededClaudeToolUseIds);
1278
+ }
1279
+ return evidence;
1280
+ }
1281
+ function collectCodexCommandEvidence(row, evidence, calls, completedCallIds, succeededCallIds) {
1282
+ if (firstString(row, [[AGENT_SESSION_JSON_FIELDS.TYPE]]) !== AGENT_SESSION_ROW_TYPE.CODEX_RESPONSE_ITEM) {
1283
+ return;
1284
+ }
1285
+ const payload = valueAtPath(row, [AGENT_SESSION_JSON_FIELDS.PAYLOAD]);
1286
+ if (!isRecord(payload)) {
1287
+ return;
1288
+ }
1289
+ const payloadType = firstString(payload, [[AGENT_SESSION_JSON_FIELDS.TYPE]]);
1290
+ if (payloadType === AGENT_TRANSCRIPT_PAYLOAD_TYPE.FUNCTION_CALL) {
1291
+ const command2 = codexFunctionCallWords(payload);
1292
+ if (command2 === null) {
1293
+ return;
1294
+ }
1295
+ const rowEvidence = {
1296
+ words: command2,
1297
+ completed: false,
1298
+ succeeded: false
1299
+ };
1300
+ const callId2 = firstString(payload, [[AGENT_SESSION_JSON_FIELDS.CALL_ID]]);
1301
+ if (callId2 !== null) {
1302
+ rowEvidence.completed = completedCallIds.has(callId2);
1303
+ rowEvidence.succeeded = succeededCallIds.has(callId2);
1304
+ calls.set(callId2, rowEvidence);
1305
+ }
1306
+ evidence.push(rowEvidence);
1307
+ return;
1308
+ }
1309
+ if (payloadType !== AGENT_TRANSCRIPT_PAYLOAD_TYPE.FUNCTION_CALL_OUTPUT) {
1310
+ return;
1311
+ }
1312
+ const callId = firstString(payload, [[AGENT_SESSION_JSON_FIELDS.CALL_ID]]);
1313
+ if (callId === null) {
1314
+ return;
1315
+ }
1316
+ completedCallIds.add(callId);
1317
+ const command = calls.get(callId);
1318
+ if (command !== void 0) {
1319
+ command.completed = true;
1320
+ }
1321
+ if (!codexFunctionCallOutputSucceeded(payload)) {
1322
+ return;
1323
+ }
1324
+ succeededCallIds.add(callId);
1325
+ if (command !== void 0) {
1326
+ command.succeeded = true;
1327
+ }
1328
+ }
1329
+ function codexFunctionCallWords(payload) {
1330
+ const toolName = firstString(payload, [[AGENT_SESSION_JSON_FIELDS.NAME]]);
1331
+ if (toolName !== AGENT_TRANSCRIPT_TOOL_NAME.CODEX_EXEC_COMMAND) {
1332
+ return null;
1333
+ }
1334
+ const rawArguments = firstString(payload, [[AGENT_SESSION_JSON_FIELDS.ARGUMENTS]]);
1335
+ if (rawArguments === null) {
1336
+ return null;
1337
+ }
1338
+ const args = parseJsonObject(rawArguments);
1339
+ if (args === null) {
1340
+ return null;
1341
+ }
1342
+ const command = firstString(args, [
1343
+ [AGENT_SESSION_JSON_FIELDS.CMD],
1344
+ [AGENT_SESSION_JSON_FIELDS.COMMAND]
1345
+ ]);
1346
+ if (command !== null) {
1347
+ return shellWords(command);
1348
+ }
1349
+ const commandArgs = valueAtPath(args, [AGENT_SESSION_JSON_FIELDS.ARGS]);
1350
+ return stringArray(commandArgs);
1351
+ }
1352
+ var CODEX_OUTPUT_EXIT_CODE_PATTERN = new RegExp(
1353
+ String.raw`${AGENT_TRANSCRIPT_CODEX_OUTPUT.PROCESS_EXITED_WITH_CODE}\s+(\d+)`,
1354
+ "u"
1355
+ );
1356
+ function codexFunctionCallOutputSucceeded(payload) {
1357
+ const output = firstString(payload, [[AGENT_SESSION_JSON_FIELDS.OUTPUT]]);
1358
+ if (output === null) {
1359
+ return false;
1360
+ }
1361
+ const match = CODEX_OUTPUT_EXIT_CODE_PATTERN.exec(output);
1362
+ return match !== null && Number(match[1]) === 0;
1363
+ }
1364
+ function collectClaudeCommandEvidence(row, evidence, toolUses, completedToolUseIds, succeededToolUseIds) {
1365
+ const content = valueAtPath(row, [AGENT_SESSION_JSON_FIELDS.MESSAGE, AGENT_SESSION_JSON_FIELDS.CONTENT]);
1366
+ if (!Array.isArray(content)) {
1367
+ return;
1368
+ }
1369
+ for (const item of content) {
1370
+ if (!isRecord(item)) {
1371
+ continue;
1372
+ }
1373
+ const itemType = firstString(item, [[AGENT_SESSION_JSON_FIELDS.TYPE]]);
1374
+ if (itemType === AGENT_TRANSCRIPT_CONTENT_TYPE.TOOL_USE) {
1375
+ collectClaudeToolUse(item, evidence, toolUses, completedToolUseIds, succeededToolUseIds);
1376
+ } else if (itemType === AGENT_TRANSCRIPT_CONTENT_TYPE.TOOL_RESULT) {
1377
+ collectClaudeToolResult(item, toolUses, completedToolUseIds, succeededToolUseIds);
1378
+ }
1379
+ }
1380
+ }
1381
+ function collectClaudeToolUse(item, evidence, toolUses, completedToolUseIds, succeededToolUseIds) {
1382
+ const toolName = firstString(item, [[AGENT_SESSION_JSON_FIELDS.NAME]]);
1383
+ if (toolName !== AGENT_TRANSCRIPT_TOOL_NAME.CLAUDE_BASH) {
1384
+ return;
1385
+ }
1386
+ const command = firstString(item, [[AGENT_SESSION_JSON_FIELDS.INPUT, AGENT_SESSION_JSON_FIELDS.COMMAND]]);
1387
+ if (command === null) {
1388
+ return;
1389
+ }
1390
+ const rowEvidence = {
1391
+ words: shellWords(command),
1392
+ completed: false,
1393
+ succeeded: false
1394
+ };
1395
+ const toolUseId = firstString(item, [[AGENT_SESSION_JSON_FIELDS.ID]]);
1396
+ if (toolUseId !== null) {
1397
+ rowEvidence.completed = completedToolUseIds.has(toolUseId);
1398
+ rowEvidence.succeeded = succeededToolUseIds.has(toolUseId);
1399
+ toolUses.set(toolUseId, rowEvidence);
1400
+ }
1401
+ evidence.push(rowEvidence);
1402
+ }
1403
+ function collectClaudeToolResult(item, toolUses, completedToolUseIds, succeededToolUseIds) {
1404
+ const toolUseId = firstString(item, [[AGENT_SESSION_JSON_FIELDS.TOOL_USE_ID]]);
1405
+ if (toolUseId === null) {
1406
+ return;
1407
+ }
1408
+ completedToolUseIds.add(toolUseId);
1409
+ const command = toolUses.get(toolUseId);
1410
+ if (command !== void 0) {
1411
+ command.completed = true;
1412
+ }
1413
+ if (valueAtPath(item, [AGENT_SESSION_JSON_FIELDS.IS_ERROR]) !== false) {
1414
+ return;
1415
+ }
1416
+ succeededToolUseIds.add(toolUseId);
1417
+ if (command !== void 0) {
1418
+ command.succeeded = true;
1419
+ }
1420
+ }
1421
+ function stringArray(value) {
1422
+ return Array.isArray(value) && value.every((item) => typeof item === "string" && item.length > 0) ? value : null;
1423
+ }
1424
+
1425
+ // src/domains/agent/search/branch-association.ts
1426
+ async function collectTopLevelBranchAssociations(files, options, parseHead) {
1427
+ const branch = options.query.branch;
1428
+ const associated = {
1429
+ commandAssociatedSessionIds: /* @__PURE__ */ new Set(),
1430
+ commandCheckedSessionIds: /* @__PURE__ */ new Set()
1431
+ };
1432
+ if (branch === null) {
1433
+ return associated;
1434
+ }
1435
+ for (const file of files) {
1436
+ const head = await options.fs.readHead(file.path, AGENT_RESUME_LIMITS.METADATA_HEAD_BYTES).catch(() => null);
1437
+ if (head === null) {
1438
+ continue;
1439
+ }
1440
+ const core = parseHead(head);
1441
+ if (core === null || !core.interactive || core.subagent || !coreMatchesSearchScope(core, options.productScopeRoot, options.branchAssociatedWorktreeRoots ?? [])) {
1442
+ continue;
1443
+ }
1444
+ const content = await options.fs.readText(file.path).catch(() => null);
1445
+ if (content === null) {
1446
+ continue;
1447
+ }
1448
+ associated.commandCheckedSessionIds.add(core.sessionId);
1449
+ if (transcriptHasAcceptedBranchCommand(content, branch)) {
1450
+ associated.commandAssociatedSessionIds.add(core.sessionId);
1451
+ }
1452
+ }
1453
+ return associated;
1454
+ }
1455
+ async function collectCodexSubagentBranchAssociations(files, options) {
1456
+ const branch = options.query.branch;
1457
+ const associated = /* @__PURE__ */ new Map();
1458
+ if (branch === null) {
1459
+ return associated;
1460
+ }
1461
+ for (const file of files) {
1462
+ const head = await options.fs.readHead(file.path, AGENT_RESUME_LIMITS.METADATA_HEAD_BYTES).catch(() => null);
1463
+ if (head === null) {
1464
+ continue;
1465
+ }
1466
+ const core = parseCodexHead(head);
1467
+ if (core === null || !core.subagent || !coreMatchesSearchScope(core, options.productScopeRoot, options.branchAssociatedWorktreeRoots ?? [])) {
1468
+ continue;
1469
+ }
1470
+ if (core.branch === branch) {
1471
+ addCodexSubagentBranchAssociation(associated, core);
1472
+ continue;
1473
+ }
1474
+ const content = await options.fs.readText(file.path).catch(() => null);
1475
+ if (content !== null && transcriptHasAcceptedBranchCommand(content, branch)) {
1476
+ addCodexSubagentBranchAssociation(associated, core);
1477
+ }
1478
+ }
1479
+ return associated;
1480
+ }
1481
+ function branchMetadataOrWorktreeMatchReasons(core, branch, topLevelBranchAssociations, subagentBranchAssociations, currentMetadataBranchAssociationCwd2) {
1482
+ if (branch === null) {
1483
+ return {
1484
+ reasons: [],
1485
+ effectiveCwd: null
1486
+ };
641
1487
  }
642
- if (options.contains !== void 0) {
643
- contentNeedles.push({ reason: AGENT_SEARCH_MATCH_REASON.CONTAINS, value: options.contains });
1488
+ if (currentMetadataBranchAssociationCwd2 !== null) {
1489
+ return branchSearchMatch(currentMetadataBranchAssociationCwd2);
1490
+ }
1491
+ if (topLevelBranchAssociations.commandAssociatedSessionIds.has(core.sessionId)) {
1492
+ return branchSearchMatch(null);
1493
+ }
1494
+ const subagentBranchAssociation = subagentBranchAssociations.get(core.sessionId) ?? null;
1495
+ return subagentBranchAssociation === null ? null : branchSearchMatch(subagentBranchAssociation.cwd);
1496
+ }
1497
+ function branchTranscriptCommandMatchReasons(content, branch) {
1498
+ if (branch === null) {
1499
+ return {
1500
+ reasons: [],
1501
+ effectiveCwd: null
1502
+ };
1503
+ }
1504
+ return content !== void 0 && transcriptHasAcceptedBranchCommand(content, branch) ? branchSearchMatch(null) : null;
1505
+ }
1506
+ function coreMatchesSearchScope(core, productScopeRoot, branchAssociatedWorktreeRoots) {
1507
+ return cwdMatchesSearchScope(core.cwd, productScopeRoot, branchAssociatedWorktreeRoots);
1508
+ }
1509
+ function currentMetadataBranchAssociationCwd(core, branch, branchAssociatedWorktreeRoots) {
1510
+ if (branch === null) {
1511
+ return null;
644
1512
  }
1513
+ if (core.branch === branch) {
1514
+ return core.cwd;
1515
+ }
1516
+ return branchAssociatedWorktreeRoots.some((root) => isPathInsideOrEqual(root, core.cwd)) ? core.cwd : null;
1517
+ }
1518
+ function cwdMatchesSearchScope(cwd, productScopeRoot, branchAssociatedWorktreeRoots) {
1519
+ return isPathInsideOrEqual(productScopeRoot, cwd) || branchAssociatedWorktreeRoots.some((root) => isPathInsideOrEqual(root, cwd));
1520
+ }
1521
+ function branchSearchMatch(effectiveCwd) {
645
1522
  return {
646
- contentNeedles,
647
- sessionId: options.sessionId ?? null,
648
- branch: options.branch ?? null,
649
- agent: options.agent ?? null,
650
- includeAll: options.all === true,
651
- limit: options.limit ?? AGENT_SEARCH_DEFAULT_LIMIT
1523
+ reasons: [AGENT_SEARCH_MATCH_REASON.BRANCH],
1524
+ effectiveCwd
652
1525
  };
653
1526
  }
1527
+ function addCodexSubagentBranchAssociation(associated, core) {
1528
+ if (associated.has(core.sessionId)) {
1529
+ return;
1530
+ }
1531
+ associated.set(core.sessionId, {
1532
+ cwd: core.cwd
1533
+ });
1534
+ }
1535
+
1536
+ // src/domains/agent/search/results.ts
654
1537
  async function searchAgentSessions(options) {
655
1538
  const selectedAgents = options.query.agent === null ? [AGENT_SESSION_KIND.CODEX, AGENT_SESSION_KIND.CLAUDE_CODE] : [options.query.agent];
656
1539
  const perAgent = await Promise.all(
@@ -658,70 +1541,134 @@ async function searchAgentSessions(options) {
658
1541
  );
659
1542
  return perAgent.flat().sort(compareSearchResults).slice(0, Math.max(0, options.query.limit));
660
1543
  }
661
- function renderAgentSearchJson(results) {
662
- return JSON.stringify(results, null, 2);
663
- }
664
- function renderAgentSearchList(results) {
665
- if (results.length === 0) {
666
- return "No matching agent sessions found.";
667
- }
668
- return results.map((result) => {
669
- const updatedAt = result.updatedAt ?? new Date(result.modifiedAtMs).toISOString();
670
- return `${updatedAt} ${AGENT_SESSION_LABEL[result.agent]} ${result.sessionId} ${result.cwd}`;
671
- }).join("\n");
672
- }
673
1544
  async function searchAgentStore(agent, options) {
674
- const paths = agent === AGENT_SESSION_KIND.CODEX ? await collectJsonlFiles(codexSessionStoreDir(options.homeDir), options.fs) : await claudeTranscriptFiles(
675
- claudeCodeSessionStoreDir(options.homeDir),
1545
+ const branchAssociatedRoots = options.branchAssociatedWorktreeRoots ?? [];
1546
+ const acceptsClaudeDir = claudeDirAcceptsProductScope(options.productScopeRoot, branchAssociatedRoots);
1547
+ const paths = agent === AGENT_SESSION_KIND.CODEX ? await collectJsonlFiles(codexSessionStoreDir(options.agentHomeDirs.codex), options.fs) : await claudeTranscriptFiles(
1548
+ claudeCodeSessionStoreDir(options.agentHomeDirs.claudeCode),
676
1549
  options.fs,
677
- claudeDirAcceptsProductScope(options.productScopeRoot)
1550
+ acceptsClaudeDir
678
1551
  );
679
- const files = await storeFiles(paths, options.fs, options.nowMs, options.query.includeAll);
680
1552
  const parser = agent === AGENT_SESSION_KIND.CODEX ? parseCodexHead : parseClaudeHead;
681
- return collectMatchingSessions(agent, files, options, parser);
682
- }
683
- function claudeDirAcceptsProductScope(productScopeRoot) {
684
- const projectPrefix = claudeProjectDirName(productScopeRoot);
685
- return (dirName) => dirName === projectPrefix || dirName.startsWith(`${projectPrefix}${CLAUDE_PROJECT_ENCODED_SEPARATOR}`);
1553
+ const needsBranchEvidence = options.query.branch !== null;
1554
+ const allFiles = needsBranchEvidence ? await storeFiles(paths, options.fs, options.nowMs, true) : [];
1555
+ const files = needsBranchEvidence ? options.query.includeAll ? allFiles : recentStoreFiles2(allFiles, options.nowMs) : await storeFiles(paths, options.fs, options.nowMs, options.query.includeAll);
1556
+ const branchEvidenceFiles = needsBranchEvidence ? nonFutureStoreFiles(allFiles, options.nowMs) : [];
1557
+ const topLevelBranchAssociations = needsBranchEvidence ? await collectTopLevelBranchAssociations(branchEvidenceFiles, options, parser) : emptyTopLevelBranchAssociations();
1558
+ const subagentBranchAssociations = needsBranchEvidence && agent === AGENT_SESSION_KIND.CODEX ? await collectCodexSubagentBranchAssociations(branchEvidenceFiles, options) : /* @__PURE__ */ new Map();
1559
+ return collectMatchingSessions(agent, files, options, parser, topLevelBranchAssociations, subagentBranchAssociations);
1560
+ }
1561
+ function claudeDirAcceptsProductScope(productScopeRoot, branchAssociatedWorktreeRoots) {
1562
+ const projectPrefixes = [productScopeRoot, ...branchAssociatedWorktreeRoots].map(claudeProjectDirName);
1563
+ return (dirName) => projectPrefixes.some(
1564
+ (projectPrefix) => dirName === projectPrefix || dirName.startsWith(`${projectPrefix}${CLAUDE_PROJECT_ENCODED_SEPARATOR}`)
1565
+ );
686
1566
  }
687
- async function collectMatchingSessions(agent, files, options, parseHead) {
1567
+ async function collectMatchingSessions(agent, files, options, parseHead, topLevelBranchAssociations, subagentBranchAssociations) {
688
1568
  const results = [];
689
1569
  const seen = /* @__PURE__ */ new Set();
1570
+ const currentMetadataSessionIds = /* @__PURE__ */ new Set();
1571
+ const currentMetadataBranchAssociationCwds = /* @__PURE__ */ new Map();
690
1572
  for (const file of files) {
691
1573
  const head = await options.fs.readHead(file.path, AGENT_RESUME_LIMITS.METADATA_HEAD_BYTES).catch(() => null);
692
1574
  if (head === null) continue;
693
1575
  const core = parseHead(head);
694
1576
  if (core === null || !core.interactive || seen.has(core.sessionId)) continue;
695
- if (!coreMatchesProductScope(core, options.productScopeRoot)) continue;
696
- const matches = await matchReasons(agent, core, file.path, options);
697
- if (matches.length === 0) continue;
1577
+ const candidateMetadataIsCurrent = !currentMetadataSessionIds.has(core.sessionId);
1578
+ currentMetadataSessionIds.add(core.sessionId);
1579
+ recordCurrentMetadataBranchAssociation(
1580
+ core,
1581
+ options,
1582
+ candidateMetadataIsCurrent,
1583
+ currentMetadataBranchAssociationCwds
1584
+ );
1585
+ if (!coreCanHaveScopedSearchResult(core, options, subagentBranchAssociations)) continue;
1586
+ const match = await matchReasons(
1587
+ agent,
1588
+ core,
1589
+ file.path,
1590
+ options,
1591
+ topLevelBranchAssociations,
1592
+ subagentBranchAssociations,
1593
+ currentMetadataBranchAssociationCwds.get(core.sessionId) ?? null
1594
+ );
1595
+ if (match === null) continue;
1596
+ const effectiveCwd = match.effectiveCwd ?? core.cwd;
1597
+ if (!cwdMatchesSearchInputScope(effectiveCwd, options)) continue;
698
1598
  seen.add(core.sessionId);
699
1599
  results.push({
700
1600
  agent,
701
1601
  sessionId: core.sessionId,
702
- cwd: core.cwd,
1602
+ cwd: effectiveCwd,
703
1603
  sourcePath: file.path,
704
1604
  modifiedAtMs: file.modifiedAtMs,
705
1605
  updatedAt: core.updatedAt,
706
1606
  branch: core.branch,
707
- matches
1607
+ matches: match.reasons
708
1608
  });
709
1609
  }
710
1610
  return results;
711
1611
  }
712
- async function matchReasons(agent, core, path7, options) {
1612
+ function recordCurrentMetadataBranchAssociation(core, options, candidateMetadataIsCurrent, currentMetadataBranchAssociationCwds) {
1613
+ if (!candidateMetadataIsCurrent) {
1614
+ return;
1615
+ }
1616
+ const branchAssociationCwd = currentMetadataBranchAssociationCwd(
1617
+ core,
1618
+ options.query.branch,
1619
+ options.branchAssociatedWorktreeRoots ?? []
1620
+ );
1621
+ if (branchAssociationCwd !== null && cwdMatchesSearchInputScope(branchAssociationCwd, options)) {
1622
+ currentMetadataBranchAssociationCwds.set(core.sessionId, branchAssociationCwd);
1623
+ }
1624
+ }
1625
+ function coreCanHaveScopedSearchResult(core, options, subagentBranchAssociations) {
1626
+ return coreMatchesSearchInputScope(core, options) || subagentBranchAssociations.has(core.sessionId);
1627
+ }
1628
+ async function matchReasons(agent, core, path7, options, topLevelBranchAssociations, subagentBranchAssociations, candidateMetadataBranchAssociationCwd) {
713
1629
  if (!hasSearchSelector(options.query)) {
714
- return [AGENT_SEARCH_MATCH_REASON.ALL];
1630
+ return {
1631
+ reasons: [AGENT_SEARCH_MATCH_REASON.ALL],
1632
+ effectiveCwd: null
1633
+ };
715
1634
  }
716
1635
  const metadataMatches = metadataMatchReasons(agent, core, options.query);
717
- const contentMatches = await contentMatchReasons(path7, options);
718
- if (metadataMatches === null || contentMatches === null) {
719
- return [];
1636
+ if (metadataMatches === null) {
1637
+ return null;
1638
+ }
1639
+ const branchMatches = branchMetadataOrWorktreeMatchReasons(
1640
+ core,
1641
+ options.query.branch,
1642
+ topLevelBranchAssociations,
1643
+ subagentBranchAssociations,
1644
+ candidateMetadataBranchAssociationCwd
1645
+ );
1646
+ if (branchMatches === null && topLevelBranchAssociations.commandCheckedSessionIds.has(core.sessionId)) {
1647
+ return null;
1648
+ }
1649
+ const needsTranscriptContent = branchMatches === null || options.query.contentNeedles.length > 0;
1650
+ const content = needsTranscriptContent ? await options.fs.readText(path7).catch(() => null) : void 0;
1651
+ if (content === null) {
1652
+ return null;
1653
+ }
1654
+ const resolvedBranchMatches = branchMatches ?? branchTranscriptCommandMatchReasons(content, options.query.branch);
1655
+ if (resolvedBranchMatches === null) {
1656
+ return null;
1657
+ }
1658
+ const contentMatches = contentMatchReasons(content, options.query);
1659
+ if (contentMatches === null) {
1660
+ return null;
720
1661
  }
721
- return [...metadataMatches, ...contentMatches];
1662
+ return {
1663
+ reasons: [...metadataMatches, ...resolvedBranchMatches.reasons, ...contentMatches],
1664
+ effectiveCwd: resolvedBranchMatches.effectiveCwd
1665
+ };
722
1666
  }
723
- function hasSearchSelector(query) {
724
- return query.contentNeedles.length > 0 || query.sessionId !== null || query.branch !== null || query.agent !== null;
1667
+ function coreMatchesSearchInputScope(core, options) {
1668
+ return coreMatchesSearchScope(core, options.productScopeRoot, options.branchAssociatedWorktreeRoots ?? []);
1669
+ }
1670
+ function cwdMatchesSearchInputScope(cwd, options) {
1671
+ return cwdMatchesSearchScope(cwd, options.productScopeRoot, options.branchAssociatedWorktreeRoots ?? []);
725
1672
  }
726
1673
  function metadataMatchReasons(agent, core, query) {
727
1674
  const matches = [];
@@ -734,26 +1681,18 @@ function metadataMatchReasons(agent, core, query) {
734
1681
  } else if (query.sessionId !== null) {
735
1682
  return null;
736
1683
  }
737
- if (query.branch !== null) {
738
- if (core.branch !== query.branch) return null;
739
- matches.push(AGENT_SEARCH_MATCH_REASON.BRANCH);
740
- }
741
1684
  return matches;
742
1685
  }
743
- async function contentMatchReasons(path7, options) {
744
- if (options.query.contentNeedles.length === 0) {
1686
+ function contentMatchReasons(content, query) {
1687
+ if (query.contentNeedles.length === 0) {
745
1688
  return [];
746
1689
  }
747
- const content = await options.fs.readText(path7).catch(() => null);
748
- return content === null ? null : matchingContentNeedles(content, options.query.contentNeedles);
1690
+ return content === void 0 ? null : matchingContentNeedles(content, query.contentNeedles);
749
1691
  }
750
1692
  function matchingContentNeedles(content, needles) {
751
1693
  const matches = needles.filter((needle) => content.includes(needle.value)).map((needle) => needle.reason);
752
1694
  return matches.length === needles.length ? matches : null;
753
1695
  }
754
- function coreMatchesProductScope(core, productScopeRoot) {
755
- return isPathInsideOrEqual(productScopeRoot, core.cwd);
756
- }
757
1696
  async function storeFiles(paths, fs8, nowMs, includeAll) {
758
1697
  const files = await mapWithConcurrency(paths, AGENT_RESUME_LIMITS.READ_CONCURRENCY, async (path7) => {
759
1698
  const stat9 = await fs8.stat(path7).catch(() => null);
@@ -763,6 +1702,18 @@ async function storeFiles(paths, fs8, nowMs, includeAll) {
763
1702
  });
764
1703
  return files.filter((file) => file !== null).sort((left, right) => right.modifiedAtMs - left.modifiedAtMs || left.path.localeCompare(right.path));
765
1704
  }
1705
+ function recentStoreFiles2(files, nowMs) {
1706
+ return files.filter((file) => isRecentAgentSessionMtime(file.modifiedAtMs, nowMs));
1707
+ }
1708
+ function nonFutureStoreFiles(files, nowMs) {
1709
+ return files.filter((file) => file.modifiedAtMs <= nowMs);
1710
+ }
1711
+ function emptyTopLevelBranchAssociations() {
1712
+ return {
1713
+ commandAssociatedSessionIds: /* @__PURE__ */ new Set(),
1714
+ commandCheckedSessionIds: /* @__PURE__ */ new Set()
1715
+ };
1716
+ }
766
1717
  function compareSearchResults(left, right) {
767
1718
  const modifiedDiff = right.modifiedAtMs - left.modifiedAtMs;
768
1719
  if (modifiedDiff !== 0) return modifiedDiff;
@@ -771,7 +1722,7 @@ function compareSearchResults(left, right) {
771
1722
 
772
1723
  // src/git/root.ts
773
1724
  import { execa } from "execa";
774
- import { basename, dirname, isAbsolute as isAbsolute2, join, resolve as resolve4 } from "path";
1725
+ import { basename, dirname, isAbsolute as isAbsolute2, join, resolve as resolve5 } from "path";
775
1726
 
776
1727
  // src/git/environment.ts
777
1728
  function withoutGitEnvironment(env) {
@@ -877,6 +1828,7 @@ var GIT_WORKTREE_LIST_PORCELAIN_ARGS = [
877
1828
  GIT_ROOT_COMMAND.PORCELAIN
878
1829
  ];
879
1830
  var GIT_WORKTREE_PORCELAIN_ROOT_PREFIX = "worktree ";
1831
+ var GIT_WORKTREE_PORCELAIN_BRANCH_PREFIX = "branch refs/heads/";
880
1832
  var GIT_WORKTREE_PORCELAIN_BARE_LINE = "bare";
881
1833
  var GIT_WORKTREE_PORCELAIN_PRUNABLE_LINE = "prunable";
882
1834
  var GIT_WORKTREE_PORCELAIN_PRUNABLE_PREFIX = `${GIT_WORKTREE_PORCELAIN_PRUNABLE_LINE} `;
@@ -952,7 +1904,7 @@ async function detectGitCommonDirProductRoot(cwd = CONFIG_PROCESS_CWD.read(), de
952
1904
  };
953
1905
  }
954
1906
  const commonDir = extractStdout(commonDirResult.stdout);
955
- const absoluteCommonDir = isAbsolute2(commonDir) ? commonDir : resolve4(toplevel, commonDir);
1907
+ const absoluteCommonDir = isAbsolute2(commonDir) ? commonDir : resolve5(toplevel, commonDir);
956
1908
  const gitCommonDirProductRoot = dirname(absoluteCommonDir);
957
1909
  return {
958
1910
  productDir: gitCommonDirProductRoot,
@@ -1059,17 +2011,26 @@ function isObservedWorktreeRoot(worktreeRoots, candidate) {
1059
2011
  function isPrunableWorktreeRecordLine(line) {
1060
2012
  return line === GIT_WORKTREE_PORCELAIN_PRUNABLE_LINE || line.startsWith(GIT_WORKTREE_PORCELAIN_PRUNABLE_PREFIX);
1061
2013
  }
1062
- function parseWorktreeRoots(stdout) {
1063
- const roots = [];
1064
- for (const record6 of stdout.split(/\n\n+/)) {
1065
- const lines = record6.split("\n");
2014
+ function parseGitWorktreePorcelainRecords(stdout) {
2015
+ const records = [];
2016
+ for (const record7 of stdout.split(/\n\n+/)) {
2017
+ const lines = record7.split("\n");
1066
2018
  if (lines.includes(GIT_WORKTREE_PORCELAIN_BARE_LINE) || lines.some(isPrunableWorktreeRecordLine)) continue;
1067
2019
  const rootLine = lines.find((line) => line.startsWith(GIT_WORKTREE_PORCELAIN_ROOT_PREFIX));
1068
2020
  if (rootLine === void 0) continue;
2021
+ const branchLine = lines.find((line) => line.startsWith(GIT_WORKTREE_PORCELAIN_BRANCH_PREFIX));
1069
2022
  const root = normalizeGitPath(rootLine.slice(GIT_WORKTREE_PORCELAIN_ROOT_PREFIX.length));
1070
- if (root.length > 0) roots.push(root);
2023
+ if (root.length > 0) {
2024
+ records.push({
2025
+ root,
2026
+ branch: branchLine === void 0 ? null : branchLine.slice(GIT_WORKTREE_PORCELAIN_BRANCH_PREFIX.length)
2027
+ });
2028
+ }
1071
2029
  }
1072
- return roots;
2030
+ return records;
2031
+ }
2032
+ function parseWorktreeRoots(stdout) {
2033
+ return parseGitWorktreePorcelainRecords(stdout).map((record7) => record7.root);
1073
2034
  }
1074
2035
  function observedWorktreeRoots(worktreeListResult) {
1075
2036
  if (worktreeListResult.exitCode !== 0) return [];
@@ -1115,7 +2076,7 @@ async function gatherGitFacts(cwd = CONFIG_PROCESS_CWD.read(), deps = defaultGit
1115
2076
  };
1116
2077
  }
1117
2078
  const rawCommonDir = extractStdout(commonDirResult.stdout);
1118
- const commonDir = isAbsolute2(rawCommonDir) ? rawCommonDir : resolve4(worktreeRoot, rawCommonDir);
2079
+ const commonDir = isAbsolute2(rawCommonDir) ? rawCommonDir : resolve5(worktreeRoot, rawCommonDir);
1119
2080
  const bareResult = await deps.execa(
1120
2081
  GIT_ROOT_COMMAND.EXECUTABLE,
1121
2082
  [...GIT_CORE_BARE_ARGS],
@@ -1168,7 +2129,7 @@ var nodeAgentSessionFileSystem = {
1168
2129
  };
1169
2130
  var defaultAgentResumeCommandDeps = {
1170
2131
  fs: nodeAgentSessionFileSystem,
1171
- homeDir: homedir,
2132
+ agentHomeDirs: resolveAgentHomeDirs,
1172
2133
  nowMs: Date.now,
1173
2134
  resolveWorktreeRoot: async (cwd, fallbackWorktreeRoot) => {
1174
2135
  const result = await detectWorktreeProductRoot(cwd);
@@ -1179,9 +2140,10 @@ async function loadAgentResumeCandidates(options) {
1179
2140
  const deps = options.deps ?? defaultAgentResumeCommandDeps;
1180
2141
  return discoverAgentResumeCandidates({
1181
2142
  invocationDir: options.cwd,
1182
- homeDir: deps.homeDir(),
2143
+ agentHomeDirs: deps.agentHomeDirs(),
1183
2144
  nowMs: deps.nowMs(),
1184
2145
  scope: options.scope,
2146
+ sinceMs: options.sinceMs,
1185
2147
  fs: deps.fs,
1186
2148
  resolveWorktreeRoot: (cwd) => deps.resolveWorktreeRoot(cwd, options.fallbackWorktreeRoot)
1187
2149
  });
@@ -1195,7 +2157,6 @@ async function jsonAgentResumeSessions(options) {
1195
2157
 
1196
2158
  // src/commands/agent/search.ts
1197
2159
  import { open as open2, readdir as readdir2, readFile, stat as stat2 } from "fs/promises";
1198
- import { homedir as homedir2 } from "os";
1199
2160
  var nodeAgentSearchFileSystem = {
1200
2161
  async readDir(path7) {
1201
2162
  const entries = await readdir2(path7, { withFileTypes: true });
@@ -1225,20 +2186,36 @@ var nodeAgentSearchFileSystem = {
1225
2186
  };
1226
2187
  var defaultAgentSearchCommandDeps = {
1227
2188
  fs: nodeAgentSearchFileSystem,
1228
- homeDir: homedir2,
2189
+ agentHomeDirs: resolveAgentHomeDirs,
1229
2190
  nowMs: Date.now,
1230
- resolveProductScopeRoot: resolveAgentSearchProductScopeRoot
2191
+ resolveProductScopeRoot: resolveAgentSearchProductScopeRoot,
2192
+ resolveBranchAssociatedWorktreeRoots: resolveAgentSearchBranchAssociatedWorktreeRoots
1231
2193
  };
1232
2194
  async function resolveAgentSearchProductScopeRoot(cwd, fallbackProductScopeRoot, gitDeps = defaultGitDependencies) {
1233
2195
  const result = await detectWorktreeProductRoot(cwd, gitDeps);
1234
2196
  return result.isGitRepo ? result.productDir : fallbackProductScopeRoot;
1235
2197
  }
2198
+ async function resolveAgentSearchBranchAssociatedWorktreeRoots(cwd, branch, gitDeps = defaultGitDependencies) {
2199
+ const result = await gitDeps.execa(
2200
+ GIT_ROOT_COMMAND.EXECUTABLE,
2201
+ [...GIT_WORKTREE_LIST_PORCELAIN_ARGS],
2202
+ { cwd, reject: false }
2203
+ ).catch(() => null);
2204
+ if (result === null) return [];
2205
+ if (result.exitCode !== 0) return [];
2206
+ return parseBranchAssociatedWorktreeRoots(result.stdout, branch);
2207
+ }
2208
+ function parseBranchAssociatedWorktreeRoots(stdout, branch) {
2209
+ return parseGitWorktreePorcelainRecords(stdout).filter((record7) => record7.branch === branch).map((record7) => record7.root);
2210
+ }
1236
2211
  async function loadAgentSearchResults(options) {
1237
2212
  const deps = options.deps ?? defaultAgentSearchCommandDeps;
2213
+ const productScopeRoot = await deps.resolveProductScopeRoot(options.cwd, options.fallbackProductScopeRoot);
1238
2214
  return searchAgentSessions({
1239
- homeDir: deps.homeDir(),
2215
+ agentHomeDirs: deps.agentHomeDirs(),
1240
2216
  nowMs: deps.nowMs(),
1241
- productScopeRoot: await deps.resolveProductScopeRoot(options.cwd, options.fallbackProductScopeRoot),
2217
+ productScopeRoot,
2218
+ branchAssociatedWorktreeRoots: options.query.branch === null ? [] : await deps.resolveBranchAssociatedWorktreeRoots(productScopeRoot, options.query.branch),
1242
2219
  fs: deps.fs,
1243
2220
  query: options.query
1244
2221
  });
@@ -1477,13 +2454,13 @@ var FOREGROUND_LAUNCH_STDIO = "inherit";
1477
2454
  var LAUNCH_FAILURE_STATUS = 1;
1478
2455
  function launchForegroundCommand(runner, suspender, command) {
1479
2456
  const restoreSignals = suspender.suspend();
1480
- return new Promise((resolve12) => {
2457
+ return new Promise((resolve13) => {
1481
2458
  let settled = false;
1482
2459
  const settle = (status) => {
1483
2460
  if (settled) return;
1484
2461
  settled = true;
1485
2462
  restoreSignals();
1486
- resolve12(status);
2463
+ resolve13(status);
1487
2464
  };
1488
2465
  const child = runner.spawn(command.command, command.args, {
1489
2466
  cwd: command.cwd,
@@ -1605,7 +2582,8 @@ var AGENT_CLI = {
1605
2582
  contains: "--contains",
1606
2583
  sessionId: "--session-id",
1607
2584
  agent: "--agent",
1608
- limit: "--limit"
2585
+ limit: "--limit",
2586
+ since: "--since"
1609
2587
  },
1610
2588
  optionArgs: {
1611
2589
  branch: "--branch <name>",
@@ -1613,7 +2591,8 @@ var AGENT_CLI = {
1613
2591
  contains: "--contains <literal>",
1614
2592
  sessionId: "--session-id <id>",
1615
2593
  agent: "--agent <kind>",
1616
- limit: "--limit <count>"
2594
+ limit: "--limit <count>",
2595
+ since: "--since <duration>"
1617
2596
  }
1618
2597
  };
1619
2598
  var AGENT_CLI_EXIT = {
@@ -1658,6 +2637,13 @@ function parseSearchLimit(value) {
1658
2637
  }
1659
2638
  return parsed;
1660
2639
  }
2640
+ function parseResumeSince(value) {
2641
+ const parsed = parseDuration(value);
2642
+ if (parsed === void 0 || !Number.isFinite(parsed) || parsed <= 0 || !Number.isSafeInteger(parsed)) {
2643
+ throw new Error(`agent resume since must be a positive safe-integer duration: ${sanitizeCliArgument(value)}`);
2644
+ }
2645
+ return parsed;
2646
+ }
1661
2647
  function parseSearchAgentKind(value) {
1662
2648
  if (value === AGENT_SESSION_KIND.CODEX || value === AGENT_SESSION_KIND.CLAUDE_CODE) {
1663
2649
  return value;
@@ -1697,7 +2683,7 @@ function createAgentDomain(deps = {}) {
1697
2683
  description: "Find and resume coding-agent sessions",
1698
2684
  register: (program, invocation) => {
1699
2685
  const agentCmd = program.command(AGENT_CLI.commandName).description("Find and resume coding-agent sessions");
1700
- agentCmd.command(AGENT_CLI.resumeCommandName).description("Resume a Codex or Claude Code agent session for this worktree").option(AGENT_CLI.flags.latest, "Launch the newest matching session").option(AGENT_CLI.flags.list, "List matching sessions").option(AGENT_CLI.flags.json, "Print matching sessions as JSON").option(AGENT_CLI.optionArgs.branch, "Scope to sessions started on the named branch, across worktrees").action(async (options) => {
2686
+ agentCmd.command(AGENT_CLI.resumeCommandName).description("Resume a Codex or Claude Code agent session for this worktree").option(AGENT_CLI.flags.latest, "Launch the newest matching session").option(AGENT_CLI.flags.list, "List matching sessions").option(AGENT_CLI.flags.json, "Print matching sessions as JSON").option(AGENT_CLI.optionArgs.branch, "Scope to sessions started on the named branch, across worktrees").option(AGENT_CLI.optionArgs.since, "Include only sessions active within the duration").action(async (options) => {
1701
2687
  let requestedExitCode = AGENT_CLI_EXIT.SUCCESS;
1702
2688
  try {
1703
2689
  const mode = resolveAgentResumeMode(options);
@@ -1710,6 +2696,7 @@ function createAgentDomain(deps = {}) {
1710
2696
  cwd: productContext.effectiveInvocationDir,
1711
2697
  fallbackWorktreeRoot: productContext.productDir,
1712
2698
  scope: resumeScopeFromOptions(options),
2699
+ sinceMs: options.since === void 0 ? void 0 : parseResumeSince(options.since),
1713
2700
  deps: resolvedDeps.resumeDeps
1714
2701
  };
1715
2702
  if (mode === AGENT_RESUME_MODE.JSON) {
@@ -1727,7 +2714,7 @@ function createAgentDomain(deps = {}) {
1727
2714
  }
1728
2715
  return invocation.io.exit(requestedExitCode);
1729
2716
  });
1730
- agentCmd.command(AGENT_CLI.searchCommandName).description("Search Codex and Claude Code agent session transcripts for this product").option(AGENT_CLI.optionArgs.pickupId, "Search for an exact SPX pickup marker").option(AGENT_CLI.optionArgs.contains, "Search transcript content for a literal string").option(AGENT_CLI.optionArgs.sessionId, "Search for an agent session id").option(AGENT_CLI.optionArgs.branch, "Search for sessions started on the named branch").option(AGENT_CLI.optionArgs.agent, "Search only one agent kind").option(AGENT_CLI.flags.all, "Include sessions outside the recent-session window").option(AGENT_CLI.optionArgs.limit, "Maximum number of results").option(AGENT_CLI.flags.json, "Print matching sessions as JSON").action(async (options) => {
2717
+ agentCmd.command(AGENT_CLI.searchCommandName).description("Search Codex and Claude Code agent session transcripts for this product").option(AGENT_CLI.optionArgs.pickupId, "Search for an exact SPX pickup marker").option(AGENT_CLI.optionArgs.contains, "Search transcript content for a literal string").option(AGENT_CLI.optionArgs.sessionId, "Search for an agent session id").option(AGENT_CLI.optionArgs.branch, "Search by branch association").option(AGENT_CLI.optionArgs.agent, "Search only one agent kind").option(AGENT_CLI.flags.all, "Include sessions outside the recent-session window").option(AGENT_CLI.optionArgs.limit, "Maximum number of results").option(AGENT_CLI.flags.json, "Print matching sessions as JSON").action(async (options) => {
1731
2718
  try {
1732
2719
  const productContext = invocation.resolveProductContext();
1733
2720
  const commandOptions = {
@@ -2054,12 +3041,19 @@ function createStateStoreRunToken(options) {
2054
3041
  function runFileName(runToken) {
2055
3042
  return `${STATE_STORE_PATH.RUN_FILE_PREFIX}${runToken}${STATE_STORE_PATH.JSONL_EXTENSION}`;
2056
3043
  }
3044
+ function runTokenStartedAt(runToken) {
3045
+ const separatorIndex = runToken.lastIndexOf(SLUG_SEPARATOR);
3046
+ return separatorIndex < 0 ? runToken : runToken.slice(0, separatorIndex);
3047
+ }
2057
3048
  function isRunFileName(name) {
2058
3049
  return name.startsWith(STATE_STORE_PATH.RUN_FILE_PREFIX) && name.endsWith(STATE_STORE_PATH.JSONL_EXTENSION) && RUN_TOKEN_PATTERN.test(name.slice(
2059
3050
  STATE_STORE_PATH.RUN_FILE_PREFIX.length,
2060
3051
  -STATE_STORE_PATH.JSONL_EXTENSION.length
2061
3052
  ));
2062
3053
  }
3054
+ function runTokenFromRunFileName(name) {
3055
+ return isRunFileName(name) ? name.slice(STATE_STORE_PATH.RUN_FILE_PREFIX.length, -STATE_STORE_PATH.JSONL_EXTENSION.length) : void 0;
3056
+ }
2063
3057
  async function createJsonlRunFile(scopeDir, domainName, options = {}) {
2064
3058
  const fs8 = options.fs ?? defaultFileSystem;
2065
3059
  const domainRunsDir = runsDir(scopeDir, domainName);
@@ -2095,7 +3089,7 @@ async function createJsonlRunFile(scopeDir, domainName, options = {}) {
2095
3089
  }
2096
3090
  return { ok: false, error: STATE_STORE_ERROR.RUN_FILE_COLLISION_LIMIT };
2097
3091
  }
2098
- async function writeJsonlRunRecord(runFilePath, record6, options = {}) {
3092
+ async function writeJsonlRunRecord(runFilePath, record7, options = {}) {
2099
3093
  const fs8 = options.fs ?? defaultFileSystem;
2100
3094
  try {
2101
3095
  const existing = await fs8.readFile(runFilePath, STATE_STORE_TEXT_ENCODING);
@@ -2109,7 +3103,7 @@ async function writeJsonlRunRecord(runFilePath, record6, options = {}) {
2109
3103
  }
2110
3104
  }
2111
3105
  try {
2112
- await fs8.writeFile(runFilePath, serializeJsonlRecord(record6), { flag: WRITE_EXISTING_FLAG });
3106
+ await fs8.writeFile(runFilePath, serializeJsonlRecord(record7), { flag: WRITE_EXISTING_FLAG });
2113
3107
  return { ok: true, value: runFilePath };
2114
3108
  } catch (error) {
2115
3109
  return {
@@ -2118,11 +3112,11 @@ async function writeJsonlRunRecord(runFilePath, record6, options = {}) {
2118
3112
  };
2119
3113
  }
2120
3114
  }
2121
- async function appendJsonlRecord(filePath, record6, options = {}) {
3115
+ async function appendJsonlRecord(filePath, record7, options = {}) {
2122
3116
  const fs8 = options.fs ?? defaultFileSystem;
2123
3117
  try {
2124
3118
  await fs8.mkdir(dirname2(filePath), { recursive: true });
2125
- await fs8.appendFile(filePath, serializeJsonlRecord(record6));
3119
+ await fs8.appendFile(filePath, serializeJsonlRecord(record7));
2126
3120
  return { ok: true, value: filePath };
2127
3121
  } catch (error) {
2128
3122
  return {
@@ -2160,6 +3154,18 @@ function compareAsciiStrings(left, right) {
2160
3154
  if (left > right) return 1;
2161
3155
  return 0;
2162
3156
  }
3157
+ function compareRunRecencyNewestFirst(left, right) {
3158
+ const startedAtOrder = compareAsciiStrings(left.startedAt, right.startedAt);
3159
+ if (startedAtOrder !== 0) return -startedAtOrder;
3160
+ const createdAtOrder = left.createdAtMs - right.createdAtMs;
3161
+ return createdAtOrder === 0 ? -compareAsciiStrings(left.runToken, right.runToken) : -createdAtOrder;
3162
+ }
3163
+ function compareRunRecencyOldestFirst(left, right) {
3164
+ const startedAtOrder = compareAsciiStrings(left.startedAt, right.startedAt);
3165
+ if (startedAtOrder !== 0) return startedAtOrder;
3166
+ const createdAtOrder = left.createdAtMs - right.createdAtMs;
3167
+ return createdAtOrder === 0 ? compareAsciiStrings(left.runToken, right.runToken) : createdAtOrder;
3168
+ }
2163
3169
  function formatStateStoreError(code, detail) {
2164
3170
  return detail === void 0 ? code : `${code}${ERROR_DETAIL_SEPARATOR}${detail}`;
2165
3171
  }
@@ -2171,8 +3177,8 @@ function parseStateStoreError(error) {
2171
3177
  }
2172
3178
  return void 0;
2173
3179
  }
2174
- function serializeJsonlRecord(record6) {
2175
- return `${JSON.stringify(record6)}${JSONL_LINE_SEPARATOR}`;
3180
+ function serializeJsonlRecord(record7) {
3181
+ return `${JSON.stringify(record7)}${JSONL_LINE_SEPARATOR}`;
2176
3182
  }
2177
3183
  function sha256Hex(value) {
2178
3184
  return createHash(SHA256_ALGORITHM).update(value).digest(HEX_ENCODING);
@@ -2953,9 +3959,9 @@ async function compactRetrieveCommand(options) {
2953
3959
  if (!stashPath.ok) return { exitCode: 1, output: EMPTY_OUTPUT };
2954
3960
  const latest = await readLatestJsonlRecord(stashPath.value);
2955
3961
  if (!latest.ok || latest.value === void 0) return { exitCode: 1, output: EMPTY_OUTPUT };
2956
- const record6 = parseCompactRecord(latest.value);
2957
- if (!record6.ok) return { exitCode: 1, output: EMPTY_OUTPUT };
2958
- return { exitCode: 0, output: `${JSON.stringify(record6.value)}
3962
+ const record7 = parseCompactRecord(latest.value);
3963
+ if (!record7.ok) return { exitCode: 1, output: EMPTY_OUTPUT };
3964
+ return { exitCode: 0, output: `${JSON.stringify(record7.value)}
2959
3965
  ` };
2960
3966
  }
2961
3967
 
@@ -2971,12 +3977,12 @@ async function compactStoreCommand(options) {
2971
3977
  } catch {
2972
3978
  return 1;
2973
3979
  }
2974
- const record6 = extractCompactRecord(transcript);
2975
- if (record6 === void 0) return 0;
3980
+ const record7 = extractCompactRecord(transcript);
3981
+ if (record7 === void 0) return 0;
2976
3982
  const worktreeScope = await resolveWorktreeScopeDir({ cwd: options.cwd });
2977
3983
  const stashPath = compactStashPath(worktreeScope, sessionToken);
2978
3984
  if (!stashPath.ok) return 1;
2979
- const written = await appendJsonlRecord(stashPath.value, record6);
3985
+ const written = await appendJsonlRecord(stashPath.value, record7);
2980
3986
  return written.ok ? 0 : 1;
2981
3987
  }
2982
3988
 
@@ -3081,7 +4087,6 @@ var AGENT = {
3081
4087
  CLAUDE_CODE: "claudeCode"
3082
4088
  };
3083
4089
  var HARNESS_ENVIRONMENT_CONFIG_FIELDS = {
3084
- METHODOLOGY: "methodology",
3085
4090
  INSTRUCTIONS: "instructions",
3086
4091
  AGENTS: "agents",
3087
4092
  PLUGIN_BOOTSTRAP: "pluginBootstrap",
@@ -3101,8 +4106,6 @@ var HARNESS_ENVIRONMENT_CONFIG_FIELDS = {
3101
4106
  VERSION: "version",
3102
4107
  MARKETPLACE: "marketplace"
3103
4108
  };
3104
- var DEFAULT_METHODOLOGY_SOURCE = "outcomeeng/spec-tree";
3105
- var DEFAULT_METHODOLOGY_VERSION = "installed";
3106
4109
  var AGENT_SET = new Set(Object.values(AGENT));
3107
4110
  var DEFAULT_AGENT_INSTRUCTION_TARGET_AGENTS = [
3108
4111
  AGENT.CODEX,
@@ -3110,10 +4113,6 @@ var DEFAULT_AGENT_INSTRUCTION_TARGET_AGENTS = [
3110
4113
  ];
3111
4114
  var DEFAULT_AGENT_INSTRUCTION_FILE_PATH = "AGENTS.md";
3112
4115
  var DEFAULT_HARNESS_ENVIRONMENT_CONFIG = {
3113
- methodology: {
3114
- source: DEFAULT_METHODOLOGY_SOURCE,
3115
- version: DEFAULT_METHODOLOGY_VERSION
3116
- },
3117
4116
  instructions: {
3118
4117
  files: [
3119
4118
  {
@@ -3147,15 +4146,10 @@ var DEFAULT_HARNESS_ENVIRONMENT_CONFIG = {
3147
4146
  }
3148
4147
  };
3149
4148
  var HARNESS_ENVIRONMENT_ALLOWED_FIELDS = /* @__PURE__ */ new Set([
3150
- HARNESS_ENVIRONMENT_CONFIG_FIELDS.METHODOLOGY,
3151
4149
  HARNESS_ENVIRONMENT_CONFIG_FIELDS.INSTRUCTIONS,
3152
4150
  HARNESS_ENVIRONMENT_CONFIG_FIELDS.AGENTS,
3153
4151
  HARNESS_ENVIRONMENT_CONFIG_FIELDS.PLUGIN_BOOTSTRAP
3154
4152
  ]);
3155
- var HARNESS_ENVIRONMENT_METHODOLOGY_ALLOWED_FIELDS = /* @__PURE__ */ new Set([
3156
- HARNESS_ENVIRONMENT_CONFIG_FIELDS.SOURCE,
3157
- HARNESS_ENVIRONMENT_CONFIG_FIELDS.VERSION
3158
- ]);
3159
4153
  var HARNESS_ENVIRONMENT_INSTRUCTIONS_ALLOWED_FIELDS = /* @__PURE__ */ new Set([
3160
4154
  HARNESS_ENVIRONMENT_CONFIG_FIELDS.FILES
3161
4155
  ]);
@@ -3198,13 +4192,36 @@ var HARNESS_ENVIRONMENT_SKILL_ALLOWED_FIELDS = /* @__PURE__ */ new Set([
3198
4192
  function isRecord2(value) {
3199
4193
  return typeof value === "object" && value !== null && !Array.isArray(value);
3200
4194
  }
4195
+ function unknownConfigFieldError(path7, field) {
4196
+ return `${path7}.${field} is not a recognized config field`;
4197
+ }
4198
+ function harnessEnvironmentUnknownConfigFieldError(field) {
4199
+ return unknownConfigFieldError(HARNESS_ENVIRONMENT_SECTION, field);
4200
+ }
4201
+ function unknownConfigFieldsErrorPrefix(path7) {
4202
+ return `${path7} has unrecognized config fields: `;
4203
+ }
4204
+ function unknownConfigFieldsError(path7, fields) {
4205
+ return `${unknownConfigFieldsErrorPrefix(path7)}${fields.join(", ")}`;
4206
+ }
4207
+ function isHarnessEnvironmentUnknownConfigFieldError(error, field) {
4208
+ const descriptorPrefix = `${HARNESS_ENVIRONMENT_SECTION}: `;
4209
+ const normalized = error.startsWith(descriptorPrefix) ? error.slice(descriptorPrefix.length) : error;
4210
+ if (normalized === unknownConfigFieldError(HARNESS_ENVIRONMENT_SECTION, field)) {
4211
+ return true;
4212
+ }
4213
+ const fieldsPrefix = unknownConfigFieldsErrorPrefix(HARNESS_ENVIRONMENT_SECTION);
4214
+ if (!normalized.startsWith(fieldsPrefix)) return false;
4215
+ return normalized.slice(fieldsPrefix.length).split(",").map((candidate) => candidate.trim()).includes(field);
4216
+ }
3201
4217
  function rejectUnknownFields(path7, value, allowed) {
3202
4218
  const unknownFields = Object.keys(value).filter((field) => !allowed.has(field));
3203
4219
  if (unknownFields.length === 1) {
3204
- return { ok: false, error: `${path7}.${unknownFields[0]} is not a recognized config field` };
4220
+ const [unknownField] = unknownFields;
4221
+ return { ok: false, error: unknownConfigFieldError(path7, unknownField) };
3205
4222
  }
3206
4223
  if (unknownFields.length > 1) {
3207
- return { ok: false, error: `${path7} has unrecognized config fields: ${unknownFields.join(", ")}` };
4224
+ return { ok: false, error: unknownConfigFieldsError(path7, unknownFields) };
3208
4225
  }
3209
4226
  return { ok: true, value: void 0 };
3210
4227
  }
@@ -3220,21 +4237,6 @@ function validateBoolean(path7, value) {
3220
4237
  }
3221
4238
  return { ok: true, value };
3222
4239
  }
3223
- function validateMethodology(raw) {
3224
- const sectionPath = `${HARNESS_ENVIRONMENT_SECTION}.${HARNESS_ENVIRONMENT_CONFIG_FIELDS.METHODOLOGY}`;
3225
- if (!isRecord2(raw)) {
3226
- return { ok: false, error: `${sectionPath} must be an object` };
3227
- }
3228
- const unknown = rejectUnknownFields(sectionPath, raw, HARNESS_ENVIRONMENT_METHODOLOGY_ALLOWED_FIELDS);
3229
- if (!unknown.ok) return unknown;
3230
- const sourceRaw = raw[HARNESS_ENVIRONMENT_CONFIG_FIELDS.SOURCE];
3231
- const source = sourceRaw === void 0 ? { ok: true, value: DEFAULT_HARNESS_ENVIRONMENT_CONFIG.methodology.source } : validateNonEmptyString(`${sectionPath}.${HARNESS_ENVIRONMENT_CONFIG_FIELDS.SOURCE}`, sourceRaw);
3232
- if (!source.ok) return source;
3233
- const versionRaw = raw[HARNESS_ENVIRONMENT_CONFIG_FIELDS.VERSION];
3234
- const version2 = versionRaw === void 0 ? { ok: true, value: DEFAULT_HARNESS_ENVIRONMENT_CONFIG.methodology.version } : validateNonEmptyString(`${sectionPath}.${HARNESS_ENVIRONMENT_CONFIG_FIELDS.VERSION}`, versionRaw);
3235
- if (!version2.ok) return version2;
3236
- return { ok: true, value: { source: source.value, version: version2.value } };
3237
- }
3238
4240
  function validateAgent(path7, value) {
3239
4241
  if (typeof value !== "string" || !isAgent(value)) {
3240
4242
  return { ok: false, error: `${path7} must be a registered agent` };
@@ -3616,9 +4618,6 @@ function validate(value) {
3616
4618
  HARNESS_ENVIRONMENT_ALLOWED_FIELDS
3617
4619
  );
3618
4620
  if (!unknown.ok) return unknown;
3619
- const methodologyRaw = value[HARNESS_ENVIRONMENT_CONFIG_FIELDS.METHODOLOGY];
3620
- const methodology = methodologyRaw === void 0 ? { ok: true, value: DEFAULT_HARNESS_ENVIRONMENT_CONFIG.methodology } : validateMethodology(methodologyRaw);
3621
- if (!methodology.ok) return methodology;
3622
4621
  const instructionsRaw = value[HARNESS_ENVIRONMENT_CONFIG_FIELDS.INSTRUCTIONS];
3623
4622
  const instructions = instructionsRaw === void 0 ? { ok: true, value: DEFAULT_HARNESS_ENVIRONMENT_CONFIG.instructions } : validateInstructions(instructionsRaw);
3624
4623
  if (!instructions.ok) return instructions;
@@ -3631,7 +4630,6 @@ function validate(value) {
3631
4630
  return {
3632
4631
  ok: true,
3633
4632
  value: {
3634
- methodology: methodology.value,
3635
4633
  instructions: instructions.value,
3636
4634
  agents: agents.value,
3637
4635
  pluginBootstrap: pluginBootstrap.value
@@ -4553,12 +5551,77 @@ var validationConfigDescriptor = {
4553
5551
  validate: validate8
4554
5552
  };
4555
5553
 
5554
+ // src/config/methodology.ts
5555
+ var METHODOLOGY_SECTION = "methodology";
5556
+ var METHODOLOGY_CONFIG_FIELDS = {
5557
+ SOURCE: "source",
5558
+ VERSION: "version"
5559
+ };
5560
+ var DEFAULT_METHODOLOGY_SOURCE = "outcomeeng/spec-tree";
5561
+ var DEFAULT_METHODOLOGY_VERSION = "installed";
5562
+ function isRecord5(value) {
5563
+ return typeof value === "object" && value !== null && !Array.isArray(value);
5564
+ }
5565
+ function rejectUnknownFields3(path7, value, allowed) {
5566
+ const unknownFields = Object.keys(value).filter((field) => !allowed.has(field));
5567
+ if (unknownFields.length === 1) {
5568
+ return { ok: false, error: `${path7}.${unknownFields[0]} is not a recognized config field` };
5569
+ }
5570
+ if (unknownFields.length > 1) {
5571
+ return { ok: false, error: `${path7} has unrecognized config fields: ${unknownFields.join(", ")}` };
5572
+ }
5573
+ return { ok: true, value: void 0 };
5574
+ }
5575
+ function validateNonEmptyString2(path7, value) {
5576
+ if (typeof value !== "string" || value.length === 0) {
5577
+ return { ok: false, error: `${path7} must be a non-empty string` };
5578
+ }
5579
+ return { ok: true, value };
5580
+ }
5581
+ var METHODOLOGY_ALLOWED_FIELDS = new Set(Object.values(METHODOLOGY_CONFIG_FIELDS));
5582
+ var METHODOLOGY_SOURCE_PATTERN = /^[A-Za-z0-9][A-Za-z0-9._-]*\/[A-Za-z0-9][A-Za-z0-9._-]*$/;
5583
+ var DEFAULT_METHODOLOGY_CONFIG = {
5584
+ source: DEFAULT_METHODOLOGY_SOURCE,
5585
+ version: DEFAULT_METHODOLOGY_VERSION
5586
+ };
5587
+ function validateMethodologySource(path7, value) {
5588
+ const source = validateNonEmptyString2(path7, value);
5589
+ if (!source.ok) return source;
5590
+ if (!METHODOLOGY_SOURCE_PATTERN.test(source.value)) {
5591
+ return { ok: false, error: `${path7} must be an owner/repository identifier` };
5592
+ }
5593
+ return source;
5594
+ }
5595
+ function validateMethodologyConfig(value) {
5596
+ if (!isRecord5(value)) {
5597
+ return { ok: false, error: `${METHODOLOGY_SECTION} section must be an object` };
5598
+ }
5599
+ const unknown = rejectUnknownFields3(METHODOLOGY_SECTION, value, METHODOLOGY_ALLOWED_FIELDS);
5600
+ if (!unknown.ok) return unknown;
5601
+ const sourceRaw = value[METHODOLOGY_CONFIG_FIELDS.SOURCE];
5602
+ const source = sourceRaw === void 0 ? { ok: true, value: DEFAULT_METHODOLOGY_CONFIG.source } : validateMethodologySource(`${METHODOLOGY_SECTION}.${METHODOLOGY_CONFIG_FIELDS.SOURCE}`, sourceRaw);
5603
+ if (!source.ok) return source;
5604
+ const versionRaw = value[METHODOLOGY_CONFIG_FIELDS.VERSION];
5605
+ const version2 = versionRaw === void 0 ? { ok: true, value: DEFAULT_METHODOLOGY_CONFIG.version } : validateNonEmptyString2(`${METHODOLOGY_SECTION}.${METHODOLOGY_CONFIG_FIELDS.VERSION}`, versionRaw);
5606
+ if (!version2.ok) return version2;
5607
+ return { ok: true, value: { source: source.value, version: version2.value } };
5608
+ }
5609
+ function resolveMethodologyIdentity(config) {
5610
+ return config;
5611
+ }
5612
+ var methodologyConfigDescriptor = {
5613
+ section: METHODOLOGY_SECTION,
5614
+ defaults: DEFAULT_METHODOLOGY_CONFIG,
5615
+ validate: validateMethodologyConfig
5616
+ };
5617
+
4556
5618
  // src/config/registry.ts
4557
5619
  var productionRegistry = [
4558
5620
  specTreeConfigDescriptor,
4559
5621
  validationConfigDescriptor,
4560
5622
  testingConfigDescriptor,
4561
5623
  fileInclusionConfigDescriptor,
5624
+ methodologyConfigDescriptor,
4562
5625
  harnessEnvironmentConfigDescriptor,
4563
5626
  diagnoseConfigDescriptor,
4564
5627
  runtimeConfigDescriptor
@@ -4630,10 +5693,10 @@ function normalizeRecord(value, path7, seen) {
4630
5693
  if (keys.some((key) => typeof key === "symbol")) {
4631
5694
  return { ok: false, error: `${path7} must not contain symbol keys` };
4632
5695
  }
4633
- const record6 = value;
5696
+ const record7 = value;
4634
5697
  const normalized = {};
4635
5698
  for (const key of keys.sort(compareUnicodeCodePointStrings)) {
4636
- const child = normalizeDescriptorJsonValue(record6[key], `${path7}.${key}`, seen);
5699
+ const child = normalizeDescriptorJsonValue(record7[key], `${path7}.${key}`, seen);
4637
5700
  if (!child.ok) return child;
4638
5701
  normalized[key] = child.value;
4639
5702
  }
@@ -4685,6 +5748,17 @@ function resolveConfigFromReadResult(detected, descriptors = productionRegistry)
4685
5748
  }
4686
5749
  return { ok: true, value: resolved };
4687
5750
  }
5751
+ function readConfigSectionFromReadResult(detected, section) {
5752
+ if (detected.kind === "ambiguous") {
5753
+ return { ok: false, error: formatConfigFileAmbiguityError(detected.detected) };
5754
+ }
5755
+ if (detected.kind === "absent") {
5756
+ return { ok: true, value: void 0 };
5757
+ }
5758
+ const sectionsResult = parseConfigFileSections(detected.file);
5759
+ if (!sectionsResult.ok) return sectionsResult;
5760
+ return { ok: true, value: sectionsResult.value[section] };
5761
+ }
4688
5762
  async function readProductConfigFile(productDir) {
4689
5763
  const detected = [];
4690
5764
  for (const format2 of CONFIG_FILE_FORMAT_ORDER) {
@@ -4979,6 +6053,32 @@ var configDomain = {
4979
6053
  import { readFile as readFile4 } from "fs/promises";
4980
6054
  import { Option } from "commander";
4981
6055
 
6056
+ // src/config/methodology-placement.ts
6057
+ var LEGACY_METHODOLOGY_CONFIG_SECTION = HARNESS_ENVIRONMENT_SECTION;
6058
+ function isLegacyHarnessMethodologyConfigError(error) {
6059
+ return isHarnessEnvironmentUnknownConfigFieldError(error, METHODOLOGY_SECTION);
6060
+ }
6061
+ function rejectLegacyHarnessMethodologyConfig(detected) {
6062
+ const harnessEnvironment = readConfigSectionFromReadResult(detected, LEGACY_METHODOLOGY_CONFIG_SECTION);
6063
+ if (!harnessEnvironment.ok) return harnessEnvironment;
6064
+ if (hasLegacyHarnessMethodologyConfig(harnessEnvironment.value)) {
6065
+ return { ok: false, error: harnessEnvironmentUnknownConfigFieldError(METHODOLOGY_SECTION) };
6066
+ }
6067
+ return { ok: true, value: void 0 };
6068
+ }
6069
+ function hasLegacyHarnessMethodologyConfig(value) {
6070
+ return typeof value === "object" && value !== null && !Array.isArray(value) && Object.hasOwn(value, METHODOLOGY_SECTION);
6071
+ }
6072
+ async function resolveMethodologyConfig(productDir) {
6073
+ const detected = await readProductConfigFile(productDir);
6074
+ if (!detected.ok) return detected;
6075
+ const legacyPlacement = rejectLegacyHarnessMethodologyConfig(detected.value);
6076
+ if (!legacyPlacement.ok) return legacyPlacement;
6077
+ const loaded = resolveConfigFromReadResult(detected.value, [methodologyConfigDescriptor]);
6078
+ if (!loaded.ok) return loaded;
6079
+ return { ok: true, value: loaded.value[methodologyConfigDescriptor.section] };
6080
+ }
6081
+
4982
6082
  // src/domains/diagnose/types.ts
4983
6083
  var VERDICT_BUCKET = {
4984
6084
  HEALTHY: "healthy",
@@ -5038,7 +6138,8 @@ var CHECK_NAME = {
5038
6138
  SPX_REACHABILITY: "spx-reachability",
5039
6139
  WORKTREE_POOL: "worktree-pool",
5040
6140
  SESSION_STORE: "session-store",
5041
- MARKETPLACE_INSTALL: "marketplace-install"
6141
+ MARKETPLACE_INSTALL: "marketplace-install",
6142
+ METHODOLOGY_CONTEXT: "methodology-context"
5042
6143
  };
5043
6144
  function validateChecks(raw, available) {
5044
6145
  if (!Array.isArray(raw) || raw.length === 0) {
@@ -5053,6 +6154,20 @@ function validateChecks(raw, available) {
5053
6154
  }
5054
6155
  return { ok: true, value: raw };
5055
6156
  }
6157
+ function validateManifestMethodology(parsed, checks) {
6158
+ if (!checks.includes(CHECK_NAME.METHODOLOGY_CONTEXT)) {
6159
+ return { ok: true, value: void 0 };
6160
+ }
6161
+ if (parsed.methodology === void 0) {
6162
+ return { ok: false, error: "manifest selects `methodology-context` but carries no `methodology`" };
6163
+ }
6164
+ if (!isRecord3(parsed.methodology) || !isNonEmptyString(parsed.methodology.source) || !isNonEmptyString(parsed.methodology.version)) {
6165
+ return { ok: false, error: "manifest selects `methodology-context` but carries incomplete `methodology`" };
6166
+ }
6167
+ const methodology = validateMethodologyConfig(parsed.methodology);
6168
+ if (!methodology.ok) return { ok: false, error: `manifest \`methodology\`: ${methodology.error}` };
6169
+ return methodology;
6170
+ }
5056
6171
  function parseManifest(rawJson, availableChecks) {
5057
6172
  let parsed;
5058
6173
  try {
@@ -5066,6 +6181,9 @@ function parseManifest(rawJson, availableChecks) {
5066
6181
  const checks = validateChecks(parsed.checks, new Set(availableChecks));
5067
6182
  if (!checks.ok) return checks;
5068
6183
  const manifest = { checks: checks.value };
6184
+ const methodology = validateManifestMethodology(parsed, checks.value);
6185
+ if (!methodology.ok) return methodology;
6186
+ manifest.methodology = methodology.value;
5069
6187
  if (checks.value.includes(CHECK_NAME.SPX_REACHABILITY)) {
5070
6188
  if (!isNonEmptyString(parsed.spx_floor)) {
5071
6189
  return { ok: false, error: "manifest selects `spx-reachability` but carries no `spx_floor`" };
@@ -5151,6 +6269,87 @@ function marketplaceInstallRunner(probe) {
5151
6269
  };
5152
6270
  }
5153
6271
 
6272
+ // src/domains/diagnose/checks/methodology-context.ts
6273
+ var METHODOLOGY_CONTEXT_VERDICT = {
6274
+ RESOLVED: "resolved",
6275
+ VERSION_MISMATCH: "version-mismatch",
6276
+ UNAVAILABLE: "unavailable",
6277
+ UNKNOWN: "unknown"
6278
+ };
6279
+ var METHODOLOGY_CONTEXT_READING_VALUE = {
6280
+ ABSENT: "(absent)"
6281
+ };
6282
+ var REMEDIATION2 = {
6283
+ [METHODOLOGY_CONTEXT_VERDICT.RESOLVED]: "Configured methodology context resolves locally; no action needed.",
6284
+ [METHODOLOGY_CONTEXT_VERDICT.VERSION_MISMATCH]: "Install the configured methodology version or change the methodology config.",
6285
+ [METHODOLOGY_CONTEXT_VERDICT.UNAVAILABLE]: "Install the configured methodology source or make it visible to the local agent runtime.",
6286
+ [METHODOLOGY_CONTEXT_VERDICT.UNKNOWN]: "Re-run diagnose; if it persists, inspect local methodology plugin installation state."
6287
+ };
6288
+ function readingValue(value) {
6289
+ return value ?? METHODOLOGY_CONTEXT_READING_VALUE.ABSENT;
6290
+ }
6291
+ function record2(verdict, bucket, reading2) {
6292
+ return {
6293
+ name: CHECK_NAME.METHODOLOGY_CONTEXT,
6294
+ verdict,
6295
+ bucket,
6296
+ readings: {
6297
+ configured: String(reading2.configured),
6298
+ configuredSource: readingValue(reading2.configuredSource),
6299
+ configuredVersion: readingValue(reading2.configuredVersion),
6300
+ observedSource: readingValue(reading2.observedSource),
6301
+ observedVersion: readingValue(reading2.observedVersion)
6302
+ },
6303
+ remediation: REMEDIATION2[verdict]
6304
+ };
6305
+ }
6306
+ function classifyMethodologyContext(reading2) {
6307
+ if (reading2.errored) {
6308
+ return record2(METHODOLOGY_CONTEXT_VERDICT.UNKNOWN, VERDICT_BUCKET.UNKNOWN, reading2);
6309
+ }
6310
+ if (reading2.observedSource === null || reading2.observedVersion === null) {
6311
+ return record2(METHODOLOGY_CONTEXT_VERDICT.UNAVAILABLE, VERDICT_BUCKET.UNKNOWN, reading2);
6312
+ }
6313
+ if (reading2.configuredVersion !== DEFAULT_METHODOLOGY_VERSION && reading2.configuredVersion !== reading2.observedVersion) {
6314
+ return record2(METHODOLOGY_CONTEXT_VERDICT.VERSION_MISMATCH, VERDICT_BUCKET.DEGRADED, reading2);
6315
+ }
6316
+ return record2(METHODOLOGY_CONTEXT_VERDICT.RESOLVED, VERDICT_BUCKET.HEALTHY, reading2);
6317
+ }
6318
+ function methodologyContextRunner(probe) {
6319
+ return async (manifest) => {
6320
+ if (manifest.methodologyError !== void 0) {
6321
+ return classifyMethodologyContext({
6322
+ configured: true,
6323
+ configuredSource: null,
6324
+ configuredVersion: null,
6325
+ observedSource: null,
6326
+ observedVersion: null,
6327
+ errored: true
6328
+ });
6329
+ }
6330
+ if (manifest.methodology === void 0) {
6331
+ return classifyMethodologyContext({
6332
+ configured: false,
6333
+ configuredSource: null,
6334
+ configuredVersion: null,
6335
+ observedSource: null,
6336
+ observedVersion: null,
6337
+ errored: true
6338
+ });
6339
+ }
6340
+ const methodology = manifest.methodology;
6341
+ const observation = await probe.probe(methodology);
6342
+ return classifyMethodologyContext({
6343
+ configured: true,
6344
+ configuredSource: methodology.source,
6345
+ configuredVersion: methodology.version,
6346
+ observedSource: observation.source,
6347
+ observedVersion: observation.version,
6348
+ errored: observation.errored
6349
+ });
6350
+ };
6351
+ }
6352
+
5154
6353
  // src/domains/diagnose/checks/session-environment.ts
5155
6354
  var SESSION_ENVIRONMENT_VERDICT = {
5156
6355
  WORKING: "working",
@@ -5166,7 +6365,7 @@ var SESSION_ENVIRONMENT_REMEDIATION = {
5166
6365
  [SESSION_ENVIRONMENT_VERDICT.NOT_APPLICABLE]: "No SessionStart hook signal or agent session identity was observed; confirm a spec-tree SessionStart hook is configured and enabled.",
5167
6366
  [SESSION_ENVIRONMENT_VERDICT.UNKNOWN]: "Re-run diagnose; if it persists, inspect the agent session id and shared worktree occupancy."
5168
6367
  };
5169
- function record2(verdict, bucket, reading2) {
6368
+ function record3(verdict, bucket, reading2) {
5170
6369
  return {
5171
6370
  name: CHECK_NAME.SESSION_ENVIRONMENT,
5172
6371
  verdict,
@@ -5181,21 +6380,21 @@ function record2(verdict, bucket, reading2) {
5181
6380
  }
5182
6381
  function classifySessionEnvironment(reading2) {
5183
6382
  if (reading2.errored) {
5184
- return record2(SESSION_ENVIRONMENT_VERDICT.UNKNOWN, VERDICT_BUCKET.UNKNOWN, reading2);
6383
+ return record3(SESSION_ENVIRONMENT_VERDICT.UNKNOWN, VERDICT_BUCKET.UNKNOWN, reading2);
5185
6384
  }
5186
6385
  if (reading2.sessionIdentity && reading2.worktreeClaimed) {
5187
- return record2(SESSION_ENVIRONMENT_VERDICT.WORKING, VERDICT_BUCKET.HEALTHY, reading2);
6386
+ return record3(SESSION_ENVIRONMENT_VERDICT.WORKING, VERDICT_BUCKET.HEALTHY, reading2);
5188
6387
  }
5189
6388
  if (reading2.sessionIdentity) {
5190
- return record2(SESSION_ENVIRONMENT_VERDICT.IDENTITY_ONLY, VERDICT_BUCKET.DEGRADED, reading2);
6389
+ return record3(SESSION_ENVIRONMENT_VERDICT.IDENTITY_ONLY, VERDICT_BUCKET.DEGRADED, reading2);
5191
6390
  }
5192
6391
  if (reading2.hookPresent && !reading2.worktreeClaimed) {
5193
- return record2(SESSION_ENVIRONMENT_VERDICT.SILENT_NO_OP, VERDICT_BUCKET.BROKEN, reading2);
6392
+ return record3(SESSION_ENVIRONMENT_VERDICT.SILENT_NO_OP, VERDICT_BUCKET.BROKEN, reading2);
5194
6393
  }
5195
6394
  if (!reading2.hookPresent && !reading2.worktreeClaimed) {
5196
- return record2(SESSION_ENVIRONMENT_VERDICT.NOT_APPLICABLE, VERDICT_BUCKET.NOT_APPLICABLE, reading2);
6395
+ return record3(SESSION_ENVIRONMENT_VERDICT.NOT_APPLICABLE, VERDICT_BUCKET.NOT_APPLICABLE, reading2);
5197
6396
  }
5198
- return record2(SESSION_ENVIRONMENT_VERDICT.UNKNOWN, VERDICT_BUCKET.UNKNOWN, reading2);
6397
+ return record3(SESSION_ENVIRONMENT_VERDICT.UNKNOWN, VERDICT_BUCKET.UNKNOWN, reading2);
5199
6398
  }
5200
6399
  function sessionEnvironmentRunner(probe) {
5201
6400
  return async () => classifySessionEnvironment(await probe.probe());
@@ -5211,12 +6410,12 @@ function doingSessionBackedByClaim(session, claimedSessionIds) {
5211
6410
  if (claimedSessionIds.has(normalizeAgentSessionToken(session.id))) return true;
5212
6411
  return session.agent_session_id !== void 0 && claimedSessionIds.has(normalizeAgentSessionToken(session.agent_session_id));
5213
6412
  }
5214
- var REMEDIATION2 = {
6413
+ var REMEDIATION3 = {
5215
6414
  [SESSION_STORE_VERDICT.CONSISTENT]: "Session store is consistent; no action needed.",
5216
6415
  [SESSION_STORE_VERDICT.ORPHANED_CLAIMS]: "Release or reclaim doing sessions whose backing worktree reads free or is absent (spx session release).",
5217
6416
  [SESSION_STORE_VERDICT.UNKNOWN]: "Re-run diagnose; if it persists, inspect spx session list and occupancy claims."
5218
6417
  };
5219
- function record3(verdict, bucket, reading2) {
6418
+ function record4(verdict, bucket, reading2) {
5220
6419
  return {
5221
6420
  name: CHECK_NAME.SESSION_STORE,
5222
6421
  verdict,
@@ -5224,17 +6423,17 @@ function record3(verdict, bucket, reading2) {
5224
6423
  readings: {
5225
6424
  orphaned: String(reading2.orphanedClaims)
5226
6425
  },
5227
- remediation: REMEDIATION2[verdict]
6426
+ remediation: REMEDIATION3[verdict]
5228
6427
  };
5229
6428
  }
5230
6429
  function classifySessionStore(reading2) {
5231
6430
  if (reading2.errored) {
5232
- return record3(SESSION_STORE_VERDICT.UNKNOWN, VERDICT_BUCKET.UNKNOWN, reading2);
6431
+ return record4(SESSION_STORE_VERDICT.UNKNOWN, VERDICT_BUCKET.UNKNOWN, reading2);
5233
6432
  }
5234
6433
  if (reading2.orphanedClaims > 0) {
5235
- return record3(SESSION_STORE_VERDICT.ORPHANED_CLAIMS, VERDICT_BUCKET.DEGRADED, reading2);
6434
+ return record4(SESSION_STORE_VERDICT.ORPHANED_CLAIMS, VERDICT_BUCKET.DEGRADED, reading2);
5236
6435
  }
5237
- return record3(SESSION_STORE_VERDICT.CONSISTENT, VERDICT_BUCKET.HEALTHY, reading2);
6436
+ return record4(SESSION_STORE_VERDICT.CONSISTENT, VERDICT_BUCKET.HEALTHY, reading2);
5238
6437
  }
5239
6438
  function sessionStoreRunner(probe) {
5240
6439
  return async () => classifySessionStore(await probe.probe());
@@ -5297,14 +6496,14 @@ function meetsFloor(version2, floor) {
5297
6496
  if (right.prerelease === null) return false;
5298
6497
  return comparePrerelease(left.prerelease, right.prerelease) >= 0;
5299
6498
  }
5300
- var REMEDIATION3 = {
6499
+ var REMEDIATION4 = {
5301
6500
  [SPX_REACHABILITY_VERDICT.REACHABLE]: "spx is on PATH at or above the required floor; no action needed.",
5302
6501
  [SPX_REACHABILITY_VERDICT.PRESENT]: "spx is on PATH; no version floor is configured to compare against, so only presence is reported.",
5303
6502
  [SPX_REACHABILITY_VERDICT.BELOW_FLOOR]: "Update spx to at least the required floor (pnpm add -g @outcomeeng/spx).",
5304
6503
  [SPX_REACHABILITY_VERDICT.UNREACHABLE]: "Install spx and ensure it resolves on PATH (pnpm add -g @outcomeeng/spx).",
5305
6504
  [SPX_REACHABILITY_VERDICT.UNKNOWN]: "Re-run diagnose; if it persists, verify the configured or manifest-supplied spx_floor is a valid semver, that spx is on PATH, and that spx --version reports a semver version."
5306
6505
  };
5307
- function record4(verdict, bucket, reading2, floor) {
6506
+ function record5(verdict, bucket, reading2, floor) {
5308
6507
  return {
5309
6508
  name: CHECK_NAME.SPX_REACHABILITY,
5310
6509
  verdict,
@@ -5314,27 +6513,27 @@ function record4(verdict, bucket, reading2, floor) {
5314
6513
  version: reading2.version ?? SPX_REACHABILITY_READING_VALUE.UNREAD_VERSION,
5315
6514
  floor: floor ?? SPX_REACHABILITY_READING_VALUE.ABSENT_FLOOR
5316
6515
  },
5317
- remediation: REMEDIATION3[verdict]
6516
+ remediation: REMEDIATION4[verdict]
5318
6517
  };
5319
6518
  }
5320
6519
  function classifySpxReachability(reading2, floor) {
5321
6520
  if (reading2.errored) {
5322
- return record4(SPX_REACHABILITY_VERDICT.UNKNOWN, VERDICT_BUCKET.UNKNOWN, reading2, floor);
6521
+ return record5(SPX_REACHABILITY_VERDICT.UNKNOWN, VERDICT_BUCKET.UNKNOWN, reading2, floor);
5323
6522
  }
5324
6523
  if (reading2.resolvedPath === null) {
5325
- return record4(SPX_REACHABILITY_VERDICT.UNREACHABLE, VERDICT_BUCKET.BROKEN, reading2, floor);
6524
+ return record5(SPX_REACHABILITY_VERDICT.UNREACHABLE, VERDICT_BUCKET.BROKEN, reading2, floor);
5326
6525
  }
5327
6526
  if (floor === void 0) {
5328
- return record4(SPX_REACHABILITY_VERDICT.PRESENT, VERDICT_BUCKET.HEALTHY, reading2, floor);
6527
+ return record5(SPX_REACHABILITY_VERDICT.PRESENT, VERDICT_BUCKET.HEALTHY, reading2, floor);
5329
6528
  }
5330
6529
  if (reading2.version === null) {
5331
- return record4(SPX_REACHABILITY_VERDICT.UNKNOWN, VERDICT_BUCKET.UNKNOWN, reading2, floor);
6530
+ return record5(SPX_REACHABILITY_VERDICT.UNKNOWN, VERDICT_BUCKET.UNKNOWN, reading2, floor);
5332
6531
  }
5333
6532
  const atOrAbove = meetsFloor(reading2.version, floor);
5334
6533
  if (atOrAbove === null) {
5335
- return record4(SPX_REACHABILITY_VERDICT.UNKNOWN, VERDICT_BUCKET.UNKNOWN, reading2, floor);
6534
+ return record5(SPX_REACHABILITY_VERDICT.UNKNOWN, VERDICT_BUCKET.UNKNOWN, reading2, floor);
5336
6535
  }
5337
- return atOrAbove ? record4(SPX_REACHABILITY_VERDICT.REACHABLE, VERDICT_BUCKET.HEALTHY, reading2, floor) : record4(SPX_REACHABILITY_VERDICT.BELOW_FLOOR, VERDICT_BUCKET.DEGRADED, reading2, floor);
6536
+ return atOrAbove ? record5(SPX_REACHABILITY_VERDICT.REACHABLE, VERDICT_BUCKET.HEALTHY, reading2, floor) : record5(SPX_REACHABILITY_VERDICT.BELOW_FLOOR, VERDICT_BUCKET.DEGRADED, reading2, floor);
5338
6537
  }
5339
6538
  function spxReachabilityRunner(probe) {
5340
6539
  return async (manifest) => classifySpxReachability(await probe.probe(), manifest.spxFloor);
@@ -5346,12 +6545,12 @@ var WORKTREE_POOL_VERDICT = {
5346
6545
  NON_COMPLIANT: "non-compliant",
5347
6546
  UNKNOWN: "unknown"
5348
6547
  };
5349
- var REMEDIATION4 = {
6548
+ var REMEDIATION5 = {
5350
6549
  [WORKTREE_POOL_VERDICT.COMPLIANT]: "Worktree layout is compliant; no action needed.",
5351
6550
  [WORKTREE_POOL_VERDICT.NON_COMPLIANT]: "Linked worktrees require a bare-repository pool; convert the layout or remove the linked worktrees.",
5352
6551
  [WORKTREE_POOL_VERDICT.UNKNOWN]: "Re-run diagnose; if it persists, inspect git worktree list and occupancy claims."
5353
6552
  };
5354
- function record5(verdict, bucket, reading2) {
6553
+ function record6(verdict, bucket, reading2) {
5355
6554
  return {
5356
6555
  name: CHECK_NAME.WORKTREE_POOL,
5357
6556
  verdict,
@@ -5362,17 +6561,17 @@ function record5(verdict, bucket, reading2) {
5362
6561
  running: String(reading2.running),
5363
6562
  free: String(reading2.free)
5364
6563
  },
5365
- remediation: REMEDIATION4[verdict]
6564
+ remediation: REMEDIATION5[verdict]
5366
6565
  };
5367
6566
  }
5368
6567
  function classifyWorktreePool(reading2) {
5369
6568
  if (reading2.errored) {
5370
- return record5(WORKTREE_POOL_VERDICT.UNKNOWN, VERDICT_BUCKET.UNKNOWN, reading2);
6569
+ return record6(WORKTREE_POOL_VERDICT.UNKNOWN, VERDICT_BUCKET.UNKNOWN, reading2);
5371
6570
  }
5372
6571
  if (!reading2.bareRepository && reading2.linkedWorktrees) {
5373
- return record5(WORKTREE_POOL_VERDICT.NON_COMPLIANT, VERDICT_BUCKET.BROKEN, reading2);
6572
+ return record6(WORKTREE_POOL_VERDICT.NON_COMPLIANT, VERDICT_BUCKET.BROKEN, reading2);
5374
6573
  }
5375
- return record5(WORKTREE_POOL_VERDICT.COMPLIANT, VERDICT_BUCKET.HEALTHY, reading2);
6574
+ return record6(WORKTREE_POOL_VERDICT.COMPLIANT, VERDICT_BUCKET.HEALTHY, reading2);
5376
6575
  }
5377
6576
  function worktreePoolRunner(probe) {
5378
6577
  return async () => classifyWorktreePool(await probe.probe());
@@ -5448,6 +6647,9 @@ var DIAGNOSE_TEXT_OVERALL_LABEL = "Diagnosis";
5448
6647
  var DIAGNOSE_TEXT_LABEL = {
5449
6648
  FIX: "Fix",
5450
6649
  INSTALLED: "Installed",
6650
+ CONFIGURED_SOURCE: "Configured source",
6651
+ CONFIGURED_VERSION: "Configured version",
6652
+ OBSERVED_VERSION: "Observed version",
5451
6653
  PATH: "Path",
5452
6654
  PROBLEM: "Problem",
5453
6655
  REQUIRED_VERSION: "Required version",
@@ -5465,6 +6667,10 @@ var DIAGNOSE_TEXT_HEADER = {
5465
6667
  MARKETPLACE_DRIFT: "plugin installation drift",
5466
6668
  MARKETPLACE_UNREGISTERED: "plugin marketplace unregistered",
5467
6669
  MARKETPLACE_UNKNOWN: "plugin marketplace state unknown",
6670
+ METHODOLOGY_RESOLVED: "methodology context resolved",
6671
+ METHODOLOGY_UNAVAILABLE: "methodology context unavailable",
6672
+ METHODOLOGY_UNKNOWN: "methodology context unknown",
6673
+ METHODOLOGY_VERSION_MISMATCH: "methodology version mismatch",
5468
6674
  RENDERING_UNAVAILABLE: "diagnosis detail unavailable",
5469
6675
  SESSION_START_NO_OP: "SessionStart hook did not establish a session",
5470
6676
  SESSION_STORE_CLEAN: "session store clean",
@@ -5484,6 +6690,9 @@ var DIAGNOSE_TEXT_DETAIL = {
5484
6690
  MARKETPLACE_CLI_UNAVAILABLE_FIX: "Install or enable the Claude or Codex plugin CLI, then rerun `spx diagnose`.",
5485
6691
  MARKETPLACE_CLI_UNAVAILABLE_PROBLEM: "A marketplace check is configured, but no plugin CLI is available to inspect it.",
5486
6692
  MARKETPLACE_CONFIGURED: "Configured plugins are installed and enabled.",
6693
+ METHODOLOGY_RESOLVED: "Configured methodology context is visible to the local agent runtime.",
6694
+ METHODOLOGY_UNAVAILABLE_FIX: "Install the configured methodology source or adjust top-level methodology config.",
6695
+ METHODOLOGY_VERSION_MISMATCH_FIX: "Install the configured methodology version or change top-level methodology.version.",
5487
6696
  MARKETPLACE_SKIPPED: "Plugin marketplace checks are not configured.",
5488
6697
  RENDERING_UNAVAILABLE: "This check produced a record this version cannot translate into diagnosis text.",
5489
6698
  SESSION_STORE_CLEAN: "No stale doing sessions found.",
@@ -5527,6 +6736,47 @@ function renderReportJson(report2) {
5527
6736
  2
5528
6737
  );
5529
6738
  }
6739
+ function methodologyContextText(check) {
6740
+ const configuredSource = reading(check, "configuredSource");
6741
+ const configuredVersion = reading(check, "configuredVersion");
6742
+ const observedVersion = reading(check, "observedVersion");
6743
+ switch (check.verdict) {
6744
+ case METHODOLOGY_CONTEXT_VERDICT.RESOLVED:
6745
+ return {
6746
+ header: DIAGNOSE_TEXT_HEADER.METHODOLOGY_RESOLVED,
6747
+ details: [
6748
+ DIAGNOSE_TEXT_DETAIL.METHODOLOGY_RESOLVED,
6749
+ `${DIAGNOSE_TEXT_LABEL.CONFIGURED_SOURCE}: ${configuredSource}`,
6750
+ `${DIAGNOSE_TEXT_LABEL.OBSERVED_VERSION}: ${observedVersion}`
6751
+ ]
6752
+ };
6753
+ case METHODOLOGY_CONTEXT_VERDICT.VERSION_MISMATCH:
6754
+ return {
6755
+ header: DIAGNOSE_TEXT_HEADER.METHODOLOGY_VERSION_MISMATCH,
6756
+ details: [
6757
+ `${DIAGNOSE_TEXT_LABEL.CONFIGURED_VERSION}: ${configuredVersion}`,
6758
+ `${DIAGNOSE_TEXT_LABEL.OBSERVED_VERSION}: ${observedVersion}`,
6759
+ `${DIAGNOSE_TEXT_LABEL.FIX}: ${DIAGNOSE_TEXT_DETAIL.METHODOLOGY_VERSION_MISMATCH_FIX}`
6760
+ ]
6761
+ };
6762
+ case METHODOLOGY_CONTEXT_VERDICT.UNAVAILABLE:
6763
+ return {
6764
+ header: DIAGNOSE_TEXT_HEADER.METHODOLOGY_UNAVAILABLE,
6765
+ details: [
6766
+ `${DIAGNOSE_TEXT_LABEL.CONFIGURED_SOURCE}: ${configuredSource}`,
6767
+ `${DIAGNOSE_TEXT_LABEL.CONFIGURED_VERSION}: ${configuredVersion}`,
6768
+ `${DIAGNOSE_TEXT_LABEL.FIX}: ${DIAGNOSE_TEXT_DETAIL.METHODOLOGY_UNAVAILABLE_FIX}`
6769
+ ]
6770
+ };
6771
+ case METHODOLOGY_CONTEXT_VERDICT.UNKNOWN:
6772
+ return {
6773
+ header: DIAGNOSE_TEXT_HEADER.METHODOLOGY_UNKNOWN,
6774
+ details: [`${DIAGNOSE_TEXT_LABEL.FIX}: ${DIAGNOSE_TEXT_DETAIL.UNKNOWN_RETRY}`]
6775
+ };
6776
+ default:
6777
+ return fallbackText(check);
6778
+ }
6779
+ }
5530
6780
  function reading(check, key) {
5531
6781
  return check.readings[key];
5532
6782
  }
@@ -5733,6 +6983,8 @@ function humanText(check) {
5733
6983
  return sessionStoreText(check);
5734
6984
  case CHECK_NAME.MARKETPLACE_INSTALL:
5735
6985
  return marketplaceInstallText(check);
6986
+ case CHECK_NAME.METHODOLOGY_CONTEXT:
6987
+ return methodologyContextText(check);
5736
6988
  default:
5737
6989
  return fallbackText(check);
5738
6990
  }
@@ -5765,33 +7017,37 @@ var CHECK_NAMES = new Set(Object.values(CHECK_NAME));
5765
7017
  function isCheckName(value) {
5766
7018
  return CHECK_NAMES.has(value);
5767
7019
  }
7020
+ function resolveDiagnoseCheckSet(config, availableChecks) {
7021
+ const configuredChecks = config.checks;
7022
+ if (configuredChecks === void 0) {
7023
+ return { ok: true, value: availableChecks };
7024
+ }
7025
+ const available = new Set(availableChecks);
7026
+ const unavailable = configuredChecks.filter((name) => !isCheckName(name) || !available.has(name));
7027
+ if (unavailable.length > 0) {
7028
+ return {
7029
+ ok: false,
7030
+ error: `diagnose config \`checks\` names checks not available in this build: ${unavailable.join(", ")}`
7031
+ };
7032
+ }
7033
+ return { ok: true, value: configuredChecks.filter(isCheckName) };
7034
+ }
5768
7035
  function resolveDiagnoseFacts(options) {
5769
7036
  if (options.manifest !== void 0) {
5770
7037
  return { ok: true, value: options.manifest };
5771
7038
  }
5772
7039
  const { config, availableChecks } = options;
5773
- const configuredChecks = config.checks;
5774
- let checks;
5775
- if (configuredChecks === void 0) {
5776
- checks = availableChecks;
5777
- } else {
5778
- const available = new Set(availableChecks);
5779
- const unavailable = configuredChecks.filter((name) => !isCheckName(name) || !available.has(name));
5780
- if (unavailable.length > 0) {
5781
- return {
5782
- ok: false,
5783
- error: `diagnose config \`checks\` names checks not available in this build: ${unavailable.join(", ")}`
5784
- };
5785
- }
5786
- checks = configuredChecks.filter(isCheckName);
5787
- }
7040
+ const checks = resolveDiagnoseCheckSet(config, availableChecks);
7041
+ if (!checks.ok) return checks;
5788
7042
  return {
5789
7043
  ok: true,
5790
7044
  value: {
5791
- checks,
7045
+ checks: checks.value,
5792
7046
  spxFloor: config.spxFloor,
5793
7047
  marketplace: config.marketplace,
5794
- expectedPlugins: config.expectedPlugins
7048
+ expectedPlugins: config.expectedPlugins,
7049
+ methodology: options.methodology,
7050
+ methodologyError: options.methodologyError
5795
7051
  }
5796
7052
  };
5797
7053
  }
@@ -5811,15 +7067,36 @@ async function readManifest(fs8, manifestPath, availableChecks) {
5811
7067
  }
5812
7068
  return parseManifest(raw, availableChecks);
5813
7069
  }
7070
+ function shouldResolveMethodologyConfig(manifest, checks) {
7071
+ if (manifest !== void 0) {
7072
+ return false;
7073
+ }
7074
+ return checks.includes(CHECK_NAME.METHODOLOGY_CONTEXT);
7075
+ }
5814
7076
  async function diagnoseCommand(options) {
5815
7077
  const availableChecks = Object.keys(options.registry);
5816
7078
  const manifest = options.manifestPath === void 0 ? void 0 : await readManifest(options.fs, options.manifestPath, availableChecks);
5817
7079
  if (manifest !== void 0 && !manifest.ok) return manifest;
5818
- const config = manifest === void 0 ? await resolveDiagnoseConfig(options.productDir) : { ok: true, value: {} };
7080
+ const config = manifest === void 0 ? await resolveDiagnoseConfig(options.productDir) : { ok: true, value: void 0 };
5819
7081
  if (!config.ok) return config;
7082
+ const resolvedChecks = resolveDiagnoseCheckSet(config.value ?? {}, availableChecks);
7083
+ if (!resolvedChecks.ok) return resolvedChecks;
7084
+ let methodology;
7085
+ let methodologyError;
7086
+ if (shouldResolveMethodologyConfig(manifest?.value, resolvedChecks.value)) {
7087
+ const methodologyConfig = await resolveMethodologyConfig(options.productDir);
7088
+ if (methodologyConfig.ok) {
7089
+ methodology = methodologyConfig.value;
7090
+ } else {
7091
+ if (isLegacyHarnessMethodologyConfigError(methodologyConfig.error)) return methodologyConfig;
7092
+ methodologyError = methodologyConfig.error;
7093
+ }
7094
+ }
5820
7095
  const resolved = resolveDiagnoseFacts({
5821
7096
  manifest: manifest?.value,
5822
- config: config.value,
7097
+ config: config.value ?? {},
7098
+ methodology,
7099
+ methodologyError,
5823
7100
  availableChecks
5824
7101
  });
5825
7102
  if (!resolved.ok) return resolved;
@@ -5835,7 +7112,8 @@ async function diagnoseCommand(options) {
5835
7112
  }
5836
7113
 
5837
7114
  // src/commands/diagnose/probes.ts
5838
- import { basename as basename3, dirname as dirname3 } from "path";
7115
+ import { readdir as readdir3 } from "fs/promises";
7116
+ import { basename as basename3, dirname as dirname3, join as join6 } from "path";
5839
7117
  import { execa as execa3 } from "execa";
5840
7118
 
5841
7119
  // src/domains/hooks/session-start.ts
@@ -5896,13 +7174,13 @@ function parseHookSessionStartPayload(content) {
5896
7174
  if (typeof parsed !== "object" || parsed === null || Array.isArray(parsed)) {
5897
7175
  return { ok: false, error: HOOK_SESSION_START_ERROR.PAYLOAD_MALFORMED };
5898
7176
  }
5899
- const record6 = parsed;
7177
+ const record7 = parsed;
5900
7178
  return {
5901
7179
  ok: true,
5902
7180
  value: {
5903
- cwd: nonEmptyString(record6[HOOK_SESSION_START_PAYLOAD.CWD]),
5904
- sessionId: nonEmptyString(record6[HOOK_SESSION_START_PAYLOAD.SESSION_ID]),
5905
- source: nonEmptyString(record6[HOOK_SESSION_START_PAYLOAD.SOURCE])
7181
+ cwd: nonEmptyString(record7[HOOK_SESSION_START_PAYLOAD.CWD]),
7182
+ sessionId: nonEmptyString(record7[HOOK_SESSION_START_PAYLOAD.SESSION_ID]),
7183
+ source: nonEmptyString(record7[HOOK_SESSION_START_PAYLOAD.SOURCE])
5906
7184
  }
5907
7185
  };
5908
7186
  }
@@ -5988,8 +7266,8 @@ function claimLockPath(claimPath) {
5988
7266
  function claimLockRecoveryPath(lockPath) {
5989
7267
  return `${lockPath}${OCCUPANCY_CLAIM.LOCK_RECOVERY_EXTENSION}`;
5990
7268
  }
5991
- function claimLockTarget(record6) {
5992
- return serializeClaim(record6);
7269
+ function claimLockTarget(record7) {
7270
+ return serializeClaim(record7);
5993
7271
  }
5994
7272
  function classifyOccupancy(claim, probe) {
5995
7273
  if (claim === void 0) return OCCUPANCY_STATUS.FREE;
@@ -6011,7 +7289,7 @@ function createClaimOperationRecord(sessionId, pid, probe) {
6011
7289
  startedAt: probe.startTimeOf(pid) ?? unreadableStartedAt(pid)
6012
7290
  };
6013
7291
  }
6014
- async function acquireClaim(worktreesDir, name, record6, probe, options) {
7292
+ async function acquireClaim(worktreesDir, name, record7, probe, options) {
6015
7293
  const pathResult = claimFilePath(worktreesDir, name);
6016
7294
  if (!pathResult.ok) return pathResult;
6017
7295
  const claimPath = pathResult.value;
@@ -6024,7 +7302,7 @@ async function acquireClaim(worktreesDir, name, record6, probe, options) {
6024
7302
  if (!lock.ok) return lock;
6025
7303
  let acquired;
6026
7304
  try {
6027
- acquired = await acquireClaimWhileLocked(claimPath, record6, probe, options);
7305
+ acquired = await acquireClaimWhileLocked(claimPath, record7, probe, options);
6028
7306
  } catch (error) {
6029
7307
  acquired = { ok: false, error: formatOccupancyError(OCCUPANCY_ERROR.CLAIM_WRITE_FAILED, toErrorMessage2(error)) };
6030
7308
  }
@@ -6070,16 +7348,16 @@ async function removeClaimWhileLocked(claimPath, owner, fs8) {
6070
7348
  return { ok: false, error: formatOccupancyError(OCCUPANCY_ERROR.CLAIM_REMOVE_FAILED, toErrorMessage2(error)) };
6071
7349
  }
6072
7350
  }
6073
- async function acquireClaimWhileLocked(claimPath, record6, probe, options) {
7351
+ async function acquireClaimWhileLocked(claimPath, record7, probe, options) {
6074
7352
  const current = await readClaimAtPath(claimPath, options.fs);
6075
7353
  if (!current.ok) return current;
6076
- if (current.value !== void 0 && sameClaimOwner(current.value, record6)) {
7354
+ if (current.value !== void 0 && sameClaimOwner(current.value, record7)) {
6077
7355
  return { ok: true, value: claimPath };
6078
7356
  }
6079
7357
  if (classifyOccupancy(current.value, probe) === OCCUPANCY_STATUS.RUNNING) {
6080
7358
  return { ok: false, error: OCCUPANCY_ERROR.CLAIM_HELD };
6081
7359
  }
6082
- return writeClaimAtPath(claimPath, record6, options);
7360
+ return writeClaimAtPath(claimPath, record7, options);
6083
7361
  }
6084
7362
  async function acquireClaimLock(lockPath, owner, probe, fs8) {
6085
7363
  try {
@@ -6118,9 +7396,9 @@ async function removeOwnedClaimLock(lockPath, expectedTarget, fs8) {
6118
7396
  return { ok: false, error: formatOccupancyError(OCCUPANCY_ERROR.CLAIM_UNLOCK_FAILED, toErrorMessage2(error)) };
6119
7397
  }
6120
7398
  }
6121
- async function writeClaimAtPath(claimPath, record6, options) {
7399
+ async function writeClaimAtPath(claimPath, record7, options) {
6122
7400
  try {
6123
- await writeFileAtomic(claimPath, serializeClaim(record6), options);
7401
+ await writeFileAtomic(claimPath, serializeClaim(record7), options);
6124
7402
  return { ok: true, value: claimPath };
6125
7403
  } catch (error) {
6126
7404
  return { ok: false, error: formatOccupancyError(OCCUPANCY_ERROR.CLAIM_WRITE_FAILED, toErrorMessage2(error)) };
@@ -6136,12 +7414,12 @@ async function readClaimAtPath(claimPath, fs8) {
6136
7414
  }
6137
7415
  return parseClaim(content);
6138
7416
  }
6139
- function serializeClaim(record6) {
7417
+ function serializeClaim(record7) {
6140
7418
  return JSON.stringify({
6141
- sessionId: record6.sessionId,
6142
- host: record6.host,
6143
- pid: record6.pid,
6144
- startedAt: record6.startedAt
7419
+ sessionId: record7.sessionId,
7420
+ host: record7.host,
7421
+ pid: record7.pid,
7422
+ startedAt: record7.startedAt
6145
7423
  });
6146
7424
  }
6147
7425
  function parseClaim(content) {
@@ -6389,6 +7667,9 @@ var defaultProcessTable = {
6389
7667
  // src/commands/diagnose/probes.ts
6390
7668
  var DIAGNOSE_SPX_EXECUTABLE = "spx";
6391
7669
  var DIAGNOSE_DOING_SESSION_ARGS = ["session", "list", "--status", "doing", "--json"];
7670
+ var PLUGIN_CACHE_SEGMENTS = ["plugins", "cache"];
7671
+ var NOT_FOUND_ERROR_CODE = "ENOENT";
7672
+ var VERSION_DIRECTORY_PATTERN = /^\d+(?:\.\d+)*$/;
6392
7673
  var defaultWorktreePoolSnapshotDependencies = {
6393
7674
  env: process.env,
6394
7675
  gatherGitFacts,
@@ -6570,15 +7851,15 @@ function parseInstalledPlugins(stdout) {
6570
7851
  const parsed = JSON.parse(stdout);
6571
7852
  if (Array.isArray(parsed)) {
6572
7853
  return parsed.map((entry) => {
6573
- const record6 = entry;
6574
- return { name: String(record6.id ?? "").split("@")[0], enabled: record6.enabled === true };
7854
+ const record7 = entry;
7855
+ return { name: String(record7.id ?? "").split("@")[0], enabled: record7.enabled === true };
6575
7856
  });
6576
7857
  }
6577
7858
  const installed2 = parsed.installed;
6578
7859
  if (Array.isArray(installed2)) {
6579
7860
  return installed2.map((entry) => {
6580
- const record6 = entry;
6581
- return { name: String(record6.name ?? ""), enabled: record6.enabled === true };
7861
+ const record7 = entry;
7862
+ return { name: String(record7.name ?? ""), enabled: record7.enabled === true };
6582
7863
  });
6583
7864
  }
6584
7865
  return null;
@@ -6591,15 +7872,15 @@ function parseRegisteredMarketplaces(stdout) {
6591
7872
  const parsed = JSON.parse(stdout);
6592
7873
  if (Array.isArray(parsed)) {
6593
7874
  return parsed.map((entry) => {
6594
- const record6 = entry;
6595
- return { name: String(record6.name ?? ""), source: String(record6.repo ?? "") };
7875
+ const record7 = entry;
7876
+ return { name: String(record7.name ?? ""), source: String(record7.repo ?? "") };
6596
7877
  });
6597
7878
  }
6598
7879
  const marketplaces = parsed.marketplaces;
6599
7880
  if (Array.isArray(marketplaces)) {
6600
7881
  return marketplaces.map((entry) => {
6601
- const record6 = entry;
6602
- return { name: String(record6.name ?? ""), source: String(record6.marketplaceSource?.source ?? "") };
7882
+ const record7 = entry;
7883
+ return { name: String(record7.name ?? ""), source: String(record7.marketplaceSource?.source ?? "") };
6603
7884
  });
6604
7885
  }
6605
7886
  return null;
@@ -6649,6 +7930,67 @@ var defaultMarketplaceInstallProbe = {
6649
7930
  return reading2;
6650
7931
  }
6651
7932
  };
7933
+ function isNodeErrorCode(error, code) {
7934
+ return error instanceof Error && "code" in error && error.code === code;
7935
+ }
7936
+ async function versionDirectories(path7) {
7937
+ try {
7938
+ const entries = await readdir3(path7, { withFileTypes: true });
7939
+ const versions = entries.filter((entry) => entry.isDirectory()).map((entry) => entry.name);
7940
+ return { errored: false, versions };
7941
+ } catch (error) {
7942
+ if (isNodeErrorCode(error, NOT_FOUND_ERROR_CODE)) {
7943
+ return { errored: false, versions: [] };
7944
+ }
7945
+ return { errored: true, versions: [] };
7946
+ }
7947
+ }
7948
+ function isVersionDirectoryName(name) {
7949
+ return VERSION_DIRECTORY_PATTERN.test(name);
7950
+ }
7951
+ function selectConfiguredVersion(readings, config) {
7952
+ const versions = readings.flatMap((reading2) => reading2.versions);
7953
+ const validVersions = versions.filter(isVersionDirectoryName).sort(compareNumericVersionIdentifiers);
7954
+ let version2;
7955
+ if (config.version === DEFAULT_METHODOLOGY_VERSION) {
7956
+ version2 = validVersions.at(-1) ?? null;
7957
+ } else {
7958
+ version2 = versions.find((candidate) => candidate === config.version) ?? validVersions.at(-1) ?? null;
7959
+ }
7960
+ return {
7961
+ errored: readings.some((reading2) => reading2.errored),
7962
+ version: version2
7963
+ };
7964
+ }
7965
+ async function configuredVersionDirectory(paths, config) {
7966
+ return selectConfiguredVersion(
7967
+ await Promise.all(paths.map((path7) => versionDirectories(path7))),
7968
+ config
7969
+ );
7970
+ }
7971
+ function createMethodologyContextProbe(...agentHomeDirs) {
7972
+ const resolvedHomes = resolveAgentHomeDirs();
7973
+ const homeDirs = agentHomeDirs.length > 0 ? agentHomeDirs : [resolvedHomes.codex, resolvedHomes.claudeCode];
7974
+ return {
7975
+ async probe(config) {
7976
+ const sourcePaths = homeDirs.map((home) => join6(home, ...PLUGIN_CACHE_SEGMENTS, ...config.source.split("/")));
7977
+ const reading2 = await configuredVersionDirectory(sourcePaths, config);
7978
+ if (reading2.version === null) {
7979
+ return { source: null, version: null, errored: reading2.errored };
7980
+ }
7981
+ return {
7982
+ source: config.source,
7983
+ version: reading2.version,
7984
+ errored: reading2.errored
7985
+ };
7986
+ }
7987
+ };
7988
+ }
7989
+ var defaultMethodologyContextProbe = {
7990
+ probe(config) {
7991
+ return createMethodologyContextProbe().probe(config);
7992
+ }
7993
+ };
6652
7994
 
6653
7995
  // src/commands/diagnose/spx-reachability-probe.ts
6654
7996
  import { execa as execa4 } from "execa";
@@ -6692,7 +8034,8 @@ function defaultRegistry() {
6692
8034
  ),
6693
8035
  [CHECK_NAME.WORKTREE_POOL]: worktreePoolRunner(worktreePoolProbeFromSnapshotProvider(worktreePoolSnapshot)),
6694
8036
  [CHECK_NAME.SESSION_STORE]: sessionStoreRunner(sessionStoreProbeFromSnapshotProvider(worktreePoolSnapshot)),
6695
- [CHECK_NAME.MARKETPLACE_INSTALL]: marketplaceInstallRunner(defaultMarketplaceInstallProbe)
8037
+ [CHECK_NAME.MARKETPLACE_INSTALL]: marketplaceInstallRunner(defaultMarketplaceInstallProbe),
8038
+ [CHECK_NAME.METHODOLOGY_CONTEXT]: methodologyContextRunner(defaultMethodologyContextProbe)
6696
8039
  };
6697
8040
  }
6698
8041
  function handleError2(error, io) {
@@ -6801,14 +8144,14 @@ function isValidPid(pid) {
6801
8144
  }
6802
8145
 
6803
8146
  // src/domains/worktree/resolve.ts
6804
- import { basename as basename4, dirname as dirname4, resolve as resolve5 } from "path";
8147
+ import { basename as basename4, dirname as dirname4, resolve as resolve6 } from "path";
6805
8148
  var WORKTREE_RESOLVE_ERROR = {
6806
8149
  AMBIGUOUS_WORKTREE_BASENAME: "ambiguous worktree basename",
6807
8150
  NOT_A_WORKTREE: "path resolves to no worktree",
6808
8151
  WORKTREE_LIST_UNAVAILABLE: "git worktree list is unavailable"
6809
8152
  };
6810
8153
  async function resolveWorktreesDir(options) {
6811
- if (options.worktreesDir !== void 0) return resolve5(options.cwd, options.worktreesDir);
8154
+ if (options.worktreesDir !== void 0) return resolve6(options.cwd, options.worktreesDir);
6812
8155
  const resolved = await resolveWorktreesScopeDir({ cwd: options.cwd, deps: options.gitDeps });
6813
8156
  options.onWarning?.(resolved.warning);
6814
8157
  return resolved.worktreesDir;
@@ -6835,7 +8178,7 @@ async function resolveAllTargetWorktrees(options) {
6835
8178
  }
6836
8179
  async function resolveTargetWorktree(options) {
6837
8180
  const base = options.cwd;
6838
- const targetPath = options.worktree === void 0 ? base : resolve5(base, options.worktree);
8181
+ const targetPath = options.worktree === void 0 ? base : resolve6(base, options.worktree);
6839
8182
  const targetGitPath = await options.pathInfo.isExistingNonDirectory(targetPath) ? dirname4(targetPath) : targetPath;
6840
8183
  const worktree = await detectWorktreeProductRoot(targetGitPath, options.gitDeps);
6841
8184
  if (!worktree.isGitRepo) {
@@ -7036,17 +8379,17 @@ function createProcessHookIo(streams) {
7036
8379
  return {
7037
8380
  readStdin: async () => {
7038
8381
  if (streams.stdin.isTTY) return { ok: true, value: void 0 };
7039
- return new Promise((resolve12) => {
8382
+ return new Promise((resolve13) => {
7040
8383
  let data = "";
7041
8384
  streams.stdin.setEncoding("utf-8");
7042
8385
  streams.stdin.on(HOOK_PROCESS_IO_EVENT.DATA, (chunk) => {
7043
8386
  data += chunk;
7044
8387
  });
7045
8388
  streams.stdin.on(HOOK_PROCESS_IO_EVENT.END, () => {
7046
- resolve12({ ok: true, value: data.length === 0 ? void 0 : data });
8389
+ resolve13({ ok: true, value: data.length === 0 ? void 0 : data });
7047
8390
  });
7048
8391
  streams.stdin.on(HOOK_PROCESS_IO_EVENT.ERROR, (error) => {
7049
- resolve12({ ok: false, error: formatStdinReadError(error) });
8392
+ resolve13({ ok: false, error: formatStdinReadError(error) });
7050
8393
  });
7051
8394
  });
7052
8395
  },
@@ -7204,7 +8547,7 @@ function resolveJournalBackend(env) {
7204
8547
  }
7205
8548
 
7206
8549
  // src/domains/journal/run-scope.ts
7207
- import { join as join6 } from "path";
8550
+ import { join as join7 } from "path";
7208
8551
 
7209
8552
  // src/domains/journal/run-state.ts
7210
8553
  var JOURNAL_RUN_STATE_STATUS = {
@@ -7222,11 +8565,14 @@ var JOURNAL_RUN_STATE_INCOMPLETE_REASON = {
7222
8565
  UNSEALED: "unsealed",
7223
8566
  SHAPE_INVALID_STATE: "shape-invalid-state"
7224
8567
  };
8568
+ var JOURNAL_RUN_EVENT_TYPE_SUFFIX = {
8569
+ COMPLETED: ".journal.run.completed"
8570
+ };
7225
8571
  var JOURNAL_RUN_EVENT = {
7226
8572
  SOURCE: "/spx/journal",
7227
8573
  STARTED_TYPE: `${RUNTIME_EVENT_NAMESPACE_DEFAULT}.journal.run.started`,
7228
8574
  PROGRESS_TYPE: `${RUNTIME_EVENT_NAMESPACE_DEFAULT}.journal.run.progress`,
7229
- COMPLETED_TYPE: `${RUNTIME_EVENT_NAMESPACE_DEFAULT}.journal.run.completed`
8575
+ COMPLETED_TYPE: `${RUNTIME_EVENT_NAMESPACE_DEFAULT}${JOURNAL_RUN_EVENT_TYPE_SUFFIX.COMPLETED}`
7230
8576
  };
7231
8577
  var JOURNAL_RUN_STATE_FIELDS = {
7232
8578
  BRANCH_NAME: "branchName",
@@ -7250,7 +8596,7 @@ function foldJournalRunState(events, sealed) {
7250
8596
  }
7251
8597
  let completed;
7252
8598
  for (const event of events) {
7253
- if (event.type === JOURNAL_RUN_EVENT.COMPLETED_TYPE) completed = event;
8599
+ if (isJournalRunCompletedEventType(event.type)) completed = event;
7254
8600
  }
7255
8601
  if (completed === void 0) {
7256
8602
  return {
@@ -7268,6 +8614,9 @@ function foldJournalRunState(events, sealed) {
7268
8614
  }
7269
8615
  return validated;
7270
8616
  }
8617
+ function isJournalRunCompletedEventType(type) {
8618
+ return type.endsWith(JOURNAL_RUN_EVENT_TYPE_SUFFIX.COMPLETED);
8619
+ }
7271
8620
  function isJournalRunStateStatus(value) {
7272
8621
  return typeof value === "string" && Object.values(JOURNAL_RUN_STATE_STATUS).includes(
7273
8622
  value
@@ -7277,7 +8626,7 @@ function isJournalTargetKind(value) {
7277
8626
  return typeof value === "string" && Object.values(JOURNAL_TARGET_KIND).includes(value);
7278
8627
  }
7279
8628
  function validateJournalRunState(value) {
7280
- if (!isRecord5(value)) {
8629
+ if (!isRecord6(value)) {
7281
8630
  return { ok: false, error: "journal run state must be an object" };
7282
8631
  }
7283
8632
  const identity = readRunStateIdentity(value);
@@ -7389,7 +8738,7 @@ function readTargetKind(value, field) {
7389
8738
  const raw = value[field];
7390
8739
  return isJournalTargetKind(raw) ? { ok: true, value: raw } : { ok: false, error: `${field} must be a journal target kind` };
7391
8740
  }
7392
- function isRecord5(value) {
8741
+ function isRecord6(value) {
7393
8742
  return typeof value === "object" && value !== null && !Array.isArray(value);
7394
8743
  }
7395
8744
 
@@ -7429,20 +8778,13 @@ function journalRunTypeFromEntry(name, isDirectory2) {
7429
8778
  }
7430
8779
  function journalRunFileNameFromEntry(name, isFile) {
7431
8780
  if (!isFile) return void 0;
7432
- const runToken = journalRunTokenFromFileName(name);
8781
+ const runToken = runTokenFromRunFileName(name);
7433
8782
  return runToken === void 0 ? void 0 : { runFileName: name, runToken };
7434
8783
  }
7435
- function journalRunTokenFromFileName(name) {
7436
- return isRunFileName(name) ? name.slice(STATE_STORE_PATH.RUN_FILE_PREFIX.length, -STATE_STORE_PATH.JSONL_EXTENSION.length) : void 0;
7437
- }
7438
8784
  function journalRunTerminalState(events, sealed) {
7439
8785
  const folded = foldJournalRunState(events, sealed);
7440
8786
  return folded.ok ? folded.value.status : folded.reason;
7441
8787
  }
7442
- function journalRunStartedAt(runToken) {
7443
- const separatorIndex = runToken.lastIndexOf("-");
7444
- return separatorIndex < 0 ? runToken : runToken.slice(0, separatorIndex);
7445
- }
7446
8788
  function journalRunMetadataMatches(scope2, metadata) {
7447
8789
  const sealed = scope2.sealed ?? JOURNAL_RUN_SEALED_FILTER.ANY;
7448
8790
  if (sealed === JOURNAL_RUN_SEALED_FILTER.SEALED && !metadata.sealed) return false;
@@ -7450,24 +8792,12 @@ function journalRunMetadataMatches(scope2, metadata) {
7450
8792
  const terminalState = scope2.terminalState ?? JOURNAL_RUN_TERMINAL_FILTER.ANY;
7451
8793
  return terminalState === JOURNAL_RUN_TERMINAL_FILTER.ANY || metadata.terminalState === terminalState;
7452
8794
  }
7453
- function compareJournalRunsNewestFirst(left, right) {
7454
- const startedAtOrder = compareAsciiStrings(left.startedAt, right.startedAt);
7455
- if (startedAtOrder !== 0) return -startedAtOrder;
7456
- const createdAtOrder = left.createdAtMs - right.createdAtMs;
7457
- return createdAtOrder === 0 ? -compareAsciiStrings(left.runToken, right.runToken) : -createdAtOrder;
7458
- }
7459
- function compareJournalRunsOldestFirst(left, right) {
7460
- const startedAtOrder = compareAsciiStrings(left.startedAt, right.startedAt);
7461
- if (startedAtOrder !== 0) return startedAtOrder;
7462
- const createdAtOrder = left.createdAtMs - right.createdAtMs;
7463
- return createdAtOrder === 0 ? compareAsciiStrings(left.runToken, right.runToken) : createdAtOrder;
7464
- }
7465
8795
  function journalRunFilePath(scope2) {
7466
8796
  const typeRunsDir = journalRunsDir(scope2);
7467
8797
  if (!typeRunsDir.ok) return typeRunsDir;
7468
8798
  const runToken = validateScopeToken(scope2.runToken);
7469
8799
  if (!runToken.ok) return runToken;
7470
- return { ok: true, value: join6(typeRunsDir.value, runFileName(runToken.value)) };
8800
+ return { ok: true, value: join7(typeRunsDir.value, runFileName(runToken.value)) };
7471
8801
  }
7472
8802
 
7473
8803
  // src/lib/artifact-journal-store/index.ts
@@ -7516,9 +8846,9 @@ function checkJournalEventConformance(value) {
7516
8846
  error: "event must be an object"
7517
8847
  };
7518
8848
  }
7519
- const record6 = value;
8849
+ const record7 = value;
7520
8850
  const allowed = /* @__PURE__ */ new Set([...JOURNAL_EVENT_ATTRIBUTES, "data"]);
7521
- for (const name of Object.keys(record6)) {
8851
+ for (const name of Object.keys(record7)) {
7522
8852
  if (!ATTRIBUTE_NAME_PATTERN.test(name)) {
7523
8853
  return {
7524
8854
  ok: false,
@@ -7535,7 +8865,7 @@ function checkJournalEventConformance(value) {
7535
8865
  }
7536
8866
  }
7537
8867
  for (const name of JOURNAL_EVENT_ATTRIBUTES) {
7538
- if (!(name in record6)) {
8868
+ if (!(name in record7)) {
7539
8869
  return {
7540
8870
  ok: false,
7541
8871
  violation: JOURNAL_CONFORMANCE_VIOLATION.MISSING_ATTRIBUTE,
@@ -7544,7 +8874,7 @@ function checkJournalEventConformance(value) {
7544
8874
  }
7545
8875
  }
7546
8876
  for (const name of STRING_ATTRIBUTES) {
7547
- if (typeof record6[name] !== "string") {
8877
+ if (typeof record7[name] !== "string") {
7548
8878
  return {
7549
8879
  ok: false,
7550
8880
  violation: JOURNAL_CONFORMANCE_VIOLATION.WRONG_TYPE,
@@ -7552,21 +8882,21 @@ function checkJournalEventConformance(value) {
7552
8882
  };
7553
8883
  }
7554
8884
  }
7555
- if (record6.specversion !== CLOUDEVENTS_SPECVERSION) {
8885
+ if (record7.specversion !== CLOUDEVENTS_SPECVERSION) {
7556
8886
  return {
7557
8887
  ok: false,
7558
8888
  violation: JOURNAL_CONFORMANCE_VIOLATION.WRONG_SPECVERSION,
7559
8889
  error: `specversion must equal "${CLOUDEVENTS_SPECVERSION}"`
7560
8890
  };
7561
8891
  }
7562
- if (typeof record6.seq !== "number" || !Number.isInteger(record6.seq)) {
8892
+ if (typeof record7.seq !== "number" || !Number.isInteger(record7.seq)) {
7563
8893
  return {
7564
8894
  ok: false,
7565
8895
  violation: JOURNAL_CONFORMANCE_VIOLATION.WRONG_TYPE,
7566
8896
  error: 'attribute "seq" must be an integer'
7567
8897
  };
7568
8898
  }
7569
- if (typeof record6.attempt !== "number" || !Number.isInteger(record6.attempt)) {
8899
+ if (typeof record7.attempt !== "number" || !Number.isInteger(record7.attempt)) {
7570
8900
  return {
7571
8901
  ok: false,
7572
8902
  violation: JOURNAL_CONFORMANCE_VIOLATION.WRONG_TYPE,
@@ -7643,12 +8973,12 @@ function createAppendableJournalStore(options) {
7643
8973
  }
7644
8974
  return {
7645
8975
  kind: JOURNAL_BACKEND_KIND.APPENDABLE,
7646
- async append(record6) {
8976
+ async append(record7) {
7647
8977
  const history = await readAll();
7648
- if (history.some((event) => event.seq === record6.seq)) {
8978
+ if (history.some((event) => event.seq === record7.seq)) {
7649
8979
  throw new Error(JOURNAL_ERROR.SEQ_CONSUMED);
7650
8980
  }
7651
- const result = await appendJsonlRecord(runFilePath, toJsonRecord(record6), { fs: fs8 });
8981
+ const result = await appendJsonlRecord(runFilePath, toJsonRecord(record7), { fs: fs8 });
7652
8982
  if (!result.ok) throw new Error(result.error);
7653
8983
  },
7654
8984
  readAll,
@@ -7957,7 +9287,7 @@ async function readRunMetadata(productDir, branchSlug, type, runFileNameValue, f
7957
9287
  runToken,
7958
9288
  runFilePath: runFile.value,
7959
9289
  runFileName: runFileName2,
7960
- startedAt: journalRunStartedAt(runToken),
9290
+ startedAt: runTokenStartedAt(runToken),
7961
9291
  createdAtMs: stats.birthtimeMs,
7962
9292
  sealed,
7963
9293
  eventCount: events.length,
@@ -8039,7 +9369,7 @@ async function listJournalRuns(scope2, options = {}) {
8039
9369
  if (!branchRuns.ok) return branchRuns;
8040
9370
  runs.push(...branchRuns.value);
8041
9371
  }
8042
- const sorted = runs.sort(compareJournalRunsNewestFirst);
9372
+ const sorted = runs.sort(compareRunRecencyNewestFirst);
8043
9373
  return { ok: true, value: applyJournalRunListLimit(sorted, scope2.limit) };
8044
9374
  }
8045
9375
  async function readSealedJournalRunSet(scope2, options = {}) {
@@ -8058,7 +9388,7 @@ async function readSealedJournalRunSet(scope2, options = {}) {
8058
9388
  }
8059
9389
  return {
8060
9390
  ok: true,
8061
- value: runs.sort((left, right) => compareJournalRunsNewestFirst(left.metadata, right.metadata)).slice(0, scope2.limit).sort((left, right) => compareJournalRunsOldestFirst(left.metadata, right.metadata)).map((run) => ({
9391
+ value: runs.sort((left, right) => compareRunRecencyNewestFirst(left.metadata, right.metadata)).slice(0, scope2.limit).sort((left, right) => compareRunRecencyOldestFirst(left.metadata, right.metadata)).map((run) => ({
8062
9392
  runToken: run.metadata.runToken,
8063
9393
  metadata: run.metadata,
8064
9394
  events: run.events.slice(0, scope2.eventLimit)
@@ -8378,14 +9708,14 @@ function validateJournalEventInput(value) {
8378
9708
  if (typeof value !== "object" || value === null || Array.isArray(value)) {
8379
9709
  return { ok: false, error: JOURNAL_CLI_ERROR.INVALID_EVENT_INPUT };
8380
9710
  }
8381
- const record6 = value;
9711
+ const record7 = value;
8382
9712
  for (const field of JOURNAL_EVENT_INPUT_STRING_FIELDS) {
8383
- const candidate = record6[field];
9713
+ const candidate = record7[field];
8384
9714
  if (typeof candidate !== "string" || candidate.length === 0) {
8385
9715
  return { ok: false, error: JOURNAL_CLI_ERROR.INVALID_EVENT_INPUT };
8386
9716
  }
8387
9717
  }
8388
- if (typeof record6.attempt !== "number" || !Number.isInteger(record6.attempt)) {
9718
+ if (typeof record7.attempt !== "number" || !Number.isInteger(record7.attempt)) {
8389
9719
  return { ok: false, error: JOURNAL_CLI_ERROR.INVALID_EVENT_INPUT };
8390
9720
  }
8391
9721
  return { ok: true, value };
@@ -8817,7 +10147,7 @@ function sessionOptionToken(option) {
8817
10147
 
8818
10148
  // src/commands/session/archive.ts
8819
10149
  import { mkdir, rename, stat as stat3 } from "fs/promises";
8820
- import { dirname as dirname7, join as join8 } from "path";
10150
+ import { dirname as dirname7, join as join9 } from "path";
8821
10151
 
8822
10152
  // src/domains/session/archive.ts
8823
10153
  var SESSION_FILE_EXTENSION = ".md";
@@ -9069,7 +10399,7 @@ var SessionInvalidJsonHeaderError = class extends SessionError {
9069
10399
  };
9070
10400
 
9071
10401
  // src/commands/session/resolve-config.ts
9072
- import { join as join7 } from "path";
10402
+ import { join as join8 } from "path";
9073
10403
 
9074
10404
  // src/config/defaults.ts
9075
10405
  var DEFAULT_CONFIG = {
@@ -9089,18 +10419,18 @@ async function resolveSessionConfig(options = {}) {
9089
10419
  if (sessionsDir) {
9090
10420
  return {
9091
10421
  config: {
9092
- todoDir: join7(sessionsDir, statusDirs2.todo),
9093
- doingDir: join7(sessionsDir, statusDirs2.doing),
9094
- archiveDir: join7(sessionsDir, statusDirs2.archive)
10422
+ todoDir: join8(sessionsDir, statusDirs2.todo),
10423
+ doingDir: join8(sessionsDir, statusDirs2.doing),
10424
+ archiveDir: join8(sessionsDir, statusDirs2.archive)
9095
10425
  }
9096
10426
  };
9097
10427
  }
9098
10428
  const { sessionsDir: baseDir, warning } = await resolveSessionsScopeDir({ cwd, deps });
9099
10429
  return {
9100
10430
  config: {
9101
- todoDir: join7(baseDir, statusDirs2.todo),
9102
- doingDir: join7(baseDir, statusDirs2.doing),
9103
- archiveDir: join7(baseDir, statusDirs2.archive)
10431
+ todoDir: join8(baseDir, statusDirs2.todo),
10432
+ doingDir: join8(baseDir, statusDirs2.doing),
10433
+ archiveDir: join8(baseDir, statusDirs2.archive)
9104
10434
  },
9105
10435
  warning
9106
10436
  };
@@ -9137,9 +10467,9 @@ async function fileExists(path7) {
9137
10467
  }
9138
10468
  async function probeSessionPaths(sessionId, config) {
9139
10469
  const filename = `${sessionId}${SESSION_FILE_EXTENSION}`;
9140
- const todoPath = join8(config.todoDir, filename);
9141
- const doingPath = join8(config.doingDir, filename);
9142
- const archivePath = join8(config.archiveDir, filename);
10470
+ const todoPath = join9(config.todoDir, filename);
10471
+ const doingPath = join9(config.doingDir, filename);
10472
+ const archivePath = join9(config.archiveDir, filename);
9143
10473
  return {
9144
10474
  todo: await fileExists(todoPath) ? todoPath : null,
9145
10475
  doing: await fileExists(doingPath) ? doingPath : null,
@@ -9183,7 +10513,7 @@ function resolveDeletePath(sessionId, existingPaths) {
9183
10513
  }
9184
10514
 
9185
10515
  // src/domains/session/show.ts
9186
- import { join as join9 } from "path";
10516
+ import { join as join10 } from "path";
9187
10517
 
9188
10518
  // src/domains/session/list.ts
9189
10519
  import { Chalk as Chalk2 } from "chalk";
@@ -9436,7 +10766,7 @@ var SESSION_RECORD_FIELD = {
9436
10766
  var SESSION_RECORD_FIELDS = Object.values(SESSION_RECORD_FIELD);
9437
10767
  function toSessionRecord(session) {
9438
10768
  const { id, status, metadata } = session;
9439
- const record6 = {
10769
+ const record7 = {
9440
10770
  id,
9441
10771
  status,
9442
10772
  priority: metadata.priority,
@@ -9447,17 +10777,17 @@ function toSessionRecord(session) {
9447
10777
  files: metadata.files
9448
10778
  };
9449
10779
  if (metadata.created_at !== void 0) {
9450
- record6.created_at = metadata.created_at;
10780
+ record7.created_at = metadata.created_at;
9451
10781
  }
9452
10782
  if (metadata.agent_session_id !== void 0) {
9453
- record6.agent_session_id = metadata.agent_session_id;
10783
+ record7.agent_session_id = metadata.agent_session_id;
9454
10784
  }
9455
- return record6;
10785
+ return record7;
9456
10786
  }
9457
- function projectSessionRecord(record6, fields) {
10787
+ function projectSessionRecord(record7, fields) {
9458
10788
  const projected = {};
9459
10789
  for (const field of fields) {
9460
- const value = record6[field];
10790
+ const value = record7[field];
9461
10791
  if (value !== void 0) {
9462
10792
  projected[field] = value;
9463
10793
  }
@@ -9542,12 +10872,12 @@ var SESSION_SHOW_LABEL = {
9542
10872
  };
9543
10873
  var SESSION_SHOW_SEPARATOR_CHAR = "\u2500";
9544
10874
  var SESSION_SHOW_SEPARATOR_WIDTH = 40;
9545
- var sessionsBaseDir = join9(STATE_STORE_SCOPE_PATH.SPX_DIR, STATE_STORE_SCOPE_PATH.SESSIONS_SCOPE);
10875
+ var sessionsBaseDir = join10(STATE_STORE_SCOPE_PATH.SPX_DIR, STATE_STORE_SCOPE_PATH.SESSIONS_SCOPE);
9546
10876
  var { statusDirs } = DEFAULT_CONFIG.sessions;
9547
10877
  var DEFAULT_SESSION_CONFIG = {
9548
- todoDir: join9(sessionsBaseDir, statusDirs.todo),
9549
- doingDir: join9(sessionsBaseDir, statusDirs.doing),
9550
- archiveDir: join9(sessionsBaseDir, statusDirs.archive)
10878
+ todoDir: join10(sessionsBaseDir, statusDirs.todo),
10879
+ doingDir: join10(sessionsBaseDir, statusDirs.doing),
10880
+ archiveDir: join10(sessionsBaseDir, statusDirs.archive)
9551
10881
  };
9552
10882
  var SEARCH_ORDER = [...SESSION_STATUSES];
9553
10883
  function resolveSessionPaths(id, config = DEFAULT_SESSION_CONFIG) {
@@ -9605,7 +10935,7 @@ async function deleteCommand(options) {
9605
10935
 
9606
10936
  // src/commands/session/handoff.ts
9607
10937
  import { mkdir as mkdir2, stat as stat5, writeFile } from "fs/promises";
9608
- import { join as join10, resolve as resolve6 } from "path";
10938
+ import { join as join11, resolve as resolve7 } from "path";
9609
10939
  import { stringify as stringifyYaml3 } from "yaml";
9610
10940
 
9611
10941
  // src/domains/session/create.ts
@@ -9855,7 +11185,7 @@ async function rejectDirectoryInjectionEntries(entries, cwd) {
9855
11185
  if (entry.length === 0) continue;
9856
11186
  let entryIsDirectory = false;
9857
11187
  try {
9858
- entryIsDirectory = (await stat5(resolve6(cwd, entry))).isDirectory();
11188
+ entryIsDirectory = (await stat5(resolve7(cwd, entry))).isDirectory();
9859
11189
  } catch {
9860
11190
  continue;
9861
11191
  }
@@ -9903,8 +11233,8 @@ async function handoffCommand(options) {
9903
11233
  const yaml = stringifyYaml3(frontMatterObject, { defaultStringType: "QUOTE_DOUBLE" }).trimEnd();
9904
11234
  const fullContent = `${SESSION_FRONT_MATTER_OPEN}${yaml}${SESSION_FRONT_MATTER_CLOSE}${body}`;
9905
11235
  const filename = `${sessionId}.md`;
9906
- const sessionPath = join10(config.todoDir, filename);
9907
- const absolutePath = resolve6(sessionPath);
11236
+ const sessionPath = join11(config.todoDir, filename);
11237
+ const absolutePath = resolve7(sessionPath);
9908
11238
  await mkdir2(config.todoDir, { recursive: true });
9909
11239
  await writeFile(sessionPath, fullContent, SESSION_FILE_ENCODING);
9910
11240
  const output = `Created handoff session ${formatSessionOutputMarker(SESSION_OUTPUT_MARKER.HANDOFF_ID, sessionId)}
@@ -9913,8 +11243,8 @@ ${formatSessionOutputMarker(SESSION_OUTPUT_MARKER.SESSION_FILE, absolutePath)}`;
9913
11243
  }
9914
11244
 
9915
11245
  // src/commands/session/list.ts
9916
- import { readdir as readdir3, readFile as readFile5 } from "fs/promises";
9917
- import { join as join11 } from "path";
11246
+ import { readdir as readdir4, readFile as readFile5 } from "fs/promises";
11247
+ import { join as join12 } from "path";
9918
11248
  var SESSION_LIST_FORMAT = {
9919
11249
  TEXT: "text",
9920
11250
  JSON: "json"
@@ -9922,12 +11252,12 @@ var SESSION_LIST_FORMAT = {
9922
11252
  var SESSION_LIST_EMPTY_TEXT = "(no sessions)";
9923
11253
  async function loadSessionsFromDir(dir, status) {
9924
11254
  try {
9925
- const files = await readdir3(dir);
11255
+ const files = await readdir4(dir);
9926
11256
  const sessions = [];
9927
11257
  for (const file of files) {
9928
11258
  if (!file.endsWith(".md")) continue;
9929
11259
  const id = file.replace(".md", "");
9930
- const filePath = join11(dir, file);
11260
+ const filePath = join12(dir, file);
9931
11261
  const content = await readFile5(filePath, SESSION_FILE_ENCODING);
9932
11262
  const metadata = parseSessionMetadata(content);
9933
11263
  sessions.push({
@@ -9973,8 +11303,8 @@ async function listCommand(options) {
9973
11303
  const recordsByStatus = {};
9974
11304
  for (const status of statuses) {
9975
11305
  recordsByStatus[status] = (sessionsByStatus[status] ?? []).map((session) => {
9976
- const record6 = toSessionRecord(session);
9977
- return fieldSelection === void 0 ? record6 : projectSessionRecord(record6, fieldSelection);
11306
+ const record7 = toSessionRecord(session);
11307
+ return fieldSelection === void 0 ? record7 : projectSessionRecord(record7, fieldSelection);
9978
11308
  });
9979
11309
  }
9980
11310
  return JSON.stringify(recordsByStatus, null, 2);
@@ -9991,8 +11321,8 @@ async function listCommand(options) {
9991
11321
  }
9992
11322
 
9993
11323
  // src/commands/session/pickup.ts
9994
- import { mkdir as mkdir3, readdir as readdir4, readFile as readFile6, rename as rename2 } from "fs/promises";
9995
- import { join as join12, resolve as resolve7 } from "path";
11324
+ import { mkdir as mkdir3, readdir as readdir5, readFile as readFile6, rename as rename2 } from "fs/promises";
11325
+ import { join as join13, resolve as resolve8 } from "path";
9996
11326
 
9997
11327
  // src/domains/session/pickup.ts
9998
11328
  function buildClaimPaths(sessionId, config) {
@@ -10031,7 +11361,7 @@ function selectBestSession(sessions) {
10031
11361
  var PICKUP_TARGET_STATUS = SESSION_STATUSES[1];
10032
11362
  var PICKUP_DEPS = {
10033
11363
  mkdir: mkdir3,
10034
- readdir: readdir4,
11364
+ readdir: readdir5,
10035
11365
  readFile: readFile6,
10036
11366
  rename: rename2
10037
11367
  };
@@ -10045,7 +11375,7 @@ async function loadTodoSessionsWithDeps(config, deps) {
10045
11375
  for (const file of files) {
10046
11376
  if (!file.endsWith(".md")) continue;
10047
11377
  const id = file.replace(".md", "");
10048
- const filePath = join12(config.todoDir, file);
11378
+ const filePath = join13(config.todoDir, file);
10049
11379
  const content = await deps.readFile(filePath, SESSION_FILE_ENCODING);
10050
11380
  const metadata = parseSessionMetadata(content);
10051
11381
  sessions.push({
@@ -10067,7 +11397,7 @@ var SESSION_INJECTION_SECTION_PREFIX = "Injected file";
10067
11397
  var SESSION_INJECTION_MISSING_WARNING_PREFIX = "Warning: missing session injection file";
10068
11398
  var SESSION_INJECTION_UNREADABLE_WARNING_PREFIX = "Warning: unreadable session injection path";
10069
11399
  function injectionPath(cwd, filePath) {
10070
- return resolve7(cwd, filePath);
11400
+ return resolve8(cwd, filePath);
10071
11401
  }
10072
11402
  function formatInjectedFile(listedPath, content) {
10073
11403
  return `${SESSION_INJECTION_SECTION_PREFIX}: ${listedPath}
@@ -10141,8 +11471,8 @@ async function loadPickCandidates(options) {
10141
11471
  }
10142
11472
 
10143
11473
  // src/commands/session/prune.ts
10144
- import { readdir as readdir5, readFile as readFile7, unlink as unlink2 } from "fs/promises";
10145
- import { join as join13 } from "path";
11474
+ import { readdir as readdir6, readFile as readFile7, unlink as unlink2 } from "fs/promises";
11475
+ import { join as join14 } from "path";
10146
11476
 
10147
11477
  // src/domains/session/prune.ts
10148
11478
  var DEFAULT_KEEP_COUNT = 5;
@@ -10186,12 +11516,12 @@ function validatePruneOptions(options) {
10186
11516
  }
10187
11517
  async function loadArchiveSessions(config) {
10188
11518
  try {
10189
- const files = await readdir5(config.archiveDir);
11519
+ const files = await readdir6(config.archiveDir);
10190
11520
  const sessions = [];
10191
11521
  for (const file of files) {
10192
11522
  if (!file.endsWith(".md")) continue;
10193
11523
  const id = file.replace(".md", "");
10194
- const filePath = join13(config.archiveDir, file);
11524
+ const filePath = join14(config.archiveDir, file);
10195
11525
  const content = await readFile7(filePath, SESSION_FILE_ENCODING);
10196
11526
  const metadata = parseSessionMetadata(content);
10197
11527
  sessions.push({
@@ -10241,7 +11571,7 @@ async function pruneCommand(options) {
10241
11571
  }
10242
11572
 
10243
11573
  // src/commands/session/release.ts
10244
- import { readdir as readdir6, rename as rename3 } from "fs/promises";
11574
+ import { readdir as readdir7, rename as rename3 } from "fs/promises";
10245
11575
 
10246
11576
  // src/domains/session/release.ts
10247
11577
  function buildReleasePaths(sessionId, config) {
@@ -10272,7 +11602,7 @@ var SESSION_RELEASE_OUTPUT = {
10272
11602
  };
10273
11603
  async function loadDoingSessions(config) {
10274
11604
  try {
10275
- const files = await readdir6(config.doingDir);
11605
+ const files = await readdir7(config.doingDir);
10276
11606
  return files.filter((file) => file.endsWith(".md")).map((file) => ({ id: file.replace(".md", "") }));
10277
11607
  } catch (error) {
10278
11608
  if (error instanceof Error && "code" in error && error.code === SESSION_FILE_ERROR_CODE.NOT_FOUND) {
@@ -10442,7 +11772,7 @@ Output:
10442
11772
  `;
10443
11773
 
10444
11774
  // src/domains/session/pick-model.ts
10445
- import { resolve as resolve8 } from "path";
11775
+ import { resolve as resolve9 } from "path";
10446
11776
  var ELLIPSIS = "\u2026";
10447
11777
  var PICKER_RUNTIME = {
10448
11778
  CLAUDE: "claude",
@@ -10459,7 +11789,7 @@ function buildPickupCommand(runtime, autoContinue, reference) {
10459
11789
  return { command: runtime, args: [prompt] };
10460
11790
  }
10461
11791
  function pickupReference(session, sessionsDir, cwd) {
10462
- return sessionsDir === void 0 ? session.id : resolve8(cwd, session.path);
11792
+ return sessionsDir === void 0 ? session.id : resolve9(cwd, session.path);
10463
11793
  }
10464
11794
  function toSingleLine(text) {
10465
11795
  return text.replaceAll(/\s+/g, " ").trim();
@@ -10706,17 +12036,17 @@ async function readStdin() {
10706
12036
  if (process.stdin.isTTY) {
10707
12037
  return void 0;
10708
12038
  }
10709
- return new Promise((resolve12) => {
12039
+ return new Promise((resolve13) => {
10710
12040
  let data = "";
10711
12041
  process.stdin.setEncoding(SESSION_FILE_ENCODING);
10712
12042
  process.stdin.on("data", (chunk) => {
10713
12043
  data += chunk;
10714
12044
  });
10715
12045
  process.stdin.on("end", () => {
10716
- resolve12(data.length === 0 ? void 0 : data);
12046
+ resolve13(data.length === 0 ? void 0 : data);
10717
12047
  });
10718
12048
  process.stdin.on("error", () => {
10719
- resolve12(void 0);
12049
+ resolve13(void 0);
10720
12050
  });
10721
12051
  });
10722
12052
  }
@@ -10992,9 +12322,46 @@ var sessionDomain = {
10992
12322
  }
10993
12323
  };
10994
12324
 
12325
+ // src/commands/spec/context.ts
12326
+ import { access } from "fs/promises";
12327
+ import { join as join16 } from "path";
12328
+
12329
+ // src/git/tracked-paths.ts
12330
+ var GIT_LS_FILES_SUBCOMMAND = "ls-files";
12331
+ var GIT_NUL_TERMINATED_FLAG = "-z";
12332
+ var TRACKED_PATH_NUL_SEPARATOR = "\0";
12333
+ var TRACKED_PATH_DIRECTORY_SEPARATOR = "/";
12334
+ var GIT_SUCCESS_EXIT_CODE = 0;
12335
+ async function listTrackedPaths(productDir, deps) {
12336
+ let result;
12337
+ try {
12338
+ result = await deps.execa(
12339
+ GIT_ROOT_COMMAND.EXECUTABLE,
12340
+ [GIT_LS_FILES_SUBCOMMAND, GIT_NUL_TERMINATED_FLAG],
12341
+ { cwd: productDir, reject: false }
12342
+ );
12343
+ } catch {
12344
+ return void 0;
12345
+ }
12346
+ if (result.exitCode !== GIT_SUCCESS_EXIT_CODE) return void 0;
12347
+ return new Set(result.stdout.split(TRACKED_PATH_NUL_SEPARATOR).filter((entry) => entry.length > 0));
12348
+ }
12349
+ function createTrackedPathInclusion(trackedPaths) {
12350
+ if (trackedPaths === void 0) return () => true;
12351
+ const admitted = new Set(trackedPaths);
12352
+ for (const trackedPath of trackedPaths) {
12353
+ let separatorIndex = trackedPath.indexOf(TRACKED_PATH_DIRECTORY_SEPARATOR);
12354
+ while (separatorIndex !== -1) {
12355
+ admitted.add(trackedPath.slice(0, separatorIndex));
12356
+ separatorIndex = trackedPath.indexOf(TRACKED_PATH_DIRECTORY_SEPARATOR, separatorIndex + 1);
12357
+ }
12358
+ }
12359
+ return (path7) => admitted.has(path7);
12360
+ }
12361
+
10995
12362
  // src/lib/spec-tree/index.ts
10996
- import { readdir as readdir7, readFile as readFile9 } from "fs/promises";
10997
- import { join as join14 } from "path";
12363
+ import { readdir as readdir8, readFile as readFile9 } from "fs/promises";
12364
+ import { join as join15 } from "path";
10998
12365
  var SPEC_TREE_FIELD_KEY = {
10999
12366
  VERSION: "version",
11000
12367
  PRODUCT: "product",
@@ -11078,32 +12445,32 @@ function createFilesystemSpecTreeSource(options) {
11078
12445
  if (ref.path === void 0) {
11079
12446
  throw new Error("Filesystem source refs require a path");
11080
12447
  }
11081
- return readFile9(join14(options.productDir, ref.path), SPEC_TREE_TEXT_ENCODING);
12448
+ return readFile9(join15(options.productDir, ref.path), SPEC_TREE_TEXT_ENCODING);
11082
12449
  }
11083
12450
  };
11084
12451
  }
11085
- function recognizeSpecTreeFilesystemEntry(record6, options = {}) {
12452
+ function recognizeSpecTreeFilesystemEntry(record7, options = {}) {
11086
12453
  const registry = options.registry ?? KIND_REGISTRY;
11087
12454
  const schemaVersions = options.schemaVersions ?? SPEC_TREE_NAMING_SCHEMA_VERSIONS;
11088
- const name = readLastPathSegment(record6.relativePath);
11089
- if (record6.type === SPEC_TREE_FILESYSTEM_RECORD_TYPE.FILE && isProductFile(record6.relativePath)) {
12455
+ const name = readLastPathSegment(record7.relativePath);
12456
+ if (record7.type === SPEC_TREE_FILESYSTEM_RECORD_TYPE.FILE && isProductFile(record7.relativePath)) {
11090
12457
  return {
11091
12458
  type: SPEC_TREE_ENTRY_TYPE.PRODUCT,
11092
- id: record6.relativePath,
12459
+ id: record7.relativePath,
11093
12460
  title: stripSuffix(name, SPEC_TREE_CONFIG.PRODUCT.SUFFIX),
11094
- ref: sourceRefForRelativePath(record6.relativePath)
12461
+ ref: sourceRefForRelativePath(record7.relativePath)
11095
12462
  };
11096
12463
  }
11097
- if (record6.type === SPEC_TREE_FILESYSTEM_RECORD_TYPE.DIRECTORY) {
11098
- return recognizeDirectoryRecord(record6, name, registry, schemaVersions);
12464
+ if (record7.type === SPEC_TREE_FILESYSTEM_RECORD_TYPE.DIRECTORY) {
12465
+ return recognizeDirectoryRecord(record7, name, registry, schemaVersions);
11099
12466
  }
11100
- if (record6.parentId !== void 0 && isEvidenceFile(record6.relativePath)) {
12467
+ if (record7.parentId !== void 0 && isEvidenceFile(record7.relativePath)) {
11101
12468
  return {
11102
12469
  type: SPEC_TREE_ENTRY_TYPE.EVIDENCE,
11103
- id: record6.relativePath,
11104
- parentId: record6.parentId,
12470
+ id: record7.relativePath,
12471
+ parentId: record7.parentId,
11105
12472
  status: SPEC_TREE_EVIDENCE_STATUS.LINKED,
11106
- ref: sourceRefForRelativePath(record6.relativePath)
12473
+ ref: sourceRefForRelativePath(record7.relativePath)
11107
12474
  };
11108
12475
  }
11109
12476
  const decisionMatch = matchKindSuffix(name, registry, SPEC_TREE_KIND_CATEGORY.DECISION);
@@ -11113,14 +12480,14 @@ function recognizeSpecTreeFilesystemEntry(record6, options = {}) {
11113
12480
  return {
11114
12481
  type: SPEC_TREE_ENTRY_TYPE.DECISION,
11115
12482
  kind: decisionMatch.kind,
11116
- id: record6.relativePath,
12483
+ id: record7.relativePath,
11117
12484
  order: parsed.order,
11118
12485
  slug: parsed.slug,
11119
- parentId: record6.parentId,
11120
- ref: sourceRefForRelativePath(record6.relativePath)
12486
+ parentId: record7.parentId,
12487
+ ref: sourceRefForRelativePath(record7.relativePath)
11121
12488
  };
11122
12489
  }
11123
- function recognizeDirectoryRecord(record6, name, registry, schemaVersions) {
12490
+ function recognizeDirectoryRecord(record7, name, registry, schemaVersions) {
11124
12491
  const canonical = canonicalNamingSchemaVersion(schemaVersions);
11125
12492
  const canonicalMatch = matchNodeSuffixFromVersion(name, canonical);
11126
12493
  if (canonicalMatch !== null) {
@@ -11129,11 +12496,11 @@ function recognizeDirectoryRecord(record6, name, registry, schemaVersions) {
11129
12496
  return {
11130
12497
  type: SPEC_TREE_ENTRY_TYPE.NODE,
11131
12498
  kind,
11132
- id: record6.relativePath,
12499
+ id: record7.relativePath,
11133
12500
  order: canonicalMatch.parsed.order,
11134
12501
  slug: canonicalMatch.parsed.slug,
11135
- parentId: record6.parentId,
11136
- ref: sourceRefForNode(record6.relativePath, canonicalMatch.parsed.slug)
12502
+ parentId: record7.parentId,
12503
+ ref: sourceRefForNode(record7.relativePath, canonicalMatch.parsed.slug)
11137
12504
  };
11138
12505
  }
11139
12506
  }
@@ -11141,19 +12508,19 @@ function recognizeDirectoryRecord(record6, name, registry, schemaVersions) {
11141
12508
  if (supersededVersion !== null) {
11142
12509
  return {
11143
12510
  type: SPEC_TREE_ENTRY_TYPE.SUPERSEDED,
11144
- id: record6.relativePath,
12511
+ id: record7.relativePath,
11145
12512
  version: supersededVersion,
11146
- parentId: record6.parentId,
11147
- ref: sourceRefForRelativePath(record6.relativePath)
12513
+ parentId: record7.parentId,
12514
+ ref: sourceRefForRelativePath(record7.relativePath)
11148
12515
  };
11149
12516
  }
11150
12517
  if (matchKindSuffix(name, registry, SPEC_TREE_KIND_CATEGORY.DECISION) !== null) return null;
11151
12518
  if (parseOrderedSlug(name) !== null) {
11152
12519
  return {
11153
12520
  type: SPEC_TREE_ENTRY_TYPE.INVALID,
11154
- id: record6.relativePath,
11155
- parentId: record6.parentId,
11156
- ref: sourceRefForRelativePath(record6.relativePath)
12521
+ id: record7.relativePath,
12522
+ parentId: record7.parentId,
12523
+ ref: sourceRefForRelativePath(record7.relativePath)
11157
12524
  };
11158
12525
  }
11159
12526
  return null;
@@ -11359,7 +12726,7 @@ function compareOrderedEntries(left, right) {
11359
12726
  }
11360
12727
  async function* readFilesystemSourceEntries(productDir, registry, schemaVersions, includePath) {
11361
12728
  yield* walkFilesystemDirectory({
11362
- absolutePath: join14(productDir, SPEC_TREE_CONFIG.ROOT_DIRECTORY),
12729
+ absolutePath: join15(productDir, SPEC_TREE_CONFIG.ROOT_DIRECTORY),
11363
12730
  relativePath: SPEC_TREE_EMPTY_RELATIVE_PATH,
11364
12731
  registry,
11365
12732
  schemaVersions,
@@ -11369,7 +12736,7 @@ async function* readFilesystemSourceEntries(productDir, registry, schemaVersions
11369
12736
  async function* walkFilesystemDirectory(context) {
11370
12737
  let entries;
11371
12738
  try {
11372
- entries = await readdir7(context.absolutePath, { withFileTypes: true });
12739
+ entries = await readdir8(context.absolutePath, { withFileTypes: true });
11373
12740
  } catch (error) {
11374
12741
  if (isFileNotFound2(error)) return;
11375
12742
  throw error;
@@ -11377,8 +12744,8 @@ async function* walkFilesystemDirectory(context) {
11377
12744
  const sortedEntries = [...entries].sort((left, right) => left.name.localeCompare(right.name));
11378
12745
  for (const entry of sortedEntries) {
11379
12746
  const relativePath = joinSpecTreePath(context.relativePath, entry.name);
11380
- const refPath = joinSpecTreePath(SPEC_TREE_CONFIG.ROOT_DIRECTORY, relativePath);
11381
- if (!await context.includePath(refPath)) continue;
12747
+ const refPath2 = joinSpecTreePath(SPEC_TREE_CONFIG.ROOT_DIRECTORY, relativePath);
12748
+ if (!await context.includePath(refPath2)) continue;
11382
12749
  const recordType = filesystemRecordType(entry);
11383
12750
  if (recordType === void 0) continue;
11384
12751
  const sourceEntry = recognizeSpecTreeFilesystemEntry(
@@ -11388,7 +12755,7 @@ async function* walkFilesystemDirectory(context) {
11388
12755
  if (sourceEntry !== null) yield sourceEntry;
11389
12756
  if (entry.isDirectory() && shouldDescendIntoDirectory(sourceEntry)) {
11390
12757
  yield* walkFilesystemDirectory({
11391
- absolutePath: join14(context.absolutePath, entry.name),
12758
+ absolutePath: join15(context.absolutePath, entry.name),
11392
12759
  relativePath,
11393
12760
  registry: context.registry,
11394
12761
  schemaVersions: context.schemaVersions,
@@ -11496,6 +12863,258 @@ async function resolveSpecProductDir(cwd, gitDependencies, onWarning) {
11496
12863
  return result.productDir;
11497
12864
  }
11498
12865
 
12866
+ // src/commands/spec/context.ts
12867
+ var SPEC_CONTEXT_DOCUMENT_ROLE = {
12868
+ PRODUCT: "product",
12869
+ ANCESTOR: "ancestor",
12870
+ TARGET: "target",
12871
+ DECISION: "decision",
12872
+ LOWER_INDEX_SIBLING: "lower-index-sibling",
12873
+ EVIDENCE: "evidence",
12874
+ COORDINATION: "coordination"
12875
+ };
12876
+ var SPEC_CONTEXT_COORDINATION_FILE = {
12877
+ PLAN: "PLAN.md",
12878
+ ISSUES: "ISSUES.md"
12879
+ };
12880
+ var JSON_INDENTATION = 2;
12881
+ var SPEC_TREE_ROOT_PREFIX = `${SPEC_TREE_CONFIG.ROOT_DIRECTORY}/`;
12882
+ var SPEC_CONTEXT_TEXT_LABEL = {
12883
+ TARGET: "Target",
12884
+ PRODUCT_ROOT: "Product root",
12885
+ METHODOLOGY: "Methodology",
12886
+ DOCUMENTS: "Documents",
12887
+ SAME_INDEX_SIBLINGS: "Same-index siblings",
12888
+ HIGHER_INDEX_SIBLINGS: "Higher-index siblings"
12889
+ };
12890
+ var TEXT_LIST_INDENT = " - ";
12891
+ function refPath(ref) {
12892
+ return ref?.path;
12893
+ }
12894
+ function sortPaths(paths) {
12895
+ return [...paths].sort((left, right) => left.localeCompare(right));
12896
+ }
12897
+ function normalizeTarget(target) {
12898
+ return target.startsWith(SPEC_TREE_ROOT_PREFIX) ? target.slice(SPEC_TREE_ROOT_PREFIX.length) : target;
12899
+ }
12900
+ function fullSpecPath(path7) {
12901
+ return path7.startsWith(SPEC_TREE_ROOT_PREFIX) ? path7 : `${SPEC_TREE_ROOT_PREFIX}${path7}`;
12902
+ }
12903
+ function childSpecPath(parent, child) {
12904
+ return `${parent}${TRACKED_PATH_DIRECTORY_SEPARATOR}${child}`;
12905
+ }
12906
+ function pushDocument(documents, role, path7) {
12907
+ if (path7 !== void 0) {
12908
+ documents.push({ role, path: path7 });
12909
+ }
12910
+ }
12911
+ async function pushExistingDocument(documents, role, productDir, path7, includePath) {
12912
+ if (path7 === void 0 || !await optionalSpecTreeFile(productDir, path7, includePath)) {
12913
+ return;
12914
+ }
12915
+ documents.push({ role, path: path7 });
12916
+ }
12917
+ function findNode(snapshot, target) {
12918
+ const normalized = normalizeTarget(target);
12919
+ return snapshot.allNodes.find((node) => node.id === normalized);
12920
+ }
12921
+ function ancestorsFor(snapshot, target) {
12922
+ const byId = new Map(snapshot.allNodes.map((node) => [node.id, node]));
12923
+ const ancestors = [];
12924
+ let currentParent = target.parentId;
12925
+ while (currentParent !== void 0) {
12926
+ const parent = byId.get(currentParent);
12927
+ if (parent === void 0) break;
12928
+ ancestors.unshift(parent);
12929
+ currentParent = parent.parentId;
12930
+ }
12931
+ return ancestors;
12932
+ }
12933
+ function siblingsFor(snapshot, target) {
12934
+ return snapshot.allNodes.filter((node) => node.parentId === target.parentId && node.id !== target.id);
12935
+ }
12936
+ function lowerIndexSiblingsForContextNodes(snapshot, contextNodes) {
12937
+ const seen = /* @__PURE__ */ new Set();
12938
+ const lowerSiblings = [];
12939
+ for (const contextNode of contextNodes) {
12940
+ for (const sibling of siblingsFor(snapshot, contextNode)) {
12941
+ if (sibling.order >= contextNode.order || seen.has(sibling.id)) continue;
12942
+ lowerSiblings.push(sibling);
12943
+ seen.add(sibling.id);
12944
+ }
12945
+ }
12946
+ lowerSiblings.sort((left, right) => {
12947
+ const parentComparison = (left.parentId ?? "").localeCompare(right.parentId ?? "");
12948
+ if (parentComparison !== 0) return parentComparison;
12949
+ const orderComparison = left.order - right.order;
12950
+ if (orderComparison !== 0) return orderComparison;
12951
+ return left.id.localeCompare(right.id);
12952
+ });
12953
+ return lowerSiblings;
12954
+ }
12955
+ function decisionsFor(snapshot, contextNodes) {
12956
+ const contextByParentId = /* @__PURE__ */ new Map();
12957
+ for (const node of contextNodes) {
12958
+ contextByParentId.set(node.parentId, node.order);
12959
+ }
12960
+ return snapshot.decisions.filter((decision) => {
12961
+ const constrainedOrder = contextByParentId.get(decision.parentId);
12962
+ return constrainedOrder !== void 0 && decision.order < constrainedOrder;
12963
+ });
12964
+ }
12965
+ function evidenceFor(snapshot, target) {
12966
+ return snapshot.entries.filter(
12967
+ (entry) => entry.type === SPEC_TREE_ENTRY_TYPE.EVIDENCE && entry.parentId === target.id
12968
+ );
12969
+ }
12970
+ async function optionalFile(productDir, relativePath, includePath) {
12971
+ return optionalSpecTreeFile(productDir, fullSpecPath(relativePath), includePath);
12972
+ }
12973
+ async function optionalSpecTreeFile(productDir, specTreePath, includePath) {
12974
+ if (!await includePath(specTreePath)) return void 0;
12975
+ try {
12976
+ await access(join16(productDir, specTreePath));
12977
+ return specTreePath;
12978
+ } catch {
12979
+ return void 0;
12980
+ }
12981
+ }
12982
+ async function coordinationDocuments(productDir, target, includePath) {
12983
+ return [
12984
+ await optionalFile(productDir, childSpecPath(target.id, SPEC_CONTEXT_COORDINATION_FILE.PLAN), includePath),
12985
+ await optionalFile(productDir, childSpecPath(target.id, SPEC_CONTEXT_COORDINATION_FILE.ISSUES), includePath)
12986
+ ].filter((path7) => path7 !== void 0);
12987
+ }
12988
+ async function buildManifest(productDir, snapshot, target, includePath) {
12989
+ const ancestors = ancestorsFor(snapshot, target);
12990
+ const contextNodes = [...ancestors, target];
12991
+ const siblings = siblingsFor(snapshot, target);
12992
+ const lowerSiblings = lowerIndexSiblingsForContextNodes(snapshot, contextNodes);
12993
+ const sameIndex = sortPaths(
12994
+ siblings.filter((node) => node.order === target.order).map((node) => fullSpecPath(node.id))
12995
+ );
12996
+ const higherIndex = sortPaths(
12997
+ siblings.filter((node) => node.order > target.order).map((node) => fullSpecPath(node.id))
12998
+ );
12999
+ const methodologyConfig = await resolveMethodologyConfig(productDir);
13000
+ if (!methodologyConfig.ok) {
13001
+ throw new Error(methodologyConfig.error);
13002
+ }
13003
+ const methodology = resolveMethodologyIdentity(methodologyConfig.value);
13004
+ const documents = [];
13005
+ await pushExistingDocument(
13006
+ documents,
13007
+ SPEC_CONTEXT_DOCUMENT_ROLE.PRODUCT,
13008
+ productDir,
13009
+ refPath(snapshot.product?.ref),
13010
+ includePath
13011
+ );
13012
+ for (const ancestor of ancestors) {
13013
+ await pushExistingDocument(
13014
+ documents,
13015
+ SPEC_CONTEXT_DOCUMENT_ROLE.ANCESTOR,
13016
+ productDir,
13017
+ refPath(ancestor.ref),
13018
+ includePath
13019
+ );
13020
+ }
13021
+ await pushExistingDocument(
13022
+ documents,
13023
+ SPEC_CONTEXT_DOCUMENT_ROLE.TARGET,
13024
+ productDir,
13025
+ refPath(target.ref),
13026
+ includePath
13027
+ );
13028
+ for (const decision of decisionsFor(snapshot, contextNodes)) {
13029
+ await pushExistingDocument(
13030
+ documents,
13031
+ SPEC_CONTEXT_DOCUMENT_ROLE.DECISION,
13032
+ productDir,
13033
+ refPath(decision.ref),
13034
+ includePath
13035
+ );
13036
+ }
13037
+ for (const sibling of lowerSiblings) {
13038
+ await pushExistingDocument(
13039
+ documents,
13040
+ SPEC_CONTEXT_DOCUMENT_ROLE.LOWER_INDEX_SIBLING,
13041
+ productDir,
13042
+ refPath(sibling.ref),
13043
+ includePath
13044
+ );
13045
+ }
13046
+ for (const evidence of evidenceFor(snapshot, target)) {
13047
+ await pushExistingDocument(
13048
+ documents,
13049
+ SPEC_CONTEXT_DOCUMENT_ROLE.EVIDENCE,
13050
+ productDir,
13051
+ refPath(evidence.ref),
13052
+ includePath
13053
+ );
13054
+ }
13055
+ for (const path7 of await coordinationDocuments(productDir, target, includePath)) {
13056
+ pushDocument(documents, SPEC_CONTEXT_DOCUMENT_ROLE.COORDINATION, path7);
13057
+ }
13058
+ return {
13059
+ methodology,
13060
+ productDir,
13061
+ target: fullSpecPath(target.id),
13062
+ documents,
13063
+ siblings: {
13064
+ sameIndex,
13065
+ higherIndex
13066
+ }
13067
+ };
13068
+ }
13069
+ async function contextManifest(options) {
13070
+ const gitDependencies = options.gitDependencies ?? defaultGitDependencies;
13071
+ const productDir = await resolveSpecProductDir(
13072
+ options.cwd ?? CONFIG_PROCESS_CWD.read(),
13073
+ gitDependencies,
13074
+ options.onWarning
13075
+ );
13076
+ const trackedPaths = await listTrackedPaths(productDir, gitDependencies);
13077
+ const includePath = createTrackedPathInclusion(trackedPaths);
13078
+ const snapshot = await readSpecTree({
13079
+ source: createFilesystemSpecTreeSource({
13080
+ productDir,
13081
+ includePath
13082
+ })
13083
+ });
13084
+ const target = findNode(snapshot, options.target);
13085
+ if (target === void 0) {
13086
+ throw new Error(`Spec context target not found: ${options.target}`);
13087
+ }
13088
+ return buildManifest(productDir, snapshot, target, includePath);
13089
+ }
13090
+ function appendList(lines, label, values) {
13091
+ lines.push(`${label}:`);
13092
+ for (const value of values) {
13093
+ lines.push(`${TEXT_LIST_INDENT}${value}`);
13094
+ }
13095
+ }
13096
+ function renderSpecContextText(manifest) {
13097
+ const lines = [
13098
+ `${SPEC_CONTEXT_TEXT_LABEL.TARGET}: ${manifest.target}`,
13099
+ `${SPEC_CONTEXT_TEXT_LABEL.PRODUCT_ROOT}: ${manifest.productDir}`,
13100
+ `${SPEC_CONTEXT_TEXT_LABEL.METHODOLOGY}: ${manifest.methodology.source}@${manifest.methodology.version}`
13101
+ ];
13102
+ appendList(
13103
+ lines,
13104
+ SPEC_CONTEXT_TEXT_LABEL.DOCUMENTS,
13105
+ manifest.documents.map((document) => `${document.role}: ${document.path}`)
13106
+ );
13107
+ appendList(lines, SPEC_CONTEXT_TEXT_LABEL.SAME_INDEX_SIBLINGS, manifest.siblings.sameIndex);
13108
+ appendList(lines, SPEC_CONTEXT_TEXT_LABEL.HIGHER_INDEX_SIBLINGS, manifest.siblings.higherIndex);
13109
+ return lines.join("\n");
13110
+ }
13111
+ async function contextCommand(options) {
13112
+ return JSON.stringify(await contextManifest(options), null, JSON_INDENTATION);
13113
+ }
13114
+ async function contextTextCommand(options) {
13115
+ return renderSpecContextText(await contextManifest(options));
13116
+ }
13117
+
11499
13118
  // src/commands/spec/next.ts
11500
13119
  var SPEC_NEXT_MESSAGE = {
11501
13120
  EMPTY: `No spec-tree nodes found in ${SPEC_TREE_CONFIG.ROOT_DIRECTORY}`,
@@ -11541,27 +13160,27 @@ function formatNextNode(node) {
11541
13160
  }
11542
13161
 
11543
13162
  // src/commands/test/discovery.ts
11544
- import { readdir as readdir8 } from "fs/promises";
11545
- import { join as join15, relative as relative2, sep as sep2 } from "path";
13163
+ import { readdir as readdir9 } from "fs/promises";
13164
+ import { join as join17, relative as relative2, sep as sep2 } from "path";
11546
13165
  var TESTS_DIRECTORY_NAME = SPEC_TREE_EVIDENCE_FILE.DIRECTORY_NAME;
11547
13166
  var SPEC_ROOT_DIRECTORY = SPEC_TREE_CONFIG.ROOT_DIRECTORY;
11548
13167
  var POSIX_SEPARATOR = "/";
11549
13168
  var ERROR_CODE_NOT_FOUND2 = "ENOENT";
11550
13169
  async function discoverTestFiles(productDir) {
11551
13170
  const found = [];
11552
- await collectTestFiles(join15(productDir, SPEC_ROOT_DIRECTORY), false, found);
13171
+ await collectTestFiles(join17(productDir, SPEC_ROOT_DIRECTORY), false, found);
11553
13172
  return found.map((absolute) => toPosixRelative(productDir, absolute)).sort(compareAscii);
11554
13173
  }
11555
13174
  async function collectTestFiles(directory, insideTestsDir, found) {
11556
13175
  let entries;
11557
13176
  try {
11558
- entries = await readdir8(directory, { withFileTypes: true });
13177
+ entries = await readdir9(directory, { withFileTypes: true });
11559
13178
  } catch (error) {
11560
13179
  if (hasErrorCode2(error, ERROR_CODE_NOT_FOUND2)) return;
11561
13180
  throw error;
11562
13181
  }
11563
13182
  for (const entry of entries) {
11564
- const childPath = join15(directory, entry.name);
13183
+ const childPath = join17(directory, entry.name);
11565
13184
  if (entry.isDirectory()) {
11566
13185
  await collectTestFiles(childPath, insideTestsDir || entry.name === TESTS_DIRECTORY_NAME, found);
11567
13186
  } else if (insideTestsDir && entry.isFile()) {
@@ -11772,11 +13391,11 @@ async function runTests(options, deps) {
11772
13391
  }
11773
13392
 
11774
13393
  // src/commands/test/run-command.ts
11775
- import { join as join17 } from "path";
13394
+ import { join as join19 } from "path";
11776
13395
 
11777
13396
  // src/test/run-state.ts
11778
13397
  import { createHash as createHash4 } from "crypto";
11779
- import { join as join16 } from "path";
13398
+ import { join as join18 } from "path";
11780
13399
  var TEST_RUN_STATE_STATUS = {
11781
13400
  PASSED: "passed",
11782
13401
  FAILED: "failed",
@@ -11868,7 +13487,7 @@ async function readTestingRuns(productDir, options = {}) {
11868
13487
  const terminalRuns = [];
11869
13488
  const incompleteRuns = [];
11870
13489
  for (const entry of entries.filter(isTestRunFileEntry)) {
11871
- const runFilePath = join16(runsDir2, entry.name);
13490
+ const runFilePath = join18(runsDir2, entry.name);
11872
13491
  const stateResult = await readTestRunStatePath(runFilePath, fs8);
11873
13492
  if (stateResult.ok) {
11874
13493
  terminalRuns.push({ runFileName: entry.name, runFilePath, state: stateResult.value });
@@ -11965,7 +13584,7 @@ async function readTestRunStatePath(runFilePath, fs8) {
11965
13584
  return { ok: true, value: validated.value };
11966
13585
  }
11967
13586
  function validateTestRunState(value) {
11968
- if (!isRecord6(value)) return { ok: false, error: "testing run state must be an object" };
13587
+ if (!isRecord7(value)) return { ok: false, error: "testing run state must be an object" };
11969
13588
  const branchName = readString2(value, TEST_RUN_STATE_FIELDS.BRANCH_NAME);
11970
13589
  if (!branchName.ok) return branchName;
11971
13590
  const headSha = readString2(value, TEST_RUN_STATE_FIELDS.HEAD_SHA);
@@ -12008,7 +13627,7 @@ function readRunnerOutcomes(raw) {
12008
13627
  }
12009
13628
  const outcomes = [];
12010
13629
  for (const entry of raw) {
12011
- if (!isRecord6(entry)) {
13630
+ if (!isRecord7(entry)) {
12012
13631
  return { ok: false, error: `${TEST_RUN_STATE_FIELDS.RUNNER_OUTCOMES} entries must be objects` };
12013
13632
  }
12014
13633
  const runnerId = readString2(entry, TEST_RUNNER_OUTCOME_FIELDS.RUNNER_ID);
@@ -12029,7 +13648,7 @@ function readProductInputDigests(raw) {
12029
13648
  }
12030
13649
  const digests = [];
12031
13650
  for (const entry of raw) {
12032
- if (!isRecord6(entry)) {
13651
+ if (!isRecord7(entry)) {
12033
13652
  return { ok: false, error: `${TEST_RUN_STATE_FIELDS.PRODUCT_INPUT_DIGESTS} entries must be objects` };
12034
13653
  }
12035
13654
  const descriptorId = readString2(entry, PRODUCT_INPUT_DIGEST_FIELDS.DESCRIPTOR_ID);
@@ -12091,7 +13710,7 @@ function testingWriteError(error) {
12091
13710
  function withDomainErrorDetail(domainError, detail) {
12092
13711
  return detail === void 0 ? domainError : `${domainError}: ${detail}`;
12093
13712
  }
12094
- function isRecord6(value) {
13713
+ function isRecord7(value) {
12095
13714
  return typeof value === "object" && value !== null && !Array.isArray(value);
12096
13715
  }
12097
13716
  function sha256Hex2(value) {
@@ -12436,7 +14055,7 @@ async function readStagedConfigFile(productDir, git) {
12436
14055
  detected.push({
12437
14056
  filename: definition.filename,
12438
14057
  format: definition.format,
12439
- path: join17(productDir, definition.filename),
14058
+ path: join19(productDir, definition.filename),
12440
14059
  raw: result.stdout
12441
14060
  });
12442
14061
  }
@@ -12512,7 +14131,7 @@ async function digestProductInputs(descriptorId, paths, readSnapshotFile) {
12512
14131
  function worktreeSnapshotFileReader(productDir, fs8) {
12513
14132
  return async (path7) => {
12514
14133
  try {
12515
- return { present: true, content: await fs8.readFile(join17(productDir, path7), TEXT_ENCODING) };
14134
+ return { present: true, content: await fs8.readFile(join19(productDir, path7), TEXT_ENCODING) };
12516
14135
  } catch (error) {
12517
14136
  if (!hasErrorCode(error, TESTING_RUN_STATE_ERROR_CODE.NOT_FOUND)) throw error;
12518
14137
  return { present: false };
@@ -12806,7 +14425,7 @@ function verificationPassed(verification) {
12806
14425
 
12807
14426
  // src/lib/node-status/exclude.ts
12808
14427
  import { readFileSync } from "fs";
12809
- import { join as join18 } from "path";
14428
+ import { join as join20 } from "path";
12810
14429
  var NODE_STATUS_EXCLUDE_FILENAME = "EXCLUDE";
12811
14430
  var PATH_SEGMENT_SEPARATOR3 = "/";
12812
14431
  var CURRENT_DIRECTORY_SEGMENT = ".";
@@ -12815,7 +14434,7 @@ function isNodeError(err) {
12815
14434
  return err instanceof Error && "code" in err;
12816
14435
  }
12817
14436
  function excludePath(productDir) {
12818
- return join18(productDir, SPEC_TREE_CONFIG.ROOT_DIRECTORY, NODE_STATUS_EXCLUDE_FILENAME);
14437
+ return join20(productDir, SPEC_TREE_CONFIG.ROOT_DIRECTORY, NODE_STATUS_EXCLUDE_FILENAME);
12819
14438
  }
12820
14439
  function parseExcludeEntries(content) {
12821
14440
  return content.split("\n").map((line) => line.trim()).filter((line) => line.length > 0 && !line.startsWith("#")).map(validateExcludeEntry);
@@ -12862,11 +14481,11 @@ function isNodeStatusEntryExcluded(excludeEntries, node) {
12862
14481
  }
12863
14482
 
12864
14483
  // src/lib/node-status/provider.ts
12865
- import { join as join20 } from "path";
14484
+ import { join as join22 } from "path";
12866
14485
 
12867
14486
  // src/lib/node-status/read.ts
12868
14487
  import { readFileSync as readFileSync2 } from "fs";
12869
- import { join as join19 } from "path";
14488
+ import { join as join21 } from "path";
12870
14489
  var NODE_STATUS_FILENAME = "spx.status.json";
12871
14490
  var NODE_STATUS_MECHANISMS = new Set(Object.values(NODE_STATUS_VERIFICATION_MECHANISM));
12872
14491
  var NODE_STATUS_EVIDENCE_OUTCOMES = new Set(Object.values(NODE_STATUS_EVIDENCE_OUTCOME));
@@ -12887,7 +14506,7 @@ function isNodeStatusMechanismOverall(value) {
12887
14506
  return typeof value === "string" && NODE_STATUS_OVERALL_VALUES.has(value);
12888
14507
  }
12889
14508
  function readNodeStatus(nodeDir) {
12890
- const filePath = join19(nodeDir, NODE_STATUS_FILENAME);
14509
+ const filePath = join21(nodeDir, NODE_STATUS_FILENAME);
12891
14510
  let content;
12892
14511
  try {
12893
14512
  content = readFileSync2(filePath, "utf8");
@@ -12962,7 +14581,7 @@ function parseMechanismRecord(candidate, source, mechanism) {
12962
14581
 
12963
14582
  // src/lib/node-status/provider.ts
12964
14583
  function nodeDirectory(productDir, node) {
12965
- return join20(productDir, SPEC_TREE_CONFIG.ROOT_DIRECTORY, node.id);
14584
+ return join22(productDir, SPEC_TREE_CONFIG.ROOT_DIRECTORY, node.id);
12966
14585
  }
12967
14586
  function createNodeStatusProvider(productDir) {
12968
14587
  const excludeReader = createNodeStatusExcludeReader(productDir);
@@ -12980,43 +14599,8 @@ function createNodeStatusProvider(productDir) {
12980
14599
  }
12981
14600
 
12982
14601
  // src/lib/node-status/update.ts
12983
- import { mkdir as mkdir4, readdir as readdir9, rm, writeFile as writeFile2 } from "fs/promises";
12984
- import { dirname as dirname8, join as join21 } from "path";
12985
-
12986
- // src/git/tracked-paths.ts
12987
- var GIT_LS_FILES_SUBCOMMAND = "ls-files";
12988
- var GIT_NUL_TERMINATED_FLAG = "-z";
12989
- var TRACKED_PATH_NUL_SEPARATOR = "\0";
12990
- var TRACKED_PATH_DIRECTORY_SEPARATOR = "/";
12991
- var GIT_SUCCESS_EXIT_CODE = 0;
12992
- async function listTrackedPaths(productDir, deps) {
12993
- let result;
12994
- try {
12995
- result = await deps.execa(
12996
- GIT_ROOT_COMMAND.EXECUTABLE,
12997
- [GIT_LS_FILES_SUBCOMMAND, GIT_NUL_TERMINATED_FLAG],
12998
- { cwd: productDir, reject: false }
12999
- );
13000
- } catch {
13001
- return void 0;
13002
- }
13003
- if (result.exitCode !== GIT_SUCCESS_EXIT_CODE) return void 0;
13004
- return new Set(result.stdout.split(TRACKED_PATH_NUL_SEPARATOR).filter((entry) => entry.length > 0));
13005
- }
13006
- function createTrackedPathInclusion(trackedPaths) {
13007
- if (trackedPaths === void 0) return () => true;
13008
- const admitted = new Set(trackedPaths);
13009
- for (const trackedPath of trackedPaths) {
13010
- let separatorIndex = trackedPath.indexOf(TRACKED_PATH_DIRECTORY_SEPARATOR);
13011
- while (separatorIndex !== -1) {
13012
- admitted.add(trackedPath.slice(0, separatorIndex));
13013
- separatorIndex = trackedPath.indexOf(TRACKED_PATH_DIRECTORY_SEPARATOR, separatorIndex + 1);
13014
- }
13015
- }
13016
- return (path7) => admitted.has(path7);
13017
- }
13018
-
13019
- // src/lib/node-status/update.ts
14602
+ import { mkdir as mkdir4, readdir as readdir10, rm, writeFile as writeFile2 } from "fs/promises";
14603
+ import { dirname as dirname8, join as join23 } from "path";
13020
14604
  var NODE_STATUS_TEXT_ENCODING = "utf8";
13021
14605
  async function updateNodeStatus(options) {
13022
14606
  const { productDir, resolveOutcome, gitDependencies = defaultGitDependencies } = options;
@@ -13083,17 +14667,17 @@ async function writeNodeStatus(filePath, verification) {
13083
14667
  await writeFile2(filePath, serializeNodeStatus(createNodeStatusFile(verification)), NODE_STATUS_TEXT_ENCODING);
13084
14668
  }
13085
14669
  function nodeStatusPath(productDir, nodeId) {
13086
- return join21(productDir, SPEC_TREE_CONFIG.ROOT_DIRECTORY, nodeId, NODE_STATUS_FILENAME);
14670
+ return join23(productDir, SPEC_TREE_CONFIG.ROOT_DIRECTORY, nodeId, NODE_STATUS_FILENAME);
13087
14671
  }
13088
14672
  async function removeStaleNodeStatusFiles(productDir, liveStatusPaths) {
13089
- const statusPaths = await collectNodeStatusFiles(join21(productDir, SPEC_TREE_CONFIG.ROOT_DIRECTORY));
14673
+ const statusPaths = await collectNodeStatusFiles(join23(productDir, SPEC_TREE_CONFIG.ROOT_DIRECTORY));
13090
14674
  await Promise.all(statusPaths.filter((path7) => !liveStatusPaths.has(path7)).map((path7) => rm(path7, { force: true })));
13091
14675
  }
13092
14676
  async function collectNodeStatusFiles(directory) {
13093
14677
  const entries = await readDirectoryEntries(directory);
13094
14678
  const statusPaths = [];
13095
14679
  for (const entry of entries) {
13096
- const entryPath = join21(directory, entry.name);
14680
+ const entryPath = join23(directory, entry.name);
13097
14681
  if (entry.isDirectory()) {
13098
14682
  statusPaths.push(...await collectNodeStatusFiles(entryPath));
13099
14683
  } else if (entry.isFile() && entry.name === NODE_STATUS_FILENAME) {
@@ -13104,7 +14688,7 @@ async function collectNodeStatusFiles(directory) {
13104
14688
  }
13105
14689
  async function readDirectoryEntries(directory) {
13106
14690
  try {
13107
- return await readdir9(directory, { withFileTypes: true });
14691
+ return await readdir10(directory, { withFileTypes: true });
13108
14692
  } catch (error) {
13109
14693
  if (isNodeError3(error) && error.code === "ENOENT") return [];
13110
14694
  throw error;
@@ -13199,7 +14783,7 @@ var SPEC_STATUS_MESSAGE = {
13199
14783
  EMPTY: `No spec-tree nodes found in ${SPEC_TREE_CONFIG.ROOT_DIRECTORY}`
13200
14784
  };
13201
14785
  var DEFAULT_FORMAT = OUTPUT_FORMAT.TEXT;
13202
- var JSON_INDENTATION = 2;
14786
+ var JSON_INDENTATION2 = 2;
13203
14787
  var STATUS_SEPARATOR = " ";
13204
14788
  var NODE_INDENT = " ";
13205
14789
  var MARKDOWN_NODE_PREFIX = "- ";
@@ -13263,7 +14847,7 @@ function renderSpecStatus(projection, format2 = DEFAULT_FORMAT) {
13263
14847
  }
13264
14848
  }
13265
14849
  function formatJSON(projection) {
13266
- return JSON.stringify(projection, null, JSON_INDENTATION);
14850
+ return JSON.stringify(projection, null, JSON_INDENTATION2);
13267
14851
  }
13268
14852
  function formatText(projection) {
13269
14853
  return projection.nodes.map((node) => formatTextNode(node)).join("\n");
@@ -13309,7 +14893,7 @@ function formatNodeLabel(node) {
13309
14893
  }
13310
14894
 
13311
14895
  // src/test/languages/python.ts
13312
- import { basename as basename6, dirname as dirname9, join as join22 } from "path/posix";
14896
+ import { basename as basename6, dirname as dirname9, join as join24 } from "path/posix";
13313
14897
 
13314
14898
  // src/validation/discovery/language-finder.ts
13315
14899
  import fs6 from "fs";
@@ -13380,7 +14964,7 @@ function coveredProductInputPaths(coveredTestPaths2) {
13380
14964
  if (!matchesTestFile(testPath)) continue;
13381
14965
  let directory = dirname9(testPath);
13382
14966
  while (directory !== "." && directory.length > 0) {
13383
- paths.add(join22(directory, PYTHON_PRODUCT_INPUT_PATH.CONFTEST));
14967
+ paths.add(join24(directory, PYTHON_PRODUCT_INPUT_PATH.CONFTEST));
13384
14968
  const parent = dirname9(directory);
13385
14969
  if (parent === directory) break;
13386
14970
  directory = parent;
@@ -13451,7 +15035,7 @@ var TSCONFIG_COMPILER_OPTIONS_KEY = "compilerOptions";
13451
15035
  var TSCONFIG_PATHS_KEY = "paths";
13452
15036
  var TSCONFIG_WILDCARD_SUFFIX = "*";
13453
15037
  var TSCONFIG_CURRENT_DIRECTORY_PREFIX = "./";
13454
- var NOT_FOUND_ERROR_CODE = "ENOENT";
15038
+ var NOT_FOUND_ERROR_CODE2 = "ENOENT";
13455
15039
  function matchesTestFile2(filePath) {
13456
15040
  return TYPESCRIPT_TEST_FILE_SUFFIXES.some((suffix) => filePath.endsWith(suffix));
13457
15041
  }
@@ -13494,7 +15078,7 @@ function importSpecifiers(sourceText, testPath) {
13494
15078
  }
13495
15079
  return specifiers;
13496
15080
  }
13497
- function isRecord7(value) {
15081
+ function isRecord8(value) {
13498
15082
  return typeof value === "object" && value !== null && !Array.isArray(value);
13499
15083
  }
13500
15084
  function hasErrorCode3(error, code) {
@@ -13529,11 +15113,11 @@ function pathMappingFromTsconfigPath(pathAlias, targets) {
13529
15113
  };
13530
15114
  }
13531
15115
  function pathMappingsFromTsconfig(config) {
13532
- if (!isRecord7(config)) return [];
15116
+ if (!isRecord8(config)) return [];
13533
15117
  const compilerOptions = config[TSCONFIG_COMPILER_OPTIONS_KEY];
13534
- if (!isRecord7(compilerOptions)) return [];
15118
+ if (!isRecord8(compilerOptions)) return [];
13535
15119
  const paths = compilerOptions[TSCONFIG_PATHS_KEY];
13536
- if (!isRecord7(paths)) return [];
15120
+ if (!isRecord8(paths)) return [];
13537
15121
  return Object.entries(paths).flatMap(([pathAlias, targets]) => {
13538
15122
  const mapping = pathMappingFromTsconfigPath(pathAlias, targets);
13539
15123
  return mapping === null ? [] : [mapping];
@@ -13646,7 +15230,7 @@ async function readCandidateModule(path7, deps, moduleTextCache) {
13646
15230
  const cached = moduleTextCache.get(path7);
13647
15231
  if (cached !== void 0) return cached;
13648
15232
  const loaded = deps.readFile(path7).catch((error) => {
13649
- if (hasErrorCode3(error, NOT_FOUND_ERROR_CODE)) return null;
15233
+ if (hasErrorCode3(error, NOT_FOUND_ERROR_CODE2)) return null;
13650
15234
  throw error;
13651
15235
  });
13652
15236
  moduleTextCache.set(path7, loaded);
@@ -13659,7 +15243,7 @@ async function readCandidateTest(path7, deps, moduleTextCache) {
13659
15243
  if (text !== null) return text;
13660
15244
  }
13661
15245
  const loaded = deps.readFile(path7).catch((error) => {
13662
- if (hasErrorCode3(error, NOT_FOUND_ERROR_CODE)) return null;
15246
+ if (hasErrorCode3(error, NOT_FOUND_ERROR_CODE2)) return null;
13663
15247
  throw error;
13664
15248
  });
13665
15249
  moduleTextCache.set(path7, loaded);
@@ -13715,7 +15299,7 @@ var testingRegistry = {
13715
15299
  import { createWriteStream } from "fs";
13716
15300
  import { mkdtemp, readFile as readFile10 } from "fs/promises";
13717
15301
  import { tmpdir } from "os";
13718
- import { join as join23 } from "path";
15302
+ import { join as join25 } from "path";
13719
15303
  import { finished } from "stream/promises";
13720
15304
  var PROCESS_FAILURE_EXIT_CODE = 1;
13721
15305
  var AGENT_ARTIFACT_DIR_PREFIX = "spx-test-agent-";
@@ -13763,14 +15347,14 @@ function createRunnerDepsFor(productDir, outStream = process.stdout) {
13763
15347
  }
13764
15348
  function createRelatedDepsFor(productDir) {
13765
15349
  const runCommand = createRelatedCommandRunner(productDir);
13766
- return () => ({ runCommand, readFile: (path7) => readFile10(join23(productDir, path7), "utf8") });
15350
+ return () => ({ runCommand, readFile: (path7) => readFile10(join25(productDir, path7), "utf8") });
13767
15351
  }
13768
15352
  function artifactFileName(index, suffix) {
13769
15353
  return `${index.toString(ARTIFACT_INDEX_RADIX).padStart(ARTIFACT_INDEX_WIDTH, "0")}-${suffix}`;
13770
15354
  }
13771
15355
  function createArtifactWriters(root, index, createArtifactWriteStream) {
13772
- const stdoutPath = join23(root, artifactFileName(index, STDOUT_FILE_SUFFIX));
13773
- const stderrPath = join23(root, artifactFileName(index, STDERR_FILE_SUFFIX));
15356
+ const stdoutPath = join25(root, artifactFileName(index, STDOUT_FILE_SUFFIX));
15357
+ const stderrPath = join25(root, artifactFileName(index, STDERR_FILE_SUFFIX));
13774
15358
  const stdoutFile = createArtifactWriteStream(stdoutPath);
13775
15359
  const stderrFile = createArtifactWriteStream(stderrPath);
13776
15360
  return {
@@ -13823,7 +15407,7 @@ function createAgentOutputCommandRunner(productDir, options = {}) {
13823
15407
  let nextArtifactIndex = 0;
13824
15408
  return async (command, args = EMPTY_RUNNER_ARGS) => {
13825
15409
  nextArtifactIndex += 1;
13826
- artifactRoot ??= mkdtemp(join23(options.tmpDir ?? tmpdir(), AGENT_ARTIFACT_DIR_PREFIX));
15410
+ artifactRoot ??= mkdtemp(join25(options.tmpDir ?? tmpdir(), AGENT_ARTIFACT_DIR_PREFIX));
13827
15411
  const root = await artifactRoot;
13828
15412
  return runCapturedCommand({
13829
15413
  productDir,
@@ -13845,6 +15429,7 @@ var SPEC_DOMAIN_CLI = {
13845
15429
  COMMAND: "spec",
13846
15430
  STATUS_COMMAND: "status",
13847
15431
  NEXT_COMMAND: "next",
15432
+ CONTEXT_COMMAND: "context",
13848
15433
  JSON_OPTION: "--json",
13849
15434
  FORMAT_OPTION_FLAG: "--format",
13850
15435
  FORMAT_OPTION_DEFINITION: "--format <format>",
@@ -13904,6 +15489,14 @@ function resolveStatusFormat(options) {
13904
15489
  function registerSpecCommands(specCmd, invocation) {
13905
15490
  const productDir = () => invocation.resolveProductContext().productDir;
13906
15491
  const onWarning = (warning) => writeInvocationWarning3(invocation.io, warning);
15492
+ 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
+ try {
15494
+ const output = options.json === true ? await contextCommand({ target, cwd: productDir(), onWarning }) : await contextTextCommand({ target, cwd: productDir(), onWarning });
15495
+ writeOutput4(invocation.io, output);
15496
+ } catch (error) {
15497
+ handleCommandError(invocation.io, error);
15498
+ }
15499
+ });
13907
15500
  specCmd.command(SPEC_DOMAIN_CLI.STATUS_COMMAND).description("Get product status").option(SPEC_DOMAIN_CLI.JSON_OPTION, "Output as JSON").option(SPEC_DOMAIN_CLI.FORMAT_OPTION_DEFINITION, "Output format (text|json|markdown|table)").option(SPEC_DOMAIN_CLI.UPDATE_OPTION, "Refresh each node's spx.status.json before reporting").action(async (options) => {
13908
15501
  try {
13909
15502
  const format2 = resolveStatusFormat(options);
@@ -14224,11 +15817,11 @@ var testingDomain = createTestingDomain();
14224
15817
 
14225
15818
  // src/interfaces/cli/validation.ts
14226
15819
  import { existsSync as existsSync9, realpathSync } from "fs";
14227
- import { isAbsolute as isAbsolute11, relative as relative9, resolve as resolve11, sep as sep4 } from "path";
15820
+ import { isAbsolute as isAbsolute11, relative as relative9, resolve as resolve12, sep as sep4 } from "path";
14228
15821
 
14229
15822
  // src/commands/validation/formatting.ts
14230
15823
  import { existsSync, statSync } from "fs";
14231
- import { isAbsolute as isAbsolute4, join as join24, relative as relative4 } from "path";
15824
+ import { isAbsolute as isAbsolute4, join as join26, relative as relative4 } from "path";
14232
15825
 
14233
15826
  // src/validation/config/path-filter.ts
14234
15827
  import { isAbsolute as isAbsolute3, relative as relative3 } from "path";
@@ -14396,7 +15989,7 @@ function buildDprintCheckArgs(options) {
14396
15989
  }
14397
15990
  async function validateFormatting(context, runner = defaultFormattingProcessRunner) {
14398
15991
  const args = buildDprintCheckArgs({ files: context.files, excludes: context.excludes });
14399
- return new Promise((resolve12) => {
15992
+ return new Promise((resolve13) => {
14400
15993
  const child = spawnManagedSubprocess(runner, DPRINT_COMMAND, args, { cwd: context.projectRoot });
14401
15994
  const chunks = [];
14402
15995
  const capture = (chunk) => {
@@ -14405,10 +15998,10 @@ async function validateFormatting(context, runner = defaultFormattingProcessRunn
14405
15998
  child.stdout?.on(VALIDATION_SUBPROCESS_EVENTS.DATA, capture);
14406
15999
  child.stderr?.on(VALIDATION_SUBPROCESS_EVENTS.DATA, capture);
14407
16000
  child.on(VALIDATION_SUBPROCESS_EVENTS.CLOSE, (code) => {
14408
- resolve12({ success: code === 0, output: chunks.join("") });
16001
+ resolve13({ success: code === 0, output: chunks.join("") });
14409
16002
  });
14410
16003
  child.on(VALIDATION_SUBPROCESS_EVENTS.ERROR, (error) => {
14411
- resolve12({ success: false, output: chunks.join(""), error: error.message });
16004
+ resolve13({ success: false, output: chunks.join(""), error: error.message });
14412
16005
  });
14413
16006
  });
14414
16007
  }
@@ -14488,7 +16081,7 @@ async function formattingCommand(options) {
14488
16081
  };
14489
16082
  }
14490
16083
  const validationConfig = loaded.value[validationConfigDescriptor.section];
14491
- if (!existsSync(join24(cwd, DPRINT_CONFIG_FILENAME))) {
16084
+ if (!existsSync(join26(cwd, DPRINT_CONFIG_FILENAME))) {
14492
16085
  const output2 = quiet ? "" : `${VALIDATION_STAGE_DISPLAY_NAMES.FORMATTING}: skipped (${FORMATTING_COMMAND_OUTPUT.NO_CONFIG_SKIP_REASON})`;
14493
16086
  return { exitCode: 0, output: output2, durationMs: Date.now() - startTime };
14494
16087
  }
@@ -14535,7 +16128,7 @@ function normalizeFormattingPathOperand(productDir, relativePath) {
14535
16128
  return `${normalizedDirectory}${DPRINT_RECURSIVE_DIRECTORY_GLOB_SUFFIX}`;
14536
16129
  }
14537
16130
  function isFormattingFileOperand(productDir, relativePath) {
14538
- const absolutePath = join24(productDir, relativePath);
16131
+ const absolutePath = join26(productDir, relativePath);
14539
16132
  return !existsSync(absolutePath) || !statSync(absolutePath).isDirectory();
14540
16133
  }
14541
16134
  function formattingPathOperandsForValidationPathFilter(productDir, relativePath, pathFilter) {
@@ -14559,11 +16152,11 @@ var formattingValidationLanguage = {
14559
16152
  };
14560
16153
 
14561
16154
  // src/commands/validation/markdown.ts
14562
- import { isAbsolute as isAbsolute5, join as join26 } from "path";
16155
+ import { isAbsolute as isAbsolute5, join as join28 } from "path";
14563
16156
 
14564
16157
  // src/validation/steps/markdown.ts
14565
16158
  import { existsSync as existsSync2, statSync as statSync2 } from "fs";
14566
- import { basename as basename7, dirname as dirname10, join as join25, relative as pathRelative } from "path";
16159
+ import { basename as basename7, dirname as dirname10, join as join27, relative as pathRelative } from "path";
14567
16160
  import { main as markdownlintMain } from "markdownlint-cli2";
14568
16161
  import relativeLinksRule from "markdownlint-rule-relative-links";
14569
16162
  var MARKDOWN_DEFAULT_DIRECTORY_NAMES = [SPEC_TREE_CONFIG.ROOT_DIRECTORY, "docs"];
@@ -14601,7 +16194,7 @@ function buildMarkdownlintConfig(directoryName) {
14601
16194
  };
14602
16195
  }
14603
16196
  function getDefaultDirectories(projectRoot) {
14604
- return MARKDOWN_DEFAULT_DIRECTORY_NAMES.map((name) => join25(projectRoot, name)).filter((dir) => existsSync2(dir));
16197
+ return MARKDOWN_DEFAULT_DIRECTORY_NAMES.map((name) => join27(projectRoot, name)).filter((dir) => existsSync2(dir));
14605
16198
  }
14606
16199
  function resolveMarkdownValidationTarget(path7, deps = defaultMarkdownValidationTargetDeps) {
14607
16200
  if (isExistingDirectory(path7, deps)) {
@@ -14620,7 +16213,7 @@ function resolveMarkdownValidationTarget(path7, deps = defaultMarkdownValidation
14620
16213
  function getExcludeGlobsForTarget(target, projectRoot, entries) {
14621
16214
  if (projectRoot === void 0 || entries.length === 0) return [];
14622
16215
  const directory = targetDirectory(target);
14623
- const specTreeRoot = join25(projectRoot, SPEC_TREE_CONFIG.ROOT_DIRECTORY);
16216
+ const specTreeRoot = join27(projectRoot, SPEC_TREE_CONFIG.ROOT_DIRECTORY);
14624
16217
  const targetPath = normalizePathPrefix(pathRelative(specTreeRoot, directory));
14625
16218
  return entries.flatMap((entry) => {
14626
16219
  const excludedPath = normalizePathPrefix(entry);
@@ -14631,7 +16224,7 @@ function getExcludeGlobsForTarget(target, projectRoot, entries) {
14631
16224
  return [];
14632
16225
  }
14633
16226
  return directMarkdownGlobs(
14634
- normalizePathPrefix(pathRelative(directory, join25(specTreeRoot, excludedPath)))
16227
+ normalizePathPrefix(pathRelative(directory, join27(specTreeRoot, excludedPath)))
14635
16228
  );
14636
16229
  });
14637
16230
  }
@@ -14754,7 +16347,7 @@ async function validateTarget(target, config, projectRoot, ignoreGlobs = []) {
14754
16347
  if (parsed) {
14755
16348
  errors.push({
14756
16349
  ...parsed,
14757
- file: join25(directory, parsed.file)
16350
+ file: join27(directory, parsed.file)
14758
16351
  });
14759
16352
  }
14760
16353
  }
@@ -14776,11 +16369,11 @@ function validationPathExcludeGlobsForTarget(target, projectRoot, excludes) {
14776
16369
  if (!pathContainsValidationPath(targetPath, excludedPath)) {
14777
16370
  return [];
14778
16371
  }
14779
- const relativeExclude = normalizePathPrefix(pathRelative(directory, join25(projectRoot, excludedPath)));
16372
+ const relativeExclude = normalizePathPrefix(pathRelative(directory, join27(projectRoot, excludedPath)));
14780
16373
  if (relativeExclude.length === 0) {
14781
16374
  return [MARKDOWN_DIRECTORY_GLOB];
14782
16375
  }
14783
- const absoluteExclude = join25(projectRoot, excludedPath);
16376
+ const absoluteExclude = join27(projectRoot, excludedPath);
14784
16377
  return [
14785
16378
  isExistingFile(absoluteExclude, defaultMarkdownValidationTargetDeps) ? relativeExclude : `${relativeExclude}/**`
14786
16379
  ];
@@ -14865,11 +16458,11 @@ async function markdownCommand(options) {
14865
16458
  return formatMarkdownResult(result, skippedOutput, quiet, durationMs);
14866
16459
  }
14867
16460
  function markdownValidationOperandPath(productDir, filePath) {
14868
- return isAbsolute5(filePath) ? filePath : join26(productDir, filePath);
16461
+ return isAbsolute5(filePath) ? filePath : join28(productDir, filePath);
14869
16462
  }
14870
16463
  function defaultMarkdownTargets(productDir, pathFilter) {
14871
16464
  return getDefaultDirectories(productDir).flatMap(
14872
- (directory) => validationPathFilterIntersections(toProjectRelativeValidationPath(productDir, directory), pathFilter).map((intersection) => resolveMarkdownValidationTarget(join26(productDir, intersection)).target).filter((target) => target !== void 0)
16465
+ (directory) => validationPathFilterIntersections(toProjectRelativeValidationPath(productDir, directory), pathFilter).map((intersection) => resolveMarkdownValidationTarget(join28(productDir, intersection)).target).filter((target) => target !== void 0)
14873
16466
  );
14874
16467
  }
14875
16468
  function formatSkippedFileScope(target) {
@@ -15765,7 +17358,7 @@ var ParseErrorCode;
15765
17358
 
15766
17359
  // src/validation/config/scope.ts
15767
17360
  import { existsSync as existsSync3, readdirSync, readFileSync as readFileSync3 } from "fs";
15768
- import { isAbsolute as isAbsolute6, join as join27, relative as relative5, resolve as resolve9 } from "path";
17361
+ import { isAbsolute as isAbsolute6, join as join29, relative as relative5, resolve as resolve10 } from "path";
15769
17362
  var TSCONFIG_FILES = {
15770
17363
  full: "tsconfig.json",
15771
17364
  production: "tsconfig.production.json"
@@ -15802,7 +17395,7 @@ var EXPLICIT_TYPESCRIPT_SCOPE_TARGET_KIND = {
15802
17395
  FILE: "file"
15803
17396
  };
15804
17397
  function resolveProjectPath(projectRoot, path7) {
15805
- return isAbsolute6(path7) ? path7 : join27(projectRoot, path7);
17398
+ return isAbsolute6(path7) ? path7 : join29(projectRoot, path7);
15806
17399
  }
15807
17400
  function parseTypeScriptConfig(configPath, deps = defaultScopeDeps) {
15808
17401
  try {
@@ -15841,12 +17434,12 @@ function hasTypeScriptFilesRecursive(dirPath, maxDepth = 2, deps = defaultScopeD
15841
17434
  try {
15842
17435
  const items = deps.readdirSync(dirPath, { withFileTypes: true });
15843
17436
  const hasDirectTsFiles = items.some(
15844
- (item) => item.isFile() && pathHasTypeScriptSourceExtension(item.name) && pathPassesTypeScriptFileDiscoveryExcludes(join27(dirPath, item.name), options)
17437
+ (item) => item.isFile() && pathHasTypeScriptSourceExtension(item.name) && pathPassesTypeScriptFileDiscoveryExcludes(join29(dirPath, item.name), options)
15845
17438
  );
15846
17439
  if (hasDirectTsFiles) return true;
15847
17440
  const subdirs = items.filter((item) => item.isDirectory() && !item.name.startsWith("."));
15848
17441
  for (const subdir of subdirs.slice(0, 5)) {
15849
- if (hasTypeScriptFilesRecursive(join27(dirPath, subdir.name), maxDepth - 1, deps, options)) {
17442
+ if (hasTypeScriptFilesRecursive(join29(dirPath, subdir.name), maxDepth - 1, deps, options)) {
15850
17443
  return true;
15851
17444
  }
15852
17445
  }
@@ -15883,7 +17476,7 @@ function directoryContributesTypeScriptScope(dir, config, projectRoot, deps) {
15883
17476
  return false;
15884
17477
  }
15885
17478
  try {
15886
- return hasTypeScriptFilesRecursive(join27(projectRoot, dir), 2, deps, {
17479
+ return hasTypeScriptFilesRecursive(join29(projectRoot, dir), 2, deps, {
15887
17480
  excludePatterns: config.exclude,
15888
17481
  projectRoot
15889
17482
  });
@@ -16112,13 +17705,13 @@ function normalizeActiveIncludePattern(pattern, projectRoot, deps) {
16112
17705
  function filterActiveIncludePatterns(patterns, excludePatterns, projectRoot, deps) {
16113
17706
  return patterns.map((pattern) => normalizeActiveIncludePattern(pattern, projectRoot, deps)).filter((pattern) => typeScriptScopePatternTargetsTypeScriptSource(pattern)).filter((pattern) => {
16114
17707
  const topLevelDir = getLiteralTopLevelPatternDirectory(pattern);
16115
- return topLevelDir === null || deps.existsSync(join27(projectRoot, topLevelDir));
17708
+ return topLevelDir === null || deps.existsSync(join29(projectRoot, topLevelDir));
16116
17709
  }).filter((pattern) => pathPassesValidationFilter(pattern, { exclude: excludePatterns }));
16117
17710
  }
16118
17711
  function getValidationDirectories(scope2, projectRoot, deps = defaultScopeDeps) {
16119
17712
  const config = resolveTypeScriptConfig(scope2, projectRoot, deps);
16120
17713
  const configDirectories = getTopLevelDirectoriesWithTypeScript(config, projectRoot, deps);
16121
- const existingDirectories = configDirectories.filter((dir) => deps.existsSync(join27(projectRoot, dir)));
17714
+ const existingDirectories = configDirectories.filter((dir) => deps.existsSync(join29(projectRoot, dir)));
16122
17715
  return existingDirectories;
16123
17716
  }
16124
17717
  function getTypeScriptScope(scope2, projectRoot, deps = defaultScopeDeps) {
@@ -16139,13 +17732,13 @@ function pathPassesTypeScriptScope(path7, scopeConfig) {
16139
17732
  return included && !excluded;
16140
17733
  }
16141
17734
  function pathStaysInsideTypeScriptScopeRoot(projectRoot, path7) {
16142
- const resolvedPath = isAbsolute6(path7) ? resolve9(path7) : resolve9(projectRoot, path7);
17735
+ const resolvedPath = isAbsolute6(path7) ? resolve10(path7) : resolve10(projectRoot, path7);
16143
17736
  const relativePath = relative5(projectRoot, resolvedPath);
16144
17737
  const segments = normalizeTypeScriptScopePath(relativePath).split(PATH_SEGMENT_SEPARATOR4);
16145
17738
  return relativePath.length === 0 || !segments.includes("..") && !isAbsolute6(relativePath);
16146
17739
  }
16147
17740
  function toProjectRelativeTypeScriptScopePath(projectRoot, path7) {
16148
- const resolvedPath = isAbsolute6(path7) ? resolve9(path7) : resolve9(projectRoot, path7);
17741
+ const resolvedPath = isAbsolute6(path7) ? resolve10(path7) : resolve10(projectRoot, path7);
16149
17742
  const relativePath = relative5(projectRoot, resolvedPath);
16150
17743
  return relativePath.length === 0 ? TYPESCRIPT_SCOPE_PROJECT_ROOT : normalizeTypeScriptScopePath(relativePath);
16151
17744
  }
@@ -16199,10 +17792,10 @@ function explicitTypeScriptScopeTargetPassesScope(target, scopeConfig) {
16199
17792
  }
16200
17793
  return scopeConfig.directories.some(
16201
17794
  (directory) => pathMatchesLiteralPrefix(directory, target.path) || pathMatchesLiteralPrefix(target.path, directory)
16202
- ) || pathPassesTypeScriptScope(join27(target.path, TYPESCRIPT_SCOPE_DIRECTORY_PROBE_FILENAME), scopeConfig);
17795
+ ) || pathPassesTypeScriptScope(join29(target.path, TYPESCRIPT_SCOPE_DIRECTORY_PROBE_FILENAME), scopeConfig);
16203
17796
  }
16204
17797
  function directoryPassesTypeScriptExcludes(directory, scopeConfig) {
16205
- const probePath = join27(directory, TYPESCRIPT_SCOPE_DIRECTORY_PROBE_FILENAME);
17798
+ const probePath = join29(directory, TYPESCRIPT_SCOPE_DIRECTORY_PROBE_FILENAME);
16206
17799
  return !scopeConfig.excludePatterns.some(
16207
17800
  (pattern) => typeScriptScopePatternCoversDirectorySourceSet(pattern, directory) || pathMatchesTypeScriptPattern(probePath, pattern)
16208
17801
  );
@@ -16482,7 +18075,7 @@ import {
16482
18075
  cruise as dependencyCruiser
16483
18076
  } from "dependency-cruiser";
16484
18077
  import extractTypeScriptConfig from "dependency-cruiser/config-utl/extract-ts-config";
16485
- import { join as join28 } from "path";
18078
+ import { join as join30 } from "path";
16486
18079
  var DEPENDENCY_CRUISER_MODULE_SYSTEMS = ["es6", "cjs"];
16487
18080
  var DEPENDENCY_CRUISER_TYPESCRIPT_SOURCE_GLOB_SUFFIXES = [...TYPESCRIPT_FALLBACK_INCLUDE_PATTERNS];
16488
18081
  var DEPENDENCY_CRUISER_TYPESCRIPT_SOURCE_PATTERN = String.raw`(?<!\.d)\.(?:[cm]?ts|tsx)$`;
@@ -16722,7 +18315,7 @@ async function validateCircularDependencies(scope2, typescriptScope, projectRoot
16722
18315
  if (analyzeSourcePatterns.length === 0) {
16723
18316
  return { success: true };
16724
18317
  }
16725
- const tsConfigFile = join28(projectRoot, TSCONFIG_FILES[scope2]);
18318
+ const tsConfigFile = join30(projectRoot, TSCONFIG_FILES[scope2]);
16726
18319
  const result = await deps.dependencyCruiser(
16727
18320
  analyzeSourcePatterns,
16728
18321
  buildDependencyCruiserOptions(typescriptScope, projectRoot, tsConfigFile),
@@ -16845,7 +18438,7 @@ async function circularCommand(options, deps = defaultCircularCommandDeps) {
16845
18438
  // src/validation/steps/knip.ts
16846
18439
  import { existsSync as existsSync4 } from "fs";
16847
18440
  import { mkdir as mkdir5, mkdtemp as mkdtemp2, rm as rm2, writeFile as writeFile3 } from "fs/promises";
16848
- import { isAbsolute as isAbsolute7, join as join29 } from "path";
18441
+ import { isAbsolute as isAbsolute7, join as join31 } from "path";
16849
18442
  var defaultKnipProcessRunner = lifecycleProcessRunner;
16850
18443
  var KNIP_COMMAND_TOKENS = {
16851
18444
  COMMAND: "knip",
@@ -16878,7 +18471,7 @@ async function validateKnip2(context, runner = defaultKnipProcessRunner, deps =
16878
18471
  }
16879
18472
  async function runKnipSubprocess(projectRoot, typescriptScope, runner, deps) {
16880
18473
  const scopedTsconfig = typescriptScope.filteredByValidationPaths ? await createScopedKnipTsconfig(projectRoot, typescriptScope, deps) : void 0;
16881
- const localBin = join29(projectRoot, "node_modules", ".bin", "knip");
18474
+ const localBin = join31(projectRoot, "node_modules", ".bin", "knip");
16882
18475
  const binary = deps.existsSync(localBin) ? localBin : KNIP_COMMAND_TOKENS.NPX_COMMAND;
16883
18476
  const baseArgs = scopedTsconfig === void 0 ? [] : [
16884
18477
  KNIP_COMMAND_TOKENS.USE_TSCONFIG_FILES_FLAG,
@@ -16908,13 +18501,13 @@ async function runKnipSubprocess(projectRoot, typescriptScope, runner, deps) {
16908
18501
  knipProcess.stderr?.on("data", (data) => {
16909
18502
  knipError += data.toString();
16910
18503
  });
16911
- return new Promise((resolve12) => {
18504
+ return new Promise((resolve13) => {
16912
18505
  const resolveAfterCleanup = (result) => {
16913
18506
  if (resultResolved) {
16914
18507
  return;
16915
18508
  }
16916
18509
  resultResolved = true;
16917
- void cleanupOnce().finally(() => resolve12(result));
18510
+ void cleanupOnce().finally(() => resolve13(result));
16918
18511
  };
16919
18512
  knipProcess.on("close", (code) => {
16920
18513
  if (code === 0) {
@@ -16933,11 +18526,11 @@ async function runKnipSubprocess(projectRoot, typescriptScope, runner, deps) {
16933
18526
  });
16934
18527
  }
16935
18528
  async function createScopedKnipTsconfig(projectRoot, typescriptScope, deps) {
16936
- const tempParentDir = join29(projectRoot, ...TEMPORARY_TSCONFIG_PARENT_SEGMENTS);
18529
+ const tempParentDir = join31(projectRoot, ...TEMPORARY_TSCONFIG_PARENT_SEGMENTS);
16937
18530
  await deps.mkdir(tempParentDir, { recursive: true });
16938
- const tempDir = await deps.mkdtemp(join29(tempParentDir, "validate-knip-"));
16939
- const configPath = join29(tempDir, TSCONFIG_FILES.full);
16940
- const toProjectPathPattern = (pattern) => isAbsolute7(pattern) ? pattern : join29(projectRoot, pattern);
18531
+ const tempDir = await deps.mkdtemp(join31(tempParentDir, "validate-knip-"));
18532
+ const configPath = join31(tempDir, TSCONFIG_FILES.full);
18533
+ const toProjectPathPattern = (pattern) => isAbsolute7(pattern) ? pattern : join31(projectRoot, pattern);
16941
18534
  const project = [
16942
18535
  ...typescriptScope.directories.flatMap(
16943
18536
  (directory) => TYPESCRIPT_FALLBACK_INCLUDE_PATTERNS.map((pattern) => `${directory}/${pattern}`)
@@ -16945,7 +18538,7 @@ async function createScopedKnipTsconfig(projectRoot, typescriptScope, deps) {
16945
18538
  ...typescriptScope.filePatterns
16946
18539
  ];
16947
18540
  const config = {
16948
- extends: join29(projectRoot, TSCONFIG_FILES.full),
18541
+ extends: join31(projectRoot, TSCONFIG_FILES.full),
16949
18542
  include: project.map(toProjectPathPattern),
16950
18543
  exclude: typescriptScope.excludePatterns.map(toProjectPathPattern)
16951
18544
  };
@@ -17013,12 +18606,12 @@ async function knipCommand(options, deps = defaultKnipCommandDeps) {
17013
18606
 
17014
18607
  // src/validation/steps/eslint.ts
17015
18608
  import { existsSync as existsSync6 } from "fs";
17016
- import { join as join31 } from "path";
18609
+ import { join as join33 } from "path";
17017
18610
 
17018
18611
  // src/validation/lint-policy.ts
17019
18612
  import { execFileSync as execFileSync2 } from "child_process";
17020
18613
  import { existsSync as existsSync5, readdirSync as readdirSync2, readFileSync as readFileSync4, statSync as statSync3 } from "fs";
17021
- import { join as join30 } from "path";
18614
+ import { join as join32 } from "path";
17022
18615
 
17023
18616
  // src/validation/lint-policy-constants.ts
17024
18617
  var LINT_POLICY_MANIFESTS = {
@@ -17066,17 +18659,17 @@ function isSpecTreeNodePath(entry) {
17066
18659
  }
17067
18660
  function readManifest2(productDir, file, key) {
17068
18661
  return parseLintPolicyManifest(
17069
- readFileSync4(join30(productDir, file), "utf-8"),
18662
+ readFileSync4(join32(productDir, file), "utf-8"),
17070
18663
  file,
17071
18664
  key
17072
18665
  );
17073
18666
  }
17074
18667
  function manifestExists(productDir, file) {
17075
- return existsSync5(join30(productDir, file));
18668
+ return existsSync5(join32(productDir, file));
17076
18669
  }
17077
18670
  function findDeprecatedSpecNodePath(productDir) {
17078
18671
  function visit2(relativeDirectory) {
17079
- const absoluteDirectory = join30(productDir, relativeDirectory);
18672
+ const absoluteDirectory = join32(productDir, relativeDirectory);
17080
18673
  for (const entry of readdirSync2(absoluteDirectory, { withFileTypes: true })) {
17081
18674
  if (!entry.isDirectory()) {
17082
18675
  continue;
@@ -17092,7 +18685,7 @@ function findDeprecatedSpecNodePath(productDir) {
17092
18685
  }
17093
18686
  return void 0;
17094
18687
  }
17095
- const specTreeRootPath = join30(productDir, SPEC_TREE_ROOT);
18688
+ const specTreeRootPath = join32(productDir, SPEC_TREE_ROOT);
17096
18689
  if (!existsSync5(specTreeRootPath)) {
17097
18690
  return void 0;
17098
18691
  }
@@ -17118,7 +18711,7 @@ function assertManifestEntries(productDir, file, entries, suffixPredicate, suffi
17118
18711
  if (!suffixPredicate(entry)) {
17119
18712
  throw new Error(`${file} entry must ${suffixDescription}: ${entry}`);
17120
18713
  }
17121
- const absoluteEntry = join30(productDir, entry);
18714
+ const absoluteEntry = join32(productDir, entry);
17122
18715
  if (!existsSync5(absoluteEntry) || !statSync3(absoluteEntry).isDirectory()) {
17123
18716
  throw new Error(`${file} entry does not exist as a directory: ${entry}`);
17124
18717
  }
@@ -17342,8 +18935,8 @@ async function validateESLint(context, runner = defaultEslintProcessRunner, outp
17342
18935
  scope: scope2,
17343
18936
  scopeConfig: context.scopeConfig
17344
18937
  });
17345
- return new Promise((resolve12) => {
17346
- const localBin = join31(projectRoot, ...ESLINT_LOCAL_BIN_SEGMENTS);
18938
+ return new Promise((resolve13) => {
18939
+ const localBin = join33(projectRoot, ...ESLINT_LOCAL_BIN_SEGMENTS);
17347
18940
  const binary = existsSync6(localBin) ? localBin : "npx";
17348
18941
  const spawnArgs = binary === "npx" ? eslintArgs : eslintArgs.slice(1);
17349
18942
  const eslintProcess = spawnManagedSubprocess(runner, binary, spawnArgs, {
@@ -17352,13 +18945,13 @@ async function validateESLint(context, runner = defaultEslintProcessRunner, outp
17352
18945
  forwardValidationSubprocessOutput(eslintProcess, outputStreams);
17353
18946
  eslintProcess.on(VALIDATION_SUBPROCESS_EVENTS.CLOSE, (code) => {
17354
18947
  if (code === 0) {
17355
- resolve12({ success: true });
18948
+ resolve13({ success: true });
17356
18949
  } else {
17357
- resolve12({ success: false, error: `ESLint exited with code ${code}` });
18950
+ resolve13({ success: false, error: `ESLint exited with code ${code}` });
17358
18951
  }
17359
18952
  });
17360
18953
  eslintProcess.on(VALIDATION_SUBPROCESS_EVENTS.ERROR, (error) => {
17361
- resolve12({ success: false, error: error.message });
18954
+ resolve13({ success: false, error: error.message });
17362
18955
  });
17363
18956
  });
17364
18957
  }
@@ -17464,16 +19057,16 @@ function formatLintResult(result, quiet, durationMs) {
17464
19057
 
17465
19058
  // src/validation/literal/index.ts
17466
19059
  import { readFile as readFile12 } from "fs/promises";
17467
- import { isAbsolute as isAbsolute9, relative as relative7, resolve as resolve10 } from "path";
19060
+ import { isAbsolute as isAbsolute9, relative as relative7, resolve as resolve11 } from "path";
17468
19061
 
17469
19062
  // src/lib/file-inclusion/pipeline.ts
17470
- import { readdir as readdir10, readFile as readFile11, stat as stat7 } from "fs/promises";
17471
- import { join as join33, relative as relative6, sep as sep3 } from "path";
19063
+ import { readdir as readdir11, readFile as readFile11, stat as stat7 } from "fs/promises";
19064
+ import { join as join35, relative as relative6, sep as sep3 } from "path";
17472
19065
 
17473
19066
  // src/lib/file-inclusion/ignore-source.ts
17474
19067
  import { execFileSync as execFileSync3 } from "child_process";
17475
19068
  import { existsSync as existsSync7 } from "fs";
17476
- import { isAbsolute as isAbsolute8, join as join32 } from "path";
19069
+ import { isAbsolute as isAbsolute8, join as join34 } from "path";
17477
19070
  var GIT_EXECUTABLE2 = "git";
17478
19071
  var GIT_LS_FILES_ARGS = {
17479
19072
  LS_FILES: "ls-files",
@@ -17591,7 +19184,7 @@ function optionalExcludeFromArgs(path7) {
17591
19184
  return existsSync7(path7) ? excludeFromArgs(path7) : [];
17592
19185
  }
17593
19186
  function resolveGitPath(productDir, path7) {
17594
- return isAbsolute8(path7) ? path7 : join32(productDir, path7);
19187
+ return isAbsolute8(path7) ? path7 : join34(productDir, path7);
17595
19188
  }
17596
19189
  function readInfoExcludePath(productDir) {
17597
19190
  const commonDir = readOptionalGit(productDir, [
@@ -17602,12 +19195,12 @@ function readInfoExcludePath(productDir) {
17602
19195
  return void 0;
17603
19196
  }
17604
19197
  const absoluteCommonDir = resolveGitPath(productDir, commonDir);
17605
- return join32(absoluteCommonDir, INFO_EXCLUDE_RELATIVE_PATH);
19198
+ return join34(absoluteCommonDir, INFO_EXCLUDE_RELATIVE_PATH);
17606
19199
  }
17607
19200
  function defaultGlobalExcludesPath(env) {
17608
19201
  const xdgConfigHome = env[GIT_GLOBAL_EXCLUDES_ENV_KEYS.XDG_CONFIG_HOME];
17609
19202
  if (xdgConfigHome !== void 0 && xdgConfigHome.length > 0) {
17610
- return join32(
19203
+ return join34(
17611
19204
  xdgConfigHome,
17612
19205
  GIT_DEFAULT_GLOBAL_IGNORE_PATH.GIT_DIRECTORY,
17613
19206
  GIT_DEFAULT_GLOBAL_IGNORE_PATH.IGNORE_FILE
@@ -17617,7 +19210,7 @@ function defaultGlobalExcludesPath(env) {
17617
19210
  if (home === void 0 || home.length === 0) {
17618
19211
  return void 0;
17619
19212
  }
17620
- return join32(
19213
+ return join34(
17621
19214
  home,
17622
19215
  GIT_DEFAULT_GLOBAL_IGNORE_PATH.CONFIG_DIRECTORY,
17623
19216
  GIT_DEFAULT_GLOBAL_IGNORE_PATH.GIT_DIRECTORY,
@@ -17794,7 +19387,7 @@ async function isGitdirPointerFile(absolutePath) {
17794
19387
  }
17795
19388
  async function readDirectoryEntries2(absoluteDir) {
17796
19389
  try {
17797
- return await readdir10(absoluteDir, { withFileTypes: true });
19390
+ return await readdir11(absoluteDir, { withFileTypes: true });
17798
19391
  } catch (err) {
17799
19392
  if (isNodeError4(err) && (err.code === "ENOENT" || err.code === "ENOTDIR")) {
17800
19393
  return [];
@@ -17814,7 +19407,7 @@ async function isGitMetadataEntry(absolutePath, entry) {
17814
19407
  }
17815
19408
  async function directoryContainsGitdirPointer(absoluteDir, entries) {
17816
19409
  for (const entry of entries) {
17817
- if (entry.name === GIT_INTERNAL_DIRECTORY && entry.isFile() && await isGitdirPointerFile(join33(absoluteDir, entry.name))) {
19410
+ if (entry.name === GIT_INTERNAL_DIRECTORY && entry.isFile() && await isGitdirPointerFile(join35(absoluteDir, entry.name))) {
17818
19411
  return true;
17819
19412
  }
17820
19413
  }
@@ -17826,7 +19419,7 @@ async function collectPaths(absoluteDir, productDir, result, mode, skipWhenGitMe
17826
19419
  return;
17827
19420
  }
17828
19421
  for (const entry of dirEntries) {
17829
- const absolutePath = join33(absoluteDir, entry.name);
19422
+ const absolutePath = join35(absoluteDir, entry.name);
17830
19423
  const relativePath = normalizeProductPath(productDir, absolutePath);
17831
19424
  if (await isGitMetadataEntry(absolutePath, entry)) continue;
17832
19425
  if (entry.isDirectory()) {
@@ -17882,7 +19475,7 @@ async function runPipeline(sequence, productDir, request, config, ignoreReader)
17882
19475
  }
17883
19476
  async function addExplicitPath(path7, productDir, explicitPathSet, included) {
17884
19477
  addExplicitEntry(path7, explicitPathSet, included);
17885
- const absolutePath = join33(productDir, path7);
19478
+ const absolutePath = join35(productDir, path7);
17886
19479
  if (!await isDirectory(absolutePath)) return;
17887
19480
  const descendantPaths = [];
17888
19481
  await collectPaths(absolutePath, productDir, descendantPaths, DIRECTORY_TRAVERSAL_MODE.EXPLICIT);
@@ -17933,7 +19526,7 @@ var SPEC_TREE_ENV_FIXTURE_WRITER_METHODS = [
17933
19526
  ];
17934
19527
 
17935
19528
  // src/validation/literal/detector.ts
17936
- var REMEDIATION5 = {
19529
+ var REMEDIATION6 = {
17937
19530
  IMPORT_FROM_SOURCE: "import-from-source",
17938
19531
  REFACTOR_TO_SOURCE_OR_GENERATOR: "refactor-to-source-or-generator"
17939
19532
  };
@@ -18314,7 +19907,7 @@ function reuseFindings(kind, value, srcLocs, allTestLocs) {
18314
19907
  kind,
18315
19908
  value,
18316
19909
  src: srcLocs,
18317
- remediation: REMEDIATION5.IMPORT_FROM_SOURCE
19910
+ remediation: REMEDIATION6.IMPORT_FROM_SOURCE
18318
19911
  }));
18319
19912
  }
18320
19913
  function dupeFindings(kind, value, allTestLocs) {
@@ -18323,7 +19916,7 @@ function dupeFindings(kind, value, allTestLocs) {
18323
19916
  kind,
18324
19917
  value,
18325
19918
  otherTests: [...allTestLocs.slice(0, index), ...allTestLocs.slice(index + 1)],
18326
- remediation: REMEDIATION5.REFACTOR_TO_SOURCE_OR_GENERATOR
19919
+ remediation: REMEDIATION6.REFACTOR_TO_SOURCE_OR_GENERATOR
18327
19920
  }));
18328
19921
  }
18329
19922
 
@@ -18352,7 +19945,7 @@ var DEFAULT_LITERAL_COLLECT_OPTIONS = {
18352
19945
  async function validateLiteralReuse(input) {
18353
19946
  const config = input.config ?? literalConfigDescriptor.defaults;
18354
19947
  const explicitPaths = input.explicitFiles?.map((f) => {
18355
- const abs = isAbsolute9(f) ? f : resolve10(input.productDir, f);
19948
+ const abs = isAbsolute9(f) ? f : resolve11(input.productDir, f);
18356
19949
  return relative7(input.productDir, abs).split(/[\\/]/g).join("/");
18357
19950
  });
18358
19951
  if (input.pathConfig !== void 0 && (explicitPaths === void 0 || explicitPaths.length === 0) && validationPathFilterHasNoMatchingIncludes(input.pathConfig)) {
@@ -18372,7 +19965,7 @@ async function validateLiteralReuse(input) {
18372
19965
  const filtered = literalScopeConfig === void 0 ? pathFiltered : pathFiltered.filter(
18373
19966
  (entry) => entry.decisionTrail.some((decision) => decision.layer === EXPLICIT_OVERRIDE_LAYER) || pathPassesTypeScriptScope(entry.path, literalScopeConfig)
18374
19967
  );
18375
- const candidateFiles = filtered.filter((entry) => isTypescriptSource(entry.path)).map((entry) => resolve10(input.productDir, entry.path));
19968
+ const candidateFiles = filtered.filter((entry) => isTypescriptSource(entry.path)).map((entry) => resolve11(input.productDir, entry.path));
18376
19969
  const collectOptions = {
18377
19970
  visitorKeys: defaultVisitorKeys,
18378
19971
  minStringLength: config.minStringLength,
@@ -18682,7 +20275,7 @@ function formatLoc(loc) {
18682
20275
  // src/validation/steps/typescript.ts
18683
20276
  import { existsSync as existsSync8, mkdirSync, rmSync, writeFileSync } from "fs";
18684
20277
  import { mkdtemp as mkdtemp3 } from "fs/promises";
18685
- import { isAbsolute as isAbsolute10, join as join34, relative as relative8 } from "path";
20278
+ import { isAbsolute as isAbsolute10, join as join36, relative as relative8 } from "path";
18686
20279
  var defaultTypeScriptProcessRunner = lifecycleProcessRunner;
18687
20280
  var defaultTypeScriptDeps = {
18688
20281
  mkdtemp: mkdtemp3,
@@ -18697,9 +20290,9 @@ function formatTypeScriptExitCodeError(code) {
18697
20290
  var TEMPORARY_TSCONFIG_COMPILER_OPTIONS = { noEmit: true };
18698
20291
  var TEMPORARY_TSCONFIG_DIR_PREFIX = "validate-ts-";
18699
20292
  async function createTemporaryTsconfigDir(projectRoot, deps) {
18700
- const parent = join34(projectRoot, ...TEMPORARY_TSCONFIG_PARENT_SEGMENTS);
20293
+ const parent = join36(projectRoot, ...TEMPORARY_TSCONFIG_PARENT_SEGMENTS);
18701
20294
  deps.mkdirSync(parent, { recursive: true });
18702
- return deps.mkdtemp(join34(parent, TEMPORARY_TSCONFIG_DIR_PREFIX));
20295
+ return deps.mkdtemp(join36(parent, TEMPORARY_TSCONFIG_DIR_PREFIX));
18703
20296
  }
18704
20297
  function buildTypeScriptArgs(context) {
18705
20298
  const { scope: scope2, configFile } = context;
@@ -18707,11 +20300,11 @@ function buildTypeScriptArgs(context) {
18707
20300
  }
18708
20301
  async function createFileSpecificTsconfig(scope2, files, projectRoot, deps = defaultTypeScriptDeps) {
18709
20302
  const tempDir = await createTemporaryTsconfigDir(projectRoot, deps);
18710
- const configPath = join34(tempDir, "tsconfig.json");
20303
+ const configPath = join36(tempDir, "tsconfig.json");
18711
20304
  const baseConfigFile = TSCONFIG_FILES[scope2];
18712
- const absoluteFiles = files.map((file) => isAbsolute10(file) ? file : join34(projectRoot, file));
20305
+ const absoluteFiles = files.map((file) => isAbsolute10(file) ? file : join36(projectRoot, file));
18713
20306
  const tempConfig = {
18714
- extends: join34(projectRoot, baseConfigFile),
20307
+ extends: join36(projectRoot, baseConfigFile),
18715
20308
  files: absoluteFiles,
18716
20309
  include: [],
18717
20310
  exclude: [],
@@ -18723,14 +20316,14 @@ async function createFileSpecificTsconfig(scope2, files, projectRoot, deps = def
18723
20316
  }
18724
20317
  async function createScopeFilteredTsconfig(scope2, projectRoot, scopeConfig, deps = defaultTypeScriptDeps) {
18725
20318
  const tempDir = await createTemporaryTsconfigDir(projectRoot, deps);
18726
- const configPath = join34(tempDir, "tsconfig.json");
20319
+ const configPath = join36(tempDir, "tsconfig.json");
18727
20320
  const baseConfigFile = TSCONFIG_FILES[scope2];
18728
20321
  const toTemporaryConfigPathPattern = (pattern) => {
18729
- const absolutePattern = isAbsolute10(pattern) ? pattern : join34(projectRoot, pattern);
20322
+ const absolutePattern = isAbsolute10(pattern) ? pattern : join36(projectRoot, pattern);
18730
20323
  return relative8(tempDir, absolutePattern);
18731
20324
  };
18732
20325
  const tempConfig = {
18733
- extends: join34(projectRoot, baseConfigFile),
20326
+ extends: join36(projectRoot, baseConfigFile),
18734
20327
  include: scopeConfigToTemporaryIncludes(scopeConfig).map(toTemporaryConfigPathPattern),
18735
20328
  exclude: scopeConfig.excludePatterns.map(toTemporaryConfigPathPattern),
18736
20329
  compilerOptions: TEMPORARY_TSCONFIG_COMPILER_OPTIONS
@@ -18805,7 +20398,7 @@ async function validateTypeScript(context, options = {}) {
18805
20398
  );
18806
20399
  }
18807
20400
  function resolveProjectTscInvocation(projectRoot, deps, tscArgs) {
18808
- const tscBin = join34(projectRoot, "node_modules", ".bin", "tsc");
20401
+ const tscBin = join36(projectRoot, "node_modules", ".bin", "tsc");
18809
20402
  const tool = deps.existsSync(tscBin) ? tscBin : "npx";
18810
20403
  return {
18811
20404
  tool,
@@ -18814,7 +20407,7 @@ function resolveProjectTscInvocation(projectRoot, deps, tscArgs) {
18814
20407
  }
18815
20408
  function runTypeScriptInvocation(projectRoot, invocation, runner, outputStreams, cleanup = () => {
18816
20409
  }) {
18817
- return new Promise((resolve12) => {
20410
+ return new Promise((resolve13) => {
18818
20411
  const tscProcess = spawnManagedSubprocess(runner, invocation.tool, invocation.args, {
18819
20412
  cwd: projectRoot
18820
20413
  });
@@ -18822,14 +20415,14 @@ function runTypeScriptInvocation(projectRoot, invocation, runner, outputStreams,
18822
20415
  tscProcess.on(VALIDATION_SUBPROCESS_EVENTS.CLOSE, (code) => {
18823
20416
  cleanup();
18824
20417
  if (code === 0) {
18825
- resolve12({ success: true, skipped: false });
20418
+ resolve13({ success: true, skipped: false });
18826
20419
  } else {
18827
- resolve12({ success: false, error: formatTypeScriptExitCodeError(code) });
20420
+ resolve13({ success: false, error: formatTypeScriptExitCodeError(code) });
18828
20421
  }
18829
20422
  });
18830
20423
  tscProcess.on(VALIDATION_SUBPROCESS_EVENTS.ERROR, (error) => {
18831
20424
  cleanup();
18832
- resolve12({ success: false, error: error.message });
20425
+ resolve13({ success: false, error: error.message });
18833
20426
  });
18834
20427
  });
18835
20428
  }
@@ -19266,9 +20859,9 @@ function addCommonOptions(cmd) {
19266
20859
  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");
19267
20860
  }
19268
20861
  function normalizeProductPathOperand(productDir, effectiveInvocationDir, operand) {
19269
- const resolvedProductDir = canonicalExistingPath(resolve11(productDir));
19270
- const resolvedInvocationDir = canonicalExistingPath(resolve11(effectiveInvocationDir));
19271
- const absoluteOperand = canonicalExistingPath(resolve11(resolvedInvocationDir, operand));
20862
+ const resolvedProductDir = canonicalExistingPath(resolve12(productDir));
20863
+ const resolvedInvocationDir = canonicalExistingPath(resolve12(effectiveInvocationDir));
20864
+ const absoluteOperand = canonicalExistingPath(resolve12(resolvedInvocationDir, operand));
19272
20865
  const relativeOperand = relative9(resolvedProductDir, absoluteOperand);
19273
20866
  if (relativeOperand === ".." || relativeOperand.startsWith(`..${sep4}`) || isAbsolute11(relativeOperand)) {
19274
20867
  return void 0;
@@ -19511,7 +21104,7 @@ function createVerificationContextDocument(payload) {
19511
21104
  import { dirname as dirname11 } from "path";
19512
21105
 
19513
21106
  // src/domains/verification-context/path.ts
19514
- import { join as join35 } from "path";
21107
+ import { join as join37 } from "path";
19515
21108
  var VERIFICATION_CONTEXT_STATE_DOMAIN = VERIFICATION_CONTEXT_PERSISTENCE.domain;
19516
21109
  var VERIFICATION_CONTEXT_STATE_PATH = {
19517
21110
  CONTEXTS_DIR: "contexts",
@@ -19532,7 +21125,7 @@ function verificationContextFilePath(scope2) {
19532
21125
  if (!contextsDir.ok) return contextsDir;
19533
21126
  const digest = validateScopeToken(scope2.digest);
19534
21127
  if (!digest.ok) return digest;
19535
- return { ok: true, value: join35(contextsDir.value, verificationContextFileName(digest.value)) };
21128
+ return { ok: true, value: join37(contextsDir.value, verificationContextFileName(digest.value)) };
19536
21129
  }
19537
21130
 
19538
21131
  // src/commands/verification-context/runtime.ts
@@ -19709,7 +21302,7 @@ import { randomBytes as randomBytes2 } from "crypto";
19709
21302
  import { dirname as dirname12 } from "path";
19710
21303
 
19711
21304
  // src/domains/verify/verify.ts
19712
- import { join as join36 } from "path";
21305
+ import { join as join38 } from "path";
19713
21306
  var VERIFY_SCOPE_TYPE = {
19714
21307
  CHANGESET: "changeset",
19715
21308
  WORKING_TREE: "working-tree"
@@ -19867,7 +21460,7 @@ function verifyInputRecordPath(scope2) {
19867
21460
  if (!token.ok) return token;
19868
21461
  return {
19869
21462
  ok: true,
19870
- value: join36(
21463
+ value: join38(
19871
21464
  runs.value,
19872
21465
  `${VERIFY_INPUT_RECORD.PREFIX}${token.value}${VERIFY_INPUT_RECORD.SUFFIX}`
19873
21466
  )
@@ -19884,12 +21477,12 @@ function parseAppendPayload(raw) {
19884
21477
  function isJsonRecord(value) {
19885
21478
  return typeof value === "object" && value !== null && !Array.isArray(value);
19886
21479
  }
19887
- function readRequiredString(record6, field) {
19888
- const value = record6[field];
21480
+ function readRequiredString(record7, field) {
21481
+ const value = record7[field];
19889
21482
  return typeof value === "string" && value.length > 0 ? value : void 0;
19890
21483
  }
19891
- function readRequiredStringValue(record6, field) {
19892
- const value = record6[field];
21484
+ function readRequiredStringValue(record7, field) {
21485
+ const value = record7[field];
19893
21486
  return typeof value === "string" ? value : void 0;
19894
21487
  }
19895
21488
  var OPTIONAL_FIELD_STATE = {
@@ -19897,26 +21490,26 @@ var OPTIONAL_FIELD_STATE = {
19897
21490
  INVALID: "invalid",
19898
21491
  PRESENT: "present"
19899
21492
  };
19900
- function readOptionalString2(record6, field) {
19901
- if (!(field in record6)) return { state: OPTIONAL_FIELD_STATE.ABSENT };
19902
- const value = record6[field];
21493
+ function readOptionalString2(record7, field) {
21494
+ if (!(field in record7)) return { state: OPTIONAL_FIELD_STATE.ABSENT };
21495
+ const value = record7[field];
19903
21496
  return typeof value === "string" && value.length > 0 ? { state: OPTIONAL_FIELD_STATE.PRESENT, value } : { state: OPTIONAL_FIELD_STATE.INVALID };
19904
21497
  }
19905
- function readOptionalPositiveInteger(record6, field) {
19906
- if (!(field in record6)) return { state: OPTIONAL_FIELD_STATE.ABSENT };
19907
- const value = record6[field];
21498
+ function readOptionalPositiveInteger(record7, field) {
21499
+ if (!(field in record7)) return { state: OPTIONAL_FIELD_STATE.ABSENT };
21500
+ const value = record7[field];
19908
21501
  return typeof value === "number" && Number.isInteger(value) && value > 0 ? { state: OPTIONAL_FIELD_STATE.PRESENT, value } : { state: OPTIONAL_FIELD_STATE.INVALID };
19909
21502
  }
19910
21503
  function optionalFieldValue(field) {
19911
21504
  return field.state === OPTIONAL_FIELD_STATE.PRESENT ? field.value : void 0;
19912
21505
  }
19913
- function readRequiredRecord(record6, field) {
19914
- const value = record6[field];
21506
+ function readRequiredRecord(record7, field) {
21507
+ const value = record7[field];
19915
21508
  return isJsonRecord(value) ? value : void 0;
19916
21509
  }
19917
- function readOptionalRecord(record6, field) {
19918
- if (!(field in record6)) return { state: OPTIONAL_FIELD_STATE.ABSENT };
19919
- const value = record6[field];
21510
+ function readOptionalRecord(record7, field) {
21511
+ if (!(field in record7)) return { state: OPTIONAL_FIELD_STATE.ABSENT };
21512
+ const value = record7[field];
19920
21513
  return isJsonRecord(value) ? { state: OPTIONAL_FIELD_STATE.PRESENT, value } : { state: OPTIONAL_FIELD_STATE.INVALID };
19921
21514
  }
19922
21515
  function hasInvalidOptionalField(...fields) {
@@ -20473,13 +22066,13 @@ async function resolveChangedScope(scope2, deps) {
20473
22066
  }
20474
22067
  return { ok: true, value: pathsFromNameStatus(diff.stdout) };
20475
22068
  }
20476
- async function persistInputRecord(runScope2, record6, deps) {
22069
+ async function persistInputRecord(runScope2, record7, deps) {
20477
22070
  const path7 = verifyInputRecordPath(runScope2);
20478
22071
  if (!path7.ok) return path7;
20479
22072
  const fs8 = deps.fs ?? defaultFileSystem;
20480
22073
  try {
20481
22074
  await fs8.mkdir(dirname12(path7.value), { recursive: true });
20482
- await writeFileAtomic(path7.value, JSON.stringify(record6), { fs: fs8, randomBytes: randomBytes2 });
22075
+ await writeFileAtomic(path7.value, JSON.stringify(record7), { fs: fs8, randomBytes: randomBytes2 });
20483
22076
  return { ok: true, value: void 0 };
20484
22077
  } catch (error) {
20485
22078
  return { ok: false, error: `${VERIFY_CLI_ERROR.INPUT_PERSIST_FAILED}: ${toMessage2(error)}` };
@@ -20527,8 +22120,8 @@ async function removeStartedRunArtifacts(artifacts, deps) {
20527
22120
  }
20528
22121
  function isStoredRecordedInput(value) {
20529
22122
  if (typeof value !== "object" || value === null) return false;
20530
- const record6 = value;
20531
- return typeof record6.scopeIdentity === "string" && typeof record6.scopeType === "string" && typeof record6.source === "string" && typeof record6.digest === "string" && typeof record6.content === "string";
22123
+ const record7 = value;
22124
+ return typeof record7.scopeIdentity === "string" && typeof record7.scopeType === "string" && typeof record7.source === "string" && typeof record7.digest === "string" && typeof record7.content === "string";
20532
22125
  }
20533
22126
  function parseRecordedInput(content) {
20534
22127
  try {
@@ -20669,11 +22262,11 @@ async function verifyStartCommand(options, deps) {
20669
22262
  async function verifyInputCommand(options, deps) {
20670
22263
  const run = await resolveExistingRun(options, deps);
20671
22264
  if (!run.ok) return errorResult3(run.error);
20672
- const record6 = run.value.recordedInput;
22265
+ const record7 = run.value.recordedInput;
20673
22266
  const report2 = {
20674
- source: record6.source,
20675
- digest: record6.digest,
20676
- content: record6.content
22267
+ source: record7.source,
22268
+ digest: record7.digest,
22269
+ content: record7.content
20677
22270
  };
20678
22271
  return okResult3(JSON.stringify(report2));
20679
22272
  }
@@ -20874,8 +22467,8 @@ function existingRunSelectorMismatch(run, options) {
20874
22467
  searchedTarget: run.inputRecordPath
20875
22468
  });
20876
22469
  }
20877
- function recordedSelectorMatches(record6, options) {
20878
- return record6.scopeType === options.scopeType && record6.scopeIdentity === options.scope;
22470
+ function recordedSelectorMatches(record7, options) {
22471
+ return record7.scopeType === options.scopeType && record7.scopeIdentity === options.scope;
20879
22472
  }
20880
22473
  async function readExistingRecordedInput(run, deps) {
20881
22474
  return readInputRecordAt(run.inputRecordPath, deps);
@@ -21331,17 +22924,17 @@ function renderStatus(records, format2, multiTargetRequest) {
21331
22924
  }
21332
22925
  return renderTextStatus(records);
21333
22926
  }
21334
- function toJsonStatusRecord(record6) {
21335
- const { worktree, status, pid, session, host } = record6;
22927
+ function toJsonStatusRecord(record7) {
22928
+ const { worktree, status, pid, session, host } = record7;
21336
22929
  return pid === void 0 ? { worktree, status } : { worktree, status, pid, session, host };
21337
22930
  }
21338
22931
  function renderTextStatus(records) {
21339
22932
  const sections = [];
21340
22933
  const sectionByParent = /* @__PURE__ */ new Map();
21341
- for (const record6 of records) {
21342
- const parent = renderParentDirectory(dirname13(record6.worktreeRoot));
22934
+ for (const record7 of records) {
22935
+ const parent = renderParentDirectory(dirname13(record7.worktreeRoot));
21343
22936
  const children = sectionByParent.get(parent);
21344
- const rendered = renderTextStatusChild(record6);
22937
+ const rendered = renderTextStatusChild(record7);
21345
22938
  if (children === void 0) {
21346
22939
  const newChildren = [rendered];
21347
22940
  sectionByParent.set(parent, newChildren);
@@ -21355,11 +22948,11 @@ function renderTextStatus(records) {
21355
22948
  function renderParentDirectory(parent) {
21356
22949
  return parent.endsWith(sep5) ? parent : `${parent}${sep5}`;
21357
22950
  }
21358
- function renderTextStatusChild(record6) {
21359
- if (record6.status === OCCUPANCY_STATUS.RUNNING) {
21360
- return `${basename8(record6.worktreeRoot)}: ${record6.runtime ?? WORKTREE_STATUS_RENDER.RUNNING_FALLBACK_RUNTIME} ${WORKTREE_STATUS_RENDER.RUNNING_WORD} [${record6.pid}]`;
22951
+ function renderTextStatusChild(record7) {
22952
+ if (record7.status === OCCUPANCY_STATUS.RUNNING) {
22953
+ return `${basename8(record7.worktreeRoot)}: ${record7.runtime ?? WORKTREE_STATUS_RENDER.RUNNING_FALLBACK_RUNTIME} ${WORKTREE_STATUS_RENDER.RUNNING_WORD} [${record7.pid}]`;
21361
22954
  }
21362
- return `${basename8(record6.worktreeRoot)}: ${WORKTREE_STATUS_RENDER.FREE}`;
22955
+ return `${basename8(record7.worktreeRoot)}: ${WORKTREE_STATUS_RENDER.FREE}`;
21363
22956
  }
21364
22957
 
21365
22958
  // src/lib/worktree-path-info.ts