@basou/cli 0.68.4 → 0.70.0
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/index.js +277 -235
- package/dist/index.js.map +1 -1
- package/dist/program.js +277 -235
- package/dist/program.js.map +1 -1
- package/package.json +2 -2
package/dist/program.js
CHANGED
|
@@ -28,7 +28,8 @@ import {
|
|
|
28
28
|
prefixedUlid,
|
|
29
29
|
readSessionYaml,
|
|
30
30
|
readYamlFile,
|
|
31
|
-
replayEvents
|
|
31
|
+
replayEvents,
|
|
32
|
+
SessionFileNotAFileError
|
|
32
33
|
} from "@basou/core";
|
|
33
34
|
|
|
34
35
|
// src/lib/error-render.ts
|
|
@@ -126,7 +127,7 @@ function printSessionSkip(sid, reason) {
|
|
|
126
127
|
const short = shortSessionId(sid);
|
|
127
128
|
if (reason === "events_jsonl_unreadable") {
|
|
128
129
|
console.error(`Warning: skipped suspect check for ${short}: events.jsonl unreadable`);
|
|
129
|
-
} else if (reason === "session_dir_not_directory") {
|
|
130
|
+
} else if (reason === "session_dir_not_directory" || reason === "session_yaml_not_a_file") {
|
|
130
131
|
console.error(`Skipped ${sid}: ${reason}`);
|
|
131
132
|
} else {
|
|
132
133
|
console.error(`Skipped ${short}: ${reason}`);
|
|
@@ -147,6 +148,11 @@ function printSessionListSkip(sid, reason) {
|
|
|
147
148
|
case "session_dir_not_directory":
|
|
148
149
|
console.error(`Skipped ${sid}: not a directory (a symlink or a file is not followed)`);
|
|
149
150
|
break;
|
|
151
|
+
case "session_yaml_not_a_file":
|
|
152
|
+
console.error(
|
|
153
|
+
`Skipped ${sid}: session.yaml is not a regular file (a symlink or anything else is not followed)`
|
|
154
|
+
);
|
|
155
|
+
break;
|
|
150
156
|
}
|
|
151
157
|
}
|
|
152
158
|
function printTaskSkip(taskId, reason) {
|
|
@@ -423,7 +429,7 @@ async function doRunApprovalShow(idInput, options, ctx) {
|
|
|
423
429
|
`Warning: session ${sessionId} is not a directory (a symlink or a file is not followed); its events are not shown`
|
|
424
430
|
);
|
|
425
431
|
} else {
|
|
426
|
-
for await (const ev of replayEvents(
|
|
432
|
+
for await (const ev of replayEvents(paths, sessionId, {
|
|
427
433
|
onWarning: (w) => printReplayWarning(w, sessionId)
|
|
428
434
|
})) {
|
|
429
435
|
if (isApprovalEvent(ev) && ev.approval_id === id) {
|
|
@@ -523,8 +529,7 @@ async function doRunApprovalResolve(idInput, options, ctx, decision) {
|
|
|
523
529
|
const sessionLock = await acquireLock(paths, "session", approval.session_id);
|
|
524
530
|
try {
|
|
525
531
|
await assertSessionDirSafe(paths, approval.session_id);
|
|
526
|
-
const
|
|
527
|
-
for await (const ev of replayEvents(sessionDir, {
|
|
532
|
+
for await (const ev of replayEvents(paths, approval.session_id, {
|
|
528
533
|
onWarning: (w) => printReplayWarning(w, approval.session_id)
|
|
529
534
|
})) {
|
|
530
535
|
if (isApprovalEvent(ev) && ev.approval_id === approval.id && (ev.type === "approval_approved" || ev.type === "approval_rejected" || ev.type === "approval_expired")) {
|
|
@@ -537,7 +542,8 @@ async function doRunApprovalResolve(idInput, options, ctx, decision) {
|
|
|
537
542
|
let sessionStatus = null;
|
|
538
543
|
try {
|
|
539
544
|
sessionStatus = (await readSessionYaml(paths, approval.session_id)).session.status;
|
|
540
|
-
} catch {
|
|
545
|
+
} catch (error) {
|
|
546
|
+
if (error instanceof SessionFileNotAFileError) throw error;
|
|
541
547
|
sessionStatus = null;
|
|
542
548
|
}
|
|
543
549
|
const attachable = sessionStatus === "initialized" || sessionStatus === "running" || sessionStatus === "waiting_approval";
|
|
@@ -1046,6 +1052,7 @@ import {
|
|
|
1046
1052
|
readSessionYaml as readSessionYaml2,
|
|
1047
1053
|
reimportPreservingId,
|
|
1048
1054
|
SESSION_IMPORT_SCHEMA_VERSION,
|
|
1055
|
+
SessionFileNotAFileError as SessionFileNotAFileError2,
|
|
1049
1056
|
SessionImportPayloadSchema
|
|
1050
1057
|
} from "@basou/core";
|
|
1051
1058
|
|
|
@@ -1219,10 +1226,11 @@ async function resolveImportTarget(ctx) {
|
|
|
1219
1226
|
async function importDerivedSessions(paths, manifest, options, sourceKind, candidates, projectPaths, boundaryDeclared) {
|
|
1220
1227
|
await assertSessionStoreSafe(paths);
|
|
1221
1228
|
if (options.dryRun !== true) await assertLockStoreSafe2(paths);
|
|
1222
|
-
const {
|
|
1223
|
-
|
|
1224
|
-
|
|
1225
|
-
|
|
1229
|
+
const {
|
|
1230
|
+
byExternalId: existingByExternalId,
|
|
1231
|
+
notDirectories,
|
|
1232
|
+
yamlNotFiles
|
|
1233
|
+
} = await loadExistingByExternalId(paths, sourceKind);
|
|
1226
1234
|
let notDirectoriesWarned = false;
|
|
1227
1235
|
const seenThisRun = /* @__PURE__ */ new Set();
|
|
1228
1236
|
const crossProjectCheck = boundaryDeclared;
|
|
@@ -1315,12 +1323,20 @@ async function importDerivedSessions(paths, manifest, options, sourceKind, candi
|
|
|
1315
1323
|
}
|
|
1316
1324
|
counts.replaced++;
|
|
1317
1325
|
}
|
|
1318
|
-
if (priors.length === 0 && notDirectories.length > 0 && !notDirectoriesWarned) {
|
|
1326
|
+
if (priors.length === 0 && (notDirectories.length > 0 || yamlNotFiles.length > 0) && !notDirectoriesWarned) {
|
|
1319
1327
|
notDirectoriesWarned = true;
|
|
1320
|
-
|
|
1321
|
-
|
|
1322
|
-
|
|
1323
|
-
|
|
1328
|
+
if (notDirectories.length > 0) {
|
|
1329
|
+
const one = notDirectories.length === 1;
|
|
1330
|
+
console.error(
|
|
1331
|
+
`Import: ${notDirectories.join(", ")} ${one ? "is not a directory" : "are not directories"} (a symlink or a file is not followed), so ${one ? "it was" : "they were"} not checked for an earlier import; a session imported there may be imported again`
|
|
1332
|
+
);
|
|
1333
|
+
}
|
|
1334
|
+
if (yamlNotFiles.length > 0) {
|
|
1335
|
+
const one = yamlNotFiles.length === 1;
|
|
1336
|
+
console.error(
|
|
1337
|
+
`Import: the session.yaml of ${yamlNotFiles.join(", ")} ${one ? "is" : "are"} not a regular file (a symlink or anything else is not followed), so ${one ? "it was" : "they were"} not checked for an earlier import; a session imported there may be imported again`
|
|
1338
|
+
);
|
|
1339
|
+
}
|
|
1324
1340
|
}
|
|
1325
1341
|
const result = await importSessionFromJson(paths, manifest, payload, {
|
|
1326
1342
|
dryRun: options.dryRun === true
|
|
@@ -1402,13 +1418,15 @@ async function loadExistingByExternalId(paths, sourceKind) {
|
|
|
1402
1418
|
try {
|
|
1403
1419
|
({ dirs: sessionIds, notDirectories } = await enumerateSessionEntries(paths));
|
|
1404
1420
|
} catch {
|
|
1405
|
-
return { byExternalId, notDirectories: [] };
|
|
1421
|
+
return { byExternalId, notDirectories: [], yamlNotFiles: [] };
|
|
1406
1422
|
}
|
|
1423
|
+
const yamlNotFiles = [];
|
|
1407
1424
|
for (const sessionId of sessionIds) {
|
|
1408
1425
|
let session;
|
|
1409
1426
|
try {
|
|
1410
1427
|
session = await readSessionYaml2(paths, sessionId);
|
|
1411
|
-
} catch {
|
|
1428
|
+
} catch (error) {
|
|
1429
|
+
if (error instanceof SessionFileNotAFileError2) yamlNotFiles.push(sessionId);
|
|
1412
1430
|
continue;
|
|
1413
1431
|
}
|
|
1414
1432
|
if (session.session.source.kind !== sourceKind) continue;
|
|
@@ -1423,7 +1441,7 @@ async function loadExistingByExternalId(paths, sourceKind) {
|
|
|
1423
1441
|
const match = typeof label === "string" ? label.match(/^claude-code import (\S+)$/) : null;
|
|
1424
1442
|
if (match?.[1] !== void 0) add(match[1], prior);
|
|
1425
1443
|
}
|
|
1426
|
-
return { byExternalId, notDirectories };
|
|
1444
|
+
return { byExternalId, notDirectories, yamlNotFiles };
|
|
1427
1445
|
}
|
|
1428
1446
|
async function selectTranscriptFiles(projectsRoot, projectPaths, options) {
|
|
1429
1447
|
if (options.session !== void 0) {
|
|
@@ -2956,9 +2974,8 @@ async function doRunChannelClear(face, options) {
|
|
|
2956
2974
|
}
|
|
2957
2975
|
|
|
2958
2976
|
// src/commands/decision.ts
|
|
2959
|
-
import { readFile as readFile3 } from "fs/promises";
|
|
2960
2977
|
import { homedir as homedir3 } from "os";
|
|
2961
|
-
import {
|
|
2978
|
+
import { resolve as resolve4 } from "path";
|
|
2962
2979
|
import {
|
|
2963
2980
|
AGENT_INFRA_DIRS as AGENT_INFRA_DIRS2,
|
|
2964
2981
|
acquireLock as acquireLock2,
|
|
@@ -2969,7 +2986,7 @@ import {
|
|
|
2969
2986
|
createAdHocSessionWithEvent,
|
|
2970
2987
|
displayPath as displayPath4,
|
|
2971
2988
|
EVENT_SCHEMA_VERSION as EVENT_SCHEMA_VERSION2,
|
|
2972
|
-
findErrorCode as
|
|
2989
|
+
findErrorCode as findErrorCode5,
|
|
2973
2990
|
isValidPrefixedId,
|
|
2974
2991
|
LOCAL_CLI_EVENT_SOURCE as LOCAL_CLI_EVENT_SOURCE2,
|
|
2975
2992
|
loadSessionEntries,
|
|
@@ -2981,6 +2998,35 @@ import {
|
|
|
2981
2998
|
} from "@basou/core";
|
|
2982
2999
|
import { InvalidArgumentError as InvalidArgumentError2 } from "commander";
|
|
2983
3000
|
|
|
3001
|
+
// src/lib/input-file.ts
|
|
3002
|
+
import { readFile as readFile3 } from "fs/promises";
|
|
3003
|
+
import { findErrorCode as findErrorCode4 } from "@basou/core";
|
|
3004
|
+
function refuseDashInputFile(flag, value, alternative) {
|
|
3005
|
+
if (value !== "-") return;
|
|
3006
|
+
if (alternative === "stdin") {
|
|
3007
|
+
throw new Error(
|
|
3008
|
+
`${flag} - is refused: - is reserved for standard input. Omit ${flag} to read stdin, or pass ${flag} ./- to read a file named -.`
|
|
3009
|
+
);
|
|
3010
|
+
}
|
|
3011
|
+
const toFile = "textFlag" in alternative ? `Write the text to a file and pass ${flag} <path>, or pass it with ${alternative.textFlag}` : `Write ${alternative.fileOnly} to a file and pass its path with ${flag}`;
|
|
3012
|
+
throw new Error(
|
|
3013
|
+
`${flag} - is refused: - is reserved for standard input, which this command does not read. ${toFile}; a file named - is read with ${flag} ./-.`
|
|
3014
|
+
);
|
|
3015
|
+
}
|
|
3016
|
+
async function readInputFileText(flag, path) {
|
|
3017
|
+
try {
|
|
3018
|
+
return await readFile3(path, "utf8");
|
|
3019
|
+
} catch (error) {
|
|
3020
|
+
if (findErrorCode4(error, "ENOENT")) {
|
|
3021
|
+
throw new Error(`${flag} names a file that does not exist`, { cause: error });
|
|
3022
|
+
}
|
|
3023
|
+
if (findErrorCode4(error, "EISDIR")) {
|
|
3024
|
+
throw new Error(`${flag} names a directory, not a file`, { cause: error });
|
|
3025
|
+
}
|
|
3026
|
+
throw new Error(`Could not read the file ${flag} names`, { cause: error });
|
|
3027
|
+
}
|
|
3028
|
+
}
|
|
3029
|
+
|
|
2984
3030
|
// src/commands/decision-gaps.ts
|
|
2985
3031
|
import {
|
|
2986
3032
|
basouPaths as basouPaths5,
|
|
@@ -3348,6 +3394,7 @@ async function runDecisionCapture(options, ctx = {}) {
|
|
|
3348
3394
|
}
|
|
3349
3395
|
}
|
|
3350
3396
|
async function doRunDecisionCapture(options, ctx) {
|
|
3397
|
+
refuseDashInputFile("--file", options.file, "stdin");
|
|
3351
3398
|
const cwd = ctx.cwd ?? process.cwd();
|
|
3352
3399
|
const repositoryRoot = await resolveBasouRootForCommand(cwd, "decision capture");
|
|
3353
3400
|
const paths = basouPaths6(repositoryRoot);
|
|
@@ -3509,9 +3556,8 @@ function isDecisionId(value) {
|
|
|
3509
3556
|
async function decisionExists(paths, decisionId) {
|
|
3510
3557
|
const entries = await loadSessionEntries(paths, { now: /* @__PURE__ */ new Date() });
|
|
3511
3558
|
for (const entry of entries) {
|
|
3512
|
-
const sessionDir = join7(paths.sessions, entry.sessionId);
|
|
3513
3559
|
try {
|
|
3514
|
-
for await (const ev of replayEvents2(
|
|
3560
|
+
for await (const ev of replayEvents2(paths, entry.sessionId, {})) {
|
|
3515
3561
|
if (ev.type === "decision_recorded" && ev.decision_id === decisionId) return true;
|
|
3516
3562
|
}
|
|
3517
3563
|
} catch {
|
|
@@ -3563,14 +3609,7 @@ function parseReason(raw) {
|
|
|
3563
3609
|
}
|
|
3564
3610
|
async function readCaptureInput(options, ctx) {
|
|
3565
3611
|
if (options.file !== void 0) {
|
|
3566
|
-
|
|
3567
|
-
return await readFile3(options.file, "utf8");
|
|
3568
|
-
} catch (error) {
|
|
3569
|
-
if (findErrorCode4(error, "ENOENT")) {
|
|
3570
|
-
throw new Error(`Input file not found: ${options.file}`);
|
|
3571
|
-
}
|
|
3572
|
-
throw error;
|
|
3573
|
-
}
|
|
3612
|
+
return await readInputFileText("--file", options.file);
|
|
3574
3613
|
}
|
|
3575
3614
|
if (ctx.readInput !== void 0) {
|
|
3576
3615
|
return await ctx.readInput();
|
|
@@ -3909,7 +3948,7 @@ async function assertWorkspaceInitialized4(basouRoot) {
|
|
|
3909
3948
|
try {
|
|
3910
3949
|
await assertBasouRootSafe4(basouRoot);
|
|
3911
3950
|
} catch (error) {
|
|
3912
|
-
if (
|
|
3951
|
+
if (findErrorCode5(error, "ENOENT")) {
|
|
3913
3952
|
throw new Error("Workspace not initialized. Run 'basou init' first.");
|
|
3914
3953
|
}
|
|
3915
3954
|
throw error;
|
|
@@ -3920,7 +3959,7 @@ async function assertWorkspaceInitialized4(basouRoot) {
|
|
|
3920
3959
|
import {
|
|
3921
3960
|
assertBasouRootSafe as assertBasouRootSafe5,
|
|
3922
3961
|
basouPaths as basouPaths7,
|
|
3923
|
-
findErrorCode as
|
|
3962
|
+
findErrorCode as findErrorCode6,
|
|
3924
3963
|
readMarkdownFile as readMarkdownFile4,
|
|
3925
3964
|
renderDecisions as renderDecisions2,
|
|
3926
3965
|
renderWithMarkers as renderWithMarkers2,
|
|
@@ -3962,7 +4001,7 @@ async function assertWorkspaceInitialized5(basouRoot) {
|
|
|
3962
4001
|
try {
|
|
3963
4002
|
await assertBasouRootSafe5(basouRoot);
|
|
3964
4003
|
} catch (error) {
|
|
3965
|
-
if (
|
|
4004
|
+
if (findErrorCode6(error, "ENOENT")) {
|
|
3966
4005
|
throw new Error("Workspace not initialized. Run 'basou init' first.");
|
|
3967
4006
|
}
|
|
3968
4007
|
throw error;
|
|
@@ -3972,7 +4011,7 @@ async function assertWorkspaceInitialized5(basouRoot) {
|
|
|
3972
4011
|
// src/commands/exec.ts
|
|
3973
4012
|
import { mkdir as mkdir2, rm as rm2 } from "fs/promises";
|
|
3974
4013
|
import { homedir as homedir4 } from "os";
|
|
3975
|
-
import { join as
|
|
4014
|
+
import { join as join7 } from "path";
|
|
3976
4015
|
import {
|
|
3977
4016
|
acquireLock as acquireLock3,
|
|
3978
4017
|
assertBasouRootSafe as assertBasouRootSafe6,
|
|
@@ -3983,17 +4022,37 @@ import {
|
|
|
3983
4022
|
EVENT_SCHEMA_VERSION as EVENT_SCHEMA_VERSION3,
|
|
3984
4023
|
finalizeSessionYaml,
|
|
3985
4024
|
getSnapshot,
|
|
4025
|
+
lstatSessionFile,
|
|
3986
4026
|
overwriteYamlFile,
|
|
3987
4027
|
parseDuration,
|
|
3988
4028
|
prefixedUlid as prefixedUlid3,
|
|
3989
4029
|
readManifest as readManifest6,
|
|
3990
4030
|
readYamlFile as readYamlFile2,
|
|
3991
4031
|
SESSION_SCHEMA_VERSION,
|
|
4032
|
+
SessionFileNotAFileError as SessionFileNotAFileError3,
|
|
3992
4033
|
SessionSchema,
|
|
3993
4034
|
sanitizeWorkingDirectory,
|
|
3994
4035
|
writeObservedDuration,
|
|
3995
4036
|
writeYamlFile
|
|
3996
4037
|
} from "@basou/core";
|
|
4038
|
+
|
|
4039
|
+
// src/lib/signal-exit-code.ts
|
|
4040
|
+
import { constants } from "os";
|
|
4041
|
+
var PROMISED_SIGNAL_NUMBERS = {
|
|
4042
|
+
SIGHUP: 1,
|
|
4043
|
+
SIGINT: 2,
|
|
4044
|
+
SIGQUIT: 3,
|
|
4045
|
+
SIGKILL: 9,
|
|
4046
|
+
SIGTERM: 15
|
|
4047
|
+
};
|
|
4048
|
+
var UNNUMBERED_SIGNAL_EXIT_CODE = 255;
|
|
4049
|
+
function signalToExitCode(sig) {
|
|
4050
|
+
if (sig === null) return 1;
|
|
4051
|
+
const num = PROMISED_SIGNAL_NUMBERS[sig] ?? constants.signals[sig];
|
|
4052
|
+
return num === void 0 ? UNNUMBERED_SIGNAL_EXIT_CODE : 128 + num;
|
|
4053
|
+
}
|
|
4054
|
+
|
|
4055
|
+
// src/commands/exec.ts
|
|
3997
4056
|
function registerExecCommand(program) {
|
|
3998
4057
|
program.command("exec <command> [args...]").description("Execute a command and record it as a Basou session").passThroughOptions().option("--timeout <duration>", "Kill the child after this duration (e.g. 30s, 5m, 1h)").option("--no-snapshot", "Skip git_snapshot before/after the command").option("--cwd <path>", "Run from a Basou root other than process.cwd()").option("-v, --verbose", "Show error causes").action(async (command, args, options) => {
|
|
3999
4058
|
try {
|
|
@@ -4016,12 +4075,12 @@ async function runExec(command, args, options, ctx = {}) {
|
|
|
4016
4075
|
const manifest = await readManifest6(paths);
|
|
4017
4076
|
await assertSessionStoreSafe2(paths);
|
|
4018
4077
|
const sessionId = prefixedUlid3("ses");
|
|
4019
|
-
const sessionDir =
|
|
4078
|
+
const sessionDir = join7(paths.sessions, sessionId);
|
|
4020
4079
|
const appendEvent = ctx.appendEvent ?? (async (_sessionDir, event) => {
|
|
4021
4080
|
await coreAppendChainedEvent(paths, sessionId, event);
|
|
4022
4081
|
});
|
|
4023
4082
|
const startedAt = now().toISOString();
|
|
4024
|
-
const sessionYamlPath =
|
|
4083
|
+
const sessionYamlPath = join7(sessionDir, "session.yaml");
|
|
4025
4084
|
const session = buildInitialSession({
|
|
4026
4085
|
id: sessionId,
|
|
4027
4086
|
command,
|
|
@@ -4070,7 +4129,7 @@ async function runExec(command, args, options, ctx = {}) {
|
|
|
4070
4129
|
});
|
|
4071
4130
|
const runningLock = await acquireLock3(paths, "session", sessionId);
|
|
4072
4131
|
try {
|
|
4073
|
-
await mutateSessionYaml(
|
|
4132
|
+
await mutateSessionYaml(paths, sessionId, (s) => {
|
|
4074
4133
|
s.session.status = "running";
|
|
4075
4134
|
});
|
|
4076
4135
|
} finally {
|
|
@@ -4190,18 +4249,6 @@ function decideFinalStatus(result, signalReceived) {
|
|
|
4190
4249
|
if (result.exit_code === 0) return "completed";
|
|
4191
4250
|
return "failed";
|
|
4192
4251
|
}
|
|
4193
|
-
var SIGNUM_MAP = {
|
|
4194
|
-
SIGHUP: 1,
|
|
4195
|
-
SIGINT: 2,
|
|
4196
|
-
SIGQUIT: 3,
|
|
4197
|
-
SIGKILL: 9,
|
|
4198
|
-
SIGTERM: 15
|
|
4199
|
-
};
|
|
4200
|
-
function signalToExitCode(sig) {
|
|
4201
|
-
if (sig === null) return 1;
|
|
4202
|
-
const num = SIGNUM_MAP[sig] ?? 1;
|
|
4203
|
-
return 128 + num;
|
|
4204
|
-
}
|
|
4205
4252
|
async function tryAppendGitSnapshot(sessionDir, sessionId, repoRoot, now, appendEvent) {
|
|
4206
4253
|
let snapshot;
|
|
4207
4254
|
try {
|
|
@@ -4259,7 +4306,14 @@ function buildInitialSession(input) {
|
|
|
4259
4306
|
}
|
|
4260
4307
|
};
|
|
4261
4308
|
}
|
|
4262
|
-
async function mutateSessionYaml(
|
|
4309
|
+
async function mutateSessionYaml(paths, sessionId, mutator) {
|
|
4310
|
+
const kind = await lstatSessionFile(paths, sessionId, "session.yaml").catch((error) => {
|
|
4311
|
+
throw new Error("Failed to read YAML file", { cause: error });
|
|
4312
|
+
});
|
|
4313
|
+
if (kind === "symlink" || kind === "not_a_file") {
|
|
4314
|
+
throw new SessionFileNotAFileError3(sessionId, "session.yaml", kind);
|
|
4315
|
+
}
|
|
4316
|
+
const filePath = join7(paths.sessions, sessionId, "session.yaml");
|
|
4263
4317
|
const raw = await readYamlFile2(filePath);
|
|
4264
4318
|
const parsed = SessionSchema.parse(raw);
|
|
4265
4319
|
mutator(parsed);
|
|
@@ -4313,7 +4367,7 @@ async function finalizeSessionAsFailed(paths, sessionDir, sessionId, appendEvent
|
|
|
4313
4367
|
import {
|
|
4314
4368
|
assertBasouRootSafe as assertBasouRootSafe7,
|
|
4315
4369
|
basouPaths as basouPaths9,
|
|
4316
|
-
findErrorCode as
|
|
4370
|
+
findErrorCode as findErrorCode7,
|
|
4317
4371
|
readMarkdownFile as readMarkdownFile5,
|
|
4318
4372
|
renderHandoff as renderHandoff2,
|
|
4319
4373
|
renderWithMarkers as renderWithMarkers3,
|
|
@@ -4371,7 +4425,7 @@ async function assertWorkspaceInitialized6(basouRoot) {
|
|
|
4371
4425
|
try {
|
|
4372
4426
|
await assertBasouRootSafe7(basouRoot);
|
|
4373
4427
|
} catch (error) {
|
|
4374
|
-
if (
|
|
4428
|
+
if (findErrorCode7(error, "ENOENT")) {
|
|
4375
4429
|
throw new Error("Workspace not initialized. Run 'basou init' first.");
|
|
4376
4430
|
}
|
|
4377
4431
|
throw error;
|
|
@@ -4382,7 +4436,7 @@ async function assertWorkspaceInitialized6(basouRoot) {
|
|
|
4382
4436
|
import { execFile } from "child_process";
|
|
4383
4437
|
import { open as open2, readFile as readFile4, realpath as realpath4, stat as stat4 } from "fs/promises";
|
|
4384
4438
|
import { homedir as homedir7 } from "os";
|
|
4385
|
-
import { join as
|
|
4439
|
+
import { join as join10 } from "path";
|
|
4386
4440
|
import { fileURLToPath } from "url";
|
|
4387
4441
|
import { promisify } from "util";
|
|
4388
4442
|
import {
|
|
@@ -4543,15 +4597,15 @@ function describeCodexHookTrust(trust) {
|
|
|
4543
4597
|
|
|
4544
4598
|
// src/lib/protocols-config.ts
|
|
4545
4599
|
import { homedir as homedir5 } from "os";
|
|
4546
|
-
import { isAbsolute, join as
|
|
4600
|
+
import { isAbsolute, join as join8, resolve as resolve5 } from "path";
|
|
4547
4601
|
import { readYamlFile as readYamlFile3 } from "@basou/core";
|
|
4548
|
-
var DEFAULT_PROTOCOLS_CONFIG_PATH =
|
|
4549
|
-
var DEFAULT_TARGET_PATH =
|
|
4602
|
+
var DEFAULT_PROTOCOLS_CONFIG_PATH = join8(homedir5(), ".basou", "protocols.yaml");
|
|
4603
|
+
var DEFAULT_TARGET_PATH = join8(homedir5(), ".claude", "CLAUDE.md");
|
|
4550
4604
|
var ALLOWED_TOP_KEYS = /* @__PURE__ */ new Set(["version", "protocols"]);
|
|
4551
4605
|
var ALLOWED_ENTRY_KEYS = /* @__PURE__ */ new Set(["source", "title"]);
|
|
4552
4606
|
function expandTilde(p) {
|
|
4553
4607
|
if (p === "~") return homedir5();
|
|
4554
|
-
if (p.startsWith("~/")) return
|
|
4608
|
+
if (p.startsWith("~/")) return join8(homedir5(), p.slice(2));
|
|
4555
4609
|
return p;
|
|
4556
4610
|
}
|
|
4557
4611
|
function isRecord(value) {
|
|
@@ -4622,19 +4676,19 @@ async function loadProtocolsConfig(configPath = DEFAULT_PROTOCOLS_CONFIG_PATH) {
|
|
|
4622
4676
|
import {
|
|
4623
4677
|
assertBasouRootSafe as assertBasouRootSafe8,
|
|
4624
4678
|
basouPaths as basouPaths10,
|
|
4625
|
-
findErrorCode as
|
|
4679
|
+
findErrorCode as findErrorCode8,
|
|
4626
4680
|
renderOrientation as renderOrientation2,
|
|
4627
4681
|
writeMarkdownFile as writeMarkdownFile4
|
|
4628
4682
|
} from "@basou/core";
|
|
4629
4683
|
|
|
4630
4684
|
// src/lib/hosts-config.ts
|
|
4631
4685
|
import { homedir as homedir6 } from "os";
|
|
4632
|
-
import { isAbsolute as isAbsolute2, join as
|
|
4686
|
+
import { isAbsolute as isAbsolute2, join as join9, resolve as resolve6 } from "path";
|
|
4633
4687
|
import { readYamlFile as readYamlFile4 } from "@basou/core";
|
|
4634
|
-
var DEFAULT_HOSTS_CONFIG_PATH =
|
|
4688
|
+
var DEFAULT_HOSTS_CONFIG_PATH = join9(homedir6(), ".basou", "hosts.yaml");
|
|
4635
4689
|
function expandTilde2(p) {
|
|
4636
4690
|
if (p === "~") return homedir6();
|
|
4637
|
-
if (p.startsWith("~/")) return
|
|
4691
|
+
if (p.startsWith("~/")) return join9(homedir6(), p.slice(2));
|
|
4638
4692
|
return p;
|
|
4639
4693
|
}
|
|
4640
4694
|
function isRecord2(value) {
|
|
@@ -4779,7 +4833,7 @@ async function assertWorkspaceInitialized7(basouRoot) {
|
|
|
4779
4833
|
try {
|
|
4780
4834
|
await assertBasouRootSafe8(basouRoot);
|
|
4781
4835
|
} catch (error) {
|
|
4782
|
-
if (
|
|
4836
|
+
if (findErrorCode8(error, "ENOENT")) {
|
|
4783
4837
|
throw new Error("Workspace not initialized. Run 'basou init' first.");
|
|
4784
4838
|
}
|
|
4785
4839
|
throw error;
|
|
@@ -5218,7 +5272,7 @@ function parseMinEdits(raw) {
|
|
|
5218
5272
|
if (raw === void 0 || !/^\d+$/.test(raw)) return void 0;
|
|
5219
5273
|
return Number(raw);
|
|
5220
5274
|
}
|
|
5221
|
-
var DEFAULT_CLAUDE_SETTINGS_PATH =
|
|
5275
|
+
var DEFAULT_CLAUDE_SETTINGS_PATH = join10(homedir7(), ".claude", "settings.json");
|
|
5222
5276
|
function resolveCliEntry() {
|
|
5223
5277
|
return fileURLToPath(import.meta.url);
|
|
5224
5278
|
}
|
|
@@ -5575,7 +5629,7 @@ function describeHookMode(tiers) {
|
|
|
5575
5629
|
const gates = tiers.review ? "capture + review" : "capture";
|
|
5576
5630
|
return `${enforcement}, ${gates}`;
|
|
5577
5631
|
}
|
|
5578
|
-
var DEFAULT_CODEX_FACE_PATH =
|
|
5632
|
+
var DEFAULT_CODEX_FACE_PATH = join10(homedir7(), ".codex", "AGENTS.md");
|
|
5579
5633
|
var LEFTOVER_FACE_NOTE = (label) => `${label} still carries an orientation block rendered by an earlier basou (0.39 or before); every Codex session on this machine reads it. \`basou channel clear codex\` removes it.`;
|
|
5580
5634
|
async function faceHasLeftoverOrientationBlock(facePath) {
|
|
5581
5635
|
try {
|
|
@@ -5587,8 +5641,8 @@ async function faceHasLeftoverOrientationBlock(facePath) {
|
|
|
5587
5641
|
return false;
|
|
5588
5642
|
}
|
|
5589
5643
|
}
|
|
5590
|
-
var DEFAULT_CODEX_HOOKS_PATH =
|
|
5591
|
-
var DEFAULT_CODEX_CONFIG_PATH =
|
|
5644
|
+
var DEFAULT_CODEX_HOOKS_PATH = join10(homedir7(), ".codex", "hooks.json");
|
|
5645
|
+
var DEFAULT_CODEX_CONFIG_PATH = join10(homedir7(), ".codex", "config.toml");
|
|
5592
5646
|
async function readHooksFile(path) {
|
|
5593
5647
|
let raw;
|
|
5594
5648
|
try {
|
|
@@ -5834,7 +5888,6 @@ function renderGitignoreWarning(error, verbose) {
|
|
|
5834
5888
|
}
|
|
5835
5889
|
|
|
5836
5890
|
// src/commands/note.ts
|
|
5837
|
-
import { readFile as readFile5 } from "fs/promises";
|
|
5838
5891
|
import { homedir as homedir8 } from "os";
|
|
5839
5892
|
import { resolve as resolve8 } from "path";
|
|
5840
5893
|
import {
|
|
@@ -5844,7 +5897,7 @@ import {
|
|
|
5844
5897
|
basouPaths as basouPaths12,
|
|
5845
5898
|
createAdHocSessionWithEvent as createAdHocSessionWithEvent2,
|
|
5846
5899
|
EVENT_SCHEMA_VERSION as EVENT_SCHEMA_VERSION4,
|
|
5847
|
-
findErrorCode as
|
|
5900
|
+
findErrorCode as findErrorCode9,
|
|
5848
5901
|
LOCAL_CLI_EVENT_SOURCE as LOCAL_CLI_EVENT_SOURCE3,
|
|
5849
5902
|
readManifest as readManifest8,
|
|
5850
5903
|
resolveSessionId as resolveSessionId2,
|
|
@@ -6005,22 +6058,12 @@ async function readNoteBody(bodyArgument, options, ctx, cwd) {
|
|
|
6005
6058
|
if (bodyArgument !== void 0 && options.file !== void 0) {
|
|
6006
6059
|
throw new Error("Pass the note text either as an argument or with --file, not both.");
|
|
6007
6060
|
}
|
|
6061
|
+
refuseDashInputFile("--file", options.file, "stdin");
|
|
6008
6062
|
if (bodyArgument !== void 0) {
|
|
6009
6063
|
return { body: bodyArgument, source: "argument" };
|
|
6010
6064
|
}
|
|
6011
6065
|
if (options.file !== void 0) {
|
|
6012
|
-
|
|
6013
|
-
try {
|
|
6014
|
-
raw = await readFile5(resolve8(cwd, options.file), "utf8");
|
|
6015
|
-
} catch (error) {
|
|
6016
|
-
if (findErrorCode8(error, "ENOENT")) {
|
|
6017
|
-
throw new Error("--file names a file that does not exist", { cause: error });
|
|
6018
|
-
}
|
|
6019
|
-
if (findErrorCode8(error, "EISDIR")) {
|
|
6020
|
-
throw new Error("--file names a directory, not a file", { cause: error });
|
|
6021
|
-
}
|
|
6022
|
-
throw new Error("Could not read the file --file names", { cause: error });
|
|
6023
|
-
}
|
|
6066
|
+
const raw = await readInputFileText("--file", resolve8(cwd, options.file));
|
|
6024
6067
|
return { body: dropTrailingNewlines(raw), source: "file" };
|
|
6025
6068
|
}
|
|
6026
6069
|
if (ctx.readInput !== void 0) {
|
|
@@ -6089,7 +6132,7 @@ async function assertWorkspaceInitialized8(basouRoot) {
|
|
|
6089
6132
|
try {
|
|
6090
6133
|
await assertBasouRootSafe9(basouRoot);
|
|
6091
6134
|
} catch (error) {
|
|
6092
|
-
if (
|
|
6135
|
+
if (findErrorCode9(error, "ENOENT")) {
|
|
6093
6136
|
throw new Error("Workspace not initialized. Run 'basou init' first.");
|
|
6094
6137
|
}
|
|
6095
6138
|
throw error;
|
|
@@ -6206,7 +6249,7 @@ import {
|
|
|
6206
6249
|
writeFileSync,
|
|
6207
6250
|
writeSync
|
|
6208
6251
|
} from "fs";
|
|
6209
|
-
import { basename as basename7, dirname as dirname7, isAbsolute as isAbsolute3, join as
|
|
6252
|
+
import { basename as basename7, dirname as dirname7, isAbsolute as isAbsolute3, join as join11, relative as relative3, resolve as resolve9 } from "path";
|
|
6210
6253
|
import {
|
|
6211
6254
|
appendBasouGitignore as appendBasouGitignore2,
|
|
6212
6255
|
basouPaths as basouPaths13,
|
|
@@ -6587,7 +6630,7 @@ function classifySourceRoot(repositoryRoot, declaredPath) {
|
|
|
6587
6630
|
} catch {
|
|
6588
6631
|
return { path: declaredPath, kind: "unresolved" };
|
|
6589
6632
|
}
|
|
6590
|
-
return { path: declaredPath, kind: existsSync(
|
|
6633
|
+
return { path: declaredPath, kind: existsSync(join11(real, ".git")) ? "repo" : "non-repo" };
|
|
6591
6634
|
}
|
|
6592
6635
|
async function doRunProjectAdopt(options, ctx) {
|
|
6593
6636
|
const cwd = ctx.cwd ?? process.cwd();
|
|
@@ -6691,7 +6734,7 @@ async function gatherRepoWiring(repositoryRoot, entry) {
|
|
|
6691
6734
|
} catch {
|
|
6692
6735
|
return { ...base, reachable: false, instructionFiles: [] };
|
|
6693
6736
|
}
|
|
6694
|
-
if (!existsSync(
|
|
6737
|
+
if (!existsSync(join11(real, ".git"))) {
|
|
6695
6738
|
return { ...base, reachable: false, instructionFiles: [] };
|
|
6696
6739
|
}
|
|
6697
6740
|
try {
|
|
@@ -6699,7 +6742,7 @@ async function gatherRepoWiring(repositoryRoot, entry) {
|
|
|
6699
6742
|
for (const name of INSTRUCTION_FILES) {
|
|
6700
6743
|
let present = true;
|
|
6701
6744
|
try {
|
|
6702
|
-
lstatSync(
|
|
6745
|
+
lstatSync(join11(real, name));
|
|
6703
6746
|
} catch {
|
|
6704
6747
|
present = false;
|
|
6705
6748
|
}
|
|
@@ -6810,10 +6853,10 @@ function gatherRepoGitignore(repositoryRoot, entry) {
|
|
|
6810
6853
|
} catch {
|
|
6811
6854
|
return { ...base, reachable: false, currentLines: [] };
|
|
6812
6855
|
}
|
|
6813
|
-
if (!existsSync(
|
|
6856
|
+
if (!existsSync(join11(real, ".git"))) {
|
|
6814
6857
|
return { ...base, reachable: false, currentLines: [] };
|
|
6815
6858
|
}
|
|
6816
|
-
return { ...base, reachable: true, currentLines: readGitignoreLines(
|
|
6859
|
+
return { ...base, reachable: true, currentLines: readGitignoreLines(join11(real, ".gitignore")) };
|
|
6817
6860
|
}
|
|
6818
6861
|
function hasErrorCode(error) {
|
|
6819
6862
|
return error instanceof Error && typeof error.code === "string";
|
|
@@ -6827,7 +6870,7 @@ function readGitignoreLines(file) {
|
|
|
6827
6870
|
}
|
|
6828
6871
|
}
|
|
6829
6872
|
function applyGitignorePlan(repositoryRoot, plan) {
|
|
6830
|
-
const file =
|
|
6873
|
+
const file = join11(realpathSync(resolve9(repositoryRoot, plan.path)), ".gitignore");
|
|
6831
6874
|
let existing = "";
|
|
6832
6875
|
try {
|
|
6833
6876
|
existing = readFileSync(file, "utf8");
|
|
@@ -6967,7 +7010,7 @@ function gatherRepoSymlinks(repositoryRoot, anchorReal, entry) {
|
|
|
6967
7010
|
return { ...base, isAnchor: false, reachable: false, canonicalPresent: false, files: [] };
|
|
6968
7011
|
}
|
|
6969
7012
|
if (real === anchorReal) {
|
|
6970
|
-
const anchorCanonical =
|
|
7013
|
+
const anchorCanonical = join11(real, CANONICAL_FILE);
|
|
6971
7014
|
const anchorState = anchorCanonicalState(anchorCanonical);
|
|
6972
7015
|
if (anchorState === "absent") {
|
|
6973
7016
|
return { ...base, isAnchor: true, reachable: true, canonicalPresent: false, files: [] };
|
|
@@ -6987,7 +7030,7 @@ function gatherRepoSymlinks(repositoryRoot, anchorReal, entry) {
|
|
|
6987
7030
|
anchorCanonical,
|
|
6988
7031
|
"self"
|
|
6989
7032
|
).map((spec) => {
|
|
6990
|
-
const { state, actualTarget } = inspectSymlink(
|
|
7033
|
+
const { state, actualTarget } = inspectSymlink(join11(real, spec.name), spec.target);
|
|
6991
7034
|
return {
|
|
6992
7035
|
name: spec.name,
|
|
6993
7036
|
expectedTarget: spec.target,
|
|
@@ -7004,16 +7047,16 @@ function gatherRepoSymlinks(repositoryRoot, anchorReal, entry) {
|
|
|
7004
7047
|
files: anchorFiles
|
|
7005
7048
|
};
|
|
7006
7049
|
}
|
|
7007
|
-
if (!existsSync(
|
|
7050
|
+
if (!existsSync(join11(real, ".git"))) {
|
|
7008
7051
|
return { ...base, isAnchor: false, reachable: false, canonicalPresent: false, files: [] };
|
|
7009
7052
|
}
|
|
7010
|
-
const canonicalFile = isSelf ?
|
|
7053
|
+
const canonicalFile = isSelf ? join11(real, CANONICAL_FILE) : join11(anchorReal, "agents", basename7(real), CANONICAL_FILE);
|
|
7011
7054
|
if (!existsSync(canonicalFile)) {
|
|
7012
7055
|
return { ...base, isAnchor: false, reachable: true, canonicalPresent: false, files: [] };
|
|
7013
7056
|
}
|
|
7014
7057
|
const files = expectedSymlinkTargets(real, canonicalFile, mode).map(
|
|
7015
7058
|
(spec) => {
|
|
7016
|
-
const { state, actualTarget } = inspectSymlink(
|
|
7059
|
+
const { state, actualTarget } = inspectSymlink(join11(real, spec.name), spec.target);
|
|
7017
7060
|
return {
|
|
7018
7061
|
name: spec.name,
|
|
7019
7062
|
expectedTarget: spec.target,
|
|
@@ -7042,7 +7085,7 @@ function applySymlinkPlan(repositoryRoot, plan) {
|
|
|
7042
7085
|
const created = [];
|
|
7043
7086
|
const failed = [];
|
|
7044
7087
|
for (const { name, target } of plan.toCreate) {
|
|
7045
|
-
const filePath =
|
|
7088
|
+
const filePath = join11(real, name);
|
|
7046
7089
|
try {
|
|
7047
7090
|
mkdirSync(dirname7(filePath), { recursive: true });
|
|
7048
7091
|
symlinkSync(target, filePath);
|
|
@@ -7076,7 +7119,7 @@ function gatherViewSymlinks(repositoryRoot, anchorReal, roster, viewDir) {
|
|
|
7076
7119
|
if (!existsSync(canonicalFile)) return { kind: "missing-canonical", viewName };
|
|
7077
7120
|
const files = expectedSymlinkTargets(viewDir, canonicalFile, "hub").map(
|
|
7078
7121
|
(spec) => {
|
|
7079
|
-
const { state, actualTarget } = inspectSymlink(
|
|
7122
|
+
const { state, actualTarget } = inspectSymlink(join11(viewDir, spec.name), spec.target);
|
|
7080
7123
|
return {
|
|
7081
7124
|
name: spec.name,
|
|
7082
7125
|
expectedTarget: spec.target,
|
|
@@ -7092,7 +7135,7 @@ function applyViewSymlinks(viewDir, files) {
|
|
|
7092
7135
|
const failed = [];
|
|
7093
7136
|
for (const f of files) {
|
|
7094
7137
|
if (f.state !== "missing") continue;
|
|
7095
|
-
const filePath =
|
|
7138
|
+
const filePath = join11(viewDir, f.name);
|
|
7096
7139
|
try {
|
|
7097
7140
|
mkdirSync(dirname7(filePath), { recursive: true });
|
|
7098
7141
|
symlinkSync(f.expectedTarget, filePath);
|
|
@@ -7314,7 +7357,7 @@ function resolveViewDir(repositoryRoot, viewPath) {
|
|
|
7314
7357
|
return realpathSync(abs);
|
|
7315
7358
|
} catch {
|
|
7316
7359
|
try {
|
|
7317
|
-
return
|
|
7360
|
+
return join11(realpathSync(dirname7(abs)), basename7(abs));
|
|
7318
7361
|
} catch {
|
|
7319
7362
|
return abs;
|
|
7320
7363
|
}
|
|
@@ -7332,7 +7375,7 @@ function gatherViewRepo(repositoryRoot, viewDir, entry) {
|
|
|
7332
7375
|
return { path: entry.path, reachable: false };
|
|
7333
7376
|
}
|
|
7334
7377
|
const linkName = basename7(repoReal);
|
|
7335
|
-
const { state, actualTarget } = inspectSymlink(
|
|
7378
|
+
const { state, actualTarget } = inspectSymlink(join11(viewDir, linkName), expectedTarget);
|
|
7336
7379
|
return {
|
|
7337
7380
|
path: entry.path,
|
|
7338
7381
|
reachable: true,
|
|
@@ -7346,7 +7389,7 @@ function applyViewPlan(viewDir, toCreate) {
|
|
|
7346
7389
|
const created = [];
|
|
7347
7390
|
const failed = [];
|
|
7348
7391
|
for (const { name, target } of toCreate) {
|
|
7349
|
-
const filePath =
|
|
7392
|
+
const filePath = join11(viewDir, name);
|
|
7350
7393
|
try {
|
|
7351
7394
|
mkdirSync(dirname7(filePath), { recursive: true });
|
|
7352
7395
|
symlinkSync(target, filePath);
|
|
@@ -7361,7 +7404,7 @@ var TOP_LEVEL_INSTRUCTION_FILES_LOWER = new Set(
|
|
|
7361
7404
|
INSTRUCTION_FILES.filter((f) => !f.includes("/")).map((f) => f.toLowerCase())
|
|
7362
7405
|
);
|
|
7363
7406
|
function classifyViewLink(viewDir, name, rosterRealpaths) {
|
|
7364
|
-
const filePath =
|
|
7407
|
+
const filePath = join11(viewDir, name);
|
|
7365
7408
|
let isLink;
|
|
7366
7409
|
try {
|
|
7367
7410
|
isLink = lstatSync(filePath).isSymbolicLink();
|
|
@@ -7390,7 +7433,7 @@ function classifyViewLink(viewDir, name, rosterRealpaths) {
|
|
|
7390
7433
|
if (!isDir) {
|
|
7391
7434
|
return { target, kind: existsSync(resolved) ? "non-repo" : "broken" };
|
|
7392
7435
|
}
|
|
7393
|
-
return { target, kind: existsSync(
|
|
7436
|
+
return { target, kind: existsSync(join11(resolved, ".git")) ? "repo" : "non-repo" };
|
|
7394
7437
|
}
|
|
7395
7438
|
function gatherExistingViewLinks(viewDir, rosterRealpaths) {
|
|
7396
7439
|
let names;
|
|
@@ -7415,7 +7458,7 @@ function pruneViewLinks(viewDir, toPrune, rosterRealpaths) {
|
|
|
7415
7458
|
const pruned = [];
|
|
7416
7459
|
const failed = [];
|
|
7417
7460
|
for (const { name } of toPrune) {
|
|
7418
|
-
const filePath =
|
|
7461
|
+
const filePath = join11(viewDir, name);
|
|
7419
7462
|
const c = classifyViewLink(viewDir, name, rosterRealpaths);
|
|
7420
7463
|
if (c === null || c.kind !== "repo") {
|
|
7421
7464
|
failed.push({
|
|
@@ -7628,10 +7671,10 @@ async function runProjectPreset(options, ctx = {}) {
|
|
|
7628
7671
|
}
|
|
7629
7672
|
}
|
|
7630
7673
|
function canonicalFileFor(anchorReal, canonicalName) {
|
|
7631
|
-
return
|
|
7674
|
+
return join11(anchorReal, "agents", canonicalName, CANONICAL_FILE);
|
|
7632
7675
|
}
|
|
7633
7676
|
function canonicalLabelFor(canonicalName) {
|
|
7634
|
-
return
|
|
7677
|
+
return join11("agents", canonicalName, CANONICAL_FILE);
|
|
7635
7678
|
}
|
|
7636
7679
|
async function gatherRepoPreset(repositoryRoot, anchorReal, entry) {
|
|
7637
7680
|
const declared = {
|
|
@@ -7652,7 +7695,7 @@ async function gatherRepoPreset(repositoryRoot, anchorReal, entry) {
|
|
|
7652
7695
|
if (real === anchorReal) {
|
|
7653
7696
|
return { ...declared, isAnchor: true, reachable: true, canonicalPresent: false };
|
|
7654
7697
|
}
|
|
7655
|
-
if (!existsSync(
|
|
7698
|
+
if (!existsSync(join11(real, ".git"))) {
|
|
7656
7699
|
return { ...declared, isAnchor: false, reachable: false, canonicalPresent: false };
|
|
7657
7700
|
}
|
|
7658
7701
|
const canonicalName = basename7(real);
|
|
@@ -8034,24 +8077,24 @@ function gatherArchiveTeardown(repositoryRoot, manifest, target) {
|
|
|
8034
8077
|
const instructionFiles = [];
|
|
8035
8078
|
for (const name of INSTRUCTION_FILES) {
|
|
8036
8079
|
try {
|
|
8037
|
-
lstatSync(
|
|
8080
|
+
lstatSync(join11(real, name));
|
|
8038
8081
|
instructionFiles.push(name);
|
|
8039
8082
|
} catch {
|
|
8040
8083
|
}
|
|
8041
8084
|
}
|
|
8042
8085
|
let ignored;
|
|
8043
8086
|
try {
|
|
8044
|
-
ignored = new Set(readGitignoreLines(
|
|
8087
|
+
ignored = new Set(readGitignoreLines(join11(real, ".gitignore")).map((l) => l.trim()));
|
|
8045
8088
|
} catch {
|
|
8046
8089
|
ignored = /* @__PURE__ */ new Set();
|
|
8047
8090
|
}
|
|
8048
8091
|
const gitignorePatterns = INSTRUCTION_FILES.filter((p) => ignored.has(p) || ignored.has(`/${p}`));
|
|
8049
|
-
const canonical2 = existsSync(
|
|
8092
|
+
const canonical2 = existsSync(join11(anchorReal, "agents", canonicalName, CANONICAL_FILE));
|
|
8050
8093
|
let viewLink = false;
|
|
8051
8094
|
const viewPath = manifest.workspace.view;
|
|
8052
8095
|
if (viewPath !== void 0) {
|
|
8053
8096
|
try {
|
|
8054
|
-
lstatSync(
|
|
8097
|
+
lstatSync(join11(resolveViewDir(repositoryRoot, viewPath), canonicalName));
|
|
8055
8098
|
viewLink = true;
|
|
8056
8099
|
} catch {
|
|
8057
8100
|
}
|
|
@@ -8065,11 +8108,11 @@ function gatherArchiveTeardown(repositoryRoot, manifest, target) {
|
|
|
8065
8108
|
};
|
|
8066
8109
|
}
|
|
8067
8110
|
function teardownExpectedTargets(repoReal, anchorReal, canonicalName) {
|
|
8068
|
-
const canonicalFile =
|
|
8111
|
+
const canonicalFile = join11(anchorReal, "agents", canonicalName, CANONICAL_FILE);
|
|
8069
8112
|
return expectedSymlinkTargets(repoReal, canonicalFile);
|
|
8070
8113
|
}
|
|
8071
8114
|
function viewLinkPointsAt(viewDir, name, repoReal) {
|
|
8072
|
-
const filePath =
|
|
8115
|
+
const filePath = join11(viewDir, name);
|
|
8073
8116
|
try {
|
|
8074
8117
|
if (!lstatSync(filePath).isSymbolicLink()) return false;
|
|
8075
8118
|
const target = readlinkSync(filePath);
|
|
@@ -8080,7 +8123,7 @@ function viewLinkPointsAt(viewDir, name, repoReal) {
|
|
|
8080
8123
|
}
|
|
8081
8124
|
}
|
|
8082
8125
|
function viewLinkPointsAtPath(viewDir, name, expectedRepoPath) {
|
|
8083
|
-
const filePath =
|
|
8126
|
+
const filePath = join11(viewDir, name);
|
|
8084
8127
|
try {
|
|
8085
8128
|
if (!lstatSync(filePath).isSymbolicLink()) return false;
|
|
8086
8129
|
const target = readlinkSync(filePath);
|
|
@@ -8131,7 +8174,7 @@ function gatherRepoTeardown(repositoryRoot, manifest, target) {
|
|
|
8131
8174
|
if (!isAnchor) {
|
|
8132
8175
|
if (repoReal !== void 0) {
|
|
8133
8176
|
for (const spec of teardownExpectedTargets(repoReal, anchorReal, canonicalName)) {
|
|
8134
|
-
const { state, actualTarget } = inspectSymlink(
|
|
8177
|
+
const { state, actualTarget } = inspectSymlink(join11(repoReal, spec.name), spec.target);
|
|
8135
8178
|
if (isSelf) {
|
|
8136
8179
|
if (state !== "missing")
|
|
8137
8180
|
items.push({
|
|
@@ -8168,7 +8211,7 @@ function gatherRepoTeardown(repositoryRoot, manifest, target) {
|
|
|
8168
8211
|
}
|
|
8169
8212
|
let ignored;
|
|
8170
8213
|
try {
|
|
8171
|
-
ignored = new Set(readGitignoreLines(
|
|
8214
|
+
ignored = new Set(readGitignoreLines(join11(repoReal, ".gitignore")).map((l) => l.trim()));
|
|
8172
8215
|
for (const p of INSTRUCTION_FILES) {
|
|
8173
8216
|
if (ignored.has(p) || ignored.has(`/${p}`)) {
|
|
8174
8217
|
items.push({
|
|
@@ -8191,7 +8234,7 @@ function gatherRepoTeardown(repositoryRoot, manifest, target) {
|
|
|
8191
8234
|
const viewPath = manifest.workspace.view;
|
|
8192
8235
|
if (viewPath !== void 0) {
|
|
8193
8236
|
const viewDir = resolveViewDir(repositoryRoot, viewPath);
|
|
8194
|
-
const linkPath =
|
|
8237
|
+
const linkPath = join11(viewDir, canonicalName);
|
|
8195
8238
|
let isLink = false;
|
|
8196
8239
|
try {
|
|
8197
8240
|
isLink = lstatSync(linkPath).isSymbolicLink();
|
|
@@ -8217,8 +8260,8 @@ function gatherRepoTeardown(repositoryRoot, manifest, target) {
|
|
|
8217
8260
|
else items.push({ kind: "view-symlink", label: canonicalName, state: "removable" });
|
|
8218
8261
|
}
|
|
8219
8262
|
}
|
|
8220
|
-
const canonicalFile =
|
|
8221
|
-
const canonicalLabel =
|
|
8263
|
+
const canonicalFile = join11(anchorReal, "agents", canonicalName, CANONICAL_FILE);
|
|
8264
|
+
const canonicalLabel = join11("agents", canonicalName, CANONICAL_FILE);
|
|
8222
8265
|
let canonicalIsLink = false;
|
|
8223
8266
|
try {
|
|
8224
8267
|
canonicalIsLink = lstatSync(canonicalFile).isSymbolicLink();
|
|
@@ -8322,12 +8365,12 @@ function applyRepoTeardown(repositoryRoot, manifest, plan) {
|
|
|
8322
8365
|
);
|
|
8323
8366
|
for (const item of removable.filter((i) => i.kind === "instruction-symlink")) {
|
|
8324
8367
|
const expected = expectedByName.get(item.label);
|
|
8325
|
-
if (repoReal === null || expected === void 0 || inspectSymlink(
|
|
8368
|
+
if (repoReal === null || expected === void 0 || inspectSymlink(join11(repoReal, item.label), expected).state !== "correct") {
|
|
8326
8369
|
changed(item.label);
|
|
8327
8370
|
continue;
|
|
8328
8371
|
}
|
|
8329
8372
|
try {
|
|
8330
|
-
unlinkSync(
|
|
8373
|
+
unlinkSync(join11(repoReal, item.label));
|
|
8331
8374
|
removed.push(item.label);
|
|
8332
8375
|
} catch (error) {
|
|
8333
8376
|
failed.push({ label: item.label, message: failureReason(error) });
|
|
@@ -8346,7 +8389,7 @@ function applyRepoTeardown(repositoryRoot, manifest, plan) {
|
|
|
8346
8389
|
continue;
|
|
8347
8390
|
}
|
|
8348
8391
|
try {
|
|
8349
|
-
unlinkSync(
|
|
8392
|
+
unlinkSync(join11(viewDir, item.label));
|
|
8350
8393
|
removed.push(`view/${item.label}`);
|
|
8351
8394
|
} catch (error) {
|
|
8352
8395
|
failed.push({ label: `view/${item.label}`, message: failureReason(error) });
|
|
@@ -8354,7 +8397,7 @@ function applyRepoTeardown(repositoryRoot, manifest, plan) {
|
|
|
8354
8397
|
}
|
|
8355
8398
|
const NOFOLLOW = fsConstants.O_NOFOLLOW ?? 0;
|
|
8356
8399
|
for (const item of removable.filter((i) => i.kind === "canonical-block")) {
|
|
8357
|
-
const canonicalFile =
|
|
8400
|
+
const canonicalFile = join11(anchorReal, "agents", canonicalName, CANONICAL_FILE);
|
|
8358
8401
|
try {
|
|
8359
8402
|
if (lstatSync(canonicalFile).isSymbolicLink()) {
|
|
8360
8403
|
changed(item.label);
|
|
@@ -8628,12 +8671,12 @@ function gatherRenameWiring(repositoryRoot, manifest, oldBasename) {
|
|
|
8628
8671
|
} catch {
|
|
8629
8672
|
return { canonicalDirOld: false, viewLinkOld: false };
|
|
8630
8673
|
}
|
|
8631
|
-
const canonicalDirOld = existsSync(
|
|
8674
|
+
const canonicalDirOld = existsSync(join11(anchorReal, "agents", oldBasename));
|
|
8632
8675
|
let viewLinkOld = false;
|
|
8633
8676
|
const viewPath = manifest.workspace.view;
|
|
8634
8677
|
if (viewPath !== void 0) {
|
|
8635
8678
|
try {
|
|
8636
|
-
lstatSync(
|
|
8679
|
+
lstatSync(join11(resolveViewDir(repositoryRoot, viewPath), oldBasename));
|
|
8637
8680
|
viewLinkOld = true;
|
|
8638
8681
|
} catch {
|
|
8639
8682
|
}
|
|
@@ -8978,7 +9021,7 @@ async function doRunProjectSeedAnchor(options, ctx) {
|
|
|
8978
9021
|
console.log("\u2139\uFE0F No repo roster declared \u2014 nothing to seed.");
|
|
8979
9022
|
return;
|
|
8980
9023
|
}
|
|
8981
|
-
const anchorDoc =
|
|
9024
|
+
const anchorDoc = join11(repositoryRoot, CANONICAL_FILE);
|
|
8982
9025
|
if (pathPresent(anchorDoc)) {
|
|
8983
9026
|
console.log(
|
|
8984
9027
|
`\u2705 The anchor's own \`${CANONICAL_FILE}\` already exists \u2014 hand-maintained, left untouched.`
|
|
@@ -9040,7 +9083,7 @@ function regularFileSpokes(repoReal) {
|
|
|
9040
9083
|
const out = [];
|
|
9041
9084
|
for (const spoke of ["CLAUDE.md", ".github/copilot-instructions.md"]) {
|
|
9042
9085
|
try {
|
|
9043
|
-
const st = lstatSync(
|
|
9086
|
+
const st = lstatSync(join11(repoReal, spoke));
|
|
9044
9087
|
if (!st.isSymbolicLink() && st.isFile()) out.push(spoke);
|
|
9045
9088
|
} catch {
|
|
9046
9089
|
}
|
|
@@ -9086,8 +9129,8 @@ function gatherRetrofit(repositoryRoot, anchorReal, roster, argAbs, argReal, vie
|
|
|
9086
9129
|
};
|
|
9087
9130
|
}
|
|
9088
9131
|
const isAnchor = argReal === anchorReal;
|
|
9089
|
-
const reachable = existsSync(
|
|
9090
|
-
const canonicalFile =
|
|
9132
|
+
const reachable = existsSync(join11(argReal, ".git"));
|
|
9133
|
+
const canonicalFile = join11(anchorReal, "agents", canonicalName, CANONICAL_FILE);
|
|
9091
9134
|
return {
|
|
9092
9135
|
path,
|
|
9093
9136
|
declared,
|
|
@@ -9096,13 +9139,13 @@ function gatherRetrofit(repositoryRoot, anchorReal, roster, argAbs, argReal, vie
|
|
|
9096
9139
|
reachable,
|
|
9097
9140
|
canonicalName,
|
|
9098
9141
|
...viewCanonicalName !== void 0 ? { viewCanonicalName } : {},
|
|
9099
|
-
agentsState: inspectAgentsState(
|
|
9142
|
+
agentsState: inspectAgentsState(join11(argReal, CANONICAL_FILE)),
|
|
9100
9143
|
canonicalExists: pathPresent(canonicalFile),
|
|
9101
9144
|
regularSpokes: regularFileSpokes(argReal)
|
|
9102
9145
|
};
|
|
9103
9146
|
}
|
|
9104
9147
|
function relocateAgentsFile(repoReal, canonicalFile) {
|
|
9105
|
-
const agentsFile =
|
|
9148
|
+
const agentsFile = join11(repoReal, CANONICAL_FILE);
|
|
9106
9149
|
try {
|
|
9107
9150
|
mkdirSync(dirname7(canonicalFile), { recursive: true });
|
|
9108
9151
|
} catch (error) {
|
|
@@ -9218,7 +9261,7 @@ async function doRunProjectRetrofit(repo, options, ctx) {
|
|
|
9218
9261
|
let failure;
|
|
9219
9262
|
let partial = false;
|
|
9220
9263
|
if (options.apply === true && plan.action === "relocate" && argReal !== void 0) {
|
|
9221
|
-
const canonicalFile =
|
|
9264
|
+
const canonicalFile = join11(anchorReal, "agents", plan.canonicalName, CANONICAL_FILE);
|
|
9222
9265
|
const res = relocateAgentsFile(argReal, canonicalFile);
|
|
9223
9266
|
if (res.ok) {
|
|
9224
9267
|
applied = true;
|
|
@@ -9418,7 +9461,7 @@ function renderProjectRetrofit(result) {
|
|
|
9418
9461
|
}
|
|
9419
9462
|
|
|
9420
9463
|
// src/commands/protocol.ts
|
|
9421
|
-
import { readFile as
|
|
9464
|
+
import { readFile as readFile5, stat as stat5 } from "fs/promises";
|
|
9422
9465
|
import {
|
|
9423
9466
|
carryForwardProtocolStamp,
|
|
9424
9467
|
PROTOCOL_END as PROTOCOL_END2,
|
|
@@ -9475,7 +9518,7 @@ async function readProtocolSources(entries) {
|
|
|
9475
9518
|
for (const entry of entries) {
|
|
9476
9519
|
let content;
|
|
9477
9520
|
try {
|
|
9478
|
-
content = await
|
|
9521
|
+
content = await readFile5(entry.source, "utf8");
|
|
9479
9522
|
} catch (error) {
|
|
9480
9523
|
if (error instanceof Error && error.code === "ENOENT") {
|
|
9481
9524
|
throw new Error(
|
|
@@ -9601,7 +9644,7 @@ async function doRunProtocolUnsync(options) {
|
|
|
9601
9644
|
import {
|
|
9602
9645
|
assertBasouRootSafe as assertBasouRootSafe10,
|
|
9603
9646
|
basouPaths as basouPaths14,
|
|
9604
|
-
findErrorCode as
|
|
9647
|
+
findErrorCode as findErrorCode11,
|
|
9605
9648
|
loadPortfolioConfig as loadPortfolioConfig5,
|
|
9606
9649
|
readManifest as readManifest10
|
|
9607
9650
|
} from "@basou/core";
|
|
@@ -9610,20 +9653,20 @@ import { InvalidArgumentError as InvalidArgumentError4 } from "commander";
|
|
|
9610
9653
|
// src/commands/refresh-watch.ts
|
|
9611
9654
|
import { readdir as readdir3, stat as stat6 } from "fs/promises";
|
|
9612
9655
|
import { homedir as homedir9 } from "os";
|
|
9613
|
-
import { join as
|
|
9656
|
+
import { join as join12 } from "path";
|
|
9614
9657
|
import {
|
|
9615
9658
|
assertApprovalStoreSafe as assertApprovalStoreSafe2,
|
|
9616
9659
|
assertLockStoreSafe as assertLockStoreSafe4,
|
|
9617
9660
|
assertTaskStoreSafe as assertTaskStoreSafe2,
|
|
9618
|
-
findErrorCode as
|
|
9661
|
+
findErrorCode as findErrorCode10
|
|
9619
9662
|
} from "@basou/core";
|
|
9620
9663
|
var DEFAULT_WATCH_INTERVAL_SEC = 30;
|
|
9621
9664
|
var MIN_WATCH_INTERVAL_SEC = 5;
|
|
9622
9665
|
var MAX_WATCH_INTERVAL_SEC = 86400;
|
|
9623
9666
|
function watchedRoots(ctx) {
|
|
9624
9667
|
return [
|
|
9625
|
-
ctx.codexSessionsDir ??
|
|
9626
|
-
ctx.claudeProjectsDir ??
|
|
9668
|
+
ctx.codexSessionsDir ?? join12(homedir9(), ".codex", "sessions"),
|
|
9669
|
+
ctx.claudeProjectsDir ?? join12(homedir9(), ".claude", "projects")
|
|
9627
9670
|
];
|
|
9628
9671
|
}
|
|
9629
9672
|
async function scanSourceLogs(roots) {
|
|
@@ -9633,11 +9676,11 @@ async function scanSourceLogs(roots) {
|
|
|
9633
9676
|
try {
|
|
9634
9677
|
entries = await readdir3(dir, { withFileTypes: true });
|
|
9635
9678
|
} catch (error) {
|
|
9636
|
-
if (
|
|
9679
|
+
if (findErrorCode10(error, "ENOENT") || findErrorCode10(error, "ENOTDIR")) return;
|
|
9637
9680
|
throw new Error("Failed to read a source log directory", { cause: error });
|
|
9638
9681
|
}
|
|
9639
9682
|
for (const entry of entries) {
|
|
9640
|
-
const full =
|
|
9683
|
+
const full = join12(dir, entry.name);
|
|
9641
9684
|
if (entry.isDirectory()) {
|
|
9642
9685
|
await walk(full);
|
|
9643
9686
|
} else if (entry.isFile() && entry.name.endsWith(".jsonl")) {
|
|
@@ -9645,7 +9688,7 @@ async function scanSourceLogs(roots) {
|
|
|
9645
9688
|
const info = await stat6(full);
|
|
9646
9689
|
out.set(full, { mtimeMs: info.mtimeMs, size: info.size });
|
|
9647
9690
|
} catch (error) {
|
|
9648
|
-
if (
|
|
9691
|
+
if (findErrorCode10(error, "ENOENT")) continue;
|
|
9649
9692
|
throw new Error("Failed to stat a source log file", { cause: error });
|
|
9650
9693
|
}
|
|
9651
9694
|
}
|
|
@@ -9979,7 +10022,7 @@ async function assertWorkspaceInitialized9(basouRoot) {
|
|
|
9979
10022
|
try {
|
|
9980
10023
|
await assertBasouRootSafe10(basouRoot);
|
|
9981
10024
|
} catch (error) {
|
|
9982
|
-
if (
|
|
10025
|
+
if (findErrorCode11(error, "ENOENT")) {
|
|
9983
10026
|
throw new Error("Workspace not initialized. Run 'basou init' first.");
|
|
9984
10027
|
}
|
|
9985
10028
|
throw error;
|
|
@@ -9991,7 +10034,7 @@ import { isAbsolute as isAbsolute4, resolve as resolve10 } from "path";
|
|
|
9991
10034
|
import {
|
|
9992
10035
|
assertBasouRootSafe as assertBasouRootSafe11,
|
|
9993
10036
|
basouPaths as basouPaths15,
|
|
9994
|
-
findErrorCode as
|
|
10037
|
+
findErrorCode as findErrorCode12,
|
|
9995
10038
|
renderReport,
|
|
9996
10039
|
writeMarkdownFile as writeMarkdownFile6
|
|
9997
10040
|
} from "@basou/core";
|
|
@@ -10043,7 +10086,7 @@ async function assertWorkspaceInitialized10(basouRoot) {
|
|
|
10043
10086
|
try {
|
|
10044
10087
|
await assertBasouRootSafe11(basouRoot);
|
|
10045
10088
|
} catch (error) {
|
|
10046
|
-
if (
|
|
10089
|
+
if (findErrorCode12(error, "ENOENT")) {
|
|
10047
10090
|
throw new Error("Workspace not initialized. Run 'basou init' first.");
|
|
10048
10091
|
}
|
|
10049
10092
|
throw error;
|
|
@@ -10051,7 +10094,6 @@ async function assertWorkspaceInitialized10(basouRoot) {
|
|
|
10051
10094
|
}
|
|
10052
10095
|
|
|
10053
10096
|
// src/commands/review.ts
|
|
10054
|
-
import { readFile as readFile7 } from "fs/promises";
|
|
10055
10097
|
import { homedir as homedir10 } from "os";
|
|
10056
10098
|
import { resolve as resolve11 } from "path";
|
|
10057
10099
|
import {
|
|
@@ -10060,7 +10102,7 @@ import {
|
|
|
10060
10102
|
buildReviewRecordedEvent,
|
|
10061
10103
|
buildReviewRecordLabel,
|
|
10062
10104
|
createAdHocSessionWithEvent as createAdHocSessionWithEvent3,
|
|
10063
|
-
findErrorCode as
|
|
10105
|
+
findErrorCode as findErrorCode13,
|
|
10064
10106
|
findUnbindableRepos,
|
|
10065
10107
|
parseReviewRecordInput,
|
|
10066
10108
|
readManifest as readManifest11,
|
|
@@ -10136,6 +10178,7 @@ async function runReviewRecord(options, ctx = {}) {
|
|
|
10136
10178
|
}
|
|
10137
10179
|
}
|
|
10138
10180
|
async function doRunReviewRecord(options, ctx) {
|
|
10181
|
+
refuseDashInputFile("--file", options.file, "stdin");
|
|
10139
10182
|
const cwd = ctx.cwd ?? process.cwd();
|
|
10140
10183
|
const repositoryRoot = await resolveBasouRootForCommand(cwd, "review record");
|
|
10141
10184
|
const paths = basouPaths16(repositoryRoot);
|
|
@@ -10194,14 +10237,7 @@ ${detail}
|
|
|
10194
10237
|
}
|
|
10195
10238
|
async function readReviewInput(options, ctx) {
|
|
10196
10239
|
if (options.file !== void 0) {
|
|
10197
|
-
|
|
10198
|
-
return await readFile7(options.file, "utf8");
|
|
10199
|
-
} catch (error) {
|
|
10200
|
-
if (findErrorCode12(error, "ENOENT")) {
|
|
10201
|
-
throw new Error(`Input file not found: ${options.file}`);
|
|
10202
|
-
}
|
|
10203
|
-
throw error;
|
|
10204
|
-
}
|
|
10240
|
+
return await readInputFileText("--file", options.file);
|
|
10205
10241
|
}
|
|
10206
10242
|
if (ctx.readInput !== void 0) {
|
|
10207
10243
|
return await ctx.readInput();
|
|
@@ -10276,7 +10312,7 @@ async function assertWorkspaceInitialized11(basouRoot) {
|
|
|
10276
10312
|
try {
|
|
10277
10313
|
await assertBasouRootSafe12(basouRoot);
|
|
10278
10314
|
} catch (error) {
|
|
10279
|
-
if (
|
|
10315
|
+
if (findErrorCode13(error, "ENOENT")) {
|
|
10280
10316
|
throw new Error("Workspace not initialized. Run 'basou init' first.");
|
|
10281
10317
|
}
|
|
10282
10318
|
throw error;
|
|
@@ -10488,9 +10524,9 @@ function unattachedLines(u) {
|
|
|
10488
10524
|
}
|
|
10489
10525
|
|
|
10490
10526
|
// src/commands/run.ts
|
|
10491
|
-
import { mkdir as mkdir3, readFile as
|
|
10527
|
+
import { mkdir as mkdir3, readFile as readFile6, rm as rm3 } from "fs/promises";
|
|
10492
10528
|
import { homedir as homedir11 } from "os";
|
|
10493
|
-
import { join as
|
|
10529
|
+
import { join as join13 } from "path";
|
|
10494
10530
|
import {
|
|
10495
10531
|
acquireLock as acquireLock5,
|
|
10496
10532
|
assertBasouRootSafe as assertBasouRootSafe13,
|
|
@@ -10505,6 +10541,7 @@ import {
|
|
|
10505
10541
|
findBasouSessionStartHook as findBasouSessionStartHook2,
|
|
10506
10542
|
getDiff,
|
|
10507
10543
|
getSnapshot as getSnapshot2,
|
|
10544
|
+
lstatSessionFile as lstatSessionFile2,
|
|
10508
10545
|
overwriteYamlFile as overwriteYamlFile2,
|
|
10509
10546
|
prefixedUlid as prefixedUlid4,
|
|
10510
10547
|
readManifest as readManifest12,
|
|
@@ -10512,6 +10549,7 @@ import {
|
|
|
10512
10549
|
resolveClaudeCodeCommand,
|
|
10513
10550
|
resolveCodexCommand,
|
|
10514
10551
|
SESSION_SCHEMA_VERSION as SESSION_SCHEMA_VERSION2,
|
|
10552
|
+
SessionFileNotAFileError as SessionFileNotAFileError4,
|
|
10515
10553
|
SessionSchema as SessionSchema2,
|
|
10516
10554
|
sanitizeRelatedFiles,
|
|
10517
10555
|
sanitizeWorkingDirectory as sanitizeWorkingDirectory2,
|
|
@@ -10571,12 +10609,12 @@ async function runTrackedTool(args, options, ctx, adapter) {
|
|
|
10571
10609
|
const manifest = await readManifest12(paths);
|
|
10572
10610
|
await assertSessionStoreSafe3(paths);
|
|
10573
10611
|
const sessionId = prefixedUlid4("ses");
|
|
10574
|
-
const sessionDir =
|
|
10612
|
+
const sessionDir = join13(paths.sessions, sessionId);
|
|
10575
10613
|
const appendEvent = ctx.appendEvent ?? (async (_sessionDir, event) => {
|
|
10576
10614
|
await coreAppendChainedEvent2(paths, sessionId, event);
|
|
10577
10615
|
});
|
|
10578
10616
|
const startedAt = now().toISOString();
|
|
10579
|
-
const sessionYamlPath =
|
|
10617
|
+
const sessionYamlPath = join13(sessionDir, "session.yaml");
|
|
10580
10618
|
const session = buildInitialSession2({
|
|
10581
10619
|
id: sessionId,
|
|
10582
10620
|
command,
|
|
@@ -10627,7 +10665,7 @@ async function runTrackedTool(args, options, ctx, adapter) {
|
|
|
10627
10665
|
});
|
|
10628
10666
|
const runningLock = await acquireLock5(paths, "session", sessionId);
|
|
10629
10667
|
try {
|
|
10630
|
-
await mutateSessionYaml2(
|
|
10668
|
+
await mutateSessionYaml2(paths, sessionId, (s) => {
|
|
10631
10669
|
s.session.status = "running";
|
|
10632
10670
|
});
|
|
10633
10671
|
} finally {
|
|
@@ -10759,7 +10797,7 @@ async function runTrackedTool(args, options, ctx, adapter) {
|
|
|
10759
10797
|
s.session.related_files = relatedFiles;
|
|
10760
10798
|
});
|
|
10761
10799
|
if (result.exit_code !== null) return result.exit_code;
|
|
10762
|
-
return
|
|
10800
|
+
return signalToExitCode(signalReceived ?? result.signal);
|
|
10763
10801
|
}
|
|
10764
10802
|
function decideFinalStatus2(result, signalReceived) {
|
|
10765
10803
|
if (signalReceived === "SIGINT" || signalReceived === "SIGTERM") return "interrupted";
|
|
@@ -10769,18 +10807,6 @@ function decideFinalStatus2(result, signalReceived) {
|
|
|
10769
10807
|
if (result.exit_code === 0) return "completed";
|
|
10770
10808
|
return "failed";
|
|
10771
10809
|
}
|
|
10772
|
-
var SIGNUM_MAP2 = {
|
|
10773
|
-
SIGHUP: 1,
|
|
10774
|
-
SIGINT: 2,
|
|
10775
|
-
SIGQUIT: 3,
|
|
10776
|
-
SIGKILL: 9,
|
|
10777
|
-
SIGTERM: 15
|
|
10778
|
-
};
|
|
10779
|
-
function signalToExitCode2(sig) {
|
|
10780
|
-
if (sig === null) return 1;
|
|
10781
|
-
const num = SIGNUM_MAP2[sig] ?? 1;
|
|
10782
|
-
return 128 + num;
|
|
10783
|
-
}
|
|
10784
10810
|
async function tryAppendGitSnapshot2(sessionDir, sessionId, repoRoot, now, appendEvent) {
|
|
10785
10811
|
let snapshot;
|
|
10786
10812
|
try {
|
|
@@ -10885,7 +10911,14 @@ function buildInitialSession2(input) {
|
|
|
10885
10911
|
}
|
|
10886
10912
|
};
|
|
10887
10913
|
}
|
|
10888
|
-
async function mutateSessionYaml2(
|
|
10914
|
+
async function mutateSessionYaml2(paths, sessionId, mutator) {
|
|
10915
|
+
const kind = await lstatSessionFile2(paths, sessionId, "session.yaml").catch((error) => {
|
|
10916
|
+
throw new Error("Failed to read YAML file", { cause: error });
|
|
10917
|
+
});
|
|
10918
|
+
if (kind === "symlink" || kind === "not_a_file") {
|
|
10919
|
+
throw new SessionFileNotAFileError4(sessionId, "session.yaml", kind);
|
|
10920
|
+
}
|
|
10921
|
+
const filePath = join13(paths.sessions, sessionId, "session.yaml");
|
|
10889
10922
|
const raw = await readYamlFile5(filePath);
|
|
10890
10923
|
const parsed = SessionSchema2.parse(raw);
|
|
10891
10924
|
mutator(parsed);
|
|
@@ -10938,7 +10971,7 @@ async function noteCodexHookStatusPreSpawn(_cwd, ctx) {
|
|
|
10938
10971
|
const hooksPath = ctx.codexHooksPath ?? DEFAULT_CODEX_HOOKS_PATH;
|
|
10939
10972
|
let location;
|
|
10940
10973
|
try {
|
|
10941
|
-
location = findBasouSessionStartHook2(JSON.parse(await
|
|
10974
|
+
location = findBasouSessionStartHook2(JSON.parse(await readFile6(hooksPath, "utf8")));
|
|
10942
10975
|
} catch (error) {
|
|
10943
10976
|
if (!(error instanceof Error && error.code === "ENOENT")) return null;
|
|
10944
10977
|
location = null;
|
|
@@ -10952,8 +10985,8 @@ async function noteCodexHookStatusPreSpawn(_cwd, ctx) {
|
|
|
10952
10985
|
}
|
|
10953
10986
|
|
|
10954
10987
|
// src/commands/session.ts
|
|
10955
|
-
import { readFile as
|
|
10956
|
-
import { basename as basename8, isAbsolute as isAbsolute5, join as
|
|
10988
|
+
import { readFile as readFile7 } from "fs/promises";
|
|
10989
|
+
import { basename as basename8, isAbsolute as isAbsolute5, join as join14, relative as relative4, sep as sep3 } from "path";
|
|
10957
10990
|
import {
|
|
10958
10991
|
acquireLock as acquireLock6,
|
|
10959
10992
|
appendEventToExistingSession as appendEventToExistingSession3,
|
|
@@ -10963,10 +10996,11 @@ import {
|
|
|
10963
10996
|
displayPath as displayPath5,
|
|
10964
10997
|
EVENT_SCHEMA_VERSION as EVENT_SCHEMA_VERSION6,
|
|
10965
10998
|
enumerateSessionEntries as enumerateSessionEntries2,
|
|
10966
|
-
findErrorCode as
|
|
10999
|
+
findErrorCode as findErrorCode14,
|
|
10967
11000
|
importSessionFromJson as importSessionFromJson2,
|
|
10968
11001
|
LOCAL_CLI_EVENT_SOURCE as LOCAL_CLI_EVENT_SOURCE4,
|
|
10969
11002
|
loadSessionEntries as loadSessionEntries2,
|
|
11003
|
+
lstatSessionFile as lstatSessionFile3,
|
|
10970
11004
|
normalizeSessionTimestamps,
|
|
10971
11005
|
readAllEvents,
|
|
10972
11006
|
readManifest as readManifest13,
|
|
@@ -10976,6 +11010,7 @@ import {
|
|
|
10976
11010
|
resolveSessionId as resolveSessionId3,
|
|
10977
11011
|
resolveTaskId,
|
|
10978
11012
|
SESSION_IMPORT_SCHEMA_VERSION as SESSION_IMPORT_SCHEMA_VERSION2,
|
|
11013
|
+
SessionFileNotAFileError as SessionFileNotAFileError5,
|
|
10979
11014
|
SessionImportPayloadSchema as SessionImportPayloadSchema2,
|
|
10980
11015
|
SessionSchema as SessionSchema3,
|
|
10981
11016
|
SessionStatusSchema,
|
|
@@ -11085,20 +11120,28 @@ async function doRunSessionShow(idInput, options, ctx) {
|
|
|
11085
11120
|
const paths = basouPaths19(repositoryRoot);
|
|
11086
11121
|
await assertWorkspaceInitialized12(paths.root);
|
|
11087
11122
|
const sessionId = await resolveSessionId3(paths, idInput);
|
|
11088
|
-
const sessionDir =
|
|
11089
|
-
const sessionYamlPath =
|
|
11123
|
+
const sessionDir = join14(paths.sessions, sessionId);
|
|
11124
|
+
const sessionYamlPath = join14(sessionDir, "session.yaml");
|
|
11125
|
+
const yamlKind = await lstatSessionFile3(paths, sessionId, "session.yaml").catch(
|
|
11126
|
+
(error) => {
|
|
11127
|
+
throw new Error("Failed to read session", { cause: error });
|
|
11128
|
+
}
|
|
11129
|
+
);
|
|
11130
|
+
if (yamlKind === "symlink" || yamlKind === "not_a_file") {
|
|
11131
|
+
throw new SessionFileNotAFileError5(sessionId, "session.yaml", yamlKind);
|
|
11132
|
+
}
|
|
11090
11133
|
let session;
|
|
11091
11134
|
try {
|
|
11092
11135
|
const raw = await readYamlFile6(sessionYamlPath);
|
|
11093
11136
|
session = SessionSchema3.parse(normalizeSessionTimestamps(raw));
|
|
11094
11137
|
} catch (error) {
|
|
11095
|
-
if (
|
|
11138
|
+
if (findErrorCode14(error, "ENOENT")) {
|
|
11096
11139
|
throw new Error(`Session not found: ${idInput}`);
|
|
11097
11140
|
}
|
|
11098
11141
|
throw new Error("Failed to read session", { cause: error });
|
|
11099
11142
|
}
|
|
11100
11143
|
let eventsLostLines = 0;
|
|
11101
|
-
const events = await readAllEvents(
|
|
11144
|
+
const events = await readAllEvents(paths, sessionId, {
|
|
11102
11145
|
onWarning: (w) => {
|
|
11103
11146
|
if (w.kind === "malformed_json" || w.kind === "schema_violation") eventsLostLines++;
|
|
11104
11147
|
printReplayWarning(w, sessionId);
|
|
@@ -11356,7 +11399,7 @@ async function assertWorkspaceInitialized12(basouRoot) {
|
|
|
11356
11399
|
try {
|
|
11357
11400
|
await assertBasouRootSafe14(basouRoot);
|
|
11358
11401
|
} catch (error) {
|
|
11359
|
-
if (
|
|
11402
|
+
if (findErrorCode14(error, "ENOENT")) {
|
|
11360
11403
|
throw new Error("Workspace not initialized. Run 'basou init' first.");
|
|
11361
11404
|
}
|
|
11362
11405
|
throw error;
|
|
@@ -11392,6 +11435,7 @@ async function runSessionImport(options, ctx = {}) {
|
|
|
11392
11435
|
}
|
|
11393
11436
|
}
|
|
11394
11437
|
async function doRunSessionImport(options, ctx) {
|
|
11438
|
+
refuseDashInputFile("--from", options.from, { fileOnly: "the JSON" });
|
|
11395
11439
|
const cwd = ctx.cwd ?? process.cwd();
|
|
11396
11440
|
const repositoryRoot = await resolveRepositoryRootForSession(cwd, "import");
|
|
11397
11441
|
const paths = basouPaths19(repositoryRoot);
|
|
@@ -11425,12 +11469,12 @@ async function doRunSessionImport(options, ctx) {
|
|
|
11425
11469
|
}
|
|
11426
11470
|
async function readInputFile(path) {
|
|
11427
11471
|
try {
|
|
11428
|
-
return await
|
|
11472
|
+
return await readFile7(path, "utf8");
|
|
11429
11473
|
} catch (error) {
|
|
11430
|
-
if (
|
|
11474
|
+
if (findErrorCode14(error, "ENOENT")) {
|
|
11431
11475
|
throw new Error("Import source not found", { cause: error });
|
|
11432
11476
|
}
|
|
11433
|
-
if (
|
|
11477
|
+
if (findErrorCode14(error, "EISDIR")) {
|
|
11434
11478
|
throw new Error("Import source is not a file", { cause: error });
|
|
11435
11479
|
}
|
|
11436
11480
|
throw new Error("Failed to read import source", { cause: error });
|
|
@@ -11505,9 +11549,7 @@ async function doRunSessionNote(sessionIdInput, options, ctx) {
|
|
|
11505
11549
|
if (hasBody && hasFromFile) {
|
|
11506
11550
|
throw new Error("--body and --from-file are mutually exclusive");
|
|
11507
11551
|
}
|
|
11508
|
-
|
|
11509
|
-
throw new Error("--from-file - (stdin) is not supported in v0.1");
|
|
11510
|
-
}
|
|
11552
|
+
refuseDashInputFile("--from-file", options.fromFile, { textFlag: "--body" });
|
|
11511
11553
|
const cwd = ctx.cwd ?? process.cwd();
|
|
11512
11554
|
const repositoryRoot = await resolveRepositoryRootForSession(cwd, "note");
|
|
11513
11555
|
const paths = basouPaths19(repositoryRoot);
|
|
@@ -11542,12 +11584,12 @@ async function doRunSessionNote(sessionIdInput, options, ctx) {
|
|
|
11542
11584
|
}
|
|
11543
11585
|
async function readNoteFile(path) {
|
|
11544
11586
|
try {
|
|
11545
|
-
return await
|
|
11587
|
+
return await readFile7(path, "utf8");
|
|
11546
11588
|
} catch (error) {
|
|
11547
|
-
if (
|
|
11589
|
+
if (findErrorCode14(error, "ENOENT")) {
|
|
11548
11590
|
throw new Error("Note source not found", { cause: error });
|
|
11549
11591
|
}
|
|
11550
|
-
if (
|
|
11592
|
+
if (findErrorCode14(error, "EISDIR")) {
|
|
11551
11593
|
throw new Error("Note source is not a file", { cause: error });
|
|
11552
11594
|
}
|
|
11553
11595
|
throw new Error("Failed to read note source", { cause: error });
|
|
@@ -11617,7 +11659,7 @@ async function doRunSessionRechain(options, ctx) {
|
|
|
11617
11659
|
}
|
|
11618
11660
|
}
|
|
11619
11661
|
const tamperedCount = rows.filter(
|
|
11620
|
-
(r) => r.reason === "tampered" || r.reason === "symlink" || r.reason === "not_a_directory"
|
|
11662
|
+
(r) => r.reason === "tampered" || r.reason === "symlink" || r.reason === "not_a_directory" || r.reason === "events_not_a_file" || r.reason === "yaml_not_a_file"
|
|
11621
11663
|
).length;
|
|
11622
11664
|
const errorCount = rows.filter((r) => r.status === "error").length;
|
|
11623
11665
|
if (options.json === true) {
|
|
@@ -11645,6 +11687,10 @@ function renderRechainRow(row, dryRun) {
|
|
|
11645
11687
|
if (row.reason === "symlink" || row.reason === "not_a_directory") {
|
|
11646
11688
|
return `skipped (${row.reason === "symlink" ? "SYMLINK" : "NOT A DIRECTORY"} \u2014 not followed; inspect with 'basou verify')`;
|
|
11647
11689
|
}
|
|
11690
|
+
if (row.reason === "events_not_a_file" || row.reason === "yaml_not_a_file") {
|
|
11691
|
+
const file = row.reason === "events_not_a_file" ? "events.jsonl" : "session.yaml";
|
|
11692
|
+
return `skipped (${file} NOT A REGULAR FILE \u2014 not followed; inspect with 'basou verify')`;
|
|
11693
|
+
}
|
|
11648
11694
|
return `skipped (${row.reason})`;
|
|
11649
11695
|
case "error":
|
|
11650
11696
|
return `error (${row.message})`;
|
|
@@ -11660,7 +11706,7 @@ import {
|
|
|
11660
11706
|
assertBasouRootSafe as assertBasouRootSafe15,
|
|
11661
11707
|
basouPaths as basouPaths20,
|
|
11662
11708
|
computeWorkStats,
|
|
11663
|
-
findErrorCode as
|
|
11709
|
+
findErrorCode as findErrorCode15
|
|
11664
11710
|
} from "@basou/core";
|
|
11665
11711
|
function registerStatsCommand(program) {
|
|
11666
11712
|
program.command("stats").description("Report how much the AI worked (output volume + time proxies) across sessions").option("--by-source", "Break the totals down by session source kind").option("--by-day", "Break billable time and volume down by calendar day").option("--json", "Output the full stats as JSON").option("-v, --verbose", "Show error causes").action(async (options) => {
|
|
@@ -11769,7 +11815,7 @@ async function assertWorkspaceInitialized13(basouRoot) {
|
|
|
11769
11815
|
try {
|
|
11770
11816
|
await assertBasouRootSafe15(basouRoot);
|
|
11771
11817
|
} catch (error) {
|
|
11772
|
-
if (
|
|
11818
|
+
if (findErrorCode15(error, "ENOENT")) {
|
|
11773
11819
|
throw new Error("Workspace not initialized. Run 'basou init' first.");
|
|
11774
11820
|
}
|
|
11775
11821
|
throw error;
|
|
@@ -11781,7 +11827,7 @@ import {
|
|
|
11781
11827
|
assertBasouRootSafe as assertBasouRootSafe16,
|
|
11782
11828
|
basouPaths as basouPaths21,
|
|
11783
11829
|
buildStatusSnapshot,
|
|
11784
|
-
findErrorCode as
|
|
11830
|
+
findErrorCode as findErrorCode16,
|
|
11785
11831
|
readManifest as readManifest14,
|
|
11786
11832
|
writeStatus
|
|
11787
11833
|
} from "@basou/core";
|
|
@@ -11805,7 +11851,7 @@ async function doRunStatus(options, ctx) {
|
|
|
11805
11851
|
try {
|
|
11806
11852
|
await assertBasouRootSafe16(paths.root);
|
|
11807
11853
|
} catch (error) {
|
|
11808
|
-
if (
|
|
11854
|
+
if (findErrorCode16(error, "ENOENT")) {
|
|
11809
11855
|
throw new Error("Workspace not initialized. Run 'basou init' first.");
|
|
11810
11856
|
}
|
|
11811
11857
|
throw error;
|
|
@@ -11814,7 +11860,7 @@ async function doRunStatus(options, ctx) {
|
|
|
11814
11860
|
try {
|
|
11815
11861
|
manifest = await readManifest14(paths);
|
|
11816
11862
|
} catch (error) {
|
|
11817
|
-
if (
|
|
11863
|
+
if (findErrorCode16(error, "ENOENT")) {
|
|
11818
11864
|
throw new Error("Workspace not initialized. Run 'basou init' first.");
|
|
11819
11865
|
}
|
|
11820
11866
|
const gateMessage = formatVersionGateMessage(error);
|
|
@@ -11852,8 +11898,7 @@ function formatVersionGateMessage(error) {
|
|
|
11852
11898
|
}
|
|
11853
11899
|
|
|
11854
11900
|
// src/commands/task.ts
|
|
11855
|
-
import { readFile as
|
|
11856
|
-
import { join as join16 } from "path";
|
|
11901
|
+
import { readFile as readFile8 } from "fs/promises";
|
|
11857
11902
|
import {
|
|
11858
11903
|
archiveTask,
|
|
11859
11904
|
assertBasouRootSafe as assertBasouRootSafe17,
|
|
@@ -11863,7 +11908,7 @@ import {
|
|
|
11863
11908
|
deleteTask,
|
|
11864
11909
|
editTask,
|
|
11865
11910
|
enumerateArchivedTaskIds,
|
|
11866
|
-
findErrorCode as
|
|
11911
|
+
findErrorCode as findErrorCode17,
|
|
11867
11912
|
loadSessionEntries as loadSessionEntries3,
|
|
11868
11913
|
loadTaskEntries,
|
|
11869
11914
|
prefixedUlid as prefixedUlid5,
|
|
@@ -11950,9 +11995,7 @@ async function doRunTaskNew(options, ctx) {
|
|
|
11950
11995
|
if (options.description !== void 0 && options.fromFile !== void 0) {
|
|
11951
11996
|
throw new Error("--description and --from-file are mutually exclusive");
|
|
11952
11997
|
}
|
|
11953
|
-
|
|
11954
|
-
throw new Error("--from-file - (stdin) is not supported in v0.1");
|
|
11955
|
-
}
|
|
11998
|
+
refuseDashInputFile("--from-file", options.fromFile, { textFlag: "--description" });
|
|
11956
11999
|
const initialStatus = options.status ?? "planned";
|
|
11957
12000
|
if (options.completedAt !== void 0 && !isTerminalStatusForCli(initialStatus)) {
|
|
11958
12001
|
throw new Error("--completed-at requires --status done or cancelled");
|
|
@@ -12180,9 +12223,8 @@ async function doRunTaskShow(idInput, options, ctx) {
|
|
|
12180
12223
|
const events = [];
|
|
12181
12224
|
const linkedSessionIds = new Set(doc.task.task.linked_sessions);
|
|
12182
12225
|
for (const s of sessions) {
|
|
12183
|
-
const sessionDir = join16(paths.sessions, s.sessionId);
|
|
12184
12226
|
try {
|
|
12185
|
-
for await (const ev of replayEvents3(
|
|
12227
|
+
for await (const ev of replayEvents3(paths, s.sessionId, {
|
|
12186
12228
|
onWarning: (w) => printReplayWarning(w, s.sessionId)
|
|
12187
12229
|
})) {
|
|
12188
12230
|
if ((ev.type === "task_created" || ev.type === "task_status_changed" || ev.type === "task_reconciled" || ev.type === "task_linkage_refreshed" || ev.type === "task_deleted" || ev.type === "task_archived") && ev.task_id === taskId) {
|
|
@@ -12869,12 +12911,12 @@ function parsePositiveInt2(raw) {
|
|
|
12869
12911
|
}
|
|
12870
12912
|
async function readDescriptionFile(path) {
|
|
12871
12913
|
try {
|
|
12872
|
-
return await
|
|
12914
|
+
return await readFile8(path, "utf8");
|
|
12873
12915
|
} catch (error) {
|
|
12874
|
-
if (
|
|
12916
|
+
if (findErrorCode17(error, "ENOENT")) {
|
|
12875
12917
|
throw new Error("Description source not found", { cause: error });
|
|
12876
12918
|
}
|
|
12877
|
-
if (
|
|
12919
|
+
if (findErrorCode17(error, "EISDIR")) {
|
|
12878
12920
|
throw new Error("Description source is not a file", { cause: error });
|
|
12879
12921
|
}
|
|
12880
12922
|
throw new Error("Failed to read description source", { cause: error });
|
|
@@ -12887,7 +12929,7 @@ async function assertWorkspaceInitialized14(basouRoot) {
|
|
|
12887
12929
|
try {
|
|
12888
12930
|
await assertBasouRootSafe17(basouRoot);
|
|
12889
12931
|
} catch (error) {
|
|
12890
|
-
if (
|
|
12932
|
+
if (findErrorCode17(error, "ENOENT")) {
|
|
12891
12933
|
throw new Error("Workspace not initialized. Run 'basou init' first.");
|
|
12892
12934
|
}
|
|
12893
12935
|
throw error;
|
|
@@ -12978,7 +13020,7 @@ import {
|
|
|
12978
13020
|
assertBasouRootSafe as assertBasouRootSafe18,
|
|
12979
13021
|
basouPaths as basouPaths23,
|
|
12980
13022
|
enumerateSessionEntries as enumerateSessionEntries3,
|
|
12981
|
-
findErrorCode as
|
|
13023
|
+
findErrorCode as findErrorCode18,
|
|
12982
13024
|
resolveSessionId as resolveSessionId5,
|
|
12983
13025
|
verifyEventsChain
|
|
12984
13026
|
} from "@basou/core";
|
|
@@ -13065,7 +13107,7 @@ async function assertWorkspaceInitialized15(basouRoot) {
|
|
|
13065
13107
|
try {
|
|
13066
13108
|
await assertBasouRootSafe18(basouRoot);
|
|
13067
13109
|
} catch (error) {
|
|
13068
|
-
if (
|
|
13110
|
+
if (findErrorCode18(error, "ENOENT")) {
|
|
13069
13111
|
throw new Error("Workspace not initialized. Run 'basou init' first.");
|
|
13070
13112
|
}
|
|
13071
13113
|
throw error;
|
|
@@ -13075,11 +13117,11 @@ async function assertWorkspaceInitialized15(basouRoot) {
|
|
|
13075
13117
|
// src/commands/view.ts
|
|
13076
13118
|
import { spawn } from "child_process";
|
|
13077
13119
|
import { createHash as createHash2 } from "crypto";
|
|
13078
|
-
import { basename as basename11, dirname as dirname9, join as
|
|
13120
|
+
import { basename as basename11, dirname as dirname9, join as join17, resolve as resolve14 } from "path";
|
|
13079
13121
|
import {
|
|
13080
13122
|
assertBasouRootSafe as assertBasouRootSafe19,
|
|
13081
13123
|
basouPaths as basouPaths25,
|
|
13082
|
-
findErrorCode as
|
|
13124
|
+
findErrorCode as findErrorCode20,
|
|
13083
13125
|
loadPortfolioConfig as loadPortfolioConfig6,
|
|
13084
13126
|
readManifest as readManifest19
|
|
13085
13127
|
} from "@basou/core";
|
|
@@ -13089,7 +13131,7 @@ import { InvalidArgumentError as InvalidArgumentError8 } from "commander";
|
|
|
13089
13131
|
import { createReadStream as createReadStream2 } from "fs";
|
|
13090
13132
|
import { readdir as readdir4, stat as stat7 } from "fs/promises";
|
|
13091
13133
|
import { homedir as homedir12 } from "os";
|
|
13092
|
-
import { basename as basename9, dirname as dirname8, join as
|
|
13134
|
+
import { basename as basename9, dirname as dirname8, join as join15 } from "path";
|
|
13093
13135
|
import { createInterface as createInterface2 } from "readline";
|
|
13094
13136
|
import { basouPaths as basouPaths24, readManifest as readManifest16 } from "@basou/core";
|
|
13095
13137
|
function uncapturedTotal(result) {
|
|
@@ -13101,8 +13143,8 @@ async function checkPortfolioCoverage(workspaces, ctx = {}) {
|
|
|
13101
13143
|
ctx.portfolioConfigPath
|
|
13102
13144
|
);
|
|
13103
13145
|
const listedDirs = new Set([...roots].map((root) => encodeProjectDir(root)));
|
|
13104
|
-
const claudeProjectsDir = ctx.claudeProjectsDir ??
|
|
13105
|
-
const codexSessionsDir = ctx.codexSessionsDir ??
|
|
13146
|
+
const claudeProjectsDir = ctx.claudeProjectsDir ?? join15(homedir12(), ".claude", "projects");
|
|
13147
|
+
const codexSessionsDir = ctx.codexSessionsDir ?? join15(homedir12(), ".codex", "sessions");
|
|
13106
13148
|
const tally = new Tally(roots);
|
|
13107
13149
|
const absentTrees = [];
|
|
13108
13150
|
const claudeFiles = await listClaudeTranscripts(claudeProjectsDir);
|
|
@@ -13240,7 +13282,7 @@ async function isDirEntry(parent, entry) {
|
|
|
13240
13282
|
if (entry.isDirectory()) return true;
|
|
13241
13283
|
if (!entry.isSymbolicLink()) return false;
|
|
13242
13284
|
try {
|
|
13243
|
-
return (await stat7(
|
|
13285
|
+
return (await stat7(join15(parent, entry.name))).isDirectory();
|
|
13244
13286
|
} catch {
|
|
13245
13287
|
return false;
|
|
13246
13288
|
}
|
|
@@ -13255,7 +13297,7 @@ async function listClaudeTranscripts(projectsRoot) {
|
|
|
13255
13297
|
const files = [];
|
|
13256
13298
|
for (const entry of entries) {
|
|
13257
13299
|
if (!await isDirEntry(projectsRoot, entry)) continue;
|
|
13258
|
-
const full =
|
|
13300
|
+
const full = join15(projectsRoot, entry.name);
|
|
13259
13301
|
let names;
|
|
13260
13302
|
try {
|
|
13261
13303
|
names = await readdir4(full);
|
|
@@ -13263,7 +13305,7 @@ async function listClaudeTranscripts(projectsRoot) {
|
|
|
13263
13305
|
continue;
|
|
13264
13306
|
}
|
|
13265
13307
|
for (const name of names) {
|
|
13266
|
-
if (name.endsWith(".jsonl")) files.push(
|
|
13308
|
+
if (name.endsWith(".jsonl")) files.push(join15(full, name));
|
|
13267
13309
|
}
|
|
13268
13310
|
}
|
|
13269
13311
|
return files.sort();
|
|
@@ -13278,7 +13320,7 @@ async function listCodexRollouts(sessionsRoot) {
|
|
|
13278
13320
|
return;
|
|
13279
13321
|
}
|
|
13280
13322
|
for (const entry of entries) {
|
|
13281
|
-
const full =
|
|
13323
|
+
const full = join15(dir, entry.name);
|
|
13282
13324
|
if (entry.isDirectory()) await walk(full);
|
|
13283
13325
|
else if (entry.isFile() && entry.name.startsWith("rollout-") && entry.name.endsWith(".jsonl")) {
|
|
13284
13326
|
found.push(full);
|
|
@@ -13409,7 +13451,7 @@ function inertLines(result) {
|
|
|
13409
13451
|
// src/lib/portfolio-safety.ts
|
|
13410
13452
|
import { execFile as execFile2 } from "child_process";
|
|
13411
13453
|
import { lstat as lstat3, realpath as realpath5 } from "fs/promises";
|
|
13412
|
-
import { isAbsolute as isAbsolute6, join as
|
|
13454
|
+
import { isAbsolute as isAbsolute6, join as join16, relative as relative5, resolve as resolve12, sep as sep4 } from "path";
|
|
13413
13455
|
import { promisify as promisify2 } from "util";
|
|
13414
13456
|
import { readManifest as readManifest17 } from "@basou/core";
|
|
13415
13457
|
var execFileAsync2 = promisify2(execFile2);
|
|
@@ -13433,7 +13475,7 @@ function isBasouPath(p) {
|
|
|
13433
13475
|
async function inspectRepo(repoPath) {
|
|
13434
13476
|
let hasEntry = false;
|
|
13435
13477
|
try {
|
|
13436
|
-
await lstat3(
|
|
13478
|
+
await lstat3(join16(repoPath, ".basou"));
|
|
13437
13479
|
hasEntry = true;
|
|
13438
13480
|
} catch (error) {
|
|
13439
13481
|
if (errorCode2(error) !== "ENOENT") {
|
|
@@ -13587,7 +13629,7 @@ function formatSafetyReport(result) {
|
|
|
13587
13629
|
|
|
13588
13630
|
// src/lib/view-server.ts
|
|
13589
13631
|
import { createServer } from "http";
|
|
13590
|
-
import { basename as basename10,
|
|
13632
|
+
import { basename as basename10, resolve as resolve13 } from "path";
|
|
13591
13633
|
import {
|
|
13592
13634
|
boardLivePage,
|
|
13593
13635
|
boardPage,
|
|
@@ -13595,7 +13637,7 @@ import {
|
|
|
13595
13637
|
boardPageUnavailable,
|
|
13596
13638
|
computeWorkStats as computeWorkStats2,
|
|
13597
13639
|
enumerateApprovals as enumerateApprovals2,
|
|
13598
|
-
findErrorCode as
|
|
13640
|
+
findErrorCode as findErrorCode19,
|
|
13599
13641
|
inspectSessionEntry as inspectSessionEntry2,
|
|
13600
13642
|
isLazyExpired as isLazyExpired2,
|
|
13601
13643
|
loadApproval as loadApproval2,
|
|
@@ -13609,7 +13651,9 @@ import {
|
|
|
13609
13651
|
renderDecisions as renderDecisions3,
|
|
13610
13652
|
renderHandoff as renderHandoff3,
|
|
13611
13653
|
resolveViewLanguageFromPaths,
|
|
13654
|
+
SessionFileNotAFileError as SessionFileNotAFileError6,
|
|
13612
13655
|
SessionIdSchema,
|
|
13656
|
+
SessionNotADirectoryError,
|
|
13613
13657
|
summarizeOrientation,
|
|
13614
13658
|
tryRemoteUrl
|
|
13615
13659
|
} from "@basou/core";
|
|
@@ -15544,7 +15588,7 @@ async function overview(ws, nowProvider, resolveRemoteUrl) {
|
|
|
15544
15588
|
try {
|
|
15545
15589
|
manifest = await readManifest18(ws.paths);
|
|
15546
15590
|
} catch (error) {
|
|
15547
|
-
if (
|
|
15591
|
+
if (findErrorCode19(error, "ENOENT")) {
|
|
15548
15592
|
return { initialized: false, repoRoot: ws.repoRoot };
|
|
15549
15593
|
}
|
|
15550
15594
|
throw error;
|
|
@@ -15612,10 +15656,7 @@ async function sessionDetail(ws, sessionId) {
|
|
|
15612
15656
|
}
|
|
15613
15657
|
const entry = await inspectSessionEntry2(ws.paths, sessionId);
|
|
15614
15658
|
if (entry === "symlink" || entry === "not_a_directory") {
|
|
15615
|
-
throw new HttpError(
|
|
15616
|
-
404,
|
|
15617
|
-
`Session ${sessionId} is not a directory; a symlink or a file there is not followed`
|
|
15618
|
-
);
|
|
15659
|
+
throw new HttpError(404, new SessionNotADirectoryError(sessionId, entry).message);
|
|
15619
15660
|
}
|
|
15620
15661
|
let session;
|
|
15621
15662
|
try {
|
|
@@ -15624,10 +15665,11 @@ async function sessionDetail(ws, sessionId) {
|
|
|
15624
15665
|
if (error instanceof Error && error.message === "YAML file not found") {
|
|
15625
15666
|
throw new HttpError(404, "Session not found");
|
|
15626
15667
|
}
|
|
15668
|
+
if (error instanceof SessionFileNotAFileError6) throw new HttpError(404, error.message);
|
|
15627
15669
|
throw error;
|
|
15628
15670
|
}
|
|
15629
15671
|
try {
|
|
15630
|
-
const events = await readAllEvents2(
|
|
15672
|
+
const events = await readAllEvents2(ws.paths, sessionId);
|
|
15631
15673
|
return { session, events };
|
|
15632
15674
|
} catch {
|
|
15633
15675
|
return { session, events: [], degraded: true };
|
|
@@ -15892,7 +15934,7 @@ async function viewBoardOf(root, paths) {
|
|
|
15892
15934
|
own = void 0;
|
|
15893
15935
|
}
|
|
15894
15936
|
if (own?.visibility !== "private") return { why: "no_board" };
|
|
15895
|
-
return { recordsDir:
|
|
15937
|
+
return { recordsDir: join17(root, dirname9(DEFAULT_BOARD_PATH), "records") };
|
|
15896
15938
|
}
|
|
15897
15939
|
async function buildPortfolioDeps(workspaceFlags, ctx, cwd) {
|
|
15898
15940
|
const specs = workspaceFlags.length > 0 ? workspaceFlags.map((p) => ({ path: resolve14(cwd, p) })) : await loadPortfolioConfig6(ctx.portfolioConfigPath);
|
|
@@ -15954,7 +15996,7 @@ async function startListening(port, deps) {
|
|
|
15954
15996
|
try {
|
|
15955
15997
|
return await startViewServer({ port, deps });
|
|
15956
15998
|
} catch (error) {
|
|
15957
|
-
if (
|
|
15999
|
+
if (findErrorCode20(error, "EADDRINUSE")) {
|
|
15958
16000
|
throw new Error(`Port ${port} is already in use. Pass --port <n> to choose another.`, {
|
|
15959
16001
|
cause: error
|
|
15960
16002
|
});
|
|
@@ -16007,7 +16049,7 @@ async function assertWorkspaceInitialized16(basouRoot) {
|
|
|
16007
16049
|
try {
|
|
16008
16050
|
await assertBasouRootSafe19(basouRoot);
|
|
16009
16051
|
} catch (error) {
|
|
16010
|
-
if (
|
|
16052
|
+
if (findErrorCode20(error, "ENOENT")) {
|
|
16011
16053
|
throw new Error("Workspace not initialized. Run 'basou init' first.");
|
|
16012
16054
|
}
|
|
16013
16055
|
throw error;
|
|
@@ -16046,7 +16088,7 @@ function installExperimentalNotice(program, names = EXPERIMENTAL_COMMANDS, write
|
|
|
16046
16088
|
function readBuildStamp() {
|
|
16047
16089
|
if (false) return void 0;
|
|
16048
16090
|
try {
|
|
16049
|
-
return JSON.parse('{"version":"0.
|
|
16091
|
+
return JSON.parse('{"version":"0.70.0","commit":"60d302a","committedAt":"2026-10-16T17:39:30+09:00"}');
|
|
16050
16092
|
} catch {
|
|
16051
16093
|
return void 0;
|
|
16052
16094
|
}
|