@outcomeeng/spx 0.6.4 → 0.6.7

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
@@ -91,120 +91,394 @@ var DEFAULT_CLI_IO = {
91
91
  exit: (exitCode) => process.exit(exitCode)
92
92
  };
93
93
 
94
- // src/commands/claude/init.ts
95
- import { execa } from "execa";
96
- async function initCommand(options = {}) {
97
- const cwd = options.cwd || CONFIG_PROCESS_CWD.read();
98
- try {
99
- const { stdout: listOutput } = await execa(
100
- "claude",
101
- ["plugin", "marketplace", "list"],
102
- { cwd }
103
- );
104
- const exists = listOutput.includes("outcomeeng");
105
- if (!exists) {
106
- await execa(
107
- "claude",
108
- ["plugin", "marketplace", "add", "outcomeeng/plugins"],
109
- { cwd }
110
- );
111
- return "\u2713 outcomeeng marketplace installed successfully\n\nRun 'claude plugin marketplace list' to view all marketplaces.";
112
- } else {
113
- await execa("claude", ["plugin", "marketplace", "update", "outcomeeng"], {
114
- cwd
115
- });
116
- return "\u2713 outcomeeng marketplace updated successfully\n\nThe marketplace is now up to date.";
94
+ // src/interfaces/cli/agent.ts
95
+ import { inspect } from "util";
96
+
97
+ // src/commands/agent/resume.ts
98
+ import { readdir, readFile, stat } from "fs/promises";
99
+ import { homedir } from "os";
100
+
101
+ // src/domains/agent/protocol.ts
102
+ var AGENT_SESSION_KIND = {
103
+ CODEX: "codex",
104
+ CLAUDE_CODE: "claude-code"
105
+ };
106
+ var AGENT_SESSION_LABEL = {
107
+ [AGENT_SESSION_KIND.CODEX]: "Codex",
108
+ [AGENT_SESSION_KIND.CLAUDE_CODE]: "Claude Code"
109
+ };
110
+ var AGENT_RESUME_COMMAND = {
111
+ CODEX_BINARY: "codex",
112
+ CODEX_RESUME: "resume",
113
+ CLAUDE_BINARY: "claude",
114
+ CLAUDE_RESUME: "--resume"
115
+ };
116
+ var AGENT_SESSION_STORE = {
117
+ CODEX_DIR: ".codex",
118
+ CODEX_SESSIONS_DIR: "sessions",
119
+ CLAUDE_DIR: ".claude",
120
+ CLAUDE_PROJECTS_DIR: "projects",
121
+ JSONL_EXTENSION: ".jsonl",
122
+ TEXT_ENCODING: "utf8"
123
+ };
124
+ var AGENT_RESUME_LIMITS = {
125
+ RECENT_DAYS: 7,
126
+ DISPLAYED_CANDIDATES: 20,
127
+ HOURS_PER_DAY: 24,
128
+ MINUTES_PER_HOUR: 60,
129
+ SECONDS_PER_MINUTE: 60,
130
+ MILLISECONDS_PER_SECOND: 1e3,
131
+ ROOT_RESOLUTION_CONCURRENCY: 8
132
+ };
133
+ var AGENT_RESUME_MODE = {
134
+ PICK: "pick",
135
+ LATEST: "latest",
136
+ LIST: "list",
137
+ JSON: "json"
138
+ };
139
+ var AGENT_RESUME_TEXT = {
140
+ NO_MATCHES: "No matching agent sessions found.",
141
+ INTERACTIVE_REQUIRED: "agent resume requires an interactive terminal.",
142
+ MODE_CONFLICT: "Choose only one resume mode"
143
+ };
144
+ var AGENT_SESSION_JSON_FIELDS = {
145
+ TIMESTAMP: "timestamp",
146
+ TYPE: "type",
147
+ CWD: "cwd",
148
+ ORIGINATOR: "originator",
149
+ SOURCE: "source",
150
+ SESSION_ID: "session_id",
151
+ SESSION_ID_CAMEL: "sessionId",
152
+ ID: "id",
153
+ PAYLOAD: "payload",
154
+ GIT_BRANCH: "gitBranch",
155
+ THREAD_SOURCE: "thread_source"
156
+ };
157
+ var AGENT_SESSION_ROW_TYPE = {
158
+ CODEX_SESSION_META: "session_meta"
159
+ };
160
+ var CODEX_SESSION_ORIGINATOR = {
161
+ TUI: "codex-tui",
162
+ CLI: "codex_cli",
163
+ VSCODE: "codex_vscode",
164
+ VSCODE_HYPHEN: "codex-vscode",
165
+ EXEC: "codex-exec"
166
+ };
167
+ 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;
168
+
169
+ // src/domains/agent/resume.ts
170
+ import { resolve as resolve3 } from "path";
171
+ var AGENT_RESUME_PICKER_ACTION = {
172
+ MOVE_UP: "move-up",
173
+ MOVE_DOWN: "move-down",
174
+ CHOOSE: "choose",
175
+ QUIT: "quit",
176
+ IGNORE: "ignore"
177
+ };
178
+ var FIRST_PICKER_INDEX = 0;
179
+ var PICKER_MOVE_UP_DELTA = -1;
180
+ var PICKER_MOVE_DOWN_DELTA = 1;
181
+ var PICKER_QUIT_INPUT = "q";
182
+ var AgentResumeModeError = class extends Error {
183
+ constructor(selectedModes) {
184
+ super(`${AGENT_RESUME_TEXT.MODE_CONFLICT}: ${selectedModes.join(", ")}`);
185
+ this.selectedModes = selectedModes;
186
+ this.name = "AgentResumeModeError";
187
+ }
188
+ selectedModes;
189
+ };
190
+ function resolveAgentResumeMode(flags) {
191
+ const selected = [];
192
+ if (flags.latest === true) selected.push(AGENT_RESUME_MODE.LATEST);
193
+ if (flags.list === true) selected.push(AGENT_RESUME_MODE.LIST);
194
+ if (flags.json === true) selected.push(AGENT_RESUME_MODE.JSON);
195
+ if (selected.length > 1) {
196
+ throw new AgentResumeModeError(selected);
197
+ }
198
+ return selected[0] ?? AGENT_RESUME_MODE.PICK;
199
+ }
200
+ function initialAgentResumePickerState() {
201
+ return { selectedIndex: FIRST_PICKER_INDEX };
202
+ }
203
+ function resolveAgentResumePickerAction(input) {
204
+ if (input.upArrow) return AGENT_RESUME_PICKER_ACTION.MOVE_UP;
205
+ if (input.downArrow) return AGENT_RESUME_PICKER_ACTION.MOVE_DOWN;
206
+ if (input.return) return AGENT_RESUME_PICKER_ACTION.CHOOSE;
207
+ if (input.escape || input.input === PICKER_QUIT_INPUT) return AGENT_RESUME_PICKER_ACTION.QUIT;
208
+ return AGENT_RESUME_PICKER_ACTION.IGNORE;
209
+ }
210
+ function reduceAgentResumePickerState(state, action, candidateCount) {
211
+ if (action === AGENT_RESUME_PICKER_ACTION.MOVE_UP) {
212
+ return moveAgentResumePickerSelection(state, PICKER_MOVE_UP_DELTA, candidateCount);
213
+ }
214
+ if (action === AGENT_RESUME_PICKER_ACTION.MOVE_DOWN) {
215
+ return moveAgentResumePickerSelection(state, PICKER_MOVE_DOWN_DELTA, candidateCount);
216
+ }
217
+ return state;
218
+ }
219
+ function moveAgentResumePickerSelection(state, delta, candidateCount) {
220
+ const lastIndex = Math.max(candidateCount - 1, FIRST_PICKER_INDEX);
221
+ const nextIndex = Math.min(Math.max(state.selectedIndex + delta, FIRST_PICKER_INDEX), lastIndex);
222
+ return { selectedIndex: nextIndex };
223
+ }
224
+ function codexSessionStoreDir(homeDir) {
225
+ return resolve3(homeDir, AGENT_SESSION_STORE.CODEX_DIR, AGENT_SESSION_STORE.CODEX_SESSIONS_DIR);
226
+ }
227
+ function claudeCodeSessionStoreDir(homeDir) {
228
+ return resolve3(homeDir, AGENT_SESSION_STORE.CLAUDE_DIR, AGENT_SESSION_STORE.CLAUDE_PROJECTS_DIR);
229
+ }
230
+ async function discoverAgentResumeCandidates(options) {
231
+ const invocationRoot = await options.resolveWorktreeRoot(options.invocationDir);
232
+ if (invocationRoot === null) {
233
+ return [];
234
+ }
235
+ const drafts = [
236
+ ...await discoverCodexResumeCandidates(
237
+ codexSessionStoreDir(options.homeDir),
238
+ options.fs,
239
+ options.nowMs
240
+ ),
241
+ ...await discoverClaudeCodeResumeCandidates(
242
+ claudeCodeSessionStoreDir(options.homeDir),
243
+ options.fs,
244
+ options.nowMs
245
+ )
246
+ ];
247
+ const sortedDrafts = [...drafts];
248
+ sortedDrafts.sort(compareCandidates);
249
+ const rootResults = await mapWithConcurrency(
250
+ sortedDrafts,
251
+ AGENT_RESUME_LIMITS.ROOT_RESOLUTION_CONCURRENCY,
252
+ async (candidate) => ({
253
+ candidate,
254
+ root: await options.resolveWorktreeRoot(candidate.cwd)
255
+ })
256
+ );
257
+ return rootResults.filter((result) => result.root !== null && samePath(result.root, invocationRoot)).map((result) => result.candidate).slice(0, AGENT_RESUME_LIMITS.DISPLAYED_CANDIDATES);
258
+ }
259
+ function buildAgentResumeLaunchCommand(candidate) {
260
+ if (candidate.agent === AGENT_SESSION_KIND.CODEX) {
261
+ return {
262
+ command: AGENT_RESUME_COMMAND.CODEX_BINARY,
263
+ args: [AGENT_RESUME_COMMAND.CODEX_RESUME, candidate.sessionId],
264
+ cwd: candidate.cwd
265
+ };
266
+ }
267
+ return {
268
+ command: AGENT_RESUME_COMMAND.CLAUDE_BINARY,
269
+ args: [AGENT_RESUME_COMMAND.CLAUDE_RESUME, candidate.sessionId],
270
+ cwd: candidate.cwd
271
+ };
272
+ }
273
+ function renderAgentResumeList(candidates) {
274
+ if (candidates.length === 0) {
275
+ return AGENT_RESUME_TEXT.NO_MATCHES;
276
+ }
277
+ return candidates.map((candidate) => {
278
+ const updatedAt = candidate.updatedAt ?? new Date(candidate.modifiedAtMs).toISOString();
279
+ return `${updatedAt} ${AGENT_SESSION_LABEL[candidate.agent]} ${candidate.sessionId} ${candidate.cwd}`;
280
+ }).join("\n");
281
+ }
282
+ function renderAgentResumeJson(candidates) {
283
+ return JSON.stringify(candidates, null, 2);
284
+ }
285
+ async function discoverCodexResumeCandidates(root, fs8, nowMs) {
286
+ return discoverStoreCandidates(root, fs8, nowMs, parseCodexCandidateFile);
287
+ }
288
+ async function discoverClaudeCodeResumeCandidates(root, fs8, nowMs) {
289
+ return discoverStoreCandidates(root, fs8, nowMs, parseClaudeCodeCandidateFile);
290
+ }
291
+ async function discoverStoreCandidates(root, fs8, nowMs, parseCandidate) {
292
+ const files = await collectJsonlFiles(root, fs8);
293
+ const candidates = [];
294
+ for (const file of files) {
295
+ const stat7 = await fs8.stat(file).catch(() => null);
296
+ if (stat7 === null || !isRecentAgentSessionMtime(stat7.mtimeMs, nowMs)) {
297
+ continue;
117
298
  }
118
- } catch (error) {
119
- if (error instanceof Error) {
120
- if (error.message.includes("ENOENT") || error.message.includes("command not found")) {
121
- throw new Error(
122
- "Claude CLI not found. Please install Claude Code first.\n\nVisit: https://docs.anthropic.com/claude-code"
123
- );
124
- }
125
- throw new Error(`Failed to initialize marketplace: ${error.message}`);
299
+ const content = await fs8.readFile(file).catch(() => null);
300
+ if (content === null) {
301
+ continue;
302
+ }
303
+ const candidate = parseCandidate(file, content, stat7.mtimeMs);
304
+ if (candidate !== null) {
305
+ candidates.push(candidate);
126
306
  }
127
- throw error;
128
307
  }
308
+ return candidates;
129
309
  }
130
-
131
- // src/lib/claude/permissions/discovery.ts
132
- import fs from "fs/promises";
133
- import path from "path";
134
- async function findSettingsFiles(root, visited = /* @__PURE__ */ new Set()) {
135
- const normalizedRoot = path.resolve(root.replace(/^~/, process.env.HOME || "~"));
136
- if (visited.has(normalizedRoot)) {
137
- return [];
310
+ async function collectJsonlFiles(root, fs8) {
311
+ const entries = await fs8.readDir(root).catch(() => []);
312
+ const files = [];
313
+ for (const entry of entries) {
314
+ const child = resolve3(root, entry.name);
315
+ if (entry.isDirectory) {
316
+ files.push(...await collectJsonlFiles(child, fs8));
317
+ } else if (entry.isFile && entry.name.endsWith(AGENT_SESSION_STORE.JSONL_EXTENSION)) {
318
+ files.push(child);
319
+ }
320
+ }
321
+ return files;
322
+ }
323
+ function isRecentAgentSessionMtime(modifiedAtMs, nowMs) {
324
+ return modifiedAtMs <= nowMs && nowMs - modifiedAtMs <= AGENT_RESUME_RECENT_WINDOW_MS;
325
+ }
326
+ function parseCodexCandidateFile(sourcePath, content, modifiedAtMs) {
327
+ let sessionId = null;
328
+ let cwd = null;
329
+ let updatedAt = null;
330
+ let isInteractiveSession = false;
331
+ for (const line of content.split("\n")) {
332
+ const row = parseJsonObject(line);
333
+ if (row === null) {
334
+ continue;
335
+ }
336
+ isInteractiveSession = isCodexInteractiveSessionRow(row) || isInteractiveSession;
337
+ sessionId ??= firstString(row, [
338
+ [AGENT_SESSION_JSON_FIELDS.SESSION_ID],
339
+ [AGENT_SESSION_JSON_FIELDS.ID],
340
+ [AGENT_SESSION_JSON_FIELDS.PAYLOAD, AGENT_SESSION_JSON_FIELDS.SESSION_ID],
341
+ [AGENT_SESSION_JSON_FIELDS.PAYLOAD, AGENT_SESSION_JSON_FIELDS.ID]
342
+ ]);
343
+ cwd = firstString(row, [
344
+ [AGENT_SESSION_JSON_FIELDS.CWD],
345
+ [AGENT_SESSION_JSON_FIELDS.PAYLOAD, AGENT_SESSION_JSON_FIELDS.CWD]
346
+ ]) ?? cwd;
347
+ updatedAt = maxIsoTimestamp(updatedAt, firstString(row, [[AGENT_SESSION_JSON_FIELDS.TIMESTAMP]]));
348
+ }
349
+ if (!isInteractiveSession || sessionId === null || cwd === null) {
350
+ return null;
351
+ }
352
+ return {
353
+ agent: AGENT_SESSION_KIND.CODEX,
354
+ sessionId,
355
+ cwd,
356
+ sourcePath,
357
+ modifiedAtMs,
358
+ updatedAt,
359
+ branch: null
360
+ };
361
+ }
362
+ function isCodexInteractiveSessionRow(row) {
363
+ if (firstString(row, [[AGENT_SESSION_JSON_FIELDS.TYPE]]) !== AGENT_SESSION_ROW_TYPE.CODEX_SESSION_META) {
364
+ return false;
365
+ }
366
+ const originator = firstString(row, [
367
+ [AGENT_SESSION_JSON_FIELDS.ORIGINATOR],
368
+ [AGENT_SESSION_JSON_FIELDS.PAYLOAD, AGENT_SESSION_JSON_FIELDS.ORIGINATOR]
369
+ ]);
370
+ return originator === CODEX_SESSION_ORIGINATOR.TUI || originator === CODEX_SESSION_ORIGINATOR.CLI || originator === CODEX_SESSION_ORIGINATOR.VSCODE || originator === CODEX_SESSION_ORIGINATOR.VSCODE_HYPHEN;
371
+ }
372
+ function parseClaudeCodeCandidateFile(sourcePath, content, modifiedAtMs) {
373
+ let sessionId = null;
374
+ let cwd = null;
375
+ let updatedAt = null;
376
+ let branch = null;
377
+ for (const line of content.split("\n")) {
378
+ const row = parseJsonObject(line);
379
+ if (row === null) {
380
+ continue;
381
+ }
382
+ sessionId ??= firstString(row, [
383
+ [AGENT_SESSION_JSON_FIELDS.SESSION_ID_CAMEL],
384
+ [AGENT_SESSION_JSON_FIELDS.SESSION_ID],
385
+ [AGENT_SESSION_JSON_FIELDS.PAYLOAD, AGENT_SESSION_JSON_FIELDS.SESSION_ID_CAMEL],
386
+ [AGENT_SESSION_JSON_FIELDS.PAYLOAD, AGENT_SESSION_JSON_FIELDS.SESSION_ID]
387
+ ]);
388
+ cwd = firstString(row, [
389
+ [AGENT_SESSION_JSON_FIELDS.CWD],
390
+ [AGENT_SESSION_JSON_FIELDS.PAYLOAD, AGENT_SESSION_JSON_FIELDS.CWD]
391
+ ]) ?? cwd;
392
+ updatedAt = maxIsoTimestamp(updatedAt, firstString(row, [[AGENT_SESSION_JSON_FIELDS.TIMESTAMP]]));
393
+ branch = firstString(row, [[AGENT_SESSION_JSON_FIELDS.GIT_BRANCH]]) ?? branch;
394
+ }
395
+ if (sessionId === null || cwd === null) {
396
+ return null;
397
+ }
398
+ return {
399
+ agent: AGENT_SESSION_KIND.CLAUDE_CODE,
400
+ sessionId,
401
+ cwd,
402
+ sourcePath,
403
+ modifiedAtMs,
404
+ updatedAt,
405
+ branch
406
+ };
407
+ }
408
+ function parseJsonObject(line) {
409
+ const trimmed = line.trim();
410
+ if (trimmed.length === 0) {
411
+ return null;
138
412
  }
139
- visited.add(normalizedRoot);
140
413
  try {
141
- return await findSettingsFilesInDirectory(normalizedRoot, visited);
142
- } catch (error) {
143
- throw discoveryError(normalizedRoot, error);
414
+ const parsed = JSON.parse(trimmed);
415
+ return isRecord(parsed) ? parsed : null;
416
+ } catch {
417
+ return null;
144
418
  }
145
419
  }
146
- async function findSettingsFilesInDirectory(normalizedRoot, visited) {
147
- const stats = await fs.stat(normalizedRoot);
148
- if (!stats.isDirectory()) {
149
- throw new Error(`Path is not a directory: ${normalizedRoot}`);
420
+ function firstString(row, paths) {
421
+ for (const path7 of paths) {
422
+ const value = valueAtPath(row, path7);
423
+ if (typeof value === "string" && value.length > 0) {
424
+ return value;
425
+ }
150
426
  }
151
- const entries = await fs.readdir(normalizedRoot, { withFileTypes: true });
152
- const results = [];
153
- for (const entry of entries) {
154
- results.push(...await settingsFilesForEntry(normalizedRoot, entry, visited));
427
+ return null;
428
+ }
429
+ function valueAtPath(row, path7) {
430
+ let current = row;
431
+ for (const segment of path7) {
432
+ if (!isRecord(current)) {
433
+ return void 0;
434
+ }
435
+ current = current[segment];
155
436
  }
156
- return results;
437
+ return current;
157
438
  }
158
- async function settingsFilesForEntry(normalizedRoot, entry, visited) {
159
- if (!entry.isDirectory()) return [];
160
- const fullPath = path.join(normalizedRoot, entry.name);
161
- if (entry.name === ".claude") {
162
- const settingsPath = path.join(fullPath, "settings.local.json");
163
- return await isValidSettingsFile(settingsPath) ? [settingsPath] : [];
439
+ function isRecord(value) {
440
+ return typeof value === "object" && value !== null && !Array.isArray(value);
441
+ }
442
+ function maxIsoTimestamp(left, right) {
443
+ if (right === null) {
444
+ return left;
164
445
  }
165
- return findSettingsFiles(fullPath, visited);
446
+ if (left === null) {
447
+ return right;
448
+ }
449
+ return Date.parse(right) > Date.parse(left) ? right : left;
166
450
  }
167
- function discoveryError(normalizedRoot, error) {
168
- if (!(error instanceof Error)) return new Error(unknownErrorMessage(error));
169
- if (error.message.includes("ENOENT")) return new Error(`Directory not found: ${normalizedRoot}`);
170
- if (error.message.includes("EACCES")) return new Error(`Permission denied: ${normalizedRoot}`);
171
- return new Error(`Failed to search directory "${normalizedRoot}": ${error.message}`);
451
+ function compareCandidates(left, right) {
452
+ const modifiedDiff = right.modifiedAtMs - left.modifiedAtMs;
453
+ if (modifiedDiff !== 0) {
454
+ return modifiedDiff;
455
+ }
456
+ return `${left.agent}:${left.sessionId}`.localeCompare(`${right.agent}:${right.sessionId}`);
172
457
  }
173
- function unknownErrorMessage(error) {
174
- if (typeof error === "string") return error;
175
- if (typeof error === "number" || typeof error === "boolean" || typeof error === "bigint") return error.toString();
176
- if (typeof error === "symbol") return error.description ?? "Symbol()";
177
- if (error === void 0) return "undefined";
178
- return Object.prototype.toString.call(error);
458
+ function samePath(left, right) {
459
+ return resolve3(left) === resolve3(right);
179
460
  }
180
- async function isValidSettingsFile(filePath) {
181
- try {
182
- await fs.access(filePath, fs.constants.R_OK);
183
- const stats = await fs.stat(filePath);
184
- if (!stats.isFile()) {
185
- return false;
461
+ async function mapWithConcurrency(items, concurrency, mapper) {
462
+ const results = new Array(items.length);
463
+ let nextIndex = 0;
464
+ async function worker() {
465
+ for (; ; ) {
466
+ const index = nextIndex;
467
+ nextIndex += 1;
468
+ if (index >= items.length) {
469
+ return;
470
+ }
471
+ results[index] = await mapper(items[index]);
186
472
  }
187
- return path.extname(filePath) === ".json";
188
- } catch {
189
- return false;
190
473
  }
474
+ const workerCount = Math.min(Math.max(1, concurrency), items.length);
475
+ await Promise.all(Array.from({ length: workerCount }, () => worker()));
476
+ return results;
191
477
  }
192
478
 
193
- // src/lib/state-store/index.ts
194
- import { createHash, randomBytes as nodeRandomBytes } from "crypto";
195
- import { constants as fsConstants } from "fs";
196
- import {
197
- lstat as nodeLstat,
198
- mkdir as nodeMkdir,
199
- open as nodeOpen,
200
- readdir as nodeReaddir,
201
- rm as nodeRm
202
- } from "fs/promises";
203
- import { dirname as dirname2, join as join2 } from "path";
204
-
205
479
  // src/git/root.ts
206
- import { execa as execa2 } from "execa";
207
- import { basename, dirname, isAbsolute, join, resolve as resolve3 } from "path";
480
+ import { execa } from "execa";
481
+ import { basename, dirname, isAbsolute, join, resolve as resolve4 } from "path";
208
482
 
209
483
  // src/git/environment.ts
210
484
  function withoutGitEnvironment(env) {
@@ -220,7 +494,7 @@ function withoutGitEnvironment(env) {
220
494
  // src/git/root.ts
221
495
  var defaultGitDependencies = {
222
496
  execa: async (command, args, options) => {
223
- const result = await execa2(command, args, {
497
+ const result = await execa(command, args, {
224
498
  ...options,
225
499
  env: withoutGitEnvironment(process.env),
226
500
  extendEnv: false
@@ -257,7 +531,8 @@ var GIT_ROOT_COMMAND = {
257
531
  CORE_BARE_KEY: "core.bare",
258
532
  REMOTE: "remote",
259
533
  GET_URL: "get-url",
260
- ORIGIN: "origin"
534
+ ORIGIN: "origin",
535
+ UNTRACKED_FILES_ALL: "--untracked-files=all"
261
536
  };
262
537
  var GIT_URL_SUFFIX = ".git";
263
538
  var GIT_DIR_BASENAME = ".git";
@@ -300,7 +575,8 @@ var GIT_ORIGIN_HEAD_REF_ARGS = [
300
575
  ];
301
576
  var GIT_STATUS_PORCELAIN_ARGS = [
302
577
  GIT_ROOT_COMMAND.STATUS,
303
- GIT_ROOT_COMMAND.PORCELAIN
578
+ GIT_ROOT_COMMAND.PORCELAIN,
579
+ GIT_ROOT_COMMAND.UNTRACKED_FILES_ALL
304
580
  ];
305
581
  var GIT_WORKTREE_LIST_PORCELAIN_ARGS = [
306
582
  GIT_ROOT_COMMAND.WORKTREE,
@@ -311,7 +587,8 @@ var GIT_WORKTREE_PORCELAIN_ROOT_PREFIX = "worktree ";
311
587
  var GIT_WORKTREE_PORCELAIN_BARE_LINE = "bare";
312
588
  var GIT_WORKTREE_PORCELAIN_PRUNABLE_LINE = "prunable";
313
589
  var GIT_WORKTREE_PORCELAIN_PRUNABLE_PREFIX = `${GIT_WORKTREE_PORCELAIN_PRUNABLE_LINE} `;
314
- var TRAILING_PATH_SEPARATORS_PATTERN = /[\\/]+$/;
590
+ var POSIX_PATH_SEPARATOR = "/";
591
+ var WINDOWS_PATH_SEPARATOR = "\\";
315
592
  async function detectWorktreeProductRoot(cwd = CONFIG_PROCESS_CWD.read(), deps = defaultGitDependencies) {
316
593
  try {
317
594
  const result = await deps.execa(
@@ -338,15 +615,20 @@ async function detectWorktreeProductRoot(cwd = CONFIG_PROCESS_CWD.read(), deps =
338
615
  };
339
616
  }
340
617
  }
618
+ function stripTrailingPathSeparators(value) {
619
+ let end = value.length;
620
+ while (end > 0 && (value[end - 1] === POSIX_PATH_SEPARATOR || value[end - 1] === WINDOWS_PATH_SEPARATOR)) {
621
+ end -= 1;
622
+ }
623
+ return value.slice(0, end);
624
+ }
341
625
  function extractStdout(stdout) {
342
626
  if (!stdout) return "";
343
- const str = typeof stdout === "string" ? stdout : String(stdout);
344
- return str.trim().replace(TRAILING_PATH_SEPARATORS_PATTERN, "");
627
+ return stripTrailingPathSeparators(stdout.trim());
345
628
  }
346
629
  function trimStdout(stdout) {
347
630
  if (!stdout) return "";
348
- const str = typeof stdout === "string" ? stdout : String(stdout);
349
- return str.trim();
631
+ return stdout.trim();
350
632
  }
351
633
  async function detectGitCommonDirProductRoot(cwd = CONFIG_PROCESS_CWD.read(), deps = defaultGitDependencies) {
352
634
  try {
@@ -377,7 +659,7 @@ async function detectGitCommonDirProductRoot(cwd = CONFIG_PROCESS_CWD.read(), de
377
659
  };
378
660
  }
379
661
  const commonDir = extractStdout(commonDirResult.stdout);
380
- const absoluteCommonDir = isAbsolute(commonDir) ? commonDir : resolve3(toplevel, commonDir);
662
+ const absoluteCommonDir = isAbsolute(commonDir) ? commonDir : resolve4(toplevel, commonDir);
381
663
  const gitCommonDirProductRoot = dirname(absoluteCommonDir);
382
664
  return {
383
665
  productDir: gitCommonDirProductRoot,
@@ -461,7 +743,7 @@ async function isWorkingTreeClean(cwd = CONFIG_PROCESS_CWD.read(), deps = defaul
461
743
  }
462
744
  function repositoryName(originUrl) {
463
745
  if (originUrl === null) return null;
464
- const trimmed = originUrl.trim().replace(TRAILING_PATH_SEPARATORS_PATTERN, "");
746
+ const trimmed = stripTrailingPathSeparators(originUrl.trim());
465
747
  const lastSeparator = Math.max(
466
748
  trimmed.lastIndexOf("/"),
467
749
  trimmed.lastIndexOf("\\"),
@@ -472,10 +754,10 @@ function repositoryName(originUrl) {
472
754
  return name.length === 0 ? null : name;
473
755
  }
474
756
  function normalizeGitPath(path7) {
475
- return path7.trim().replace(TRAILING_PATH_SEPARATORS_PATTERN, "");
757
+ return stripTrailingPathSeparators(path7.trim());
476
758
  }
477
759
  function normalizedGitPathKey(path7) {
478
- return normalizeGitPath(path7).replace(/\\/g, "/");
760
+ return normalizeGitPath(path7).replaceAll(WINDOWS_PATH_SEPARATOR, POSIX_PATH_SEPARATOR);
479
761
  }
480
762
  function isObservedWorktreeRoot(worktreeRoots, candidate) {
481
763
  const candidateKey = normalizedGitPathKey(candidate);
@@ -539,21 +821,561 @@ async function gatherGitFacts(cwd = CONFIG_PROCESS_CWD.read(), deps = defaultGit
539
821
  originUrl
540
822
  };
541
823
  }
542
- const rawCommonDir = extractStdout(commonDirResult.stdout);
543
- const commonDir = isAbsolute(rawCommonDir) ? rawCommonDir : resolve3(worktreeRoot, rawCommonDir);
544
- const bareResult = await deps.execa(
545
- GIT_ROOT_COMMAND.EXECUTABLE,
546
- [...GIT_CORE_BARE_ARGS],
547
- { cwd, reject: false }
548
- );
549
- const commonDirIsBare = bareResult.exitCode === 0 && extractStdout(bareResult.stdout) === GIT_CORE_BARE_TRUE;
550
- return { worktreeRoot, worktreeRoots, worktreeListRead, commonDir, commonDirIsBare, originUrl };
824
+ const rawCommonDir = extractStdout(commonDirResult.stdout);
825
+ const commonDir = isAbsolute(rawCommonDir) ? rawCommonDir : resolve4(worktreeRoot, rawCommonDir);
826
+ const bareResult = await deps.execa(
827
+ GIT_ROOT_COMMAND.EXECUTABLE,
828
+ [...GIT_CORE_BARE_ARGS],
829
+ { cwd, reject: false }
830
+ );
831
+ const commonDirIsBare = bareResult.exitCode === 0 && extractStdout(bareResult.stdout) === GIT_CORE_BARE_TRUE;
832
+ return { worktreeRoot, worktreeRoots, worktreeListRead, commonDir, commonDirIsBare, originUrl };
833
+ } catch {
834
+ return null;
835
+ }
836
+ }
837
+
838
+ // src/commands/agent/resume.ts
839
+ var nodeAgentSessionFileSystem = {
840
+ async readDir(path7) {
841
+ const entries = await readdir(path7, { withFileTypes: true });
842
+ return entries.map((entry) => ({
843
+ name: entry.name,
844
+ isDirectory: entry.isDirectory(),
845
+ isFile: entry.isFile()
846
+ }));
847
+ },
848
+ async readFile(path7) {
849
+ return readFile(path7, AGENT_SESSION_STORE.TEXT_ENCODING);
850
+ },
851
+ async stat(path7) {
852
+ const result = await stat(path7);
853
+ return { mtimeMs: result.mtimeMs };
854
+ }
855
+ };
856
+ var defaultAgentResumeCommandDeps = {
857
+ fs: nodeAgentSessionFileSystem,
858
+ homeDir: homedir,
859
+ nowMs: Date.now,
860
+ resolveWorktreeRoot: async (cwd) => {
861
+ const result = await detectWorktreeProductRoot(cwd);
862
+ return result.isGitRepo ? result.productDir : null;
863
+ }
864
+ };
865
+ async function loadAgentResumeCandidates(options) {
866
+ const deps = options.deps ?? defaultAgentResumeCommandDeps;
867
+ return discoverAgentResumeCandidates({
868
+ invocationDir: options.cwd,
869
+ homeDir: deps.homeDir(),
870
+ nowMs: deps.nowMs(),
871
+ fs: deps.fs,
872
+ resolveWorktreeRoot: deps.resolveWorktreeRoot
873
+ });
874
+ }
875
+ async function listAgentResumeSessions(options) {
876
+ return renderAgentResumeList(await loadAgentResumeCandidates(options));
877
+ }
878
+ async function jsonAgentResumeSessions(options) {
879
+ return renderAgentResumeJson(await loadAgentResumeCandidates(options));
880
+ }
881
+
882
+ // src/lib/process-lifecycle/exit-codes.ts
883
+ var SIGINT_EXIT_CODE = 130;
884
+ var SIGTERM_EXIT_CODE = 143;
885
+ var EPIPE_EXIT_CODE = 0;
886
+ var UNCAUGHT_EXIT_CODE = 1;
887
+
888
+ // src/lib/process-lifecycle/foreground-handoff.ts
889
+ var FOREGROUND_SIGNALS = ["SIGINT", "SIGTERM"];
890
+ function createSignalSuspender(target) {
891
+ return {
892
+ suspend() {
893
+ const ignore = () => {
894
+ };
895
+ const restorers = FOREGROUND_SIGNALS.map((signal) => {
896
+ const originals = target.listeners(signal);
897
+ for (const listener of originals) target.removeListener(signal, listener);
898
+ target.on(signal, ignore);
899
+ return () => {
900
+ target.removeListener(signal, ignore);
901
+ for (const listener of originals) target.on(signal, listener);
902
+ };
903
+ });
904
+ return () => {
905
+ for (const restore of restorers) restore();
906
+ };
907
+ }
908
+ };
909
+ }
910
+
911
+ // src/lib/process-lifecycle/handlers.ts
912
+ var SIGINT_NAME = "SIGINT";
913
+ var SIGTERM_NAME = "SIGTERM";
914
+ function createHandlers(deps) {
915
+ let cleanupOnce = false;
916
+ function killEachChild(signal) {
917
+ deps.registry.forEach((child) => {
918
+ if (!child.killed) child.kill(signal);
919
+ });
920
+ }
921
+ function exitOnce(code, killSignal) {
922
+ if (cleanupOnce) {
923
+ deps.exitController.exit(code);
924
+ return;
925
+ }
926
+ cleanupOnce = true;
927
+ if (killSignal !== void 0) killEachChild(killSignal);
928
+ deps.exitController.exit(code);
929
+ }
930
+ return {
931
+ onSigint() {
932
+ exitOnce(SIGINT_EXIT_CODE, SIGINT_NAME);
933
+ },
934
+ onSigterm() {
935
+ exitOnce(SIGTERM_EXIT_CODE, SIGTERM_NAME);
936
+ },
937
+ onEpipe() {
938
+ exitOnce(EPIPE_EXIT_CODE, SIGTERM_NAME);
939
+ },
940
+ onUncaught(_error) {
941
+ exitOnce(UNCAUGHT_EXIT_CODE, SIGTERM_NAME);
942
+ }
943
+ };
944
+ }
945
+
946
+ // src/lib/process-lifecycle/install.ts
947
+ import { spawn } from "child_process";
948
+
949
+ // src/lib/process-lifecycle/registry.ts
950
+ function createRegistry() {
951
+ const tracked = /* @__PURE__ */ new Set();
952
+ return {
953
+ add(child) {
954
+ tracked.add(child);
955
+ },
956
+ remove(child) {
957
+ tracked.delete(child);
958
+ },
959
+ forEach(fn) {
960
+ for (const child of tracked) fn(child);
961
+ },
962
+ get size() {
963
+ return tracked.size;
964
+ }
965
+ };
966
+ }
967
+
968
+ // src/lib/process-lifecycle/runner.ts
969
+ function createLifecycleRunner(deps) {
970
+ return {
971
+ spawn(command, args, options) {
972
+ const child = deps.spawn(command, args, options);
973
+ deps.registry.add(child);
974
+ child.on("exit", () => deps.registry.remove(child));
975
+ return child;
976
+ }
977
+ };
978
+ }
979
+
980
+ // src/lib/process-lifecycle/install.ts
981
+ var moduleRegistry = createRegistry();
982
+ var moduleExitController = {
983
+ exit(code) {
984
+ process.exit(code);
985
+ }
986
+ };
987
+ var installed = false;
988
+ var lifecycleProcessRunner = createLifecycleRunner({
989
+ registry: moduleRegistry,
990
+ spawn
991
+ });
992
+ var foregroundProcessRunner = { spawn };
993
+ var processSignalTarget = {
994
+ listeners: (signal) => process.listeners(signal),
995
+ on: (signal, listener) => {
996
+ process.on(signal, listener);
997
+ },
998
+ removeListener: (signal, listener) => {
999
+ process.removeListener(signal, listener);
1000
+ }
1001
+ };
1002
+ var lifecycleSignalSuspender = createSignalSuspender(processSignalTarget);
1003
+ var EPIPE_CODE = "EPIPE";
1004
+ var UNCAUGHT_PREFIX = "Uncaught: ";
1005
+ var NEWLINE = "\n";
1006
+ function formatUncaught(error) {
1007
+ if (error instanceof Error && error.stack !== void 0) {
1008
+ return UNCAUGHT_PREFIX + error.stack + NEWLINE;
1009
+ }
1010
+ return UNCAUGHT_PREFIX + String(error) + NEWLINE;
1011
+ }
1012
+ function logUncaughtToStderr(error) {
1013
+ try {
1014
+ process.stderr.write(formatUncaught(error));
1015
+ } catch {
1016
+ }
1017
+ }
1018
+ function installLifecycle() {
1019
+ if (installed) return;
1020
+ installed = true;
1021
+ const handlers = createHandlers({
1022
+ registry: moduleRegistry,
1023
+ exitController: moduleExitController
1024
+ });
1025
+ process.on("uncaughtException", (error) => {
1026
+ logUncaughtToStderr(error);
1027
+ handlers.onUncaught(error);
1028
+ });
1029
+ process.on("unhandledRejection", (reason) => {
1030
+ logUncaughtToStderr(reason);
1031
+ handlers.onUncaught(reason);
1032
+ });
1033
+ process.on("SIGTERM", () => handlers.onSigterm());
1034
+ process.on("SIGINT", () => handlers.onSigint());
1035
+ process.stdout.on("error", (error) => {
1036
+ if (error.code === EPIPE_CODE) {
1037
+ handlers.onEpipe();
1038
+ return;
1039
+ }
1040
+ handlers.onUncaught(error);
1041
+ });
1042
+ process.stderr.on("error", (error) => {
1043
+ if (error.code === EPIPE_CODE) {
1044
+ handlers.onEpipe();
1045
+ return;
1046
+ }
1047
+ handlers.onUncaught(error);
1048
+ });
1049
+ }
1050
+
1051
+ // src/lib/process-lifecycle/managed-subprocess.ts
1052
+ var MANAGED_SUBPROCESS_STDIO = "pipe";
1053
+ function spawnManagedSubprocess(runner, command, args, options) {
1054
+ return runner.spawn(command, args, {
1055
+ ...options,
1056
+ stdio: MANAGED_SUBPROCESS_STDIO
1057
+ });
1058
+ }
1059
+
1060
+ // src/interfaces/cli/foreground-launch.ts
1061
+ var FOREGROUND_LAUNCH_STDIO = "inherit";
1062
+ var LAUNCH_FAILURE_STATUS = 1;
1063
+ function launchForegroundCommand(runner, suspender, command) {
1064
+ const restoreSignals = suspender.suspend();
1065
+ return new Promise((resolve12) => {
1066
+ let settled = false;
1067
+ const settle = (status) => {
1068
+ if (settled) return;
1069
+ settled = true;
1070
+ restoreSignals();
1071
+ resolve12(status);
1072
+ };
1073
+ const child = runner.spawn(command.command, command.args, {
1074
+ cwd: command.cwd,
1075
+ stdio: FOREGROUND_LAUNCH_STDIO
1076
+ });
1077
+ child.once("exit", (code) => settle(code ?? LAUNCH_FAILURE_STATUS));
1078
+ child.once("error", () => settle(LAUNCH_FAILURE_STATUS));
1079
+ });
1080
+ }
1081
+
1082
+ // src/interfaces/cli/agent/resume/launch-agent-resume.ts
1083
+ function launchAgentResume(runner, suspender, command) {
1084
+ return launchForegroundCommand(runner, suspender, command);
1085
+ }
1086
+
1087
+ // src/interfaces/cli/agent/resume/run-picker.tsx
1088
+ import { render } from "ink";
1089
+
1090
+ // src/interfaces/cli/agent/resume/AgentResumePicker.tsx
1091
+ import { Box, Text, useInput } from "ink";
1092
+ import { useState } from "react";
1093
+ import { jsx, jsxs } from "react/jsx-runtime";
1094
+ var SELECTED_COLOR = "cyan";
1095
+ var SELECTED_MARKER = ">";
1096
+ var UNSELECTED_MARKER = " ";
1097
+ function AgentResumePicker(props) {
1098
+ const [state, setState] = useState(initialAgentResumePickerState);
1099
+ useInput((input, key) => {
1100
+ const action = resolveAgentResumePickerAction({
1101
+ input,
1102
+ upArrow: key.upArrow,
1103
+ downArrow: key.downArrow,
1104
+ return: key.return,
1105
+ escape: key.escape
1106
+ });
1107
+ if (action === AGENT_RESUME_PICKER_ACTION.CHOOSE) {
1108
+ const selected = props.candidates.at(state.selectedIndex);
1109
+ if (selected !== void 0) {
1110
+ props.onChoose(selected);
1111
+ }
1112
+ return;
1113
+ }
1114
+ if (action === AGENT_RESUME_PICKER_ACTION.QUIT) {
1115
+ props.onQuit();
1116
+ return;
1117
+ }
1118
+ setState((current) => reduceAgentResumePickerState(current, action, props.candidates.length));
1119
+ });
1120
+ if (props.candidates.length === 0) {
1121
+ return /* @__PURE__ */ jsx(Text, { children: AGENT_RESUME_TEXT.NO_MATCHES });
1122
+ }
1123
+ return /* @__PURE__ */ jsx(Box, { flexDirection: "column", children: props.candidates.map((candidate, index) => /* @__PURE__ */ jsxs(
1124
+ Text,
1125
+ {
1126
+ color: index === state.selectedIndex ? SELECTED_COLOR : void 0,
1127
+ children: [
1128
+ index === state.selectedIndex ? SELECTED_MARKER : UNSELECTED_MARKER,
1129
+ " ",
1130
+ AGENT_SESSION_LABEL[candidate.agent],
1131
+ " ",
1132
+ candidate.sessionId,
1133
+ " ",
1134
+ candidate.cwd
1135
+ ]
1136
+ },
1137
+ `${candidate.agent}:${candidate.sessionId}`
1138
+ )) });
1139
+ }
1140
+
1141
+ // src/interfaces/cli/agent/resume/run-picker.tsx
1142
+ import { jsx as jsx2 } from "react/jsx-runtime";
1143
+ var AGENT_RESUME_PICKER_RESULT = {
1144
+ SELECTED: "selected",
1145
+ QUIT: "quit"
1146
+ };
1147
+ function selectedAgentResumeCandidate(candidate) {
1148
+ return { kind: AGENT_RESUME_PICKER_RESULT.SELECTED, candidate };
1149
+ }
1150
+ function quitAgentResumePicker() {
1151
+ return { kind: AGENT_RESUME_PICKER_RESULT.QUIT };
1152
+ }
1153
+ async function runAgentResumePicker(candidates, renderPicker = render) {
1154
+ let result = quitAgentResumePicker();
1155
+ let unmount = () => {
1156
+ };
1157
+ const instance = renderPicker(
1158
+ /* @__PURE__ */ jsx2(
1159
+ AgentResumePicker,
1160
+ {
1161
+ candidates,
1162
+ onChoose: (candidate) => {
1163
+ result = selectedAgentResumeCandidate(candidate);
1164
+ unmount();
1165
+ },
1166
+ onQuit: () => {
1167
+ result = quitAgentResumePicker();
1168
+ unmount();
1169
+ }
1170
+ }
1171
+ )
1172
+ );
1173
+ unmount = instance.unmount;
1174
+ await instance.waitUntilExit();
1175
+ return result;
1176
+ }
1177
+
1178
+ // src/interfaces/cli/agent.ts
1179
+ var AGENT_CLI = {
1180
+ commandName: "agent",
1181
+ resumeCommandName: "resume",
1182
+ flags: {
1183
+ latest: "--latest",
1184
+ list: "--list",
1185
+ json: "--json"
1186
+ }
1187
+ };
1188
+ var AGENT_CLI_EXIT = {
1189
+ SUCCESS: 0,
1190
+ FAILURE: 1
1191
+ };
1192
+ var DEFAULT_AGENT_CLI_DEPENDENCIES = {
1193
+ isInteractiveTerminal: () => Boolean(process.stdin.isTTY) && Boolean(process.stdout.isTTY),
1194
+ pickCandidate: runAgentResumePicker,
1195
+ launchCandidate: async (candidate) => {
1196
+ return launchAgentResume(
1197
+ foregroundProcessRunner,
1198
+ lifecycleSignalSuspender,
1199
+ buildAgentResumeLaunchCommand(candidate)
1200
+ );
1201
+ }
1202
+ };
1203
+ function writeOutput(invocation, output) {
1204
+ invocation.io.writeStdout(`${output}
1205
+ `);
1206
+ }
1207
+ function writeError(invocation, output) {
1208
+ invocation.io.writeStderr(`${output}
1209
+ `);
1210
+ }
1211
+ function handleError(invocation, error) {
1212
+ const message = error instanceof Error ? `${error.name}: ${error.message}` : inspect(error);
1213
+ writeError(invocation, `Error: ${message}`);
1214
+ return invocation.io.exit(AGENT_CLI_EXIT.FAILURE);
1215
+ }
1216
+ function createAgentDomain(deps = {}) {
1217
+ const resolvedDeps = {
1218
+ ...DEFAULT_AGENT_CLI_DEPENDENCIES,
1219
+ ...deps
1220
+ };
1221
+ return {
1222
+ name: AGENT_CLI.commandName,
1223
+ description: "Find and resume coding-agent sessions",
1224
+ register: (program, invocation) => {
1225
+ const agentCmd = program.command(AGENT_CLI.commandName).description("Find and resume coding-agent sessions");
1226
+ 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").action(async (options) => {
1227
+ let requestedExitCode = AGENT_CLI_EXIT.SUCCESS;
1228
+ try {
1229
+ const mode = resolveAgentResumeMode(options);
1230
+ if (mode === AGENT_RESUME_MODE.PICK && !resolvedDeps.isInteractiveTerminal()) {
1231
+ writeError(invocation, AGENT_RESUME_TEXT.INTERACTIVE_REQUIRED);
1232
+ requestedExitCode = AGENT_CLI_EXIT.FAILURE;
1233
+ } else {
1234
+ const commandOptions = {
1235
+ cwd: invocation.resolveEffectiveInvocationDir(),
1236
+ deps: resolvedDeps.resumeDeps
1237
+ };
1238
+ if (mode === AGENT_RESUME_MODE.JSON) {
1239
+ writeOutput(invocation, await jsonAgentResumeSessions(commandOptions));
1240
+ return;
1241
+ }
1242
+ if (mode === AGENT_RESUME_MODE.LIST) {
1243
+ writeOutput(invocation, await listAgentResumeSessions(commandOptions));
1244
+ return;
1245
+ }
1246
+ const candidates = await loadAgentResumeCandidates(commandOptions);
1247
+ if (candidates.length === 0) {
1248
+ writeError(invocation, AGENT_RESUME_TEXT.NO_MATCHES);
1249
+ requestedExitCode = AGENT_CLI_EXIT.FAILURE;
1250
+ } else if (mode === AGENT_RESUME_MODE.LATEST) {
1251
+ requestedExitCode = await resolvedDeps.launchCandidate(candidates[0]);
1252
+ } else {
1253
+ const pickerResult = await resolvedDeps.pickCandidate(candidates);
1254
+ if (pickerResult.kind === AGENT_RESUME_PICKER_RESULT.SELECTED) {
1255
+ requestedExitCode = await resolvedDeps.launchCandidate(pickerResult.candidate);
1256
+ }
1257
+ }
1258
+ }
1259
+ } catch (error) {
1260
+ handleError(invocation, error);
1261
+ }
1262
+ return invocation.io.exit(requestedExitCode);
1263
+ });
1264
+ }
1265
+ };
1266
+ }
1267
+ var agentDomain = createAgentDomain();
1268
+
1269
+ // src/commands/claude/init.ts
1270
+ import { execa as execa2 } from "execa";
1271
+ async function initCommand(options = {}) {
1272
+ const cwd = options.cwd || CONFIG_PROCESS_CWD.read();
1273
+ try {
1274
+ const { stdout: listOutput } = await execa2(
1275
+ "claude",
1276
+ ["plugin", "marketplace", "list"],
1277
+ { cwd }
1278
+ );
1279
+ const exists = listOutput.includes("outcomeeng");
1280
+ if (!exists) {
1281
+ await execa2(
1282
+ "claude",
1283
+ ["plugin", "marketplace", "add", "outcomeeng/plugins"],
1284
+ { cwd }
1285
+ );
1286
+ return "\u2713 outcomeeng marketplace installed successfully\n\nRun 'claude plugin marketplace list' to view all marketplaces.";
1287
+ } else {
1288
+ await execa2("claude", ["plugin", "marketplace", "update", "outcomeeng"], {
1289
+ cwd
1290
+ });
1291
+ return "\u2713 outcomeeng marketplace updated successfully\n\nThe marketplace is now up to date.";
1292
+ }
1293
+ } catch (error) {
1294
+ if (error instanceof Error) {
1295
+ if (error.message.includes("ENOENT") || error.message.includes("command not found")) {
1296
+ throw new Error(
1297
+ "Claude CLI not found. Please install Claude Code first.\n\nVisit: https://docs.anthropic.com/claude-code"
1298
+ );
1299
+ }
1300
+ throw new Error(`Failed to initialize marketplace: ${error.message}`);
1301
+ }
1302
+ throw error;
1303
+ }
1304
+ }
1305
+
1306
+ // src/lib/claude/permissions/discovery.ts
1307
+ import fs from "fs/promises";
1308
+ import path from "path";
1309
+ async function findSettingsFiles(root, visited = /* @__PURE__ */ new Set()) {
1310
+ const normalizedRoot = path.resolve(root.replace(/^~/, process.env.HOME || "~"));
1311
+ if (visited.has(normalizedRoot)) {
1312
+ return [];
1313
+ }
1314
+ visited.add(normalizedRoot);
1315
+ try {
1316
+ return await findSettingsFilesInDirectory(normalizedRoot, visited);
1317
+ } catch (error) {
1318
+ throw discoveryError(normalizedRoot, error);
1319
+ }
1320
+ }
1321
+ async function findSettingsFilesInDirectory(normalizedRoot, visited) {
1322
+ const stats = await fs.stat(normalizedRoot);
1323
+ if (!stats.isDirectory()) {
1324
+ throw new Error(`Path is not a directory: ${normalizedRoot}`);
1325
+ }
1326
+ const entries = await fs.readdir(normalizedRoot, { withFileTypes: true });
1327
+ const results = [];
1328
+ for (const entry of entries) {
1329
+ results.push(...await settingsFilesForEntry(normalizedRoot, entry, visited));
1330
+ }
1331
+ return results;
1332
+ }
1333
+ async function settingsFilesForEntry(normalizedRoot, entry, visited) {
1334
+ if (!entry.isDirectory()) return [];
1335
+ const fullPath = path.join(normalizedRoot, entry.name);
1336
+ if (entry.name === ".claude") {
1337
+ const settingsPath = path.join(fullPath, "settings.local.json");
1338
+ return await isValidSettingsFile(settingsPath) ? [settingsPath] : [];
1339
+ }
1340
+ return findSettingsFiles(fullPath, visited);
1341
+ }
1342
+ function discoveryError(normalizedRoot, error) {
1343
+ if (!(error instanceof Error)) return new Error(unknownErrorMessage(error));
1344
+ if (error.message.includes("ENOENT")) return new Error(`Directory not found: ${normalizedRoot}`);
1345
+ if (error.message.includes("EACCES")) return new Error(`Permission denied: ${normalizedRoot}`);
1346
+ return new Error(`Failed to search directory "${normalizedRoot}": ${error.message}`);
1347
+ }
1348
+ function unknownErrorMessage(error) {
1349
+ if (typeof error === "string") return error;
1350
+ if (typeof error === "number" || typeof error === "boolean" || typeof error === "bigint") return error.toString();
1351
+ if (typeof error === "symbol") return error.description ?? "Symbol()";
1352
+ if (error === void 0) return "undefined";
1353
+ return Object.prototype.toString.call(error);
1354
+ }
1355
+ async function isValidSettingsFile(filePath) {
1356
+ try {
1357
+ await fs.access(filePath, fs.constants.R_OK);
1358
+ const stats = await fs.stat(filePath);
1359
+ if (!stats.isFile()) {
1360
+ return false;
1361
+ }
1362
+ return path.extname(filePath) === ".json";
551
1363
  } catch {
552
- return null;
1364
+ return false;
553
1365
  }
554
1366
  }
555
1367
 
556
1368
  // src/lib/state-store/index.ts
1369
+ import { createHash, randomBytes as nodeRandomBytes } from "crypto";
1370
+ import { constants as fsConstants } from "fs";
1371
+ import {
1372
+ lstat as nodeLstat,
1373
+ mkdir as nodeMkdir,
1374
+ open as nodeOpen,
1375
+ readdir as nodeReaddir,
1376
+ rm as nodeRm
1377
+ } from "fs/promises";
1378
+ import { dirname as dirname2, join as join2 } from "path";
557
1379
  var STATE_STORE_PATH = {
558
1380
  SPX_DIR: ".spx",
559
1381
  BRANCH_SCOPE: "branch",
@@ -1405,16 +2227,16 @@ function formatError(error) {
1405
2227
  }
1406
2228
  return "Unknown error";
1407
2229
  }
1408
- function writeOutput(io, output) {
2230
+ function writeOutput2(io, output) {
1409
2231
  io.writeStdout(`${output}
1410
2232
  `);
1411
2233
  }
1412
- function writeError(io, output) {
2234
+ function writeError2(io, output) {
1413
2235
  io.writeStderr(`${output}
1414
2236
  `);
1415
2237
  }
1416
2238
  function exitWithError(io, error) {
1417
- writeError(io, `Error: ${formatError(error)}`);
2239
+ writeError2(io, `Error: ${formatError(error)}`);
1418
2240
  return io.exit(1);
1419
2241
  }
1420
2242
  function registerClaudeCommands(claudeCmd, invocation) {
@@ -1422,7 +2244,7 @@ function registerClaudeCommands(claudeCmd, invocation) {
1422
2244
  claudeCmd.command("init").description("Initialize or update outcomeeng marketplace plugin").action(async () => {
1423
2245
  try {
1424
2246
  const output = await initCommand({ cwd: productDir() });
1425
- writeOutput(invocation.io, output);
2247
+ writeOutput2(invocation.io, output);
1426
2248
  } catch (error) {
1427
2249
  exitWithError(invocation.io, error);
1428
2250
  }
@@ -1443,7 +2265,7 @@ function registerClaudeCommands(claudeCmd, invocation) {
1443
2265
  async (options) => {
1444
2266
  try {
1445
2267
  if (options.write && options.outputFile) {
1446
- writeError(
2268
+ writeError2(
1447
2269
  invocation.io,
1448
2270
  "Error: --write and --output-file are mutually exclusive\nUse --write to modify global settings, or --output-file to write to a different location"
1449
2271
  );
@@ -1455,7 +2277,7 @@ function registerClaudeCommands(claudeCmd, invocation) {
1455
2277
  root: options.root,
1456
2278
  globalSettings: options.globalSettings
1457
2279
  });
1458
- writeOutput(invocation.io, output);
2280
+ writeOutput2(invocation.io, output);
1459
2281
  } catch (error) {
1460
2282
  exitWithError(invocation.io, error);
1461
2283
  }
@@ -1645,14 +2467,14 @@ async function compactRetrieveCommand(options) {
1645
2467
  }
1646
2468
 
1647
2469
  // src/commands/compact/store.ts
1648
- import { readFile } from "fs/promises";
2470
+ import { readFile as readFile2 } from "fs/promises";
1649
2471
  var UTF8_ENCODING = "utf8";
1650
2472
  async function compactStoreCommand(options) {
1651
2473
  const sessionToken = resolveCompactSessionToken(options.sessionId, options.env ?? process.env);
1652
2474
  if (sessionToken === void 0) return 1;
1653
2475
  let transcript;
1654
2476
  try {
1655
- transcript = await readFile(options.transcript, UTF8_ENCODING);
2477
+ transcript = await readFile2(options.transcript, UTF8_ENCODING);
1656
2478
  } catch {
1657
2479
  return 1;
1658
2480
  }
@@ -1708,7 +2530,7 @@ var compactDomain = {
1708
2530
  };
1709
2531
 
1710
2532
  // src/config/index.ts
1711
- import { readFile as readFile2 } from "fs/promises";
2533
+ import { readFile as readFile3 } from "fs/promises";
1712
2534
  import { join as join5 } from "path";
1713
2535
  import { parse as parseToml, stringify as stringifyToml } from "smol-toml";
1714
2536
  import { parse as parseYaml, parseDocument as parseYamlDocument, stringify as stringifyYaml } from "yaml";
@@ -1811,7 +2633,7 @@ var AGENT_ENVIRONMENT_SKILL_ALLOWED_FIELDS = /* @__PURE__ */ new Set([
1811
2633
  AGENT_ENVIRONMENT_CONFIG_FIELDS.SOURCE,
1812
2634
  AGENT_ENVIRONMENT_CONFIG_FIELDS.VERSION
1813
2635
  ]);
1814
- function isRecord(value) {
2636
+ function isRecord2(value) {
1815
2637
  return typeof value === "object" && value !== null && !Array.isArray(value);
1816
2638
  }
1817
2639
  function rejectUnknownFields(path7, value, allowed) {
@@ -1864,7 +2686,7 @@ function validateRuntimeArray(path7, value) {
1864
2686
  }
1865
2687
  function validateInstructions(raw) {
1866
2688
  const sectionPath = `${AGENT_ENVIRONMENT_SECTION}.${AGENT_ENVIRONMENT_CONFIG_FIELDS.INSTRUCTIONS}`;
1867
- if (!isRecord(raw)) {
2689
+ if (!isRecord2(raw)) {
1868
2690
  return { ok: false, error: `${sectionPath} must be an object` };
1869
2691
  }
1870
2692
  const unknown = rejectUnknownFields(sectionPath, raw, AGENT_ENVIRONMENT_INSTRUCTIONS_ALLOWED_FIELDS);
@@ -1888,7 +2710,7 @@ function validateInstructions(raw) {
1888
2710
  return { ok: true, value: { files } };
1889
2711
  }
1890
2712
  function validateInstructionFile(path7, raw) {
1891
- if (!isRecord(raw)) return { ok: false, error: `${path7} must be an object` };
2713
+ if (!isRecord2(raw)) return { ok: false, error: `${path7} must be an object` };
1892
2714
  const unknown = rejectUnknownFields(
1893
2715
  path7,
1894
2716
  raw,
@@ -1909,7 +2731,7 @@ function validateInstructionFile(path7, raw) {
1909
2731
  }
1910
2732
  function validateRuntimes(raw) {
1911
2733
  const sectionPath = `${AGENT_ENVIRONMENT_SECTION}.${AGENT_ENVIRONMENT_CONFIG_FIELDS.RUNTIMES}`;
1912
- if (!isRecord(raw)) {
2734
+ if (!isRecord2(raw)) {
1913
2735
  return { ok: false, error: `${sectionPath} must be an object` };
1914
2736
  }
1915
2737
  const runtimes = { ...DEFAULT_AGENT_ENVIRONMENT_CONFIG.runtimes };
@@ -1923,7 +2745,7 @@ function validateRuntimes(raw) {
1923
2745
  return { ok: true, value: runtimes };
1924
2746
  }
1925
2747
  function validateRuntimeConfig(path7, raw, defaults5) {
1926
- if (!isRecord(raw)) return { ok: false, error: `${path7} must be an object` };
2748
+ if (!isRecord2(raw)) return { ok: false, error: `${path7} must be an object` };
1927
2749
  const unknown = rejectUnknownFields(path7, raw, AGENT_ENVIRONMENT_RUNTIME_CONFIG_ALLOWED_FIELDS);
1928
2750
  if (!unknown.ok) return unknown;
1929
2751
  const enabledRaw = raw[AGENT_ENVIRONMENT_CONFIG_FIELDS.ENABLED];
@@ -1934,7 +2756,7 @@ function validateRuntimeConfig(path7, raw, defaults5) {
1934
2756
  }
1935
2757
  function validatePluginBootstrap(raw) {
1936
2758
  const sectionPath = `${AGENT_ENVIRONMENT_SECTION}.${AGENT_ENVIRONMENT_CONFIG_FIELDS.PLUGIN_BOOTSTRAP}`;
1937
- if (!isRecord(raw)) {
2759
+ if (!isRecord2(raw)) {
1938
2760
  return { ok: false, error: `${sectionPath} must be an object` };
1939
2761
  }
1940
2762
  const unknown = rejectUnknownFields(
@@ -2053,7 +2875,7 @@ function validatePluginMarketplaceReferences(path7, marketplaces, plugins) {
2053
2875
  return { ok: true, value: void 0 };
2054
2876
  }
2055
2877
  function validateMarketplace(path7, raw) {
2056
- if (!isRecord(raw)) return { ok: false, error: `${path7} must be an object` };
2878
+ if (!isRecord2(raw)) return { ok: false, error: `${path7} must be an object` };
2057
2879
  const unknown = rejectUnknownFields(
2058
2880
  path7,
2059
2881
  raw,
@@ -2070,7 +2892,7 @@ function validateMarketplace(path7, raw) {
2070
2892
  return { ok: true, value: { ...base.value, source: source.value } };
2071
2893
  }
2072
2894
  function validatePlugin(path7, raw) {
2073
- if (!isRecord(raw)) return { ok: false, error: `${path7} must be an object` };
2895
+ if (!isRecord2(raw)) return { ok: false, error: `${path7} must be an object` };
2074
2896
  const unknown = rejectUnknownFields(
2075
2897
  path7,
2076
2898
  raw,
@@ -2099,7 +2921,7 @@ function validatePlugin(path7, raw) {
2099
2921
  };
2100
2922
  }
2101
2923
  function validateSkill(path7, raw) {
2102
- if (!isRecord(raw)) return { ok: false, error: `${path7} must be an object` };
2924
+ if (!isRecord2(raw)) return { ok: false, error: `${path7} must be an object` };
2103
2925
  const unknown = rejectUnknownFields(
2104
2926
  path7,
2105
2927
  raw,
@@ -2145,7 +2967,7 @@ function validateOptionalString(path7, value) {
2145
2967
  return validateNonEmptyString(path7, value);
2146
2968
  }
2147
2969
  function validate(value) {
2148
- if (!isRecord(value)) {
2970
+ if (!isRecord2(value)) {
2149
2971
  return { ok: false, error: `${AGENT_ENVIRONMENT_SECTION} section must be an object` };
2150
2972
  }
2151
2973
  const unknown = rejectUnknownFields(
@@ -2179,7 +3001,7 @@ var agentEnvironmentConfigDescriptor = {
2179
3001
  };
2180
3002
 
2181
3003
  // src/domains/diagnose/facts.ts
2182
- function isRecord2(value) {
3004
+ function isRecord3(value) {
2183
3005
  return typeof value === "object" && value !== null && !Array.isArray(value);
2184
3006
  }
2185
3007
  function isNonEmptyString(value) {
@@ -2189,7 +3011,7 @@ function isNonEmptyStringArray(value) {
2189
3011
  return Array.isArray(value) && value.length > 0 && value.every(isNonEmptyString);
2190
3012
  }
2191
3013
  function validateMarketplaceIdentity(value, field) {
2192
- if (!isRecord2(value) || !isNonEmptyString(value.name) || !isNonEmptyString(value.source)) {
3014
+ if (!isRecord3(value) || !isNonEmptyString(value.name) || !isNonEmptyString(value.source)) {
2193
3015
  return { ok: false, error: `${field} must carry a non-empty \`name\` and \`source\`` };
2194
3016
  }
2195
3017
  return { ok: true, value: { name: value.name, source: value.source } };
@@ -2721,7 +3543,7 @@ var defaults2 = {
2721
3543
  scope: DEFAULT_SCOPE_CONFIG,
2722
3544
  tools: DEFAULT_TOOLS_CONFIG
2723
3545
  };
2724
- function isRecord3(value) {
3546
+ function isRecord4(value) {
2725
3547
  return typeof value === "object" && value !== null && !Array.isArray(value);
2726
3548
  }
2727
3549
  function validateStringField(section, field, value) {
@@ -2744,7 +3566,7 @@ function rejectUnknownFields2(section, value, allowed) {
2744
3566
  return { ok: true, value: void 0 };
2745
3567
  }
2746
3568
  function validateScope(raw) {
2747
- if (!isRecord3(raw)) {
3569
+ if (!isRecord4(raw)) {
2748
3570
  return {
2749
3571
  ok: false,
2750
3572
  error: `${FILE_INCLUSION_SECTION}.${FILE_INCLUSION_CONFIG_FIELDS.SCOPE} must be an object`
@@ -2801,7 +3623,7 @@ function validateScope(raw) {
2801
3623
  };
2802
3624
  }
2803
3625
  function validateTools(raw) {
2804
- if (!isRecord3(raw)) {
3626
+ if (!isRecord4(raw)) {
2805
3627
  return {
2806
3628
  ok: false,
2807
3629
  error: `${FILE_INCLUSION_SECTION}.${FILE_INCLUSION_CONFIG_FIELDS.TOOLS} must be an object`
@@ -2816,7 +3638,7 @@ function validateTools(raw) {
2816
3638
  error: `${FILE_INCLUSION_SECTION}.${FILE_INCLUSION_CONFIG_FIELDS.TOOLS}.${toolName} is not a recognized tool`
2817
3639
  };
2818
3640
  }
2819
- if (!isRecord3(adapterRaw)) {
3641
+ if (!isRecord4(adapterRaw)) {
2820
3642
  return {
2821
3643
  ok: false,
2822
3644
  error: `${FILE_INCLUSION_SECTION}.${FILE_INCLUSION_CONFIG_FIELDS.TOOLS}.${toolName} must be an object`
@@ -3418,7 +4240,7 @@ async function readProductConfigFile(productDir) {
3418
4240
  const path7 = join5(productDir, filename);
3419
4241
  let raw;
3420
4242
  try {
3421
- raw = await readFile2(path7, "utf8");
4243
+ raw = await readFile3(path7, "utf8");
3422
4244
  } catch (error) {
3423
4245
  if (isFileNotFound(error)) continue;
3424
4246
  return { ok: false, error: `failed to read ${filename}: ${toMessage(error)}` };
@@ -3702,7 +4524,7 @@ var configDomain = {
3702
4524
  };
3703
4525
 
3704
4526
  // src/interfaces/cli/diagnose.ts
3705
- import { readFile as readFile3 } from "fs/promises";
4527
+ import { readFile as readFile4 } from "fs/promises";
3706
4528
  import { Option } from "commander";
3707
4529
 
3708
4530
  // src/domains/diagnose/types.ts
@@ -3786,7 +4608,7 @@ function parseManifest(rawJson, availableChecks) {
3786
4608
  } catch (error) {
3787
4609
  return { ok: false, error: `manifest is not valid JSON: ${error.message}` };
3788
4610
  }
3789
- if (!isRecord2(parsed)) {
4611
+ if (!isRecord3(parsed)) {
3790
4612
  return { ok: false, error: "manifest must be a JSON object" };
3791
4613
  }
3792
4614
  const checks = validateChecks(parsed.checks, new Set(availableChecks));
@@ -4036,7 +4858,14 @@ function sessionStoreRunner(probe) {
4036
4858
  // src/domains/hooks/session-start.ts
4037
4859
  var HOOK_SESSION_START_PAYLOAD = {
4038
4860
  CWD: "cwd",
4039
- SESSION_ID: "session_id"
4861
+ SESSION_ID: "session_id",
4862
+ SOURCE: "source"
4863
+ };
4864
+ var HOOK_SESSION_START_SOURCE = {
4865
+ CLEAR: "clear",
4866
+ COMPACT: "compact",
4867
+ RESUME: "resume",
4868
+ STARTUP: "startup"
4040
4869
  };
4041
4870
  var HOOK_SESSION_START_ENV = {
4042
4871
  CLAUDE_ENV_FILE: "CLAUDE_ENV_FILE",
@@ -4080,7 +4909,8 @@ function parseHookSessionStartPayload(content) {
4080
4909
  ok: true,
4081
4910
  value: {
4082
4911
  cwd: nonEmptyString(record6[HOOK_SESSION_START_PAYLOAD.CWD]),
4083
- sessionId: nonEmptyString(record6[HOOK_SESSION_START_PAYLOAD.SESSION_ID])
4912
+ sessionId: nonEmptyString(record6[HOOK_SESSION_START_PAYLOAD.SESSION_ID]),
4913
+ source: nonEmptyString(record6[HOOK_SESSION_START_PAYLOAD.SOURCE])
4084
4914
  }
4085
4915
  };
4086
4916
  }
@@ -4090,6 +4920,21 @@ function resolveHookSessionStartSessionId(payload, env) {
4090
4920
  function resolveHookSessionStartProductDir(payload, cwd) {
4091
4921
  return payload.cwd ?? cwd;
4092
4922
  }
4923
+ var NO_STARTUP_DIRECTIVE = "";
4924
+ var HOOK_COMPACT_FOUNDATION_ACTION = {
4925
+ CONTEXTUALIZE: "/contextualize",
4926
+ UNDERSTAND: "/understand"
4927
+ };
4928
+ var HOOK_COMPACT_FOUNDATION_REASON = `Hook fired because the agent runtime reported ${HOOK_SESSION_START_PAYLOAD.SOURCE}=${HOOK_SESSION_START_SOURCE.COMPACT}.`;
4929
+ var HOOK_COMPACT_FOUNDATION_DIRECTIVE = [
4930
+ HOOK_COMPACT_FOUNDATION_REASON,
4931
+ "Spec-tree foundation was reset by this compaction.",
4932
+ `Before any spec-governed action, including resuming an in-flight PR, /apply, or /handoff, re-invoke ${HOOK_COMPACT_FOUNDATION_ACTION.UNDERSTAND} then ${HOOK_COMPACT_FOUNDATION_ACTION.CONTEXTUALIZE} on every spec node still in scope (not just the next one) before any gh/git archaeology or reading spec-governed source.`,
4933
+ "Skill text carried in the compaction summary is context only, outside active tool authority."
4934
+ ].join("\n");
4935
+ function renderSessionStartStdout(source) {
4936
+ return source === HOOK_SESSION_START_SOURCE.COMPACT ? HOOK_COMPACT_FOUNDATION_DIRECTIVE : NO_STARTUP_DIRECTIVE;
4937
+ }
4093
4938
  function resolveHookSessionStartEnvFile(env, explicitEnvFile) {
4094
4939
  return nonEmptyString(explicitEnvFile) ?? nonEmptyString(env[HOOK_SESSION_START_ENV.CLAUDE_ENV_FILE]);
4095
4940
  }
@@ -4866,7 +5711,7 @@ var DEFAULT_REGISTRY = {
4866
5711
  [CHECK_NAME.SESSION_STORE]: sessionStoreRunner(defaultSessionStoreProbe),
4867
5712
  [CHECK_NAME.MARKETPLACE_INSTALL]: marketplaceInstallRunner(defaultMarketplaceInstallProbe)
4868
5713
  };
4869
- function handleError(error, io) {
5714
+ function handleError2(error, io) {
4870
5715
  io.writeStderr(`Error: ${sanitizeCliArgument(error)}
4871
5716
  `);
4872
5717
  return io.exit(1);
@@ -4891,10 +5736,10 @@ var diagnoseDomain = {
4891
5736
  isTty: Boolean(process.stdout.isTTY)
4892
5737
  }),
4893
5738
  registry: DEFAULT_REGISTRY,
4894
- fs: { readFile: (path7) => readFile3(path7, "utf8") }
5739
+ fs: { readFile: (path7) => readFile4(path7, "utf8") }
4895
5740
  });
4896
5741
  if (!result.ok) {
4897
- handleError(result.error, invocation.io);
5742
+ handleError2(result.error, invocation.io);
4898
5743
  }
4899
5744
  invocation.io.writeStdout(`${result.value.output}
4900
5745
  `);
@@ -4952,9 +5797,10 @@ function isValidPid(pid) {
4952
5797
  }
4953
5798
 
4954
5799
  // src/domains/worktree/resolve.ts
4955
- import { dirname as dirname4, resolve as resolve4 } from "path";
5800
+ import { dirname as dirname4, resolve as resolve5 } from "path";
4956
5801
  var WORKTREE_RESOLVE_ERROR = {
4957
- NOT_A_WORKTREE: "path resolves to no worktree"
5802
+ NOT_A_WORKTREE: "path resolves to no worktree",
5803
+ WORKTREE_LIST_UNAVAILABLE: "git worktree list is unavailable"
4958
5804
  };
4959
5805
  async function resolveWorktreesDir(options) {
4960
5806
  if (options.worktreesDir !== void 0) return options.worktreesDir;
@@ -4966,9 +5812,25 @@ async function resolveCurrentWorktreeName(options) {
4966
5812
  const worktree = await detectWorktreeProductRoot(options.cwd, options.gitDeps);
4967
5813
  return worktreeClaimName(worktree.productDir);
4968
5814
  }
5815
+ async function resolveAllTargetWorktrees(options) {
5816
+ const facts = await gatherGitFacts(options.cwd, options.gitDeps);
5817
+ if (facts === null) {
5818
+ return { ok: false, error: `${WORKTREE_RESOLVE_ERROR.NOT_A_WORKTREE}: ${options.cwd}` };
5819
+ }
5820
+ if (!facts.worktreeListRead) {
5821
+ return { ok: false, error: WORKTREE_RESOLVE_ERROR.WORKTREE_LIST_UNAVAILABLE };
5822
+ }
5823
+ return {
5824
+ ok: true,
5825
+ value: facts.worktreeRoots.map((worktreeRoot) => ({
5826
+ name: worktreeClaimName(worktreeRoot),
5827
+ worktreeRoot
5828
+ }))
5829
+ };
5830
+ }
4969
5831
  async function resolveTargetWorktree(options) {
4970
5832
  const base = options.cwd;
4971
- const targetPath = options.worktree === void 0 ? base : resolve4(base, options.worktree);
5833
+ const targetPath = options.worktree === void 0 ? base : resolve5(base, options.worktree);
4972
5834
  const targetGitPath = await options.pathInfo.isExistingNonDirectory(targetPath) ? dirname4(targetPath) : targetPath;
4973
5835
  const worktree = await detectWorktreeProductRoot(targetGitPath, options.gitDeps);
4974
5836
  if (!worktree.isGitRepo) {
@@ -5003,7 +5865,6 @@ var defaultHookEnvFileSystem = {
5003
5865
  }
5004
5866
  };
5005
5867
  var ERROR_DETAIL_SEPARATOR3 = ": ";
5006
- var EMPTY_HOOK_STDOUT = "";
5007
5868
  async function runSessionStartHook(options) {
5008
5869
  const diagnostics = [];
5009
5870
  const payloadResult = parseHookSessionStartPayload(options.content);
@@ -5042,7 +5903,7 @@ async function runSessionStartHook(options) {
5042
5903
  diagnostics,
5043
5904
  envFileWritten,
5044
5905
  productDir,
5045
- stdout: EMPTY_HOOK_STDOUT,
5906
+ stdout: renderSessionStartStdout(payload.source),
5046
5907
  ...sessionId === void 0 ? {} : { sessionId }
5047
5908
  }
5048
5909
  };
@@ -5135,17 +5996,17 @@ function createProcessHookIo(streams) {
5135
5996
  return {
5136
5997
  readStdin: async () => {
5137
5998
  if (streams.stdin.isTTY) return { ok: true, value: void 0 };
5138
- return new Promise((resolve10) => {
5999
+ return new Promise((resolve12) => {
5139
6000
  let data = "";
5140
6001
  streams.stdin.setEncoding("utf-8");
5141
6002
  streams.stdin.on(HOOK_PROCESS_IO_EVENT.DATA, (chunk) => {
5142
6003
  data += chunk;
5143
6004
  });
5144
6005
  streams.stdin.on(HOOK_PROCESS_IO_EVENT.END, () => {
5145
- resolve10({ ok: true, value: data.length === 0 ? void 0 : data });
6006
+ resolve12({ ok: true, value: data.length === 0 ? void 0 : data });
5146
6007
  });
5147
6008
  streams.stdin.on(HOOK_PROCESS_IO_EVENT.ERROR, (error) => {
5148
- resolve10({ ok: false, error: formatStdinReadError(error) });
6009
+ resolve12({ ok: false, error: formatStdinReadError(error) });
5149
6010
  });
5150
6011
  });
5151
6012
  },
@@ -5192,13 +6053,13 @@ var HOOK_CLI = {
5192
6053
  WORKTREES_DIR_FLAG: "--worktrees-dir"
5193
6054
  };
5194
6055
  var HOOK_DOMAIN_DESCRIPTION = "Run host lifecycle hook events";
5195
- function writeError2(invocation, output) {
6056
+ function writeError3(invocation, output) {
5196
6057
  invocation.io.writeStderr(`${output}
5197
6058
  `);
5198
6059
  }
5199
6060
  function writeInvocationWarning(invocation, warning) {
5200
6061
  if (warning !== void 0) {
5201
- writeError2(invocation, warning);
6062
+ writeError3(invocation, warning);
5202
6063
  }
5203
6064
  }
5204
6065
  function registerHookCommands(hookCmd, invocation) {
@@ -6181,7 +7042,7 @@ function sessionOptionToken(option) {
6181
7042
  }
6182
7043
 
6183
7044
  // src/commands/session/archive.ts
6184
- import { mkdir, rename, stat } from "fs/promises";
7045
+ import { mkdir, rename, stat as stat2 } from "fs/promises";
6185
7046
  import { dirname as dirname6, join as join9 } from "path";
6186
7047
 
6187
7048
  // src/domains/session/archive.ts
@@ -6255,16 +7116,16 @@ ${output}`;
6255
7116
  // src/domains/session/handoff-base-checklist.ts
6256
7117
  var SESSION_HANDOFF_BASE_ERROR_NAME = "SessionHandoffBaseError";
6257
7118
  var HANDOFF_BASE_MARK = {
6258
- MET: "\u2713",
6259
- UNMET: "\u2717"
7119
+ MET: "[PASS]:",
7120
+ UNMET: "[FAIL]:"
6260
7121
  };
6261
7122
  var HANDOFF_BASE_PREREQUISITE_LABEL = {
6262
7123
  CLEAN_WORKING_TREE: "working tree is clean",
6263
7124
  DETACHED_AT_DEFAULT_TIP: "HEAD is detached at the default-branch tip"
6264
7125
  };
6265
7126
  var HANDOFF_BASE_REMEDY = {
6266
- /** Unclean working tree: commit, or hand off from the main checkout. */
6267
- COMMIT_OR_MAIN_CHECKOUT: "commit the changes, or run handoff from the main checkout",
7127
+ /** Unclean working tree: commit before any handoff proceeds. */
7128
+ COMMIT_BEFORE_HANDOFF: "commit the changes before handoff. DO NOT PROCEED while this checkout is dirty",
6268
7129
  /** Off the default-branch tip: detach to it, or hand off from the main checkout. */
6269
7130
  DETACH_TO_TIP_OR_MAIN_CHECKOUT: "detach HEAD to the default-branch tip, or run handoff from the main checkout",
6270
7131
  /** Default branch unresolved: only the main checkout can anchor the base. */
@@ -6281,23 +7142,34 @@ var HANDOFF_BASE_UNRESOLVED = "unresolved";
6281
7142
  var CHECKLIST_INDENT = " ";
6282
7143
  var REMEDY_SEPARATOR = " \u2014 ";
6283
7144
  var CHECKLIST_HEADER = `${SESSION_HANDOFF_BASE_ERROR_NAME}: cannot create a handoff session from this worktree \u2014 it is not the main checkout.`;
7145
+ var HANDOFF_BASE_DIRTY_HEADER = `${SESSION_HANDOFF_BASE_ERROR_NAME}: YOUR CHECKOUT IS DIRTY. YOU CANNOT HANDOFF.`;
7146
+ var HANDOFF_BASE_FACTS_HEADER = "GIT FACTS:";
7147
+ var HANDOFF_BASE_CHECKS_HEADER = "HERE ARE THE CHECKS AND THEIR STATUS:";
6284
7148
  function renderFactLine(label, value) {
6285
7149
  return `${CHECKLIST_INDENT}${label}: ${value ?? HANDOFF_BASE_UNRESOLVED}`;
6286
7150
  }
6287
- function renderPrerequisiteLine(prerequisite) {
7151
+ function renderPrerequisiteLine(prerequisite, index) {
6288
7152
  const mark = prerequisite.met ? HANDOFF_BASE_MARK.MET : HANDOFF_BASE_MARK.UNMET;
6289
- const base = `${CHECKLIST_INDENT}${mark} ${prerequisite.label}`;
7153
+ const lineNumber = index + 1;
7154
+ const base = `${lineNumber}. ${mark} ${prerequisite.label}`;
6290
7155
  return prerequisite.met ? base : `${base}${REMEDY_SEPARATOR}${prerequisite.remedy}`;
6291
7156
  }
7157
+ function isDirtyCheckoutRefusal(checklist) {
7158
+ return checklist.prerequisites.some(
7159
+ (prerequisite) => prerequisite.label === HANDOFF_BASE_PREREQUISITE_LABEL.CLEAN_WORKING_TREE && !prerequisite.met
7160
+ );
7161
+ }
6292
7162
  function renderHandoffBaseChecklist(checklist) {
6293
7163
  return [
6294
- CHECKLIST_HEADER,
7164
+ isDirtyCheckoutRefusal(checklist) ? HANDOFF_BASE_DIRTY_HEADER : CHECKLIST_HEADER,
7165
+ HANDOFF_BASE_FACTS_HEADER,
6295
7166
  renderFactLine(HANDOFF_BASE_FACT_LABEL.DEFAULT_BRANCH, checklist.defaultBranch),
6296
7167
  renderFactLine(HANDOFF_BASE_FACT_LABEL.DEFAULT_TIP, checklist.defaultTipSha),
6297
7168
  renderFactLine(HANDOFF_BASE_FACT_LABEL.HEAD, checklist.headSha),
6298
7169
  renderFactLine(HANDOFF_BASE_FACT_LABEL.CURRENT_WORKTREE, checklist.currentWorktreePath),
6299
7170
  renderFactLine(HANDOFF_BASE_FACT_LABEL.MAIN_CHECKOUT, checklist.mainCheckoutPath),
6300
- ...checklist.prerequisites.map(renderPrerequisiteLine)
7171
+ HANDOFF_BASE_CHECKS_HEADER,
7172
+ ...checklist.prerequisites.map((prerequisite, index) => renderPrerequisiteLine(prerequisite, index))
6301
7173
  ].join("\n");
6302
7174
  }
6303
7175
 
@@ -6404,6 +7276,17 @@ var SessionWorkBranchNotOnOriginError = class extends SessionError {
6404
7276
  this.workBranch = workBranch;
6405
7277
  }
6406
7278
  };
7279
+ var SessionInjectionDirectoryError = class extends SessionError {
7280
+ /** The listed `specs`/`files` entry that resolves to an existing directory. */
7281
+ entry;
7282
+ constructor(entry) {
7283
+ super(
7284
+ `Session injection entry resolves to a directory: ${entry}. The specs and files arrays hold file paths; list the file inside the directory rather than the directory itself.`
7285
+ );
7286
+ this.name = "SessionInjectionDirectoryError";
7287
+ this.entry = entry;
7288
+ }
7289
+ };
6407
7290
  var SessionInvalidJsonHeaderError = class extends SessionError {
6408
7291
  constructor(reason) {
6409
7292
  super(`Invalid JSON header for handoff: ${reason}`);
@@ -6472,7 +7355,7 @@ var SessionAlreadyArchivedError = class extends Error {
6472
7355
  };
6473
7356
  async function fileExists(path7) {
6474
7357
  try {
6475
- const stats = await stat(path7);
7358
+ const stats = await stat2(path7);
6476
7359
  return stats.isFile();
6477
7360
  } catch {
6478
7361
  return false;
@@ -6514,7 +7397,7 @@ async function archiveCommand(options) {
6514
7397
  }
6515
7398
 
6516
7399
  // src/commands/session/delete.ts
6517
- import { stat as stat2, unlink } from "fs/promises";
7400
+ import { stat as stat3, unlink } from "fs/promises";
6518
7401
 
6519
7402
  // src/domains/session/delete.ts
6520
7403
  function resolveDeletePath(sessionId, existingPaths) {
@@ -6963,7 +7846,7 @@ async function findExistingPaths(paths) {
6963
7846
  const existing = [];
6964
7847
  for (const path7 of paths) {
6965
7848
  try {
6966
- const stats = await stat2(path7);
7849
+ const stats = await stat3(path7);
6967
7850
  if (stats.isFile()) {
6968
7851
  existing.push(path7);
6969
7852
  }
@@ -6985,8 +7868,8 @@ async function deleteCommand(options) {
6985
7868
  }
6986
7869
 
6987
7870
  // src/commands/session/handoff.ts
6988
- import { mkdir as mkdir2, writeFile } from "fs/promises";
6989
- import { join as join11, resolve as resolve5 } from "path";
7871
+ import { mkdir as mkdir2, stat as stat4, writeFile } from "fs/promises";
7872
+ import { join as join11, resolve as resolve6 } from "path";
6990
7873
  import { stringify as stringifyYaml3 } from "yaml";
6991
7874
 
6992
7875
  // src/domains/session/create.ts
@@ -7007,7 +7890,7 @@ function cleanPrerequisite(facts) {
7007
7890
  return {
7008
7891
  label: HANDOFF_BASE_PREREQUISITE_LABEL.CLEAN_WORKING_TREE,
7009
7892
  met: facts.isClean,
7010
- remedy: facts.isClean ? "" : HANDOFF_BASE_REMEDY.COMMIT_OR_MAIN_CHECKOUT
7893
+ remedy: facts.isClean ? "" : HANDOFF_BASE_REMEDY.COMMIT_BEFORE_HANDOFF
7011
7894
  };
7012
7895
  }
7013
7896
  function detachedAtTipPrerequisite(facts, met) {
@@ -7231,6 +8114,20 @@ async function resolveRecordedGitRef(suppliedRef, gateRef, cwd, deps) {
7231
8114
  const existsOnOrigin = await originBranchExists(suppliedRef, cwd, deps);
7232
8115
  return resolveWorkBranchGitRef(suppliedRef, existsOnOrigin);
7233
8116
  }
8117
+ async function rejectDirectoryInjectionEntries(entries, cwd) {
8118
+ for (const entry of entries) {
8119
+ if (entry.length === 0) continue;
8120
+ let entryIsDirectory = false;
8121
+ try {
8122
+ entryIsDirectory = (await stat4(resolve6(cwd, entry))).isDirectory();
8123
+ } catch {
8124
+ continue;
8125
+ }
8126
+ if (entryIsDirectory) {
8127
+ throw new SessionInjectionDirectoryError(entry);
8128
+ }
8129
+ }
8130
+ }
7234
8131
  async function handoffCommand(options) {
7235
8132
  const { config } = await resolveSessionConfig({
7236
8133
  sessionsDir: options.sessionsDir,
@@ -7250,6 +8147,8 @@ async function handoffCommand(options) {
7250
8147
  }
7251
8148
  const gateRef = await resolveSessionGitRef(options.cwd, options.deps);
7252
8149
  const gitRef = await resolveRecordedGitRef(header.git_ref, gateRef, options.cwd, options.deps);
8150
+ const injectionCwd = options.cwd ?? CONFIG_PROCESS_CWD.read();
8151
+ await rejectDirectoryInjectionEntries([...header.specs, ...header.files], injectionCwd);
7253
8152
  const sessionId = generateSessionId();
7254
8153
  const agentSessionId = resolveAgentSessionId(options.env ?? process.env);
7255
8154
  const createdAt = (/* @__PURE__ */ new Date()).toISOString();
@@ -7269,7 +8168,7 @@ async function handoffCommand(options) {
7269
8168
  const fullContent = `${SESSION_FRONT_MATTER_OPEN}${yaml}${SESSION_FRONT_MATTER_CLOSE}${body}`;
7270
8169
  const filename = `${sessionId}.md`;
7271
8170
  const sessionPath = join11(config.todoDir, filename);
7272
- const absolutePath = resolve5(sessionPath);
8171
+ const absolutePath = resolve6(sessionPath);
7273
8172
  await mkdir2(config.todoDir, { recursive: true });
7274
8173
  await writeFile(sessionPath, fullContent, SESSION_FILE_ENCODING);
7275
8174
  const output = `Created handoff session ${formatSessionOutputMarker(SESSION_OUTPUT_MARKER.HANDOFF_ID, sessionId)}
@@ -7278,7 +8177,7 @@ ${formatSessionOutputMarker(SESSION_OUTPUT_MARKER.SESSION_FILE, absolutePath)}`;
7278
8177
  }
7279
8178
 
7280
8179
  // src/commands/session/list.ts
7281
- import { readdir, readFile as readFile4 } from "fs/promises";
8180
+ import { readdir as readdir2, readFile as readFile5 } from "fs/promises";
7282
8181
  import { join as join12 } from "path";
7283
8182
  var SESSION_LIST_FORMAT = {
7284
8183
  TEXT: "text",
@@ -7287,13 +8186,13 @@ var SESSION_LIST_FORMAT = {
7287
8186
  var SESSION_LIST_EMPTY_TEXT = "(no sessions)";
7288
8187
  async function loadSessionsFromDir(dir, status) {
7289
8188
  try {
7290
- const files = await readdir(dir);
8189
+ const files = await readdir2(dir);
7291
8190
  const sessions = [];
7292
8191
  for (const file of files) {
7293
8192
  if (!file.endsWith(".md")) continue;
7294
8193
  const id = file.replace(".md", "");
7295
8194
  const filePath = join12(dir, file);
7296
- const content = await readFile4(filePath, SESSION_FILE_ENCODING);
8195
+ const content = await readFile5(filePath, SESSION_FILE_ENCODING);
7297
8196
  const metadata = parseSessionMetadata(content);
7298
8197
  sessions.push({
7299
8198
  id,
@@ -7356,8 +8255,8 @@ async function listCommand(options) {
7356
8255
  }
7357
8256
 
7358
8257
  // src/commands/session/pickup.ts
7359
- import { mkdir as mkdir3, readdir as readdir2, readFile as readFile5, rename as rename2 } from "fs/promises";
7360
- import { join as join13, resolve as resolve6 } from "path";
8258
+ import { mkdir as mkdir3, readdir as readdir3, readFile as readFile6, rename as rename2 } from "fs/promises";
8259
+ import { join as join13, resolve as resolve7 } from "path";
7361
8260
 
7362
8261
  // src/domains/session/pickup.ts
7363
8262
  function buildClaimPaths(sessionId, config) {
@@ -7396,8 +8295,8 @@ function selectBestSession(sessions) {
7396
8295
  var PICKUP_TARGET_STATUS = SESSION_STATUSES[1];
7397
8296
  var PICKUP_DEPS = {
7398
8297
  mkdir: mkdir3,
7399
- readdir: readdir2,
7400
- readFile: readFile5,
8298
+ readdir: readdir3,
8299
+ readFile: readFile6,
7401
8300
  rename: rename2
7402
8301
  };
7403
8302
  async function loadTodoSessions(config) {
@@ -7430,13 +8329,19 @@ async function loadTodoSessionsWithDeps(config, deps) {
7430
8329
  }
7431
8330
  var SESSION_INJECTION_SECTION_PREFIX = "Injected file";
7432
8331
  var SESSION_INJECTION_MISSING_WARNING_PREFIX = "Warning: missing session injection file";
8332
+ var SESSION_INJECTION_UNREADABLE_WARNING_PREFIX = "Warning: unreadable session injection path";
7433
8333
  function injectionPath(cwd, filePath) {
7434
- return resolve6(cwd, filePath);
8334
+ return resolve7(cwd, filePath);
7435
8335
  }
7436
8336
  function formatInjectedFile(listedPath, content) {
7437
8337
  return `${SESSION_INJECTION_SECTION_PREFIX}: ${listedPath}
7438
8338
  ${content}`;
7439
8339
  }
8340
+ function formatInjectionWarning(error, listedPath) {
8341
+ const isAbsent = error instanceof Error && "code" in error && error.code === SESSION_FILE_ERROR_CODE.NOT_FOUND;
8342
+ const prefix = isAbsent ? SESSION_INJECTION_MISSING_WARNING_PREFIX : SESSION_INJECTION_UNREADABLE_WARNING_PREFIX;
8343
+ return `${prefix}: ${listedPath}`;
8344
+ }
7440
8345
  async function readInjectedFiles(metadata, cwd, deps, onWarning) {
7441
8346
  const sections = [];
7442
8347
  for (const listedPath of [...metadata.specs, ...metadata.files]) {
@@ -7444,11 +8349,7 @@ async function readInjectedFiles(metadata, cwd, deps, onWarning) {
7444
8349
  const content = await deps.readFile(injectionPath(cwd, listedPath), SESSION_FILE_ENCODING);
7445
8350
  sections.push(formatInjectedFile(listedPath, content));
7446
8351
  } catch (error) {
7447
- if (error instanceof Error && "code" in error && error.code === SESSION_FILE_ERROR_CODE.NOT_FOUND) {
7448
- onWarning?.(`${SESSION_INJECTION_MISSING_WARNING_PREFIX}: ${listedPath}`);
7449
- continue;
7450
- }
7451
- throw error;
8352
+ onWarning?.(formatInjectionWarning(error, listedPath));
7452
8353
  }
7453
8354
  }
7454
8355
  return sections;
@@ -7504,7 +8405,7 @@ async function loadPickCandidates(options) {
7504
8405
  }
7505
8406
 
7506
8407
  // src/commands/session/prune.ts
7507
- import { readdir as readdir3, readFile as readFile6, unlink as unlink2 } from "fs/promises";
8408
+ import { readdir as readdir4, readFile as readFile7, unlink as unlink2 } from "fs/promises";
7508
8409
  import { join as join14 } from "path";
7509
8410
 
7510
8411
  // src/domains/session/prune.ts
@@ -7549,13 +8450,13 @@ function validatePruneOptions(options) {
7549
8450
  }
7550
8451
  async function loadArchiveSessions(config) {
7551
8452
  try {
7552
- const files = await readdir3(config.archiveDir);
8453
+ const files = await readdir4(config.archiveDir);
7553
8454
  const sessions = [];
7554
8455
  for (const file of files) {
7555
8456
  if (!file.endsWith(".md")) continue;
7556
8457
  const id = file.replace(".md", "");
7557
8458
  const filePath = join14(config.archiveDir, file);
7558
- const content = await readFile6(filePath, SESSION_FILE_ENCODING);
8459
+ const content = await readFile7(filePath, SESSION_FILE_ENCODING);
7559
8460
  const metadata = parseSessionMetadata(content);
7560
8461
  sessions.push({
7561
8462
  id,
@@ -7604,7 +8505,7 @@ async function pruneCommand(options) {
7604
8505
  }
7605
8506
 
7606
8507
  // src/commands/session/release.ts
7607
- import { readdir as readdir4, rename as rename3 } from "fs/promises";
8508
+ import { readdir as readdir5, rename as rename3 } from "fs/promises";
7608
8509
 
7609
8510
  // src/domains/session/release.ts
7610
8511
  function buildReleasePaths(sessionId, config) {
@@ -7635,7 +8536,7 @@ var SESSION_RELEASE_OUTPUT = {
7635
8536
  };
7636
8537
  async function loadDoingSessions(config) {
7637
8538
  try {
7638
- const files = await readdir4(config.doingDir);
8539
+ const files = await readdir5(config.doingDir);
7639
8540
  return files.filter((file) => file.endsWith(".md")).map((file) => ({ id: file.replace(".md", "") }));
7640
8541
  } catch (error) {
7641
8542
  if (error instanceof Error && "code" in error && error.code === SESSION_FILE_ERROR_CODE.NOT_FOUND) {
@@ -7672,12 +8573,12 @@ async function releaseCommand(options) {
7672
8573
  }
7673
8574
 
7674
8575
  // src/commands/session/show.ts
7675
- import { readFile as readFile7, stat as stat3 } from "fs/promises";
8576
+ import { readFile as readFile8, stat as stat5 } from "fs/promises";
7676
8577
  async function findExistingPath(paths, _config) {
7677
8578
  for (let i = 0; i < paths.length; i++) {
7678
8579
  const filePath = paths[i];
7679
8580
  try {
7680
- const stats = await stat3(filePath);
8581
+ const stats = await stat5(filePath);
7681
8582
  if (stats.isFile()) {
7682
8583
  return { path: filePath, status: SEARCH_ORDER[i] };
7683
8584
  }
@@ -7692,7 +8593,7 @@ async function resolveSession(sessionId, config) {
7692
8593
  if (!found) {
7693
8594
  throw new SessionNotFoundError(sessionId);
7694
8595
  }
7695
- const content = await readFile7(found.path, SESSION_FILE_ENCODING);
8596
+ const content = await readFile8(found.path, SESSION_FILE_ENCODING);
7696
8597
  return { status: found.status, path: found.path, content };
7697
8598
  }
7698
8599
  async function showSingle(sessionId, config) {
@@ -7805,7 +8706,7 @@ Output:
7805
8706
  `;
7806
8707
 
7807
8708
  // src/domains/session/pick-model.ts
7808
- import { resolve as resolve7 } from "path";
8709
+ import { resolve as resolve8 } from "path";
7809
8710
  var ELLIPSIS = "\u2026";
7810
8711
  var PICKER_RUNTIME = {
7811
8712
  CLAUDE: "claude",
@@ -7822,7 +8723,7 @@ function buildPickupCommand(runtime, autoContinue, reference) {
7822
8723
  return { command: runtime, args: [prompt] };
7823
8724
  }
7824
8725
  function pickupReference(session, sessionsDir, cwd) {
7825
- return sessionsDir === void 0 ? session.id : resolve7(cwd, session.path);
8726
+ return sessionsDir === void 0 ? session.id : resolve8(cwd, session.path);
7826
8727
  }
7827
8728
  function toSingleLine(text) {
7828
8729
  return text.replaceAll(/\s+/g, " ").trim();
@@ -7916,230 +8817,39 @@ function reducePicker(state, action) {
7916
8817
  case PICKER_ACTION.ENTER_FILTER:
7917
8818
  return { ...state, mode: PICKER_MODE.FILTER };
7918
8819
  case PICKER_ACTION.APPLY_FILTER:
7919
- return { ...state, mode: PICKER_MODE.BROWSE };
7920
- case PICKER_ACTION.CLEAR_FILTER: {
7921
- const count = filterCandidates(state.candidates, "").length;
7922
- return { ...state, mode: PICKER_MODE.BROWSE, query: "", selectedIndex: clampIndex(state.selectedIndex, count) };
7923
- }
7924
- case PICKER_ACTION.FILTER_APPEND: {
7925
- const query = state.query + action.char;
7926
- const count = filterCandidates(state.candidates, query).length;
7927
- return { ...state, query, selectedIndex: clampIndex(state.selectedIndex, count) };
7928
- }
7929
- case PICKER_ACTION.FILTER_DELETE: {
7930
- const query = state.query.slice(0, -1);
7931
- const count = filterCandidates(state.candidates, query).length;
7932
- return { ...state, query, selectedIndex: clampIndex(state.selectedIndex, count) };
7933
- }
7934
- case PICKER_ACTION.LAUNCH:
7935
- case PICKER_ACTION.QUIT:
7936
- return state;
7937
- }
7938
- }
7939
-
7940
- // src/lib/process-lifecycle/exit-codes.ts
7941
- var SIGINT_EXIT_CODE = 130;
7942
- var SIGTERM_EXIT_CODE = 143;
7943
- var EPIPE_EXIT_CODE = 0;
7944
- var UNCAUGHT_EXIT_CODE = 1;
7945
-
7946
- // src/lib/process-lifecycle/foreground-handoff.ts
7947
- var FOREGROUND_SIGNALS = ["SIGINT", "SIGTERM"];
7948
- function createSignalSuspender(target) {
7949
- return {
7950
- suspend() {
7951
- const ignore = () => {
7952
- };
7953
- const restorers = FOREGROUND_SIGNALS.map((signal) => {
7954
- const originals = target.listeners(signal);
7955
- for (const listener of originals) target.removeListener(signal, listener);
7956
- target.on(signal, ignore);
7957
- return () => {
7958
- target.removeListener(signal, ignore);
7959
- for (const listener of originals) target.on(signal, listener);
7960
- };
7961
- });
7962
- return () => {
7963
- for (const restore of restorers) restore();
7964
- };
7965
- }
7966
- };
7967
- }
7968
-
7969
- // src/lib/process-lifecycle/handlers.ts
7970
- var SIGINT_NAME = "SIGINT";
7971
- var SIGTERM_NAME = "SIGTERM";
7972
- function createHandlers(deps) {
7973
- let cleanupOnce = false;
7974
- function killEachChild(signal) {
7975
- deps.registry.forEach((child) => {
7976
- if (!child.killed) child.kill(signal);
7977
- });
7978
- }
7979
- function exitOnce(code, killSignal) {
7980
- if (cleanupOnce) {
7981
- deps.exitController.exit(code);
7982
- return;
7983
- }
7984
- cleanupOnce = true;
7985
- if (killSignal !== void 0) killEachChild(killSignal);
7986
- deps.exitController.exit(code);
7987
- }
7988
- return {
7989
- onSigint() {
7990
- exitOnce(SIGINT_EXIT_CODE, SIGINT_NAME);
7991
- },
7992
- onSigterm() {
7993
- exitOnce(SIGTERM_EXIT_CODE, SIGTERM_NAME);
7994
- },
7995
- onEpipe() {
7996
- exitOnce(EPIPE_EXIT_CODE, SIGTERM_NAME);
7997
- },
7998
- onUncaught(_error) {
7999
- exitOnce(UNCAUGHT_EXIT_CODE, SIGTERM_NAME);
8000
- }
8001
- };
8002
- }
8003
-
8004
- // src/lib/process-lifecycle/install.ts
8005
- import { spawn } from "child_process";
8006
-
8007
- // src/lib/process-lifecycle/registry.ts
8008
- function createRegistry() {
8009
- const tracked = /* @__PURE__ */ new Set();
8010
- return {
8011
- add(child) {
8012
- tracked.add(child);
8013
- },
8014
- remove(child) {
8015
- tracked.delete(child);
8016
- },
8017
- forEach(fn) {
8018
- for (const child of tracked) fn(child);
8019
- },
8020
- get size() {
8021
- return tracked.size;
8022
- }
8023
- };
8024
- }
8025
-
8026
- // src/lib/process-lifecycle/runner.ts
8027
- function createLifecycleRunner(deps) {
8028
- return {
8029
- spawn(command, args, options) {
8030
- const child = deps.spawn(command, args, options);
8031
- deps.registry.add(child);
8032
- child.on("exit", () => deps.registry.remove(child));
8033
- return child;
8034
- }
8035
- };
8036
- }
8037
-
8038
- // src/lib/process-lifecycle/install.ts
8039
- var moduleRegistry = createRegistry();
8040
- var moduleExitController = {
8041
- exit(code) {
8042
- process.exit(code);
8043
- }
8044
- };
8045
- var installed = false;
8046
- var lifecycleProcessRunner = createLifecycleRunner({
8047
- registry: moduleRegistry,
8048
- spawn
8049
- });
8050
- var foregroundProcessRunner = { spawn };
8051
- var processSignalTarget = {
8052
- listeners: (signal) => process.listeners(signal),
8053
- on: (signal, listener) => {
8054
- process.on(signal, listener);
8055
- },
8056
- removeListener: (signal, listener) => {
8057
- process.removeListener(signal, listener);
8058
- }
8059
- };
8060
- var lifecycleSignalSuspender = createSignalSuspender(processSignalTarget);
8061
- var EPIPE_CODE = "EPIPE";
8062
- var UNCAUGHT_PREFIX = "Uncaught: ";
8063
- var NEWLINE = "\n";
8064
- function formatUncaught(error) {
8065
- if (error instanceof Error && error.stack !== void 0) {
8066
- return UNCAUGHT_PREFIX + error.stack + NEWLINE;
8067
- }
8068
- return UNCAUGHT_PREFIX + String(error) + NEWLINE;
8069
- }
8070
- function logUncaughtToStderr(error) {
8071
- try {
8072
- process.stderr.write(formatUncaught(error));
8073
- } catch {
8074
- }
8075
- }
8076
- function installLifecycle() {
8077
- if (installed) return;
8078
- installed = true;
8079
- const handlers = createHandlers({
8080
- registry: moduleRegistry,
8081
- exitController: moduleExitController
8082
- });
8083
- process.on("uncaughtException", (error) => {
8084
- logUncaughtToStderr(error);
8085
- handlers.onUncaught(error);
8086
- });
8087
- process.on("unhandledRejection", (reason) => {
8088
- logUncaughtToStderr(reason);
8089
- handlers.onUncaught(reason);
8090
- });
8091
- process.on("SIGTERM", () => handlers.onSigterm());
8092
- process.on("SIGINT", () => handlers.onSigint());
8093
- process.stdout.on("error", (error) => {
8094
- if (error.code === EPIPE_CODE) {
8095
- handlers.onEpipe();
8096
- return;
8820
+ return { ...state, mode: PICKER_MODE.BROWSE };
8821
+ case PICKER_ACTION.CLEAR_FILTER: {
8822
+ const count = filterCandidates(state.candidates, "").length;
8823
+ return { ...state, mode: PICKER_MODE.BROWSE, query: "", selectedIndex: clampIndex(state.selectedIndex, count) };
8097
8824
  }
8098
- handlers.onUncaught(error);
8099
- });
8100
- process.stderr.on("error", (error) => {
8101
- if (error.code === EPIPE_CODE) {
8102
- handlers.onEpipe();
8103
- return;
8825
+ case PICKER_ACTION.FILTER_APPEND: {
8826
+ const query = state.query + action.char;
8827
+ const count = filterCandidates(state.candidates, query).length;
8828
+ return { ...state, query, selectedIndex: clampIndex(state.selectedIndex, count) };
8104
8829
  }
8105
- handlers.onUncaught(error);
8106
- });
8107
- }
8108
-
8109
- // src/lib/process-lifecycle/managed-subprocess.ts
8110
- var MANAGED_SUBPROCESS_STDIO = "pipe";
8111
- function spawnManagedSubprocess(runner, command, args, options) {
8112
- return runner.spawn(command, args, {
8113
- ...options,
8114
- stdio: MANAGED_SUBPROCESS_STDIO
8115
- });
8830
+ case PICKER_ACTION.FILTER_DELETE: {
8831
+ const query = state.query.slice(0, -1);
8832
+ const count = filterCandidates(state.candidates, query).length;
8833
+ return { ...state, query, selectedIndex: clampIndex(state.selectedIndex, count) };
8834
+ }
8835
+ case PICKER_ACTION.LAUNCH:
8836
+ case PICKER_ACTION.QUIT:
8837
+ return state;
8838
+ }
8116
8839
  }
8117
8840
 
8118
8841
  // src/interfaces/cli/session/pick/launch-agent.ts
8119
- var LAUNCH_FAILURE_STATUS = 1;
8120
8842
  function launchAgent(runner, suspender, command) {
8121
- const restoreSignals = suspender.suspend();
8122
- return new Promise((resolve10) => {
8123
- let settled = false;
8124
- const settle = (status) => {
8125
- if (settled) return;
8126
- settled = true;
8127
- restoreSignals();
8128
- resolve10(status);
8129
- };
8130
- const child = runner.spawn(command.command, command.args, { stdio: "inherit" });
8131
- child.once("exit", (code) => settle(code ?? LAUNCH_FAILURE_STATUS));
8132
- child.once("error", () => settle(LAUNCH_FAILURE_STATUS));
8133
- });
8843
+ return launchForegroundCommand(runner, suspender, command);
8134
8844
  }
8135
8845
 
8136
8846
  // src/interfaces/cli/session/pick/run-picker.tsx
8137
- import { render } from "ink";
8847
+ import { render as render2 } from "ink";
8138
8848
 
8139
8849
  // src/interfaces/cli/session/pick/SessionPicker.tsx
8140
- import { Box, Text, useInput, useStdout } from "ink";
8141
- import { useState } from "react";
8142
- import { jsx, jsxs } from "react/jsx-runtime";
8850
+ import { Box as Box2, Text as Text2, useInput as useInput2, useStdout } from "ink";
8851
+ import { useState as useState2 } from "react";
8852
+ import { jsx as jsx3, jsxs as jsxs2 } from "react/jsx-runtime";
8143
8853
  var SESSION_PICKER_TITLE = "Pick a session to launch";
8144
8854
  var SESSION_PICKER_EMPTY_TEXT = "No claimable sessions.";
8145
8855
  var SESSION_PICKER_BROWSE_HINT = "\u2191\u2193 move \xB7 / filter \xB7 c/C claude \xB7 x/X codex \xB7 q quit";
@@ -8172,33 +8882,33 @@ function SessionRow({ session, selected, columns }) {
8172
8882
  const badge = priority === DEFAULT_PRIORITY ? "" : ` [${priority}]`;
8173
8883
  const reserved = visibleWidth(marker) + visibleWidth(" ") + visibleWidth(session.id) + visibleWidth(badge) + visibleWidth(" ");
8174
8884
  const goal = truncateToWidth(toSingleLine(session.metadata.goal), Math.max(MIN_GOAL_WIDTH, columns - reserved));
8175
- return /* @__PURE__ */ jsxs(Text, { wrap: "truncate", color: selected ? "cyan" : void 0, children: [
8885
+ return /* @__PURE__ */ jsxs2(Text2, { wrap: "truncate", color: selected ? "cyan" : void 0, children: [
8176
8886
  marker,
8177
8887
  " ",
8178
8888
  session.id,
8179
- /* @__PURE__ */ jsx(Text, { color: PRIORITY_COLOR[priority], children: badge }),
8889
+ /* @__PURE__ */ jsx3(Text2, { color: PRIORITY_COLOR[priority], children: badge }),
8180
8890
  " ",
8181
8891
  goal
8182
8892
  ] });
8183
8893
  }
8184
8894
  function PreviewField({ label, value }) {
8185
- return /* @__PURE__ */ jsx(Text, { children: `${label} ${value}` });
8895
+ return /* @__PURE__ */ jsx3(Text2, { children: `${label} ${value}` });
8186
8896
  }
8187
8897
  function PreviewPane({ session }) {
8188
8898
  if (session === null) {
8189
- return /* @__PURE__ */ jsx(Box, { marginTop: 1, children: /* @__PURE__ */ jsx(Text, { dimColor: true, children: SESSION_PICKER_EMPTY_TEXT }) });
8899
+ return /* @__PURE__ */ jsx3(Box2, { marginTop: 1, children: /* @__PURE__ */ jsx3(Text2, { dimColor: true, children: SESSION_PICKER_EMPTY_TEXT }) });
8190
8900
  }
8191
- return /* @__PURE__ */ jsxs(Box, { flexDirection: "column", marginTop: 1, children: [
8192
- /* @__PURE__ */ jsx(PreviewField, { label: PREVIEW_GOAL_LABEL, value: session.metadata.goal }),
8193
- /* @__PURE__ */ jsx(PreviewField, { label: PREVIEW_NEXT_LABEL, value: session.metadata.next_step })
8901
+ return /* @__PURE__ */ jsxs2(Box2, { flexDirection: "column", marginTop: 1, children: [
8902
+ /* @__PURE__ */ jsx3(PreviewField, { label: PREVIEW_GOAL_LABEL, value: session.metadata.goal }),
8903
+ /* @__PURE__ */ jsx3(PreviewField, { label: PREVIEW_NEXT_LABEL, value: session.metadata.next_step })
8194
8904
  ] });
8195
8905
  }
8196
8906
  function SessionPicker({ sessions, onLaunch, onQuit, columns: columnsProp }) {
8197
8907
  const { stdout } = useStdout();
8198
8908
  const stdoutColumns = Reflect.get(stdout, "columns");
8199
8909
  const columns = columnsProp ?? (typeof stdoutColumns === "number" ? stdoutColumns : FALLBACK_COLUMNS);
8200
- const [state, setState] = useState(() => initialPickerState(sessions));
8201
- useInput((input, key) => {
8910
+ const [state, setState] = useState2(() => initialPickerState(sessions));
8911
+ useInput2((input, key) => {
8202
8912
  const action = keyToAction(toPickerKey(input, key), state.mode);
8203
8913
  if (action === null) return;
8204
8914
  if (action.type === PICKER_ACTION.LAUNCH) {
@@ -8215,28 +8925,28 @@ function SessionPicker({ sessions, onLaunch, onQuit, columns: columnsProp }) {
8215
8925
  const visible = visibleCandidates(state);
8216
8926
  const selected = selectedSession(state);
8217
8927
  const hint = state.mode === PICKER_MODE.FILTER ? SESSION_PICKER_FILTER_HINT : SESSION_PICKER_BROWSE_HINT;
8218
- return /* @__PURE__ */ jsxs(Box, { flexDirection: "column", children: [
8219
- /* @__PURE__ */ jsx(Text, { bold: true, children: SESSION_PICKER_TITLE }),
8220
- /* @__PURE__ */ jsxs(Text, { dimColor: true, children: [
8928
+ return /* @__PURE__ */ jsxs2(Box2, { flexDirection: "column", children: [
8929
+ /* @__PURE__ */ jsx3(Text2, { bold: true, children: SESSION_PICKER_TITLE }),
8930
+ /* @__PURE__ */ jsxs2(Text2, { dimColor: true, children: [
8221
8931
  SESSION_PICKER_FILTER_LABEL,
8222
8932
  " ",
8223
8933
  state.query
8224
8934
  ] }),
8225
- visible.length === 0 ? /* @__PURE__ */ jsx(Text, { dimColor: true, children: SESSION_PICKER_EMPTY_TEXT }) : visible.map((session, index) => /* @__PURE__ */ jsx(SessionRow, { session, selected: index === state.selectedIndex, columns }, session.id)),
8226
- /* @__PURE__ */ jsx(PreviewPane, { session: selected }),
8227
- /* @__PURE__ */ jsx(Box, { marginTop: 1, children: /* @__PURE__ */ jsx(Text, { dimColor: true, children: hint }) })
8935
+ visible.length === 0 ? /* @__PURE__ */ jsx3(Text2, { dimColor: true, children: SESSION_PICKER_EMPTY_TEXT }) : visible.map((session, index) => /* @__PURE__ */ jsx3(SessionRow, { session, selected: index === state.selectedIndex, columns }, session.id)),
8936
+ /* @__PURE__ */ jsx3(PreviewPane, { session: selected }),
8937
+ /* @__PURE__ */ jsx3(Box2, { marginTop: 1, children: /* @__PURE__ */ jsx3(Text2, { dimColor: true, children: hint }) })
8228
8938
  ] });
8229
8939
  }
8230
8940
 
8231
8941
  // src/interfaces/cli/session/pick/run-picker.tsx
8232
- import { jsx as jsx2 } from "react/jsx-runtime";
8942
+ import { jsx as jsx4 } from "react/jsx-runtime";
8233
8943
  var PICK_NON_TTY_MESSAGE = "session pick requires an interactive terminal. Run `claude '/pickup <id>'` or `codex '$pickup <id>'` directly in a non-interactive context.";
8234
- async function runPicker(sessions, renderPicker = render) {
8944
+ async function runPicker(sessions, renderPicker = render2) {
8235
8945
  let choice = null;
8236
8946
  let unmount = () => {
8237
8947
  };
8238
8948
  const instance = renderPicker(
8239
- /* @__PURE__ */ jsx2(
8949
+ /* @__PURE__ */ jsx4(
8240
8950
  SessionPicker,
8241
8951
  {
8242
8952
  sessions,
@@ -8260,31 +8970,31 @@ async function readStdin() {
8260
8970
  if (process.stdin.isTTY) {
8261
8971
  return void 0;
8262
8972
  }
8263
- return new Promise((resolve10) => {
8973
+ return new Promise((resolve12) => {
8264
8974
  let data = "";
8265
8975
  process.stdin.setEncoding(SESSION_FILE_ENCODING);
8266
8976
  process.stdin.on("data", (chunk) => {
8267
8977
  data += chunk;
8268
8978
  });
8269
8979
  process.stdin.on("end", () => {
8270
- resolve10(data.length === 0 ? void 0 : data);
8980
+ resolve12(data.length === 0 ? void 0 : data);
8271
8981
  });
8272
8982
  process.stdin.on("error", () => {
8273
- resolve10(void 0);
8983
+ resolve12(void 0);
8274
8984
  });
8275
8985
  });
8276
8986
  }
8277
- function writeOutput2(invocation, output) {
8987
+ function writeOutput3(invocation, output) {
8278
8988
  invocation.io.writeStdout(`${output}
8279
8989
  `);
8280
8990
  }
8281
- function writeError3(invocation, output) {
8991
+ function writeError4(invocation, output) {
8282
8992
  invocation.io.writeStderr(`${output}
8283
8993
  `);
8284
8994
  }
8285
8995
  function writeInvocationWarning2(invocation, warning) {
8286
8996
  if (warning !== void 0) {
8287
- writeError3(invocation, warning);
8997
+ writeError4(invocation, warning);
8288
8998
  }
8289
8999
  }
8290
9000
  function formatError2(error) {
@@ -8293,8 +9003,8 @@ function formatError2(error) {
8293
9003
  }
8294
9004
  return toMessage(error);
8295
9005
  }
8296
- function handleError2(invocation, error) {
8297
- writeError3(invocation, `Error: ${formatError2(error)}`);
9006
+ function handleError3(invocation, error) {
9007
+ writeError4(invocation, `Error: ${formatError2(error)}`);
8298
9008
  return invocation.io.exit(1);
8299
9009
  }
8300
9010
  function colorFlagFromOption(colorOption) {
@@ -8347,9 +9057,9 @@ function registerSessionCommands(sessionCmd, invocation) {
8347
9057
  cwd: effectiveInvocationDir(),
8348
9058
  onWarning: (warning) => writeInvocationWarning2(invocation, warning)
8349
9059
  });
8350
- writeOutput2(invocation, output);
9060
+ writeOutput3(invocation, output);
8351
9061
  } catch (error) {
8352
- handleError2(invocation, error);
9062
+ handleError3(invocation, error);
8353
9063
  }
8354
9064
  }
8355
9065
  );
@@ -8359,7 +9069,7 @@ function registerSessionCommands(sessionCmd, invocation) {
8359
9069
  ).action(async (options) => {
8360
9070
  try {
8361
9071
  if (!process.stdin.isTTY || !process.stdout.isTTY) {
8362
- writeError3(invocation, PICK_NON_TTY_MESSAGE);
9072
+ writeError4(invocation, PICK_NON_TTY_MESSAGE);
8363
9073
  invocation.io.exit(1);
8364
9074
  }
8365
9075
  const sessions = await loadPickCandidates({
@@ -8375,7 +9085,7 @@ function registerSessionCommands(sessionCmd, invocation) {
8375
9085
  invocation.io.exit(code);
8376
9086
  }
8377
9087
  } catch (error) {
8378
- handleError2(invocation, error);
9088
+ handleError3(invocation, error);
8379
9089
  }
8380
9090
  });
8381
9091
  addSessionOptions(
@@ -8393,9 +9103,9 @@ function registerSessionCommands(sessionCmd, invocation) {
8393
9103
  cwd: effectiveInvocationDir(),
8394
9104
  onWarning: (warning) => writeInvocationWarning2(invocation, warning)
8395
9105
  });
8396
- writeOutput2(invocation, output);
9106
+ writeOutput3(invocation, output);
8397
9107
  } catch (error) {
8398
- handleError2(invocation, error);
9108
+ handleError3(invocation, error);
8399
9109
  }
8400
9110
  });
8401
9111
  addSessionOptions(
@@ -8410,9 +9120,9 @@ function registerSessionCommands(sessionCmd, invocation) {
8410
9120
  cwd: effectiveInvocationDir(),
8411
9121
  onWarning: (warning) => writeInvocationWarning2(invocation, warning)
8412
9122
  });
8413
- writeOutput2(invocation, output);
9123
+ writeOutput3(invocation, output);
8414
9124
  } catch (error) {
8415
- handleError2(invocation, error);
9125
+ handleError3(invocation, error);
8416
9126
  }
8417
9127
  });
8418
9128
  addSessionOptions(
@@ -8421,7 +9131,7 @@ function registerSessionCommands(sessionCmd, invocation) {
8421
9131
  ).addHelpText("after", PICKUP_SELECTION_HELP).action(async (ids, options) => {
8422
9132
  try {
8423
9133
  if (ids.length === 0 && !options.auto) {
8424
- writeError3(invocation, "Error: Either session ID or --auto flag is required");
9134
+ writeError4(invocation, "Error: Either session ID or --auto flag is required");
8425
9135
  invocation.io.exit(1);
8426
9136
  }
8427
9137
  const output = await pickupCommand({
@@ -8432,9 +9142,9 @@ function registerSessionCommands(sessionCmd, invocation) {
8432
9142
  cwd: effectiveInvocationDir(),
8433
9143
  onWarning: (warning) => writeInvocationWarning2(invocation, warning)
8434
9144
  });
8435
- writeOutput2(invocation, output);
9145
+ writeOutput3(invocation, output);
8436
9146
  } catch (error) {
8437
- handleError2(invocation, error);
9147
+ handleError3(invocation, error);
8438
9148
  }
8439
9149
  });
8440
9150
  addSessionOptions(
@@ -8448,9 +9158,9 @@ function registerSessionCommands(sessionCmd, invocation) {
8448
9158
  cwd: effectiveInvocationDir(),
8449
9159
  onWarning: (warning) => writeInvocationWarning2(invocation, warning)
8450
9160
  });
8451
- writeOutput2(invocation, output);
9161
+ writeOutput3(invocation, output);
8452
9162
  } catch (error) {
8453
- handleError2(invocation, error);
9163
+ handleError3(invocation, error);
8454
9164
  }
8455
9165
  });
8456
9166
  addSessionOptions(
@@ -8465,17 +9175,17 @@ function registerSessionCommands(sessionCmd, invocation) {
8465
9175
  cwd: effectiveInvocationDir(),
8466
9176
  env: process.env
8467
9177
  });
8468
- writeOutput2(invocation, result.output);
9178
+ writeOutput3(invocation, result.output);
8469
9179
  } catch (error) {
8470
9180
  if (error instanceof SessionHandoffBaseError) {
8471
9181
  if (error.checklist !== null) {
8472
- writeError3(invocation, renderHandoffBaseChecklist(error.checklist));
9182
+ writeError4(invocation, renderHandoffBaseChecklist(error.checklist));
8473
9183
  } else if (!error.silent) {
8474
- writeError3(invocation, `Error: ${error.name}: ${error.message}`);
9184
+ writeError4(invocation, `Error: ${error.name}: ${error.message}`);
8475
9185
  }
8476
9186
  invocation.io.exit(1);
8477
9187
  }
8478
- handleError2(invocation, error);
9188
+ handleError3(invocation, error);
8479
9189
  }
8480
9190
  });
8481
9191
  addSessionOptions(
@@ -8489,9 +9199,9 @@ function registerSessionCommands(sessionCmd, invocation) {
8489
9199
  cwd: effectiveInvocationDir(),
8490
9200
  onWarning: (warning) => writeInvocationWarning2(invocation, warning)
8491
9201
  });
8492
- writeOutput2(invocation, output);
9202
+ writeOutput3(invocation, output);
8493
9203
  } catch (error) {
8494
- handleError2(invocation, error);
9204
+ handleError3(invocation, error);
8495
9205
  }
8496
9206
  });
8497
9207
  addSessionOptions(
@@ -8507,13 +9217,13 @@ function registerSessionCommands(sessionCmd, invocation) {
8507
9217
  cwd: effectiveInvocationDir(),
8508
9218
  onWarning: (warning) => writeInvocationWarning2(invocation, warning)
8509
9219
  });
8510
- writeOutput2(invocation, output);
9220
+ writeOutput3(invocation, output);
8511
9221
  } catch (error) {
8512
9222
  if (error instanceof PruneValidationError) {
8513
- writeError3(invocation, `Error: ${error.message}`);
9223
+ writeError4(invocation, `Error: ${error.message}`);
8514
9224
  invocation.io.exit(1);
8515
9225
  }
8516
- handleError2(invocation, error);
9226
+ handleError3(invocation, error);
8517
9227
  }
8518
9228
  });
8519
9229
  addSessionOptions(
@@ -8527,13 +9237,13 @@ function registerSessionCommands(sessionCmd, invocation) {
8527
9237
  cwd: effectiveInvocationDir(),
8528
9238
  onWarning: (warning) => writeInvocationWarning2(invocation, warning)
8529
9239
  });
8530
- writeOutput2(invocation, output);
9240
+ writeOutput3(invocation, output);
8531
9241
  } catch (error) {
8532
9242
  if (error instanceof SessionAlreadyArchivedError) {
8533
- writeError3(invocation, `Error: ${error.message}`);
9243
+ writeError4(invocation, `Error: ${error.message}`);
8534
9244
  invocation.io.exit(1);
8535
9245
  }
8536
- handleError2(invocation, error);
9246
+ handleError3(invocation, error);
8537
9247
  }
8538
9248
  });
8539
9249
  }
@@ -8547,7 +9257,7 @@ var sessionDomain = {
8547
9257
  };
8548
9258
 
8549
9259
  // src/lib/spec-tree/index.ts
8550
- import { readdir as readdir5, readFile as readFile8 } from "fs/promises";
9260
+ import { readdir as readdir6, readFile as readFile9 } from "fs/promises";
8551
9261
  import { join as join15 } from "path";
8552
9262
  var SPEC_TREE_FIELD_KEY = {
8553
9263
  VERSION: "version",
@@ -8632,7 +9342,7 @@ function createFilesystemSpecTreeSource(options) {
8632
9342
  if (ref.path === void 0) {
8633
9343
  throw new Error("Filesystem source refs require a path");
8634
9344
  }
8635
- return readFile8(join15(options.productDir, ref.path), SPEC_TREE_TEXT_ENCODING);
9345
+ return readFile9(join15(options.productDir, ref.path), SPEC_TREE_TEXT_ENCODING);
8636
9346
  }
8637
9347
  };
8638
9348
  }
@@ -8923,7 +9633,7 @@ async function* readFilesystemSourceEntries(productDir, registry, schemaVersions
8923
9633
  async function* walkFilesystemDirectory(context) {
8924
9634
  let entries;
8925
9635
  try {
8926
- entries = await readdir5(context.absolutePath, { withFileTypes: true });
9636
+ entries = await readdir6(context.absolutePath, { withFileTypes: true });
8927
9637
  } catch (error) {
8928
9638
  if (isFileNotFound2(error)) return;
8929
9639
  throw error;
@@ -9095,7 +9805,7 @@ function formatNextNode(node) {
9095
9805
  }
9096
9806
 
9097
9807
  // src/commands/test/discovery.ts
9098
- import { readdir as readdir6 } from "fs/promises";
9808
+ import { readdir as readdir7 } from "fs/promises";
9099
9809
  import { join as join16, relative, sep } from "path";
9100
9810
  var TESTS_DIRECTORY_NAME = SPEC_TREE_EVIDENCE_FILE.DIRECTORY_NAME;
9101
9811
  var SPEC_ROOT_DIRECTORY = SPEC_TREE_CONFIG.ROOT_DIRECTORY;
@@ -9109,7 +9819,7 @@ async function discoverTestFiles(productDir) {
9109
9819
  async function collectTestFiles(directory, insideTestsDir, found) {
9110
9820
  let entries;
9111
9821
  try {
9112
- entries = await readdir6(directory, { withFileTypes: true });
9822
+ entries = await readdir7(directory, { withFileTypes: true });
9113
9823
  } catch (error) {
9114
9824
  if (hasErrorCode2(error, ERROR_CODE_NOT_FOUND2)) return;
9115
9825
  throw error;
@@ -9443,7 +10153,7 @@ async function readTestRunStatePath(runFilePath, fs8) {
9443
10153
  return { ok: true, value: validated.value };
9444
10154
  }
9445
10155
  function validateTestRunState(value) {
9446
- if (!isRecord4(value)) return { ok: false, error: "testing run state must be an object" };
10156
+ if (!isRecord5(value)) return { ok: false, error: "testing run state must be an object" };
9447
10157
  const branchName = readString(value, TEST_RUN_STATE_FIELDS.BRANCH_NAME);
9448
10158
  if (!branchName.ok) return branchName;
9449
10159
  const headSha = readString(value, TEST_RUN_STATE_FIELDS.HEAD_SHA);
@@ -9486,7 +10196,7 @@ function readRunnerOutcomes(raw) {
9486
10196
  }
9487
10197
  const outcomes = [];
9488
10198
  for (const entry of raw) {
9489
- if (!isRecord4(entry)) {
10199
+ if (!isRecord5(entry)) {
9490
10200
  return { ok: false, error: `${TEST_RUN_STATE_FIELDS.RUNNER_OUTCOMES} entries must be objects` };
9491
10201
  }
9492
10202
  const runnerId = readString(entry, TEST_RUNNER_OUTCOME_FIELDS.RUNNER_ID);
@@ -9507,7 +10217,7 @@ function readProductInputDigests(raw) {
9507
10217
  }
9508
10218
  const digests = [];
9509
10219
  for (const entry of raw) {
9510
- if (!isRecord4(entry)) {
10220
+ if (!isRecord5(entry)) {
9511
10221
  return { ok: false, error: `${TEST_RUN_STATE_FIELDS.PRODUCT_INPUT_DIGESTS} entries must be objects` };
9512
10222
  }
9513
10223
  const descriptorId = readString(entry, PRODUCT_INPUT_DIGEST_FIELDS.DESCRIPTOR_ID);
@@ -9569,7 +10279,7 @@ function testingWriteError(error) {
9569
10279
  function withDomainErrorDetail(domainError, detail) {
9570
10280
  return detail === void 0 ? domainError : `${domainError}: ${detail}`;
9571
10281
  }
9572
- function isRecord4(value) {
10282
+ function isRecord5(value) {
9573
10283
  return typeof value === "object" && value !== null && !Array.isArray(value);
9574
10284
  }
9575
10285
  function sha256Hex2(value) {
@@ -9951,19 +10661,58 @@ function createNodeStatusProvider(productDir) {
9951
10661
  }
9952
10662
 
9953
10663
  // src/lib/node-status/update.ts
9954
- import { mkdir as mkdir4, readdir as readdir7, rm, writeFile as writeFile2 } from "fs/promises";
10664
+ import { mkdir as mkdir4, readdir as readdir8, rm, writeFile as writeFile2 } from "fs/promises";
9955
10665
  import { dirname as dirname7, join as join21 } from "path";
10666
+
10667
+ // src/git/tracked-paths.ts
10668
+ var GIT_LS_FILES_SUBCOMMAND = "ls-files";
10669
+ var GIT_NUL_TERMINATED_FLAG = "-z";
10670
+ var TRACKED_PATH_NUL_SEPARATOR = "\0";
10671
+ var TRACKED_PATH_DIRECTORY_SEPARATOR = "/";
10672
+ var GIT_SUCCESS_EXIT_CODE = 0;
10673
+ async function listTrackedPaths(productDir, deps) {
10674
+ let result;
10675
+ try {
10676
+ result = await deps.execa(
10677
+ GIT_ROOT_COMMAND.EXECUTABLE,
10678
+ [GIT_LS_FILES_SUBCOMMAND, GIT_NUL_TERMINATED_FLAG],
10679
+ { cwd: productDir, reject: false }
10680
+ );
10681
+ } catch {
10682
+ return void 0;
10683
+ }
10684
+ if (result.exitCode !== GIT_SUCCESS_EXIT_CODE) return void 0;
10685
+ return new Set(result.stdout.split(TRACKED_PATH_NUL_SEPARATOR).filter((entry) => entry.length > 0));
10686
+ }
10687
+ function createTrackedPathInclusion(trackedPaths) {
10688
+ if (trackedPaths === void 0) return () => true;
10689
+ const admitted = new Set(trackedPaths);
10690
+ for (const trackedPath of trackedPaths) {
10691
+ let separatorIndex = trackedPath.indexOf(TRACKED_PATH_DIRECTORY_SEPARATOR);
10692
+ while (separatorIndex !== -1) {
10693
+ admitted.add(trackedPath.slice(0, separatorIndex));
10694
+ separatorIndex = trackedPath.indexOf(TRACKED_PATH_DIRECTORY_SEPARATOR, separatorIndex + 1);
10695
+ }
10696
+ }
10697
+ return (path7) => admitted.has(path7);
10698
+ }
10699
+
10700
+ // src/lib/node-status/update.ts
9956
10701
  var NODE_STATUS_TEXT_ENCODING = "utf8";
9957
10702
  async function updateNodeStatus(options) {
9958
- const { productDir, resolveOutcome } = options;
10703
+ const { productDir, resolveOutcome, gitDependencies = defaultGitDependencies } = options;
9959
10704
  const snapshot = await readSpecTree({ source: createFilesystemSpecTreeSource({ productDir }) });
9960
10705
  const ignoreReader = createIgnoreSourceReader(productDir, {
9961
10706
  ignoreSourceFilename: IGNORE_SOURCE_FILENAME_DEFAULT,
9962
10707
  specTreeRootSegment: SPEC_TREE_CONFIG.ROOT_DIRECTORY
9963
10708
  });
10709
+ const isTrackedNodeDirectory = createTrackedPathInclusion(await listTrackedPaths(productDir, gitDependencies));
9964
10710
  const evidenceByNode = collectEvidenceByNode(snapshot);
9965
10711
  const liveStatusPaths = /* @__PURE__ */ new Set();
9966
10712
  for (const node of snapshot.allNodes) {
10713
+ if (!isTrackedNodeDirectory(`${SPEC_TREE_CONFIG.ROOT_DIRECTORY}${TRACKED_PATH_DIRECTORY_SEPARATOR}${node.id}`)) {
10714
+ continue;
10715
+ }
9967
10716
  const evidence = evidenceByNode.get(node.id) ?? [];
9968
10717
  const verification = await resolveVerification(node, {
9969
10718
  evidence,
@@ -10039,7 +10788,7 @@ async function collectNodeStatusFiles(directory) {
10039
10788
  }
10040
10789
  async function readDirectoryEntries(directory) {
10041
10790
  try {
10042
- return await readdir7(directory, { withFileTypes: true });
10791
+ return await readdir8(directory, { withFileTypes: true });
10043
10792
  } catch (error) {
10044
10793
  if (isNodeError3(error) && error.code === "ENOENT") return [];
10045
10794
  throw error;
@@ -10163,13 +10912,14 @@ async function statusCommand(options = {}) {
10163
10912
  }
10164
10913
  return renderSpecStatus(projectSpecTree(await readSpecTree({ source: options.source })), options.format);
10165
10914
  }
10915
+ const gitDependencies = options.gitDependencies ?? defaultGitDependencies;
10166
10916
  const productDir = await resolveSpecProductDir(
10167
10917
  options.cwd ?? CONFIG_PROCESS_CWD.read(),
10168
- options.gitDependencies,
10918
+ gitDependencies,
10169
10919
  options.onWarning
10170
10920
  );
10171
10921
  if (options.update === true) {
10172
- await updateNodeStatus({ productDir, resolveOutcome: options.resolveOutcomeFor(productDir) });
10922
+ await updateNodeStatus({ productDir, resolveOutcome: options.resolveOutcomeFor(productDir), gitDependencies });
10173
10923
  }
10174
10924
  const snapshot = await readSpecTree({
10175
10925
  source: createFilesystemSpecTreeSource({ productDir }),
@@ -10541,7 +11291,7 @@ var VALID_STATUS_FORMATS = [
10541
11291
  OUTPUT_FORMAT.TABLE
10542
11292
  ];
10543
11293
  var UNPRINTABLE_ERROR_MESSAGE = "unprintable error";
10544
- function writeOutput3(io, output) {
11294
+ function writeOutput4(io, output) {
10545
11295
  io.writeStdout(`${output}
10546
11296
  `);
10547
11297
  }
@@ -10599,7 +11349,7 @@ function registerSpecCommands(specCmd, invocation) {
10599
11349
  runnerDepsFor: createRunnerDepsFor(productDir2, process.stderr)
10600
11350
  })
10601
11351
  }) : await statusCommand({ cwd: productDir(), format: format2, onWarning });
10602
- writeOutput3(invocation.io, output);
11352
+ writeOutput4(invocation.io, output);
10603
11353
  } catch (error) {
10604
11354
  handleCommandError(invocation.io, error);
10605
11355
  }
@@ -10607,7 +11357,7 @@ function registerSpecCommands(specCmd, invocation) {
10607
11357
  specCmd.command(SPEC_DOMAIN_CLI.NEXT_COMMAND).description("Find next spec-tree node to work on").action(async () => {
10608
11358
  try {
10609
11359
  const output = await nextCommand({ cwd: productDir(), onWarning });
10610
- writeOutput3(invocation.io, output);
11360
+ writeOutput4(invocation.io, output);
10611
11361
  } catch (error) {
10612
11362
  handleCommandError(invocation.io, error);
10613
11363
  }
@@ -10859,8 +11609,12 @@ function createTestingDomain(deps) {
10859
11609
  }
10860
11610
  var testingDomain = createTestingDomain();
10861
11611
 
11612
+ // src/interfaces/cli/validation.ts
11613
+ import { existsSync as existsSync8, realpathSync } from "fs";
11614
+ import { isAbsolute as isAbsolute9, relative as relative9, resolve as resolve11, sep as sep3 } from "path";
11615
+
10862
11616
  // src/commands/validation/formatting.ts
10863
- import { existsSync } from "fs";
11617
+ import { existsSync, statSync } from "fs";
10864
11618
  import { isAbsolute as isAbsolute3, join as join24, relative as relative3 } from "path";
10865
11619
 
10866
11620
  // src/validation/config/path-filter.ts
@@ -10870,11 +11624,24 @@ function hasEffectiveValidationPathMetadata(filter) {
10870
11624
  return "hasIncludeFilter" in filter && typeof filter.hasIncludeFilter === "boolean" && "noMatchingIncludes" in filter && typeof filter.noMatchingIncludes === "boolean";
10871
11625
  }
10872
11626
  function normalizePathPrefix2(prefix) {
10873
- return prefix.split(/[\\/]/g).join(PATH_PREFIX_SEPARATOR).replace(/^\.\//, "").replace(/\/+$/, "");
11627
+ const normalizedSeparators = prefix.split("\\").join(PATH_PREFIX_SEPARATOR);
11628
+ const withoutLeadingDot = normalizedSeparators.startsWith(`.${PATH_PREFIX_SEPARATOR}`) ? normalizedSeparators.slice(2) : normalizedSeparators;
11629
+ const normalized = trimTrailingPathSeparators(withoutLeadingDot);
11630
+ return normalized === "." ? "" : normalized;
11631
+ }
11632
+ function trimTrailingPathSeparators(path7) {
11633
+ let end = path7.length;
11634
+ while (end > 0 && path7[end - 1] === PATH_PREFIX_SEPARATOR) {
11635
+ end -= 1;
11636
+ }
11637
+ return path7.slice(0, end);
10874
11638
  }
10875
11639
  function pathMatchesPrefix2(path7, prefix) {
10876
11640
  const normalizedPath = normalizePathPrefix2(path7);
10877
11641
  const normalizedPrefix = normalizePathPrefix2(prefix);
11642
+ if (normalizedPrefix.length === 0) {
11643
+ return true;
11644
+ }
10878
11645
  return normalizedPath === normalizedPrefix || normalizedPath.startsWith(`${normalizedPrefix}${PATH_PREFIX_SEPARATOR}`);
10879
11646
  }
10880
11647
  function unique(values) {
@@ -10930,6 +11697,27 @@ function pathPassesValidationFilter(path7, filter) {
10930
11697
  }
10931
11698
  return !exclude.some((prefix) => pathMatchesPrefix2(path7, prefix));
10932
11699
  }
11700
+ function pathIntersectsValidationFilter(path7, filter) {
11701
+ return validationPathFilterIntersections(path7, filter).length > 0;
11702
+ }
11703
+ function validationPathFilterExcludes(filter) {
11704
+ return unique(nonEmpty(filter.exclude).map(normalizePathPrefix2));
11705
+ }
11706
+ function validationPathFilterIntersections(path7, filter) {
11707
+ if (hasEffectiveValidationPathMetadata(filter) && filter.noMatchingIncludes) {
11708
+ return [];
11709
+ }
11710
+ const normalizedPath = normalizePathPrefix2(path7);
11711
+ const include = nonEmpty(filter.include);
11712
+ const exclude = nonEmpty(filter.exclude);
11713
+ const candidates = include.length > 0 ? include.flatMap((prefix) => {
11714
+ const normalizedPrefix = normalizePathPrefix2(prefix);
11715
+ if (pathMatchesPrefix2(normalizedPath, normalizedPrefix)) return [normalizedPath];
11716
+ if (pathMatchesPrefix2(normalizedPrefix, normalizedPath)) return [normalizedPrefix];
11717
+ return [];
11718
+ }) : [normalizedPath];
11719
+ return unique(candidates).filter((candidate) => !exclude.some((prefix) => pathMatchesPrefix2(candidate, prefix)));
11720
+ }
10933
11721
  function applyValidationPathFilterToScope(scopeConfig, filter) {
10934
11722
  if (!hasValidationPathFilter(filter)) {
10935
11723
  return scopeConfig;
@@ -10945,7 +11733,14 @@ function applyValidationPathFilterToScope(scopeConfig, filter) {
10945
11733
  (include) => scopeConfig.directories.some((directory) => pathMatchesPrefix2(include, directory))
10946
11734
  );
10947
11735
  const noMatchingIncludes = hasEffectiveMetadata && filter.noMatchingIncludes || hasIncludeFilter && scopedDirectories.length === 0 && scopedFilePatterns.length === 0 && includeFallbacksWithinScope.length === 0;
10948
- const directories = noMatchingIncludes ? [] : scopedDirectories.length > 0 ? scopedDirectories : includeFallbacksWithinScope;
11736
+ let directories;
11737
+ if (noMatchingIncludes) {
11738
+ directories = [];
11739
+ } else if (scopedDirectories.length > 0) {
11740
+ directories = scopedDirectories;
11741
+ } else {
11742
+ directories = includeFallbacksWithinScope;
11743
+ }
10949
11744
  const filePatterns = scopedFilePatterns.length > 0 ? scopedFilePatterns : [...directories];
10950
11745
  return {
10951
11746
  ...scopeConfig,
@@ -10991,14 +11786,23 @@ function forwardChunkWithBackpressure(source, stream, chunk) {
10991
11786
  // src/validation/steps/formatting.ts
10992
11787
  var DPRINT_COMMAND = "dprint";
10993
11788
  var DPRINT_CHECK_SUBCOMMAND = "check";
11789
+ var DPRINT_EXCLUDES_OPTION = "--excludes";
11790
+ var DPRINT_OPTIONS_TERMINATOR = "--";
10994
11791
  var DPRINT_CONFIG_FILENAME = "dprint.jsonc";
10995
11792
  var defaultFormattingProcessRunner = lifecycleProcessRunner;
10996
11793
  function buildDprintCheckArgs(options) {
10997
- return [DPRINT_CHECK_SUBCOMMAND, ...options.files ?? []];
11794
+ const excludes = options.excludes ?? [];
11795
+ const files = options.files ?? [];
11796
+ return [
11797
+ DPRINT_CHECK_SUBCOMMAND,
11798
+ ...excludes.length > 0 ? [DPRINT_EXCLUDES_OPTION, ...excludes] : [],
11799
+ ...files.length > 0 ? [DPRINT_OPTIONS_TERMINATOR] : [],
11800
+ ...files
11801
+ ];
10998
11802
  }
10999
11803
  async function validateFormatting(context, runner = defaultFormattingProcessRunner) {
11000
- const args = buildDprintCheckArgs({ files: context.files });
11001
- return new Promise((resolve10) => {
11804
+ const args = buildDprintCheckArgs({ files: context.files, excludes: context.excludes });
11805
+ return new Promise((resolve12) => {
11002
11806
  const child = spawnManagedSubprocess(runner, DPRINT_COMMAND, args, { cwd: context.projectRoot });
11003
11807
  const chunks = [];
11004
11808
  const capture = (chunk) => {
@@ -11007,10 +11811,10 @@ async function validateFormatting(context, runner = defaultFormattingProcessRunn
11007
11811
  child.stdout?.on(VALIDATION_SUBPROCESS_EVENTS.DATA, capture);
11008
11812
  child.stderr?.on(VALIDATION_SUBPROCESS_EVENTS.DATA, capture);
11009
11813
  child.on(VALIDATION_SUBPROCESS_EVENTS.CLOSE, (code) => {
11010
- resolve10({ success: code === 0, output: chunks.join("") });
11814
+ resolve12({ success: code === 0, output: chunks.join("") });
11011
11815
  });
11012
11816
  child.on(VALIDATION_SUBPROCESS_EVENTS.ERROR, (error) => {
11013
- resolve10({ success: false, output: chunks.join(""), error: error.message });
11817
+ resolve12({ success: false, output: chunks.join(""), error: error.message });
11014
11818
  });
11015
11819
  });
11016
11820
  }
@@ -11032,7 +11836,7 @@ var VALIDATION_SKIP_LABELS = {
11032
11836
  DISABLED_BY_PREFIX: "disabled by",
11033
11837
  TYPESCRIPT_ABSENT_REASON: "TypeScript not detected in project",
11034
11838
  VALIDATION_PATHS_NO_TARGETS_REASON: "validation paths matched no files",
11035
- MARKDOWN_NO_SCOPE_REASON: "no markdown files in --files scope",
11839
+ MARKDOWN_NO_SCOPE_REASON: "no markdown files in explicit path scope",
11036
11840
  MARKDOWN_NO_DEFAULT_DIRECTORIES_REASON: "no spx/ or docs/ directories found"
11037
11841
  };
11038
11842
  var VALIDATION_COMMAND_OUTPUT = {
@@ -11077,6 +11881,7 @@ var FORMATTING_COMMAND_OUTPUT = {
11077
11881
  NO_CONFIG_SKIP_REASON: `no ${DPRINT_CONFIG_FILENAME} at product root`
11078
11882
  };
11079
11883
  var FORMATTING_CONFIG_ERROR_MESSAGE = `${VALIDATION_STAGE_DISPLAY_NAMES.FORMATTING}: \u2717 config error`;
11884
+ var DPRINT_RECURSIVE_DIRECTORY_GLOB_SUFFIX = "/**/*";
11080
11885
  async function formattingCommand(options) {
11081
11886
  const { cwd, files, quiet } = options;
11082
11887
  const startTime = Date.now();
@@ -11098,12 +11903,22 @@ async function formattingCommand(options) {
11098
11903
  VALIDATION_PATH_TOOL_SUBSECTIONS.FORMATTING
11099
11904
  );
11100
11905
  const hasExplicitScope = files !== void 0 && files.length > 0;
11101
- const scopedFiles = hasExplicitScope ? files.map((filePath) => isAbsolute3(filePath) ? relative3(cwd, filePath) : filePath).filter((relativePath) => pathPassesValidationFilter(relativePath, pathFilter)) : void 0;
11906
+ const scopedFiles = hasExplicitScope ? files.flatMap(
11907
+ (filePath) => formattingPathOperandsForValidationPathFilter(
11908
+ cwd,
11909
+ isAbsolute3(filePath) ? relative3(cwd, filePath) : filePath,
11910
+ pathFilter
11911
+ )
11912
+ ) : void 0;
11102
11913
  if (hasExplicitScope && (scopedFiles === void 0 || scopedFiles.length === 0)) {
11103
11914
  const output2 = quiet ? "" : `${VALIDATION_STAGE_DISPLAY_NAMES.FORMATTING}: skipped (${FORMATTING_COMMAND_OUTPUT.EMPTY_SCOPE_REASON})`;
11104
11915
  return { exitCode: 0, output: output2, durationMs: Date.now() - startTime };
11105
11916
  }
11106
- const result = await validateFormatting({ projectRoot: cwd, files: scopedFiles });
11917
+ const result = await validateFormatting({
11918
+ projectRoot: cwd,
11919
+ files: scopedFiles,
11920
+ excludes: validationPathFilterExcludes(pathFilter)
11921
+ });
11107
11922
  const durationMs = Date.now() - startTime;
11108
11923
  if (result.success) {
11109
11924
  return { exitCode: 0, output: quiet ? "" : FORMATTING_COMMAND_OUTPUT.NO_ISSUES, durationMs };
@@ -11112,6 +11927,24 @@ async function formattingCommand(options) {
11112
11927
  const output = [FORMATTING_COMMAND_OUTPUT.FAILURE_SUMMARY, detail].filter((line) => line.length > 0).join("\n");
11113
11928
  return { exitCode: 1, output, durationMs };
11114
11929
  }
11930
+ function normalizeFormattingPathOperand(productDir, relativePath) {
11931
+ const absolutePath = join24(productDir, relativePath);
11932
+ if (!existsSync(absolutePath) || !statSync(absolutePath).isDirectory()) {
11933
+ return relativePath;
11934
+ }
11935
+ const normalizedDirectory = trimTrailingPathSeparators(relativePath);
11936
+ if (normalizedDirectory.length === 0 || normalizedDirectory === ".") {
11937
+ return DPRINT_RECURSIVE_DIRECTORY_GLOB_SUFFIX.slice(1);
11938
+ }
11939
+ return `${normalizedDirectory}${DPRINT_RECURSIVE_DIRECTORY_GLOB_SUFFIX}`;
11940
+ }
11941
+ function formattingPathOperandsForValidationPathFilter(productDir, relativePath, pathFilter) {
11942
+ const absolutePath = join24(productDir, relativePath);
11943
+ if (!existsSync(absolutePath) || !statSync(absolutePath).isDirectory()) {
11944
+ return pathPassesValidationFilter(relativePath, pathFilter) ? [relativePath] : [];
11945
+ }
11946
+ return validationPathFilterIntersections(relativePath, pathFilter).map((path7) => normalizeFormattingPathOperand(productDir, path7));
11947
+ }
11115
11948
 
11116
11949
  // src/validation/languages/formatting.ts
11117
11950
  var FORMATTING_LANGUAGE_NAME = "formatting";
@@ -11127,10 +11960,10 @@ var formattingValidationLanguage = {
11127
11960
  };
11128
11961
 
11129
11962
  // src/commands/validation/markdown.ts
11130
- import { relative as relative4 } from "path";
11963
+ import { isAbsolute as isAbsolute4, join as join26, relative as relative4 } from "path";
11131
11964
 
11132
11965
  // src/validation/steps/markdown.ts
11133
- import { existsSync as existsSync2, statSync } from "fs";
11966
+ import { existsSync as existsSync2, statSync as statSync2 } from "fs";
11134
11967
  import { basename as basename4, dirname as dirname9, join as join25, relative as pathRelative } from "path";
11135
11968
  import { main as markdownlintMain } from "markdownlint-cli2";
11136
11969
  import relativeLinksRule from "markdownlint-rule-relative-links";
@@ -11154,9 +11987,8 @@ var MARKDOWN_VALIDATION_TARGET_KIND = {
11154
11987
  var MARKDOWN_VALIDATION_TARGET_DIAGNOSTICS = {
11155
11988
  MISSING_OR_UNRELATED_SCOPE: "not an existing directory or markdown file"
11156
11989
  };
11157
- var ERROR_LINE_PATTERN = /^(.+?):(\d+)(?::\d+)?\s+(.+)$/;
11158
11990
  var defaultMarkdownValidationTargetDeps = {
11159
- statSync
11991
+ statSync: statSync2
11160
11992
  };
11161
11993
  function buildMarkdownlintConfig(directoryName) {
11162
11994
  const md024Disabled = MD024_DISABLED_DIRECTORIES.includes(
@@ -11199,25 +12031,71 @@ function parseErrorLine(line) {
11199
12031
  if (DATA_URI_PATTERN.test(line)) {
11200
12032
  return null;
11201
12033
  }
11202
- const match = ERROR_LINE_PATTERN.exec(line);
11203
- if (!match) {
11204
- return null;
12034
+ const parsed = parseMarkdownlintErrorLine(line);
12035
+ if (parsed === null) return null;
12036
+ return {
12037
+ file: parsed.file,
12038
+ line: Number.parseInt(parsed.line, 10),
12039
+ detail: parsed.detail
12040
+ };
12041
+ }
12042
+ function parseMarkdownlintErrorLine(line) {
12043
+ for (let fileSeparator = line.indexOf(":"); fileSeparator > 0; fileSeparator = line.indexOf(":", fileSeparator + 1)) {
12044
+ const parsed = parseMarkdownlintErrorLineAtSeparator(line, fileSeparator);
12045
+ if (parsed !== null) return parsed;
11205
12046
  }
11206
- const [, file, lineStr, detail] = match;
12047
+ return null;
12048
+ }
12049
+ function parseMarkdownlintErrorLineAtSeparator(line, fileSeparator) {
12050
+ const lineStart = fileSeparator + 1;
12051
+ const lineEnd = scanDigits(line, lineStart);
12052
+ if (lineEnd === lineStart) return null;
12053
+ const detailStart = scanMarkdownlintColumnAndWhitespace(line, lineEnd);
12054
+ if (detailStart === null || detailStart >= line.length) return null;
11207
12055
  return {
11208
- file,
11209
- line: parseInt(lineStr, 10),
11210
- detail
12056
+ file: line.slice(0, fileSeparator),
12057
+ line: line.slice(lineStart, lineEnd),
12058
+ detail: line.slice(detailStart)
11211
12059
  };
11212
12060
  }
12061
+ function scanDigits(value, start) {
12062
+ let index = start;
12063
+ while (index < value.length && isAsciiDigit(value[index] ?? "")) {
12064
+ index += 1;
12065
+ }
12066
+ return index;
12067
+ }
12068
+ function scanMarkdownlintColumnAndWhitespace(value, start) {
12069
+ let index = start;
12070
+ if (value[index] === ":") {
12071
+ const columnStart = index + 1;
12072
+ index = scanDigits(value, columnStart);
12073
+ if (index === columnStart) return null;
12074
+ }
12075
+ if (!isAsciiWhitespace(value[index] ?? "")) return null;
12076
+ while (index < value.length && isAsciiWhitespace(value[index] ?? "")) {
12077
+ index += 1;
12078
+ }
12079
+ return index;
12080
+ }
12081
+ function isAsciiDigit(value) {
12082
+ return value >= "0" && value <= "9";
12083
+ }
12084
+ function isAsciiWhitespace(value) {
12085
+ return value === " " || value === " ";
12086
+ }
11213
12087
  async function validateMarkdown(options) {
11214
- const { targets, projectRoot } = options;
12088
+ const { targets, projectRoot, validationPathExcludes = [] } = options;
11215
12089
  const errors = [];
11216
- const excludeGlobs = getExcludeGlobs(projectRoot);
12090
+ const specTreeExcludeGlobs = getExcludeGlobs(projectRoot);
11217
12091
  for (const target of targets) {
11218
12092
  const directory = targetDirectory(target);
11219
12093
  const dirName = markdownlintConfigDirectoryName(directory, projectRoot);
11220
12094
  const config = buildMarkdownlintConfig(dirName);
12095
+ const excludeGlobs = [
12096
+ ...specTreeExcludeGlobs,
12097
+ ...validationPathExcludeGlobsForTarget(target, projectRoot, validationPathExcludes)
12098
+ ];
11221
12099
  const dirErrors = await validateTarget(target, config, projectRoot, excludeGlobs);
11222
12100
  errors.push(...dirErrors);
11223
12101
  }
@@ -11263,6 +12141,32 @@ async function validateTarget(target, config, projectRoot, ignoreGlobs = []) {
11263
12141
  function targetDirectory(target) {
11264
12142
  return target.kind === MARKDOWN_VALIDATION_TARGET_KIND.FILE ? dirname9(target.path) : target.path;
11265
12143
  }
12144
+ function validationPathExcludeGlobsForTarget(target, projectRoot, excludes) {
12145
+ if (projectRoot === void 0 || excludes.length === 0) return [];
12146
+ const directory = targetDirectory(target);
12147
+ const targetPath = normalizePathPrefix2(pathRelative(projectRoot, directory));
12148
+ return excludes.flatMap((exclude) => {
12149
+ const excludedPath = normalizePathPrefix2(exclude);
12150
+ if (pathContainsValidationPath(excludedPath, targetPath)) {
12151
+ return [MARKDOWN_DIRECTORY_GLOB];
12152
+ }
12153
+ if (!pathContainsValidationPath(targetPath, excludedPath)) {
12154
+ return [];
12155
+ }
12156
+ const relativeExclude = normalizePathPrefix2(pathRelative(directory, join25(projectRoot, excludedPath)));
12157
+ if (relativeExclude.length === 0) {
12158
+ return [MARKDOWN_DIRECTORY_GLOB];
12159
+ }
12160
+ const absoluteExclude = join25(projectRoot, excludedPath);
12161
+ return [
12162
+ isExistingFile(absoluteExclude, defaultMarkdownValidationTargetDeps) ? relativeExclude : `${relativeExclude}/**`
12163
+ ];
12164
+ });
12165
+ }
12166
+ function pathContainsValidationPath(prefix, path7) {
12167
+ if (prefix.length === 0) return true;
12168
+ return path7 === prefix || path7.startsWith(`${prefix}/`);
12169
+ }
11266
12170
  function hasMarkdownExtension(path7) {
11267
12171
  const lastDot = path7.lastIndexOf(".");
11268
12172
  if (lastDot < 0) return false;
@@ -11315,11 +12219,12 @@ async function markdownCommand(options) {
11315
12219
  validationConfig.paths,
11316
12220
  VALIDATION_PATH_TOOL_SUBSECTIONS.MARKDOWN
11317
12221
  );
11318
- const targetResolutions = files && files.length > 0 ? files.map((filePath) => resolveMarkdownValidationTarget(filePath)) : void 0;
12222
+ const targetResolutions = files && files.length > 0 ? files.map((filePath) => resolveMarkdownValidationTarget(markdownValidationOperandPath(cwd, filePath))) : void 0;
12223
+ const explicitTargets = targetResolutions === void 0 ? void 0 : targetResolutions.map((resolution) => resolution.target).filter((target) => target !== void 0).flatMap((target) => markdownTargetsForValidationPathFilter(cwd, target, pathFilter));
11319
12224
  const unfilteredTargets = targetResolutions === void 0 ? getDefaultDirectories(cwd).map((path7) => ({
11320
12225
  kind: MARKDOWN_VALIDATION_TARGET_KIND.DIRECTORY,
11321
12226
  path: path7
11322
- })) : targetResolutions.map((resolution) => resolution.target).filter((target) => target !== void 0);
12227
+ })) : explicitTargets ?? [];
11323
12228
  const targets = unfilteredTargets.filter(
11324
12229
  (target) => pathPassesValidationFilter(relative4(cwd, target.path), pathFilter)
11325
12230
  );
@@ -11335,11 +12240,22 @@ async function markdownCommand(options) {
11335
12240
  }
11336
12241
  const result = await validateMarkdown({
11337
12242
  targets,
11338
- projectRoot: cwd
12243
+ projectRoot: cwd,
12244
+ validationPathExcludes: validationPathFilterExcludes(pathFilter)
11339
12245
  });
11340
12246
  const durationMs = Date.now() - startTime;
11341
12247
  return formatMarkdownResult(result, skippedOutput, quiet, durationMs);
11342
12248
  }
12249
+ function markdownValidationOperandPath(productDir, filePath) {
12250
+ return isAbsolute4(filePath) ? filePath : join26(productDir, filePath);
12251
+ }
12252
+ function markdownTargetsForValidationPathFilter(productDir, target, pathFilter) {
12253
+ const relativePath = relative4(productDir, target.path);
12254
+ if (target.kind === MARKDOWN_VALIDATION_TARGET_KIND.FILE) {
12255
+ return pathPassesValidationFilter(relativePath, pathFilter) ? [target] : [];
12256
+ }
12257
+ return validationPathFilterIntersections(relativePath, pathFilter).map((path7) => resolveMarkdownValidationTarget(markdownValidationOperandPath(productDir, path7)).target).filter((filteredTarget) => filteredTarget !== void 0);
12258
+ }
11343
12259
  function formatSkippedFileScope(target) {
11344
12260
  return `${MARKDOWN_COMMAND_OUTPUT.SKIPPED_FILE_SCOPE_PREFIX}: ${target.path} (${target.reason})`;
11345
12261
  }
@@ -11916,39 +12832,39 @@ function visit(text, visitor, options = ParseOptions.DEFAULT) {
11916
12832
  const token = _scanner.scan();
11917
12833
  switch (_scanner.getTokenError()) {
11918
12834
  case 4:
11919
- handleError4(
12835
+ handleError5(
11920
12836
  14
11921
12837
  /* ParseErrorCode.InvalidUnicode */
11922
12838
  );
11923
12839
  break;
11924
12840
  case 5:
11925
- handleError4(
12841
+ handleError5(
11926
12842
  15
11927
12843
  /* ParseErrorCode.InvalidEscapeCharacter */
11928
12844
  );
11929
12845
  break;
11930
12846
  case 3:
11931
- handleError4(
12847
+ handleError5(
11932
12848
  13
11933
12849
  /* ParseErrorCode.UnexpectedEndOfNumber */
11934
12850
  );
11935
12851
  break;
11936
12852
  case 1:
11937
12853
  if (!disallowComments) {
11938
- handleError4(
12854
+ handleError5(
11939
12855
  11
11940
12856
  /* ParseErrorCode.UnexpectedEndOfComment */
11941
12857
  );
11942
12858
  }
11943
12859
  break;
11944
12860
  case 2:
11945
- handleError4(
12861
+ handleError5(
11946
12862
  12
11947
12863
  /* ParseErrorCode.UnexpectedEndOfString */
11948
12864
  );
11949
12865
  break;
11950
12866
  case 6:
11951
- handleError4(
12867
+ handleError5(
11952
12868
  16
11953
12869
  /* ParseErrorCode.InvalidCharacter */
11954
12870
  );
@@ -11958,7 +12874,7 @@ function visit(text, visitor, options = ParseOptions.DEFAULT) {
11958
12874
  case 12:
11959
12875
  case 13:
11960
12876
  if (disallowComments) {
11961
- handleError4(
12877
+ handleError5(
11962
12878
  10
11963
12879
  /* ParseErrorCode.InvalidCommentToken */
11964
12880
  );
@@ -11967,7 +12883,7 @@ function visit(text, visitor, options = ParseOptions.DEFAULT) {
11967
12883
  }
11968
12884
  break;
11969
12885
  case 16:
11970
- handleError4(
12886
+ handleError5(
11971
12887
  1
11972
12888
  /* ParseErrorCode.InvalidSymbol */
11973
12889
  );
@@ -11980,7 +12896,7 @@ function visit(text, visitor, options = ParseOptions.DEFAULT) {
11980
12896
  }
11981
12897
  }
11982
12898
  }
11983
- function handleError4(error, skipUntilAfter = [], skipUntil = []) {
12899
+ function handleError5(error, skipUntilAfter = [], skipUntil = []) {
11984
12900
  onError(error);
11985
12901
  if (skipUntilAfter.length + skipUntil.length > 0) {
11986
12902
  let token = _scanner.getToken();
@@ -12012,7 +12928,7 @@ function visit(text, visitor, options = ParseOptions.DEFAULT) {
12012
12928
  const tokenValue = _scanner.getTokenValue();
12013
12929
  let value = Number(tokenValue);
12014
12930
  if (isNaN(value)) {
12015
- handleError4(
12931
+ handleError5(
12016
12932
  2
12017
12933
  /* ParseErrorCode.InvalidNumberFormat */
12018
12934
  );
@@ -12037,7 +12953,7 @@ function visit(text, visitor, options = ParseOptions.DEFAULT) {
12037
12953
  }
12038
12954
  function parseProperty() {
12039
12955
  if (_scanner.getToken() !== 10) {
12040
- handleError4(3, [], [
12956
+ handleError5(3, [], [
12041
12957
  2,
12042
12958
  5
12043
12959
  /* SyntaxKind.CommaToken */
@@ -12049,14 +12965,14 @@ function visit(text, visitor, options = ParseOptions.DEFAULT) {
12049
12965
  onSeparator(":");
12050
12966
  scanNext();
12051
12967
  if (!parseValue()) {
12052
- handleError4(4, [], [
12968
+ handleError5(4, [], [
12053
12969
  2,
12054
12970
  5
12055
12971
  /* SyntaxKind.CommaToken */
12056
12972
  ]);
12057
12973
  }
12058
12974
  } else {
12059
- handleError4(5, [], [
12975
+ handleError5(5, [], [
12060
12976
  2,
12061
12977
  5
12062
12978
  /* SyntaxKind.CommaToken */
@@ -12072,7 +12988,7 @@ function visit(text, visitor, options = ParseOptions.DEFAULT) {
12072
12988
  while (_scanner.getToken() !== 2 && _scanner.getToken() !== 17) {
12073
12989
  if (_scanner.getToken() === 5) {
12074
12990
  if (!needsComma) {
12075
- handleError4(4, [], []);
12991
+ handleError5(4, [], []);
12076
12992
  }
12077
12993
  onSeparator(",");
12078
12994
  scanNext();
@@ -12080,10 +12996,10 @@ function visit(text, visitor, options = ParseOptions.DEFAULT) {
12080
12996
  break;
12081
12997
  }
12082
12998
  } else if (needsComma) {
12083
- handleError4(6, [], []);
12999
+ handleError5(6, [], []);
12084
13000
  }
12085
13001
  if (!parseProperty()) {
12086
- handleError4(4, [], [
13002
+ handleError5(4, [], [
12087
13003
  2,
12088
13004
  5
12089
13005
  /* SyntaxKind.CommaToken */
@@ -12093,7 +13009,7 @@ function visit(text, visitor, options = ParseOptions.DEFAULT) {
12093
13009
  }
12094
13010
  onObjectEnd();
12095
13011
  if (_scanner.getToken() !== 2) {
12096
- handleError4(7, [
13012
+ handleError5(7, [
12097
13013
  2
12098
13014
  /* SyntaxKind.CloseBraceToken */
12099
13015
  ], []);
@@ -12110,7 +13026,7 @@ function visit(text, visitor, options = ParseOptions.DEFAULT) {
12110
13026
  while (_scanner.getToken() !== 4 && _scanner.getToken() !== 17) {
12111
13027
  if (_scanner.getToken() === 5) {
12112
13028
  if (!needsComma) {
12113
- handleError4(4, [], []);
13029
+ handleError5(4, [], []);
12114
13030
  }
12115
13031
  onSeparator(",");
12116
13032
  scanNext();
@@ -12118,7 +13034,7 @@ function visit(text, visitor, options = ParseOptions.DEFAULT) {
12118
13034
  break;
12119
13035
  }
12120
13036
  } else if (needsComma) {
12121
- handleError4(6, [], []);
13037
+ handleError5(6, [], []);
12122
13038
  }
12123
13039
  if (isFirstElement) {
12124
13040
  _jsonPath.push(0);
@@ -12127,7 +13043,7 @@ function visit(text, visitor, options = ParseOptions.DEFAULT) {
12127
13043
  _jsonPath[_jsonPath.length - 1]++;
12128
13044
  }
12129
13045
  if (!parseValue()) {
12130
- handleError4(4, [], [
13046
+ handleError5(4, [], [
12131
13047
  4,
12132
13048
  5
12133
13049
  /* SyntaxKind.CommaToken */
@@ -12140,7 +13056,7 @@ function visit(text, visitor, options = ParseOptions.DEFAULT) {
12140
13056
  _jsonPath.pop();
12141
13057
  }
12142
13058
  if (_scanner.getToken() !== 4) {
12143
- handleError4(8, [
13059
+ handleError5(8, [
12144
13060
  4
12145
13061
  /* SyntaxKind.CloseBracketToken */
12146
13062
  ], []);
@@ -12166,15 +13082,15 @@ function visit(text, visitor, options = ParseOptions.DEFAULT) {
12166
13082
  if (options.allowEmptyContent) {
12167
13083
  return true;
12168
13084
  }
12169
- handleError4(4, [], []);
13085
+ handleError5(4, [], []);
12170
13086
  return false;
12171
13087
  }
12172
13088
  if (!parseValue()) {
12173
- handleError4(4, [], []);
13089
+ handleError5(4, [], []);
12174
13090
  return false;
12175
13091
  }
12176
13092
  if (_scanner.getToken() !== 17) {
12177
- handleError4(9, [], []);
13093
+ handleError5(9, [], []);
12178
13094
  }
12179
13095
  return true;
12180
13096
  }
@@ -12233,7 +13149,7 @@ var ParseErrorCode;
12233
13149
 
12234
13150
  // src/validation/config/scope.ts
12235
13151
  import { existsSync as existsSync3, readdirSync, readFileSync as readFileSync3 } from "fs";
12236
- import { isAbsolute as isAbsolute4, join as join26, relative as relative5, resolve as resolve8 } from "path";
13152
+ import { isAbsolute as isAbsolute5, join as join27, relative as relative5, resolve as resolve9 } from "path";
12237
13153
  var TSCONFIG_FILES = {
12238
13154
  full: "tsconfig.json",
12239
13155
  production: "tsconfig.production.json"
@@ -12270,7 +13186,7 @@ var EXPLICIT_TYPESCRIPT_SCOPE_TARGET_KIND = {
12270
13186
  FILE: "file"
12271
13187
  };
12272
13188
  function resolveProjectPath(projectRoot, path7) {
12273
- return isAbsolute4(path7) ? path7 : join26(projectRoot, path7);
13189
+ return isAbsolute5(path7) ? path7 : join27(projectRoot, path7);
12274
13190
  }
12275
13191
  function parseTypeScriptConfig(configPath, deps = defaultScopeDeps) {
12276
13192
  try {
@@ -12309,12 +13225,12 @@ function hasTypeScriptFilesRecursive(dirPath, maxDepth = 2, deps = defaultScopeD
12309
13225
  try {
12310
13226
  const items = deps.readdirSync(dirPath, { withFileTypes: true });
12311
13227
  const hasDirectTsFiles = items.some(
12312
- (item) => item.isFile() && pathHasTypeScriptSourceExtension(item.name) && pathPassesTypeScriptFileDiscoveryExcludes(join26(dirPath, item.name), options)
13228
+ (item) => item.isFile() && pathHasTypeScriptSourceExtension(item.name) && pathPassesTypeScriptFileDiscoveryExcludes(join27(dirPath, item.name), options)
12313
13229
  );
12314
13230
  if (hasDirectTsFiles) return true;
12315
13231
  const subdirs = items.filter((item) => item.isDirectory() && !item.name.startsWith("."));
12316
13232
  for (const subdir of subdirs.slice(0, 5)) {
12317
- if (hasTypeScriptFilesRecursive(join26(dirPath, subdir.name), maxDepth - 1, deps, options)) {
13233
+ if (hasTypeScriptFilesRecursive(join27(dirPath, subdir.name), maxDepth - 1, deps, options)) {
12318
13234
  return true;
12319
13235
  }
12320
13236
  }
@@ -12351,7 +13267,7 @@ function directoryContributesTypeScriptScope(dir, config, projectRoot, deps) {
12351
13267
  return false;
12352
13268
  }
12353
13269
  try {
12354
- return hasTypeScriptFilesRecursive(join26(projectRoot, dir), 2, deps, {
13270
+ return hasTypeScriptFilesRecursive(join27(projectRoot, dir), 2, deps, {
12355
13271
  excludePatterns: config.exclude,
12356
13272
  projectRoot
12357
13273
  });
@@ -12386,7 +13302,7 @@ function directoryPassesIncludePatterns(directory, patterns, projectRoot, deps)
12386
13302
  (pattern) => includePatternTargetsTypeScriptScope(pattern, projectRoot, deps) && typeScriptScopePatternIntersectsDirectory(pattern, directory)
12387
13303
  );
12388
13304
  }
12389
- function stripTrailingPathSeparators(value) {
13305
+ function stripTrailingPathSeparators2(value) {
12390
13306
  let end = value.length;
12391
13307
  while (end > 0 && value.charAt(end - 1) === PATH_SEGMENT_SEPARATOR3) {
12392
13308
  end -= 1;
@@ -12395,7 +13311,7 @@ function stripTrailingPathSeparators(value) {
12395
13311
  }
12396
13312
  function normalizeTypeScriptScopePath(path7) {
12397
13313
  const joined = path7.split(/[\\/]/gu).join(PATH_SEGMENT_SEPARATOR3).replace(/^\.\//u, "");
12398
- return stripTrailingPathSeparators(joined);
13314
+ return stripTrailingPathSeparators2(joined);
12399
13315
  }
12400
13316
  function pathMatchesLiteralPrefix(path7, prefix) {
12401
13317
  const normalizedPath = normalizeTypeScriptScopePath(path7);
@@ -12415,7 +13331,7 @@ function globLiteralPrefix(pattern) {
12415
13331
  if (globIndex === -1) {
12416
13332
  return normalizedPattern;
12417
13333
  }
12418
- return stripTrailingPathSeparators(normalizedPattern.slice(0, globIndex));
13334
+ return stripTrailingPathSeparators2(normalizedPattern.slice(0, globIndex));
12419
13335
  }
12420
13336
  function firstGlobMarkerIndex(path7) {
12421
13337
  const globIndex = path7.indexOf(GLOB_MARKER);
@@ -12580,13 +13496,13 @@ function normalizeActiveIncludePattern(pattern, projectRoot, deps) {
12580
13496
  function filterActiveIncludePatterns(patterns, excludePatterns, projectRoot, deps) {
12581
13497
  return patterns.map((pattern) => normalizeActiveIncludePattern(pattern, projectRoot, deps)).filter((pattern) => typeScriptScopePatternTargetsTypeScriptSource(pattern)).filter((pattern) => {
12582
13498
  const topLevelDir = getLiteralTopLevelPatternDirectory(pattern);
12583
- return topLevelDir === null || deps.existsSync(join26(projectRoot, topLevelDir));
13499
+ return topLevelDir === null || deps.existsSync(join27(projectRoot, topLevelDir));
12584
13500
  }).filter((pattern) => pathPassesValidationFilter(pattern, { exclude: excludePatterns }));
12585
13501
  }
12586
13502
  function getValidationDirectories(scope2, projectRoot, deps = defaultScopeDeps) {
12587
13503
  const config = resolveTypeScriptConfig(scope2, projectRoot, deps);
12588
13504
  const configDirectories = getTopLevelDirectoriesWithTypeScript(config, projectRoot, deps);
12589
- const existingDirectories = configDirectories.filter((dir) => deps.existsSync(join26(projectRoot, dir)));
13505
+ const existingDirectories = configDirectories.filter((dir) => deps.existsSync(join27(projectRoot, dir)));
12590
13506
  return existingDirectories;
12591
13507
  }
12592
13508
  function getTypeScriptScope(scope2, projectRoot, deps = defaultScopeDeps) {
@@ -12607,13 +13523,13 @@ function pathPassesTypeScriptScope(path7, scopeConfig) {
12607
13523
  return included && !excluded;
12608
13524
  }
12609
13525
  function pathStaysInsideTypeScriptScopeRoot(projectRoot, path7) {
12610
- const resolvedPath = isAbsolute4(path7) ? resolve8(path7) : resolve8(projectRoot, path7);
13526
+ const resolvedPath = isAbsolute5(path7) ? resolve9(path7) : resolve9(projectRoot, path7);
12611
13527
  const relativePath = relative5(projectRoot, resolvedPath);
12612
13528
  const segments = normalizeTypeScriptScopePath(relativePath).split(PATH_SEGMENT_SEPARATOR3);
12613
- return relativePath.length === 0 || !segments.includes("..") && !isAbsolute4(relativePath);
13529
+ return relativePath.length === 0 || !segments.includes("..") && !isAbsolute5(relativePath);
12614
13530
  }
12615
13531
  function toProjectRelativeTypeScriptScopePath(projectRoot, path7) {
12616
- const resolvedPath = isAbsolute4(path7) ? resolve8(path7) : resolve8(projectRoot, path7);
13532
+ const resolvedPath = isAbsolute5(path7) ? resolve9(path7) : resolve9(projectRoot, path7);
12617
13533
  const relativePath = relative5(projectRoot, resolvedPath);
12618
13534
  return relativePath.length === 0 ? TYPESCRIPT_SCOPE_PROJECT_ROOT : normalizeTypeScriptScopePath(relativePath);
12619
13535
  }
@@ -12632,7 +13548,10 @@ function filterExplicitTypeScriptScopeTargets(filter, deps = defaultScopeDeps) {
12632
13548
  if (paths === void 0) {
12633
13549
  return void 0;
12634
13550
  }
12635
- return paths.filter((path7) => pathStaysInsideTypeScriptScopeRoot(projectRoot, path7)).map((path7) => toExplicitTypeScriptScopeTarget(projectRoot, path7, deps)).filter((target) => !requireExistingPaths || explicitTypeScriptScopeTargetExists(projectRoot, target, deps)).filter((target) => explicitTypeScriptScopeTargetPassesSourceKind(target)).filter((target) => pathPassesValidationFilter(target.path, validationPathFilter)).filter((target) => explicitTypeScriptScopeTargetPassesScope(target, scopeConfig));
13551
+ return paths.filter((path7) => pathStaysInsideTypeScriptScopeRoot(projectRoot, path7)).map((path7) => toExplicitTypeScriptScopeTarget(projectRoot, path7, deps)).filter((target) => !requireExistingPaths || explicitTypeScriptScopeTargetExists(projectRoot, target, deps)).filter((target) => explicitTypeScriptScopeTargetPassesSourceKind(target)).filter((target) => explicitTypeScriptScopeTargetIntersectsValidationPathFilter(target, validationPathFilter)).filter((target) => explicitTypeScriptScopeTargetPassesScope(target, scopeConfig));
13552
+ }
13553
+ function explicitTypeScriptScopeTargetIntersectsValidationPathFilter(target, validationPathFilter) {
13554
+ return target.kind === EXPLICIT_TYPESCRIPT_SCOPE_TARGET_KIND.DIRECTORY ? pathIntersectsValidationFilter(target.path, validationPathFilter) : pathPassesValidationFilter(target.path, validationPathFilter);
12636
13555
  }
12637
13556
  function explicitTypeScriptScopeTargetPassesSourceKind(target) {
12638
13557
  return target.kind === EXPLICIT_TYPESCRIPT_SCOPE_TARGET_KIND.DIRECTORY || pathHasTypeScriptSourceExtension(target.path);
@@ -12653,10 +13572,12 @@ function explicitTypeScriptScopeTargetPassesScope(target, scopeConfig) {
12653
13572
  if (typeScriptSourcePatterns.length > 0) {
12654
13573
  return typeScriptSourcePatterns.some((pattern) => typeScriptScopePatternIntersectsDirectory(pattern, target.path));
12655
13574
  }
12656
- return pathPassesTypeScriptScope(join26(target.path, TYPESCRIPT_SCOPE_DIRECTORY_PROBE_FILENAME), scopeConfig);
13575
+ return scopeConfig.directories.some(
13576
+ (directory) => pathMatchesLiteralPrefix(directory, target.path) || pathMatchesLiteralPrefix(target.path, directory)
13577
+ ) || pathPassesTypeScriptScope(join27(target.path, TYPESCRIPT_SCOPE_DIRECTORY_PROBE_FILENAME), scopeConfig);
12657
13578
  }
12658
13579
  function directoryPassesTypeScriptExcludes(directory, scopeConfig) {
12659
- const probePath = join26(directory, TYPESCRIPT_SCOPE_DIRECTORY_PROBE_FILENAME);
13580
+ const probePath = join27(directory, TYPESCRIPT_SCOPE_DIRECTORY_PROBE_FILENAME);
12660
13581
  return !scopeConfig.excludePatterns.some(
12661
13582
  (pattern) => typeScriptScopePatternCoversDirectorySourceSet(pattern, directory) || pathMatchesTypeScriptPattern(probePath, pattern)
12662
13583
  );
@@ -12683,6 +13604,15 @@ function constrainTypeScriptScopeToExplicitTargets(scopeConfig, targets) {
12683
13604
  const retainedDirectoryFilePatterns = scopeConfig.filePatterns.filter(
12684
13605
  (pattern) => !typeScriptScopePatternHasGlob(pattern) && pathHasTypeScriptSourceExtension(pattern) && retainedDirectories.some((directory) => pathMatchesLiteralPrefix(pattern, directory))
12685
13606
  );
13607
+ const retainedDirectoryPatterns = scopeConfig.filePatterns.filter(
13608
+ (pattern) => !typeScriptScopePatternHasGlob(pattern) && !pathHasTypeScriptSourceExtension(pattern) && retainedDirectories.some((directory) => pathMatchesLiteralPrefix(pattern, directory))
13609
+ ).map((pattern) => typeScriptLiteralDirectoryPattern(pattern));
13610
+ const retainedDirectoryScopePatterns = retainedDirectories.filter(
13611
+ (directory) => scopeConfig.filePatterns.some(
13612
+ (pattern) => !typeScriptScopePatternHasGlob(pattern) && !pathHasTypeScriptSourceExtension(pattern) && pathMatchesLiteralPrefix(directory, pattern)
13613
+ )
13614
+ ).map((directory) => typeScriptLiteralDirectoryPattern(directory));
13615
+ const retainedDirectoryOperandPatterns = scopeConfig.filePatterns.length === 0 ? retainedDirectories.map(typeScriptLiteralDirectoryPattern) : [];
12686
13616
  const explicitFileTargets = targets.filter((target) => target.kind === EXPLICIT_TYPESCRIPT_SCOPE_TARGET_KIND.FILE).map((target) => target.path).filter(
12687
13617
  (path7) => !retainedDirectories.some(
12688
13618
  (directory) => path7 === directory || path7.startsWith(`${directory}${PATH_SEGMENT_SEPARATOR3}`)
@@ -12695,12 +13625,20 @@ function constrainTypeScriptScopeToExplicitTargets(scopeConfig, targets) {
12695
13625
  ...scopeConfig,
12696
13626
  directories: retainedDirectories,
12697
13627
  filePatterns: [
12698
- ...scopedFilePatternsForDirectoryTargets,
12699
- ...retainedDirectoryFilePatterns,
12700
- ...uncoveredExplicitFileTargets
13628
+ .../* @__PURE__ */ new Set([
13629
+ ...scopedFilePatternsForDirectoryTargets,
13630
+ ...retainedDirectoryFilePatterns,
13631
+ ...retainedDirectoryPatterns,
13632
+ ...retainedDirectoryScopePatterns,
13633
+ ...retainedDirectoryOperandPatterns,
13634
+ ...uncoveredExplicitFileTargets
13635
+ ])
12701
13636
  ]
12702
13637
  };
12703
13638
  }
13639
+ function typeScriptLiteralDirectoryPattern(pattern) {
13640
+ return pattern === TYPESCRIPT_SCOPE_PROJECT_ROOT ? TYPESCRIPT_SCOPE_DIRECTORY_PATTERN_SUFFIX.slice(1) : `${pattern}${TYPESCRIPT_SCOPE_DIRECTORY_PATTERN_SUFFIX}`;
13641
+ }
12704
13642
  function resolveTypeScriptValidationScope(filter, deps = defaultScopeDeps) {
12705
13643
  const scopeConfig = applyValidationPathFilterToScope(
12706
13644
  getTypeScriptScope(filter.scope, filter.projectRoot, deps),
@@ -12847,10 +13785,10 @@ function resolvedModulePath(resolvedPath) {
12847
13785
  return resolvedPath;
12848
13786
  }
12849
13787
  }
12850
- function nearestPackageRoot(filePath, existsSync8) {
13788
+ function nearestPackageRoot(filePath, existsSync9) {
12851
13789
  let currentDirectory = path6.dirname(filePath);
12852
13790
  for (; ; ) {
12853
- if (existsSync8(path6.join(currentDirectory, PACKAGE_MANIFEST_FILENAME))) {
13791
+ if (existsSync9(path6.join(currentDirectory, PACKAGE_MANIFEST_FILENAME))) {
12854
13792
  return currentDirectory;
12855
13793
  }
12856
13794
  const parentDirectory = path6.dirname(currentDirectory);
@@ -12860,9 +13798,9 @@ function nearestPackageRoot(filePath, existsSync8) {
12860
13798
  currentDirectory = parentDirectory;
12861
13799
  }
12862
13800
  }
12863
- function bundledToolPath(resolvedPath, existsSync8) {
13801
+ function bundledToolPath(resolvedPath, existsSync9) {
12864
13802
  const bundledFilePath = resolvedModulePath(resolvedPath);
12865
- return nearestPackageRoot(bundledFilePath, existsSync8) ?? path6.dirname(bundledFilePath);
13803
+ return nearestPackageRoot(bundledFilePath, existsSync9) ?? path6.dirname(bundledFilePath);
12866
13804
  }
12867
13805
  async function discoverTool(tool, options = {}) {
12868
13806
  const { projectRoot = CONFIG_PROCESS_CWD.read(), deps = defaultToolDiscoveryDeps } = options;
@@ -12919,7 +13857,7 @@ import {
12919
13857
  cruise as dependencyCruiser
12920
13858
  } from "dependency-cruiser";
12921
13859
  import extractTypeScriptConfig from "dependency-cruiser/config-utl/extract-ts-config";
12922
- import { join as join27 } from "path";
13860
+ import { join as join28 } from "path";
12923
13861
  var DEPENDENCY_CRUISER_MODULE_SYSTEMS = ["es6", "cjs"];
12924
13862
  var DEPENDENCY_CRUISER_TYPESCRIPT_SOURCE_GLOB_SUFFIXES = [...TYPESCRIPT_FALLBACK_INCLUDE_PATTERNS];
12925
13863
  var DEPENDENCY_CRUISER_TYPESCRIPT_SOURCE_PATTERN = String.raw`(?<!\.d)\.(?:[cm]?ts|tsx)$`;
@@ -13159,7 +14097,7 @@ async function validateCircularDependencies(scope2, typescriptScope, projectRoot
13159
14097
  if (analyzeSourcePatterns.length === 0) {
13160
14098
  return { success: true };
13161
14099
  }
13162
- const tsConfigFile = join27(projectRoot, TSCONFIG_FILES[scope2]);
14100
+ const tsConfigFile = join28(projectRoot, TSCONFIG_FILES[scope2]);
13163
14101
  const result = await deps.dependencyCruiser(
13164
14102
  analyzeSourcePatterns,
13165
14103
  buildDependencyCruiserOptions(typescriptScope, projectRoot, tsConfigFile),
@@ -13281,7 +14219,7 @@ async function circularCommand(options, deps = defaultCircularCommandDeps) {
13281
14219
  // src/validation/steps/knip.ts
13282
14220
  import { existsSync as existsSync4 } from "fs";
13283
14221
  import { mkdir as mkdir5, mkdtemp as mkdtemp2, rm as rm2, writeFile as writeFile3 } from "fs/promises";
13284
- import { isAbsolute as isAbsolute5, join as join28 } from "path";
14222
+ import { isAbsolute as isAbsolute6, join as join29 } from "path";
13285
14223
  var defaultKnipProcessRunner = lifecycleProcessRunner;
13286
14224
  var KNIP_COMMAND_TOKENS = {
13287
14225
  COMMAND: "knip",
@@ -13314,7 +14252,7 @@ async function validateKnip2(context, runner = defaultKnipProcessRunner, deps =
13314
14252
  }
13315
14253
  async function runKnipSubprocess(projectRoot, typescriptScope, runner, deps) {
13316
14254
  const scopedTsconfig = typescriptScope.filteredByValidationPaths ? await createScopedKnipTsconfig(projectRoot, typescriptScope, deps) : void 0;
13317
- const localBin = join28(projectRoot, "node_modules", ".bin", "knip");
14255
+ const localBin = join29(projectRoot, "node_modules", ".bin", "knip");
13318
14256
  const binary = deps.existsSync(localBin) ? localBin : KNIP_COMMAND_TOKENS.NPX_COMMAND;
13319
14257
  const baseArgs = scopedTsconfig === void 0 ? [] : [
13320
14258
  KNIP_COMMAND_TOKENS.USE_TSCONFIG_FILES_FLAG,
@@ -13344,13 +14282,13 @@ async function runKnipSubprocess(projectRoot, typescriptScope, runner, deps) {
13344
14282
  knipProcess.stderr?.on("data", (data) => {
13345
14283
  knipError += data.toString();
13346
14284
  });
13347
- return new Promise((resolve10) => {
14285
+ return new Promise((resolve12) => {
13348
14286
  const resolveAfterCleanup = (result) => {
13349
14287
  if (resultResolved) {
13350
14288
  return;
13351
14289
  }
13352
14290
  resultResolved = true;
13353
- void cleanupOnce().finally(() => resolve10(result));
14291
+ void cleanupOnce().finally(() => resolve12(result));
13354
14292
  };
13355
14293
  knipProcess.on("close", (code) => {
13356
14294
  if (code === 0) {
@@ -13369,11 +14307,11 @@ async function runKnipSubprocess(projectRoot, typescriptScope, runner, deps) {
13369
14307
  });
13370
14308
  }
13371
14309
  async function createScopedKnipTsconfig(projectRoot, typescriptScope, deps) {
13372
- const tempParentDir = join28(projectRoot, ...TEMPORARY_TSCONFIG_PARENT_SEGMENTS);
14310
+ const tempParentDir = join29(projectRoot, ...TEMPORARY_TSCONFIG_PARENT_SEGMENTS);
13373
14311
  await deps.mkdir(tempParentDir, { recursive: true });
13374
- const tempDir = await deps.mkdtemp(join28(tempParentDir, "validate-knip-"));
13375
- const configPath = join28(tempDir, TSCONFIG_FILES.full);
13376
- const toProjectPathPattern = (pattern) => isAbsolute5(pattern) ? pattern : join28(projectRoot, pattern);
14312
+ const tempDir = await deps.mkdtemp(join29(tempParentDir, "validate-knip-"));
14313
+ const configPath = join29(tempDir, TSCONFIG_FILES.full);
14314
+ const toProjectPathPattern = (pattern) => isAbsolute6(pattern) ? pattern : join29(projectRoot, pattern);
13377
14315
  const project = [
13378
14316
  ...typescriptScope.directories.flatMap(
13379
14317
  (directory) => TYPESCRIPT_FALLBACK_INCLUDE_PATTERNS.map((pattern) => `${directory}/${pattern}`)
@@ -13381,7 +14319,7 @@ async function createScopedKnipTsconfig(projectRoot, typescriptScope, deps) {
13381
14319
  ...typescriptScope.filePatterns
13382
14320
  ];
13383
14321
  const config = {
13384
- extends: join28(projectRoot, TSCONFIG_FILES.full),
14322
+ extends: join29(projectRoot, TSCONFIG_FILES.full),
13385
14323
  include: project.map(toProjectPathPattern),
13386
14324
  exclude: typescriptScope.excludePatterns.map(toProjectPathPattern)
13387
14325
  };
@@ -13448,12 +14386,12 @@ async function knipCommand(options, deps = defaultKnipCommandDeps) {
13448
14386
 
13449
14387
  // src/validation/steps/eslint.ts
13450
14388
  import { existsSync as existsSync6 } from "fs";
13451
- import { join as join30 } from "path";
14389
+ import { join as join31 } from "path";
13452
14390
 
13453
14391
  // src/validation/lint-policy.ts
13454
14392
  import { execFileSync as execFileSync2 } from "child_process";
13455
- import { existsSync as existsSync5, readdirSync as readdirSync2, readFileSync as readFileSync4, statSync as statSync2 } from "fs";
13456
- import { join as join29 } from "path";
14393
+ import { existsSync as existsSync5, readdirSync as readdirSync2, readFileSync as readFileSync4, statSync as statSync3 } from "fs";
14394
+ import { join as join30 } from "path";
13457
14395
 
13458
14396
  // src/validation/lint-policy-constants.ts
13459
14397
  var LINT_POLICY_MANIFESTS = {
@@ -13501,17 +14439,17 @@ function isSpecTreeNodePath(entry) {
13501
14439
  }
13502
14440
  function readManifest2(productDir, file, key) {
13503
14441
  return parseLintPolicyManifest(
13504
- readFileSync4(join29(productDir, file), "utf-8"),
14442
+ readFileSync4(join30(productDir, file), "utf-8"),
13505
14443
  file,
13506
14444
  key
13507
14445
  );
13508
14446
  }
13509
14447
  function manifestExists(productDir, file) {
13510
- return existsSync5(join29(productDir, file));
14448
+ return existsSync5(join30(productDir, file));
13511
14449
  }
13512
14450
  function findDeprecatedSpecNodePath(productDir) {
13513
14451
  function visit2(relativeDirectory) {
13514
- const absoluteDirectory = join29(productDir, relativeDirectory);
14452
+ const absoluteDirectory = join30(productDir, relativeDirectory);
13515
14453
  for (const entry of readdirSync2(absoluteDirectory, { withFileTypes: true })) {
13516
14454
  if (!entry.isDirectory()) {
13517
14455
  continue;
@@ -13527,7 +14465,7 @@ function findDeprecatedSpecNodePath(productDir) {
13527
14465
  }
13528
14466
  return void 0;
13529
14467
  }
13530
- const specTreeRootPath = join29(productDir, SPEC_TREE_ROOT);
14468
+ const specTreeRootPath = join30(productDir, SPEC_TREE_ROOT);
13531
14469
  if (!existsSync5(specTreeRootPath)) {
13532
14470
  return void 0;
13533
14471
  }
@@ -13553,8 +14491,8 @@ function assertManifestEntries(productDir, file, entries, suffixPredicate, suffi
13553
14491
  if (!suffixPredicate(entry)) {
13554
14492
  throw new Error(`${file} entry must ${suffixDescription}: ${entry}`);
13555
14493
  }
13556
- const absoluteEntry = join29(productDir, entry);
13557
- if (!existsSync5(absoluteEntry) || !statSync2(absoluteEntry).isDirectory()) {
14494
+ const absoluteEntry = join30(productDir, entry);
14495
+ if (!existsSync5(absoluteEntry) || !statSync3(absoluteEntry).isDirectory()) {
13558
14496
  throw new Error(`${file} entry does not exist as a directory: ${entry}`);
13559
14497
  }
13560
14498
  }
@@ -13715,7 +14653,14 @@ var ESLINT_COMMAND_TOKENS = {
13715
14653
  };
13716
14654
  var ESLINT_LOCAL_BIN_SEGMENTS = ["node_modules", ".bin", ESLINT_COMMAND_TOKENS.COMMAND];
13717
14655
  function buildEslintArgs(context) {
13718
- const { validatedFiles, mode, configFile = DEFAULT_ESLINT_CONFIG_FILE, scope: scope2, scopeConfig } = context;
14656
+ const {
14657
+ validatedFiles,
14658
+ validatedFileIgnorePatterns = [],
14659
+ mode,
14660
+ configFile = DEFAULT_ESLINT_CONFIG_FILE,
14661
+ scope: scope2,
14662
+ scopeConfig
14663
+ } = context;
13719
14664
  const fixArg = mode === EXECUTION_MODES.WRITE ? [ESLINT_COMMAND_TOKENS.FIX_FLAG] : [];
13720
14665
  if (validatedFiles && validatedFiles.length > 0) {
13721
14666
  return [
@@ -13723,6 +14668,7 @@ function buildEslintArgs(context) {
13723
14668
  ESLINT_COMMAND_TOKENS.CONFIG_FLAG,
13724
14669
  configFile,
13725
14670
  ...fixArg,
14671
+ ...buildIgnorePatternArgs(validatedFileIgnorePatterns),
13726
14672
  ESLINT_COMMAND_TOKENS.FILE_SEPARATOR,
13727
14673
  ...validatedFiles
13728
14674
  ];
@@ -13763,13 +14709,14 @@ async function validateESLint(context, runner = defaultEslintProcessRunner, outp
13763
14709
  }
13764
14710
  const eslintArgs = buildEslintArgs({
13765
14711
  validatedFiles,
14712
+ validatedFileIgnorePatterns: context.validatedFileIgnorePatterns,
13766
14713
  mode,
13767
14714
  configFile: eslintConfigFile,
13768
14715
  scope: scope2,
13769
14716
  scopeConfig: context.scopeConfig
13770
14717
  });
13771
- return new Promise((resolve10) => {
13772
- const localBin = join30(projectRoot, ...ESLINT_LOCAL_BIN_SEGMENTS);
14718
+ return new Promise((resolve12) => {
14719
+ const localBin = join31(projectRoot, ...ESLINT_LOCAL_BIN_SEGMENTS);
13773
14720
  const binary = existsSync6(localBin) ? localBin : "npx";
13774
14721
  const spawnArgs = binary === "npx" ? eslintArgs : eslintArgs.slice(1);
13775
14722
  const eslintProcess = spawnManagedSubprocess(runner, binary, spawnArgs, {
@@ -13778,13 +14725,13 @@ async function validateESLint(context, runner = defaultEslintProcessRunner, outp
13778
14725
  forwardValidationSubprocessOutput(eslintProcess, outputStreams);
13779
14726
  eslintProcess.on(VALIDATION_SUBPROCESS_EVENTS.CLOSE, (code) => {
13780
14727
  if (code === 0) {
13781
- resolve10({ success: true });
14728
+ resolve12({ success: true });
13782
14729
  } else {
13783
- resolve10({ success: false, error: `ESLint exited with code ${code}` });
14730
+ resolve12({ success: false, error: `ESLint exited with code ${code}` });
13784
14731
  }
13785
14732
  });
13786
14733
  eslintProcess.on(VALIDATION_SUBPROCESS_EVENTS.ERROR, (error) => {
13787
- resolve10({ success: false, error: error.message });
14734
+ resolve12({ success: false, error: error.message });
13788
14735
  });
13789
14736
  });
13790
14737
  }
@@ -13832,7 +14779,12 @@ async function lintCommand(options) {
13832
14779
  getTypeScriptScope(scope2, cwd),
13833
14780
  validationPathFilter
13834
14781
  );
13835
- const validatedFiles = files?.map((file) => toProjectRelativeValidationPath(cwd, file)).filter((file) => pathPassesValidationFilter(file, validationPathFilter));
14782
+ const validatedFiles = files?.flatMap(
14783
+ (file) => validationPathFilterIntersections(
14784
+ toProjectRelativeValidationPath(cwd, file),
14785
+ validationPathFilter
14786
+ ).map(formatLintValidationOperand)
14787
+ );
13836
14788
  if (scopeConfig.filteredByValidationPathNoMatches || files !== void 0 && files.length > 0 && validatedFiles?.length === 0) {
13837
14789
  return {
13838
14790
  exitCode: 0,
@@ -13852,6 +14804,7 @@ async function lintCommand(options) {
13852
14804
  mode: fix ? "write" : "read",
13853
14805
  enabledValidations: { ESLINT: true },
13854
14806
  validatedFiles,
14807
+ validatedFileIgnorePatterns: files === void 0 ? void 0 : validationPathFilterExcludes(validationPathFilter),
13855
14808
  isFileSpecificMode: Boolean(validatedFiles && validatedFiles.length > 0),
13856
14809
  eslintConfigFile
13857
14810
  };
@@ -13859,6 +14812,9 @@ async function lintCommand(options) {
13859
14812
  const durationMs = Date.now() - startTime;
13860
14813
  return formatLintResult(result, quiet, durationMs);
13861
14814
  }
14815
+ function formatLintValidationOperand(path7) {
14816
+ return path7.length === 0 ? "." : path7;
14817
+ }
13862
14818
  function formatLintResult(result, quiet, durationMs) {
13863
14819
  if (result.skipped) {
13864
14820
  const output2 = quiet ? "" : VALIDATION_PATHS_NO_TARGETS_MESSAGE;
@@ -13873,8 +14829,8 @@ function formatLintResult(result, quiet, durationMs) {
13873
14829
  }
13874
14830
 
13875
14831
  // src/validation/literal/index.ts
13876
- import { readFile as readFile9 } from "fs/promises";
13877
- import { isAbsolute as isAbsolute6, relative as relative7, resolve as resolve9 } from "path";
14832
+ import { readFile as readFile10 } from "fs/promises";
14833
+ import { isAbsolute as isAbsolute7, relative as relative7, resolve as resolve10 } from "path";
13878
14834
 
13879
14835
  // src/lib/file-inclusion/predicates/ignore-source.ts
13880
14836
  var IGNORE_SOURCE_LAYER = "ignore-source";
@@ -13908,8 +14864,8 @@ var ignoreSourceLayer = makeLayer(
13908
14864
  );
13909
14865
 
13910
14866
  // src/lib/file-inclusion/pipeline.ts
13911
- import { readdir as readdir8 } from "fs/promises";
13912
- import { join as join31, relative as relative6, sep as sep2 } from "path";
14867
+ import { readdir as readdir9 } from "fs/promises";
14868
+ import { join as join32, relative as relative6, sep as sep2 } from "path";
13913
14869
  var EXPLICIT_OVERRIDE_LAYER = "explicit-override";
13914
14870
  function isNodeError4(err) {
13915
14871
  return err instanceof Error && "code" in err;
@@ -13917,7 +14873,7 @@ function isNodeError4(err) {
13917
14873
  async function collectPaths(absoluteDir, projectRoot, result, artifactDirs) {
13918
14874
  let dirEntries;
13919
14875
  try {
13920
- dirEntries = await readdir8(absoluteDir, { withFileTypes: true });
14876
+ dirEntries = await readdir9(absoluteDir, { withFileTypes: true });
13921
14877
  } catch (err) {
13922
14878
  if (isNodeError4(err) && (err.code === "ENOENT" || err.code === "ENOTDIR")) {
13923
14879
  return;
@@ -13927,10 +14883,10 @@ async function collectPaths(absoluteDir, projectRoot, result, artifactDirs) {
13927
14883
  for (const entry of dirEntries) {
13928
14884
  if (entry.isDirectory()) {
13929
14885
  if (artifactDirs.has(entry.name)) continue;
13930
- const absolutePath = join31(absoluteDir, entry.name);
14886
+ const absolutePath = join32(absoluteDir, entry.name);
13931
14887
  await collectPaths(absolutePath, projectRoot, result, artifactDirs);
13932
14888
  } else if (entry.isFile()) {
13933
- const absolutePath = join31(absoluteDir, entry.name);
14889
+ const absolutePath = join32(absoluteDir, entry.name);
13934
14890
  const rel = relative6(projectRoot, absolutePath);
13935
14891
  result.push(sep2 === "/" ? rel : rel.split(sep2).join("/"));
13936
14892
  }
@@ -14414,37 +15370,22 @@ function isTestFile(relPath) {
14414
15370
  }
14415
15371
 
14416
15372
  // src/validation/literal/index.ts
14417
- var PATH_PREFIX_SEPARATOR2 = "/";
14418
15373
  var DEFAULT_LITERAL_COLLECT_OPTIONS = {
14419
15374
  visitorKeys: defaultVisitorKeys,
14420
15375
  minStringLength: literalConfigDescriptor.defaults.minStringLength,
14421
15376
  minNumberDigits: literalConfigDescriptor.defaults.minNumberDigits
14422
15377
  };
14423
- function normalizePathPrefix3(prefix) {
14424
- const posix = prefix.split(/[\\/]/g).join(PATH_PREFIX_SEPARATOR2);
14425
- return posix.endsWith(PATH_PREFIX_SEPARATOR2) ? posix : `${posix}${PATH_PREFIX_SEPARATOR2}`;
14426
- }
14427
15378
  function applyPathFilter2(entries, pathConfig) {
14428
15379
  if (pathConfig === void 0) {
14429
15380
  return entries;
14430
15381
  }
14431
- const includePrefixes = (pathConfig.include ?? []).map(normalizePathPrefix3);
14432
- const excludePrefixes = (pathConfig.exclude ?? []).map(normalizePathPrefix3);
14433
- return entries.filter((entry) => {
14434
- if (includePrefixes.length > 0 && !includePrefixes.some((p) => entry.path.startsWith(p))) {
14435
- return false;
14436
- }
14437
- if (excludePrefixes.some((p) => entry.path.startsWith(p))) {
14438
- return false;
14439
- }
14440
- return true;
14441
- });
15382
+ return entries.filter((entry) => pathPassesValidationFilter(entry.path, pathConfig));
14442
15383
  }
14443
15384
  async function validateLiteralReuse(input) {
14444
15385
  const config = input.config ?? literalConfigDescriptor.defaults;
14445
- const request = input.files ? {
15386
+ const request = input.scopeConfig === void 0 && input.files ? {
14446
15387
  explicit: input.files.map((f) => {
14447
- const abs = isAbsolute6(f) ? f : resolve9(input.productDir, f);
15388
+ const abs = isAbsolute7(f) ? f : resolve10(input.productDir, f);
14448
15389
  return relative7(input.productDir, abs).split(/[\\/]/g).join("/");
14449
15390
  })
14450
15391
  } : { walkRoot: input.productDir };
@@ -14455,8 +15396,10 @@ async function validateLiteralReuse(input) {
14455
15396
  DEFAULT_SCOPE_CONFIG,
14456
15397
  EMPTY_IGNORE_READER
14457
15398
  );
14458
- const filtered = applyPathFilter2(scope2.included, input.pathConfig);
14459
- const candidateFiles = filtered.filter((entry) => isTypescriptSource(entry.path)).map((entry) => resolve9(input.productDir, entry.path));
15399
+ const pathFiltered = applyPathFilter2(scope2.included, input.pathConfig);
15400
+ const literalScopeConfig = input.scopeConfig;
15401
+ const filtered = literalScopeConfig === void 0 ? pathFiltered : pathFiltered.filter((entry) => pathPassesTypeScriptScope(entry.path, literalScopeConfig));
15402
+ const candidateFiles = filtered.filter((entry) => isTypescriptSource(entry.path)).map((entry) => resolve10(input.productDir, entry.path));
14460
15403
  const collectOptions = {
14461
15404
  visitorKeys: defaultVisitorKeys,
14462
15405
  minStringLength: config.minStringLength,
@@ -14483,11 +15426,15 @@ async function validateLiteralReuse(input) {
14483
15426
  testOccurrencesByFile,
14484
15427
  allowlist: resolveAllowlist(config)
14485
15428
  });
14486
- return { findings, indexedOccurrencesByFile };
15429
+ return {
15430
+ findings,
15431
+ indexedOccurrencesByFile,
15432
+ filteredByValidationPathNoMatches: input.scopeConfig?.filteredByValidationPathNoMatches
15433
+ };
14487
15434
  }
14488
15435
  async function readSafe(path7) {
14489
15436
  try {
14490
- return await readFile9(path7, "utf8");
15437
+ return await readFile10(path7, "utf8");
14491
15438
  } catch (err) {
14492
15439
  if (typeof err === "object" && err !== null && "code" in err) {
14493
15440
  const code = err.code;
@@ -14518,6 +15465,9 @@ var LITERAL_EXIT_CODES = {
14518
15465
  var TYPESCRIPT_ABSENT_MESSAGE3 = formatTypeScriptAbsentSkipMessage(
14519
15466
  VALIDATION_STAGE_DISPLAY_NAMES.LITERAL
14520
15467
  );
15468
+ var VALIDATION_PATHS_NO_TARGETS_MESSAGE2 = formatValidationPathsNoTargetsSkipMessage(
15469
+ VALIDATION_STAGE_DISPLAY_NAMES.LITERAL
15470
+ );
14521
15471
  var LITERAL_DISABLED_MESSAGE = `\u23ED ${VALIDATION_SKIP_LABELS.VERB} ${VALIDATION_STAGE_DISPLAY_NAMES.LITERAL} (${VALIDATION_SKIP_LABELS.DISABLED_BY_PREFIX} validation.literal.enabled)`;
14522
15472
  var NO_PROBLEMS_MESSAGE = "Literal: \u2713 No problems";
14523
15473
  function formatNoProblemsOfKind(kind) {
@@ -14533,31 +15483,15 @@ async function literalCommand(options) {
14533
15483
  durationMs: Date.now() - start
14534
15484
  };
14535
15485
  }
14536
- let resolvedEnabled;
14537
- let resolvedLiteralConfig;
14538
- let resolvedPathConfig;
14539
- if (options.config === void 0) {
14540
- const loaded = await resolveConfig(options.cwd, [validationConfigDescriptor]);
14541
- if (!loaded.ok) {
14542
- return {
14543
- exitCode: LITERAL_EXIT_CODES.CONFIG_ERROR,
14544
- output: `Literal: \u2717 config error \u2014 ${loaded.error}`,
14545
- durationMs: Date.now() - start
14546
- };
14547
- }
14548
- const validationConfig = loaded.value[validationConfigDescriptor.section];
14549
- resolvedEnabled = validationConfig.literal.enabled;
14550
- resolvedLiteralConfig = validationConfig.literal.values;
14551
- resolvedPathConfig = validationPathFilterForTool(
14552
- validationConfig.paths,
14553
- VALIDATION_PATH_TOOL_SUBSECTIONS.LITERAL
14554
- );
14555
- } else {
14556
- resolvedEnabled = options.enabled ?? validationConfigDescriptor.defaults.literal.enabled;
14557
- resolvedLiteralConfig = options.config;
14558
- resolvedPathConfig = options.pathConfig ?? validationConfigDescriptor.defaults.paths;
15486
+ const resolved = await resolveLiteralCommandConfig(options);
15487
+ if (typeof resolved === "string") {
15488
+ return {
15489
+ exitCode: LITERAL_EXIT_CODES.CONFIG_ERROR,
15490
+ output: `Literal: \u2717 config error \u2014 ${resolved}`,
15491
+ durationMs: Date.now() - start
15492
+ };
14559
15493
  }
14560
- if (!resolvedEnabled) {
15494
+ if (!resolved.enabled) {
14561
15495
  return {
14562
15496
  exitCode: LITERAL_EXIT_CODES.OK,
14563
15497
  output: options.quiet ? "" : LITERAL_DISABLED_MESSAGE,
@@ -14566,10 +15500,17 @@ async function literalCommand(options) {
14566
15500
  }
14567
15501
  const result = await validateLiteralReuse({
14568
15502
  productDir: options.cwd,
14569
- files: options.files,
14570
- config: resolvedLiteralConfig,
14571
- pathConfig: resolvedPathConfig
15503
+ config: resolved.literalConfig,
15504
+ pathConfig: resolved.pathConfig,
15505
+ scopeConfig: resolveExplicitLiteralTypeScriptScope(options, resolved.pathConfig)
14572
15506
  });
15507
+ if (options.files !== void 0 && options.files.length > 0 && result.filteredByValidationPathNoMatches) {
15508
+ return {
15509
+ exitCode: LITERAL_EXIT_CODES.OK,
15510
+ output: options.quiet ? "" : VALIDATION_PATHS_NO_TARGETS_MESSAGE2,
15511
+ durationMs: Date.now() - start
15512
+ };
15513
+ }
14573
15514
  const filteredFindings = filterLiteralFindings(result.findings, options.kind);
14574
15515
  const totalProblems = countLiteralProblems(filteredFindings);
14575
15516
  const exitCode = totalProblems === 0 ? LITERAL_EXIT_CODES.OK : LITERAL_EXIT_CODES.FINDINGS;
@@ -14583,6 +15524,38 @@ async function literalCommand(options) {
14583
15524
  }
14584
15525
  return { exitCode, output, durationMs: Date.now() - start };
14585
15526
  }
15527
+ async function resolveLiteralCommandConfig(options) {
15528
+ if (options.config !== void 0) {
15529
+ return {
15530
+ enabled: options.enabled ?? validationConfigDescriptor.defaults.literal.enabled,
15531
+ literalConfig: options.config,
15532
+ pathConfig: options.pathConfig ?? validationConfigDescriptor.defaults.paths
15533
+ };
15534
+ }
15535
+ const loaded = await resolveConfig(options.cwd, [validationConfigDescriptor]);
15536
+ if (!loaded.ok) return loaded.error;
15537
+ const validationConfig = loaded.value[validationConfigDescriptor.section];
15538
+ return {
15539
+ enabled: validationConfig.literal.enabled,
15540
+ literalConfig: validationConfig.literal.values,
15541
+ pathConfig: validationPathFilterForTool(
15542
+ validationConfig.paths,
15543
+ VALIDATION_PATH_TOOL_SUBSECTIONS.LITERAL
15544
+ )
15545
+ };
15546
+ }
15547
+ function resolveExplicitLiteralTypeScriptScope(options, pathConfig) {
15548
+ if (options.files === void 0 || options.files.length === 0) {
15549
+ return void 0;
15550
+ }
15551
+ return resolveTypeScriptValidationScope({
15552
+ projectRoot: options.cwd,
15553
+ scope: options.scope ?? VALIDATION_SCOPES.FULL,
15554
+ paths: options.files,
15555
+ validationPathFilter: pathConfig,
15556
+ markExplicitPathsAsValidationFilter: true
15557
+ });
15558
+ }
14586
15559
  function filterLiteralFindings(findings, kind) {
14587
15560
  return {
14588
15561
  srcReuse: kind === LITERAL_PROBLEM_KIND.DUPE ? [] : sortReuseFindings(findings.srcReuse),
@@ -14706,7 +15679,7 @@ function formatLoc(loc) {
14706
15679
  // src/validation/steps/typescript.ts
14707
15680
  import { existsSync as existsSync7, mkdirSync, rmSync, writeFileSync } from "fs";
14708
15681
  import { mkdtemp as mkdtemp3 } from "fs/promises";
14709
- import { isAbsolute as isAbsolute7, join as join32 } from "path";
15682
+ import { isAbsolute as isAbsolute8, join as join33, relative as relative8 } from "path";
14710
15683
  var defaultTypeScriptProcessRunner = lifecycleProcessRunner;
14711
15684
  var defaultTypeScriptDeps = {
14712
15685
  mkdtemp: mkdtemp3,
@@ -14718,9 +15691,9 @@ var defaultTypeScriptDeps = {
14718
15691
  var TEMPORARY_TSCONFIG_COMPILER_OPTIONS = { noEmit: true };
14719
15692
  var TEMPORARY_TSCONFIG_DIR_PREFIX = "validate-ts-";
14720
15693
  async function createTemporaryTsconfigDir(projectRoot, deps) {
14721
- const parent = join32(projectRoot, ...TEMPORARY_TSCONFIG_PARENT_SEGMENTS);
15694
+ const parent = join33(projectRoot, ...TEMPORARY_TSCONFIG_PARENT_SEGMENTS);
14722
15695
  deps.mkdirSync(parent, { recursive: true });
14723
- return deps.mkdtemp(join32(parent, TEMPORARY_TSCONFIG_DIR_PREFIX));
15696
+ return deps.mkdtemp(join33(parent, TEMPORARY_TSCONFIG_DIR_PREFIX));
14724
15697
  }
14725
15698
  function buildTypeScriptArgs(context) {
14726
15699
  const { scope: scope2, configFile } = context;
@@ -14728,11 +15701,11 @@ function buildTypeScriptArgs(context) {
14728
15701
  }
14729
15702
  async function createFileSpecificTsconfig(scope2, files, projectRoot, deps = defaultTypeScriptDeps) {
14730
15703
  const tempDir = await createTemporaryTsconfigDir(projectRoot, deps);
14731
- const configPath = join32(tempDir, "tsconfig.json");
15704
+ const configPath = join33(tempDir, "tsconfig.json");
14732
15705
  const baseConfigFile = TSCONFIG_FILES[scope2];
14733
- const absoluteFiles = files.map((file) => isAbsolute7(file) ? file : join32(projectRoot, file));
15706
+ const absoluteFiles = files.map((file) => isAbsolute8(file) ? file : join33(projectRoot, file));
14734
15707
  const tempConfig = {
14735
- extends: join32(projectRoot, baseConfigFile),
15708
+ extends: join33(projectRoot, baseConfigFile),
14736
15709
  files: absoluteFiles,
14737
15710
  include: [],
14738
15711
  exclude: [],
@@ -14744,13 +15717,16 @@ async function createFileSpecificTsconfig(scope2, files, projectRoot, deps = def
14744
15717
  }
14745
15718
  async function createScopeFilteredTsconfig(scope2, projectRoot, scopeConfig, deps = defaultTypeScriptDeps) {
14746
15719
  const tempDir = await createTemporaryTsconfigDir(projectRoot, deps);
14747
- const configPath = join32(tempDir, "tsconfig.json");
15720
+ const configPath = join33(tempDir, "tsconfig.json");
14748
15721
  const baseConfigFile = TSCONFIG_FILES[scope2];
14749
- const toProjectPathPattern = (pattern) => isAbsolute7(pattern) ? pattern : join32(projectRoot, pattern);
15722
+ const toTemporaryConfigPathPattern = (pattern) => {
15723
+ const absolutePattern = isAbsolute8(pattern) ? pattern : join33(projectRoot, pattern);
15724
+ return relative8(tempDir, absolutePattern);
15725
+ };
14750
15726
  const tempConfig = {
14751
- extends: join32(projectRoot, baseConfigFile),
14752
- include: scopeConfig.filePatterns.map(toProjectPathPattern),
14753
- exclude: scopeConfig.excludePatterns.map(toProjectPathPattern),
15727
+ extends: join33(projectRoot, baseConfigFile),
15728
+ include: scopeConfig.filePatterns.map(toTemporaryConfigPathPattern),
15729
+ exclude: scopeConfig.excludePatterns.map(toTemporaryConfigPathPattern),
14754
15730
  compilerOptions: TEMPORARY_TSCONFIG_COMPILER_OPTIONS
14755
15731
  };
14756
15732
  deps.writeFileSync(configPath, JSON.stringify(tempConfig, null, 2));
@@ -14812,7 +15788,7 @@ async function validateTypeScript(context, options = {}) {
14812
15788
  );
14813
15789
  }
14814
15790
  function resolveProjectTscInvocation(projectRoot, deps, tscArgs) {
14815
- const tscBin = join32(projectRoot, "node_modules", ".bin", "tsc");
15791
+ const tscBin = join33(projectRoot, "node_modules", ".bin", "tsc");
14816
15792
  const tool = deps.existsSync(tscBin) ? tscBin : "npx";
14817
15793
  return {
14818
15794
  tool,
@@ -14821,7 +15797,7 @@ function resolveProjectTscInvocation(projectRoot, deps, tscArgs) {
14821
15797
  }
14822
15798
  function runTypeScriptInvocation(projectRoot, invocation, runner, outputStreams, cleanup = () => {
14823
15799
  }) {
14824
- return new Promise((resolve10) => {
15800
+ return new Promise((resolve12) => {
14825
15801
  const tscProcess = spawnManagedSubprocess(runner, invocation.tool, invocation.args, {
14826
15802
  cwd: projectRoot
14827
15803
  });
@@ -14829,14 +15805,14 @@ function runTypeScriptInvocation(projectRoot, invocation, runner, outputStreams,
14829
15805
  tscProcess.on(VALIDATION_SUBPROCESS_EVENTS.CLOSE, (code) => {
14830
15806
  cleanup();
14831
15807
  if (code === 0) {
14832
- resolve10({ success: true, skipped: false });
15808
+ resolve12({ success: true, skipped: false });
14833
15809
  } else {
14834
- resolve10({ success: false, error: `TypeScript exited with code ${code}` });
15810
+ resolve12({ success: false, error: `TypeScript exited with code ${code}` });
14835
15811
  }
14836
15812
  });
14837
15813
  tscProcess.on(VALIDATION_SUBPROCESS_EVENTS.ERROR, (error) => {
14838
15814
  cleanup();
14839
- resolve10({ success: false, error: error.message });
15815
+ resolve12({ success: false, error: error.message });
14840
15816
  });
14841
15817
  });
14842
15818
  }
@@ -14869,16 +15845,17 @@ async function typescriptCommand(options) {
14869
15845
  };
14870
15846
  }
14871
15847
  const validationConfig = loaded.value[validationConfigDescriptor.section];
14872
- const validationPathFilter = validationPathFilterForTool(
14873
- validationConfig.paths,
14874
- VALIDATION_PATH_TOOL_SUBSECTIONS.TYPESCRIPT
14875
- );
14876
- const scopeConfig = applyValidationPathFilterToScope(
14877
- getTypeScriptScope(scope2, cwd),
14878
- validationPathFilter
14879
- );
14880
- const filteredFiles = files?.map((file) => toProjectRelativeValidationPath(cwd, file)).filter((file) => pathPassesValidationFilter(file, validationPathFilter));
14881
- if (scopeConfig.filteredByValidationPathNoMatches || files !== void 0 && files.length > 0 && filteredFiles?.length === 0) {
15848
+ const scopeConfig = resolveTypeScriptValidationScope({
15849
+ projectRoot: cwd,
15850
+ scope: scope2,
15851
+ paths: files,
15852
+ validationPathFilter: validationPathFilterForTool(
15853
+ validationConfig.paths,
15854
+ VALIDATION_PATH_TOOL_SUBSECTIONS.TYPESCRIPT
15855
+ ),
15856
+ markExplicitPathsAsValidationFilter: true
15857
+ });
15858
+ if (scopeConfig.filteredByValidationPathNoMatches) {
14882
15859
  return {
14883
15860
  exitCode: 0,
14884
15861
  output: quiet ? "" : TYPESCRIPT_VALIDATION_MESSAGES.NO_VALIDATION_PATH_TARGETS,
@@ -14893,7 +15870,6 @@ async function typescriptCommand(options) {
14893
15870
  const result = await validateTypeScript({
14894
15871
  scope: scope2,
14895
15872
  projectRoot: cwd,
14896
- files: filteredFiles,
14897
15873
  scopeConfig
14898
15874
  });
14899
15875
  const durationMs = Date.now() - startTime;
@@ -14937,6 +15913,7 @@ async function runLiteralStage(context) {
14937
15913
  }
14938
15914
  return literalCommand({
14939
15915
  cwd: context.cwd,
15916
+ scope: context.scope,
14940
15917
  files: context.files,
14941
15918
  quiet: context.quiet,
14942
15919
  json: context.json
@@ -15066,7 +16043,7 @@ async function allCommand(options) {
15066
16043
  // src/validation/literal/allowlist-existing.ts
15067
16044
  import { randomBytes } from "crypto";
15068
16045
  import { rename as rename4, writeFile as writeFile4 } from "fs/promises";
15069
- import { dirname as dirname10, join as join33 } from "path";
16046
+ import { dirname as dirname10, join as join34 } from "path";
15070
16047
  var EXIT_OK = 0;
15071
16048
  var EXIT_ERROR = 1;
15072
16049
  var INCLUDE_FIELD = "include";
@@ -15086,7 +16063,7 @@ var productionWriter = {
15086
16063
  async write(filePath, content) {
15087
16064
  const dir = dirname10(filePath);
15088
16065
  const random = randomBytes(RANDOM_TOKEN_BYTES).toString("hex");
15089
- const tmpPath = join33(dir, `${TEMP_FILE_PREFIX}${random}${TEMP_FILE_SUFFIX}`);
16066
+ const tmpPath = join34(dir, `${TEMP_FILE_PREFIX}${random}${TEMP_FILE_SUFFIX}`);
15090
16067
  await writeFile4(tmpPath, content, "utf8");
15091
16068
  await rename4(tmpPath, filePath);
15092
16069
  }
@@ -15102,17 +16079,22 @@ async function allowlistExisting(options) {
15102
16079
  if (configRead.kind === "ambiguous") {
15103
16080
  return { exitCode: EXIT_ERROR, output: formatConfigFileAmbiguityError(configRead.detected) };
15104
16081
  }
15105
- const currentLiteralConfig = readCurrentLiteralConfig(configRead);
15106
- if (!currentLiteralConfig.ok) {
15107
- return { exitCode: EXIT_ERROR, output: currentLiteralConfig.error };
16082
+ const resolvedConfig = resolveConfigFromReadResult(configRead, [validationConfigDescriptor]);
16083
+ if (!resolvedConfig.ok) {
16084
+ return { exitCode: EXIT_ERROR, output: resolvedConfig.error };
15108
16085
  }
16086
+ const validationConfig = resolvedConfig.value[validationConfigDescriptor.section];
15109
16087
  const detection = await validateLiteralReuse({
15110
16088
  productDir: options.productDir,
15111
- config: currentLiteralConfig.value
16089
+ config: validationConfig.literal.values,
16090
+ pathConfig: validationPathFilterForTool(
16091
+ validationConfig.paths,
16092
+ VALIDATION_PATH_TOOL_SUBSECTIONS.LITERAL
16093
+ )
15112
16094
  });
15113
16095
  const findingValues = collectFindingValues(detection.findings);
15114
16096
  const updatedInclude = computeUpdatedInclude(
15115
- currentLiteralConfig.value.include,
16097
+ validationConfig.literal.values.include,
15116
16098
  findingValues
15117
16099
  );
15118
16100
  const target = configRead.kind === "ok" ? configRead.file : configFileForFormat(options.productDir, DEFAULT_CONFIG_FILE_FORMAT);
@@ -15123,25 +16105,6 @@ async function allowlistExisting(options) {
15123
16105
  await writer.write(target.path, serialized.value);
15124
16106
  return { exitCode: EXIT_OK, output: "" };
15125
16107
  }
15126
- function readCurrentLiteralConfig(read) {
15127
- if (read.kind !== "ok") return { ok: true, value: literalConfigDescriptor.defaults };
15128
- const sections = parseConfigFileSections(read.file);
15129
- if (!sections.ok) return sections;
15130
- const validationRaw = sections.value[VALIDATION_SECTION];
15131
- if (typeof validationRaw !== "object" || validationRaw === null) {
15132
- return { ok: true, value: literalConfigDescriptor.defaults };
15133
- }
15134
- const literalRaw = validationRaw[VALIDATION_LITERAL_SUBSECTION];
15135
- if (typeof literalRaw !== "object" || literalRaw === null) {
15136
- return { ok: true, value: literalConfigDescriptor.defaults };
15137
- }
15138
- const valuesRaw = literalRaw[VALIDATION_LITERAL_VALUES_SUBSECTION];
15139
- if (valuesRaw === void 0) {
15140
- return { ok: true, value: literalConfigDescriptor.defaults };
15141
- }
15142
- const validated = literalConfigDescriptor.validate(valuesRaw);
15143
- return validated.ok ? validated : { ok: true, value: literalConfigDescriptor.defaults };
15144
- }
15145
16108
  function collectFindingValues(findings) {
15146
16109
  const values = /* @__PURE__ */ new Set();
15147
16110
  for (const finding of findings.srcReuse) values.add(finding.value);
@@ -15206,6 +16169,10 @@ var validationCliDefinition = {
15206
16169
  longFlag: "--help",
15207
16170
  shortFlag: "-h"
15208
16171
  },
16172
+ pathOperands: {
16173
+ optionalVariadic: "[paths...]",
16174
+ description: "Specific files/directories to validate"
16175
+ },
15209
16176
  diagnostics: {
15210
16177
  unknownSubcommand: {
15211
16178
  messageLabel: "unknown subcommand",
@@ -15214,6 +16181,11 @@ var validationCliDefinition = {
15214
16181
  unknownLiteralProblemKind: {
15215
16182
  messageLabel: "unknown problem kind",
15216
16183
  exitCode: 1
16184
+ },
16185
+ invalidPathOperand: {
16186
+ messageLabel: "invalid path operand",
16187
+ reason: "escapes product directory",
16188
+ exitCode: 1
15217
16189
  }
15218
16190
  }
15219
16191
  };
@@ -15269,7 +16241,50 @@ function emitValidationResult(result, io) {
15269
16241
  return io.exit(result.exitCode);
15270
16242
  }
15271
16243
  function addCommonOptions(cmd) {
15272
- return cmd.option("--scope <scope>", "Validation scope (full|production)", "full").option("--files <paths...>", "Specific files/directories to validate").option("--quiet", "Suppress progress output").option("--json", "Output results as JSON");
16244
+ const { pathOperands } = validationCliDefinition;
16245
+ 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");
16246
+ }
16247
+ function normalizeProductPathOperand(productDir, effectiveInvocationDir, operand) {
16248
+ const resolvedProductDir = canonicalExistingPath(resolve11(productDir));
16249
+ const resolvedInvocationDir = canonicalExistingPath(resolve11(effectiveInvocationDir));
16250
+ const absoluteOperand = canonicalExistingPath(resolve11(resolvedInvocationDir, operand));
16251
+ const relativeOperand = relative9(resolvedProductDir, absoluteOperand);
16252
+ if (relativeOperand === ".." || relativeOperand.startsWith(`..${sep3}`) || isAbsolute9(relativeOperand)) {
16253
+ return void 0;
16254
+ }
16255
+ return relativeOperand.length > 0 ? relativeOperand.replaceAll("\\", "/") : ".";
16256
+ }
16257
+ function canonicalExistingPath(path7) {
16258
+ return existsSync8(path7) ? realpathSync.native(path7) : path7;
16259
+ }
16260
+ function normalizePathOperands(productDir, effectiveInvocationDir, pathOperands) {
16261
+ if (pathOperands.length === 0) return void 0;
16262
+ const normalized = [];
16263
+ for (const operand of pathOperands) {
16264
+ const path7 = normalizeProductPathOperand(productDir, effectiveInvocationDir, operand);
16265
+ if (path7 === void 0) return void 0;
16266
+ normalized.push(path7);
16267
+ }
16268
+ return normalized;
16269
+ }
16270
+ function resolveValidationPaths(invocation, pathOperands) {
16271
+ const context = invocation.resolveProductContext();
16272
+ const files = normalizePathOperands(context.productDir, context.effectiveInvocationDir, pathOperands);
16273
+ if (pathOperands.length > 0 && files === void 0) {
16274
+ const { invalidPathOperand } = validationCliDefinition.diagnostics;
16275
+ const invalidOperand = pathOperands.find(
16276
+ (operand) => normalizeProductPathOperand(context.productDir, context.effectiveInvocationDir, operand) === void 0
16277
+ );
16278
+ invocation.io.writeStderr(
16279
+ `spx ${validationCliDefinition.domain.commandName}: ${invalidPathOperand.messageLabel}: ${sanitizeCliArgument(invalidOperand)} (${invalidPathOperand.reason})
16280
+ `
16281
+ );
16282
+ invocation.io.exit(invalidPathOperand.exitCode);
16283
+ }
16284
+ return {
16285
+ productDir: context.productDir,
16286
+ files
16287
+ };
15273
16288
  }
15274
16289
  function addValidationSubcommand(validationCmd, definition) {
15275
16290
  let subcommand = validationCmd.command(definition.commandName).description(definition.description);
@@ -15280,23 +16295,24 @@ function addValidationSubcommand(validationCmd, definition) {
15280
16295
  }
15281
16296
  function registerValidationCommands(validationCmd, invocation) {
15282
16297
  const { subcommands } = validationCliDefinition;
15283
- const productDir = () => invocation.resolveProductContext().productDir;
15284
- const tsCmd = addValidationSubcommand(validationCmd, subcommands.typescript).action(async (options) => {
16298
+ const tsCmd = addValidationSubcommand(validationCmd, subcommands.typescript).action(async (pathOperands, options) => {
16299
+ const paths = resolveValidationPaths(invocation, pathOperands);
15285
16300
  const result = await typescriptCommand({
15286
- cwd: productDir(),
16301
+ cwd: paths.productDir,
15287
16302
  scope: options.scope,
15288
- files: options.files,
16303
+ files: paths.files,
15289
16304
  quiet: options.quiet,
15290
16305
  json: options.json
15291
16306
  });
15292
16307
  emitValidationResult(result, invocation.io);
15293
16308
  });
15294
16309
  addCommonOptions(tsCmd);
15295
- const lintCmd = addValidationSubcommand(validationCmd, subcommands.lint).option("--fix", "Auto-fix issues").action(async (options) => {
16310
+ const lintCmd = addValidationSubcommand(validationCmd, subcommands.lint).option("--fix", "Auto-fix issues").action(async (pathOperands, options) => {
16311
+ const paths = resolveValidationPaths(invocation, pathOperands);
15296
16312
  const result = await lintCommand({
15297
- cwd: productDir(),
16313
+ cwd: paths.productDir,
15298
16314
  scope: options.scope,
15299
- files: options.files,
16315
+ files: paths.files,
15300
16316
  fix: options.fix,
15301
16317
  quiet: options.quiet,
15302
16318
  json: options.json
@@ -15304,22 +16320,24 @@ function registerValidationCommands(validationCmd, invocation) {
15304
16320
  emitValidationResult(result, invocation.io);
15305
16321
  });
15306
16322
  addCommonOptions(lintCmd);
15307
- const circularCmd = addValidationSubcommand(validationCmd, subcommands.circular).action(async (options) => {
16323
+ const circularCmd = addValidationSubcommand(validationCmd, subcommands.circular).action(async (pathOperands, options) => {
16324
+ const paths = resolveValidationPaths(invocation, pathOperands);
15308
16325
  const result = await circularCommand({
15309
- cwd: productDir(),
16326
+ cwd: paths.productDir,
15310
16327
  scope: options.scope,
15311
- files: options.files,
16328
+ files: paths.files,
15312
16329
  quiet: options.quiet,
15313
16330
  json: options.json
15314
16331
  });
15315
16332
  emitValidationResult(result, invocation.io);
15316
16333
  });
15317
16334
  addCommonOptions(circularCmd);
15318
- const knipCmd = addValidationSubcommand(validationCmd, subcommands.knip).action(async (options) => {
16335
+ const knipCmd = addValidationSubcommand(validationCmd, subcommands.knip).action(async (pathOperands, options) => {
16336
+ const paths = resolveValidationPaths(invocation, pathOperands);
15319
16337
  const result = await knipCommand({
15320
- cwd: productDir(),
16338
+ cwd: paths.productDir,
15321
16339
  scope: options.scope,
15322
- files: options.files,
16340
+ files: paths.files,
15323
16341
  quiet: options.quiet,
15324
16342
  json: options.json
15325
16343
  });
@@ -15335,9 +16353,10 @@ function registerValidationCommands(validationCmd, invocation) {
15335
16353
  ).option(literalValidationCliOptions.literals.flag, literalValidationCliOptions.literals.description).option(literalValidationCliOptions.verbose.flag, literalValidationCliOptions.verbose.description).addHelpText(
15336
16354
  "after",
15337
16355
  "\nEnabled for TypeScript projects by default. Set validation.literal.enabled=false\nin spx.config.* to skip during migration."
15338
- ).action(async (options) => {
16356
+ ).action(async (pathOperands, options) => {
16357
+ const paths = resolveValidationPaths(invocation, pathOperands);
15339
16358
  if (options.allowlistExisting) {
15340
- const result2 = await allowlistExisting({ productDir: productDir() });
16359
+ const result2 = await allowlistExisting({ productDir: paths.productDir });
15341
16360
  emitValidationResult(result2, invocation.io);
15342
16361
  }
15343
16362
  let kind;
@@ -15353,8 +16372,9 @@ function registerValidationCommands(validationCmd, invocation) {
15353
16372
  }
15354
16373
  }
15355
16374
  const result = await literalCommand({
15356
- cwd: productDir(),
15357
- files: options.files,
16375
+ cwd: paths.productDir,
16376
+ scope: options.scope,
16377
+ files: paths.files,
15358
16378
  kind,
15359
16379
  filesWithProblems: options.filesWithProblems,
15360
16380
  literals: options.literals,
@@ -15368,29 +16388,32 @@ function registerValidationCommands(validationCmd, invocation) {
15368
16388
  const markdownCmd = addValidationSubcommand(validationCmd, subcommands.markdown).addHelpText(
15369
16389
  "after",
15370
16390
  "\nValidates spx/ and docs/ by default. Nodes listed in spx/EXCLUDE are\nskipped \u2014 use this for declared-state nodes whose [test] links point\nto files that do not exist yet."
15371
- ).action(async (options) => {
16391
+ ).action(async (pathOperands, options) => {
16392
+ const paths = resolveValidationPaths(invocation, pathOperands);
15372
16393
  const result = await markdownCommand({
15373
- cwd: productDir(),
15374
- files: options.files,
16394
+ cwd: paths.productDir,
16395
+ files: paths.files,
15375
16396
  quiet: options.quiet
15376
16397
  });
15377
16398
  emitValidationResult(result, invocation.io);
15378
16399
  });
15379
16400
  addCommonOptions(markdownCmd);
15380
- const formatCmd = addValidationSubcommand(validationCmd, subcommands.format).action(async (options) => {
16401
+ const formatCmd = addValidationSubcommand(validationCmd, subcommands.format).action(async (pathOperands, options) => {
16402
+ const paths = resolveValidationPaths(invocation, pathOperands);
15381
16403
  const result = await formattingCommand({
15382
- cwd: productDir(),
15383
- files: options.files,
16404
+ cwd: paths.productDir,
16405
+ files: paths.files,
15384
16406
  quiet: options.quiet
15385
16407
  });
15386
16408
  emitValidationResult(result, invocation.io);
15387
16409
  });
15388
16410
  addCommonOptions(formatCmd);
15389
- const allCmd = addValidationSubcommand(validationCmd, subcommands.all).option("--fix", "Auto-fix ESLint issues").option(allValidationCliOptions.skipCircular.flag, allValidationCliOptions.skipCircular.description).option(allValidationCliOptions.skipLiteral.flag, allValidationCliOptions.skipLiteral.description).action(async (options) => {
16411
+ const allCmd = addValidationSubcommand(validationCmd, subcommands.all).option("--fix", "Auto-fix ESLint issues").option(allValidationCliOptions.skipCircular.flag, allValidationCliOptions.skipCircular.description).option(allValidationCliOptions.skipLiteral.flag, allValidationCliOptions.skipLiteral.description).action(async (pathOperands, options) => {
16412
+ const paths = resolveValidationPaths(invocation, pathOperands);
15390
16413
  const result = await allCommand({
15391
- cwd: productDir(),
16414
+ cwd: paths.productDir,
15392
16415
  scope: options.scope,
15393
- files: options.files,
16416
+ files: paths.files,
15394
16417
  fix: options.fix,
15395
16418
  skipCircular: options.skipCircular,
15396
16419
  skipLiteral: options.skipLiteral,
@@ -15430,7 +16453,7 @@ var validationDomain = {
15430
16453
  };
15431
16454
 
15432
16455
  // src/commands/verification-context/cli.ts
15433
- import { isAbsolute as isAbsolute8, win32 } from "path";
16456
+ import { isAbsolute as isAbsolute10, win32 } from "path";
15434
16457
 
15435
16458
  // src/domains/verification-context/context.ts
15436
16459
  var VERIFICATION_CONTEXT_SCHEMA_VERSION = "verification-context.v1";
@@ -15467,7 +16490,7 @@ function createVerificationContextDocument(payload) {
15467
16490
  import { dirname as dirname11 } from "path";
15468
16491
 
15469
16492
  // src/domains/verification-context/path.ts
15470
- import { join as join34 } from "path";
16493
+ import { join as join35 } from "path";
15471
16494
  var VERIFICATION_CONTEXT_STATE_DOMAIN = VERIFICATION_CONTEXT_PERSISTENCE.domain;
15472
16495
  var VERIFICATION_CONTEXT_STATE_PATH = {
15473
16496
  CONTEXTS_DIR: "contexts",
@@ -15488,7 +16511,7 @@ function verificationContextFilePath(scope2) {
15488
16511
  if (!contextsDir.ok) return contextsDir;
15489
16512
  const digest = validateScopeToken(scope2.digest);
15490
16513
  if (!digest.ok) return digest;
15491
- return { ok: true, value: join34(contextsDir.value, verificationContextFileName(digest.value)) };
16514
+ return { ok: true, value: join35(contextsDir.value, verificationContextFileName(digest.value)) };
15492
16515
  }
15493
16516
 
15494
16517
  // src/commands/verification-context/runtime.ts
@@ -15581,7 +16604,7 @@ function normalizeFileSubjectPath(path7) {
15581
16604
  VERIFICATION_CONTEXT_FILE_SUBJECT_PATH.SEPARATOR.CANONICAL
15582
16605
  );
15583
16606
  const segments = normalized.split(VERIFICATION_CONTEXT_FILE_SUBJECT_PATH.SEPARATOR.CANONICAL);
15584
- if (isAbsolute8(path7) || win32.isAbsolute(path7) || windowsRoot.length > 0 || segments.includes(VERIFICATION_CONTEXT_FILE_SUBJECT_PATH.PARENT_DIRECTORY.SEGMENT)) {
16607
+ if (isAbsolute10(path7) || win32.isAbsolute(path7) || windowsRoot.length > 0 || segments.includes(VERIFICATION_CONTEXT_FILE_SUBJECT_PATH.PARENT_DIRECTORY.SEGMENT)) {
15585
16608
  return void 0;
15586
16609
  }
15587
16610
  return normalized;
@@ -15689,8 +16712,11 @@ var WORKTREE_STATUS_RENDER = {
15689
16712
  FREE: "-",
15690
16713
  RUNNING_PID_PREFIX: "PID "
15691
16714
  };
16715
+ var WORKTREE_STATUS_ERROR = {
16716
+ ALL_WITH_EXPLICIT_TARGETS: "worktree status --all cannot be combined with explicit worktree operands"
16717
+ };
15692
16718
  async function statusCommand2(options) {
15693
- const multiTargetRequest = options.worktrees !== void 0 && options.worktrees.length > 1;
16719
+ const multiTargetRequest = options.all === true || options.worktrees !== void 0 && options.worktrees.length > 1;
15694
16720
  const targets = await resolveStatusTargets(options);
15695
16721
  if (!targets.ok) return targets;
15696
16722
  const records = [];
@@ -15708,6 +16734,12 @@ async function statusCommand2(options) {
15708
16734
  }
15709
16735
  async function resolveStatusTargets(options) {
15710
16736
  const requested = options.worktrees;
16737
+ if (options.all === true) {
16738
+ if (requested !== void 0 && requested.length > 0) {
16739
+ return { ok: false, error: WORKTREE_STATUS_ERROR.ALL_WITH_EXPLICIT_TARGETS };
16740
+ }
16741
+ return resolveAllTargetWorktrees(options);
16742
+ }
15711
16743
  if (requested === void 0 || requested.length === 0) {
15712
16744
  const target = await resolveTargetWorktree(options);
15713
16745
  if (!target.ok) return target;
@@ -15745,11 +16777,11 @@ function renderTextStatus(record6) {
15745
16777
  }
15746
16778
 
15747
16779
  // src/lib/worktree-path-info.ts
15748
- import { stat as stat4 } from "fs/promises";
16780
+ import { stat as stat6 } from "fs/promises";
15749
16781
  var defaultWorktreePathInfo = {
15750
16782
  isExistingNonDirectory: async (path7) => {
15751
16783
  try {
15752
- const pathStats = await stat4(path7);
16784
+ const pathStats = await stat6(path7);
15753
16785
  return !pathStats.isDirectory();
15754
16786
  } catch {
15755
16787
  return false;
@@ -15765,25 +16797,26 @@ var WORKTREE_CLI = {
15765
16797
  RELEASE: "release",
15766
16798
  WORKTREE_ARGUMENT: "[worktrees...]",
15767
16799
  SESSION_ID_FLAG: "--session-id",
16800
+ ALL_FLAG: "--all",
15768
16801
  FORMAT_FLAG: "--format",
15769
16802
  WORKTREES_DIR_FLAG: "--worktrees-dir"
15770
16803
  };
15771
16804
  var WORKTREE_DOMAIN_DESCRIPTION = "Coordinate worktree occupancy across a bare-repository pool";
15772
- function writeOutput4(invocation, output) {
16805
+ function writeOutput5(invocation, output) {
15773
16806
  invocation.io.writeStdout(`${output}
15774
16807
  `);
15775
16808
  }
15776
- function writeError4(invocation, output) {
16809
+ function writeError5(invocation, output) {
15777
16810
  invocation.io.writeStderr(`${output}
15778
16811
  `);
15779
16812
  }
15780
16813
  function writeInvocationWarning4(invocation, warning) {
15781
16814
  if (warning !== void 0) {
15782
- writeError4(invocation, warning);
16815
+ writeError5(invocation, warning);
15783
16816
  }
15784
16817
  }
15785
- function handleError3(invocation, error) {
15786
- writeError4(invocation, `Error: ${error}`);
16818
+ function handleError4(invocation, error) {
16819
+ writeError5(invocation, `Error: ${error}`);
15787
16820
  return invocation.io.exit(1);
15788
16821
  }
15789
16822
  function registerWorktreeCommands(worktreeCmd, invocation) {
@@ -15801,23 +16834,26 @@ function registerWorktreeCommands(worktreeCmd, invocation) {
15801
16834
  worktreesDir: options.worktreesDir,
15802
16835
  onWarning: (warning) => writeInvocationWarning4(invocation, warning)
15803
16836
  });
15804
- if (!result.ok) handleError3(invocation, result.error);
15805
- });
15806
- worktreeCmd.command(`${WORKTREE_CLI.STATUS} ${WORKTREE_CLI.WORKTREE_ARGUMENT}`).description("Report a worktree's occupancy (running | free)").option(`${WORKTREE_CLI.FORMAT_FLAG} <format>`, "Output format (text|json)", WORKTREE_STATUS_FORMAT.TEXT).option(`${WORKTREE_CLI.WORKTREES_DIR_FLAG} <path>`, "Explicit .spx/worktrees directory").action(async (worktrees, options) => {
15807
- const result = await statusCommand2({
15808
- cwd: effectiveInvocationDir(),
15809
- fs: defaultOccupancyFileSystem,
15810
- gitDeps: defaultGitDependencies,
15811
- worktrees,
15812
- format: options.format,
15813
- pathInfo: defaultWorktreePathInfo,
15814
- processTable: defaultProcessTable,
15815
- worktreesDir: options.worktreesDir,
15816
- onWarning: (warning) => writeInvocationWarning4(invocation, warning)
15817
- });
15818
- if (!result.ok) handleError3(invocation, result.error);
15819
- writeOutput4(invocation, result.value);
16837
+ if (!result.ok) handleError4(invocation, result.error);
15820
16838
  });
16839
+ worktreeCmd.command(`${WORKTREE_CLI.STATUS} ${WORKTREE_CLI.WORKTREE_ARGUMENT}`).description("Report a worktree's occupancy (running | free)").option(WORKTREE_CLI.ALL_FLAG, "Report every git-observed worktree").option(`${WORKTREE_CLI.FORMAT_FLAG} <format>`, "Output format (text|json)", WORKTREE_STATUS_FORMAT.TEXT).option(`${WORKTREE_CLI.WORKTREES_DIR_FLAG} <path>`, "Explicit .spx/worktrees directory").action(
16840
+ async (worktrees, options) => {
16841
+ const result = await statusCommand2({
16842
+ cwd: effectiveInvocationDir(),
16843
+ fs: defaultOccupancyFileSystem,
16844
+ gitDeps: defaultGitDependencies,
16845
+ worktrees,
16846
+ all: options.all,
16847
+ format: options.format,
16848
+ pathInfo: defaultWorktreePathInfo,
16849
+ processTable: defaultProcessTable,
16850
+ worktreesDir: options.worktreesDir,
16851
+ onWarning: (warning) => writeInvocationWarning4(invocation, warning)
16852
+ });
16853
+ if (!result.ok) handleError4(invocation, result.error);
16854
+ writeOutput5(invocation, result.value);
16855
+ }
16856
+ );
15821
16857
  worktreeCmd.command(WORKTREE_CLI.RELEASE).description("Release the running worktree's occupancy claim").option(`${WORKTREE_CLI.WORKTREES_DIR_FLAG} <path>`, "Explicit .spx/worktrees directory").action(async (options) => {
15822
16858
  const result = await releaseCommand2({
15823
16859
  cwd: effectiveInvocationDir(),
@@ -15826,7 +16862,7 @@ function registerWorktreeCommands(worktreeCmd, invocation) {
15826
16862
  worktreesDir: options.worktreesDir,
15827
16863
  onWarning: (warning) => writeInvocationWarning4(invocation, warning)
15828
16864
  });
15829
- if (!result.ok) handleError3(invocation, result.error);
16865
+ if (!result.ok) handleError4(invocation, result.error);
15830
16866
  });
15831
16867
  }
15832
16868
  var worktreeDomain = {
@@ -15840,6 +16876,7 @@ var worktreeDomain = {
15840
16876
 
15841
16877
  // src/interfaces/cli/registry.ts
15842
16878
  var CLI_DOMAINS = [
16879
+ agentDomain,
15843
16880
  claudeDomain,
15844
16881
  compactDomain,
15845
16882
  configDomain,