@basou/cli 0.69.0 → 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 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(join(paths.sessions, sessionId), {
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 sessionDir = join(paths.sessions, approval.session_id);
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 { byExternalId: existingByExternalId, notDirectories } = await loadExistingByExternalId(
1225
- paths,
1226
- sourceKind
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
- const one = notDirectories.length === 1;
1323
- console.error(
1324
- `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`
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) {
@@ -2959,7 +2977,7 @@ async function doRunChannelClear(face, options) {
2959
2977
 
2960
2978
  // src/commands/decision.ts
2961
2979
  import { homedir as homedir3 } from "os";
2962
- import { join as join7, resolve as resolve4 } from "path";
2980
+ import { resolve as resolve4 } from "path";
2963
2981
  import {
2964
2982
  AGENT_INFRA_DIRS as AGENT_INFRA_DIRS2,
2965
2983
  acquireLock as acquireLock2,
@@ -3540,9 +3558,8 @@ function isDecisionId(value) {
3540
3558
  async function decisionExists(paths, decisionId) {
3541
3559
  const entries = await loadSessionEntries(paths, { now: /* @__PURE__ */ new Date() });
3542
3560
  for (const entry of entries) {
3543
- const sessionDir = join7(paths.sessions, entry.sessionId);
3544
3561
  try {
3545
- for await (const ev of replayEvents2(sessionDir, {})) {
3562
+ for await (const ev of replayEvents2(paths, entry.sessionId, {})) {
3546
3563
  if (ev.type === "decision_recorded" && ev.decision_id === decisionId) return true;
3547
3564
  }
3548
3565
  } catch {
@@ -3996,7 +4013,7 @@ async function assertWorkspaceInitialized5(basouRoot) {
3996
4013
  // src/commands/exec.ts
3997
4014
  import { mkdir as mkdir2, rm as rm2 } from "fs/promises";
3998
4015
  import { homedir as homedir4 } from "os";
3999
- import { join as join8 } from "path";
4016
+ import { join as join7 } from "path";
4000
4017
  import {
4001
4018
  acquireLock as acquireLock3,
4002
4019
  assertBasouRootSafe as assertBasouRootSafe6,
@@ -4007,12 +4024,14 @@ import {
4007
4024
  EVENT_SCHEMA_VERSION as EVENT_SCHEMA_VERSION3,
4008
4025
  finalizeSessionYaml,
4009
4026
  getSnapshot,
4027
+ lstatSessionFile,
4010
4028
  overwriteYamlFile,
4011
4029
  parseDuration,
4012
4030
  prefixedUlid as prefixedUlid3,
4013
4031
  readManifest as readManifest6,
4014
4032
  readYamlFile as readYamlFile2,
4015
4033
  SESSION_SCHEMA_VERSION,
4034
+ SessionFileNotAFileError as SessionFileNotAFileError3,
4016
4035
  SessionSchema,
4017
4036
  sanitizeWorkingDirectory,
4018
4037
  writeObservedDuration,
@@ -4058,12 +4077,12 @@ async function runExec(command, args, options, ctx = {}) {
4058
4077
  const manifest = await readManifest6(paths);
4059
4078
  await assertSessionStoreSafe2(paths);
4060
4079
  const sessionId = prefixedUlid3("ses");
4061
- const sessionDir = join8(paths.sessions, sessionId);
4080
+ const sessionDir = join7(paths.sessions, sessionId);
4062
4081
  const appendEvent = ctx.appendEvent ?? (async (_sessionDir, event) => {
4063
4082
  await coreAppendChainedEvent(paths, sessionId, event);
4064
4083
  });
4065
4084
  const startedAt = now().toISOString();
4066
- const sessionYamlPath = join8(sessionDir, "session.yaml");
4085
+ const sessionYamlPath = join7(sessionDir, "session.yaml");
4067
4086
  const session = buildInitialSession({
4068
4087
  id: sessionId,
4069
4088
  command,
@@ -4112,7 +4131,7 @@ async function runExec(command, args, options, ctx = {}) {
4112
4131
  });
4113
4132
  const runningLock = await acquireLock3(paths, "session", sessionId);
4114
4133
  try {
4115
- await mutateSessionYaml(sessionYamlPath, (s) => {
4134
+ await mutateSessionYaml(paths, sessionId, (s) => {
4116
4135
  s.session.status = "running";
4117
4136
  });
4118
4137
  } finally {
@@ -4289,7 +4308,14 @@ function buildInitialSession(input) {
4289
4308
  }
4290
4309
  };
4291
4310
  }
4292
- async function mutateSessionYaml(filePath, mutator) {
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");
4293
4319
  const raw = await readYamlFile2(filePath);
4294
4320
  const parsed = SessionSchema.parse(raw);
4295
4321
  mutator(parsed);
@@ -4412,7 +4438,7 @@ async function assertWorkspaceInitialized6(basouRoot) {
4412
4438
  import { execFile } from "child_process";
4413
4439
  import { open as open2, readFile as readFile4, realpath as realpath4, stat as stat4 } from "fs/promises";
4414
4440
  import { homedir as homedir7 } from "os";
4415
- import { join as join11 } from "path";
4441
+ import { join as join10 } from "path";
4416
4442
  import { fileURLToPath } from "url";
4417
4443
  import { promisify } from "util";
4418
4444
  import {
@@ -4573,15 +4599,15 @@ function describeCodexHookTrust(trust) {
4573
4599
 
4574
4600
  // src/lib/protocols-config.ts
4575
4601
  import { homedir as homedir5 } from "os";
4576
- import { isAbsolute, join as join9, resolve as resolve5 } from "path";
4602
+ import { isAbsolute, join as join8, resolve as resolve5 } from "path";
4577
4603
  import { readYamlFile as readYamlFile3 } from "@basou/core";
4578
- var DEFAULT_PROTOCOLS_CONFIG_PATH = join9(homedir5(), ".basou", "protocols.yaml");
4579
- var DEFAULT_TARGET_PATH = join9(homedir5(), ".claude", "CLAUDE.md");
4604
+ var DEFAULT_PROTOCOLS_CONFIG_PATH = join8(homedir5(), ".basou", "protocols.yaml");
4605
+ var DEFAULT_TARGET_PATH = join8(homedir5(), ".claude", "CLAUDE.md");
4580
4606
  var ALLOWED_TOP_KEYS = /* @__PURE__ */ new Set(["version", "protocols"]);
4581
4607
  var ALLOWED_ENTRY_KEYS = /* @__PURE__ */ new Set(["source", "title"]);
4582
4608
  function expandTilde(p) {
4583
4609
  if (p === "~") return homedir5();
4584
- if (p.startsWith("~/")) return join9(homedir5(), p.slice(2));
4610
+ if (p.startsWith("~/")) return join8(homedir5(), p.slice(2));
4585
4611
  return p;
4586
4612
  }
4587
4613
  function isRecord(value) {
@@ -4659,12 +4685,12 @@ import {
4659
4685
 
4660
4686
  // src/lib/hosts-config.ts
4661
4687
  import { homedir as homedir6 } from "os";
4662
- import { isAbsolute as isAbsolute2, join as join10, resolve as resolve6 } from "path";
4688
+ import { isAbsolute as isAbsolute2, join as join9, resolve as resolve6 } from "path";
4663
4689
  import { readYamlFile as readYamlFile4 } from "@basou/core";
4664
- var DEFAULT_HOSTS_CONFIG_PATH = join10(homedir6(), ".basou", "hosts.yaml");
4690
+ var DEFAULT_HOSTS_CONFIG_PATH = join9(homedir6(), ".basou", "hosts.yaml");
4665
4691
  function expandTilde2(p) {
4666
4692
  if (p === "~") return homedir6();
4667
- if (p.startsWith("~/")) return join10(homedir6(), p.slice(2));
4693
+ if (p.startsWith("~/")) return join9(homedir6(), p.slice(2));
4668
4694
  return p;
4669
4695
  }
4670
4696
  function isRecord2(value) {
@@ -5248,7 +5274,7 @@ function parseMinEdits(raw) {
5248
5274
  if (raw === void 0 || !/^\d+$/.test(raw)) return void 0;
5249
5275
  return Number(raw);
5250
5276
  }
5251
- var DEFAULT_CLAUDE_SETTINGS_PATH = join11(homedir7(), ".claude", "settings.json");
5277
+ var DEFAULT_CLAUDE_SETTINGS_PATH = join10(homedir7(), ".claude", "settings.json");
5252
5278
  function resolveCliEntry() {
5253
5279
  return fileURLToPath(import.meta.url);
5254
5280
  }
@@ -5605,7 +5631,7 @@ function describeHookMode(tiers) {
5605
5631
  const gates = tiers.review ? "capture + review" : "capture";
5606
5632
  return `${enforcement}, ${gates}`;
5607
5633
  }
5608
- var DEFAULT_CODEX_FACE_PATH = join11(homedir7(), ".codex", "AGENTS.md");
5634
+ var DEFAULT_CODEX_FACE_PATH = join10(homedir7(), ".codex", "AGENTS.md");
5609
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.`;
5610
5636
  async function faceHasLeftoverOrientationBlock(facePath) {
5611
5637
  try {
@@ -5617,8 +5643,8 @@ async function faceHasLeftoverOrientationBlock(facePath) {
5617
5643
  return false;
5618
5644
  }
5619
5645
  }
5620
- var DEFAULT_CODEX_HOOKS_PATH = join11(homedir7(), ".codex", "hooks.json");
5621
- var DEFAULT_CODEX_CONFIG_PATH = join11(homedir7(), ".codex", "config.toml");
5646
+ var DEFAULT_CODEX_HOOKS_PATH = join10(homedir7(), ".codex", "hooks.json");
5647
+ var DEFAULT_CODEX_CONFIG_PATH = join10(homedir7(), ".codex", "config.toml");
5622
5648
  async function readHooksFile(path) {
5623
5649
  let raw;
5624
5650
  try {
@@ -6225,7 +6251,7 @@ import {
6225
6251
  writeFileSync,
6226
6252
  writeSync
6227
6253
  } from "fs";
6228
- import { basename as basename7, dirname as dirname7, isAbsolute as isAbsolute3, join as join12, relative as relative3, resolve as resolve9 } from "path";
6254
+ import { basename as basename7, dirname as dirname7, isAbsolute as isAbsolute3, join as join11, relative as relative3, resolve as resolve9 } from "path";
6229
6255
  import {
6230
6256
  appendBasouGitignore as appendBasouGitignore2,
6231
6257
  basouPaths as basouPaths13,
@@ -6606,7 +6632,7 @@ function classifySourceRoot(repositoryRoot, declaredPath) {
6606
6632
  } catch {
6607
6633
  return { path: declaredPath, kind: "unresolved" };
6608
6634
  }
6609
- return { path: declaredPath, kind: existsSync(join12(real, ".git")) ? "repo" : "non-repo" };
6635
+ return { path: declaredPath, kind: existsSync(join11(real, ".git")) ? "repo" : "non-repo" };
6610
6636
  }
6611
6637
  async function doRunProjectAdopt(options, ctx) {
6612
6638
  const cwd = ctx.cwd ?? process.cwd();
@@ -6710,7 +6736,7 @@ async function gatherRepoWiring(repositoryRoot, entry) {
6710
6736
  } catch {
6711
6737
  return { ...base, reachable: false, instructionFiles: [] };
6712
6738
  }
6713
- if (!existsSync(join12(real, ".git"))) {
6739
+ if (!existsSync(join11(real, ".git"))) {
6714
6740
  return { ...base, reachable: false, instructionFiles: [] };
6715
6741
  }
6716
6742
  try {
@@ -6718,7 +6744,7 @@ async function gatherRepoWiring(repositoryRoot, entry) {
6718
6744
  for (const name of INSTRUCTION_FILES) {
6719
6745
  let present = true;
6720
6746
  try {
6721
- lstatSync(join12(real, name));
6747
+ lstatSync(join11(real, name));
6722
6748
  } catch {
6723
6749
  present = false;
6724
6750
  }
@@ -6829,10 +6855,10 @@ function gatherRepoGitignore(repositoryRoot, entry) {
6829
6855
  } catch {
6830
6856
  return { ...base, reachable: false, currentLines: [] };
6831
6857
  }
6832
- if (!existsSync(join12(real, ".git"))) {
6858
+ if (!existsSync(join11(real, ".git"))) {
6833
6859
  return { ...base, reachable: false, currentLines: [] };
6834
6860
  }
6835
- return { ...base, reachable: true, currentLines: readGitignoreLines(join12(real, ".gitignore")) };
6861
+ return { ...base, reachable: true, currentLines: readGitignoreLines(join11(real, ".gitignore")) };
6836
6862
  }
6837
6863
  function hasErrorCode(error) {
6838
6864
  return error instanceof Error && typeof error.code === "string";
@@ -6846,7 +6872,7 @@ function readGitignoreLines(file) {
6846
6872
  }
6847
6873
  }
6848
6874
  function applyGitignorePlan(repositoryRoot, plan) {
6849
- const file = join12(realpathSync(resolve9(repositoryRoot, plan.path)), ".gitignore");
6875
+ const file = join11(realpathSync(resolve9(repositoryRoot, plan.path)), ".gitignore");
6850
6876
  let existing = "";
6851
6877
  try {
6852
6878
  existing = readFileSync(file, "utf8");
@@ -6986,7 +7012,7 @@ function gatherRepoSymlinks(repositoryRoot, anchorReal, entry) {
6986
7012
  return { ...base, isAnchor: false, reachable: false, canonicalPresent: false, files: [] };
6987
7013
  }
6988
7014
  if (real === anchorReal) {
6989
- const anchorCanonical = join12(real, CANONICAL_FILE);
7015
+ const anchorCanonical = join11(real, CANONICAL_FILE);
6990
7016
  const anchorState = anchorCanonicalState(anchorCanonical);
6991
7017
  if (anchorState === "absent") {
6992
7018
  return { ...base, isAnchor: true, reachable: true, canonicalPresent: false, files: [] };
@@ -7006,7 +7032,7 @@ function gatherRepoSymlinks(repositoryRoot, anchorReal, entry) {
7006
7032
  anchorCanonical,
7007
7033
  "self"
7008
7034
  ).map((spec) => {
7009
- const { state, actualTarget } = inspectSymlink(join12(real, spec.name), spec.target);
7035
+ const { state, actualTarget } = inspectSymlink(join11(real, spec.name), spec.target);
7010
7036
  return {
7011
7037
  name: spec.name,
7012
7038
  expectedTarget: spec.target,
@@ -7023,16 +7049,16 @@ function gatherRepoSymlinks(repositoryRoot, anchorReal, entry) {
7023
7049
  files: anchorFiles
7024
7050
  };
7025
7051
  }
7026
- if (!existsSync(join12(real, ".git"))) {
7052
+ if (!existsSync(join11(real, ".git"))) {
7027
7053
  return { ...base, isAnchor: false, reachable: false, canonicalPresent: false, files: [] };
7028
7054
  }
7029
- const canonicalFile = isSelf ? join12(real, CANONICAL_FILE) : join12(anchorReal, "agents", basename7(real), CANONICAL_FILE);
7055
+ const canonicalFile = isSelf ? join11(real, CANONICAL_FILE) : join11(anchorReal, "agents", basename7(real), CANONICAL_FILE);
7030
7056
  if (!existsSync(canonicalFile)) {
7031
7057
  return { ...base, isAnchor: false, reachable: true, canonicalPresent: false, files: [] };
7032
7058
  }
7033
7059
  const files = expectedSymlinkTargets(real, canonicalFile, mode).map(
7034
7060
  (spec) => {
7035
- const { state, actualTarget } = inspectSymlink(join12(real, spec.name), spec.target);
7061
+ const { state, actualTarget } = inspectSymlink(join11(real, spec.name), spec.target);
7036
7062
  return {
7037
7063
  name: spec.name,
7038
7064
  expectedTarget: spec.target,
@@ -7061,7 +7087,7 @@ function applySymlinkPlan(repositoryRoot, plan) {
7061
7087
  const created = [];
7062
7088
  const failed = [];
7063
7089
  for (const { name, target } of plan.toCreate) {
7064
- const filePath = join12(real, name);
7090
+ const filePath = join11(real, name);
7065
7091
  try {
7066
7092
  mkdirSync(dirname7(filePath), { recursive: true });
7067
7093
  symlinkSync(target, filePath);
@@ -7095,7 +7121,7 @@ function gatherViewSymlinks(repositoryRoot, anchorReal, roster, viewDir) {
7095
7121
  if (!existsSync(canonicalFile)) return { kind: "missing-canonical", viewName };
7096
7122
  const files = expectedSymlinkTargets(viewDir, canonicalFile, "hub").map(
7097
7123
  (spec) => {
7098
- const { state, actualTarget } = inspectSymlink(join12(viewDir, spec.name), spec.target);
7124
+ const { state, actualTarget } = inspectSymlink(join11(viewDir, spec.name), spec.target);
7099
7125
  return {
7100
7126
  name: spec.name,
7101
7127
  expectedTarget: spec.target,
@@ -7111,7 +7137,7 @@ function applyViewSymlinks(viewDir, files) {
7111
7137
  const failed = [];
7112
7138
  for (const f of files) {
7113
7139
  if (f.state !== "missing") continue;
7114
- const filePath = join12(viewDir, f.name);
7140
+ const filePath = join11(viewDir, f.name);
7115
7141
  try {
7116
7142
  mkdirSync(dirname7(filePath), { recursive: true });
7117
7143
  symlinkSync(f.expectedTarget, filePath);
@@ -7333,7 +7359,7 @@ function resolveViewDir(repositoryRoot, viewPath) {
7333
7359
  return realpathSync(abs);
7334
7360
  } catch {
7335
7361
  try {
7336
- return join12(realpathSync(dirname7(abs)), basename7(abs));
7362
+ return join11(realpathSync(dirname7(abs)), basename7(abs));
7337
7363
  } catch {
7338
7364
  return abs;
7339
7365
  }
@@ -7351,7 +7377,7 @@ function gatherViewRepo(repositoryRoot, viewDir, entry) {
7351
7377
  return { path: entry.path, reachable: false };
7352
7378
  }
7353
7379
  const linkName = basename7(repoReal);
7354
- const { state, actualTarget } = inspectSymlink(join12(viewDir, linkName), expectedTarget);
7380
+ const { state, actualTarget } = inspectSymlink(join11(viewDir, linkName), expectedTarget);
7355
7381
  return {
7356
7382
  path: entry.path,
7357
7383
  reachable: true,
@@ -7365,7 +7391,7 @@ function applyViewPlan(viewDir, toCreate) {
7365
7391
  const created = [];
7366
7392
  const failed = [];
7367
7393
  for (const { name, target } of toCreate) {
7368
- const filePath = join12(viewDir, name);
7394
+ const filePath = join11(viewDir, name);
7369
7395
  try {
7370
7396
  mkdirSync(dirname7(filePath), { recursive: true });
7371
7397
  symlinkSync(target, filePath);
@@ -7380,7 +7406,7 @@ var TOP_LEVEL_INSTRUCTION_FILES_LOWER = new Set(
7380
7406
  INSTRUCTION_FILES.filter((f) => !f.includes("/")).map((f) => f.toLowerCase())
7381
7407
  );
7382
7408
  function classifyViewLink(viewDir, name, rosterRealpaths) {
7383
- const filePath = join12(viewDir, name);
7409
+ const filePath = join11(viewDir, name);
7384
7410
  let isLink;
7385
7411
  try {
7386
7412
  isLink = lstatSync(filePath).isSymbolicLink();
@@ -7409,7 +7435,7 @@ function classifyViewLink(viewDir, name, rosterRealpaths) {
7409
7435
  if (!isDir) {
7410
7436
  return { target, kind: existsSync(resolved) ? "non-repo" : "broken" };
7411
7437
  }
7412
- return { target, kind: existsSync(join12(resolved, ".git")) ? "repo" : "non-repo" };
7438
+ return { target, kind: existsSync(join11(resolved, ".git")) ? "repo" : "non-repo" };
7413
7439
  }
7414
7440
  function gatherExistingViewLinks(viewDir, rosterRealpaths) {
7415
7441
  let names;
@@ -7434,7 +7460,7 @@ function pruneViewLinks(viewDir, toPrune, rosterRealpaths) {
7434
7460
  const pruned = [];
7435
7461
  const failed = [];
7436
7462
  for (const { name } of toPrune) {
7437
- const filePath = join12(viewDir, name);
7463
+ const filePath = join11(viewDir, name);
7438
7464
  const c = classifyViewLink(viewDir, name, rosterRealpaths);
7439
7465
  if (c === null || c.kind !== "repo") {
7440
7466
  failed.push({
@@ -7647,10 +7673,10 @@ async function runProjectPreset(options, ctx = {}) {
7647
7673
  }
7648
7674
  }
7649
7675
  function canonicalFileFor(anchorReal, canonicalName) {
7650
- return join12(anchorReal, "agents", canonicalName, CANONICAL_FILE);
7676
+ return join11(anchorReal, "agents", canonicalName, CANONICAL_FILE);
7651
7677
  }
7652
7678
  function canonicalLabelFor(canonicalName) {
7653
- return join12("agents", canonicalName, CANONICAL_FILE);
7679
+ return join11("agents", canonicalName, CANONICAL_FILE);
7654
7680
  }
7655
7681
  async function gatherRepoPreset(repositoryRoot, anchorReal, entry) {
7656
7682
  const declared = {
@@ -7671,7 +7697,7 @@ async function gatherRepoPreset(repositoryRoot, anchorReal, entry) {
7671
7697
  if (real === anchorReal) {
7672
7698
  return { ...declared, isAnchor: true, reachable: true, canonicalPresent: false };
7673
7699
  }
7674
- if (!existsSync(join12(real, ".git"))) {
7700
+ if (!existsSync(join11(real, ".git"))) {
7675
7701
  return { ...declared, isAnchor: false, reachable: false, canonicalPresent: false };
7676
7702
  }
7677
7703
  const canonicalName = basename7(real);
@@ -8053,24 +8079,24 @@ function gatherArchiveTeardown(repositoryRoot, manifest, target) {
8053
8079
  const instructionFiles = [];
8054
8080
  for (const name of INSTRUCTION_FILES) {
8055
8081
  try {
8056
- lstatSync(join12(real, name));
8082
+ lstatSync(join11(real, name));
8057
8083
  instructionFiles.push(name);
8058
8084
  } catch {
8059
8085
  }
8060
8086
  }
8061
8087
  let ignored;
8062
8088
  try {
8063
- ignored = new Set(readGitignoreLines(join12(real, ".gitignore")).map((l) => l.trim()));
8089
+ ignored = new Set(readGitignoreLines(join11(real, ".gitignore")).map((l) => l.trim()));
8064
8090
  } catch {
8065
8091
  ignored = /* @__PURE__ */ new Set();
8066
8092
  }
8067
8093
  const gitignorePatterns = INSTRUCTION_FILES.filter((p) => ignored.has(p) || ignored.has(`/${p}`));
8068
- const canonical2 = existsSync(join12(anchorReal, "agents", canonicalName, CANONICAL_FILE));
8094
+ const canonical2 = existsSync(join11(anchorReal, "agents", canonicalName, CANONICAL_FILE));
8069
8095
  let viewLink = false;
8070
8096
  const viewPath = manifest.workspace.view;
8071
8097
  if (viewPath !== void 0) {
8072
8098
  try {
8073
- lstatSync(join12(resolveViewDir(repositoryRoot, viewPath), canonicalName));
8099
+ lstatSync(join11(resolveViewDir(repositoryRoot, viewPath), canonicalName));
8074
8100
  viewLink = true;
8075
8101
  } catch {
8076
8102
  }
@@ -8084,11 +8110,11 @@ function gatherArchiveTeardown(repositoryRoot, manifest, target) {
8084
8110
  };
8085
8111
  }
8086
8112
  function teardownExpectedTargets(repoReal, anchorReal, canonicalName) {
8087
- const canonicalFile = join12(anchorReal, "agents", canonicalName, CANONICAL_FILE);
8113
+ const canonicalFile = join11(anchorReal, "agents", canonicalName, CANONICAL_FILE);
8088
8114
  return expectedSymlinkTargets(repoReal, canonicalFile);
8089
8115
  }
8090
8116
  function viewLinkPointsAt(viewDir, name, repoReal) {
8091
- const filePath = join12(viewDir, name);
8117
+ const filePath = join11(viewDir, name);
8092
8118
  try {
8093
8119
  if (!lstatSync(filePath).isSymbolicLink()) return false;
8094
8120
  const target = readlinkSync(filePath);
@@ -8099,7 +8125,7 @@ function viewLinkPointsAt(viewDir, name, repoReal) {
8099
8125
  }
8100
8126
  }
8101
8127
  function viewLinkPointsAtPath(viewDir, name, expectedRepoPath) {
8102
- const filePath = join12(viewDir, name);
8128
+ const filePath = join11(viewDir, name);
8103
8129
  try {
8104
8130
  if (!lstatSync(filePath).isSymbolicLink()) return false;
8105
8131
  const target = readlinkSync(filePath);
@@ -8150,7 +8176,7 @@ function gatherRepoTeardown(repositoryRoot, manifest, target) {
8150
8176
  if (!isAnchor) {
8151
8177
  if (repoReal !== void 0) {
8152
8178
  for (const spec of teardownExpectedTargets(repoReal, anchorReal, canonicalName)) {
8153
- const { state, actualTarget } = inspectSymlink(join12(repoReal, spec.name), spec.target);
8179
+ const { state, actualTarget } = inspectSymlink(join11(repoReal, spec.name), spec.target);
8154
8180
  if (isSelf) {
8155
8181
  if (state !== "missing")
8156
8182
  items.push({
@@ -8187,7 +8213,7 @@ function gatherRepoTeardown(repositoryRoot, manifest, target) {
8187
8213
  }
8188
8214
  let ignored;
8189
8215
  try {
8190
- ignored = new Set(readGitignoreLines(join12(repoReal, ".gitignore")).map((l) => l.trim()));
8216
+ ignored = new Set(readGitignoreLines(join11(repoReal, ".gitignore")).map((l) => l.trim()));
8191
8217
  for (const p of INSTRUCTION_FILES) {
8192
8218
  if (ignored.has(p) || ignored.has(`/${p}`)) {
8193
8219
  items.push({
@@ -8210,7 +8236,7 @@ function gatherRepoTeardown(repositoryRoot, manifest, target) {
8210
8236
  const viewPath = manifest.workspace.view;
8211
8237
  if (viewPath !== void 0) {
8212
8238
  const viewDir = resolveViewDir(repositoryRoot, viewPath);
8213
- const linkPath = join12(viewDir, canonicalName);
8239
+ const linkPath = join11(viewDir, canonicalName);
8214
8240
  let isLink = false;
8215
8241
  try {
8216
8242
  isLink = lstatSync(linkPath).isSymbolicLink();
@@ -8236,8 +8262,8 @@ function gatherRepoTeardown(repositoryRoot, manifest, target) {
8236
8262
  else items.push({ kind: "view-symlink", label: canonicalName, state: "removable" });
8237
8263
  }
8238
8264
  }
8239
- const canonicalFile = join12(anchorReal, "agents", canonicalName, CANONICAL_FILE);
8240
- const canonicalLabel = join12("agents", canonicalName, CANONICAL_FILE);
8265
+ const canonicalFile = join11(anchorReal, "agents", canonicalName, CANONICAL_FILE);
8266
+ const canonicalLabel = join11("agents", canonicalName, CANONICAL_FILE);
8241
8267
  let canonicalIsLink = false;
8242
8268
  try {
8243
8269
  canonicalIsLink = lstatSync(canonicalFile).isSymbolicLink();
@@ -8341,12 +8367,12 @@ function applyRepoTeardown(repositoryRoot, manifest, plan) {
8341
8367
  );
8342
8368
  for (const item of removable.filter((i) => i.kind === "instruction-symlink")) {
8343
8369
  const expected = expectedByName.get(item.label);
8344
- if (repoReal === null || expected === void 0 || inspectSymlink(join12(repoReal, item.label), expected).state !== "correct") {
8370
+ if (repoReal === null || expected === void 0 || inspectSymlink(join11(repoReal, item.label), expected).state !== "correct") {
8345
8371
  changed(item.label);
8346
8372
  continue;
8347
8373
  }
8348
8374
  try {
8349
- unlinkSync(join12(repoReal, item.label));
8375
+ unlinkSync(join11(repoReal, item.label));
8350
8376
  removed.push(item.label);
8351
8377
  } catch (error) {
8352
8378
  failed.push({ label: item.label, message: failureReason(error) });
@@ -8365,7 +8391,7 @@ function applyRepoTeardown(repositoryRoot, manifest, plan) {
8365
8391
  continue;
8366
8392
  }
8367
8393
  try {
8368
- unlinkSync(join12(viewDir, item.label));
8394
+ unlinkSync(join11(viewDir, item.label));
8369
8395
  removed.push(`view/${item.label}`);
8370
8396
  } catch (error) {
8371
8397
  failed.push({ label: `view/${item.label}`, message: failureReason(error) });
@@ -8373,7 +8399,7 @@ function applyRepoTeardown(repositoryRoot, manifest, plan) {
8373
8399
  }
8374
8400
  const NOFOLLOW = fsConstants.O_NOFOLLOW ?? 0;
8375
8401
  for (const item of removable.filter((i) => i.kind === "canonical-block")) {
8376
- const canonicalFile = join12(anchorReal, "agents", canonicalName, CANONICAL_FILE);
8402
+ const canonicalFile = join11(anchorReal, "agents", canonicalName, CANONICAL_FILE);
8377
8403
  try {
8378
8404
  if (lstatSync(canonicalFile).isSymbolicLink()) {
8379
8405
  changed(item.label);
@@ -8647,12 +8673,12 @@ function gatherRenameWiring(repositoryRoot, manifest, oldBasename) {
8647
8673
  } catch {
8648
8674
  return { canonicalDirOld: false, viewLinkOld: false };
8649
8675
  }
8650
- const canonicalDirOld = existsSync(join12(anchorReal, "agents", oldBasename));
8676
+ const canonicalDirOld = existsSync(join11(anchorReal, "agents", oldBasename));
8651
8677
  let viewLinkOld = false;
8652
8678
  const viewPath = manifest.workspace.view;
8653
8679
  if (viewPath !== void 0) {
8654
8680
  try {
8655
- lstatSync(join12(resolveViewDir(repositoryRoot, viewPath), oldBasename));
8681
+ lstatSync(join11(resolveViewDir(repositoryRoot, viewPath), oldBasename));
8656
8682
  viewLinkOld = true;
8657
8683
  } catch {
8658
8684
  }
@@ -8997,7 +9023,7 @@ async function doRunProjectSeedAnchor(options, ctx) {
8997
9023
  console.log("\u2139\uFE0F No repo roster declared \u2014 nothing to seed.");
8998
9024
  return;
8999
9025
  }
9000
- const anchorDoc = join12(repositoryRoot, CANONICAL_FILE);
9026
+ const anchorDoc = join11(repositoryRoot, CANONICAL_FILE);
9001
9027
  if (pathPresent(anchorDoc)) {
9002
9028
  console.log(
9003
9029
  `\u2705 The anchor's own \`${CANONICAL_FILE}\` already exists \u2014 hand-maintained, left untouched.`
@@ -9059,7 +9085,7 @@ function regularFileSpokes(repoReal) {
9059
9085
  const out = [];
9060
9086
  for (const spoke of ["CLAUDE.md", ".github/copilot-instructions.md"]) {
9061
9087
  try {
9062
- const st = lstatSync(join12(repoReal, spoke));
9088
+ const st = lstatSync(join11(repoReal, spoke));
9063
9089
  if (!st.isSymbolicLink() && st.isFile()) out.push(spoke);
9064
9090
  } catch {
9065
9091
  }
@@ -9105,8 +9131,8 @@ function gatherRetrofit(repositoryRoot, anchorReal, roster, argAbs, argReal, vie
9105
9131
  };
9106
9132
  }
9107
9133
  const isAnchor = argReal === anchorReal;
9108
- const reachable = existsSync(join12(argReal, ".git"));
9109
- const canonicalFile = join12(anchorReal, "agents", canonicalName, CANONICAL_FILE);
9134
+ const reachable = existsSync(join11(argReal, ".git"));
9135
+ const canonicalFile = join11(anchorReal, "agents", canonicalName, CANONICAL_FILE);
9110
9136
  return {
9111
9137
  path,
9112
9138
  declared,
@@ -9115,13 +9141,13 @@ function gatherRetrofit(repositoryRoot, anchorReal, roster, argAbs, argReal, vie
9115
9141
  reachable,
9116
9142
  canonicalName,
9117
9143
  ...viewCanonicalName !== void 0 ? { viewCanonicalName } : {},
9118
- agentsState: inspectAgentsState(join12(argReal, CANONICAL_FILE)),
9144
+ agentsState: inspectAgentsState(join11(argReal, CANONICAL_FILE)),
9119
9145
  canonicalExists: pathPresent(canonicalFile),
9120
9146
  regularSpokes: regularFileSpokes(argReal)
9121
9147
  };
9122
9148
  }
9123
9149
  function relocateAgentsFile(repoReal, canonicalFile) {
9124
- const agentsFile = join12(repoReal, CANONICAL_FILE);
9150
+ const agentsFile = join11(repoReal, CANONICAL_FILE);
9125
9151
  try {
9126
9152
  mkdirSync(dirname7(canonicalFile), { recursive: true });
9127
9153
  } catch (error) {
@@ -9237,7 +9263,7 @@ async function doRunProjectRetrofit(repo, options, ctx) {
9237
9263
  let failure;
9238
9264
  let partial = false;
9239
9265
  if (options.apply === true && plan.action === "relocate" && argReal !== void 0) {
9240
- const canonicalFile = join12(anchorReal, "agents", plan.canonicalName, CANONICAL_FILE);
9266
+ const canonicalFile = join11(anchorReal, "agents", plan.canonicalName, CANONICAL_FILE);
9241
9267
  const res = relocateAgentsFile(argReal, canonicalFile);
9242
9268
  if (res.ok) {
9243
9269
  applied = true;
@@ -9629,7 +9655,7 @@ import { InvalidArgumentError as InvalidArgumentError4 } from "commander";
9629
9655
  // src/commands/refresh-watch.ts
9630
9656
  import { readdir as readdir3, stat as stat6 } from "fs/promises";
9631
9657
  import { homedir as homedir9 } from "os";
9632
- import { join as join13 } from "path";
9658
+ import { join as join12 } from "path";
9633
9659
  import {
9634
9660
  assertApprovalStoreSafe as assertApprovalStoreSafe2,
9635
9661
  assertLockStoreSafe as assertLockStoreSafe4,
@@ -9641,8 +9667,8 @@ var MIN_WATCH_INTERVAL_SEC = 5;
9641
9667
  var MAX_WATCH_INTERVAL_SEC = 86400;
9642
9668
  function watchedRoots(ctx) {
9643
9669
  return [
9644
- ctx.codexSessionsDir ?? join13(homedir9(), ".codex", "sessions"),
9645
- ctx.claudeProjectsDir ?? join13(homedir9(), ".claude", "projects")
9670
+ ctx.codexSessionsDir ?? join12(homedir9(), ".codex", "sessions"),
9671
+ ctx.claudeProjectsDir ?? join12(homedir9(), ".claude", "projects")
9646
9672
  ];
9647
9673
  }
9648
9674
  async function scanSourceLogs(roots) {
@@ -9656,7 +9682,7 @@ async function scanSourceLogs(roots) {
9656
9682
  throw new Error("Failed to read a source log directory", { cause: error });
9657
9683
  }
9658
9684
  for (const entry of entries) {
9659
- const full = join13(dir, entry.name);
9685
+ const full = join12(dir, entry.name);
9660
9686
  if (entry.isDirectory()) {
9661
9687
  await walk(full);
9662
9688
  } else if (entry.isFile() && entry.name.endsWith(".jsonl")) {
@@ -10502,7 +10528,7 @@ function unattachedLines(u) {
10502
10528
  // src/commands/run.ts
10503
10529
  import { mkdir as mkdir3, readFile as readFile6, rm as rm3 } from "fs/promises";
10504
10530
  import { homedir as homedir11 } from "os";
10505
- import { join as join14 } from "path";
10531
+ import { join as join13 } from "path";
10506
10532
  import {
10507
10533
  acquireLock as acquireLock5,
10508
10534
  assertBasouRootSafe as assertBasouRootSafe13,
@@ -10517,6 +10543,7 @@ import {
10517
10543
  findBasouSessionStartHook as findBasouSessionStartHook2,
10518
10544
  getDiff,
10519
10545
  getSnapshot as getSnapshot2,
10546
+ lstatSessionFile as lstatSessionFile2,
10520
10547
  overwriteYamlFile as overwriteYamlFile2,
10521
10548
  prefixedUlid as prefixedUlid4,
10522
10549
  readManifest as readManifest12,
@@ -10524,6 +10551,7 @@ import {
10524
10551
  resolveClaudeCodeCommand,
10525
10552
  resolveCodexCommand,
10526
10553
  SESSION_SCHEMA_VERSION as SESSION_SCHEMA_VERSION2,
10554
+ SessionFileNotAFileError as SessionFileNotAFileError4,
10527
10555
  SessionSchema as SessionSchema2,
10528
10556
  sanitizeRelatedFiles,
10529
10557
  sanitizeWorkingDirectory as sanitizeWorkingDirectory2,
@@ -10583,12 +10611,12 @@ async function runTrackedTool(args, options, ctx, adapter) {
10583
10611
  const manifest = await readManifest12(paths);
10584
10612
  await assertSessionStoreSafe3(paths);
10585
10613
  const sessionId = prefixedUlid4("ses");
10586
- const sessionDir = join14(paths.sessions, sessionId);
10614
+ const sessionDir = join13(paths.sessions, sessionId);
10587
10615
  const appendEvent = ctx.appendEvent ?? (async (_sessionDir, event) => {
10588
10616
  await coreAppendChainedEvent2(paths, sessionId, event);
10589
10617
  });
10590
10618
  const startedAt = now().toISOString();
10591
- const sessionYamlPath = join14(sessionDir, "session.yaml");
10619
+ const sessionYamlPath = join13(sessionDir, "session.yaml");
10592
10620
  const session = buildInitialSession2({
10593
10621
  id: sessionId,
10594
10622
  command,
@@ -10639,7 +10667,7 @@ async function runTrackedTool(args, options, ctx, adapter) {
10639
10667
  });
10640
10668
  const runningLock = await acquireLock5(paths, "session", sessionId);
10641
10669
  try {
10642
- await mutateSessionYaml2(sessionYamlPath, (s) => {
10670
+ await mutateSessionYaml2(paths, sessionId, (s) => {
10643
10671
  s.session.status = "running";
10644
10672
  });
10645
10673
  } finally {
@@ -10885,7 +10913,14 @@ function buildInitialSession2(input) {
10885
10913
  }
10886
10914
  };
10887
10915
  }
10888
- async function mutateSessionYaml2(filePath, mutator) {
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");
10889
10924
  const raw = await readYamlFile5(filePath);
10890
10925
  const parsed = SessionSchema2.parse(raw);
10891
10926
  mutator(parsed);
@@ -10953,7 +10988,7 @@ async function noteCodexHookStatusPreSpawn(_cwd, ctx) {
10953
10988
 
10954
10989
  // src/commands/session.ts
10955
10990
  import { readFile as readFile7 } from "fs/promises";
10956
- import { basename as basename8, isAbsolute as isAbsolute5, join as join15, relative as relative4, sep as sep3 } from "path";
10991
+ import { basename as basename8, isAbsolute as isAbsolute5, join as join14, relative as relative4, sep as sep3 } from "path";
10957
10992
  import {
10958
10993
  acquireLock as acquireLock6,
10959
10994
  appendEventToExistingSession as appendEventToExistingSession3,
@@ -10967,6 +11002,7 @@ import {
10967
11002
  importSessionFromJson as importSessionFromJson2,
10968
11003
  LOCAL_CLI_EVENT_SOURCE as LOCAL_CLI_EVENT_SOURCE4,
10969
11004
  loadSessionEntries as loadSessionEntries2,
11005
+ lstatSessionFile as lstatSessionFile3,
10970
11006
  normalizeSessionTimestamps,
10971
11007
  readAllEvents,
10972
11008
  readManifest as readManifest13,
@@ -10976,6 +11012,7 @@ import {
10976
11012
  resolveSessionId as resolveSessionId3,
10977
11013
  resolveTaskId,
10978
11014
  SESSION_IMPORT_SCHEMA_VERSION as SESSION_IMPORT_SCHEMA_VERSION2,
11015
+ SessionFileNotAFileError as SessionFileNotAFileError5,
10979
11016
  SessionImportPayloadSchema as SessionImportPayloadSchema2,
10980
11017
  SessionSchema as SessionSchema3,
10981
11018
  SessionStatusSchema,
@@ -11085,8 +11122,16 @@ async function doRunSessionShow(idInput, options, ctx) {
11085
11122
  const paths = basouPaths19(repositoryRoot);
11086
11123
  await assertWorkspaceInitialized12(paths.root);
11087
11124
  const sessionId = await resolveSessionId3(paths, idInput);
11088
- const sessionDir = join15(paths.sessions, sessionId);
11089
- const sessionYamlPath = join15(sessionDir, "session.yaml");
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
+ }
11090
11135
  let session;
11091
11136
  try {
11092
11137
  const raw = await readYamlFile6(sessionYamlPath);
@@ -11098,7 +11143,7 @@ async function doRunSessionShow(idInput, options, ctx) {
11098
11143
  throw new Error("Failed to read session", { cause: error });
11099
11144
  }
11100
11145
  let eventsLostLines = 0;
11101
- const events = await readAllEvents(sessionDir, {
11146
+ const events = await readAllEvents(paths, sessionId, {
11102
11147
  onWarning: (w) => {
11103
11148
  if (w.kind === "malformed_json" || w.kind === "schema_violation") eventsLostLines++;
11104
11149
  printReplayWarning(w, sessionId);
@@ -11616,7 +11661,7 @@ async function doRunSessionRechain(options, ctx) {
11616
11661
  }
11617
11662
  }
11618
11663
  const tamperedCount = rows.filter(
11619
- (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"
11620
11665
  ).length;
11621
11666
  const errorCount = rows.filter((r) => r.status === "error").length;
11622
11667
  if (options.json === true) {
@@ -11644,6 +11689,10 @@ function renderRechainRow(row, dryRun) {
11644
11689
  if (row.reason === "symlink" || row.reason === "not_a_directory") {
11645
11690
  return `skipped (${row.reason === "symlink" ? "SYMLINK" : "NOT A DIRECTORY"} \u2014 not followed; inspect with 'basou verify')`;
11646
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
+ }
11647
11696
  return `skipped (${row.reason})`;
11648
11697
  case "error":
11649
11698
  return `error (${row.message})`;
@@ -11852,7 +11901,6 @@ function formatVersionGateMessage(error) {
11852
11901
 
11853
11902
  // src/commands/task.ts
11854
11903
  import { readFile as readFile8 } from "fs/promises";
11855
- import { join as join16 } from "path";
11856
11904
  import {
11857
11905
  archiveTask,
11858
11906
  assertBasouRootSafe as assertBasouRootSafe17,
@@ -12177,9 +12225,8 @@ async function doRunTaskShow(idInput, options, ctx) {
12177
12225
  const events = [];
12178
12226
  const linkedSessionIds = new Set(doc.task.task.linked_sessions);
12179
12227
  for (const s of sessions) {
12180
- const sessionDir = join16(paths.sessions, s.sessionId);
12181
12228
  try {
12182
- for await (const ev of replayEvents3(sessionDir, {
12229
+ for await (const ev of replayEvents3(paths, s.sessionId, {
12183
12230
  onWarning: (w) => printReplayWarning(w, s.sessionId)
12184
12231
  })) {
12185
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) {
@@ -13072,7 +13119,7 @@ async function assertWorkspaceInitialized15(basouRoot) {
13072
13119
  // src/commands/view.ts
13073
13120
  import { spawn } from "child_process";
13074
13121
  import { createHash as createHash2 } from "crypto";
13075
- import { basename as basename11, dirname as dirname9, join as join20, resolve as resolve14 } from "path";
13122
+ import { basename as basename11, dirname as dirname9, join as join17, resolve as resolve14 } from "path";
13076
13123
  import {
13077
13124
  assertBasouRootSafe as assertBasouRootSafe19,
13078
13125
  basouPaths as basouPaths25,
@@ -13086,7 +13133,7 @@ import { InvalidArgumentError as InvalidArgumentError8 } from "commander";
13086
13133
  import { createReadStream as createReadStream2 } from "fs";
13087
13134
  import { readdir as readdir4, stat as stat7 } from "fs/promises";
13088
13135
  import { homedir as homedir12 } from "os";
13089
- import { basename as basename9, dirname as dirname8, join as join17 } from "path";
13136
+ import { basename as basename9, dirname as dirname8, join as join15 } from "path";
13090
13137
  import { createInterface as createInterface2 } from "readline";
13091
13138
  import { basouPaths as basouPaths24, readManifest as readManifest16 } from "@basou/core";
13092
13139
  function uncapturedTotal(result) {
@@ -13098,8 +13145,8 @@ async function checkPortfolioCoverage(workspaces, ctx = {}) {
13098
13145
  ctx.portfolioConfigPath
13099
13146
  );
13100
13147
  const listedDirs = new Set([...roots].map((root) => encodeProjectDir(root)));
13101
- const claudeProjectsDir = ctx.claudeProjectsDir ?? join17(homedir12(), ".claude", "projects");
13102
- const codexSessionsDir = ctx.codexSessionsDir ?? join17(homedir12(), ".codex", "sessions");
13148
+ const claudeProjectsDir = ctx.claudeProjectsDir ?? join15(homedir12(), ".claude", "projects");
13149
+ const codexSessionsDir = ctx.codexSessionsDir ?? join15(homedir12(), ".codex", "sessions");
13103
13150
  const tally = new Tally(roots);
13104
13151
  const absentTrees = [];
13105
13152
  const claudeFiles = await listClaudeTranscripts(claudeProjectsDir);
@@ -13237,7 +13284,7 @@ async function isDirEntry(parent, entry) {
13237
13284
  if (entry.isDirectory()) return true;
13238
13285
  if (!entry.isSymbolicLink()) return false;
13239
13286
  try {
13240
- return (await stat7(join17(parent, entry.name))).isDirectory();
13287
+ return (await stat7(join15(parent, entry.name))).isDirectory();
13241
13288
  } catch {
13242
13289
  return false;
13243
13290
  }
@@ -13252,7 +13299,7 @@ async function listClaudeTranscripts(projectsRoot) {
13252
13299
  const files = [];
13253
13300
  for (const entry of entries) {
13254
13301
  if (!await isDirEntry(projectsRoot, entry)) continue;
13255
- const full = join17(projectsRoot, entry.name);
13302
+ const full = join15(projectsRoot, entry.name);
13256
13303
  let names;
13257
13304
  try {
13258
13305
  names = await readdir4(full);
@@ -13260,7 +13307,7 @@ async function listClaudeTranscripts(projectsRoot) {
13260
13307
  continue;
13261
13308
  }
13262
13309
  for (const name of names) {
13263
- if (name.endsWith(".jsonl")) files.push(join17(full, name));
13310
+ if (name.endsWith(".jsonl")) files.push(join15(full, name));
13264
13311
  }
13265
13312
  }
13266
13313
  return files.sort();
@@ -13275,7 +13322,7 @@ async function listCodexRollouts(sessionsRoot) {
13275
13322
  return;
13276
13323
  }
13277
13324
  for (const entry of entries) {
13278
- const full = join17(dir, entry.name);
13325
+ const full = join15(dir, entry.name);
13279
13326
  if (entry.isDirectory()) await walk(full);
13280
13327
  else if (entry.isFile() && entry.name.startsWith("rollout-") && entry.name.endsWith(".jsonl")) {
13281
13328
  found.push(full);
@@ -13406,7 +13453,7 @@ function inertLines(result) {
13406
13453
  // src/lib/portfolio-safety.ts
13407
13454
  import { execFile as execFile2 } from "child_process";
13408
13455
  import { lstat as lstat3, realpath as realpath5 } from "fs/promises";
13409
- import { isAbsolute as isAbsolute6, join as join18, relative as relative5, resolve as resolve12, sep as sep4 } from "path";
13456
+ import { isAbsolute as isAbsolute6, join as join16, relative as relative5, resolve as resolve12, sep as sep4 } from "path";
13410
13457
  import { promisify as promisify2 } from "util";
13411
13458
  import { readManifest as readManifest17 } from "@basou/core";
13412
13459
  var execFileAsync2 = promisify2(execFile2);
@@ -13430,7 +13477,7 @@ function isBasouPath(p) {
13430
13477
  async function inspectRepo(repoPath) {
13431
13478
  let hasEntry = false;
13432
13479
  try {
13433
- await lstat3(join18(repoPath, ".basou"));
13480
+ await lstat3(join16(repoPath, ".basou"));
13434
13481
  hasEntry = true;
13435
13482
  } catch (error) {
13436
13483
  if (errorCode2(error) !== "ENOENT") {
@@ -13584,7 +13631,7 @@ function formatSafetyReport(result) {
13584
13631
 
13585
13632
  // src/lib/view-server.ts
13586
13633
  import { createServer } from "http";
13587
- import { basename as basename10, join as join19, resolve as resolve13 } from "path";
13634
+ import { basename as basename10, resolve as resolve13 } from "path";
13588
13635
  import {
13589
13636
  boardLivePage,
13590
13637
  boardPage,
@@ -13606,7 +13653,9 @@ import {
13606
13653
  renderDecisions as renderDecisions3,
13607
13654
  renderHandoff as renderHandoff3,
13608
13655
  resolveViewLanguageFromPaths,
13656
+ SessionFileNotAFileError as SessionFileNotAFileError6,
13609
13657
  SessionIdSchema,
13658
+ SessionNotADirectoryError,
13610
13659
  summarizeOrientation,
13611
13660
  tryRemoteUrl
13612
13661
  } from "@basou/core";
@@ -15609,10 +15658,7 @@ async function sessionDetail(ws, sessionId) {
15609
15658
  }
15610
15659
  const entry = await inspectSessionEntry2(ws.paths, sessionId);
15611
15660
  if (entry === "symlink" || entry === "not_a_directory") {
15612
- throw new HttpError(
15613
- 404,
15614
- `Session ${sessionId} is not a directory; a symlink or a file there is not followed`
15615
- );
15661
+ throw new HttpError(404, new SessionNotADirectoryError(sessionId, entry).message);
15616
15662
  }
15617
15663
  let session;
15618
15664
  try {
@@ -15621,10 +15667,11 @@ async function sessionDetail(ws, sessionId) {
15621
15667
  if (error instanceof Error && error.message === "YAML file not found") {
15622
15668
  throw new HttpError(404, "Session not found");
15623
15669
  }
15670
+ if (error instanceof SessionFileNotAFileError6) throw new HttpError(404, error.message);
15624
15671
  throw error;
15625
15672
  }
15626
15673
  try {
15627
- const events = await readAllEvents2(join19(ws.paths.sessions, sessionId));
15674
+ const events = await readAllEvents2(ws.paths, sessionId);
15628
15675
  return { session, events };
15629
15676
  } catch {
15630
15677
  return { session, events: [], degraded: true };
@@ -15889,7 +15936,7 @@ async function viewBoardOf(root, paths) {
15889
15936
  own = void 0;
15890
15937
  }
15891
15938
  if (own?.visibility !== "private") return { why: "no_board" };
15892
- return { recordsDir: join20(root, dirname9(DEFAULT_BOARD_PATH), "records") };
15939
+ return { recordsDir: join17(root, dirname9(DEFAULT_BOARD_PATH), "records") };
15893
15940
  }
15894
15941
  async function buildPortfolioDeps(workspaceFlags, ctx, cwd) {
15895
15942
  const specs = workspaceFlags.length > 0 ? workspaceFlags.map((p) => ({ path: resolve14(cwd, p) })) : await loadPortfolioConfig6(ctx.portfolioConfigPath);
@@ -16043,7 +16090,7 @@ function installExperimentalNotice(program2, names = EXPERIMENTAL_COMMANDS, writ
16043
16090
  function readBuildStamp() {
16044
16091
  if (false) return void 0;
16045
16092
  try {
16046
- return JSON.parse('{"version":"0.69.0","commit":"bffcfee","committedAt":"2026-10-16T08:07:08+09:00"}');
16093
+ return JSON.parse('{"version":"0.70.0","commit":"60d302a","committedAt":"2026-10-16T17:39:30+09:00"}');
16047
16094
  } catch {
16048
16095
  return void 0;
16049
16096
  }