@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/index.js
CHANGED
|
@@ -95,7 +95,7 @@ function printSessionSkip(sid, reason) {
|
|
|
95
95
|
const short = shortSessionId(sid);
|
|
96
96
|
if (reason === "events_jsonl_unreadable") {
|
|
97
97
|
console.error(`Warning: skipped suspect check for ${short}: events.jsonl unreadable`);
|
|
98
|
-
} else if (reason === "session_dir_not_directory") {
|
|
98
|
+
} else if (reason === "session_dir_not_directory" || reason === "session_yaml_not_a_file") {
|
|
99
99
|
console.error(`Skipped ${sid}: ${reason}`);
|
|
100
100
|
} else {
|
|
101
101
|
console.error(`Skipped ${short}: ${reason}`);
|
|
@@ -116,6 +116,11 @@ function printSessionListSkip(sid, reason) {
|
|
|
116
116
|
case "session_dir_not_directory":
|
|
117
117
|
console.error(`Skipped ${sid}: not a directory (a symlink or a file is not followed)`);
|
|
118
118
|
break;
|
|
119
|
+
case "session_yaml_not_a_file":
|
|
120
|
+
console.error(
|
|
121
|
+
`Skipped ${sid}: session.yaml is not a regular file (a symlink or anything else is not followed)`
|
|
122
|
+
);
|
|
123
|
+
break;
|
|
119
124
|
}
|
|
120
125
|
}
|
|
121
126
|
function printTaskSkip(taskId, reason) {
|
|
@@ -152,7 +157,8 @@ import {
|
|
|
152
157
|
prefixedUlid,
|
|
153
158
|
readSessionYaml,
|
|
154
159
|
readYamlFile,
|
|
155
|
-
replayEvents
|
|
160
|
+
replayEvents,
|
|
161
|
+
SessionFileNotAFileError
|
|
156
162
|
} from "@basou/core";
|
|
157
163
|
|
|
158
164
|
// src/lib/repo-root.ts
|
|
@@ -425,7 +431,7 @@ async function doRunApprovalShow(idInput, options, ctx) {
|
|
|
425
431
|
`Warning: session ${sessionId} is not a directory (a symlink or a file is not followed); its events are not shown`
|
|
426
432
|
);
|
|
427
433
|
} else {
|
|
428
|
-
for await (const ev of replayEvents(
|
|
434
|
+
for await (const ev of replayEvents(paths, sessionId, {
|
|
429
435
|
onWarning: (w) => printReplayWarning(w, sessionId)
|
|
430
436
|
})) {
|
|
431
437
|
if (isApprovalEvent(ev) && ev.approval_id === id) {
|
|
@@ -525,8 +531,7 @@ async function doRunApprovalResolve(idInput, options, ctx, decision) {
|
|
|
525
531
|
const sessionLock = await acquireLock(paths, "session", approval.session_id);
|
|
526
532
|
try {
|
|
527
533
|
await assertSessionDirSafe(paths, approval.session_id);
|
|
528
|
-
const
|
|
529
|
-
for await (const ev of replayEvents(sessionDir, {
|
|
534
|
+
for await (const ev of replayEvents(paths, approval.session_id, {
|
|
530
535
|
onWarning: (w) => printReplayWarning(w, approval.session_id)
|
|
531
536
|
})) {
|
|
532
537
|
if (isApprovalEvent(ev) && ev.approval_id === approval.id && (ev.type === "approval_approved" || ev.type === "approval_rejected" || ev.type === "approval_expired")) {
|
|
@@ -539,7 +544,8 @@ async function doRunApprovalResolve(idInput, options, ctx, decision) {
|
|
|
539
544
|
let sessionStatus = null;
|
|
540
545
|
try {
|
|
541
546
|
sessionStatus = (await readSessionYaml(paths, approval.session_id)).session.status;
|
|
542
|
-
} catch {
|
|
547
|
+
} catch (error) {
|
|
548
|
+
if (error instanceof SessionFileNotAFileError) throw error;
|
|
543
549
|
sessionStatus = null;
|
|
544
550
|
}
|
|
545
551
|
const attachable = sessionStatus === "initialized" || sessionStatus === "running" || sessionStatus === "waiting_approval";
|
|
@@ -1048,6 +1054,7 @@ import {
|
|
|
1048
1054
|
readSessionYaml as readSessionYaml2,
|
|
1049
1055
|
reimportPreservingId,
|
|
1050
1056
|
SESSION_IMPORT_SCHEMA_VERSION,
|
|
1057
|
+
SessionFileNotAFileError as SessionFileNotAFileError2,
|
|
1051
1058
|
SessionImportPayloadSchema
|
|
1052
1059
|
} from "@basou/core";
|
|
1053
1060
|
|
|
@@ -1221,10 +1228,11 @@ async function resolveImportTarget(ctx) {
|
|
|
1221
1228
|
async function importDerivedSessions(paths, manifest, options, sourceKind, candidates, projectPaths, boundaryDeclared) {
|
|
1222
1229
|
await assertSessionStoreSafe(paths);
|
|
1223
1230
|
if (options.dryRun !== true) await assertLockStoreSafe2(paths);
|
|
1224
|
-
const {
|
|
1225
|
-
|
|
1226
|
-
|
|
1227
|
-
|
|
1231
|
+
const {
|
|
1232
|
+
byExternalId: existingByExternalId,
|
|
1233
|
+
notDirectories,
|
|
1234
|
+
yamlNotFiles
|
|
1235
|
+
} = await loadExistingByExternalId(paths, sourceKind);
|
|
1228
1236
|
let notDirectoriesWarned = false;
|
|
1229
1237
|
const seenThisRun = /* @__PURE__ */ new Set();
|
|
1230
1238
|
const crossProjectCheck = boundaryDeclared;
|
|
@@ -1317,12 +1325,20 @@ async function importDerivedSessions(paths, manifest, options, sourceKind, candi
|
|
|
1317
1325
|
}
|
|
1318
1326
|
counts.replaced++;
|
|
1319
1327
|
}
|
|
1320
|
-
if (priors.length === 0 && notDirectories.length > 0 && !notDirectoriesWarned) {
|
|
1328
|
+
if (priors.length === 0 && (notDirectories.length > 0 || yamlNotFiles.length > 0) && !notDirectoriesWarned) {
|
|
1321
1329
|
notDirectoriesWarned = true;
|
|
1322
|
-
|
|
1323
|
-
|
|
1324
|
-
|
|
1325
|
-
|
|
1330
|
+
if (notDirectories.length > 0) {
|
|
1331
|
+
const one = notDirectories.length === 1;
|
|
1332
|
+
console.error(
|
|
1333
|
+
`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`
|
|
1334
|
+
);
|
|
1335
|
+
}
|
|
1336
|
+
if (yamlNotFiles.length > 0) {
|
|
1337
|
+
const one = yamlNotFiles.length === 1;
|
|
1338
|
+
console.error(
|
|
1339
|
+
`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`
|
|
1340
|
+
);
|
|
1341
|
+
}
|
|
1326
1342
|
}
|
|
1327
1343
|
const result = await importSessionFromJson(paths, manifest, payload, {
|
|
1328
1344
|
dryRun: options.dryRun === true
|
|
@@ -1404,13 +1420,15 @@ async function loadExistingByExternalId(paths, sourceKind) {
|
|
|
1404
1420
|
try {
|
|
1405
1421
|
({ dirs: sessionIds, notDirectories } = await enumerateSessionEntries(paths));
|
|
1406
1422
|
} catch {
|
|
1407
|
-
return { byExternalId, notDirectories: [] };
|
|
1423
|
+
return { byExternalId, notDirectories: [], yamlNotFiles: [] };
|
|
1408
1424
|
}
|
|
1425
|
+
const yamlNotFiles = [];
|
|
1409
1426
|
for (const sessionId of sessionIds) {
|
|
1410
1427
|
let session;
|
|
1411
1428
|
try {
|
|
1412
1429
|
session = await readSessionYaml2(paths, sessionId);
|
|
1413
|
-
} catch {
|
|
1430
|
+
} catch (error) {
|
|
1431
|
+
if (error instanceof SessionFileNotAFileError2) yamlNotFiles.push(sessionId);
|
|
1414
1432
|
continue;
|
|
1415
1433
|
}
|
|
1416
1434
|
if (session.session.source.kind !== sourceKind) continue;
|
|
@@ -1425,7 +1443,7 @@ async function loadExistingByExternalId(paths, sourceKind) {
|
|
|
1425
1443
|
const match = typeof label === "string" ? label.match(/^claude-code import (\S+)$/) : null;
|
|
1426
1444
|
if (match?.[1] !== void 0) add(match[1], prior);
|
|
1427
1445
|
}
|
|
1428
|
-
return { byExternalId, notDirectories };
|
|
1446
|
+
return { byExternalId, notDirectories, yamlNotFiles };
|
|
1429
1447
|
}
|
|
1430
1448
|
async function selectTranscriptFiles(projectsRoot, projectPaths, options) {
|
|
1431
1449
|
if (options.session !== void 0) {
|
|
@@ -2958,9 +2976,8 @@ async function doRunChannelClear(face, options) {
|
|
|
2958
2976
|
}
|
|
2959
2977
|
|
|
2960
2978
|
// src/commands/decision.ts
|
|
2961
|
-
import { readFile as readFile3 } from "fs/promises";
|
|
2962
2979
|
import { homedir as homedir3 } from "os";
|
|
2963
|
-
import {
|
|
2980
|
+
import { resolve as resolve4 } from "path";
|
|
2964
2981
|
import {
|
|
2965
2982
|
AGENT_INFRA_DIRS as AGENT_INFRA_DIRS2,
|
|
2966
2983
|
acquireLock as acquireLock2,
|
|
@@ -2971,7 +2988,7 @@ import {
|
|
|
2971
2988
|
createAdHocSessionWithEvent,
|
|
2972
2989
|
displayPath as displayPath4,
|
|
2973
2990
|
EVENT_SCHEMA_VERSION as EVENT_SCHEMA_VERSION2,
|
|
2974
|
-
findErrorCode as
|
|
2991
|
+
findErrorCode as findErrorCode5,
|
|
2975
2992
|
isValidPrefixedId,
|
|
2976
2993
|
LOCAL_CLI_EVENT_SOURCE as LOCAL_CLI_EVENT_SOURCE2,
|
|
2977
2994
|
loadSessionEntries,
|
|
@@ -2983,6 +3000,35 @@ import {
|
|
|
2983
3000
|
} from "@basou/core";
|
|
2984
3001
|
import { InvalidArgumentError as InvalidArgumentError2 } from "commander";
|
|
2985
3002
|
|
|
3003
|
+
// src/lib/input-file.ts
|
|
3004
|
+
import { readFile as readFile3 } from "fs/promises";
|
|
3005
|
+
import { findErrorCode as findErrorCode4 } from "@basou/core";
|
|
3006
|
+
function refuseDashInputFile(flag, value, alternative) {
|
|
3007
|
+
if (value !== "-") return;
|
|
3008
|
+
if (alternative === "stdin") {
|
|
3009
|
+
throw new Error(
|
|
3010
|
+
`${flag} - is refused: - is reserved for standard input. Omit ${flag} to read stdin, or pass ${flag} ./- to read a file named -.`
|
|
3011
|
+
);
|
|
3012
|
+
}
|
|
3013
|
+
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}`;
|
|
3014
|
+
throw new Error(
|
|
3015
|
+
`${flag} - is refused: - is reserved for standard input, which this command does not read. ${toFile}; a file named - is read with ${flag} ./-.`
|
|
3016
|
+
);
|
|
3017
|
+
}
|
|
3018
|
+
async function readInputFileText(flag, path) {
|
|
3019
|
+
try {
|
|
3020
|
+
return await readFile3(path, "utf8");
|
|
3021
|
+
} catch (error) {
|
|
3022
|
+
if (findErrorCode4(error, "ENOENT")) {
|
|
3023
|
+
throw new Error(`${flag} names a file that does not exist`, { cause: error });
|
|
3024
|
+
}
|
|
3025
|
+
if (findErrorCode4(error, "EISDIR")) {
|
|
3026
|
+
throw new Error(`${flag} names a directory, not a file`, { cause: error });
|
|
3027
|
+
}
|
|
3028
|
+
throw new Error(`Could not read the file ${flag} names`, { cause: error });
|
|
3029
|
+
}
|
|
3030
|
+
}
|
|
3031
|
+
|
|
2986
3032
|
// src/commands/decision-gaps.ts
|
|
2987
3033
|
import {
|
|
2988
3034
|
basouPaths as basouPaths5,
|
|
@@ -3350,6 +3396,7 @@ async function runDecisionCapture(options, ctx = {}) {
|
|
|
3350
3396
|
}
|
|
3351
3397
|
}
|
|
3352
3398
|
async function doRunDecisionCapture(options, ctx) {
|
|
3399
|
+
refuseDashInputFile("--file", options.file, "stdin");
|
|
3353
3400
|
const cwd = ctx.cwd ?? process.cwd();
|
|
3354
3401
|
const repositoryRoot = await resolveBasouRootForCommand(cwd, "decision capture");
|
|
3355
3402
|
const paths = basouPaths6(repositoryRoot);
|
|
@@ -3511,9 +3558,8 @@ function isDecisionId(value) {
|
|
|
3511
3558
|
async function decisionExists(paths, decisionId) {
|
|
3512
3559
|
const entries = await loadSessionEntries(paths, { now: /* @__PURE__ */ new Date() });
|
|
3513
3560
|
for (const entry of entries) {
|
|
3514
|
-
const sessionDir = join7(paths.sessions, entry.sessionId);
|
|
3515
3561
|
try {
|
|
3516
|
-
for await (const ev of replayEvents2(
|
|
3562
|
+
for await (const ev of replayEvents2(paths, entry.sessionId, {})) {
|
|
3517
3563
|
if (ev.type === "decision_recorded" && ev.decision_id === decisionId) return true;
|
|
3518
3564
|
}
|
|
3519
3565
|
} catch {
|
|
@@ -3565,14 +3611,7 @@ function parseReason(raw) {
|
|
|
3565
3611
|
}
|
|
3566
3612
|
async function readCaptureInput(options, ctx) {
|
|
3567
3613
|
if (options.file !== void 0) {
|
|
3568
|
-
|
|
3569
|
-
return await readFile3(options.file, "utf8");
|
|
3570
|
-
} catch (error) {
|
|
3571
|
-
if (findErrorCode4(error, "ENOENT")) {
|
|
3572
|
-
throw new Error(`Input file not found: ${options.file}`);
|
|
3573
|
-
}
|
|
3574
|
-
throw error;
|
|
3575
|
-
}
|
|
3614
|
+
return await readInputFileText("--file", options.file);
|
|
3576
3615
|
}
|
|
3577
3616
|
if (ctx.readInput !== void 0) {
|
|
3578
3617
|
return await ctx.readInput();
|
|
@@ -3911,7 +3950,7 @@ async function assertWorkspaceInitialized4(basouRoot) {
|
|
|
3911
3950
|
try {
|
|
3912
3951
|
await assertBasouRootSafe4(basouRoot);
|
|
3913
3952
|
} catch (error) {
|
|
3914
|
-
if (
|
|
3953
|
+
if (findErrorCode5(error, "ENOENT")) {
|
|
3915
3954
|
throw new Error("Workspace not initialized. Run 'basou init' first.");
|
|
3916
3955
|
}
|
|
3917
3956
|
throw error;
|
|
@@ -3922,7 +3961,7 @@ async function assertWorkspaceInitialized4(basouRoot) {
|
|
|
3922
3961
|
import {
|
|
3923
3962
|
assertBasouRootSafe as assertBasouRootSafe5,
|
|
3924
3963
|
basouPaths as basouPaths7,
|
|
3925
|
-
findErrorCode as
|
|
3964
|
+
findErrorCode as findErrorCode6,
|
|
3926
3965
|
readMarkdownFile as readMarkdownFile4,
|
|
3927
3966
|
renderDecisions as renderDecisions2,
|
|
3928
3967
|
renderWithMarkers as renderWithMarkers2,
|
|
@@ -3964,7 +4003,7 @@ async function assertWorkspaceInitialized5(basouRoot) {
|
|
|
3964
4003
|
try {
|
|
3965
4004
|
await assertBasouRootSafe5(basouRoot);
|
|
3966
4005
|
} catch (error) {
|
|
3967
|
-
if (
|
|
4006
|
+
if (findErrorCode6(error, "ENOENT")) {
|
|
3968
4007
|
throw new Error("Workspace not initialized. Run 'basou init' first.");
|
|
3969
4008
|
}
|
|
3970
4009
|
throw error;
|
|
@@ -3974,7 +4013,7 @@ async function assertWorkspaceInitialized5(basouRoot) {
|
|
|
3974
4013
|
// src/commands/exec.ts
|
|
3975
4014
|
import { mkdir as mkdir2, rm as rm2 } from "fs/promises";
|
|
3976
4015
|
import { homedir as homedir4 } from "os";
|
|
3977
|
-
import { join as
|
|
4016
|
+
import { join as join7 } from "path";
|
|
3978
4017
|
import {
|
|
3979
4018
|
acquireLock as acquireLock3,
|
|
3980
4019
|
assertBasouRootSafe as assertBasouRootSafe6,
|
|
@@ -3985,17 +4024,37 @@ import {
|
|
|
3985
4024
|
EVENT_SCHEMA_VERSION as EVENT_SCHEMA_VERSION3,
|
|
3986
4025
|
finalizeSessionYaml,
|
|
3987
4026
|
getSnapshot,
|
|
4027
|
+
lstatSessionFile,
|
|
3988
4028
|
overwriteYamlFile,
|
|
3989
4029
|
parseDuration,
|
|
3990
4030
|
prefixedUlid as prefixedUlid3,
|
|
3991
4031
|
readManifest as readManifest6,
|
|
3992
4032
|
readYamlFile as readYamlFile2,
|
|
3993
4033
|
SESSION_SCHEMA_VERSION,
|
|
4034
|
+
SessionFileNotAFileError as SessionFileNotAFileError3,
|
|
3994
4035
|
SessionSchema,
|
|
3995
4036
|
sanitizeWorkingDirectory,
|
|
3996
4037
|
writeObservedDuration,
|
|
3997
4038
|
writeYamlFile
|
|
3998
4039
|
} from "@basou/core";
|
|
4040
|
+
|
|
4041
|
+
// src/lib/signal-exit-code.ts
|
|
4042
|
+
import { constants } from "os";
|
|
4043
|
+
var PROMISED_SIGNAL_NUMBERS = {
|
|
4044
|
+
SIGHUP: 1,
|
|
4045
|
+
SIGINT: 2,
|
|
4046
|
+
SIGQUIT: 3,
|
|
4047
|
+
SIGKILL: 9,
|
|
4048
|
+
SIGTERM: 15
|
|
4049
|
+
};
|
|
4050
|
+
var UNNUMBERED_SIGNAL_EXIT_CODE = 255;
|
|
4051
|
+
function signalToExitCode(sig) {
|
|
4052
|
+
if (sig === null) return 1;
|
|
4053
|
+
const num = PROMISED_SIGNAL_NUMBERS[sig] ?? constants.signals[sig];
|
|
4054
|
+
return num === void 0 ? UNNUMBERED_SIGNAL_EXIT_CODE : 128 + num;
|
|
4055
|
+
}
|
|
4056
|
+
|
|
4057
|
+
// src/commands/exec.ts
|
|
3999
4058
|
function registerExecCommand(program2) {
|
|
4000
4059
|
program2.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) => {
|
|
4001
4060
|
try {
|
|
@@ -4018,12 +4077,12 @@ async function runExec(command, args, options, ctx = {}) {
|
|
|
4018
4077
|
const manifest = await readManifest6(paths);
|
|
4019
4078
|
await assertSessionStoreSafe2(paths);
|
|
4020
4079
|
const sessionId = prefixedUlid3("ses");
|
|
4021
|
-
const sessionDir =
|
|
4080
|
+
const sessionDir = join7(paths.sessions, sessionId);
|
|
4022
4081
|
const appendEvent = ctx.appendEvent ?? (async (_sessionDir, event) => {
|
|
4023
4082
|
await coreAppendChainedEvent(paths, sessionId, event);
|
|
4024
4083
|
});
|
|
4025
4084
|
const startedAt = now().toISOString();
|
|
4026
|
-
const sessionYamlPath =
|
|
4085
|
+
const sessionYamlPath = join7(sessionDir, "session.yaml");
|
|
4027
4086
|
const session = buildInitialSession({
|
|
4028
4087
|
id: sessionId,
|
|
4029
4088
|
command,
|
|
@@ -4072,7 +4131,7 @@ async function runExec(command, args, options, ctx = {}) {
|
|
|
4072
4131
|
});
|
|
4073
4132
|
const runningLock = await acquireLock3(paths, "session", sessionId);
|
|
4074
4133
|
try {
|
|
4075
|
-
await mutateSessionYaml(
|
|
4134
|
+
await mutateSessionYaml(paths, sessionId, (s) => {
|
|
4076
4135
|
s.session.status = "running";
|
|
4077
4136
|
});
|
|
4078
4137
|
} finally {
|
|
@@ -4192,18 +4251,6 @@ function decideFinalStatus(result, signalReceived) {
|
|
|
4192
4251
|
if (result.exit_code === 0) return "completed";
|
|
4193
4252
|
return "failed";
|
|
4194
4253
|
}
|
|
4195
|
-
var SIGNUM_MAP = {
|
|
4196
|
-
SIGHUP: 1,
|
|
4197
|
-
SIGINT: 2,
|
|
4198
|
-
SIGQUIT: 3,
|
|
4199
|
-
SIGKILL: 9,
|
|
4200
|
-
SIGTERM: 15
|
|
4201
|
-
};
|
|
4202
|
-
function signalToExitCode(sig) {
|
|
4203
|
-
if (sig === null) return 1;
|
|
4204
|
-
const num = SIGNUM_MAP[sig] ?? 1;
|
|
4205
|
-
return 128 + num;
|
|
4206
|
-
}
|
|
4207
4254
|
async function tryAppendGitSnapshot(sessionDir, sessionId, repoRoot, now, appendEvent) {
|
|
4208
4255
|
let snapshot;
|
|
4209
4256
|
try {
|
|
@@ -4261,7 +4308,14 @@ function buildInitialSession(input) {
|
|
|
4261
4308
|
}
|
|
4262
4309
|
};
|
|
4263
4310
|
}
|
|
4264
|
-
async function mutateSessionYaml(
|
|
4311
|
+
async function mutateSessionYaml(paths, sessionId, mutator) {
|
|
4312
|
+
const kind = await lstatSessionFile(paths, sessionId, "session.yaml").catch((error) => {
|
|
4313
|
+
throw new Error("Failed to read YAML file", { cause: error });
|
|
4314
|
+
});
|
|
4315
|
+
if (kind === "symlink" || kind === "not_a_file") {
|
|
4316
|
+
throw new SessionFileNotAFileError3(sessionId, "session.yaml", kind);
|
|
4317
|
+
}
|
|
4318
|
+
const filePath = join7(paths.sessions, sessionId, "session.yaml");
|
|
4265
4319
|
const raw = await readYamlFile2(filePath);
|
|
4266
4320
|
const parsed = SessionSchema.parse(raw);
|
|
4267
4321
|
mutator(parsed);
|
|
@@ -4315,7 +4369,7 @@ async function finalizeSessionAsFailed(paths, sessionDir, sessionId, appendEvent
|
|
|
4315
4369
|
import {
|
|
4316
4370
|
assertBasouRootSafe as assertBasouRootSafe7,
|
|
4317
4371
|
basouPaths as basouPaths9,
|
|
4318
|
-
findErrorCode as
|
|
4372
|
+
findErrorCode as findErrorCode7,
|
|
4319
4373
|
readMarkdownFile as readMarkdownFile5,
|
|
4320
4374
|
renderHandoff as renderHandoff2,
|
|
4321
4375
|
renderWithMarkers as renderWithMarkers3,
|
|
@@ -4373,7 +4427,7 @@ async function assertWorkspaceInitialized6(basouRoot) {
|
|
|
4373
4427
|
try {
|
|
4374
4428
|
await assertBasouRootSafe7(basouRoot);
|
|
4375
4429
|
} catch (error) {
|
|
4376
|
-
if (
|
|
4430
|
+
if (findErrorCode7(error, "ENOENT")) {
|
|
4377
4431
|
throw new Error("Workspace not initialized. Run 'basou init' first.");
|
|
4378
4432
|
}
|
|
4379
4433
|
throw error;
|
|
@@ -4384,7 +4438,7 @@ async function assertWorkspaceInitialized6(basouRoot) {
|
|
|
4384
4438
|
import { execFile } from "child_process";
|
|
4385
4439
|
import { open as open2, readFile as readFile4, realpath as realpath4, stat as stat4 } from "fs/promises";
|
|
4386
4440
|
import { homedir as homedir7 } from "os";
|
|
4387
|
-
import { join as
|
|
4441
|
+
import { join as join10 } from "path";
|
|
4388
4442
|
import { fileURLToPath } from "url";
|
|
4389
4443
|
import { promisify } from "util";
|
|
4390
4444
|
import {
|
|
@@ -4545,15 +4599,15 @@ function describeCodexHookTrust(trust) {
|
|
|
4545
4599
|
|
|
4546
4600
|
// src/lib/protocols-config.ts
|
|
4547
4601
|
import { homedir as homedir5 } from "os";
|
|
4548
|
-
import { isAbsolute, join as
|
|
4602
|
+
import { isAbsolute, join as join8, resolve as resolve5 } from "path";
|
|
4549
4603
|
import { readYamlFile as readYamlFile3 } from "@basou/core";
|
|
4550
|
-
var DEFAULT_PROTOCOLS_CONFIG_PATH =
|
|
4551
|
-
var DEFAULT_TARGET_PATH =
|
|
4604
|
+
var DEFAULT_PROTOCOLS_CONFIG_PATH = join8(homedir5(), ".basou", "protocols.yaml");
|
|
4605
|
+
var DEFAULT_TARGET_PATH = join8(homedir5(), ".claude", "CLAUDE.md");
|
|
4552
4606
|
var ALLOWED_TOP_KEYS = /* @__PURE__ */ new Set(["version", "protocols"]);
|
|
4553
4607
|
var ALLOWED_ENTRY_KEYS = /* @__PURE__ */ new Set(["source", "title"]);
|
|
4554
4608
|
function expandTilde(p) {
|
|
4555
4609
|
if (p === "~") return homedir5();
|
|
4556
|
-
if (p.startsWith("~/")) return
|
|
4610
|
+
if (p.startsWith("~/")) return join8(homedir5(), p.slice(2));
|
|
4557
4611
|
return p;
|
|
4558
4612
|
}
|
|
4559
4613
|
function isRecord(value) {
|
|
@@ -4624,19 +4678,19 @@ async function loadProtocolsConfig(configPath = DEFAULT_PROTOCOLS_CONFIG_PATH) {
|
|
|
4624
4678
|
import {
|
|
4625
4679
|
assertBasouRootSafe as assertBasouRootSafe8,
|
|
4626
4680
|
basouPaths as basouPaths10,
|
|
4627
|
-
findErrorCode as
|
|
4681
|
+
findErrorCode as findErrorCode8,
|
|
4628
4682
|
renderOrientation as renderOrientation2,
|
|
4629
4683
|
writeMarkdownFile as writeMarkdownFile4
|
|
4630
4684
|
} from "@basou/core";
|
|
4631
4685
|
|
|
4632
4686
|
// src/lib/hosts-config.ts
|
|
4633
4687
|
import { homedir as homedir6 } from "os";
|
|
4634
|
-
import { isAbsolute as isAbsolute2, join as
|
|
4688
|
+
import { isAbsolute as isAbsolute2, join as join9, resolve as resolve6 } from "path";
|
|
4635
4689
|
import { readYamlFile as readYamlFile4 } from "@basou/core";
|
|
4636
|
-
var DEFAULT_HOSTS_CONFIG_PATH =
|
|
4690
|
+
var DEFAULT_HOSTS_CONFIG_PATH = join9(homedir6(), ".basou", "hosts.yaml");
|
|
4637
4691
|
function expandTilde2(p) {
|
|
4638
4692
|
if (p === "~") return homedir6();
|
|
4639
|
-
if (p.startsWith("~/")) return
|
|
4693
|
+
if (p.startsWith("~/")) return join9(homedir6(), p.slice(2));
|
|
4640
4694
|
return p;
|
|
4641
4695
|
}
|
|
4642
4696
|
function isRecord2(value) {
|
|
@@ -4781,7 +4835,7 @@ async function assertWorkspaceInitialized7(basouRoot) {
|
|
|
4781
4835
|
try {
|
|
4782
4836
|
await assertBasouRootSafe8(basouRoot);
|
|
4783
4837
|
} catch (error) {
|
|
4784
|
-
if (
|
|
4838
|
+
if (findErrorCode8(error, "ENOENT")) {
|
|
4785
4839
|
throw new Error("Workspace not initialized. Run 'basou init' first.");
|
|
4786
4840
|
}
|
|
4787
4841
|
throw error;
|
|
@@ -5220,7 +5274,7 @@ function parseMinEdits(raw) {
|
|
|
5220
5274
|
if (raw === void 0 || !/^\d+$/.test(raw)) return void 0;
|
|
5221
5275
|
return Number(raw);
|
|
5222
5276
|
}
|
|
5223
|
-
var DEFAULT_CLAUDE_SETTINGS_PATH =
|
|
5277
|
+
var DEFAULT_CLAUDE_SETTINGS_PATH = join10(homedir7(), ".claude", "settings.json");
|
|
5224
5278
|
function resolveCliEntry() {
|
|
5225
5279
|
return fileURLToPath(import.meta.url);
|
|
5226
5280
|
}
|
|
@@ -5577,7 +5631,7 @@ function describeHookMode(tiers) {
|
|
|
5577
5631
|
const gates = tiers.review ? "capture + review" : "capture";
|
|
5578
5632
|
return `${enforcement}, ${gates}`;
|
|
5579
5633
|
}
|
|
5580
|
-
var DEFAULT_CODEX_FACE_PATH =
|
|
5634
|
+
var DEFAULT_CODEX_FACE_PATH = join10(homedir7(), ".codex", "AGENTS.md");
|
|
5581
5635
|
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.`;
|
|
5582
5636
|
async function faceHasLeftoverOrientationBlock(facePath) {
|
|
5583
5637
|
try {
|
|
@@ -5589,8 +5643,8 @@ async function faceHasLeftoverOrientationBlock(facePath) {
|
|
|
5589
5643
|
return false;
|
|
5590
5644
|
}
|
|
5591
5645
|
}
|
|
5592
|
-
var DEFAULT_CODEX_HOOKS_PATH =
|
|
5593
|
-
var DEFAULT_CODEX_CONFIG_PATH =
|
|
5646
|
+
var DEFAULT_CODEX_HOOKS_PATH = join10(homedir7(), ".codex", "hooks.json");
|
|
5647
|
+
var DEFAULT_CODEX_CONFIG_PATH = join10(homedir7(), ".codex", "config.toml");
|
|
5594
5648
|
async function readHooksFile(path) {
|
|
5595
5649
|
let raw;
|
|
5596
5650
|
try {
|
|
@@ -5836,7 +5890,6 @@ function renderGitignoreWarning(error, verbose) {
|
|
|
5836
5890
|
}
|
|
5837
5891
|
|
|
5838
5892
|
// src/commands/note.ts
|
|
5839
|
-
import { readFile as readFile5 } from "fs/promises";
|
|
5840
5893
|
import { homedir as homedir8 } from "os";
|
|
5841
5894
|
import { resolve as resolve8 } from "path";
|
|
5842
5895
|
import {
|
|
@@ -5846,7 +5899,7 @@ import {
|
|
|
5846
5899
|
basouPaths as basouPaths12,
|
|
5847
5900
|
createAdHocSessionWithEvent as createAdHocSessionWithEvent2,
|
|
5848
5901
|
EVENT_SCHEMA_VERSION as EVENT_SCHEMA_VERSION4,
|
|
5849
|
-
findErrorCode as
|
|
5902
|
+
findErrorCode as findErrorCode9,
|
|
5850
5903
|
LOCAL_CLI_EVENT_SOURCE as LOCAL_CLI_EVENT_SOURCE3,
|
|
5851
5904
|
readManifest as readManifest8,
|
|
5852
5905
|
resolveSessionId as resolveSessionId2,
|
|
@@ -6007,22 +6060,12 @@ async function readNoteBody(bodyArgument, options, ctx, cwd) {
|
|
|
6007
6060
|
if (bodyArgument !== void 0 && options.file !== void 0) {
|
|
6008
6061
|
throw new Error("Pass the note text either as an argument or with --file, not both.");
|
|
6009
6062
|
}
|
|
6063
|
+
refuseDashInputFile("--file", options.file, "stdin");
|
|
6010
6064
|
if (bodyArgument !== void 0) {
|
|
6011
6065
|
return { body: bodyArgument, source: "argument" };
|
|
6012
6066
|
}
|
|
6013
6067
|
if (options.file !== void 0) {
|
|
6014
|
-
|
|
6015
|
-
try {
|
|
6016
|
-
raw = await readFile5(resolve8(cwd, options.file), "utf8");
|
|
6017
|
-
} catch (error) {
|
|
6018
|
-
if (findErrorCode8(error, "ENOENT")) {
|
|
6019
|
-
throw new Error("--file names a file that does not exist", { cause: error });
|
|
6020
|
-
}
|
|
6021
|
-
if (findErrorCode8(error, "EISDIR")) {
|
|
6022
|
-
throw new Error("--file names a directory, not a file", { cause: error });
|
|
6023
|
-
}
|
|
6024
|
-
throw new Error("Could not read the file --file names", { cause: error });
|
|
6025
|
-
}
|
|
6068
|
+
const raw = await readInputFileText("--file", resolve8(cwd, options.file));
|
|
6026
6069
|
return { body: dropTrailingNewlines(raw), source: "file" };
|
|
6027
6070
|
}
|
|
6028
6071
|
if (ctx.readInput !== void 0) {
|
|
@@ -6091,7 +6134,7 @@ async function assertWorkspaceInitialized8(basouRoot) {
|
|
|
6091
6134
|
try {
|
|
6092
6135
|
await assertBasouRootSafe9(basouRoot);
|
|
6093
6136
|
} catch (error) {
|
|
6094
|
-
if (
|
|
6137
|
+
if (findErrorCode9(error, "ENOENT")) {
|
|
6095
6138
|
throw new Error("Workspace not initialized. Run 'basou init' first.");
|
|
6096
6139
|
}
|
|
6097
6140
|
throw error;
|
|
@@ -6208,7 +6251,7 @@ import {
|
|
|
6208
6251
|
writeFileSync,
|
|
6209
6252
|
writeSync
|
|
6210
6253
|
} from "fs";
|
|
6211
|
-
import { basename as basename7, dirname as dirname7, isAbsolute as isAbsolute3, join as
|
|
6254
|
+
import { basename as basename7, dirname as dirname7, isAbsolute as isAbsolute3, join as join11, relative as relative3, resolve as resolve9 } from "path";
|
|
6212
6255
|
import {
|
|
6213
6256
|
appendBasouGitignore as appendBasouGitignore2,
|
|
6214
6257
|
basouPaths as basouPaths13,
|
|
@@ -6589,7 +6632,7 @@ function classifySourceRoot(repositoryRoot, declaredPath) {
|
|
|
6589
6632
|
} catch {
|
|
6590
6633
|
return { path: declaredPath, kind: "unresolved" };
|
|
6591
6634
|
}
|
|
6592
|
-
return { path: declaredPath, kind: existsSync(
|
|
6635
|
+
return { path: declaredPath, kind: existsSync(join11(real, ".git")) ? "repo" : "non-repo" };
|
|
6593
6636
|
}
|
|
6594
6637
|
async function doRunProjectAdopt(options, ctx) {
|
|
6595
6638
|
const cwd = ctx.cwd ?? process.cwd();
|
|
@@ -6693,7 +6736,7 @@ async function gatherRepoWiring(repositoryRoot, entry) {
|
|
|
6693
6736
|
} catch {
|
|
6694
6737
|
return { ...base, reachable: false, instructionFiles: [] };
|
|
6695
6738
|
}
|
|
6696
|
-
if (!existsSync(
|
|
6739
|
+
if (!existsSync(join11(real, ".git"))) {
|
|
6697
6740
|
return { ...base, reachable: false, instructionFiles: [] };
|
|
6698
6741
|
}
|
|
6699
6742
|
try {
|
|
@@ -6701,7 +6744,7 @@ async function gatherRepoWiring(repositoryRoot, entry) {
|
|
|
6701
6744
|
for (const name of INSTRUCTION_FILES) {
|
|
6702
6745
|
let present = true;
|
|
6703
6746
|
try {
|
|
6704
|
-
lstatSync(
|
|
6747
|
+
lstatSync(join11(real, name));
|
|
6705
6748
|
} catch {
|
|
6706
6749
|
present = false;
|
|
6707
6750
|
}
|
|
@@ -6812,10 +6855,10 @@ function gatherRepoGitignore(repositoryRoot, entry) {
|
|
|
6812
6855
|
} catch {
|
|
6813
6856
|
return { ...base, reachable: false, currentLines: [] };
|
|
6814
6857
|
}
|
|
6815
|
-
if (!existsSync(
|
|
6858
|
+
if (!existsSync(join11(real, ".git"))) {
|
|
6816
6859
|
return { ...base, reachable: false, currentLines: [] };
|
|
6817
6860
|
}
|
|
6818
|
-
return { ...base, reachable: true, currentLines: readGitignoreLines(
|
|
6861
|
+
return { ...base, reachable: true, currentLines: readGitignoreLines(join11(real, ".gitignore")) };
|
|
6819
6862
|
}
|
|
6820
6863
|
function hasErrorCode(error) {
|
|
6821
6864
|
return error instanceof Error && typeof error.code === "string";
|
|
@@ -6829,7 +6872,7 @@ function readGitignoreLines(file) {
|
|
|
6829
6872
|
}
|
|
6830
6873
|
}
|
|
6831
6874
|
function applyGitignorePlan(repositoryRoot, plan) {
|
|
6832
|
-
const file =
|
|
6875
|
+
const file = join11(realpathSync(resolve9(repositoryRoot, plan.path)), ".gitignore");
|
|
6833
6876
|
let existing = "";
|
|
6834
6877
|
try {
|
|
6835
6878
|
existing = readFileSync(file, "utf8");
|
|
@@ -6969,7 +7012,7 @@ function gatherRepoSymlinks(repositoryRoot, anchorReal, entry) {
|
|
|
6969
7012
|
return { ...base, isAnchor: false, reachable: false, canonicalPresent: false, files: [] };
|
|
6970
7013
|
}
|
|
6971
7014
|
if (real === anchorReal) {
|
|
6972
|
-
const anchorCanonical =
|
|
7015
|
+
const anchorCanonical = join11(real, CANONICAL_FILE);
|
|
6973
7016
|
const anchorState = anchorCanonicalState(anchorCanonical);
|
|
6974
7017
|
if (anchorState === "absent") {
|
|
6975
7018
|
return { ...base, isAnchor: true, reachable: true, canonicalPresent: false, files: [] };
|
|
@@ -6989,7 +7032,7 @@ function gatherRepoSymlinks(repositoryRoot, anchorReal, entry) {
|
|
|
6989
7032
|
anchorCanonical,
|
|
6990
7033
|
"self"
|
|
6991
7034
|
).map((spec) => {
|
|
6992
|
-
const { state, actualTarget } = inspectSymlink(
|
|
7035
|
+
const { state, actualTarget } = inspectSymlink(join11(real, spec.name), spec.target);
|
|
6993
7036
|
return {
|
|
6994
7037
|
name: spec.name,
|
|
6995
7038
|
expectedTarget: spec.target,
|
|
@@ -7006,16 +7049,16 @@ function gatherRepoSymlinks(repositoryRoot, anchorReal, entry) {
|
|
|
7006
7049
|
files: anchorFiles
|
|
7007
7050
|
};
|
|
7008
7051
|
}
|
|
7009
|
-
if (!existsSync(
|
|
7052
|
+
if (!existsSync(join11(real, ".git"))) {
|
|
7010
7053
|
return { ...base, isAnchor: false, reachable: false, canonicalPresent: false, files: [] };
|
|
7011
7054
|
}
|
|
7012
|
-
const canonicalFile = isSelf ?
|
|
7055
|
+
const canonicalFile = isSelf ? join11(real, CANONICAL_FILE) : join11(anchorReal, "agents", basename7(real), CANONICAL_FILE);
|
|
7013
7056
|
if (!existsSync(canonicalFile)) {
|
|
7014
7057
|
return { ...base, isAnchor: false, reachable: true, canonicalPresent: false, files: [] };
|
|
7015
7058
|
}
|
|
7016
7059
|
const files = expectedSymlinkTargets(real, canonicalFile, mode).map(
|
|
7017
7060
|
(spec) => {
|
|
7018
|
-
const { state, actualTarget } = inspectSymlink(
|
|
7061
|
+
const { state, actualTarget } = inspectSymlink(join11(real, spec.name), spec.target);
|
|
7019
7062
|
return {
|
|
7020
7063
|
name: spec.name,
|
|
7021
7064
|
expectedTarget: spec.target,
|
|
@@ -7044,7 +7087,7 @@ function applySymlinkPlan(repositoryRoot, plan) {
|
|
|
7044
7087
|
const created = [];
|
|
7045
7088
|
const failed = [];
|
|
7046
7089
|
for (const { name, target } of plan.toCreate) {
|
|
7047
|
-
const filePath =
|
|
7090
|
+
const filePath = join11(real, name);
|
|
7048
7091
|
try {
|
|
7049
7092
|
mkdirSync(dirname7(filePath), { recursive: true });
|
|
7050
7093
|
symlinkSync(target, filePath);
|
|
@@ -7078,7 +7121,7 @@ function gatherViewSymlinks(repositoryRoot, anchorReal, roster, viewDir) {
|
|
|
7078
7121
|
if (!existsSync(canonicalFile)) return { kind: "missing-canonical", viewName };
|
|
7079
7122
|
const files = expectedSymlinkTargets(viewDir, canonicalFile, "hub").map(
|
|
7080
7123
|
(spec) => {
|
|
7081
|
-
const { state, actualTarget } = inspectSymlink(
|
|
7124
|
+
const { state, actualTarget } = inspectSymlink(join11(viewDir, spec.name), spec.target);
|
|
7082
7125
|
return {
|
|
7083
7126
|
name: spec.name,
|
|
7084
7127
|
expectedTarget: spec.target,
|
|
@@ -7094,7 +7137,7 @@ function applyViewSymlinks(viewDir, files) {
|
|
|
7094
7137
|
const failed = [];
|
|
7095
7138
|
for (const f of files) {
|
|
7096
7139
|
if (f.state !== "missing") continue;
|
|
7097
|
-
const filePath =
|
|
7140
|
+
const filePath = join11(viewDir, f.name);
|
|
7098
7141
|
try {
|
|
7099
7142
|
mkdirSync(dirname7(filePath), { recursive: true });
|
|
7100
7143
|
symlinkSync(f.expectedTarget, filePath);
|
|
@@ -7316,7 +7359,7 @@ function resolveViewDir(repositoryRoot, viewPath) {
|
|
|
7316
7359
|
return realpathSync(abs);
|
|
7317
7360
|
} catch {
|
|
7318
7361
|
try {
|
|
7319
|
-
return
|
|
7362
|
+
return join11(realpathSync(dirname7(abs)), basename7(abs));
|
|
7320
7363
|
} catch {
|
|
7321
7364
|
return abs;
|
|
7322
7365
|
}
|
|
@@ -7334,7 +7377,7 @@ function gatherViewRepo(repositoryRoot, viewDir, entry) {
|
|
|
7334
7377
|
return { path: entry.path, reachable: false };
|
|
7335
7378
|
}
|
|
7336
7379
|
const linkName = basename7(repoReal);
|
|
7337
|
-
const { state, actualTarget } = inspectSymlink(
|
|
7380
|
+
const { state, actualTarget } = inspectSymlink(join11(viewDir, linkName), expectedTarget);
|
|
7338
7381
|
return {
|
|
7339
7382
|
path: entry.path,
|
|
7340
7383
|
reachable: true,
|
|
@@ -7348,7 +7391,7 @@ function applyViewPlan(viewDir, toCreate) {
|
|
|
7348
7391
|
const created = [];
|
|
7349
7392
|
const failed = [];
|
|
7350
7393
|
for (const { name, target } of toCreate) {
|
|
7351
|
-
const filePath =
|
|
7394
|
+
const filePath = join11(viewDir, name);
|
|
7352
7395
|
try {
|
|
7353
7396
|
mkdirSync(dirname7(filePath), { recursive: true });
|
|
7354
7397
|
symlinkSync(target, filePath);
|
|
@@ -7363,7 +7406,7 @@ var TOP_LEVEL_INSTRUCTION_FILES_LOWER = new Set(
|
|
|
7363
7406
|
INSTRUCTION_FILES.filter((f) => !f.includes("/")).map((f) => f.toLowerCase())
|
|
7364
7407
|
);
|
|
7365
7408
|
function classifyViewLink(viewDir, name, rosterRealpaths) {
|
|
7366
|
-
const filePath =
|
|
7409
|
+
const filePath = join11(viewDir, name);
|
|
7367
7410
|
let isLink;
|
|
7368
7411
|
try {
|
|
7369
7412
|
isLink = lstatSync(filePath).isSymbolicLink();
|
|
@@ -7392,7 +7435,7 @@ function classifyViewLink(viewDir, name, rosterRealpaths) {
|
|
|
7392
7435
|
if (!isDir) {
|
|
7393
7436
|
return { target, kind: existsSync(resolved) ? "non-repo" : "broken" };
|
|
7394
7437
|
}
|
|
7395
|
-
return { target, kind: existsSync(
|
|
7438
|
+
return { target, kind: existsSync(join11(resolved, ".git")) ? "repo" : "non-repo" };
|
|
7396
7439
|
}
|
|
7397
7440
|
function gatherExistingViewLinks(viewDir, rosterRealpaths) {
|
|
7398
7441
|
let names;
|
|
@@ -7417,7 +7460,7 @@ function pruneViewLinks(viewDir, toPrune, rosterRealpaths) {
|
|
|
7417
7460
|
const pruned = [];
|
|
7418
7461
|
const failed = [];
|
|
7419
7462
|
for (const { name } of toPrune) {
|
|
7420
|
-
const filePath =
|
|
7463
|
+
const filePath = join11(viewDir, name);
|
|
7421
7464
|
const c = classifyViewLink(viewDir, name, rosterRealpaths);
|
|
7422
7465
|
if (c === null || c.kind !== "repo") {
|
|
7423
7466
|
failed.push({
|
|
@@ -7630,10 +7673,10 @@ async function runProjectPreset(options, ctx = {}) {
|
|
|
7630
7673
|
}
|
|
7631
7674
|
}
|
|
7632
7675
|
function canonicalFileFor(anchorReal, canonicalName) {
|
|
7633
|
-
return
|
|
7676
|
+
return join11(anchorReal, "agents", canonicalName, CANONICAL_FILE);
|
|
7634
7677
|
}
|
|
7635
7678
|
function canonicalLabelFor(canonicalName) {
|
|
7636
|
-
return
|
|
7679
|
+
return join11("agents", canonicalName, CANONICAL_FILE);
|
|
7637
7680
|
}
|
|
7638
7681
|
async function gatherRepoPreset(repositoryRoot, anchorReal, entry) {
|
|
7639
7682
|
const declared = {
|
|
@@ -7654,7 +7697,7 @@ async function gatherRepoPreset(repositoryRoot, anchorReal, entry) {
|
|
|
7654
7697
|
if (real === anchorReal) {
|
|
7655
7698
|
return { ...declared, isAnchor: true, reachable: true, canonicalPresent: false };
|
|
7656
7699
|
}
|
|
7657
|
-
if (!existsSync(
|
|
7700
|
+
if (!existsSync(join11(real, ".git"))) {
|
|
7658
7701
|
return { ...declared, isAnchor: false, reachable: false, canonicalPresent: false };
|
|
7659
7702
|
}
|
|
7660
7703
|
const canonicalName = basename7(real);
|
|
@@ -8036,24 +8079,24 @@ function gatherArchiveTeardown(repositoryRoot, manifest, target) {
|
|
|
8036
8079
|
const instructionFiles = [];
|
|
8037
8080
|
for (const name of INSTRUCTION_FILES) {
|
|
8038
8081
|
try {
|
|
8039
|
-
lstatSync(
|
|
8082
|
+
lstatSync(join11(real, name));
|
|
8040
8083
|
instructionFiles.push(name);
|
|
8041
8084
|
} catch {
|
|
8042
8085
|
}
|
|
8043
8086
|
}
|
|
8044
8087
|
let ignored;
|
|
8045
8088
|
try {
|
|
8046
|
-
ignored = new Set(readGitignoreLines(
|
|
8089
|
+
ignored = new Set(readGitignoreLines(join11(real, ".gitignore")).map((l) => l.trim()));
|
|
8047
8090
|
} catch {
|
|
8048
8091
|
ignored = /* @__PURE__ */ new Set();
|
|
8049
8092
|
}
|
|
8050
8093
|
const gitignorePatterns = INSTRUCTION_FILES.filter((p) => ignored.has(p) || ignored.has(`/${p}`));
|
|
8051
|
-
const canonical2 = existsSync(
|
|
8094
|
+
const canonical2 = existsSync(join11(anchorReal, "agents", canonicalName, CANONICAL_FILE));
|
|
8052
8095
|
let viewLink = false;
|
|
8053
8096
|
const viewPath = manifest.workspace.view;
|
|
8054
8097
|
if (viewPath !== void 0) {
|
|
8055
8098
|
try {
|
|
8056
|
-
lstatSync(
|
|
8099
|
+
lstatSync(join11(resolveViewDir(repositoryRoot, viewPath), canonicalName));
|
|
8057
8100
|
viewLink = true;
|
|
8058
8101
|
} catch {
|
|
8059
8102
|
}
|
|
@@ -8067,11 +8110,11 @@ function gatherArchiveTeardown(repositoryRoot, manifest, target) {
|
|
|
8067
8110
|
};
|
|
8068
8111
|
}
|
|
8069
8112
|
function teardownExpectedTargets(repoReal, anchorReal, canonicalName) {
|
|
8070
|
-
const canonicalFile =
|
|
8113
|
+
const canonicalFile = join11(anchorReal, "agents", canonicalName, CANONICAL_FILE);
|
|
8071
8114
|
return expectedSymlinkTargets(repoReal, canonicalFile);
|
|
8072
8115
|
}
|
|
8073
8116
|
function viewLinkPointsAt(viewDir, name, repoReal) {
|
|
8074
|
-
const filePath =
|
|
8117
|
+
const filePath = join11(viewDir, name);
|
|
8075
8118
|
try {
|
|
8076
8119
|
if (!lstatSync(filePath).isSymbolicLink()) return false;
|
|
8077
8120
|
const target = readlinkSync(filePath);
|
|
@@ -8082,7 +8125,7 @@ function viewLinkPointsAt(viewDir, name, repoReal) {
|
|
|
8082
8125
|
}
|
|
8083
8126
|
}
|
|
8084
8127
|
function viewLinkPointsAtPath(viewDir, name, expectedRepoPath) {
|
|
8085
|
-
const filePath =
|
|
8128
|
+
const filePath = join11(viewDir, name);
|
|
8086
8129
|
try {
|
|
8087
8130
|
if (!lstatSync(filePath).isSymbolicLink()) return false;
|
|
8088
8131
|
const target = readlinkSync(filePath);
|
|
@@ -8133,7 +8176,7 @@ function gatherRepoTeardown(repositoryRoot, manifest, target) {
|
|
|
8133
8176
|
if (!isAnchor) {
|
|
8134
8177
|
if (repoReal !== void 0) {
|
|
8135
8178
|
for (const spec of teardownExpectedTargets(repoReal, anchorReal, canonicalName)) {
|
|
8136
|
-
const { state, actualTarget } = inspectSymlink(
|
|
8179
|
+
const { state, actualTarget } = inspectSymlink(join11(repoReal, spec.name), spec.target);
|
|
8137
8180
|
if (isSelf) {
|
|
8138
8181
|
if (state !== "missing")
|
|
8139
8182
|
items.push({
|
|
@@ -8170,7 +8213,7 @@ function gatherRepoTeardown(repositoryRoot, manifest, target) {
|
|
|
8170
8213
|
}
|
|
8171
8214
|
let ignored;
|
|
8172
8215
|
try {
|
|
8173
|
-
ignored = new Set(readGitignoreLines(
|
|
8216
|
+
ignored = new Set(readGitignoreLines(join11(repoReal, ".gitignore")).map((l) => l.trim()));
|
|
8174
8217
|
for (const p of INSTRUCTION_FILES) {
|
|
8175
8218
|
if (ignored.has(p) || ignored.has(`/${p}`)) {
|
|
8176
8219
|
items.push({
|
|
@@ -8193,7 +8236,7 @@ function gatherRepoTeardown(repositoryRoot, manifest, target) {
|
|
|
8193
8236
|
const viewPath = manifest.workspace.view;
|
|
8194
8237
|
if (viewPath !== void 0) {
|
|
8195
8238
|
const viewDir = resolveViewDir(repositoryRoot, viewPath);
|
|
8196
|
-
const linkPath =
|
|
8239
|
+
const linkPath = join11(viewDir, canonicalName);
|
|
8197
8240
|
let isLink = false;
|
|
8198
8241
|
try {
|
|
8199
8242
|
isLink = lstatSync(linkPath).isSymbolicLink();
|
|
@@ -8219,8 +8262,8 @@ function gatherRepoTeardown(repositoryRoot, manifest, target) {
|
|
|
8219
8262
|
else items.push({ kind: "view-symlink", label: canonicalName, state: "removable" });
|
|
8220
8263
|
}
|
|
8221
8264
|
}
|
|
8222
|
-
const canonicalFile =
|
|
8223
|
-
const canonicalLabel =
|
|
8265
|
+
const canonicalFile = join11(anchorReal, "agents", canonicalName, CANONICAL_FILE);
|
|
8266
|
+
const canonicalLabel = join11("agents", canonicalName, CANONICAL_FILE);
|
|
8224
8267
|
let canonicalIsLink = false;
|
|
8225
8268
|
try {
|
|
8226
8269
|
canonicalIsLink = lstatSync(canonicalFile).isSymbolicLink();
|
|
@@ -8324,12 +8367,12 @@ function applyRepoTeardown(repositoryRoot, manifest, plan) {
|
|
|
8324
8367
|
);
|
|
8325
8368
|
for (const item of removable.filter((i) => i.kind === "instruction-symlink")) {
|
|
8326
8369
|
const expected = expectedByName.get(item.label);
|
|
8327
|
-
if (repoReal === null || expected === void 0 || inspectSymlink(
|
|
8370
|
+
if (repoReal === null || expected === void 0 || inspectSymlink(join11(repoReal, item.label), expected).state !== "correct") {
|
|
8328
8371
|
changed(item.label);
|
|
8329
8372
|
continue;
|
|
8330
8373
|
}
|
|
8331
8374
|
try {
|
|
8332
|
-
unlinkSync(
|
|
8375
|
+
unlinkSync(join11(repoReal, item.label));
|
|
8333
8376
|
removed.push(item.label);
|
|
8334
8377
|
} catch (error) {
|
|
8335
8378
|
failed.push({ label: item.label, message: failureReason(error) });
|
|
@@ -8348,7 +8391,7 @@ function applyRepoTeardown(repositoryRoot, manifest, plan) {
|
|
|
8348
8391
|
continue;
|
|
8349
8392
|
}
|
|
8350
8393
|
try {
|
|
8351
|
-
unlinkSync(
|
|
8394
|
+
unlinkSync(join11(viewDir, item.label));
|
|
8352
8395
|
removed.push(`view/${item.label}`);
|
|
8353
8396
|
} catch (error) {
|
|
8354
8397
|
failed.push({ label: `view/${item.label}`, message: failureReason(error) });
|
|
@@ -8356,7 +8399,7 @@ function applyRepoTeardown(repositoryRoot, manifest, plan) {
|
|
|
8356
8399
|
}
|
|
8357
8400
|
const NOFOLLOW = fsConstants.O_NOFOLLOW ?? 0;
|
|
8358
8401
|
for (const item of removable.filter((i) => i.kind === "canonical-block")) {
|
|
8359
|
-
const canonicalFile =
|
|
8402
|
+
const canonicalFile = join11(anchorReal, "agents", canonicalName, CANONICAL_FILE);
|
|
8360
8403
|
try {
|
|
8361
8404
|
if (lstatSync(canonicalFile).isSymbolicLink()) {
|
|
8362
8405
|
changed(item.label);
|
|
@@ -8630,12 +8673,12 @@ function gatherRenameWiring(repositoryRoot, manifest, oldBasename) {
|
|
|
8630
8673
|
} catch {
|
|
8631
8674
|
return { canonicalDirOld: false, viewLinkOld: false };
|
|
8632
8675
|
}
|
|
8633
|
-
const canonicalDirOld = existsSync(
|
|
8676
|
+
const canonicalDirOld = existsSync(join11(anchorReal, "agents", oldBasename));
|
|
8634
8677
|
let viewLinkOld = false;
|
|
8635
8678
|
const viewPath = manifest.workspace.view;
|
|
8636
8679
|
if (viewPath !== void 0) {
|
|
8637
8680
|
try {
|
|
8638
|
-
lstatSync(
|
|
8681
|
+
lstatSync(join11(resolveViewDir(repositoryRoot, viewPath), oldBasename));
|
|
8639
8682
|
viewLinkOld = true;
|
|
8640
8683
|
} catch {
|
|
8641
8684
|
}
|
|
@@ -8980,7 +9023,7 @@ async function doRunProjectSeedAnchor(options, ctx) {
|
|
|
8980
9023
|
console.log("\u2139\uFE0F No repo roster declared \u2014 nothing to seed.");
|
|
8981
9024
|
return;
|
|
8982
9025
|
}
|
|
8983
|
-
const anchorDoc =
|
|
9026
|
+
const anchorDoc = join11(repositoryRoot, CANONICAL_FILE);
|
|
8984
9027
|
if (pathPresent(anchorDoc)) {
|
|
8985
9028
|
console.log(
|
|
8986
9029
|
`\u2705 The anchor's own \`${CANONICAL_FILE}\` already exists \u2014 hand-maintained, left untouched.`
|
|
@@ -9042,7 +9085,7 @@ function regularFileSpokes(repoReal) {
|
|
|
9042
9085
|
const out = [];
|
|
9043
9086
|
for (const spoke of ["CLAUDE.md", ".github/copilot-instructions.md"]) {
|
|
9044
9087
|
try {
|
|
9045
|
-
const st = lstatSync(
|
|
9088
|
+
const st = lstatSync(join11(repoReal, spoke));
|
|
9046
9089
|
if (!st.isSymbolicLink() && st.isFile()) out.push(spoke);
|
|
9047
9090
|
} catch {
|
|
9048
9091
|
}
|
|
@@ -9088,8 +9131,8 @@ function gatherRetrofit(repositoryRoot, anchorReal, roster, argAbs, argReal, vie
|
|
|
9088
9131
|
};
|
|
9089
9132
|
}
|
|
9090
9133
|
const isAnchor = argReal === anchorReal;
|
|
9091
|
-
const reachable = existsSync(
|
|
9092
|
-
const canonicalFile =
|
|
9134
|
+
const reachable = existsSync(join11(argReal, ".git"));
|
|
9135
|
+
const canonicalFile = join11(anchorReal, "agents", canonicalName, CANONICAL_FILE);
|
|
9093
9136
|
return {
|
|
9094
9137
|
path,
|
|
9095
9138
|
declared,
|
|
@@ -9098,13 +9141,13 @@ function gatherRetrofit(repositoryRoot, anchorReal, roster, argAbs, argReal, vie
|
|
|
9098
9141
|
reachable,
|
|
9099
9142
|
canonicalName,
|
|
9100
9143
|
...viewCanonicalName !== void 0 ? { viewCanonicalName } : {},
|
|
9101
|
-
agentsState: inspectAgentsState(
|
|
9144
|
+
agentsState: inspectAgentsState(join11(argReal, CANONICAL_FILE)),
|
|
9102
9145
|
canonicalExists: pathPresent(canonicalFile),
|
|
9103
9146
|
regularSpokes: regularFileSpokes(argReal)
|
|
9104
9147
|
};
|
|
9105
9148
|
}
|
|
9106
9149
|
function relocateAgentsFile(repoReal, canonicalFile) {
|
|
9107
|
-
const agentsFile =
|
|
9150
|
+
const agentsFile = join11(repoReal, CANONICAL_FILE);
|
|
9108
9151
|
try {
|
|
9109
9152
|
mkdirSync(dirname7(canonicalFile), { recursive: true });
|
|
9110
9153
|
} catch (error) {
|
|
@@ -9220,7 +9263,7 @@ async function doRunProjectRetrofit(repo, options, ctx) {
|
|
|
9220
9263
|
let failure;
|
|
9221
9264
|
let partial = false;
|
|
9222
9265
|
if (options.apply === true && plan.action === "relocate" && argReal !== void 0) {
|
|
9223
|
-
const canonicalFile =
|
|
9266
|
+
const canonicalFile = join11(anchorReal, "agents", plan.canonicalName, CANONICAL_FILE);
|
|
9224
9267
|
const res = relocateAgentsFile(argReal, canonicalFile);
|
|
9225
9268
|
if (res.ok) {
|
|
9226
9269
|
applied = true;
|
|
@@ -9420,7 +9463,7 @@ function renderProjectRetrofit(result) {
|
|
|
9420
9463
|
}
|
|
9421
9464
|
|
|
9422
9465
|
// src/commands/protocol.ts
|
|
9423
|
-
import { readFile as
|
|
9466
|
+
import { readFile as readFile5, stat as stat5 } from "fs/promises";
|
|
9424
9467
|
import {
|
|
9425
9468
|
carryForwardProtocolStamp,
|
|
9426
9469
|
PROTOCOL_END as PROTOCOL_END2,
|
|
@@ -9477,7 +9520,7 @@ async function readProtocolSources(entries) {
|
|
|
9477
9520
|
for (const entry of entries) {
|
|
9478
9521
|
let content;
|
|
9479
9522
|
try {
|
|
9480
|
-
content = await
|
|
9523
|
+
content = await readFile5(entry.source, "utf8");
|
|
9481
9524
|
} catch (error) {
|
|
9482
9525
|
if (error instanceof Error && error.code === "ENOENT") {
|
|
9483
9526
|
throw new Error(
|
|
@@ -9603,7 +9646,7 @@ async function doRunProtocolUnsync(options) {
|
|
|
9603
9646
|
import {
|
|
9604
9647
|
assertBasouRootSafe as assertBasouRootSafe10,
|
|
9605
9648
|
basouPaths as basouPaths14,
|
|
9606
|
-
findErrorCode as
|
|
9649
|
+
findErrorCode as findErrorCode11,
|
|
9607
9650
|
loadPortfolioConfig as loadPortfolioConfig5,
|
|
9608
9651
|
readManifest as readManifest10
|
|
9609
9652
|
} from "@basou/core";
|
|
@@ -9612,20 +9655,20 @@ import { InvalidArgumentError as InvalidArgumentError4 } from "commander";
|
|
|
9612
9655
|
// src/commands/refresh-watch.ts
|
|
9613
9656
|
import { readdir as readdir3, stat as stat6 } from "fs/promises";
|
|
9614
9657
|
import { homedir as homedir9 } from "os";
|
|
9615
|
-
import { join as
|
|
9658
|
+
import { join as join12 } from "path";
|
|
9616
9659
|
import {
|
|
9617
9660
|
assertApprovalStoreSafe as assertApprovalStoreSafe2,
|
|
9618
9661
|
assertLockStoreSafe as assertLockStoreSafe4,
|
|
9619
9662
|
assertTaskStoreSafe as assertTaskStoreSafe2,
|
|
9620
|
-
findErrorCode as
|
|
9663
|
+
findErrorCode as findErrorCode10
|
|
9621
9664
|
} from "@basou/core";
|
|
9622
9665
|
var DEFAULT_WATCH_INTERVAL_SEC = 30;
|
|
9623
9666
|
var MIN_WATCH_INTERVAL_SEC = 5;
|
|
9624
9667
|
var MAX_WATCH_INTERVAL_SEC = 86400;
|
|
9625
9668
|
function watchedRoots(ctx) {
|
|
9626
9669
|
return [
|
|
9627
|
-
ctx.codexSessionsDir ??
|
|
9628
|
-
ctx.claudeProjectsDir ??
|
|
9670
|
+
ctx.codexSessionsDir ?? join12(homedir9(), ".codex", "sessions"),
|
|
9671
|
+
ctx.claudeProjectsDir ?? join12(homedir9(), ".claude", "projects")
|
|
9629
9672
|
];
|
|
9630
9673
|
}
|
|
9631
9674
|
async function scanSourceLogs(roots) {
|
|
@@ -9635,11 +9678,11 @@ async function scanSourceLogs(roots) {
|
|
|
9635
9678
|
try {
|
|
9636
9679
|
entries = await readdir3(dir, { withFileTypes: true });
|
|
9637
9680
|
} catch (error) {
|
|
9638
|
-
if (
|
|
9681
|
+
if (findErrorCode10(error, "ENOENT") || findErrorCode10(error, "ENOTDIR")) return;
|
|
9639
9682
|
throw new Error("Failed to read a source log directory", { cause: error });
|
|
9640
9683
|
}
|
|
9641
9684
|
for (const entry of entries) {
|
|
9642
|
-
const full =
|
|
9685
|
+
const full = join12(dir, entry.name);
|
|
9643
9686
|
if (entry.isDirectory()) {
|
|
9644
9687
|
await walk(full);
|
|
9645
9688
|
} else if (entry.isFile() && entry.name.endsWith(".jsonl")) {
|
|
@@ -9647,7 +9690,7 @@ async function scanSourceLogs(roots) {
|
|
|
9647
9690
|
const info = await stat6(full);
|
|
9648
9691
|
out.set(full, { mtimeMs: info.mtimeMs, size: info.size });
|
|
9649
9692
|
} catch (error) {
|
|
9650
|
-
if (
|
|
9693
|
+
if (findErrorCode10(error, "ENOENT")) continue;
|
|
9651
9694
|
throw new Error("Failed to stat a source log file", { cause: error });
|
|
9652
9695
|
}
|
|
9653
9696
|
}
|
|
@@ -9981,7 +10024,7 @@ async function assertWorkspaceInitialized9(basouRoot) {
|
|
|
9981
10024
|
try {
|
|
9982
10025
|
await assertBasouRootSafe10(basouRoot);
|
|
9983
10026
|
} catch (error) {
|
|
9984
|
-
if (
|
|
10027
|
+
if (findErrorCode11(error, "ENOENT")) {
|
|
9985
10028
|
throw new Error("Workspace not initialized. Run 'basou init' first.");
|
|
9986
10029
|
}
|
|
9987
10030
|
throw error;
|
|
@@ -9993,7 +10036,7 @@ import { isAbsolute as isAbsolute4, resolve as resolve10 } from "path";
|
|
|
9993
10036
|
import {
|
|
9994
10037
|
assertBasouRootSafe as assertBasouRootSafe11,
|
|
9995
10038
|
basouPaths as basouPaths15,
|
|
9996
|
-
findErrorCode as
|
|
10039
|
+
findErrorCode as findErrorCode12,
|
|
9997
10040
|
renderReport,
|
|
9998
10041
|
writeMarkdownFile as writeMarkdownFile6
|
|
9999
10042
|
} from "@basou/core";
|
|
@@ -10045,7 +10088,7 @@ async function assertWorkspaceInitialized10(basouRoot) {
|
|
|
10045
10088
|
try {
|
|
10046
10089
|
await assertBasouRootSafe11(basouRoot);
|
|
10047
10090
|
} catch (error) {
|
|
10048
|
-
if (
|
|
10091
|
+
if (findErrorCode12(error, "ENOENT")) {
|
|
10049
10092
|
throw new Error("Workspace not initialized. Run 'basou init' first.");
|
|
10050
10093
|
}
|
|
10051
10094
|
throw error;
|
|
@@ -10053,7 +10096,6 @@ async function assertWorkspaceInitialized10(basouRoot) {
|
|
|
10053
10096
|
}
|
|
10054
10097
|
|
|
10055
10098
|
// src/commands/review.ts
|
|
10056
|
-
import { readFile as readFile7 } from "fs/promises";
|
|
10057
10099
|
import { homedir as homedir10 } from "os";
|
|
10058
10100
|
import { resolve as resolve11 } from "path";
|
|
10059
10101
|
import {
|
|
@@ -10062,7 +10104,7 @@ import {
|
|
|
10062
10104
|
buildReviewRecordedEvent,
|
|
10063
10105
|
buildReviewRecordLabel,
|
|
10064
10106
|
createAdHocSessionWithEvent as createAdHocSessionWithEvent3,
|
|
10065
|
-
findErrorCode as
|
|
10107
|
+
findErrorCode as findErrorCode13,
|
|
10066
10108
|
findUnbindableRepos,
|
|
10067
10109
|
parseReviewRecordInput,
|
|
10068
10110
|
readManifest as readManifest11,
|
|
@@ -10138,6 +10180,7 @@ async function runReviewRecord(options, ctx = {}) {
|
|
|
10138
10180
|
}
|
|
10139
10181
|
}
|
|
10140
10182
|
async function doRunReviewRecord(options, ctx) {
|
|
10183
|
+
refuseDashInputFile("--file", options.file, "stdin");
|
|
10141
10184
|
const cwd = ctx.cwd ?? process.cwd();
|
|
10142
10185
|
const repositoryRoot = await resolveBasouRootForCommand(cwd, "review record");
|
|
10143
10186
|
const paths = basouPaths16(repositoryRoot);
|
|
@@ -10196,14 +10239,7 @@ ${detail}
|
|
|
10196
10239
|
}
|
|
10197
10240
|
async function readReviewInput(options, ctx) {
|
|
10198
10241
|
if (options.file !== void 0) {
|
|
10199
|
-
|
|
10200
|
-
return await readFile7(options.file, "utf8");
|
|
10201
|
-
} catch (error) {
|
|
10202
|
-
if (findErrorCode12(error, "ENOENT")) {
|
|
10203
|
-
throw new Error(`Input file not found: ${options.file}`);
|
|
10204
|
-
}
|
|
10205
|
-
throw error;
|
|
10206
|
-
}
|
|
10242
|
+
return await readInputFileText("--file", options.file);
|
|
10207
10243
|
}
|
|
10208
10244
|
if (ctx.readInput !== void 0) {
|
|
10209
10245
|
return await ctx.readInput();
|
|
@@ -10278,7 +10314,7 @@ async function assertWorkspaceInitialized11(basouRoot) {
|
|
|
10278
10314
|
try {
|
|
10279
10315
|
await assertBasouRootSafe12(basouRoot);
|
|
10280
10316
|
} catch (error) {
|
|
10281
|
-
if (
|
|
10317
|
+
if (findErrorCode13(error, "ENOENT")) {
|
|
10282
10318
|
throw new Error("Workspace not initialized. Run 'basou init' first.");
|
|
10283
10319
|
}
|
|
10284
10320
|
throw error;
|
|
@@ -10490,9 +10526,9 @@ function unattachedLines(u) {
|
|
|
10490
10526
|
}
|
|
10491
10527
|
|
|
10492
10528
|
// src/commands/run.ts
|
|
10493
|
-
import { mkdir as mkdir3, readFile as
|
|
10529
|
+
import { mkdir as mkdir3, readFile as readFile6, rm as rm3 } from "fs/promises";
|
|
10494
10530
|
import { homedir as homedir11 } from "os";
|
|
10495
|
-
import { join as
|
|
10531
|
+
import { join as join13 } from "path";
|
|
10496
10532
|
import {
|
|
10497
10533
|
acquireLock as acquireLock5,
|
|
10498
10534
|
assertBasouRootSafe as assertBasouRootSafe13,
|
|
@@ -10507,6 +10543,7 @@ import {
|
|
|
10507
10543
|
findBasouSessionStartHook as findBasouSessionStartHook2,
|
|
10508
10544
|
getDiff,
|
|
10509
10545
|
getSnapshot as getSnapshot2,
|
|
10546
|
+
lstatSessionFile as lstatSessionFile2,
|
|
10510
10547
|
overwriteYamlFile as overwriteYamlFile2,
|
|
10511
10548
|
prefixedUlid as prefixedUlid4,
|
|
10512
10549
|
readManifest as readManifest12,
|
|
@@ -10514,6 +10551,7 @@ import {
|
|
|
10514
10551
|
resolveClaudeCodeCommand,
|
|
10515
10552
|
resolveCodexCommand,
|
|
10516
10553
|
SESSION_SCHEMA_VERSION as SESSION_SCHEMA_VERSION2,
|
|
10554
|
+
SessionFileNotAFileError as SessionFileNotAFileError4,
|
|
10517
10555
|
SessionSchema as SessionSchema2,
|
|
10518
10556
|
sanitizeRelatedFiles,
|
|
10519
10557
|
sanitizeWorkingDirectory as sanitizeWorkingDirectory2,
|
|
@@ -10573,12 +10611,12 @@ async function runTrackedTool(args, options, ctx, adapter) {
|
|
|
10573
10611
|
const manifest = await readManifest12(paths);
|
|
10574
10612
|
await assertSessionStoreSafe3(paths);
|
|
10575
10613
|
const sessionId = prefixedUlid4("ses");
|
|
10576
|
-
const sessionDir =
|
|
10614
|
+
const sessionDir = join13(paths.sessions, sessionId);
|
|
10577
10615
|
const appendEvent = ctx.appendEvent ?? (async (_sessionDir, event) => {
|
|
10578
10616
|
await coreAppendChainedEvent2(paths, sessionId, event);
|
|
10579
10617
|
});
|
|
10580
10618
|
const startedAt = now().toISOString();
|
|
10581
|
-
const sessionYamlPath =
|
|
10619
|
+
const sessionYamlPath = join13(sessionDir, "session.yaml");
|
|
10582
10620
|
const session = buildInitialSession2({
|
|
10583
10621
|
id: sessionId,
|
|
10584
10622
|
command,
|
|
@@ -10629,7 +10667,7 @@ async function runTrackedTool(args, options, ctx, adapter) {
|
|
|
10629
10667
|
});
|
|
10630
10668
|
const runningLock = await acquireLock5(paths, "session", sessionId);
|
|
10631
10669
|
try {
|
|
10632
|
-
await mutateSessionYaml2(
|
|
10670
|
+
await mutateSessionYaml2(paths, sessionId, (s) => {
|
|
10633
10671
|
s.session.status = "running";
|
|
10634
10672
|
});
|
|
10635
10673
|
} finally {
|
|
@@ -10761,7 +10799,7 @@ async function runTrackedTool(args, options, ctx, adapter) {
|
|
|
10761
10799
|
s.session.related_files = relatedFiles;
|
|
10762
10800
|
});
|
|
10763
10801
|
if (result.exit_code !== null) return result.exit_code;
|
|
10764
|
-
return
|
|
10802
|
+
return signalToExitCode(signalReceived ?? result.signal);
|
|
10765
10803
|
}
|
|
10766
10804
|
function decideFinalStatus2(result, signalReceived) {
|
|
10767
10805
|
if (signalReceived === "SIGINT" || signalReceived === "SIGTERM") return "interrupted";
|
|
@@ -10771,18 +10809,6 @@ function decideFinalStatus2(result, signalReceived) {
|
|
|
10771
10809
|
if (result.exit_code === 0) return "completed";
|
|
10772
10810
|
return "failed";
|
|
10773
10811
|
}
|
|
10774
|
-
var SIGNUM_MAP2 = {
|
|
10775
|
-
SIGHUP: 1,
|
|
10776
|
-
SIGINT: 2,
|
|
10777
|
-
SIGQUIT: 3,
|
|
10778
|
-
SIGKILL: 9,
|
|
10779
|
-
SIGTERM: 15
|
|
10780
|
-
};
|
|
10781
|
-
function signalToExitCode2(sig) {
|
|
10782
|
-
if (sig === null) return 1;
|
|
10783
|
-
const num = SIGNUM_MAP2[sig] ?? 1;
|
|
10784
|
-
return 128 + num;
|
|
10785
|
-
}
|
|
10786
10812
|
async function tryAppendGitSnapshot2(sessionDir, sessionId, repoRoot, now, appendEvent) {
|
|
10787
10813
|
let snapshot;
|
|
10788
10814
|
try {
|
|
@@ -10887,7 +10913,14 @@ function buildInitialSession2(input) {
|
|
|
10887
10913
|
}
|
|
10888
10914
|
};
|
|
10889
10915
|
}
|
|
10890
|
-
async function mutateSessionYaml2(
|
|
10916
|
+
async function mutateSessionYaml2(paths, sessionId, mutator) {
|
|
10917
|
+
const kind = await lstatSessionFile2(paths, sessionId, "session.yaml").catch((error) => {
|
|
10918
|
+
throw new Error("Failed to read YAML file", { cause: error });
|
|
10919
|
+
});
|
|
10920
|
+
if (kind === "symlink" || kind === "not_a_file") {
|
|
10921
|
+
throw new SessionFileNotAFileError4(sessionId, "session.yaml", kind);
|
|
10922
|
+
}
|
|
10923
|
+
const filePath = join13(paths.sessions, sessionId, "session.yaml");
|
|
10891
10924
|
const raw = await readYamlFile5(filePath);
|
|
10892
10925
|
const parsed = SessionSchema2.parse(raw);
|
|
10893
10926
|
mutator(parsed);
|
|
@@ -10940,7 +10973,7 @@ async function noteCodexHookStatusPreSpawn(_cwd, ctx) {
|
|
|
10940
10973
|
const hooksPath = ctx.codexHooksPath ?? DEFAULT_CODEX_HOOKS_PATH;
|
|
10941
10974
|
let location;
|
|
10942
10975
|
try {
|
|
10943
|
-
location = findBasouSessionStartHook2(JSON.parse(await
|
|
10976
|
+
location = findBasouSessionStartHook2(JSON.parse(await readFile6(hooksPath, "utf8")));
|
|
10944
10977
|
} catch (error) {
|
|
10945
10978
|
if (!(error instanceof Error && error.code === "ENOENT")) return null;
|
|
10946
10979
|
location = null;
|
|
@@ -10954,8 +10987,8 @@ async function noteCodexHookStatusPreSpawn(_cwd, ctx) {
|
|
|
10954
10987
|
}
|
|
10955
10988
|
|
|
10956
10989
|
// src/commands/session.ts
|
|
10957
|
-
import { readFile as
|
|
10958
|
-
import { basename as basename8, isAbsolute as isAbsolute5, join as
|
|
10990
|
+
import { readFile as readFile7 } from "fs/promises";
|
|
10991
|
+
import { basename as basename8, isAbsolute as isAbsolute5, join as join14, relative as relative4, sep as sep3 } from "path";
|
|
10959
10992
|
import {
|
|
10960
10993
|
acquireLock as acquireLock6,
|
|
10961
10994
|
appendEventToExistingSession as appendEventToExistingSession3,
|
|
@@ -10965,10 +10998,11 @@ import {
|
|
|
10965
10998
|
displayPath as displayPath5,
|
|
10966
10999
|
EVENT_SCHEMA_VERSION as EVENT_SCHEMA_VERSION6,
|
|
10967
11000
|
enumerateSessionEntries as enumerateSessionEntries2,
|
|
10968
|
-
findErrorCode as
|
|
11001
|
+
findErrorCode as findErrorCode14,
|
|
10969
11002
|
importSessionFromJson as importSessionFromJson2,
|
|
10970
11003
|
LOCAL_CLI_EVENT_SOURCE as LOCAL_CLI_EVENT_SOURCE4,
|
|
10971
11004
|
loadSessionEntries as loadSessionEntries2,
|
|
11005
|
+
lstatSessionFile as lstatSessionFile3,
|
|
10972
11006
|
normalizeSessionTimestamps,
|
|
10973
11007
|
readAllEvents,
|
|
10974
11008
|
readManifest as readManifest13,
|
|
@@ -10978,6 +11012,7 @@ import {
|
|
|
10978
11012
|
resolveSessionId as resolveSessionId3,
|
|
10979
11013
|
resolveTaskId,
|
|
10980
11014
|
SESSION_IMPORT_SCHEMA_VERSION as SESSION_IMPORT_SCHEMA_VERSION2,
|
|
11015
|
+
SessionFileNotAFileError as SessionFileNotAFileError5,
|
|
10981
11016
|
SessionImportPayloadSchema as SessionImportPayloadSchema2,
|
|
10982
11017
|
SessionSchema as SessionSchema3,
|
|
10983
11018
|
SessionStatusSchema,
|
|
@@ -11087,20 +11122,28 @@ async function doRunSessionShow(idInput, options, ctx) {
|
|
|
11087
11122
|
const paths = basouPaths19(repositoryRoot);
|
|
11088
11123
|
await assertWorkspaceInitialized12(paths.root);
|
|
11089
11124
|
const sessionId = await resolveSessionId3(paths, idInput);
|
|
11090
|
-
const sessionDir =
|
|
11091
|
-
const sessionYamlPath =
|
|
11125
|
+
const sessionDir = join14(paths.sessions, sessionId);
|
|
11126
|
+
const sessionYamlPath = join14(sessionDir, "session.yaml");
|
|
11127
|
+
const yamlKind = await lstatSessionFile3(paths, sessionId, "session.yaml").catch(
|
|
11128
|
+
(error) => {
|
|
11129
|
+
throw new Error("Failed to read session", { cause: error });
|
|
11130
|
+
}
|
|
11131
|
+
);
|
|
11132
|
+
if (yamlKind === "symlink" || yamlKind === "not_a_file") {
|
|
11133
|
+
throw new SessionFileNotAFileError5(sessionId, "session.yaml", yamlKind);
|
|
11134
|
+
}
|
|
11092
11135
|
let session;
|
|
11093
11136
|
try {
|
|
11094
11137
|
const raw = await readYamlFile6(sessionYamlPath);
|
|
11095
11138
|
session = SessionSchema3.parse(normalizeSessionTimestamps(raw));
|
|
11096
11139
|
} catch (error) {
|
|
11097
|
-
if (
|
|
11140
|
+
if (findErrorCode14(error, "ENOENT")) {
|
|
11098
11141
|
throw new Error(`Session not found: ${idInput}`);
|
|
11099
11142
|
}
|
|
11100
11143
|
throw new Error("Failed to read session", { cause: error });
|
|
11101
11144
|
}
|
|
11102
11145
|
let eventsLostLines = 0;
|
|
11103
|
-
const events = await readAllEvents(
|
|
11146
|
+
const events = await readAllEvents(paths, sessionId, {
|
|
11104
11147
|
onWarning: (w) => {
|
|
11105
11148
|
if (w.kind === "malformed_json" || w.kind === "schema_violation") eventsLostLines++;
|
|
11106
11149
|
printReplayWarning(w, sessionId);
|
|
@@ -11358,7 +11401,7 @@ async function assertWorkspaceInitialized12(basouRoot) {
|
|
|
11358
11401
|
try {
|
|
11359
11402
|
await assertBasouRootSafe14(basouRoot);
|
|
11360
11403
|
} catch (error) {
|
|
11361
|
-
if (
|
|
11404
|
+
if (findErrorCode14(error, "ENOENT")) {
|
|
11362
11405
|
throw new Error("Workspace not initialized. Run 'basou init' first.");
|
|
11363
11406
|
}
|
|
11364
11407
|
throw error;
|
|
@@ -11394,6 +11437,7 @@ async function runSessionImport(options, ctx = {}) {
|
|
|
11394
11437
|
}
|
|
11395
11438
|
}
|
|
11396
11439
|
async function doRunSessionImport(options, ctx) {
|
|
11440
|
+
refuseDashInputFile("--from", options.from, { fileOnly: "the JSON" });
|
|
11397
11441
|
const cwd = ctx.cwd ?? process.cwd();
|
|
11398
11442
|
const repositoryRoot = await resolveRepositoryRootForSession(cwd, "import");
|
|
11399
11443
|
const paths = basouPaths19(repositoryRoot);
|
|
@@ -11427,12 +11471,12 @@ async function doRunSessionImport(options, ctx) {
|
|
|
11427
11471
|
}
|
|
11428
11472
|
async function readInputFile(path) {
|
|
11429
11473
|
try {
|
|
11430
|
-
return await
|
|
11474
|
+
return await readFile7(path, "utf8");
|
|
11431
11475
|
} catch (error) {
|
|
11432
|
-
if (
|
|
11476
|
+
if (findErrorCode14(error, "ENOENT")) {
|
|
11433
11477
|
throw new Error("Import source not found", { cause: error });
|
|
11434
11478
|
}
|
|
11435
|
-
if (
|
|
11479
|
+
if (findErrorCode14(error, "EISDIR")) {
|
|
11436
11480
|
throw new Error("Import source is not a file", { cause: error });
|
|
11437
11481
|
}
|
|
11438
11482
|
throw new Error("Failed to read import source", { cause: error });
|
|
@@ -11507,9 +11551,7 @@ async function doRunSessionNote(sessionIdInput, options, ctx) {
|
|
|
11507
11551
|
if (hasBody && hasFromFile) {
|
|
11508
11552
|
throw new Error("--body and --from-file are mutually exclusive");
|
|
11509
11553
|
}
|
|
11510
|
-
|
|
11511
|
-
throw new Error("--from-file - (stdin) is not supported in v0.1");
|
|
11512
|
-
}
|
|
11554
|
+
refuseDashInputFile("--from-file", options.fromFile, { textFlag: "--body" });
|
|
11513
11555
|
const cwd = ctx.cwd ?? process.cwd();
|
|
11514
11556
|
const repositoryRoot = await resolveRepositoryRootForSession(cwd, "note");
|
|
11515
11557
|
const paths = basouPaths19(repositoryRoot);
|
|
@@ -11544,12 +11586,12 @@ async function doRunSessionNote(sessionIdInput, options, ctx) {
|
|
|
11544
11586
|
}
|
|
11545
11587
|
async function readNoteFile(path) {
|
|
11546
11588
|
try {
|
|
11547
|
-
return await
|
|
11589
|
+
return await readFile7(path, "utf8");
|
|
11548
11590
|
} catch (error) {
|
|
11549
|
-
if (
|
|
11591
|
+
if (findErrorCode14(error, "ENOENT")) {
|
|
11550
11592
|
throw new Error("Note source not found", { cause: error });
|
|
11551
11593
|
}
|
|
11552
|
-
if (
|
|
11594
|
+
if (findErrorCode14(error, "EISDIR")) {
|
|
11553
11595
|
throw new Error("Note source is not a file", { cause: error });
|
|
11554
11596
|
}
|
|
11555
11597
|
throw new Error("Failed to read note source", { cause: error });
|
|
@@ -11619,7 +11661,7 @@ async function doRunSessionRechain(options, ctx) {
|
|
|
11619
11661
|
}
|
|
11620
11662
|
}
|
|
11621
11663
|
const tamperedCount = rows.filter(
|
|
11622
|
-
(r) => r.reason === "tampered" || r.reason === "symlink" || r.reason === "not_a_directory"
|
|
11664
|
+
(r) => r.reason === "tampered" || r.reason === "symlink" || r.reason === "not_a_directory" || r.reason === "events_not_a_file" || r.reason === "yaml_not_a_file"
|
|
11623
11665
|
).length;
|
|
11624
11666
|
const errorCount = rows.filter((r) => r.status === "error").length;
|
|
11625
11667
|
if (options.json === true) {
|
|
@@ -11647,6 +11689,10 @@ function renderRechainRow(row, dryRun) {
|
|
|
11647
11689
|
if (row.reason === "symlink" || row.reason === "not_a_directory") {
|
|
11648
11690
|
return `skipped (${row.reason === "symlink" ? "SYMLINK" : "NOT A DIRECTORY"} \u2014 not followed; inspect with 'basou verify')`;
|
|
11649
11691
|
}
|
|
11692
|
+
if (row.reason === "events_not_a_file" || row.reason === "yaml_not_a_file") {
|
|
11693
|
+
const file = row.reason === "events_not_a_file" ? "events.jsonl" : "session.yaml";
|
|
11694
|
+
return `skipped (${file} NOT A REGULAR FILE \u2014 not followed; inspect with 'basou verify')`;
|
|
11695
|
+
}
|
|
11650
11696
|
return `skipped (${row.reason})`;
|
|
11651
11697
|
case "error":
|
|
11652
11698
|
return `error (${row.message})`;
|
|
@@ -11662,7 +11708,7 @@ import {
|
|
|
11662
11708
|
assertBasouRootSafe as assertBasouRootSafe15,
|
|
11663
11709
|
basouPaths as basouPaths20,
|
|
11664
11710
|
computeWorkStats,
|
|
11665
|
-
findErrorCode as
|
|
11711
|
+
findErrorCode as findErrorCode15
|
|
11666
11712
|
} from "@basou/core";
|
|
11667
11713
|
function registerStatsCommand(program2) {
|
|
11668
11714
|
program2.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) => {
|
|
@@ -11771,7 +11817,7 @@ async function assertWorkspaceInitialized13(basouRoot) {
|
|
|
11771
11817
|
try {
|
|
11772
11818
|
await assertBasouRootSafe15(basouRoot);
|
|
11773
11819
|
} catch (error) {
|
|
11774
|
-
if (
|
|
11820
|
+
if (findErrorCode15(error, "ENOENT")) {
|
|
11775
11821
|
throw new Error("Workspace not initialized. Run 'basou init' first.");
|
|
11776
11822
|
}
|
|
11777
11823
|
throw error;
|
|
@@ -11783,7 +11829,7 @@ import {
|
|
|
11783
11829
|
assertBasouRootSafe as assertBasouRootSafe16,
|
|
11784
11830
|
basouPaths as basouPaths21,
|
|
11785
11831
|
buildStatusSnapshot,
|
|
11786
|
-
findErrorCode as
|
|
11832
|
+
findErrorCode as findErrorCode16,
|
|
11787
11833
|
readManifest as readManifest14,
|
|
11788
11834
|
writeStatus
|
|
11789
11835
|
} from "@basou/core";
|
|
@@ -11807,7 +11853,7 @@ async function doRunStatus(options, ctx) {
|
|
|
11807
11853
|
try {
|
|
11808
11854
|
await assertBasouRootSafe16(paths.root);
|
|
11809
11855
|
} catch (error) {
|
|
11810
|
-
if (
|
|
11856
|
+
if (findErrorCode16(error, "ENOENT")) {
|
|
11811
11857
|
throw new Error("Workspace not initialized. Run 'basou init' first.");
|
|
11812
11858
|
}
|
|
11813
11859
|
throw error;
|
|
@@ -11816,7 +11862,7 @@ async function doRunStatus(options, ctx) {
|
|
|
11816
11862
|
try {
|
|
11817
11863
|
manifest = await readManifest14(paths);
|
|
11818
11864
|
} catch (error) {
|
|
11819
|
-
if (
|
|
11865
|
+
if (findErrorCode16(error, "ENOENT")) {
|
|
11820
11866
|
throw new Error("Workspace not initialized. Run 'basou init' first.");
|
|
11821
11867
|
}
|
|
11822
11868
|
const gateMessage = formatVersionGateMessage(error);
|
|
@@ -11854,8 +11900,7 @@ function formatVersionGateMessage(error) {
|
|
|
11854
11900
|
}
|
|
11855
11901
|
|
|
11856
11902
|
// src/commands/task.ts
|
|
11857
|
-
import { readFile as
|
|
11858
|
-
import { join as join16 } from "path";
|
|
11903
|
+
import { readFile as readFile8 } from "fs/promises";
|
|
11859
11904
|
import {
|
|
11860
11905
|
archiveTask,
|
|
11861
11906
|
assertBasouRootSafe as assertBasouRootSafe17,
|
|
@@ -11865,7 +11910,7 @@ import {
|
|
|
11865
11910
|
deleteTask,
|
|
11866
11911
|
editTask,
|
|
11867
11912
|
enumerateArchivedTaskIds,
|
|
11868
|
-
findErrorCode as
|
|
11913
|
+
findErrorCode as findErrorCode17,
|
|
11869
11914
|
loadSessionEntries as loadSessionEntries3,
|
|
11870
11915
|
loadTaskEntries,
|
|
11871
11916
|
prefixedUlid as prefixedUlid5,
|
|
@@ -11952,9 +11997,7 @@ async function doRunTaskNew(options, ctx) {
|
|
|
11952
11997
|
if (options.description !== void 0 && options.fromFile !== void 0) {
|
|
11953
11998
|
throw new Error("--description and --from-file are mutually exclusive");
|
|
11954
11999
|
}
|
|
11955
|
-
|
|
11956
|
-
throw new Error("--from-file - (stdin) is not supported in v0.1");
|
|
11957
|
-
}
|
|
12000
|
+
refuseDashInputFile("--from-file", options.fromFile, { textFlag: "--description" });
|
|
11958
12001
|
const initialStatus = options.status ?? "planned";
|
|
11959
12002
|
if (options.completedAt !== void 0 && !isTerminalStatusForCli(initialStatus)) {
|
|
11960
12003
|
throw new Error("--completed-at requires --status done or cancelled");
|
|
@@ -12182,9 +12225,8 @@ async function doRunTaskShow(idInput, options, ctx) {
|
|
|
12182
12225
|
const events = [];
|
|
12183
12226
|
const linkedSessionIds = new Set(doc.task.task.linked_sessions);
|
|
12184
12227
|
for (const s of sessions) {
|
|
12185
|
-
const sessionDir = join16(paths.sessions, s.sessionId);
|
|
12186
12228
|
try {
|
|
12187
|
-
for await (const ev of replayEvents3(
|
|
12229
|
+
for await (const ev of replayEvents3(paths, s.sessionId, {
|
|
12188
12230
|
onWarning: (w) => printReplayWarning(w, s.sessionId)
|
|
12189
12231
|
})) {
|
|
12190
12232
|
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) {
|
|
@@ -12871,12 +12913,12 @@ function parsePositiveInt2(raw) {
|
|
|
12871
12913
|
}
|
|
12872
12914
|
async function readDescriptionFile(path) {
|
|
12873
12915
|
try {
|
|
12874
|
-
return await
|
|
12916
|
+
return await readFile8(path, "utf8");
|
|
12875
12917
|
} catch (error) {
|
|
12876
|
-
if (
|
|
12918
|
+
if (findErrorCode17(error, "ENOENT")) {
|
|
12877
12919
|
throw new Error("Description source not found", { cause: error });
|
|
12878
12920
|
}
|
|
12879
|
-
if (
|
|
12921
|
+
if (findErrorCode17(error, "EISDIR")) {
|
|
12880
12922
|
throw new Error("Description source is not a file", { cause: error });
|
|
12881
12923
|
}
|
|
12882
12924
|
throw new Error("Failed to read description source", { cause: error });
|
|
@@ -12889,7 +12931,7 @@ async function assertWorkspaceInitialized14(basouRoot) {
|
|
|
12889
12931
|
try {
|
|
12890
12932
|
await assertBasouRootSafe17(basouRoot);
|
|
12891
12933
|
} catch (error) {
|
|
12892
|
-
if (
|
|
12934
|
+
if (findErrorCode17(error, "ENOENT")) {
|
|
12893
12935
|
throw new Error("Workspace not initialized. Run 'basou init' first.");
|
|
12894
12936
|
}
|
|
12895
12937
|
throw error;
|
|
@@ -12980,7 +13022,7 @@ import {
|
|
|
12980
13022
|
assertBasouRootSafe as assertBasouRootSafe18,
|
|
12981
13023
|
basouPaths as basouPaths23,
|
|
12982
13024
|
enumerateSessionEntries as enumerateSessionEntries3,
|
|
12983
|
-
findErrorCode as
|
|
13025
|
+
findErrorCode as findErrorCode18,
|
|
12984
13026
|
resolveSessionId as resolveSessionId5,
|
|
12985
13027
|
verifyEventsChain
|
|
12986
13028
|
} from "@basou/core";
|
|
@@ -13067,7 +13109,7 @@ async function assertWorkspaceInitialized15(basouRoot) {
|
|
|
13067
13109
|
try {
|
|
13068
13110
|
await assertBasouRootSafe18(basouRoot);
|
|
13069
13111
|
} catch (error) {
|
|
13070
|
-
if (
|
|
13112
|
+
if (findErrorCode18(error, "ENOENT")) {
|
|
13071
13113
|
throw new Error("Workspace not initialized. Run 'basou init' first.");
|
|
13072
13114
|
}
|
|
13073
13115
|
throw error;
|
|
@@ -13077,11 +13119,11 @@ async function assertWorkspaceInitialized15(basouRoot) {
|
|
|
13077
13119
|
// src/commands/view.ts
|
|
13078
13120
|
import { spawn } from "child_process";
|
|
13079
13121
|
import { createHash as createHash2 } from "crypto";
|
|
13080
|
-
import { basename as basename11, dirname as dirname9, join as
|
|
13122
|
+
import { basename as basename11, dirname as dirname9, join as join17, resolve as resolve14 } from "path";
|
|
13081
13123
|
import {
|
|
13082
13124
|
assertBasouRootSafe as assertBasouRootSafe19,
|
|
13083
13125
|
basouPaths as basouPaths25,
|
|
13084
|
-
findErrorCode as
|
|
13126
|
+
findErrorCode as findErrorCode20,
|
|
13085
13127
|
loadPortfolioConfig as loadPortfolioConfig6,
|
|
13086
13128
|
readManifest as readManifest19
|
|
13087
13129
|
} from "@basou/core";
|
|
@@ -13091,7 +13133,7 @@ import { InvalidArgumentError as InvalidArgumentError8 } from "commander";
|
|
|
13091
13133
|
import { createReadStream as createReadStream2 } from "fs";
|
|
13092
13134
|
import { readdir as readdir4, stat as stat7 } from "fs/promises";
|
|
13093
13135
|
import { homedir as homedir12 } from "os";
|
|
13094
|
-
import { basename as basename9, dirname as dirname8, join as
|
|
13136
|
+
import { basename as basename9, dirname as dirname8, join as join15 } from "path";
|
|
13095
13137
|
import { createInterface as createInterface2 } from "readline";
|
|
13096
13138
|
import { basouPaths as basouPaths24, readManifest as readManifest16 } from "@basou/core";
|
|
13097
13139
|
function uncapturedTotal(result) {
|
|
@@ -13103,8 +13145,8 @@ async function checkPortfolioCoverage(workspaces, ctx = {}) {
|
|
|
13103
13145
|
ctx.portfolioConfigPath
|
|
13104
13146
|
);
|
|
13105
13147
|
const listedDirs = new Set([...roots].map((root) => encodeProjectDir(root)));
|
|
13106
|
-
const claudeProjectsDir = ctx.claudeProjectsDir ??
|
|
13107
|
-
const codexSessionsDir = ctx.codexSessionsDir ??
|
|
13148
|
+
const claudeProjectsDir = ctx.claudeProjectsDir ?? join15(homedir12(), ".claude", "projects");
|
|
13149
|
+
const codexSessionsDir = ctx.codexSessionsDir ?? join15(homedir12(), ".codex", "sessions");
|
|
13108
13150
|
const tally = new Tally(roots);
|
|
13109
13151
|
const absentTrees = [];
|
|
13110
13152
|
const claudeFiles = await listClaudeTranscripts(claudeProjectsDir);
|
|
@@ -13242,7 +13284,7 @@ async function isDirEntry(parent, entry) {
|
|
|
13242
13284
|
if (entry.isDirectory()) return true;
|
|
13243
13285
|
if (!entry.isSymbolicLink()) return false;
|
|
13244
13286
|
try {
|
|
13245
|
-
return (await stat7(
|
|
13287
|
+
return (await stat7(join15(parent, entry.name))).isDirectory();
|
|
13246
13288
|
} catch {
|
|
13247
13289
|
return false;
|
|
13248
13290
|
}
|
|
@@ -13257,7 +13299,7 @@ async function listClaudeTranscripts(projectsRoot) {
|
|
|
13257
13299
|
const files = [];
|
|
13258
13300
|
for (const entry of entries) {
|
|
13259
13301
|
if (!await isDirEntry(projectsRoot, entry)) continue;
|
|
13260
|
-
const full =
|
|
13302
|
+
const full = join15(projectsRoot, entry.name);
|
|
13261
13303
|
let names;
|
|
13262
13304
|
try {
|
|
13263
13305
|
names = await readdir4(full);
|
|
@@ -13265,7 +13307,7 @@ async function listClaudeTranscripts(projectsRoot) {
|
|
|
13265
13307
|
continue;
|
|
13266
13308
|
}
|
|
13267
13309
|
for (const name of names) {
|
|
13268
|
-
if (name.endsWith(".jsonl")) files.push(
|
|
13310
|
+
if (name.endsWith(".jsonl")) files.push(join15(full, name));
|
|
13269
13311
|
}
|
|
13270
13312
|
}
|
|
13271
13313
|
return files.sort();
|
|
@@ -13280,7 +13322,7 @@ async function listCodexRollouts(sessionsRoot) {
|
|
|
13280
13322
|
return;
|
|
13281
13323
|
}
|
|
13282
13324
|
for (const entry of entries) {
|
|
13283
|
-
const full =
|
|
13325
|
+
const full = join15(dir, entry.name);
|
|
13284
13326
|
if (entry.isDirectory()) await walk(full);
|
|
13285
13327
|
else if (entry.isFile() && entry.name.startsWith("rollout-") && entry.name.endsWith(".jsonl")) {
|
|
13286
13328
|
found.push(full);
|
|
@@ -13411,7 +13453,7 @@ function inertLines(result) {
|
|
|
13411
13453
|
// src/lib/portfolio-safety.ts
|
|
13412
13454
|
import { execFile as execFile2 } from "child_process";
|
|
13413
13455
|
import { lstat as lstat3, realpath as realpath5 } from "fs/promises";
|
|
13414
|
-
import { isAbsolute as isAbsolute6, join as
|
|
13456
|
+
import { isAbsolute as isAbsolute6, join as join16, relative as relative5, resolve as resolve12, sep as sep4 } from "path";
|
|
13415
13457
|
import { promisify as promisify2 } from "util";
|
|
13416
13458
|
import { readManifest as readManifest17 } from "@basou/core";
|
|
13417
13459
|
var execFileAsync2 = promisify2(execFile2);
|
|
@@ -13435,7 +13477,7 @@ function isBasouPath(p) {
|
|
|
13435
13477
|
async function inspectRepo(repoPath) {
|
|
13436
13478
|
let hasEntry = false;
|
|
13437
13479
|
try {
|
|
13438
|
-
await lstat3(
|
|
13480
|
+
await lstat3(join16(repoPath, ".basou"));
|
|
13439
13481
|
hasEntry = true;
|
|
13440
13482
|
} catch (error) {
|
|
13441
13483
|
if (errorCode2(error) !== "ENOENT") {
|
|
@@ -13589,7 +13631,7 @@ function formatSafetyReport(result) {
|
|
|
13589
13631
|
|
|
13590
13632
|
// src/lib/view-server.ts
|
|
13591
13633
|
import { createServer } from "http";
|
|
13592
|
-
import { basename as basename10,
|
|
13634
|
+
import { basename as basename10, resolve as resolve13 } from "path";
|
|
13593
13635
|
import {
|
|
13594
13636
|
boardLivePage,
|
|
13595
13637
|
boardPage,
|
|
@@ -13597,7 +13639,7 @@ import {
|
|
|
13597
13639
|
boardPageUnavailable,
|
|
13598
13640
|
computeWorkStats as computeWorkStats2,
|
|
13599
13641
|
enumerateApprovals as enumerateApprovals2,
|
|
13600
|
-
findErrorCode as
|
|
13642
|
+
findErrorCode as findErrorCode19,
|
|
13601
13643
|
inspectSessionEntry as inspectSessionEntry2,
|
|
13602
13644
|
isLazyExpired as isLazyExpired2,
|
|
13603
13645
|
loadApproval as loadApproval2,
|
|
@@ -13611,7 +13653,9 @@ import {
|
|
|
13611
13653
|
renderDecisions as renderDecisions3,
|
|
13612
13654
|
renderHandoff as renderHandoff3,
|
|
13613
13655
|
resolveViewLanguageFromPaths,
|
|
13656
|
+
SessionFileNotAFileError as SessionFileNotAFileError6,
|
|
13614
13657
|
SessionIdSchema,
|
|
13658
|
+
SessionNotADirectoryError,
|
|
13615
13659
|
summarizeOrientation,
|
|
13616
13660
|
tryRemoteUrl
|
|
13617
13661
|
} from "@basou/core";
|
|
@@ -15546,7 +15590,7 @@ async function overview(ws, nowProvider, resolveRemoteUrl) {
|
|
|
15546
15590
|
try {
|
|
15547
15591
|
manifest = await readManifest18(ws.paths);
|
|
15548
15592
|
} catch (error) {
|
|
15549
|
-
if (
|
|
15593
|
+
if (findErrorCode19(error, "ENOENT")) {
|
|
15550
15594
|
return { initialized: false, repoRoot: ws.repoRoot };
|
|
15551
15595
|
}
|
|
15552
15596
|
throw error;
|
|
@@ -15614,10 +15658,7 @@ async function sessionDetail(ws, sessionId) {
|
|
|
15614
15658
|
}
|
|
15615
15659
|
const entry = await inspectSessionEntry2(ws.paths, sessionId);
|
|
15616
15660
|
if (entry === "symlink" || entry === "not_a_directory") {
|
|
15617
|
-
throw new HttpError(
|
|
15618
|
-
404,
|
|
15619
|
-
`Session ${sessionId} is not a directory; a symlink or a file there is not followed`
|
|
15620
|
-
);
|
|
15661
|
+
throw new HttpError(404, new SessionNotADirectoryError(sessionId, entry).message);
|
|
15621
15662
|
}
|
|
15622
15663
|
let session;
|
|
15623
15664
|
try {
|
|
@@ -15626,10 +15667,11 @@ async function sessionDetail(ws, sessionId) {
|
|
|
15626
15667
|
if (error instanceof Error && error.message === "YAML file not found") {
|
|
15627
15668
|
throw new HttpError(404, "Session not found");
|
|
15628
15669
|
}
|
|
15670
|
+
if (error instanceof SessionFileNotAFileError6) throw new HttpError(404, error.message);
|
|
15629
15671
|
throw error;
|
|
15630
15672
|
}
|
|
15631
15673
|
try {
|
|
15632
|
-
const events = await readAllEvents2(
|
|
15674
|
+
const events = await readAllEvents2(ws.paths, sessionId);
|
|
15633
15675
|
return { session, events };
|
|
15634
15676
|
} catch {
|
|
15635
15677
|
return { session, events: [], degraded: true };
|
|
@@ -15894,7 +15936,7 @@ async function viewBoardOf(root, paths) {
|
|
|
15894
15936
|
own = void 0;
|
|
15895
15937
|
}
|
|
15896
15938
|
if (own?.visibility !== "private") return { why: "no_board" };
|
|
15897
|
-
return { recordsDir:
|
|
15939
|
+
return { recordsDir: join17(root, dirname9(DEFAULT_BOARD_PATH), "records") };
|
|
15898
15940
|
}
|
|
15899
15941
|
async function buildPortfolioDeps(workspaceFlags, ctx, cwd) {
|
|
15900
15942
|
const specs = workspaceFlags.length > 0 ? workspaceFlags.map((p) => ({ path: resolve14(cwd, p) })) : await loadPortfolioConfig6(ctx.portfolioConfigPath);
|
|
@@ -15956,7 +15998,7 @@ async function startListening(port, deps) {
|
|
|
15956
15998
|
try {
|
|
15957
15999
|
return await startViewServer({ port, deps });
|
|
15958
16000
|
} catch (error) {
|
|
15959
|
-
if (
|
|
16001
|
+
if (findErrorCode20(error, "EADDRINUSE")) {
|
|
15960
16002
|
throw new Error(`Port ${port} is already in use. Pass --port <n> to choose another.`, {
|
|
15961
16003
|
cause: error
|
|
15962
16004
|
});
|
|
@@ -16009,7 +16051,7 @@ async function assertWorkspaceInitialized16(basouRoot) {
|
|
|
16009
16051
|
try {
|
|
16010
16052
|
await assertBasouRootSafe19(basouRoot);
|
|
16011
16053
|
} catch (error) {
|
|
16012
|
-
if (
|
|
16054
|
+
if (findErrorCode20(error, "ENOENT")) {
|
|
16013
16055
|
throw new Error("Workspace not initialized. Run 'basou init' first.");
|
|
16014
16056
|
}
|
|
16015
16057
|
throw error;
|
|
@@ -16048,7 +16090,7 @@ function installExperimentalNotice(program2, names = EXPERIMENTAL_COMMANDS, writ
|
|
|
16048
16090
|
function readBuildStamp() {
|
|
16049
16091
|
if (false) return void 0;
|
|
16050
16092
|
try {
|
|
16051
|
-
return JSON.parse('{"version":"0.
|
|
16093
|
+
return JSON.parse('{"version":"0.70.0","commit":"60d302a","committedAt":"2026-10-16T17:39:30+09:00"}');
|
|
16052
16094
|
} catch {
|
|
16053
16095
|
return void 0;
|
|
16054
16096
|
}
|