@outcomeeng/spx 0.6.10 → 0.6.11
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli.js +448 -145
- package/dist/cli.js.map +1 -1
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -289,9 +289,6 @@ async function resolveAgentResumeScopeContext(options) {
|
|
|
289
289
|
return { match: (core) => core.branch === target, claudeDirAccepts: () => true };
|
|
290
290
|
}
|
|
291
291
|
const invocationRoot = await options.resolveWorktreeRoot(options.invocationDir);
|
|
292
|
-
if (invocationRoot === null) {
|
|
293
|
-
return null;
|
|
294
|
-
}
|
|
295
292
|
const projectPrefix = claudeProjectDirName(invocationRoot);
|
|
296
293
|
return {
|
|
297
294
|
match: (core) => isPathInsideOrEqual(invocationRoot, core.cwd),
|
|
@@ -326,11 +323,11 @@ function renderAgentResumeJson(candidates) {
|
|
|
326
323
|
}
|
|
327
324
|
async function recentStoreFiles(paths, fs8, nowMs) {
|
|
328
325
|
const stats = await mapWithConcurrency(paths, AGENT_RESUME_LIMITS.READ_CONCURRENCY, async (path7) => {
|
|
329
|
-
const
|
|
330
|
-
if (
|
|
326
|
+
const stat9 = await fs8.stat(path7).catch(() => null);
|
|
327
|
+
if (stat9 === null || !isRecentAgentSessionMtime(stat9.mtimeMs, nowMs)) {
|
|
331
328
|
return null;
|
|
332
329
|
}
|
|
333
|
-
return { path: path7, modifiedAtMs:
|
|
330
|
+
return { path: path7, modifiedAtMs: stat9.mtimeMs };
|
|
334
331
|
});
|
|
335
332
|
return stats.filter((file) => file !== null).sort((left, right) => right.modifiedAtMs - left.modifiedAtMs || left.path.localeCompare(right.path));
|
|
336
333
|
}
|
|
@@ -529,6 +526,195 @@ async function mapWithConcurrency(items, concurrency, mapper) {
|
|
|
529
526
|
return results;
|
|
530
527
|
}
|
|
531
528
|
|
|
529
|
+
// src/domains/session/types.ts
|
|
530
|
+
var SESSION_PRIORITY = {
|
|
531
|
+
HIGH: "high",
|
|
532
|
+
MEDIUM: "medium",
|
|
533
|
+
LOW: "low"
|
|
534
|
+
};
|
|
535
|
+
var SESSION_STATUSES = ["todo", "doing", "archive"];
|
|
536
|
+
var DEFAULT_LIST_STATUSES = ["doing", "todo"];
|
|
537
|
+
var SESSION_FILE_ENCODING = "utf-8";
|
|
538
|
+
var SESSION_FILE_ERROR_CODE = {
|
|
539
|
+
NOT_FOUND: "ENOENT"
|
|
540
|
+
};
|
|
541
|
+
var SESSION_OUTPUT_MARKER = {
|
|
542
|
+
HANDOFF_ID: "HANDOFF_ID",
|
|
543
|
+
PICKUP_ID: "PICKUP_ID",
|
|
544
|
+
SESSION_FILE: "SESSION_FILE"
|
|
545
|
+
};
|
|
546
|
+
function formatSessionOutputMarker(marker, value) {
|
|
547
|
+
return `<${marker}>${value}</${marker}>`;
|
|
548
|
+
}
|
|
549
|
+
var CLAIMABLE_STATUS = SESSION_STATUSES[0];
|
|
550
|
+
var PRIORITY_ORDER = {
|
|
551
|
+
[SESSION_PRIORITY.HIGH]: 0,
|
|
552
|
+
[SESSION_PRIORITY.MEDIUM]: 1,
|
|
553
|
+
[SESSION_PRIORITY.LOW]: 2
|
|
554
|
+
};
|
|
555
|
+
var DEFAULT_PRIORITY = SESSION_PRIORITY.MEDIUM;
|
|
556
|
+
var SESSION_FRONT_MATTER = {
|
|
557
|
+
PRIORITY: "priority",
|
|
558
|
+
GIT_REF: "git_ref",
|
|
559
|
+
GOAL: "goal",
|
|
560
|
+
NEXT_STEP: "next_step",
|
|
561
|
+
CREATED_AT: "created_at",
|
|
562
|
+
AGENT_SESSION_ID: "agent_session_id",
|
|
563
|
+
SPECS: "specs",
|
|
564
|
+
FILES: "files"
|
|
565
|
+
};
|
|
566
|
+
|
|
567
|
+
// src/domains/agent/search.ts
|
|
568
|
+
var AGENT_SEARCH_DEFAULT_LIMIT = 20;
|
|
569
|
+
var AGENT_SEARCH_MATCH_REASON = {
|
|
570
|
+
ALL: "all",
|
|
571
|
+
PICKUP_ID: "pickup-id",
|
|
572
|
+
CONTAINS: "contains",
|
|
573
|
+
SESSION_ID: "session-id",
|
|
574
|
+
AGENT: "agent",
|
|
575
|
+
BRANCH: "branch"
|
|
576
|
+
};
|
|
577
|
+
function pickupIdSearchLiteral(pickupId) {
|
|
578
|
+
return formatSessionOutputMarker(SESSION_OUTPUT_MARKER.PICKUP_ID, pickupId);
|
|
579
|
+
}
|
|
580
|
+
function agentSearchQueryFromOptions(options) {
|
|
581
|
+
const contentNeedles = [];
|
|
582
|
+
if (options.pickupId !== void 0) {
|
|
583
|
+
contentNeedles.push({
|
|
584
|
+
reason: AGENT_SEARCH_MATCH_REASON.PICKUP_ID,
|
|
585
|
+
value: pickupIdSearchLiteral(options.pickupId)
|
|
586
|
+
});
|
|
587
|
+
}
|
|
588
|
+
if (options.contains !== void 0) {
|
|
589
|
+
contentNeedles.push({ reason: AGENT_SEARCH_MATCH_REASON.CONTAINS, value: options.contains });
|
|
590
|
+
}
|
|
591
|
+
return {
|
|
592
|
+
contentNeedles,
|
|
593
|
+
sessionId: options.sessionId ?? null,
|
|
594
|
+
branch: options.branch ?? null,
|
|
595
|
+
agent: options.agent ?? null,
|
|
596
|
+
includeAll: options.all === true,
|
|
597
|
+
limit: options.limit ?? AGENT_SEARCH_DEFAULT_LIMIT
|
|
598
|
+
};
|
|
599
|
+
}
|
|
600
|
+
async function searchAgentSessions(options) {
|
|
601
|
+
const selectedAgents = options.query.agent === null ? [AGENT_SESSION_KIND.CODEX, AGENT_SESSION_KIND.CLAUDE_CODE] : [options.query.agent];
|
|
602
|
+
const perAgent = await Promise.all(
|
|
603
|
+
selectedAgents.map((agent) => searchAgentStore(agent, options))
|
|
604
|
+
);
|
|
605
|
+
return perAgent.flat().sort(compareSearchResults).slice(0, Math.max(0, options.query.limit));
|
|
606
|
+
}
|
|
607
|
+
function renderAgentSearchJson(results) {
|
|
608
|
+
return JSON.stringify(results, null, 2);
|
|
609
|
+
}
|
|
610
|
+
function renderAgentSearchList(results) {
|
|
611
|
+
if (results.length === 0) {
|
|
612
|
+
return "No matching agent sessions found.";
|
|
613
|
+
}
|
|
614
|
+
return results.map((result) => {
|
|
615
|
+
const updatedAt = result.updatedAt ?? new Date(result.modifiedAtMs).toISOString();
|
|
616
|
+
return `${updatedAt} ${AGENT_SESSION_LABEL[result.agent]} ${result.sessionId} ${result.cwd}`;
|
|
617
|
+
}).join("\n");
|
|
618
|
+
}
|
|
619
|
+
async function searchAgentStore(agent, options) {
|
|
620
|
+
const paths = agent === AGENT_SESSION_KIND.CODEX ? await collectJsonlFiles(codexSessionStoreDir(options.homeDir), options.fs) : await claudeTranscriptFiles(
|
|
621
|
+
claudeCodeSessionStoreDir(options.homeDir),
|
|
622
|
+
options.fs,
|
|
623
|
+
claudeDirAcceptsProductScope(options.productScopeRoot)
|
|
624
|
+
);
|
|
625
|
+
const files = await storeFiles(paths, options.fs, options.nowMs, options.query.includeAll);
|
|
626
|
+
const parser = agent === AGENT_SESSION_KIND.CODEX ? parseCodexHead : parseClaudeHead;
|
|
627
|
+
return collectMatchingSessions(agent, files, options, parser);
|
|
628
|
+
}
|
|
629
|
+
function claudeDirAcceptsProductScope(productScopeRoot) {
|
|
630
|
+
const projectPrefix = claudeProjectDirName(productScopeRoot);
|
|
631
|
+
return (dirName) => dirName === projectPrefix || dirName.startsWith(`${projectPrefix}${CLAUDE_PROJECT_ENCODED_SEPARATOR}`);
|
|
632
|
+
}
|
|
633
|
+
async function collectMatchingSessions(agent, files, options, parseHead) {
|
|
634
|
+
const results = [];
|
|
635
|
+
const seen = /* @__PURE__ */ new Set();
|
|
636
|
+
for (const file of files) {
|
|
637
|
+
const head = await options.fs.readHead(file.path, AGENT_RESUME_LIMITS.METADATA_HEAD_BYTES).catch(() => null);
|
|
638
|
+
if (head === null) continue;
|
|
639
|
+
const core = parseHead(head);
|
|
640
|
+
if (core === null || !core.interactive || seen.has(core.sessionId)) continue;
|
|
641
|
+
if (!coreMatchesProductScope(core, options.productScopeRoot)) continue;
|
|
642
|
+
const matches = await matchReasons(agent, core, file.path, options);
|
|
643
|
+
if (matches.length === 0) continue;
|
|
644
|
+
seen.add(core.sessionId);
|
|
645
|
+
results.push({
|
|
646
|
+
agent,
|
|
647
|
+
sessionId: core.sessionId,
|
|
648
|
+
cwd: core.cwd,
|
|
649
|
+
sourcePath: file.path,
|
|
650
|
+
modifiedAtMs: file.modifiedAtMs,
|
|
651
|
+
updatedAt: core.updatedAt,
|
|
652
|
+
branch: core.branch,
|
|
653
|
+
matches
|
|
654
|
+
});
|
|
655
|
+
}
|
|
656
|
+
return results;
|
|
657
|
+
}
|
|
658
|
+
async function matchReasons(agent, core, path7, options) {
|
|
659
|
+
if (!hasSearchSelector(options.query)) {
|
|
660
|
+
return [AGENT_SEARCH_MATCH_REASON.ALL];
|
|
661
|
+
}
|
|
662
|
+
const metadataMatches = metadataMatchReasons(agent, core, options.query);
|
|
663
|
+
const contentMatches = await contentMatchReasons(path7, options);
|
|
664
|
+
if (metadataMatches === null || contentMatches === null) {
|
|
665
|
+
return [];
|
|
666
|
+
}
|
|
667
|
+
return [...metadataMatches, ...contentMatches];
|
|
668
|
+
}
|
|
669
|
+
function hasSearchSelector(query) {
|
|
670
|
+
return query.contentNeedles.length > 0 || query.sessionId !== null || query.branch !== null || query.agent !== null;
|
|
671
|
+
}
|
|
672
|
+
function metadataMatchReasons(agent, core, query) {
|
|
673
|
+
const matches = [];
|
|
674
|
+
if (query.agent !== null) {
|
|
675
|
+
if (agent !== query.agent) return null;
|
|
676
|
+
matches.push(AGENT_SEARCH_MATCH_REASON.AGENT);
|
|
677
|
+
}
|
|
678
|
+
if (query.sessionId !== null && core.sessionId === query.sessionId) {
|
|
679
|
+
matches.push(AGENT_SEARCH_MATCH_REASON.SESSION_ID);
|
|
680
|
+
} else if (query.sessionId !== null) {
|
|
681
|
+
return null;
|
|
682
|
+
}
|
|
683
|
+
if (query.branch !== null) {
|
|
684
|
+
if (core.branch !== query.branch) return null;
|
|
685
|
+
matches.push(AGENT_SEARCH_MATCH_REASON.BRANCH);
|
|
686
|
+
}
|
|
687
|
+
return matches;
|
|
688
|
+
}
|
|
689
|
+
async function contentMatchReasons(path7, options) {
|
|
690
|
+
if (options.query.contentNeedles.length === 0) {
|
|
691
|
+
return [];
|
|
692
|
+
}
|
|
693
|
+
const content = await options.fs.readText(path7).catch(() => null);
|
|
694
|
+
return content === null ? null : matchingContentNeedles(content, options.query.contentNeedles);
|
|
695
|
+
}
|
|
696
|
+
function matchingContentNeedles(content, needles) {
|
|
697
|
+
const matches = needles.filter((needle) => content.includes(needle.value)).map((needle) => needle.reason);
|
|
698
|
+
return matches.length === needles.length ? matches : null;
|
|
699
|
+
}
|
|
700
|
+
function coreMatchesProductScope(core, productScopeRoot) {
|
|
701
|
+
return isPathInsideOrEqual(productScopeRoot, core.cwd);
|
|
702
|
+
}
|
|
703
|
+
async function storeFiles(paths, fs8, nowMs, includeAll) {
|
|
704
|
+
const files = await mapWithConcurrency(paths, AGENT_RESUME_LIMITS.READ_CONCURRENCY, async (path7) => {
|
|
705
|
+
const stat9 = await fs8.stat(path7).catch(() => null);
|
|
706
|
+
if (stat9 === null) return null;
|
|
707
|
+
if (!includeAll && !isRecentAgentSessionMtime(stat9.mtimeMs, nowMs)) return null;
|
|
708
|
+
return { path: path7, modifiedAtMs: stat9.mtimeMs };
|
|
709
|
+
});
|
|
710
|
+
return files.filter((file) => file !== null).sort((left, right) => right.modifiedAtMs - left.modifiedAtMs || left.path.localeCompare(right.path));
|
|
711
|
+
}
|
|
712
|
+
function compareSearchResults(left, right) {
|
|
713
|
+
const modifiedDiff = right.modifiedAtMs - left.modifiedAtMs;
|
|
714
|
+
if (modifiedDiff !== 0) return modifiedDiff;
|
|
715
|
+
return `${left.agent}:${left.sessionId}`.localeCompare(`${right.agent}:${right.sessionId}`);
|
|
716
|
+
}
|
|
717
|
+
|
|
532
718
|
// src/git/root.ts
|
|
533
719
|
import { execa } from "execa";
|
|
534
720
|
import { basename, dirname, isAbsolute as isAbsolute2, join, resolve as resolve4 } from "path";
|
|
@@ -917,9 +1103,9 @@ var defaultAgentResumeCommandDeps = {
|
|
|
917
1103
|
fs: nodeAgentSessionFileSystem,
|
|
918
1104
|
homeDir: homedir,
|
|
919
1105
|
nowMs: Date.now,
|
|
920
|
-
resolveWorktreeRoot: async (cwd) => {
|
|
1106
|
+
resolveWorktreeRoot: async (cwd, fallbackWorktreeRoot) => {
|
|
921
1107
|
const result = await detectWorktreeProductRoot(cwd);
|
|
922
|
-
return result.isGitRepo ? result.productDir :
|
|
1108
|
+
return result.isGitRepo ? result.productDir : fallbackWorktreeRoot;
|
|
923
1109
|
}
|
|
924
1110
|
};
|
|
925
1111
|
async function loadAgentResumeCandidates(options) {
|
|
@@ -930,7 +1116,7 @@ async function loadAgentResumeCandidates(options) {
|
|
|
930
1116
|
nowMs: deps.nowMs(),
|
|
931
1117
|
scope: options.scope,
|
|
932
1118
|
fs: deps.fs,
|
|
933
|
-
resolveWorktreeRoot: deps.resolveWorktreeRoot
|
|
1119
|
+
resolveWorktreeRoot: (cwd) => deps.resolveWorktreeRoot(cwd, options.fallbackWorktreeRoot)
|
|
934
1120
|
});
|
|
935
1121
|
}
|
|
936
1122
|
async function listAgentResumeSessions(options) {
|
|
@@ -940,6 +1126,63 @@ async function jsonAgentResumeSessions(options) {
|
|
|
940
1126
|
return renderAgentResumeJson(await loadAgentResumeCandidates(options));
|
|
941
1127
|
}
|
|
942
1128
|
|
|
1129
|
+
// src/commands/agent/search.ts
|
|
1130
|
+
import { open as open2, readdir as readdir2, readFile, stat as stat2 } from "fs/promises";
|
|
1131
|
+
import { homedir as homedir2 } from "os";
|
|
1132
|
+
var nodeAgentSearchFileSystem = {
|
|
1133
|
+
async readDir(path7) {
|
|
1134
|
+
const entries = await readdir2(path7, { withFileTypes: true });
|
|
1135
|
+
return entries.map((entry) => ({
|
|
1136
|
+
name: entry.name,
|
|
1137
|
+
isDirectory: entry.isDirectory(),
|
|
1138
|
+
isFile: entry.isFile()
|
|
1139
|
+
}));
|
|
1140
|
+
},
|
|
1141
|
+
async readHead(path7, maxBytes) {
|
|
1142
|
+
const handle = await open2(path7, "r");
|
|
1143
|
+
try {
|
|
1144
|
+
const buffer = Buffer.alloc(maxBytes);
|
|
1145
|
+
const { bytesRead } = await handle.read(buffer, 0, maxBytes, 0);
|
|
1146
|
+
return buffer.toString(AGENT_SESSION_STORE.TEXT_ENCODING, 0, bytesRead);
|
|
1147
|
+
} finally {
|
|
1148
|
+
await handle.close();
|
|
1149
|
+
}
|
|
1150
|
+
},
|
|
1151
|
+
async readText(path7) {
|
|
1152
|
+
return readFile(path7, AGENT_SESSION_STORE.TEXT_ENCODING);
|
|
1153
|
+
},
|
|
1154
|
+
async stat(path7) {
|
|
1155
|
+
const result = await stat2(path7);
|
|
1156
|
+
return { mtimeMs: result.mtimeMs };
|
|
1157
|
+
}
|
|
1158
|
+
};
|
|
1159
|
+
var defaultAgentSearchCommandDeps = {
|
|
1160
|
+
fs: nodeAgentSearchFileSystem,
|
|
1161
|
+
homeDir: homedir2,
|
|
1162
|
+
nowMs: Date.now,
|
|
1163
|
+
resolveProductScopeRoot: resolveAgentSearchProductScopeRoot
|
|
1164
|
+
};
|
|
1165
|
+
async function resolveAgentSearchProductScopeRoot(cwd, fallbackProductScopeRoot, gitDeps = defaultGitDependencies) {
|
|
1166
|
+
const result = await detectWorktreeProductRoot(cwd, gitDeps);
|
|
1167
|
+
return result.isGitRepo ? result.productDir : fallbackProductScopeRoot;
|
|
1168
|
+
}
|
|
1169
|
+
async function loadAgentSearchResults(options) {
|
|
1170
|
+
const deps = options.deps ?? defaultAgentSearchCommandDeps;
|
|
1171
|
+
return searchAgentSessions({
|
|
1172
|
+
homeDir: deps.homeDir(),
|
|
1173
|
+
nowMs: deps.nowMs(),
|
|
1174
|
+
productScopeRoot: await deps.resolveProductScopeRoot(options.cwd, options.fallbackProductScopeRoot),
|
|
1175
|
+
fs: deps.fs,
|
|
1176
|
+
query: options.query
|
|
1177
|
+
});
|
|
1178
|
+
}
|
|
1179
|
+
async function listAgentSearchSessions(options) {
|
|
1180
|
+
return renderAgentSearchList(await loadAgentSearchResults(options));
|
|
1181
|
+
}
|
|
1182
|
+
async function jsonAgentSearchSessions(options) {
|
|
1183
|
+
return renderAgentSearchJson(await loadAgentSearchResults(options));
|
|
1184
|
+
}
|
|
1185
|
+
|
|
943
1186
|
// src/lib/process-lifecycle/exit-codes.ts
|
|
944
1187
|
var SIGINT_EXIT_CODE = 130;
|
|
945
1188
|
var SIGTERM_EXIT_CODE = 143;
|
|
@@ -1118,6 +1361,50 @@ function spawnManagedSubprocess(runner, command, args, options) {
|
|
|
1118
1361
|
});
|
|
1119
1362
|
}
|
|
1120
1363
|
|
|
1364
|
+
// src/lib/sanitize-cli-argument.ts
|
|
1365
|
+
var MAX_CLI_ARGUMENT_DISPLAY_LENGTH = 120;
|
|
1366
|
+
var ELLIPSIS_TOKEN = "...";
|
|
1367
|
+
var SENTINEL_UNDEFINED = "<undefined>";
|
|
1368
|
+
var SENTINEL_NULL = "<null>";
|
|
1369
|
+
var SENTINEL_EMPTY = "<empty>";
|
|
1370
|
+
var CONTROL_CHAR_UPPER_BOUND = 31;
|
|
1371
|
+
var DEL_CHAR_CODE = 127;
|
|
1372
|
+
var HEX_RADIX = 16;
|
|
1373
|
+
var HEX_PAD = 2;
|
|
1374
|
+
function formatHexEscape(code) {
|
|
1375
|
+
return String.raw`\x${code.toString(HEX_RADIX).padStart(HEX_PAD, "0")}`;
|
|
1376
|
+
}
|
|
1377
|
+
function nonStringSentinel(type) {
|
|
1378
|
+
return `<non-string:${type}>`;
|
|
1379
|
+
}
|
|
1380
|
+
function sanitizeCliArgument(input) {
|
|
1381
|
+
return truncate(escapeCliArgument(input));
|
|
1382
|
+
}
|
|
1383
|
+
function escapeCliArgument(input) {
|
|
1384
|
+
if (input === void 0) return SENTINEL_UNDEFINED;
|
|
1385
|
+
if (input === null) return SENTINEL_NULL;
|
|
1386
|
+
if (typeof input !== "string") return nonStringSentinel(typeof input);
|
|
1387
|
+
if (input.length === 0) return SENTINEL_EMPTY;
|
|
1388
|
+
return escapeControlCharacters(input);
|
|
1389
|
+
}
|
|
1390
|
+
function escapeControlCharacters(value) {
|
|
1391
|
+
let out = "";
|
|
1392
|
+
for (const char of value) {
|
|
1393
|
+
const code = char.codePointAt(0);
|
|
1394
|
+
if (code === void 0) continue;
|
|
1395
|
+
if (code <= CONTROL_CHAR_UPPER_BOUND || code === DEL_CHAR_CODE) {
|
|
1396
|
+
out += formatHexEscape(code);
|
|
1397
|
+
} else {
|
|
1398
|
+
out += char;
|
|
1399
|
+
}
|
|
1400
|
+
}
|
|
1401
|
+
return out;
|
|
1402
|
+
}
|
|
1403
|
+
function truncate(value) {
|
|
1404
|
+
if (value.length <= MAX_CLI_ARGUMENT_DISPLAY_LENGTH) return value;
|
|
1405
|
+
return value.slice(0, MAX_CLI_ARGUMENT_DISPLAY_LENGTH - ELLIPSIS_TOKEN.length) + ELLIPSIS_TOKEN;
|
|
1406
|
+
}
|
|
1407
|
+
|
|
1121
1408
|
// src/interfaces/cli/foreground-launch.ts
|
|
1122
1409
|
var FOREGROUND_LAUNCH_STDIO = "inherit";
|
|
1123
1410
|
var LAUNCH_FAILURE_STATUS = 1;
|
|
@@ -1240,14 +1527,26 @@ async function runAgentResumePicker(candidates, renderPicker = render) {
|
|
|
1240
1527
|
var AGENT_CLI = {
|
|
1241
1528
|
commandName: "agent",
|
|
1242
1529
|
resumeCommandName: "resume",
|
|
1530
|
+
searchCommandName: "search",
|
|
1243
1531
|
flags: {
|
|
1244
1532
|
latest: "--latest",
|
|
1245
1533
|
list: "--list",
|
|
1246
1534
|
json: "--json",
|
|
1247
|
-
branch: "--branch"
|
|
1535
|
+
branch: "--branch",
|
|
1536
|
+
all: "--all",
|
|
1537
|
+
pickupId: "--pickup-id",
|
|
1538
|
+
contains: "--contains",
|
|
1539
|
+
sessionId: "--session-id",
|
|
1540
|
+
agent: "--agent",
|
|
1541
|
+
limit: "--limit"
|
|
1248
1542
|
},
|
|
1249
1543
|
optionArgs: {
|
|
1250
|
-
branch: "--branch <name>"
|
|
1544
|
+
branch: "--branch <name>",
|
|
1545
|
+
pickupId: "--pickup-id <id>",
|
|
1546
|
+
contains: "--contains <literal>",
|
|
1547
|
+
sessionId: "--session-id <id>",
|
|
1548
|
+
agent: "--agent <kind>",
|
|
1549
|
+
limit: "--limit <count>"
|
|
1251
1550
|
}
|
|
1252
1551
|
};
|
|
1253
1552
|
var AGENT_CLI_EXIT = {
|
|
@@ -1265,6 +1564,7 @@ var DEFAULT_AGENT_CLI_DEPENDENCIES = {
|
|
|
1265
1564
|
);
|
|
1266
1565
|
}
|
|
1267
1566
|
};
|
|
1567
|
+
var POSITIVE_DECIMAL_INTEGER_PATTERN = /^[1-9][0-9]*$/;
|
|
1268
1568
|
function writeOutput(invocation, output) {
|
|
1269
1569
|
invocation.io.writeStdout(`${output}
|
|
1270
1570
|
`);
|
|
@@ -1281,6 +1581,33 @@ function handleError(invocation, error) {
|
|
|
1281
1581
|
function resumeScopeFromOptions(options) {
|
|
1282
1582
|
return options.branch === void 0 ? worktreeResumeScope() : branchResumeScope(options.branch);
|
|
1283
1583
|
}
|
|
1584
|
+
function parseSearchLimit(value) {
|
|
1585
|
+
if (!POSITIVE_DECIMAL_INTEGER_PATTERN.test(value)) {
|
|
1586
|
+
throw new Error(`agent search limit must be a positive integer: ${sanitizeCliArgument(value)}`);
|
|
1587
|
+
}
|
|
1588
|
+
const parsed = Number.parseInt(value, 10);
|
|
1589
|
+
if (!Number.isSafeInteger(parsed)) {
|
|
1590
|
+
throw new Error(`agent search limit must be a positive integer: ${sanitizeCliArgument(value)}`);
|
|
1591
|
+
}
|
|
1592
|
+
return parsed;
|
|
1593
|
+
}
|
|
1594
|
+
function parseSearchAgentKind(value) {
|
|
1595
|
+
if (value === AGENT_SESSION_KIND.CODEX || value === AGENT_SESSION_KIND.CLAUDE_CODE) {
|
|
1596
|
+
return value;
|
|
1597
|
+
}
|
|
1598
|
+
throw new Error(`agent search kind must be codex or claude-code: ${sanitizeCliArgument(value)}`);
|
|
1599
|
+
}
|
|
1600
|
+
function searchQueryFromOptions(options) {
|
|
1601
|
+
return {
|
|
1602
|
+
pickupId: options.pickupId,
|
|
1603
|
+
contains: options.contains,
|
|
1604
|
+
sessionId: options.sessionId,
|
|
1605
|
+
branch: options.branch,
|
|
1606
|
+
agent: options.agent === void 0 ? void 0 : parseSearchAgentKind(options.agent),
|
|
1607
|
+
all: options.all,
|
|
1608
|
+
limit: options.limit === void 0 ? AGENT_SEARCH_DEFAULT_LIMIT : parseSearchLimit(options.limit)
|
|
1609
|
+
};
|
|
1610
|
+
}
|
|
1284
1611
|
async function dispatchInteractiveResume(mode, commandOptions, deps, invocation) {
|
|
1285
1612
|
const candidates = await loadAgentResumeCandidates(commandOptions);
|
|
1286
1613
|
if (candidates.length === 0) {
|
|
@@ -1307,12 +1634,14 @@ function createAgentDomain(deps = {}) {
|
|
|
1307
1634
|
let requestedExitCode = AGENT_CLI_EXIT.SUCCESS;
|
|
1308
1635
|
try {
|
|
1309
1636
|
const mode = resolveAgentResumeMode(options);
|
|
1637
|
+
const productContext = invocation.resolveProductContext();
|
|
1310
1638
|
if (mode === AGENT_RESUME_MODE.PICK && !resolvedDeps.isInteractiveTerminal()) {
|
|
1311
1639
|
writeError(invocation, AGENT_RESUME_TEXT.INTERACTIVE_REQUIRED);
|
|
1312
1640
|
requestedExitCode = AGENT_CLI_EXIT.FAILURE;
|
|
1313
1641
|
} else {
|
|
1314
1642
|
const commandOptions = {
|
|
1315
|
-
cwd:
|
|
1643
|
+
cwd: productContext.effectiveInvocationDir,
|
|
1644
|
+
fallbackWorktreeRoot: productContext.productDir,
|
|
1316
1645
|
scope: resumeScopeFromOptions(options),
|
|
1317
1646
|
deps: resolvedDeps.resumeDeps
|
|
1318
1647
|
};
|
|
@@ -1331,6 +1660,21 @@ function createAgentDomain(deps = {}) {
|
|
|
1331
1660
|
}
|
|
1332
1661
|
return invocation.io.exit(requestedExitCode);
|
|
1333
1662
|
});
|
|
1663
|
+
agentCmd.command(AGENT_CLI.searchCommandName).description("Search Codex and Claude Code agent session transcripts for this product").option(AGENT_CLI.optionArgs.pickupId, "Search for an exact SPX pickup marker").option(AGENT_CLI.optionArgs.contains, "Search transcript content for a literal string").option(AGENT_CLI.optionArgs.sessionId, "Search for an agent session id").option(AGENT_CLI.optionArgs.branch, "Search for sessions started on the named branch").option(AGENT_CLI.optionArgs.agent, "Search only one agent kind").option(AGENT_CLI.flags.all, "Include sessions outside the recent-session window").option(AGENT_CLI.optionArgs.limit, "Maximum number of results").option(AGENT_CLI.flags.json, "Print matching sessions as JSON").action(async (options) => {
|
|
1664
|
+
try {
|
|
1665
|
+
const productContext = invocation.resolveProductContext();
|
|
1666
|
+
const commandOptions = {
|
|
1667
|
+
cwd: productContext.effectiveInvocationDir,
|
|
1668
|
+
fallbackProductScopeRoot: productContext.productDir,
|
|
1669
|
+
query: agentSearchQueryFromOptions(searchQueryFromOptions(options)),
|
|
1670
|
+
deps: resolvedDeps.searchDeps
|
|
1671
|
+
};
|
|
1672
|
+
const output = options.json === true ? await jsonAgentSearchSessions(commandOptions) : await listAgentSearchSessions(commandOptions);
|
|
1673
|
+
writeOutput(invocation, output);
|
|
1674
|
+
} catch (error) {
|
|
1675
|
+
handleError(invocation, error);
|
|
1676
|
+
}
|
|
1677
|
+
});
|
|
1334
1678
|
}
|
|
1335
1679
|
};
|
|
1336
1680
|
}
|
|
@@ -2545,14 +2889,14 @@ async function compactRetrieveCommand(options) {
|
|
|
2545
2889
|
}
|
|
2546
2890
|
|
|
2547
2891
|
// src/commands/compact/store.ts
|
|
2548
|
-
import { readFile } from "fs/promises";
|
|
2892
|
+
import { readFile as readFile2 } from "fs/promises";
|
|
2549
2893
|
var UTF8_ENCODING = "utf8";
|
|
2550
2894
|
async function compactStoreCommand(options) {
|
|
2551
2895
|
const sessionToken = resolveCompactSessionToken(options.sessionId, options.env ?? process.env);
|
|
2552
2896
|
if (sessionToken === void 0) return 1;
|
|
2553
2897
|
let transcript;
|
|
2554
2898
|
try {
|
|
2555
|
-
transcript = await
|
|
2899
|
+
transcript = await readFile2(options.transcript, UTF8_ENCODING);
|
|
2556
2900
|
} catch {
|
|
2557
2901
|
return 1;
|
|
2558
2902
|
}
|
|
@@ -2608,7 +2952,7 @@ var compactDomain = {
|
|
|
2608
2952
|
};
|
|
2609
2953
|
|
|
2610
2954
|
// src/config/index.ts
|
|
2611
|
-
import { readFile as
|
|
2955
|
+
import { readFile as readFile3 } from "fs/promises";
|
|
2612
2956
|
import { join as join4 } from "path";
|
|
2613
2957
|
import { parse as parseToml, stringify as stringifyToml } from "smol-toml";
|
|
2614
2958
|
import { parse as parseYaml, parseDocument as parseYamlDocument, stringify as stringifyYaml } from "yaml";
|
|
@@ -4269,7 +4613,7 @@ async function readProductConfigFile(productDir) {
|
|
|
4269
4613
|
const path7 = join4(productDir, filename);
|
|
4270
4614
|
let raw;
|
|
4271
4615
|
try {
|
|
4272
|
-
raw = await
|
|
4616
|
+
raw = await readFile3(path7, "utf8");
|
|
4273
4617
|
} catch (error) {
|
|
4274
4618
|
if (isFileNotFound(error)) continue;
|
|
4275
4619
|
return { ok: false, error: `failed to read ${filename}: ${toMessage(error)}` };
|
|
@@ -4553,7 +4897,7 @@ var configDomain = {
|
|
|
4553
4897
|
};
|
|
4554
4898
|
|
|
4555
4899
|
// src/interfaces/cli/diagnose.ts
|
|
4556
|
-
import { readFile as
|
|
4900
|
+
import { readFile as readFile4 } from "fs/promises";
|
|
4557
4901
|
import { Option } from "commander";
|
|
4558
4902
|
|
|
4559
4903
|
// src/domains/diagnose/types.ts
|
|
@@ -6251,50 +6595,6 @@ var defaultSpxReachabilityProbe = {
|
|
|
6251
6595
|
}
|
|
6252
6596
|
};
|
|
6253
6597
|
|
|
6254
|
-
// src/lib/sanitize-cli-argument.ts
|
|
6255
|
-
var MAX_CLI_ARGUMENT_DISPLAY_LENGTH = 120;
|
|
6256
|
-
var ELLIPSIS_TOKEN = "...";
|
|
6257
|
-
var SENTINEL_UNDEFINED = "<undefined>";
|
|
6258
|
-
var SENTINEL_NULL = "<null>";
|
|
6259
|
-
var SENTINEL_EMPTY = "<empty>";
|
|
6260
|
-
var CONTROL_CHAR_UPPER_BOUND = 31;
|
|
6261
|
-
var DEL_CHAR_CODE = 127;
|
|
6262
|
-
var HEX_RADIX = 16;
|
|
6263
|
-
var HEX_PAD = 2;
|
|
6264
|
-
function formatHexEscape(code) {
|
|
6265
|
-
return String.raw`\x${code.toString(HEX_RADIX).padStart(HEX_PAD, "0")}`;
|
|
6266
|
-
}
|
|
6267
|
-
function nonStringSentinel(type) {
|
|
6268
|
-
return `<non-string:${type}>`;
|
|
6269
|
-
}
|
|
6270
|
-
function sanitizeCliArgument(input) {
|
|
6271
|
-
return truncate(escapeCliArgument(input));
|
|
6272
|
-
}
|
|
6273
|
-
function escapeCliArgument(input) {
|
|
6274
|
-
if (input === void 0) return SENTINEL_UNDEFINED;
|
|
6275
|
-
if (input === null) return SENTINEL_NULL;
|
|
6276
|
-
if (typeof input !== "string") return nonStringSentinel(typeof input);
|
|
6277
|
-
if (input.length === 0) return SENTINEL_EMPTY;
|
|
6278
|
-
return escapeControlCharacters(input);
|
|
6279
|
-
}
|
|
6280
|
-
function escapeControlCharacters(value) {
|
|
6281
|
-
let out = "";
|
|
6282
|
-
for (const char of value) {
|
|
6283
|
-
const code = char.codePointAt(0);
|
|
6284
|
-
if (code === void 0) continue;
|
|
6285
|
-
if (code <= CONTROL_CHAR_UPPER_BOUND || code === DEL_CHAR_CODE) {
|
|
6286
|
-
out += formatHexEscape(code);
|
|
6287
|
-
} else {
|
|
6288
|
-
out += char;
|
|
6289
|
-
}
|
|
6290
|
-
}
|
|
6291
|
-
return out;
|
|
6292
|
-
}
|
|
6293
|
-
function truncate(value) {
|
|
6294
|
-
if (value.length <= MAX_CLI_ARGUMENT_DISPLAY_LENGTH) return value;
|
|
6295
|
-
return value.slice(0, MAX_CLI_ARGUMENT_DISPLAY_LENGTH - ELLIPSIS_TOKEN.length) + ELLIPSIS_TOKEN;
|
|
6296
|
-
}
|
|
6297
|
-
|
|
6298
6598
|
// src/interfaces/cli/diagnose.ts
|
|
6299
6599
|
var DIAGNOSE_CLI = {
|
|
6300
6600
|
COMMAND: "diagnose",
|
|
@@ -6341,7 +6641,7 @@ var diagnoseDomain = {
|
|
|
6341
6641
|
isTty: Boolean(process.stdout.isTTY)
|
|
6342
6642
|
}),
|
|
6343
6643
|
registry: defaultRegistry(),
|
|
6344
|
-
fs: { readFile: (path7) =>
|
|
6644
|
+
fs: { readFile: (path7) => readFile4(path7, "utf8") }
|
|
6345
6645
|
});
|
|
6346
6646
|
if (!result.ok) {
|
|
6347
6647
|
handleError2(result.error, invocation.io);
|
|
@@ -7658,6 +7958,18 @@ async function readSealedJournalRunSet(scope2, options = {}) {
|
|
|
7658
7958
|
}))
|
|
7659
7959
|
};
|
|
7660
7960
|
}
|
|
7961
|
+
async function findJournalRunBranchSlugs(scope2, options = {}) {
|
|
7962
|
+
const fs8 = options.fs ?? defaultFileSystem;
|
|
7963
|
+
const branches = await branchSlugs(scope2, fs8);
|
|
7964
|
+
if (!branches.ok) return branches;
|
|
7965
|
+
const matches = [];
|
|
7966
|
+
for (const branchSlug of branches.value) {
|
|
7967
|
+
const runFilePath = bindRunFilePath({ ...scope2, branchSlug });
|
|
7968
|
+
if (!runFilePath.ok) return runFilePath;
|
|
7969
|
+
if (await runFileExists(fs8, runFilePath.value)) matches.push(branchSlug);
|
|
7970
|
+
}
|
|
7971
|
+
return { ok: true, value: matches };
|
|
7972
|
+
}
|
|
7661
7973
|
async function appendJournalEvent(ref, input, sink, options = {}) {
|
|
7662
7974
|
const bound = await bindJournal(ref, options.fs);
|
|
7663
7975
|
if (!bound.ok) return bound;
|
|
@@ -7736,6 +8048,7 @@ var JOURNAL_CLI_ERROR = {
|
|
|
7736
8048
|
INVALID_READ_SET_EVENT_LIMIT: "journal read-set event limit must be a positive whole integer",
|
|
7737
8049
|
INVALID_SEALED_FILTER: "journal list sealed filter is not registered",
|
|
7738
8050
|
INVALID_TERMINAL_STATE_FILTER: "journal list terminal-state filter is not registered",
|
|
8051
|
+
RUN_TOKEN_AMBIGUOUS: "journal run token matches multiple branch scopes; rerun with --branch-slug",
|
|
7739
8052
|
OPEN_HYDRATION_FAILED: "journal open failed to hydrate the pull request's prior runs"
|
|
7740
8053
|
};
|
|
7741
8054
|
var CURSOR_PATTERN = /^\d+$/;
|
|
@@ -7858,6 +8171,34 @@ function verbOptions(deps) {
|
|
|
7858
8171
|
function runRef(context, runToken) {
|
|
7859
8172
|
return { productDir: context.productDir, branchSlug: context.branchSlug, type: context.type, runToken };
|
|
7860
8173
|
}
|
|
8174
|
+
async function inspectionRunRef(scope2, deps) {
|
|
8175
|
+
const context = await resolveJournalRunContext(scope2, deps);
|
|
8176
|
+
if (!context.ok) return context;
|
|
8177
|
+
if (scope2.branchSlug !== void 0) return { ok: true, value: runRef(context.value, scope2.runToken) };
|
|
8178
|
+
const branches = await findJournalRunBranchSlugs(
|
|
8179
|
+
{
|
|
8180
|
+
productDir: context.value.productDir,
|
|
8181
|
+
type: context.value.type,
|
|
8182
|
+
runToken: scope2.runToken
|
|
8183
|
+
},
|
|
8184
|
+
verbOptions(deps)
|
|
8185
|
+
);
|
|
8186
|
+
if (!branches.ok) return branches;
|
|
8187
|
+
if (branches.value.length === 1) {
|
|
8188
|
+
const branchSlug = branches.value[0];
|
|
8189
|
+
return {
|
|
8190
|
+
ok: true,
|
|
8191
|
+
value: {
|
|
8192
|
+
productDir: context.value.productDir,
|
|
8193
|
+
branchSlug,
|
|
8194
|
+
type: context.value.type,
|
|
8195
|
+
runToken: scope2.runToken
|
|
8196
|
+
}
|
|
8197
|
+
};
|
|
8198
|
+
}
|
|
8199
|
+
if (branches.value.length > 1) return { ok: false, error: JOURNAL_CLI_ERROR.RUN_TOKEN_AMBIGUOUS };
|
|
8200
|
+
return { ok: true, value: runRef(context.value, scope2.runToken) };
|
|
8201
|
+
}
|
|
7861
8202
|
function okResult(output) {
|
|
7862
8203
|
return { exitCode: JOURNAL_CLI_EXIT_CODE.OK, output };
|
|
7863
8204
|
}
|
|
@@ -7974,9 +8315,9 @@ async function journalAppendCommand(scope2, input, binding, deps = {}) {
|
|
|
7974
8315
|
async function journalReadCommand(scope2, fromCursor, deps = {}) {
|
|
7975
8316
|
const cursor = parseJournalCursor(fromCursor);
|
|
7976
8317
|
if (!cursor.ok) return errorResult(cursor.error);
|
|
7977
|
-
const
|
|
7978
|
-
if (!
|
|
7979
|
-
const events = await readJournalEvents(
|
|
8318
|
+
const ref = await inspectionRunRef(scope2, deps);
|
|
8319
|
+
if (!ref.ok) return errorResult(ref.error);
|
|
8320
|
+
const events = await readJournalEvents(ref.value, cursor.value, verbOptions(deps));
|
|
7980
8321
|
if (!events.ok) return errorResult(events.error);
|
|
7981
8322
|
return okResult(JSON.stringify(events.value));
|
|
7982
8323
|
}
|
|
@@ -8025,10 +8366,10 @@ async function journalSealCommand(scope2, deps = {}) {
|
|
|
8025
8366
|
return okResult(JSON.stringify({ sealed: true }));
|
|
8026
8367
|
}
|
|
8027
8368
|
async function journalRenderCommand(scope2, deps = {}) {
|
|
8028
|
-
const
|
|
8029
|
-
if (!
|
|
8369
|
+
const ref = await inspectionRunRef(scope2, deps);
|
|
8370
|
+
if (!ref.ok) return errorResult(ref.error);
|
|
8030
8371
|
const rendered = await renderJournalRun(
|
|
8031
|
-
|
|
8372
|
+
ref.value,
|
|
8032
8373
|
(events) => [...events],
|
|
8033
8374
|
verbOptions(deps)
|
|
8034
8375
|
);
|
|
@@ -8354,7 +8695,7 @@ function sessionOptionToken(option) {
|
|
|
8354
8695
|
}
|
|
8355
8696
|
|
|
8356
8697
|
// src/commands/session/archive.ts
|
|
8357
|
-
import { mkdir, rename, stat as
|
|
8698
|
+
import { mkdir, rename, stat as stat3 } from "fs/promises";
|
|
8358
8699
|
import { dirname as dirname7, join as join8 } from "path";
|
|
8359
8700
|
|
|
8360
8701
|
// src/domains/session/archive.ts
|
|
@@ -8667,7 +9008,7 @@ var SessionAlreadyArchivedError = class extends Error {
|
|
|
8667
9008
|
};
|
|
8668
9009
|
async function fileExists(path7) {
|
|
8669
9010
|
try {
|
|
8670
|
-
const stats = await
|
|
9011
|
+
const stats = await stat3(path7);
|
|
8671
9012
|
return stats.isFile();
|
|
8672
9013
|
} catch {
|
|
8673
9014
|
return false;
|
|
@@ -8709,7 +9050,7 @@ async function archiveCommand(options) {
|
|
|
8709
9050
|
}
|
|
8710
9051
|
|
|
8711
9052
|
// src/commands/session/delete.ts
|
|
8712
|
-
import { stat as
|
|
9053
|
+
import { stat as stat4, unlink } from "fs/promises";
|
|
8713
9054
|
|
|
8714
9055
|
// src/domains/session/delete.ts
|
|
8715
9056
|
function resolveDeletePath(sessionId, existingPaths) {
|
|
@@ -8882,44 +9223,6 @@ function parseSessionId(id) {
|
|
|
8882
9223
|
return date;
|
|
8883
9224
|
}
|
|
8884
9225
|
|
|
8885
|
-
// src/domains/session/types.ts
|
|
8886
|
-
var SESSION_PRIORITY = {
|
|
8887
|
-
HIGH: "high",
|
|
8888
|
-
MEDIUM: "medium",
|
|
8889
|
-
LOW: "low"
|
|
8890
|
-
};
|
|
8891
|
-
var SESSION_STATUSES = ["todo", "doing", "archive"];
|
|
8892
|
-
var DEFAULT_LIST_STATUSES = ["doing", "todo"];
|
|
8893
|
-
var SESSION_FILE_ENCODING = "utf-8";
|
|
8894
|
-
var SESSION_FILE_ERROR_CODE = {
|
|
8895
|
-
NOT_FOUND: "ENOENT"
|
|
8896
|
-
};
|
|
8897
|
-
var SESSION_OUTPUT_MARKER = {
|
|
8898
|
-
HANDOFF_ID: "HANDOFF_ID",
|
|
8899
|
-
PICKUP_ID: "PICKUP_ID",
|
|
8900
|
-
SESSION_FILE: "SESSION_FILE"
|
|
8901
|
-
};
|
|
8902
|
-
function formatSessionOutputMarker(marker, value) {
|
|
8903
|
-
return `<${marker}>${value}</${marker}>`;
|
|
8904
|
-
}
|
|
8905
|
-
var CLAIMABLE_STATUS = SESSION_STATUSES[0];
|
|
8906
|
-
var PRIORITY_ORDER = {
|
|
8907
|
-
[SESSION_PRIORITY.HIGH]: 0,
|
|
8908
|
-
[SESSION_PRIORITY.MEDIUM]: 1,
|
|
8909
|
-
[SESSION_PRIORITY.LOW]: 2
|
|
8910
|
-
};
|
|
8911
|
-
var DEFAULT_PRIORITY = SESSION_PRIORITY.MEDIUM;
|
|
8912
|
-
var SESSION_FRONT_MATTER = {
|
|
8913
|
-
PRIORITY: "priority",
|
|
8914
|
-
GIT_REF: "git_ref",
|
|
8915
|
-
GOAL: "goal",
|
|
8916
|
-
NEXT_STEP: "next_step",
|
|
8917
|
-
CREATED_AT: "created_at",
|
|
8918
|
-
AGENT_SESSION_ID: "agent_session_id",
|
|
8919
|
-
SPECS: "specs",
|
|
8920
|
-
FILES: "files"
|
|
8921
|
-
};
|
|
8922
|
-
|
|
8923
9226
|
// src/domains/session/list.ts
|
|
8924
9227
|
var FRONT_MATTER_PATTERN = /^---\r?\n([\s\S]*?)\r?\n(?:---|\.\.\.)\r?\n?/;
|
|
8925
9228
|
var SESSION_PRIORITY_VALUES = Object.values(SESSION_PRIORITY);
|
|
@@ -9158,7 +9461,7 @@ async function findExistingPaths(paths) {
|
|
|
9158
9461
|
const existing = [];
|
|
9159
9462
|
for (const path7 of paths) {
|
|
9160
9463
|
try {
|
|
9161
|
-
const stats = await
|
|
9464
|
+
const stats = await stat4(path7);
|
|
9162
9465
|
if (stats.isFile()) {
|
|
9163
9466
|
existing.push(path7);
|
|
9164
9467
|
}
|
|
@@ -9180,7 +9483,7 @@ async function deleteCommand(options) {
|
|
|
9180
9483
|
}
|
|
9181
9484
|
|
|
9182
9485
|
// src/commands/session/handoff.ts
|
|
9183
|
-
import { mkdir as mkdir2, stat as
|
|
9486
|
+
import { mkdir as mkdir2, stat as stat5, writeFile } from "fs/promises";
|
|
9184
9487
|
import { join as join10, resolve as resolve6 } from "path";
|
|
9185
9488
|
import { stringify as stringifyYaml3 } from "yaml";
|
|
9186
9489
|
|
|
@@ -9431,7 +9734,7 @@ async function rejectDirectoryInjectionEntries(entries, cwd) {
|
|
|
9431
9734
|
if (entry.length === 0) continue;
|
|
9432
9735
|
let entryIsDirectory = false;
|
|
9433
9736
|
try {
|
|
9434
|
-
entryIsDirectory = (await
|
|
9737
|
+
entryIsDirectory = (await stat5(resolve6(cwd, entry))).isDirectory();
|
|
9435
9738
|
} catch {
|
|
9436
9739
|
continue;
|
|
9437
9740
|
}
|
|
@@ -9489,7 +9792,7 @@ ${formatSessionOutputMarker(SESSION_OUTPUT_MARKER.SESSION_FILE, absolutePath)}`;
|
|
|
9489
9792
|
}
|
|
9490
9793
|
|
|
9491
9794
|
// src/commands/session/list.ts
|
|
9492
|
-
import { readdir as
|
|
9795
|
+
import { readdir as readdir3, readFile as readFile5 } from "fs/promises";
|
|
9493
9796
|
import { join as join11 } from "path";
|
|
9494
9797
|
var SESSION_LIST_FORMAT = {
|
|
9495
9798
|
TEXT: "text",
|
|
@@ -9498,13 +9801,13 @@ var SESSION_LIST_FORMAT = {
|
|
|
9498
9801
|
var SESSION_LIST_EMPTY_TEXT = "(no sessions)";
|
|
9499
9802
|
async function loadSessionsFromDir(dir, status) {
|
|
9500
9803
|
try {
|
|
9501
|
-
const files = await
|
|
9804
|
+
const files = await readdir3(dir);
|
|
9502
9805
|
const sessions = [];
|
|
9503
9806
|
for (const file of files) {
|
|
9504
9807
|
if (!file.endsWith(".md")) continue;
|
|
9505
9808
|
const id = file.replace(".md", "");
|
|
9506
9809
|
const filePath = join11(dir, file);
|
|
9507
|
-
const content = await
|
|
9810
|
+
const content = await readFile5(filePath, SESSION_FILE_ENCODING);
|
|
9508
9811
|
const metadata = parseSessionMetadata(content);
|
|
9509
9812
|
sessions.push({
|
|
9510
9813
|
id,
|
|
@@ -9567,7 +9870,7 @@ async function listCommand(options) {
|
|
|
9567
9870
|
}
|
|
9568
9871
|
|
|
9569
9872
|
// src/commands/session/pickup.ts
|
|
9570
|
-
import { mkdir as mkdir3, readdir as
|
|
9873
|
+
import { mkdir as mkdir3, readdir as readdir4, readFile as readFile6, rename as rename2 } from "fs/promises";
|
|
9571
9874
|
import { join as join12, resolve as resolve7 } from "path";
|
|
9572
9875
|
|
|
9573
9876
|
// src/domains/session/pickup.ts
|
|
@@ -9607,8 +9910,8 @@ function selectBestSession(sessions) {
|
|
|
9607
9910
|
var PICKUP_TARGET_STATUS = SESSION_STATUSES[1];
|
|
9608
9911
|
var PICKUP_DEPS = {
|
|
9609
9912
|
mkdir: mkdir3,
|
|
9610
|
-
readdir:
|
|
9611
|
-
readFile:
|
|
9913
|
+
readdir: readdir4,
|
|
9914
|
+
readFile: readFile6,
|
|
9612
9915
|
rename: rename2
|
|
9613
9916
|
};
|
|
9614
9917
|
async function loadTodoSessions(config) {
|
|
@@ -9717,7 +10020,7 @@ async function loadPickCandidates(options) {
|
|
|
9717
10020
|
}
|
|
9718
10021
|
|
|
9719
10022
|
// src/commands/session/prune.ts
|
|
9720
|
-
import { readdir as
|
|
10023
|
+
import { readdir as readdir5, readFile as readFile7, unlink as unlink2 } from "fs/promises";
|
|
9721
10024
|
import { join as join13 } from "path";
|
|
9722
10025
|
|
|
9723
10026
|
// src/domains/session/prune.ts
|
|
@@ -9762,13 +10065,13 @@ function validatePruneOptions(options) {
|
|
|
9762
10065
|
}
|
|
9763
10066
|
async function loadArchiveSessions(config) {
|
|
9764
10067
|
try {
|
|
9765
|
-
const files = await
|
|
10068
|
+
const files = await readdir5(config.archiveDir);
|
|
9766
10069
|
const sessions = [];
|
|
9767
10070
|
for (const file of files) {
|
|
9768
10071
|
if (!file.endsWith(".md")) continue;
|
|
9769
10072
|
const id = file.replace(".md", "");
|
|
9770
10073
|
const filePath = join13(config.archiveDir, file);
|
|
9771
|
-
const content = await
|
|
10074
|
+
const content = await readFile7(filePath, SESSION_FILE_ENCODING);
|
|
9772
10075
|
const metadata = parseSessionMetadata(content);
|
|
9773
10076
|
sessions.push({
|
|
9774
10077
|
id,
|
|
@@ -9817,7 +10120,7 @@ async function pruneCommand(options) {
|
|
|
9817
10120
|
}
|
|
9818
10121
|
|
|
9819
10122
|
// src/commands/session/release.ts
|
|
9820
|
-
import { readdir as
|
|
10123
|
+
import { readdir as readdir6, rename as rename3 } from "fs/promises";
|
|
9821
10124
|
|
|
9822
10125
|
// src/domains/session/release.ts
|
|
9823
10126
|
function buildReleasePaths(sessionId, config) {
|
|
@@ -9848,7 +10151,7 @@ var SESSION_RELEASE_OUTPUT = {
|
|
|
9848
10151
|
};
|
|
9849
10152
|
async function loadDoingSessions(config) {
|
|
9850
10153
|
try {
|
|
9851
|
-
const files = await
|
|
10154
|
+
const files = await readdir6(config.doingDir);
|
|
9852
10155
|
return files.filter((file) => file.endsWith(".md")).map((file) => ({ id: file.replace(".md", "") }));
|
|
9853
10156
|
} catch (error) {
|
|
9854
10157
|
if (error instanceof Error && "code" in error && error.code === SESSION_FILE_ERROR_CODE.NOT_FOUND) {
|
|
@@ -9885,12 +10188,12 @@ async function releaseCommand(options) {
|
|
|
9885
10188
|
}
|
|
9886
10189
|
|
|
9887
10190
|
// src/commands/session/show.ts
|
|
9888
|
-
import { readFile as
|
|
10191
|
+
import { readFile as readFile8, stat as stat6 } from "fs/promises";
|
|
9889
10192
|
async function findExistingPath(paths, _config) {
|
|
9890
10193
|
for (let i = 0; i < paths.length; i++) {
|
|
9891
10194
|
const filePath = paths[i];
|
|
9892
10195
|
try {
|
|
9893
|
-
const stats = await
|
|
10196
|
+
const stats = await stat6(filePath);
|
|
9894
10197
|
if (stats.isFile()) {
|
|
9895
10198
|
return { path: filePath, status: SEARCH_ORDER[i] };
|
|
9896
10199
|
}
|
|
@@ -9905,7 +10208,7 @@ async function resolveSession(sessionId, config) {
|
|
|
9905
10208
|
if (!found) {
|
|
9906
10209
|
throw new SessionNotFoundError(sessionId);
|
|
9907
10210
|
}
|
|
9908
|
-
const content = await
|
|
10211
|
+
const content = await readFile8(found.path, SESSION_FILE_ENCODING);
|
|
9909
10212
|
return { status: found.status, path: found.path, content };
|
|
9910
10213
|
}
|
|
9911
10214
|
async function showSingle(sessionId, config) {
|
|
@@ -10569,7 +10872,7 @@ var sessionDomain = {
|
|
|
10569
10872
|
};
|
|
10570
10873
|
|
|
10571
10874
|
// src/lib/spec-tree/index.ts
|
|
10572
|
-
import { readdir as
|
|
10875
|
+
import { readdir as readdir7, readFile as readFile9 } from "fs/promises";
|
|
10573
10876
|
import { join as join14 } from "path";
|
|
10574
10877
|
var SPEC_TREE_FIELD_KEY = {
|
|
10575
10878
|
VERSION: "version",
|
|
@@ -10654,7 +10957,7 @@ function createFilesystemSpecTreeSource(options) {
|
|
|
10654
10957
|
if (ref.path === void 0) {
|
|
10655
10958
|
throw new Error("Filesystem source refs require a path");
|
|
10656
10959
|
}
|
|
10657
|
-
return
|
|
10960
|
+
return readFile9(join14(options.productDir, ref.path), SPEC_TREE_TEXT_ENCODING);
|
|
10658
10961
|
}
|
|
10659
10962
|
};
|
|
10660
10963
|
}
|
|
@@ -10945,7 +11248,7 @@ async function* readFilesystemSourceEntries(productDir, registry, schemaVersions
|
|
|
10945
11248
|
async function* walkFilesystemDirectory(context) {
|
|
10946
11249
|
let entries;
|
|
10947
11250
|
try {
|
|
10948
|
-
entries = await
|
|
11251
|
+
entries = await readdir7(context.absolutePath, { withFileTypes: true });
|
|
10949
11252
|
} catch (error) {
|
|
10950
11253
|
if (isFileNotFound2(error)) return;
|
|
10951
11254
|
throw error;
|
|
@@ -11117,7 +11420,7 @@ function formatNextNode(node) {
|
|
|
11117
11420
|
}
|
|
11118
11421
|
|
|
11119
11422
|
// src/commands/test/discovery.ts
|
|
11120
|
-
import { readdir as
|
|
11423
|
+
import { readdir as readdir8 } from "fs/promises";
|
|
11121
11424
|
import { join as join15, relative as relative2, sep as sep2 } from "path";
|
|
11122
11425
|
var TESTS_DIRECTORY_NAME = SPEC_TREE_EVIDENCE_FILE.DIRECTORY_NAME;
|
|
11123
11426
|
var SPEC_ROOT_DIRECTORY = SPEC_TREE_CONFIG.ROOT_DIRECTORY;
|
|
@@ -11131,7 +11434,7 @@ async function discoverTestFiles(productDir) {
|
|
|
11131
11434
|
async function collectTestFiles(directory, insideTestsDir, found) {
|
|
11132
11435
|
let entries;
|
|
11133
11436
|
try {
|
|
11134
|
-
entries = await
|
|
11437
|
+
entries = await readdir8(directory, { withFileTypes: true });
|
|
11135
11438
|
} catch (error) {
|
|
11136
11439
|
if (hasErrorCode2(error, ERROR_CODE_NOT_FOUND2)) return;
|
|
11137
11440
|
throw error;
|
|
@@ -12542,7 +12845,7 @@ function createNodeStatusProvider(productDir) {
|
|
|
12542
12845
|
}
|
|
12543
12846
|
|
|
12544
12847
|
// src/lib/node-status/update.ts
|
|
12545
|
-
import { mkdir as mkdir4, readdir as
|
|
12848
|
+
import { mkdir as mkdir4, readdir as readdir9, rm, writeFile as writeFile2 } from "fs/promises";
|
|
12546
12849
|
import { dirname as dirname8, join as join21 } from "path";
|
|
12547
12850
|
|
|
12548
12851
|
// src/git/tracked-paths.ts
|
|
@@ -12666,7 +12969,7 @@ async function collectNodeStatusFiles(directory) {
|
|
|
12666
12969
|
}
|
|
12667
12970
|
async function readDirectoryEntries(directory) {
|
|
12668
12971
|
try {
|
|
12669
|
-
return await
|
|
12972
|
+
return await readdir9(directory, { withFileTypes: true });
|
|
12670
12973
|
} catch (error) {
|
|
12671
12974
|
if (isNodeError3(error) && error.code === "ENOENT") return [];
|
|
12672
12975
|
throw error;
|
|
@@ -13275,7 +13578,7 @@ var testingRegistry = {
|
|
|
13275
13578
|
|
|
13276
13579
|
// src/interfaces/cli/test-runner-deps.ts
|
|
13277
13580
|
import { createWriteStream } from "fs";
|
|
13278
|
-
import { mkdtemp, readFile as
|
|
13581
|
+
import { mkdtemp, readFile as readFile10 } from "fs/promises";
|
|
13279
13582
|
import { tmpdir } from "os";
|
|
13280
13583
|
import { join as join23 } from "path";
|
|
13281
13584
|
import { finished } from "stream/promises";
|
|
@@ -13325,7 +13628,7 @@ function createRunnerDepsFor(productDir, outStream = process.stdout) {
|
|
|
13325
13628
|
}
|
|
13326
13629
|
function createRelatedDepsFor(productDir) {
|
|
13327
13630
|
const runCommand = createRelatedCommandRunner(productDir);
|
|
13328
|
-
return () => ({ runCommand, readFile: (path7) =>
|
|
13631
|
+
return () => ({ runCommand, readFile: (path7) => readFile10(join23(productDir, path7), "utf8") });
|
|
13329
13632
|
}
|
|
13330
13633
|
function artifactFileName(index, suffix) {
|
|
13331
13634
|
return `${index.toString(ARTIFACT_INDEX_RADIX).padStart(ARTIFACT_INDEX_WIDTH, "0")}-${suffix}`;
|
|
@@ -17025,11 +17328,11 @@ function formatLintResult(result, quiet, durationMs) {
|
|
|
17025
17328
|
}
|
|
17026
17329
|
|
|
17027
17330
|
// src/validation/literal/index.ts
|
|
17028
|
-
import { readFile as
|
|
17331
|
+
import { readFile as readFile12 } from "fs/promises";
|
|
17029
17332
|
import { isAbsolute as isAbsolute9, relative as relative7, resolve as resolve10 } from "path";
|
|
17030
17333
|
|
|
17031
17334
|
// src/lib/file-inclusion/pipeline.ts
|
|
17032
|
-
import { readdir as
|
|
17335
|
+
import { readdir as readdir10, readFile as readFile11, stat as stat7 } from "fs/promises";
|
|
17033
17336
|
import { join as join33, relative as relative6, sep as sep3 } from "path";
|
|
17034
17337
|
|
|
17035
17338
|
// src/lib/file-inclusion/ignore-source.ts
|
|
@@ -17335,7 +17638,7 @@ function isNodeError4(err) {
|
|
|
17335
17638
|
}
|
|
17336
17639
|
async function isDirectory(absolutePath) {
|
|
17337
17640
|
try {
|
|
17338
|
-
return (await
|
|
17641
|
+
return (await stat7(absolutePath)).isDirectory();
|
|
17339
17642
|
} catch (err) {
|
|
17340
17643
|
if (isNodeError4(err) && (err.code === "ENOENT" || err.code === "ENOTDIR")) {
|
|
17341
17644
|
return false;
|
|
@@ -17345,7 +17648,7 @@ async function isDirectory(absolutePath) {
|
|
|
17345
17648
|
}
|
|
17346
17649
|
async function isGitdirPointerFile(absolutePath) {
|
|
17347
17650
|
try {
|
|
17348
|
-
const content = await
|
|
17651
|
+
const content = await readFile11(absolutePath, "utf8");
|
|
17349
17652
|
return content.startsWith(GITDIR_POINTER_PREFIX);
|
|
17350
17653
|
} catch (err) {
|
|
17351
17654
|
if (isNodeError4(err) && (err.code === "ENOENT" || err.code === "ENOTDIR")) {
|
|
@@ -17356,7 +17659,7 @@ async function isGitdirPointerFile(absolutePath) {
|
|
|
17356
17659
|
}
|
|
17357
17660
|
async function readDirectoryEntries2(absoluteDir) {
|
|
17358
17661
|
try {
|
|
17359
|
-
return await
|
|
17662
|
+
return await readdir10(absoluteDir, { withFileTypes: true });
|
|
17360
17663
|
} catch (err) {
|
|
17361
17664
|
if (isNodeError4(err) && (err.code === "ENOENT" || err.code === "ENOTDIR")) {
|
|
17362
17665
|
return [];
|
|
@@ -17969,7 +18272,7 @@ async function validateLiteralReuse(input) {
|
|
|
17969
18272
|
}
|
|
17970
18273
|
async function readSafe(path7) {
|
|
17971
18274
|
try {
|
|
17972
|
-
return await
|
|
18275
|
+
return await readFile12(path7, "utf8");
|
|
17973
18276
|
} catch (err) {
|
|
17974
18277
|
if (typeof err === "object" && err !== null && "code" in err) {
|
|
17975
18278
|
const code = err.code;
|
|
@@ -19264,7 +19567,7 @@ var verificationContextDomain = {
|
|
|
19264
19567
|
};
|
|
19265
19568
|
|
|
19266
19569
|
// src/interfaces/cli/verify.ts
|
|
19267
|
-
import { readFile as
|
|
19570
|
+
import { readFile as readFile13 } from "fs/promises";
|
|
19268
19571
|
|
|
19269
19572
|
// src/commands/verify/cli.ts
|
|
19270
19573
|
import { dirname as dirname12 } from "path";
|
|
@@ -19741,7 +20044,7 @@ async function readStdinText() {
|
|
|
19741
20044
|
}
|
|
19742
20045
|
async function readCliSource(source) {
|
|
19743
20046
|
if (source === VERIFY_INPUT_SOURCE.STDIN) return readStdinText();
|
|
19744
|
-
return
|
|
20047
|
+
return readFile13(source, CLI_SOURCE_ENCODING);
|
|
19745
20048
|
}
|
|
19746
20049
|
var verifyDomain = {
|
|
19747
20050
|
name: VERIFY_CLI.commandName,
|
|
@@ -19927,11 +20230,11 @@ function renderTextStatusChild(record6) {
|
|
|
19927
20230
|
}
|
|
19928
20231
|
|
|
19929
20232
|
// src/lib/worktree-path-info.ts
|
|
19930
|
-
import { stat as
|
|
20233
|
+
import { stat as stat8 } from "fs/promises";
|
|
19931
20234
|
var defaultWorktreePathInfo = {
|
|
19932
20235
|
isExistingNonDirectory: async (path7) => {
|
|
19933
20236
|
try {
|
|
19934
|
-
const pathStats = await
|
|
20237
|
+
const pathStats = await stat8(path7);
|
|
19935
20238
|
return !pathStats.isDirectory();
|
|
19936
20239
|
} catch {
|
|
19937
20240
|
return false;
|