@basou/cli 0.69.0 → 0.70.1

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/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(join(paths.sessions, sessionId), {
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 sessionDir = join(paths.sessions, approval.session_id);
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 { byExternalId: existingByExternalId, notDirectories } = await loadExistingByExternalId(
1223
- paths,
1224
- sourceKind
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
- const one = notDirectories.length === 1;
1321
- console.error(
1322
- `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`
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) {
@@ -2600,7 +2618,7 @@ function repoLines(m) {
2600
2618
  `commits ${measured("commits", String) ?? "not measured"}`,
2601
2619
  `files ${measured("files", String) ?? "not measured"}`,
2602
2620
  `uncommitted ${measured("uncommitted", String) ?? "not measured"}`,
2603
- `behind origin/main ${measured("behind_main", String) ?? "(no origin/main)"}`
2621
+ `behind origin's default branch ${measured("behind_main", String) ?? "(origin/HEAD names no branch, and there is no origin/main)"}`
2604
2622
  ];
2605
2623
  return ` ${displayPath3(repo.path)}${name} ${parts.join(", ")}`;
2606
2624
  });
@@ -2957,7 +2975,7 @@ async function doRunChannelClear(face, options) {
2957
2975
 
2958
2976
  // src/commands/decision.ts
2959
2977
  import { homedir as homedir3 } from "os";
2960
- import { join as join7, resolve as resolve4 } from "path";
2978
+ import { resolve as resolve4 } from "path";
2961
2979
  import {
2962
2980
  AGENT_INFRA_DIRS as AGENT_INFRA_DIRS2,
2963
2981
  acquireLock as acquireLock2,
@@ -3538,9 +3556,8 @@ function isDecisionId(value) {
3538
3556
  async function decisionExists(paths, decisionId) {
3539
3557
  const entries = await loadSessionEntries(paths, { now: /* @__PURE__ */ new Date() });
3540
3558
  for (const entry of entries) {
3541
- const sessionDir = join7(paths.sessions, entry.sessionId);
3542
3559
  try {
3543
- for await (const ev of replayEvents2(sessionDir, {})) {
3560
+ for await (const ev of replayEvents2(paths, entry.sessionId, {})) {
3544
3561
  if (ev.type === "decision_recorded" && ev.decision_id === decisionId) return true;
3545
3562
  }
3546
3563
  } catch {
@@ -3994,7 +4011,7 @@ async function assertWorkspaceInitialized5(basouRoot) {
3994
4011
  // src/commands/exec.ts
3995
4012
  import { mkdir as mkdir2, rm as rm2 } from "fs/promises";
3996
4013
  import { homedir as homedir4 } from "os";
3997
- import { join as join8 } from "path";
4014
+ import { join as join7 } from "path";
3998
4015
  import {
3999
4016
  acquireLock as acquireLock3,
4000
4017
  assertBasouRootSafe as assertBasouRootSafe6,
@@ -4005,12 +4022,14 @@ import {
4005
4022
  EVENT_SCHEMA_VERSION as EVENT_SCHEMA_VERSION3,
4006
4023
  finalizeSessionYaml,
4007
4024
  getSnapshot,
4025
+ lstatSessionFile,
4008
4026
  overwriteYamlFile,
4009
4027
  parseDuration,
4010
4028
  prefixedUlid as prefixedUlid3,
4011
4029
  readManifest as readManifest6,
4012
4030
  readYamlFile as readYamlFile2,
4013
4031
  SESSION_SCHEMA_VERSION,
4032
+ SessionFileNotAFileError as SessionFileNotAFileError3,
4014
4033
  SessionSchema,
4015
4034
  sanitizeWorkingDirectory,
4016
4035
  writeObservedDuration,
@@ -4056,12 +4075,12 @@ async function runExec(command, args, options, ctx = {}) {
4056
4075
  const manifest = await readManifest6(paths);
4057
4076
  await assertSessionStoreSafe2(paths);
4058
4077
  const sessionId = prefixedUlid3("ses");
4059
- const sessionDir = join8(paths.sessions, sessionId);
4078
+ const sessionDir = join7(paths.sessions, sessionId);
4060
4079
  const appendEvent = ctx.appendEvent ?? (async (_sessionDir, event) => {
4061
4080
  await coreAppendChainedEvent(paths, sessionId, event);
4062
4081
  });
4063
4082
  const startedAt = now().toISOString();
4064
- const sessionYamlPath = join8(sessionDir, "session.yaml");
4083
+ const sessionYamlPath = join7(sessionDir, "session.yaml");
4065
4084
  const session = buildInitialSession({
4066
4085
  id: sessionId,
4067
4086
  command,
@@ -4110,7 +4129,7 @@ async function runExec(command, args, options, ctx = {}) {
4110
4129
  });
4111
4130
  const runningLock = await acquireLock3(paths, "session", sessionId);
4112
4131
  try {
4113
- await mutateSessionYaml(sessionYamlPath, (s) => {
4132
+ await mutateSessionYaml(paths, sessionId, (s) => {
4114
4133
  s.session.status = "running";
4115
4134
  });
4116
4135
  } finally {
@@ -4287,7 +4306,14 @@ function buildInitialSession(input) {
4287
4306
  }
4288
4307
  };
4289
4308
  }
4290
- async function mutateSessionYaml(filePath, mutator) {
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");
4291
4317
  const raw = await readYamlFile2(filePath);
4292
4318
  const parsed = SessionSchema.parse(raw);
4293
4319
  mutator(parsed);
@@ -4410,7 +4436,7 @@ async function assertWorkspaceInitialized6(basouRoot) {
4410
4436
  import { execFile } from "child_process";
4411
4437
  import { open as open2, readFile as readFile4, realpath as realpath4, stat as stat4 } from "fs/promises";
4412
4438
  import { homedir as homedir7 } from "os";
4413
- import { join as join11 } from "path";
4439
+ import { join as join10 } from "path";
4414
4440
  import { fileURLToPath } from "url";
4415
4441
  import { promisify } from "util";
4416
4442
  import {
@@ -4571,15 +4597,15 @@ function describeCodexHookTrust(trust) {
4571
4597
 
4572
4598
  // src/lib/protocols-config.ts
4573
4599
  import { homedir as homedir5 } from "os";
4574
- import { isAbsolute, join as join9, resolve as resolve5 } from "path";
4600
+ import { isAbsolute, join as join8, resolve as resolve5 } from "path";
4575
4601
  import { readYamlFile as readYamlFile3 } from "@basou/core";
4576
- var DEFAULT_PROTOCOLS_CONFIG_PATH = join9(homedir5(), ".basou", "protocols.yaml");
4577
- var DEFAULT_TARGET_PATH = join9(homedir5(), ".claude", "CLAUDE.md");
4602
+ var DEFAULT_PROTOCOLS_CONFIG_PATH = join8(homedir5(), ".basou", "protocols.yaml");
4603
+ var DEFAULT_TARGET_PATH = join8(homedir5(), ".claude", "CLAUDE.md");
4578
4604
  var ALLOWED_TOP_KEYS = /* @__PURE__ */ new Set(["version", "protocols"]);
4579
4605
  var ALLOWED_ENTRY_KEYS = /* @__PURE__ */ new Set(["source", "title"]);
4580
4606
  function expandTilde(p) {
4581
4607
  if (p === "~") return homedir5();
4582
- if (p.startsWith("~/")) return join9(homedir5(), p.slice(2));
4608
+ if (p.startsWith("~/")) return join8(homedir5(), p.slice(2));
4583
4609
  return p;
4584
4610
  }
4585
4611
  function isRecord(value) {
@@ -4657,12 +4683,12 @@ import {
4657
4683
 
4658
4684
  // src/lib/hosts-config.ts
4659
4685
  import { homedir as homedir6 } from "os";
4660
- import { isAbsolute as isAbsolute2, join as join10, resolve as resolve6 } from "path";
4686
+ import { isAbsolute as isAbsolute2, join as join9, resolve as resolve6 } from "path";
4661
4687
  import { readYamlFile as readYamlFile4 } from "@basou/core";
4662
- var DEFAULT_HOSTS_CONFIG_PATH = join10(homedir6(), ".basou", "hosts.yaml");
4688
+ var DEFAULT_HOSTS_CONFIG_PATH = join9(homedir6(), ".basou", "hosts.yaml");
4663
4689
  function expandTilde2(p) {
4664
4690
  if (p === "~") return homedir6();
4665
- if (p.startsWith("~/")) return join10(homedir6(), p.slice(2));
4691
+ if (p.startsWith("~/")) return join9(homedir6(), p.slice(2));
4666
4692
  return p;
4667
4693
  }
4668
4694
  function isRecord2(value) {
@@ -5246,7 +5272,7 @@ function parseMinEdits(raw) {
5246
5272
  if (raw === void 0 || !/^\d+$/.test(raw)) return void 0;
5247
5273
  return Number(raw);
5248
5274
  }
5249
- var DEFAULT_CLAUDE_SETTINGS_PATH = join11(homedir7(), ".claude", "settings.json");
5275
+ var DEFAULT_CLAUDE_SETTINGS_PATH = join10(homedir7(), ".claude", "settings.json");
5250
5276
  function resolveCliEntry() {
5251
5277
  return fileURLToPath(import.meta.url);
5252
5278
  }
@@ -5603,7 +5629,7 @@ function describeHookMode(tiers) {
5603
5629
  const gates = tiers.review ? "capture + review" : "capture";
5604
5630
  return `${enforcement}, ${gates}`;
5605
5631
  }
5606
- var DEFAULT_CODEX_FACE_PATH = join11(homedir7(), ".codex", "AGENTS.md");
5632
+ var DEFAULT_CODEX_FACE_PATH = join10(homedir7(), ".codex", "AGENTS.md");
5607
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.`;
5608
5634
  async function faceHasLeftoverOrientationBlock(facePath) {
5609
5635
  try {
@@ -5615,8 +5641,8 @@ async function faceHasLeftoverOrientationBlock(facePath) {
5615
5641
  return false;
5616
5642
  }
5617
5643
  }
5618
- var DEFAULT_CODEX_HOOKS_PATH = join11(homedir7(), ".codex", "hooks.json");
5619
- var DEFAULT_CODEX_CONFIG_PATH = join11(homedir7(), ".codex", "config.toml");
5644
+ var DEFAULT_CODEX_HOOKS_PATH = join10(homedir7(), ".codex", "hooks.json");
5645
+ var DEFAULT_CODEX_CONFIG_PATH = join10(homedir7(), ".codex", "config.toml");
5620
5646
  async function readHooksFile(path) {
5621
5647
  let raw;
5622
5648
  try {
@@ -6223,7 +6249,7 @@ import {
6223
6249
  writeFileSync,
6224
6250
  writeSync
6225
6251
  } from "fs";
6226
- import { basename as basename7, dirname as dirname7, isAbsolute as isAbsolute3, join as join12, relative as relative3, resolve as resolve9 } from "path";
6252
+ import { basename as basename7, dirname as dirname7, isAbsolute as isAbsolute3, join as join11, relative as relative3, resolve as resolve9 } from "path";
6227
6253
  import {
6228
6254
  appendBasouGitignore as appendBasouGitignore2,
6229
6255
  basouPaths as basouPaths13,
@@ -6604,7 +6630,7 @@ function classifySourceRoot(repositoryRoot, declaredPath) {
6604
6630
  } catch {
6605
6631
  return { path: declaredPath, kind: "unresolved" };
6606
6632
  }
6607
- return { path: declaredPath, kind: existsSync(join12(real, ".git")) ? "repo" : "non-repo" };
6633
+ return { path: declaredPath, kind: existsSync(join11(real, ".git")) ? "repo" : "non-repo" };
6608
6634
  }
6609
6635
  async function doRunProjectAdopt(options, ctx) {
6610
6636
  const cwd = ctx.cwd ?? process.cwd();
@@ -6708,7 +6734,7 @@ async function gatherRepoWiring(repositoryRoot, entry) {
6708
6734
  } catch {
6709
6735
  return { ...base, reachable: false, instructionFiles: [] };
6710
6736
  }
6711
- if (!existsSync(join12(real, ".git"))) {
6737
+ if (!existsSync(join11(real, ".git"))) {
6712
6738
  return { ...base, reachable: false, instructionFiles: [] };
6713
6739
  }
6714
6740
  try {
@@ -6716,7 +6742,7 @@ async function gatherRepoWiring(repositoryRoot, entry) {
6716
6742
  for (const name of INSTRUCTION_FILES) {
6717
6743
  let present = true;
6718
6744
  try {
6719
- lstatSync(join12(real, name));
6745
+ lstatSync(join11(real, name));
6720
6746
  } catch {
6721
6747
  present = false;
6722
6748
  }
@@ -6827,10 +6853,10 @@ function gatherRepoGitignore(repositoryRoot, entry) {
6827
6853
  } catch {
6828
6854
  return { ...base, reachable: false, currentLines: [] };
6829
6855
  }
6830
- if (!existsSync(join12(real, ".git"))) {
6856
+ if (!existsSync(join11(real, ".git"))) {
6831
6857
  return { ...base, reachable: false, currentLines: [] };
6832
6858
  }
6833
- return { ...base, reachable: true, currentLines: readGitignoreLines(join12(real, ".gitignore")) };
6859
+ return { ...base, reachable: true, currentLines: readGitignoreLines(join11(real, ".gitignore")) };
6834
6860
  }
6835
6861
  function hasErrorCode(error) {
6836
6862
  return error instanceof Error && typeof error.code === "string";
@@ -6844,7 +6870,7 @@ function readGitignoreLines(file) {
6844
6870
  }
6845
6871
  }
6846
6872
  function applyGitignorePlan(repositoryRoot, plan) {
6847
- const file = join12(realpathSync(resolve9(repositoryRoot, plan.path)), ".gitignore");
6873
+ const file = join11(realpathSync(resolve9(repositoryRoot, plan.path)), ".gitignore");
6848
6874
  let existing = "";
6849
6875
  try {
6850
6876
  existing = readFileSync(file, "utf8");
@@ -6984,7 +7010,7 @@ function gatherRepoSymlinks(repositoryRoot, anchorReal, entry) {
6984
7010
  return { ...base, isAnchor: false, reachable: false, canonicalPresent: false, files: [] };
6985
7011
  }
6986
7012
  if (real === anchorReal) {
6987
- const anchorCanonical = join12(real, CANONICAL_FILE);
7013
+ const anchorCanonical = join11(real, CANONICAL_FILE);
6988
7014
  const anchorState = anchorCanonicalState(anchorCanonical);
6989
7015
  if (anchorState === "absent") {
6990
7016
  return { ...base, isAnchor: true, reachable: true, canonicalPresent: false, files: [] };
@@ -7004,7 +7030,7 @@ function gatherRepoSymlinks(repositoryRoot, anchorReal, entry) {
7004
7030
  anchorCanonical,
7005
7031
  "self"
7006
7032
  ).map((spec) => {
7007
- const { state, actualTarget } = inspectSymlink(join12(real, spec.name), spec.target);
7033
+ const { state, actualTarget } = inspectSymlink(join11(real, spec.name), spec.target);
7008
7034
  return {
7009
7035
  name: spec.name,
7010
7036
  expectedTarget: spec.target,
@@ -7021,16 +7047,16 @@ function gatherRepoSymlinks(repositoryRoot, anchorReal, entry) {
7021
7047
  files: anchorFiles
7022
7048
  };
7023
7049
  }
7024
- if (!existsSync(join12(real, ".git"))) {
7050
+ if (!existsSync(join11(real, ".git"))) {
7025
7051
  return { ...base, isAnchor: false, reachable: false, canonicalPresent: false, files: [] };
7026
7052
  }
7027
- const canonicalFile = isSelf ? join12(real, CANONICAL_FILE) : join12(anchorReal, "agents", basename7(real), CANONICAL_FILE);
7053
+ const canonicalFile = isSelf ? join11(real, CANONICAL_FILE) : join11(anchorReal, "agents", basename7(real), CANONICAL_FILE);
7028
7054
  if (!existsSync(canonicalFile)) {
7029
7055
  return { ...base, isAnchor: false, reachable: true, canonicalPresent: false, files: [] };
7030
7056
  }
7031
7057
  const files = expectedSymlinkTargets(real, canonicalFile, mode).map(
7032
7058
  (spec) => {
7033
- const { state, actualTarget } = inspectSymlink(join12(real, spec.name), spec.target);
7059
+ const { state, actualTarget } = inspectSymlink(join11(real, spec.name), spec.target);
7034
7060
  return {
7035
7061
  name: spec.name,
7036
7062
  expectedTarget: spec.target,
@@ -7059,7 +7085,7 @@ function applySymlinkPlan(repositoryRoot, plan) {
7059
7085
  const created = [];
7060
7086
  const failed = [];
7061
7087
  for (const { name, target } of plan.toCreate) {
7062
- const filePath = join12(real, name);
7088
+ const filePath = join11(real, name);
7063
7089
  try {
7064
7090
  mkdirSync(dirname7(filePath), { recursive: true });
7065
7091
  symlinkSync(target, filePath);
@@ -7093,7 +7119,7 @@ function gatherViewSymlinks(repositoryRoot, anchorReal, roster, viewDir) {
7093
7119
  if (!existsSync(canonicalFile)) return { kind: "missing-canonical", viewName };
7094
7120
  const files = expectedSymlinkTargets(viewDir, canonicalFile, "hub").map(
7095
7121
  (spec) => {
7096
- const { state, actualTarget } = inspectSymlink(join12(viewDir, spec.name), spec.target);
7122
+ const { state, actualTarget } = inspectSymlink(join11(viewDir, spec.name), spec.target);
7097
7123
  return {
7098
7124
  name: spec.name,
7099
7125
  expectedTarget: spec.target,
@@ -7109,7 +7135,7 @@ function applyViewSymlinks(viewDir, files) {
7109
7135
  const failed = [];
7110
7136
  for (const f of files) {
7111
7137
  if (f.state !== "missing") continue;
7112
- const filePath = join12(viewDir, f.name);
7138
+ const filePath = join11(viewDir, f.name);
7113
7139
  try {
7114
7140
  mkdirSync(dirname7(filePath), { recursive: true });
7115
7141
  symlinkSync(f.expectedTarget, filePath);
@@ -7331,7 +7357,7 @@ function resolveViewDir(repositoryRoot, viewPath) {
7331
7357
  return realpathSync(abs);
7332
7358
  } catch {
7333
7359
  try {
7334
- return join12(realpathSync(dirname7(abs)), basename7(abs));
7360
+ return join11(realpathSync(dirname7(abs)), basename7(abs));
7335
7361
  } catch {
7336
7362
  return abs;
7337
7363
  }
@@ -7349,7 +7375,7 @@ function gatherViewRepo(repositoryRoot, viewDir, entry) {
7349
7375
  return { path: entry.path, reachable: false };
7350
7376
  }
7351
7377
  const linkName = basename7(repoReal);
7352
- const { state, actualTarget } = inspectSymlink(join12(viewDir, linkName), expectedTarget);
7378
+ const { state, actualTarget } = inspectSymlink(join11(viewDir, linkName), expectedTarget);
7353
7379
  return {
7354
7380
  path: entry.path,
7355
7381
  reachable: true,
@@ -7363,7 +7389,7 @@ function applyViewPlan(viewDir, toCreate) {
7363
7389
  const created = [];
7364
7390
  const failed = [];
7365
7391
  for (const { name, target } of toCreate) {
7366
- const filePath = join12(viewDir, name);
7392
+ const filePath = join11(viewDir, name);
7367
7393
  try {
7368
7394
  mkdirSync(dirname7(filePath), { recursive: true });
7369
7395
  symlinkSync(target, filePath);
@@ -7378,7 +7404,7 @@ var TOP_LEVEL_INSTRUCTION_FILES_LOWER = new Set(
7378
7404
  INSTRUCTION_FILES.filter((f) => !f.includes("/")).map((f) => f.toLowerCase())
7379
7405
  );
7380
7406
  function classifyViewLink(viewDir, name, rosterRealpaths) {
7381
- const filePath = join12(viewDir, name);
7407
+ const filePath = join11(viewDir, name);
7382
7408
  let isLink;
7383
7409
  try {
7384
7410
  isLink = lstatSync(filePath).isSymbolicLink();
@@ -7407,7 +7433,7 @@ function classifyViewLink(viewDir, name, rosterRealpaths) {
7407
7433
  if (!isDir) {
7408
7434
  return { target, kind: existsSync(resolved) ? "non-repo" : "broken" };
7409
7435
  }
7410
- return { target, kind: existsSync(join12(resolved, ".git")) ? "repo" : "non-repo" };
7436
+ return { target, kind: existsSync(join11(resolved, ".git")) ? "repo" : "non-repo" };
7411
7437
  }
7412
7438
  function gatherExistingViewLinks(viewDir, rosterRealpaths) {
7413
7439
  let names;
@@ -7432,7 +7458,7 @@ function pruneViewLinks(viewDir, toPrune, rosterRealpaths) {
7432
7458
  const pruned = [];
7433
7459
  const failed = [];
7434
7460
  for (const { name } of toPrune) {
7435
- const filePath = join12(viewDir, name);
7461
+ const filePath = join11(viewDir, name);
7436
7462
  const c = classifyViewLink(viewDir, name, rosterRealpaths);
7437
7463
  if (c === null || c.kind !== "repo") {
7438
7464
  failed.push({
@@ -7645,10 +7671,10 @@ async function runProjectPreset(options, ctx = {}) {
7645
7671
  }
7646
7672
  }
7647
7673
  function canonicalFileFor(anchorReal, canonicalName) {
7648
- return join12(anchorReal, "agents", canonicalName, CANONICAL_FILE);
7674
+ return join11(anchorReal, "agents", canonicalName, CANONICAL_FILE);
7649
7675
  }
7650
7676
  function canonicalLabelFor(canonicalName) {
7651
- return join12("agents", canonicalName, CANONICAL_FILE);
7677
+ return join11("agents", canonicalName, CANONICAL_FILE);
7652
7678
  }
7653
7679
  async function gatherRepoPreset(repositoryRoot, anchorReal, entry) {
7654
7680
  const declared = {
@@ -7669,7 +7695,7 @@ async function gatherRepoPreset(repositoryRoot, anchorReal, entry) {
7669
7695
  if (real === anchorReal) {
7670
7696
  return { ...declared, isAnchor: true, reachable: true, canonicalPresent: false };
7671
7697
  }
7672
- if (!existsSync(join12(real, ".git"))) {
7698
+ if (!existsSync(join11(real, ".git"))) {
7673
7699
  return { ...declared, isAnchor: false, reachable: false, canonicalPresent: false };
7674
7700
  }
7675
7701
  const canonicalName = basename7(real);
@@ -8051,24 +8077,24 @@ function gatherArchiveTeardown(repositoryRoot, manifest, target) {
8051
8077
  const instructionFiles = [];
8052
8078
  for (const name of INSTRUCTION_FILES) {
8053
8079
  try {
8054
- lstatSync(join12(real, name));
8080
+ lstatSync(join11(real, name));
8055
8081
  instructionFiles.push(name);
8056
8082
  } catch {
8057
8083
  }
8058
8084
  }
8059
8085
  let ignored;
8060
8086
  try {
8061
- ignored = new Set(readGitignoreLines(join12(real, ".gitignore")).map((l) => l.trim()));
8087
+ ignored = new Set(readGitignoreLines(join11(real, ".gitignore")).map((l) => l.trim()));
8062
8088
  } catch {
8063
8089
  ignored = /* @__PURE__ */ new Set();
8064
8090
  }
8065
8091
  const gitignorePatterns = INSTRUCTION_FILES.filter((p) => ignored.has(p) || ignored.has(`/${p}`));
8066
- const canonical2 = existsSync(join12(anchorReal, "agents", canonicalName, CANONICAL_FILE));
8092
+ const canonical2 = existsSync(join11(anchorReal, "agents", canonicalName, CANONICAL_FILE));
8067
8093
  let viewLink = false;
8068
8094
  const viewPath = manifest.workspace.view;
8069
8095
  if (viewPath !== void 0) {
8070
8096
  try {
8071
- lstatSync(join12(resolveViewDir(repositoryRoot, viewPath), canonicalName));
8097
+ lstatSync(join11(resolveViewDir(repositoryRoot, viewPath), canonicalName));
8072
8098
  viewLink = true;
8073
8099
  } catch {
8074
8100
  }
@@ -8082,11 +8108,11 @@ function gatherArchiveTeardown(repositoryRoot, manifest, target) {
8082
8108
  };
8083
8109
  }
8084
8110
  function teardownExpectedTargets(repoReal, anchorReal, canonicalName) {
8085
- const canonicalFile = join12(anchorReal, "agents", canonicalName, CANONICAL_FILE);
8111
+ const canonicalFile = join11(anchorReal, "agents", canonicalName, CANONICAL_FILE);
8086
8112
  return expectedSymlinkTargets(repoReal, canonicalFile);
8087
8113
  }
8088
8114
  function viewLinkPointsAt(viewDir, name, repoReal) {
8089
- const filePath = join12(viewDir, name);
8115
+ const filePath = join11(viewDir, name);
8090
8116
  try {
8091
8117
  if (!lstatSync(filePath).isSymbolicLink()) return false;
8092
8118
  const target = readlinkSync(filePath);
@@ -8097,7 +8123,7 @@ function viewLinkPointsAt(viewDir, name, repoReal) {
8097
8123
  }
8098
8124
  }
8099
8125
  function viewLinkPointsAtPath(viewDir, name, expectedRepoPath) {
8100
- const filePath = join12(viewDir, name);
8126
+ const filePath = join11(viewDir, name);
8101
8127
  try {
8102
8128
  if (!lstatSync(filePath).isSymbolicLink()) return false;
8103
8129
  const target = readlinkSync(filePath);
@@ -8148,7 +8174,7 @@ function gatherRepoTeardown(repositoryRoot, manifest, target) {
8148
8174
  if (!isAnchor) {
8149
8175
  if (repoReal !== void 0) {
8150
8176
  for (const spec of teardownExpectedTargets(repoReal, anchorReal, canonicalName)) {
8151
- const { state, actualTarget } = inspectSymlink(join12(repoReal, spec.name), spec.target);
8177
+ const { state, actualTarget } = inspectSymlink(join11(repoReal, spec.name), spec.target);
8152
8178
  if (isSelf) {
8153
8179
  if (state !== "missing")
8154
8180
  items.push({
@@ -8185,7 +8211,7 @@ function gatherRepoTeardown(repositoryRoot, manifest, target) {
8185
8211
  }
8186
8212
  let ignored;
8187
8213
  try {
8188
- ignored = new Set(readGitignoreLines(join12(repoReal, ".gitignore")).map((l) => l.trim()));
8214
+ ignored = new Set(readGitignoreLines(join11(repoReal, ".gitignore")).map((l) => l.trim()));
8189
8215
  for (const p of INSTRUCTION_FILES) {
8190
8216
  if (ignored.has(p) || ignored.has(`/${p}`)) {
8191
8217
  items.push({
@@ -8208,7 +8234,7 @@ function gatherRepoTeardown(repositoryRoot, manifest, target) {
8208
8234
  const viewPath = manifest.workspace.view;
8209
8235
  if (viewPath !== void 0) {
8210
8236
  const viewDir = resolveViewDir(repositoryRoot, viewPath);
8211
- const linkPath = join12(viewDir, canonicalName);
8237
+ const linkPath = join11(viewDir, canonicalName);
8212
8238
  let isLink = false;
8213
8239
  try {
8214
8240
  isLink = lstatSync(linkPath).isSymbolicLink();
@@ -8234,8 +8260,8 @@ function gatherRepoTeardown(repositoryRoot, manifest, target) {
8234
8260
  else items.push({ kind: "view-symlink", label: canonicalName, state: "removable" });
8235
8261
  }
8236
8262
  }
8237
- const canonicalFile = join12(anchorReal, "agents", canonicalName, CANONICAL_FILE);
8238
- const canonicalLabel = join12("agents", canonicalName, CANONICAL_FILE);
8263
+ const canonicalFile = join11(anchorReal, "agents", canonicalName, CANONICAL_FILE);
8264
+ const canonicalLabel = join11("agents", canonicalName, CANONICAL_FILE);
8239
8265
  let canonicalIsLink = false;
8240
8266
  try {
8241
8267
  canonicalIsLink = lstatSync(canonicalFile).isSymbolicLink();
@@ -8339,12 +8365,12 @@ function applyRepoTeardown(repositoryRoot, manifest, plan) {
8339
8365
  );
8340
8366
  for (const item of removable.filter((i) => i.kind === "instruction-symlink")) {
8341
8367
  const expected = expectedByName.get(item.label);
8342
- if (repoReal === null || expected === void 0 || inspectSymlink(join12(repoReal, item.label), expected).state !== "correct") {
8368
+ if (repoReal === null || expected === void 0 || inspectSymlink(join11(repoReal, item.label), expected).state !== "correct") {
8343
8369
  changed(item.label);
8344
8370
  continue;
8345
8371
  }
8346
8372
  try {
8347
- unlinkSync(join12(repoReal, item.label));
8373
+ unlinkSync(join11(repoReal, item.label));
8348
8374
  removed.push(item.label);
8349
8375
  } catch (error) {
8350
8376
  failed.push({ label: item.label, message: failureReason(error) });
@@ -8363,7 +8389,7 @@ function applyRepoTeardown(repositoryRoot, manifest, plan) {
8363
8389
  continue;
8364
8390
  }
8365
8391
  try {
8366
- unlinkSync(join12(viewDir, item.label));
8392
+ unlinkSync(join11(viewDir, item.label));
8367
8393
  removed.push(`view/${item.label}`);
8368
8394
  } catch (error) {
8369
8395
  failed.push({ label: `view/${item.label}`, message: failureReason(error) });
@@ -8371,7 +8397,7 @@ function applyRepoTeardown(repositoryRoot, manifest, plan) {
8371
8397
  }
8372
8398
  const NOFOLLOW = fsConstants.O_NOFOLLOW ?? 0;
8373
8399
  for (const item of removable.filter((i) => i.kind === "canonical-block")) {
8374
- const canonicalFile = join12(anchorReal, "agents", canonicalName, CANONICAL_FILE);
8400
+ const canonicalFile = join11(anchorReal, "agents", canonicalName, CANONICAL_FILE);
8375
8401
  try {
8376
8402
  if (lstatSync(canonicalFile).isSymbolicLink()) {
8377
8403
  changed(item.label);
@@ -8645,12 +8671,12 @@ function gatherRenameWiring(repositoryRoot, manifest, oldBasename) {
8645
8671
  } catch {
8646
8672
  return { canonicalDirOld: false, viewLinkOld: false };
8647
8673
  }
8648
- const canonicalDirOld = existsSync(join12(anchorReal, "agents", oldBasename));
8674
+ const canonicalDirOld = existsSync(join11(anchorReal, "agents", oldBasename));
8649
8675
  let viewLinkOld = false;
8650
8676
  const viewPath = manifest.workspace.view;
8651
8677
  if (viewPath !== void 0) {
8652
8678
  try {
8653
- lstatSync(join12(resolveViewDir(repositoryRoot, viewPath), oldBasename));
8679
+ lstatSync(join11(resolveViewDir(repositoryRoot, viewPath), oldBasename));
8654
8680
  viewLinkOld = true;
8655
8681
  } catch {
8656
8682
  }
@@ -8995,7 +9021,7 @@ async function doRunProjectSeedAnchor(options, ctx) {
8995
9021
  console.log("\u2139\uFE0F No repo roster declared \u2014 nothing to seed.");
8996
9022
  return;
8997
9023
  }
8998
- const anchorDoc = join12(repositoryRoot, CANONICAL_FILE);
9024
+ const anchorDoc = join11(repositoryRoot, CANONICAL_FILE);
8999
9025
  if (pathPresent(anchorDoc)) {
9000
9026
  console.log(
9001
9027
  `\u2705 The anchor's own \`${CANONICAL_FILE}\` already exists \u2014 hand-maintained, left untouched.`
@@ -9057,7 +9083,7 @@ function regularFileSpokes(repoReal) {
9057
9083
  const out = [];
9058
9084
  for (const spoke of ["CLAUDE.md", ".github/copilot-instructions.md"]) {
9059
9085
  try {
9060
- const st = lstatSync(join12(repoReal, spoke));
9086
+ const st = lstatSync(join11(repoReal, spoke));
9061
9087
  if (!st.isSymbolicLink() && st.isFile()) out.push(spoke);
9062
9088
  } catch {
9063
9089
  }
@@ -9103,8 +9129,8 @@ function gatherRetrofit(repositoryRoot, anchorReal, roster, argAbs, argReal, vie
9103
9129
  };
9104
9130
  }
9105
9131
  const isAnchor = argReal === anchorReal;
9106
- const reachable = existsSync(join12(argReal, ".git"));
9107
- const canonicalFile = join12(anchorReal, "agents", canonicalName, CANONICAL_FILE);
9132
+ const reachable = existsSync(join11(argReal, ".git"));
9133
+ const canonicalFile = join11(anchorReal, "agents", canonicalName, CANONICAL_FILE);
9108
9134
  return {
9109
9135
  path,
9110
9136
  declared,
@@ -9113,13 +9139,13 @@ function gatherRetrofit(repositoryRoot, anchorReal, roster, argAbs, argReal, vie
9113
9139
  reachable,
9114
9140
  canonicalName,
9115
9141
  ...viewCanonicalName !== void 0 ? { viewCanonicalName } : {},
9116
- agentsState: inspectAgentsState(join12(argReal, CANONICAL_FILE)),
9142
+ agentsState: inspectAgentsState(join11(argReal, CANONICAL_FILE)),
9117
9143
  canonicalExists: pathPresent(canonicalFile),
9118
9144
  regularSpokes: regularFileSpokes(argReal)
9119
9145
  };
9120
9146
  }
9121
9147
  function relocateAgentsFile(repoReal, canonicalFile) {
9122
- const agentsFile = join12(repoReal, CANONICAL_FILE);
9148
+ const agentsFile = join11(repoReal, CANONICAL_FILE);
9123
9149
  try {
9124
9150
  mkdirSync(dirname7(canonicalFile), { recursive: true });
9125
9151
  } catch (error) {
@@ -9235,7 +9261,7 @@ async function doRunProjectRetrofit(repo, options, ctx) {
9235
9261
  let failure;
9236
9262
  let partial = false;
9237
9263
  if (options.apply === true && plan.action === "relocate" && argReal !== void 0) {
9238
- const canonicalFile = join12(anchorReal, "agents", plan.canonicalName, CANONICAL_FILE);
9264
+ const canonicalFile = join11(anchorReal, "agents", plan.canonicalName, CANONICAL_FILE);
9239
9265
  const res = relocateAgentsFile(argReal, canonicalFile);
9240
9266
  if (res.ok) {
9241
9267
  applied = true;
@@ -9627,7 +9653,7 @@ import { InvalidArgumentError as InvalidArgumentError4 } from "commander";
9627
9653
  // src/commands/refresh-watch.ts
9628
9654
  import { readdir as readdir3, stat as stat6 } from "fs/promises";
9629
9655
  import { homedir as homedir9 } from "os";
9630
- import { join as join13 } from "path";
9656
+ import { join as join12 } from "path";
9631
9657
  import {
9632
9658
  assertApprovalStoreSafe as assertApprovalStoreSafe2,
9633
9659
  assertLockStoreSafe as assertLockStoreSafe4,
@@ -9639,8 +9665,8 @@ var MIN_WATCH_INTERVAL_SEC = 5;
9639
9665
  var MAX_WATCH_INTERVAL_SEC = 86400;
9640
9666
  function watchedRoots(ctx) {
9641
9667
  return [
9642
- ctx.codexSessionsDir ?? join13(homedir9(), ".codex", "sessions"),
9643
- ctx.claudeProjectsDir ?? join13(homedir9(), ".claude", "projects")
9668
+ ctx.codexSessionsDir ?? join12(homedir9(), ".codex", "sessions"),
9669
+ ctx.claudeProjectsDir ?? join12(homedir9(), ".claude", "projects")
9644
9670
  ];
9645
9671
  }
9646
9672
  async function scanSourceLogs(roots) {
@@ -9654,7 +9680,7 @@ async function scanSourceLogs(roots) {
9654
9680
  throw new Error("Failed to read a source log directory", { cause: error });
9655
9681
  }
9656
9682
  for (const entry of entries) {
9657
- const full = join13(dir, entry.name);
9683
+ const full = join12(dir, entry.name);
9658
9684
  if (entry.isDirectory()) {
9659
9685
  await walk(full);
9660
9686
  } else if (entry.isFile() && entry.name.endsWith(".jsonl")) {
@@ -10500,7 +10526,7 @@ function unattachedLines(u) {
10500
10526
  // src/commands/run.ts
10501
10527
  import { mkdir as mkdir3, readFile as readFile6, rm as rm3 } from "fs/promises";
10502
10528
  import { homedir as homedir11 } from "os";
10503
- import { join as join14 } from "path";
10529
+ import { join as join13 } from "path";
10504
10530
  import {
10505
10531
  acquireLock as acquireLock5,
10506
10532
  assertBasouRootSafe as assertBasouRootSafe13,
@@ -10515,6 +10541,7 @@ import {
10515
10541
  findBasouSessionStartHook as findBasouSessionStartHook2,
10516
10542
  getDiff,
10517
10543
  getSnapshot as getSnapshot2,
10544
+ lstatSessionFile as lstatSessionFile2,
10518
10545
  overwriteYamlFile as overwriteYamlFile2,
10519
10546
  prefixedUlid as prefixedUlid4,
10520
10547
  readManifest as readManifest12,
@@ -10522,6 +10549,7 @@ import {
10522
10549
  resolveClaudeCodeCommand,
10523
10550
  resolveCodexCommand,
10524
10551
  SESSION_SCHEMA_VERSION as SESSION_SCHEMA_VERSION2,
10552
+ SessionFileNotAFileError as SessionFileNotAFileError4,
10525
10553
  SessionSchema as SessionSchema2,
10526
10554
  sanitizeRelatedFiles,
10527
10555
  sanitizeWorkingDirectory as sanitizeWorkingDirectory2,
@@ -10581,12 +10609,12 @@ async function runTrackedTool(args, options, ctx, adapter) {
10581
10609
  const manifest = await readManifest12(paths);
10582
10610
  await assertSessionStoreSafe3(paths);
10583
10611
  const sessionId = prefixedUlid4("ses");
10584
- const sessionDir = join14(paths.sessions, sessionId);
10612
+ const sessionDir = join13(paths.sessions, sessionId);
10585
10613
  const appendEvent = ctx.appendEvent ?? (async (_sessionDir, event) => {
10586
10614
  await coreAppendChainedEvent2(paths, sessionId, event);
10587
10615
  });
10588
10616
  const startedAt = now().toISOString();
10589
- const sessionYamlPath = join14(sessionDir, "session.yaml");
10617
+ const sessionYamlPath = join13(sessionDir, "session.yaml");
10590
10618
  const session = buildInitialSession2({
10591
10619
  id: sessionId,
10592
10620
  command,
@@ -10637,7 +10665,7 @@ async function runTrackedTool(args, options, ctx, adapter) {
10637
10665
  });
10638
10666
  const runningLock = await acquireLock5(paths, "session", sessionId);
10639
10667
  try {
10640
- await mutateSessionYaml2(sessionYamlPath, (s) => {
10668
+ await mutateSessionYaml2(paths, sessionId, (s) => {
10641
10669
  s.session.status = "running";
10642
10670
  });
10643
10671
  } finally {
@@ -10883,7 +10911,14 @@ function buildInitialSession2(input) {
10883
10911
  }
10884
10912
  };
10885
10913
  }
10886
- async function mutateSessionYaml2(filePath, mutator) {
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");
10887
10922
  const raw = await readYamlFile5(filePath);
10888
10923
  const parsed = SessionSchema2.parse(raw);
10889
10924
  mutator(parsed);
@@ -10951,7 +10986,7 @@ async function noteCodexHookStatusPreSpawn(_cwd, ctx) {
10951
10986
 
10952
10987
  // src/commands/session.ts
10953
10988
  import { readFile as readFile7 } from "fs/promises";
10954
- import { basename as basename8, isAbsolute as isAbsolute5, join as join15, relative as relative4, sep as sep3 } from "path";
10989
+ import { basename as basename8, isAbsolute as isAbsolute5, join as join14, relative as relative4, sep as sep3 } from "path";
10955
10990
  import {
10956
10991
  acquireLock as acquireLock6,
10957
10992
  appendEventToExistingSession as appendEventToExistingSession3,
@@ -10965,6 +11000,7 @@ import {
10965
11000
  importSessionFromJson as importSessionFromJson2,
10966
11001
  LOCAL_CLI_EVENT_SOURCE as LOCAL_CLI_EVENT_SOURCE4,
10967
11002
  loadSessionEntries as loadSessionEntries2,
11003
+ lstatSessionFile as lstatSessionFile3,
10968
11004
  normalizeSessionTimestamps,
10969
11005
  readAllEvents,
10970
11006
  readManifest as readManifest13,
@@ -10974,6 +11010,7 @@ import {
10974
11010
  resolveSessionId as resolveSessionId3,
10975
11011
  resolveTaskId,
10976
11012
  SESSION_IMPORT_SCHEMA_VERSION as SESSION_IMPORT_SCHEMA_VERSION2,
11013
+ SessionFileNotAFileError as SessionFileNotAFileError5,
10977
11014
  SessionImportPayloadSchema as SessionImportPayloadSchema2,
10978
11015
  SessionSchema as SessionSchema3,
10979
11016
  SessionStatusSchema,
@@ -11083,8 +11120,16 @@ async function doRunSessionShow(idInput, options, ctx) {
11083
11120
  const paths = basouPaths19(repositoryRoot);
11084
11121
  await assertWorkspaceInitialized12(paths.root);
11085
11122
  const sessionId = await resolveSessionId3(paths, idInput);
11086
- const sessionDir = join15(paths.sessions, sessionId);
11087
- const sessionYamlPath = join15(sessionDir, "session.yaml");
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
+ }
11088
11133
  let session;
11089
11134
  try {
11090
11135
  const raw = await readYamlFile6(sessionYamlPath);
@@ -11096,7 +11141,7 @@ async function doRunSessionShow(idInput, options, ctx) {
11096
11141
  throw new Error("Failed to read session", { cause: error });
11097
11142
  }
11098
11143
  let eventsLostLines = 0;
11099
- const events = await readAllEvents(sessionDir, {
11144
+ const events = await readAllEvents(paths, sessionId, {
11100
11145
  onWarning: (w) => {
11101
11146
  if (w.kind === "malformed_json" || w.kind === "schema_violation") eventsLostLines++;
11102
11147
  printReplayWarning(w, sessionId);
@@ -11614,7 +11659,7 @@ async function doRunSessionRechain(options, ctx) {
11614
11659
  }
11615
11660
  }
11616
11661
  const tamperedCount = rows.filter(
11617
- (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"
11618
11663
  ).length;
11619
11664
  const errorCount = rows.filter((r) => r.status === "error").length;
11620
11665
  if (options.json === true) {
@@ -11642,6 +11687,10 @@ function renderRechainRow(row, dryRun) {
11642
11687
  if (row.reason === "symlink" || row.reason === "not_a_directory") {
11643
11688
  return `skipped (${row.reason === "symlink" ? "SYMLINK" : "NOT A DIRECTORY"} \u2014 not followed; inspect with 'basou verify')`;
11644
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
+ }
11645
11694
  return `skipped (${row.reason})`;
11646
11695
  case "error":
11647
11696
  return `error (${row.message})`;
@@ -11850,7 +11899,6 @@ function formatVersionGateMessage(error) {
11850
11899
 
11851
11900
  // src/commands/task.ts
11852
11901
  import { readFile as readFile8 } from "fs/promises";
11853
- import { join as join16 } from "path";
11854
11902
  import {
11855
11903
  archiveTask,
11856
11904
  assertBasouRootSafe as assertBasouRootSafe17,
@@ -12175,9 +12223,8 @@ async function doRunTaskShow(idInput, options, ctx) {
12175
12223
  const events = [];
12176
12224
  const linkedSessionIds = new Set(doc.task.task.linked_sessions);
12177
12225
  for (const s of sessions) {
12178
- const sessionDir = join16(paths.sessions, s.sessionId);
12179
12226
  try {
12180
- for await (const ev of replayEvents3(sessionDir, {
12227
+ for await (const ev of replayEvents3(paths, s.sessionId, {
12181
12228
  onWarning: (w) => printReplayWarning(w, s.sessionId)
12182
12229
  })) {
12183
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) {
@@ -13070,7 +13117,7 @@ async function assertWorkspaceInitialized15(basouRoot) {
13070
13117
  // src/commands/view.ts
13071
13118
  import { spawn } from "child_process";
13072
13119
  import { createHash as createHash2 } from "crypto";
13073
- import { basename as basename11, dirname as dirname9, join as join20, resolve as resolve14 } from "path";
13120
+ import { basename as basename11, dirname as dirname9, join as join17, resolve as resolve14 } from "path";
13074
13121
  import {
13075
13122
  assertBasouRootSafe as assertBasouRootSafe19,
13076
13123
  basouPaths as basouPaths25,
@@ -13084,7 +13131,7 @@ import { InvalidArgumentError as InvalidArgumentError8 } from "commander";
13084
13131
  import { createReadStream as createReadStream2 } from "fs";
13085
13132
  import { readdir as readdir4, stat as stat7 } from "fs/promises";
13086
13133
  import { homedir as homedir12 } from "os";
13087
- import { basename as basename9, dirname as dirname8, join as join17 } from "path";
13134
+ import { basename as basename9, dirname as dirname8, join as join15 } from "path";
13088
13135
  import { createInterface as createInterface2 } from "readline";
13089
13136
  import { basouPaths as basouPaths24, readManifest as readManifest16 } from "@basou/core";
13090
13137
  function uncapturedTotal(result) {
@@ -13096,8 +13143,8 @@ async function checkPortfolioCoverage(workspaces, ctx = {}) {
13096
13143
  ctx.portfolioConfigPath
13097
13144
  );
13098
13145
  const listedDirs = new Set([...roots].map((root) => encodeProjectDir(root)));
13099
- const claudeProjectsDir = ctx.claudeProjectsDir ?? join17(homedir12(), ".claude", "projects");
13100
- const codexSessionsDir = ctx.codexSessionsDir ?? join17(homedir12(), ".codex", "sessions");
13146
+ const claudeProjectsDir = ctx.claudeProjectsDir ?? join15(homedir12(), ".claude", "projects");
13147
+ const codexSessionsDir = ctx.codexSessionsDir ?? join15(homedir12(), ".codex", "sessions");
13101
13148
  const tally = new Tally(roots);
13102
13149
  const absentTrees = [];
13103
13150
  const claudeFiles = await listClaudeTranscripts(claudeProjectsDir);
@@ -13235,7 +13282,7 @@ async function isDirEntry(parent, entry) {
13235
13282
  if (entry.isDirectory()) return true;
13236
13283
  if (!entry.isSymbolicLink()) return false;
13237
13284
  try {
13238
- return (await stat7(join17(parent, entry.name))).isDirectory();
13285
+ return (await stat7(join15(parent, entry.name))).isDirectory();
13239
13286
  } catch {
13240
13287
  return false;
13241
13288
  }
@@ -13250,7 +13297,7 @@ async function listClaudeTranscripts(projectsRoot) {
13250
13297
  const files = [];
13251
13298
  for (const entry of entries) {
13252
13299
  if (!await isDirEntry(projectsRoot, entry)) continue;
13253
- const full = join17(projectsRoot, entry.name);
13300
+ const full = join15(projectsRoot, entry.name);
13254
13301
  let names;
13255
13302
  try {
13256
13303
  names = await readdir4(full);
@@ -13258,7 +13305,7 @@ async function listClaudeTranscripts(projectsRoot) {
13258
13305
  continue;
13259
13306
  }
13260
13307
  for (const name of names) {
13261
- if (name.endsWith(".jsonl")) files.push(join17(full, name));
13308
+ if (name.endsWith(".jsonl")) files.push(join15(full, name));
13262
13309
  }
13263
13310
  }
13264
13311
  return files.sort();
@@ -13273,7 +13320,7 @@ async function listCodexRollouts(sessionsRoot) {
13273
13320
  return;
13274
13321
  }
13275
13322
  for (const entry of entries) {
13276
- const full = join17(dir, entry.name);
13323
+ const full = join15(dir, entry.name);
13277
13324
  if (entry.isDirectory()) await walk(full);
13278
13325
  else if (entry.isFile() && entry.name.startsWith("rollout-") && entry.name.endsWith(".jsonl")) {
13279
13326
  found.push(full);
@@ -13404,7 +13451,7 @@ function inertLines(result) {
13404
13451
  // src/lib/portfolio-safety.ts
13405
13452
  import { execFile as execFile2 } from "child_process";
13406
13453
  import { lstat as lstat3, realpath as realpath5 } from "fs/promises";
13407
- import { isAbsolute as isAbsolute6, join as join18, relative as relative5, resolve as resolve12, sep as sep4 } from "path";
13454
+ import { isAbsolute as isAbsolute6, join as join16, relative as relative5, resolve as resolve12, sep as sep4 } from "path";
13408
13455
  import { promisify as promisify2 } from "util";
13409
13456
  import { readManifest as readManifest17 } from "@basou/core";
13410
13457
  var execFileAsync2 = promisify2(execFile2);
@@ -13428,7 +13475,7 @@ function isBasouPath(p) {
13428
13475
  async function inspectRepo(repoPath) {
13429
13476
  let hasEntry = false;
13430
13477
  try {
13431
- await lstat3(join18(repoPath, ".basou"));
13478
+ await lstat3(join16(repoPath, ".basou"));
13432
13479
  hasEntry = true;
13433
13480
  } catch (error) {
13434
13481
  if (errorCode2(error) !== "ENOENT") {
@@ -13582,7 +13629,7 @@ function formatSafetyReport(result) {
13582
13629
 
13583
13630
  // src/lib/view-server.ts
13584
13631
  import { createServer } from "http";
13585
- import { basename as basename10, join as join19, resolve as resolve13 } from "path";
13632
+ import { basename as basename10, resolve as resolve13 } from "path";
13586
13633
  import {
13587
13634
  boardLivePage,
13588
13635
  boardPage,
@@ -13604,7 +13651,9 @@ import {
13604
13651
  renderDecisions as renderDecisions3,
13605
13652
  renderHandoff as renderHandoff3,
13606
13653
  resolveViewLanguageFromPaths,
13654
+ SessionFileNotAFileError as SessionFileNotAFileError6,
13607
13655
  SessionIdSchema,
13656
+ SessionNotADirectoryError,
13608
13657
  summarizeOrientation,
13609
13658
  tryRemoteUrl
13610
13659
  } from "@basou/core";
@@ -14363,7 +14412,7 @@ var BOARD_HTML = `<!doctype html>
14363
14412
  var rows = l.repos.map(function (r) {
14364
14413
  var at = 'repos[' + r.path + ']';
14365
14414
  // A null the measurement says it could not measure, or one that means
14366
- // so (a detached HEAD, no commit yet, no origin/main), drawn as '-'.
14415
+ // so (a detached HEAD, no commit yet, no default branch), drawn as '-'.
14367
14416
  var value = function (field, v, show) {
14368
14417
  if (v !== null) return show(v);
14369
14418
  return missing(at) || missing(at + '.' + field) ? S.tiles.notMeasured : '-';
@@ -15607,10 +15656,7 @@ async function sessionDetail(ws, sessionId) {
15607
15656
  }
15608
15657
  const entry = await inspectSessionEntry2(ws.paths, sessionId);
15609
15658
  if (entry === "symlink" || entry === "not_a_directory") {
15610
- throw new HttpError(
15611
- 404,
15612
- `Session ${sessionId} is not a directory; a symlink or a file there is not followed`
15613
- );
15659
+ throw new HttpError(404, new SessionNotADirectoryError(sessionId, entry).message);
15614
15660
  }
15615
15661
  let session;
15616
15662
  try {
@@ -15619,10 +15665,11 @@ async function sessionDetail(ws, sessionId) {
15619
15665
  if (error instanceof Error && error.message === "YAML file not found") {
15620
15666
  throw new HttpError(404, "Session not found");
15621
15667
  }
15668
+ if (error instanceof SessionFileNotAFileError6) throw new HttpError(404, error.message);
15622
15669
  throw error;
15623
15670
  }
15624
15671
  try {
15625
- const events = await readAllEvents2(join19(ws.paths.sessions, sessionId));
15672
+ const events = await readAllEvents2(ws.paths, sessionId);
15626
15673
  return { session, events };
15627
15674
  } catch {
15628
15675
  return { session, events: [], degraded: true };
@@ -15887,7 +15934,7 @@ async function viewBoardOf(root, paths) {
15887
15934
  own = void 0;
15888
15935
  }
15889
15936
  if (own?.visibility !== "private") return { why: "no_board" };
15890
- return { recordsDir: join20(root, dirname9(DEFAULT_BOARD_PATH), "records") };
15937
+ return { recordsDir: join17(root, dirname9(DEFAULT_BOARD_PATH), "records") };
15891
15938
  }
15892
15939
  async function buildPortfolioDeps(workspaceFlags, ctx, cwd) {
15893
15940
  const specs = workspaceFlags.length > 0 ? workspaceFlags.map((p) => ({ path: resolve14(cwd, p) })) : await loadPortfolioConfig6(ctx.portfolioConfigPath);
@@ -16041,7 +16088,7 @@ function installExperimentalNotice(program, names = EXPERIMENTAL_COMMANDS, write
16041
16088
  function readBuildStamp() {
16042
16089
  if (false) return void 0;
16043
16090
  try {
16044
- return JSON.parse('{"version":"0.69.0","commit":"bffcfee","committedAt":"2026-10-16T08:07:08+09:00"}');
16091
+ return JSON.parse('{"version":"0.70.1","commit":"5fbd37a","committedAt":"2026-10-17T01:11:15+09:00"}');
16045
16092
  } catch {
16046
16093
  return void 0;
16047
16094
  }