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