@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/README.md +4 -3
- package/dist/cli.js +1782 -745
- package/dist/cli.js.map +1 -1
- package/package.json +3 -3
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/
|
|
95
|
-
import {
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
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
|
-
|
|
119
|
-
if (
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
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
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
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
|
-
|
|
142
|
-
|
|
143
|
-
|
|
414
|
+
const parsed = JSON.parse(trimmed);
|
|
415
|
+
return isRecord(parsed) ? parsed : null;
|
|
416
|
+
} catch {
|
|
417
|
+
return null;
|
|
144
418
|
}
|
|
145
419
|
}
|
|
146
|
-
|
|
147
|
-
const
|
|
148
|
-
|
|
149
|
-
|
|
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
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
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
|
|
437
|
+
return current;
|
|
157
438
|
}
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
return
|
|
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
|
-
|
|
446
|
+
if (left === null) {
|
|
447
|
+
return right;
|
|
448
|
+
}
|
|
449
|
+
return Date.parse(right) > Date.parse(left) ? right : left;
|
|
166
450
|
}
|
|
167
|
-
function
|
|
168
|
-
|
|
169
|
-
if (
|
|
170
|
-
|
|
171
|
-
|
|
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
|
|
174
|
-
|
|
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
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
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
|
|
207
|
-
import { basename, dirname, isAbsolute, join, resolve as
|
|
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
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
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 :
|
|
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()
|
|
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()
|
|
757
|
+
return stripTrailingPathSeparators(path7.trim());
|
|
476
758
|
}
|
|
477
759
|
function normalizedGitPathKey(path7) {
|
|
478
|
-
return normalizeGitPath(path7).
|
|
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 :
|
|
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
|
|
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
|
|
2230
|
+
function writeOutput2(io, output) {
|
|
1409
2231
|
io.writeStdout(`${output}
|
|
1410
2232
|
`);
|
|
1411
2233
|
}
|
|
1412
|
-
function
|
|
2234
|
+
function writeError2(io, output) {
|
|
1413
2235
|
io.writeStderr(`${output}
|
|
1414
2236
|
`);
|
|
1415
2237
|
}
|
|
1416
2238
|
function exitWithError(io, error) {
|
|
1417
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
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
|
|
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
|
|
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 (!
|
|
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 (!
|
|
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 (!
|
|
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 (!
|
|
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 (!
|
|
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 (!
|
|
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 (!
|
|
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 (!
|
|
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 (!
|
|
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
|
|
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 (!
|
|
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
|
|
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 (!
|
|
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 (!
|
|
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 (!
|
|
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
|
|
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
|
|
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 (!
|
|
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
|
|
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) =>
|
|
5739
|
+
fs: { readFile: (path7) => readFile4(path7, "utf8") }
|
|
4895
5740
|
});
|
|
4896
5741
|
if (!result.ok) {
|
|
4897
|
-
|
|
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
|
|
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 :
|
|
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:
|
|
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((
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
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: "
|
|
6259
|
-
UNMET: "
|
|
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
|
|
6267
|
-
|
|
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
|
|
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
|
-
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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.
|
|
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 =
|
|
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
|
|
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
|
|
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
|
|
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
|
|
7360
|
-
import { join as join13, resolve as
|
|
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:
|
|
7400
|
-
readFile:
|
|
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
|
|
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
|
-
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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 :
|
|
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
|
-
|
|
8099
|
-
|
|
8100
|
-
|
|
8101
|
-
|
|
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
|
-
|
|
8106
|
-
|
|
8107
|
-
|
|
8108
|
-
|
|
8109
|
-
|
|
8110
|
-
|
|
8111
|
-
|
|
8112
|
-
|
|
8113
|
-
|
|
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
|
-
|
|
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__ */
|
|
8885
|
+
return /* @__PURE__ */ jsxs2(Text2, { wrap: "truncate", color: selected ? "cyan" : void 0, children: [
|
|
8176
8886
|
marker,
|
|
8177
8887
|
" ",
|
|
8178
8888
|
session.id,
|
|
8179
|
-
/* @__PURE__ */
|
|
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__ */
|
|
8895
|
+
return /* @__PURE__ */ jsx3(Text2, { children: `${label} ${value}` });
|
|
8186
8896
|
}
|
|
8187
8897
|
function PreviewPane({ session }) {
|
|
8188
8898
|
if (session === null) {
|
|
8189
|
-
return /* @__PURE__ */
|
|
8899
|
+
return /* @__PURE__ */ jsx3(Box2, { marginTop: 1, children: /* @__PURE__ */ jsx3(Text2, { dimColor: true, children: SESSION_PICKER_EMPTY_TEXT }) });
|
|
8190
8900
|
}
|
|
8191
|
-
return /* @__PURE__ */
|
|
8192
|
-
/* @__PURE__ */
|
|
8193
|
-
/* @__PURE__ */
|
|
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] =
|
|
8201
|
-
|
|
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__ */
|
|
8219
|
-
/* @__PURE__ */
|
|
8220
|
-
/* @__PURE__ */
|
|
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__ */
|
|
8226
|
-
/* @__PURE__ */
|
|
8227
|
-
/* @__PURE__ */
|
|
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
|
|
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 =
|
|
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__ */
|
|
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((
|
|
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
|
-
|
|
8980
|
+
resolve12(data.length === 0 ? void 0 : data);
|
|
8271
8981
|
});
|
|
8272
8982
|
process.stdin.on("error", () => {
|
|
8273
|
-
|
|
8983
|
+
resolve12(void 0);
|
|
8274
8984
|
});
|
|
8275
8985
|
});
|
|
8276
8986
|
}
|
|
8277
|
-
function
|
|
8987
|
+
function writeOutput3(invocation, output) {
|
|
8278
8988
|
invocation.io.writeStdout(`${output}
|
|
8279
8989
|
`);
|
|
8280
8990
|
}
|
|
8281
|
-
function
|
|
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
|
-
|
|
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
|
|
8297
|
-
|
|
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
|
-
|
|
9060
|
+
writeOutput3(invocation, output);
|
|
8351
9061
|
} catch (error) {
|
|
8352
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
9106
|
+
writeOutput3(invocation, output);
|
|
8397
9107
|
} catch (error) {
|
|
8398
|
-
|
|
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
|
-
|
|
9123
|
+
writeOutput3(invocation, output);
|
|
8414
9124
|
} catch (error) {
|
|
8415
|
-
|
|
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
|
-
|
|
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
|
-
|
|
9145
|
+
writeOutput3(invocation, output);
|
|
8436
9146
|
} catch (error) {
|
|
8437
|
-
|
|
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
|
-
|
|
9161
|
+
writeOutput3(invocation, output);
|
|
8452
9162
|
} catch (error) {
|
|
8453
|
-
|
|
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
|
-
|
|
9178
|
+
writeOutput3(invocation, result.output);
|
|
8469
9179
|
} catch (error) {
|
|
8470
9180
|
if (error instanceof SessionHandoffBaseError) {
|
|
8471
9181
|
if (error.checklist !== null) {
|
|
8472
|
-
|
|
9182
|
+
writeError4(invocation, renderHandoffBaseChecklist(error.checklist));
|
|
8473
9183
|
} else if (!error.silent) {
|
|
8474
|
-
|
|
9184
|
+
writeError4(invocation, `Error: ${error.name}: ${error.message}`);
|
|
8475
9185
|
}
|
|
8476
9186
|
invocation.io.exit(1);
|
|
8477
9187
|
}
|
|
8478
|
-
|
|
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
|
-
|
|
9202
|
+
writeOutput3(invocation, output);
|
|
8493
9203
|
} catch (error) {
|
|
8494
|
-
|
|
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
|
-
|
|
9220
|
+
writeOutput3(invocation, output);
|
|
8511
9221
|
} catch (error) {
|
|
8512
9222
|
if (error instanceof PruneValidationError) {
|
|
8513
|
-
|
|
9223
|
+
writeError4(invocation, `Error: ${error.message}`);
|
|
8514
9224
|
invocation.io.exit(1);
|
|
8515
9225
|
}
|
|
8516
|
-
|
|
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
|
-
|
|
9240
|
+
writeOutput3(invocation, output);
|
|
8531
9241
|
} catch (error) {
|
|
8532
9242
|
if (error instanceof SessionAlreadyArchivedError) {
|
|
8533
|
-
|
|
9243
|
+
writeError4(invocation, `Error: ${error.message}`);
|
|
8534
9244
|
invocation.io.exit(1);
|
|
8535
9245
|
}
|
|
8536
|
-
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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 (!
|
|
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 (!
|
|
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 (!
|
|
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
|
|
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
|
|
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
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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((
|
|
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
|
-
|
|
11814
|
+
resolve12({ success: code === 0, output: chunks.join("") });
|
|
11011
11815
|
});
|
|
11012
11816
|
child.on(VALIDATION_SUBPROCESS_EVENTS.ERROR, (error) => {
|
|
11013
|
-
|
|
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
|
|
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.
|
|
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({
|
|
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
|
|
11203
|
-
if (
|
|
11204
|
-
|
|
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
|
-
|
|
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:
|
|
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
|
|
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
|
-
})) :
|
|
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
|
-
|
|
12835
|
+
handleError5(
|
|
11920
12836
|
14
|
|
11921
12837
|
/* ParseErrorCode.InvalidUnicode */
|
|
11922
12838
|
);
|
|
11923
12839
|
break;
|
|
11924
12840
|
case 5:
|
|
11925
|
-
|
|
12841
|
+
handleError5(
|
|
11926
12842
|
15
|
|
11927
12843
|
/* ParseErrorCode.InvalidEscapeCharacter */
|
|
11928
12844
|
);
|
|
11929
12845
|
break;
|
|
11930
12846
|
case 3:
|
|
11931
|
-
|
|
12847
|
+
handleError5(
|
|
11932
12848
|
13
|
|
11933
12849
|
/* ParseErrorCode.UnexpectedEndOfNumber */
|
|
11934
12850
|
);
|
|
11935
12851
|
break;
|
|
11936
12852
|
case 1:
|
|
11937
12853
|
if (!disallowComments) {
|
|
11938
|
-
|
|
12854
|
+
handleError5(
|
|
11939
12855
|
11
|
|
11940
12856
|
/* ParseErrorCode.UnexpectedEndOfComment */
|
|
11941
12857
|
);
|
|
11942
12858
|
}
|
|
11943
12859
|
break;
|
|
11944
12860
|
case 2:
|
|
11945
|
-
|
|
12861
|
+
handleError5(
|
|
11946
12862
|
12
|
|
11947
12863
|
/* ParseErrorCode.UnexpectedEndOfString */
|
|
11948
12864
|
);
|
|
11949
12865
|
break;
|
|
11950
12866
|
case 6:
|
|
11951
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
12968
|
+
handleError5(4, [], [
|
|
12053
12969
|
2,
|
|
12054
12970
|
5
|
|
12055
12971
|
/* SyntaxKind.CommaToken */
|
|
12056
12972
|
]);
|
|
12057
12973
|
}
|
|
12058
12974
|
} else {
|
|
12059
|
-
|
|
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
|
-
|
|
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
|
-
|
|
12999
|
+
handleError5(6, [], []);
|
|
12084
13000
|
}
|
|
12085
13001
|
if (!parseProperty()) {
|
|
12086
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
13085
|
+
handleError5(4, [], []);
|
|
12170
13086
|
return false;
|
|
12171
13087
|
}
|
|
12172
13088
|
if (!parseValue()) {
|
|
12173
|
-
|
|
13089
|
+
handleError5(4, [], []);
|
|
12174
13090
|
return false;
|
|
12175
13091
|
}
|
|
12176
13092
|
if (_scanner.getToken() !== 17) {
|
|
12177
|
-
|
|
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
|
|
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
|
|
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(
|
|
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(
|
|
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(
|
|
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
|
|
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
|
|
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
|
|
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(
|
|
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(
|
|
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 =
|
|
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("..") && !
|
|
13529
|
+
return relativePath.length === 0 || !segments.includes("..") && !isAbsolute5(relativePath);
|
|
12614
13530
|
}
|
|
12615
13531
|
function toProjectRelativeTypeScriptScopePath(projectRoot, path7) {
|
|
12616
|
-
const resolvedPath =
|
|
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) =>
|
|
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
|
|
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 =
|
|
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
|
-
|
|
12699
|
-
|
|
12700
|
-
|
|
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,
|
|
13788
|
+
function nearestPackageRoot(filePath, existsSync9) {
|
|
12851
13789
|
let currentDirectory = path6.dirname(filePath);
|
|
12852
13790
|
for (; ; ) {
|
|
12853
|
-
if (
|
|
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,
|
|
13801
|
+
function bundledToolPath(resolvedPath, existsSync9) {
|
|
12864
13802
|
const bundledFilePath = resolvedModulePath(resolvedPath);
|
|
12865
|
-
return nearestPackageRoot(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
|
|
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 =
|
|
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
|
|
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 =
|
|
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((
|
|
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(() =>
|
|
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 =
|
|
14310
|
+
const tempParentDir = join29(projectRoot, ...TEMPORARY_TSCONFIG_PARENT_SEGMENTS);
|
|
13373
14311
|
await deps.mkdir(tempParentDir, { recursive: true });
|
|
13374
|
-
const tempDir = await deps.mkdtemp(
|
|
13375
|
-
const configPath =
|
|
13376
|
-
const toProjectPathPattern = (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:
|
|
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
|
|
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
|
|
13456
|
-
import { join as
|
|
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(
|
|
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(
|
|
14448
|
+
return existsSync5(join30(productDir, file));
|
|
13511
14449
|
}
|
|
13512
14450
|
function findDeprecatedSpecNodePath(productDir) {
|
|
13513
14451
|
function visit2(relativeDirectory) {
|
|
13514
|
-
const absoluteDirectory =
|
|
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 =
|
|
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 =
|
|
13557
|
-
if (!existsSync5(absoluteEntry) || !
|
|
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 {
|
|
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((
|
|
13772
|
-
const localBin =
|
|
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
|
-
|
|
14728
|
+
resolve12({ success: true });
|
|
13782
14729
|
} else {
|
|
13783
|
-
|
|
14730
|
+
resolve12({ success: false, error: `ESLint exited with code ${code}` });
|
|
13784
14731
|
}
|
|
13785
14732
|
});
|
|
13786
14733
|
eslintProcess.on(VALIDATION_SUBPROCESS_EVENTS.ERROR, (error) => {
|
|
13787
|
-
|
|
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?.
|
|
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
|
|
13877
|
-
import { isAbsolute as
|
|
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
|
|
13912
|
-
import { join as
|
|
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
|
|
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 =
|
|
14886
|
+
const absolutePath = join32(absoluteDir, entry.name);
|
|
13931
14887
|
await collectPaths(absolutePath, projectRoot, result, artifactDirs);
|
|
13932
14888
|
} else if (entry.isFile()) {
|
|
13933
|
-
const absolutePath =
|
|
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
|
-
|
|
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 =
|
|
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
|
|
14459
|
-
const
|
|
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 {
|
|
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
|
|
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
|
-
|
|
14537
|
-
|
|
14538
|
-
|
|
14539
|
-
|
|
14540
|
-
|
|
14541
|
-
|
|
14542
|
-
|
|
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 (!
|
|
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
|
-
|
|
14570
|
-
|
|
14571
|
-
|
|
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
|
|
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 =
|
|
15694
|
+
const parent = join33(projectRoot, ...TEMPORARY_TSCONFIG_PARENT_SEGMENTS);
|
|
14722
15695
|
deps.mkdirSync(parent, { recursive: true });
|
|
14723
|
-
return deps.mkdtemp(
|
|
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 =
|
|
15704
|
+
const configPath = join33(tempDir, "tsconfig.json");
|
|
14732
15705
|
const baseConfigFile = TSCONFIG_FILES[scope2];
|
|
14733
|
-
const absoluteFiles = files.map((file) =>
|
|
15706
|
+
const absoluteFiles = files.map((file) => isAbsolute8(file) ? file : join33(projectRoot, file));
|
|
14734
15707
|
const tempConfig = {
|
|
14735
|
-
extends:
|
|
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 =
|
|
15720
|
+
const configPath = join33(tempDir, "tsconfig.json");
|
|
14748
15721
|
const baseConfigFile = TSCONFIG_FILES[scope2];
|
|
14749
|
-
const
|
|
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:
|
|
14752
|
-
include: scopeConfig.filePatterns.map(
|
|
14753
|
-
exclude: scopeConfig.excludePatterns.map(
|
|
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 =
|
|
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((
|
|
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
|
-
|
|
15808
|
+
resolve12({ success: true, skipped: false });
|
|
14833
15809
|
} else {
|
|
14834
|
-
|
|
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
|
-
|
|
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
|
|
14873
|
-
|
|
14874
|
-
|
|
14875
|
-
|
|
14876
|
-
|
|
14877
|
-
|
|
14878
|
-
|
|
14879
|
-
|
|
14880
|
-
|
|
14881
|
-
|
|
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
|
|
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 =
|
|
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
|
|
15106
|
-
if (!
|
|
15107
|
-
return { exitCode: EXIT_ERROR, output:
|
|
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:
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
15284
|
-
|
|
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:
|
|
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:
|
|
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:
|
|
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:
|
|
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
|
-
|
|
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:
|
|
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:
|
|
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:
|
|
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
|
|
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
|
|
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:
|
|
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 (
|
|
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
|
|
16780
|
+
import { stat as stat6 } from "fs/promises";
|
|
15749
16781
|
var defaultWorktreePathInfo = {
|
|
15750
16782
|
isExistingNonDirectory: async (path7) => {
|
|
15751
16783
|
try {
|
|
15752
|
-
const pathStats = await
|
|
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
|
|
16805
|
+
function writeOutput5(invocation, output) {
|
|
15773
16806
|
invocation.io.writeStdout(`${output}
|
|
15774
16807
|
`);
|
|
15775
16808
|
}
|
|
15776
|
-
function
|
|
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
|
-
|
|
16815
|
+
writeError5(invocation, warning);
|
|
15783
16816
|
}
|
|
15784
16817
|
}
|
|
15785
|
-
function
|
|
15786
|
-
|
|
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)
|
|
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)
|
|
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,
|