@basou/cli 0.67.0 → 0.68.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
@@ -830,33 +830,190 @@ function printNoApprovals(options) {
830
830
  }
831
831
 
832
832
  // src/commands/board.ts
833
- import { lstat, readFile as readFile2, realpath as realpath2 } from "fs/promises";
834
- import { basename as basename3, dirname as dirname3, join as join3, relative, resolve as resolve3, sep } from "path";
833
+ import { lstat, readdir as readdir2, readFile as readFile2, realpath as realpath3 } from "fs/promises";
834
+ import { basename as basename4, dirname as dirname5, join as join4, relative, resolve as resolve3, sep as sep2 } from "path";
835
835
  import {
836
836
  assertBasouRootSafe as assertBasouRootSafe3,
837
+ BOARD_INIT_SAVE,
837
838
  basouPaths as basouPaths4,
839
+ boardGuide,
840
+ boardInitText,
838
841
  buildRecord,
839
842
  byCodePoint,
840
843
  diffCells,
841
844
  diffObserved,
842
845
  displayPath as displayPath3,
846
+ effortStartOf,
843
847
  findErrorCode as findErrorCode3,
848
+ isBoardTimeZone,
844
849
  measureBoard,
845
850
  measureBoardLive,
846
851
  NO_PREVIOUS_RECORDS,
847
852
  parseBoardDeclaration,
848
853
  parseRecordInput,
849
- readManifest as readManifest3,
854
+ readManifest as readManifest4,
850
855
  readPreviousRecords,
856
+ recordIds,
857
+ resolveViewLanguage,
858
+ shellWord,
851
859
  writeRecord
852
860
  } from "@basou/core";
853
861
 
862
+ // src/lib/foreign-workspace-warn.ts
863
+ import { realpath as realpath2 } from "fs/promises";
864
+ import { dirname } from "path";
865
+ import {
866
+ isPositionBoardLine,
867
+ loadPortfolioConfig as loadPortfolioConfig2,
868
+ PortfolioConfigMissingError,
869
+ readMarkdownFile
870
+ } from "@basou/core";
871
+
872
+ // src/lib/foreign-workspace-scan.ts
873
+ import { homedir as osHomedir } from "os";
874
+ import { basename as basename2, normalize, sep } from "path";
875
+ var MIN_TOKEN_LENGTH = 4;
876
+ function stripTrailingSep(p) {
877
+ return p.length > 1 && p.endsWith(sep) ? p.slice(0, -sep.length) : p;
878
+ }
879
+ function toTildePath(absPath, home) {
880
+ if (absPath === home) return "~";
881
+ if (absPath.startsWith(home + sep)) return `~${absPath.slice(home.length)}`;
882
+ return null;
883
+ }
884
+ function encodedSpelling(name) {
885
+ return name.replace(/[^a-zA-Z0-9]/g, "-");
886
+ }
887
+ function directoryNameSpellings(dir) {
888
+ const paired = /^(.+)-(planning|workspace)$/.exec(dir);
889
+ const plain = paired !== null ? [`${paired[1]}-planning`, `${paired[1]}-workspace`] : [dir, `${dir}-workspace`];
890
+ return [...plain, ...plain.map(encodedSpelling)];
891
+ }
892
+ function nameTokensFor(workspacePath, home) {
893
+ const abs = stripTrailingSep(normalize(workspacePath));
894
+ const tokens = /* @__PURE__ */ new Set([abs]);
895
+ const tilde = toTildePath(abs, home);
896
+ if (tilde !== null) tokens.add(tilde);
897
+ for (const name of directoryNameSpellings(basename2(abs))) tokens.add(name);
898
+ return [...tokens].filter((t) => t.length >= MIN_TOKEN_LENGTH);
899
+ }
900
+ function isSpellingOfSelf(workspacePath, selfPath) {
901
+ const own = new Set(directoryNameSpellings(basename2(stripTrailingSep(normalize(selfPath)))));
902
+ return directoryNameSpellings(basename2(stripTrailingSep(normalize(workspacePath)))).some(
903
+ (s) => own.has(s)
904
+ );
905
+ }
906
+ function scanForeignWorkspaceNames(input) {
907
+ if (input.text.length === 0 || input.workspacePaths.length === 0) return [];
908
+ const home = input.homedir ?? osHomedir();
909
+ const own = input.selfPath === void 0 ? [] : nameTokensFor(input.selfPath, home);
910
+ const lines = input.text.split("\n");
911
+ const hits = [];
912
+ for (const workspacePath of input.workspacePaths) {
913
+ if (input.selfPath !== void 0 && isSpellingOfSelf(workspacePath, input.selfPath)) continue;
914
+ const tokens = nameTokensFor(workspacePath, home).filter(
915
+ (token) => !own.some((ownToken) => ownToken.includes(token))
916
+ );
917
+ if (tokens.length === 0) continue;
918
+ const matched = /* @__PURE__ */ new Set();
919
+ const matchedLines = [];
920
+ for (const [index, line] of lines.entries()) {
921
+ let lineMatched = false;
922
+ for (const token of tokens) {
923
+ if (line.includes(token)) {
924
+ matched.add(token);
925
+ lineMatched = true;
926
+ }
927
+ }
928
+ if (lineMatched) matchedLines.push(index + 1);
929
+ }
930
+ if (matchedLines.length === 0) continue;
931
+ hits.push({
932
+ workspacePath,
933
+ tokens: [...matched].sort((a, b) => b.length - a.length || a.localeCompare(b)),
934
+ lines: matchedLines
935
+ });
936
+ }
937
+ return hits;
938
+ }
939
+
940
+ // src/lib/foreign-workspace-warn.ts
941
+ var MAX_LISTED_LINES = 5;
942
+ async function canonicalize(path) {
943
+ try {
944
+ return await realpath2(path);
945
+ } catch {
946
+ return path;
947
+ }
948
+ }
949
+ async function findForeignWorkspaceNames(args) {
950
+ let workspacePaths;
951
+ let selfPath;
952
+ try {
953
+ const workspaces = await loadPortfolioConfig2(args.configPath);
954
+ workspacePaths = await Promise.all(workspaces.map((w) => canonicalize(w.path)));
955
+ selfPath = args.selfPath === void 0 ? void 0 : await canonicalize(args.selfPath);
956
+ } catch {
957
+ return null;
958
+ }
959
+ const text = args.text.split("\n").map((line) => isPositionBoardLine(line) ? "" : line).join("\n");
960
+ const hits = scanForeignWorkspaceNames({ text, workspacePaths, selfPath });
961
+ if (hits.length === 0) return null;
962
+ const lines = [...new Set(hits.flatMap((h) => h.lines))].sort((a, b) => a - b);
963
+ return { workspaceCount: hits.length, lines };
964
+ }
965
+ async function findForeignWorkspaceFields(args) {
966
+ let workspacePaths;
967
+ let selfPath;
968
+ try {
969
+ const workspaces = await loadPortfolioConfig2(args.configPath);
970
+ workspacePaths = await Promise.all(workspaces.map((w) => canonicalize(w.path)));
971
+ selfPath = await canonicalize(args.selfPath);
972
+ } catch (error) {
973
+ return error instanceof PortfolioConfigMissingError ? { status: "no_registry" } : { status: "unreadable" };
974
+ }
975
+ const at = args.fields.filter(
976
+ (field) => scanForeignWorkspaceNames({ text: field.text, workspacePaths, selfPath }).length > 0
977
+ ).map((field) => field.at);
978
+ return { status: "checked", at };
979
+ }
980
+ function describeForeignWorkspaceLines(lines) {
981
+ const listed = lines.slice(0, MAX_LISTED_LINES).join(", ");
982
+ const rest = lines.length - MAX_LISTED_LINES;
983
+ const numbers = rest > 0 ? `${listed} and ${rest} more` : listed;
984
+ return `${lines.length === 1 ? "line" : "lines"} ${numbers}`;
985
+ }
986
+ function positionForeignWorkspaceWarning(report, where) {
987
+ const subject = report.workspaceCount === 1 ? "names another registered workspace" : `names ${report.workspaceCount} other registered workspaces`;
988
+ return `basou: this workspace's position ${subject} (${where}, ${describeForeignWorkspaceLines(report.lines)}). The position is handed to the agent session running in this workspace, so those names travel with it. Advisory only: nothing was withheld.`;
989
+ }
990
+ function protocolForeignWorkspaceWarning(report) {
991
+ const subject = report.workspaceCount === 1 ? "names a registered workspace" : `names ${report.workspaceCount} registered workspaces`;
992
+ return `basou: the protocol block ${subject} (block ${describeForeignWorkspaceLines(report.lines)}, counted from the block's first line, not the file's). Standing protocols are written to the user-global CLAUDE.md, which every project on this machine loads, so a workspace-specific name there reaches every workspace's sessions. Only the basou-managed block is checked; the rest of that file is not basou's to inspect. Advisory only: nothing was withheld.`;
993
+ }
994
+ async function warnIfPositionNamesOtherWorkspaces(args) {
995
+ try {
996
+ const body = await readMarkdownFile(args.paths.files.orientation);
997
+ if (body === null) return;
998
+ const report = await findForeignWorkspaceNames({
999
+ text: body,
1000
+ selfPath: dirname(args.paths.root),
1001
+ configPath: args.configPath
1002
+ });
1003
+ if (report !== null) {
1004
+ console.error(positionForeignWorkspaceWarning(report, args.paths.files.orientation));
1005
+ }
1006
+ } catch {
1007
+ }
1008
+ }
1009
+
854
1010
  // src/lib/provenance-actions.ts
1011
+ import { dirname as dirname4 } from "path";
855
1012
  import {
856
1013
  assertApprovalStoreSafe,
857
1014
  assertLockStoreSafe as assertLockStoreSafe3,
858
1015
  assertTaskStoreSafe,
859
- readMarkdownFile,
1016
+ readMarkdownFile as readMarkdownFile2,
860
1017
  renderDecisions,
861
1018
  renderHandoff,
862
1019
  renderOrientation,
@@ -868,7 +1025,7 @@ import {
868
1025
  import { createReadStream } from "fs";
869
1026
  import { readdir, readFile, rm, stat as stat2 } from "fs/promises";
870
1027
  import { homedir } from "os";
871
- import { basename as basename2, dirname as dirname2, join as join2, resolve as resolve2 } from "path";
1028
+ import { basename as basename3, dirname as dirname3, join as join2, resolve as resolve2 } from "path";
872
1029
  import { createInterface } from "readline";
873
1030
  import {
874
1031
  AGENT_INFRA_DIRS,
@@ -895,7 +1052,7 @@ import {
895
1052
  } from "@basou/core";
896
1053
 
897
1054
  // src/lib/observation-warn.ts
898
- import { dirname } from "path";
1055
+ import { dirname as dirname2 } from "path";
899
1056
  import { assertObservationsDirSafe, displayPath } from "@basou/core";
900
1057
  async function warnIfObservationsRefused(paths) {
901
1058
  try {
@@ -903,7 +1060,7 @@ async function warnIfObservationsRefused(paths) {
903
1060
  } catch (error) {
904
1061
  const reason = error instanceof Error ? error.message : String(error);
905
1062
  console.error(
906
- `basou: ${reason} (in ${displayPath(dirname(paths.root))}). The files a session changes through the shell are not observed there, so a session imported now records only the files its transcript names.`
1063
+ `basou: ${reason} (in ${displayPath(dirname2(paths.root))}). The files a session changes through the shell are not observed there, so a session imported now records only the files its transcript names.`
907
1064
  );
908
1065
  }
909
1066
  }
@@ -980,7 +1137,7 @@ async function doRunImportClaudeCode(options, ctx) {
980
1137
  const files = await selectTranscriptFiles(projectsRoot, projectPaths, options);
981
1138
  const projectSet = new Set(projectPaths);
982
1139
  const candidates = files.map((file) => {
983
- const externalId = basename2(file, ".jsonl");
1140
+ const externalId = basename3(file, ".jsonl");
984
1141
  return {
985
1142
  externalId,
986
1143
  sourcePath: file,
@@ -1079,7 +1236,7 @@ async function importDerivedSessions(paths, manifest, options, sourceKind, candi
1079
1236
  files: payload.session.related_files ?? [],
1080
1237
  workingDirectory: payload.session.working_directory,
1081
1238
  sourceRoots: projectPaths,
1082
- masterRoot: dirname2(paths.root),
1239
+ masterRoot: dirname3(paths.root),
1083
1240
  extraInRoot: AGENT_INFRA_DIRS
1084
1241
  });
1085
1242
  if (scope.outOfRoot.length > 0) crossProject.push({ externalId, outOfRoot: scope.outOfRoot });
@@ -1517,6 +1674,19 @@ async function assertWorkspaceInitialized2(basouRoot) {
1517
1674
  }
1518
1675
  }
1519
1676
 
1677
+ // src/lib/board-glance.ts
1678
+ import { join as join3 } from "path";
1679
+ import { glanceBoard, readManifest as readManifest3 } from "@basou/core";
1680
+ async function glanceDefaultBoard(root, paths) {
1681
+ try {
1682
+ const own = (await readManifest3(paths)).repos?.find((repo) => repo.path === ".");
1683
+ if (own?.visibility !== "private") return null;
1684
+ } catch {
1685
+ return null;
1686
+ }
1687
+ return glanceBoard(join3(root, DEFAULT_BOARD_PATH));
1688
+ }
1689
+
1520
1690
  // src/lib/provenance-actions.ts
1521
1691
  var capturing = Promise.resolve();
1522
1692
  function captureImportJson(fn) {
@@ -1601,7 +1771,7 @@ function importCodex(options, ctx) {
1601
1771
  }
1602
1772
  async function regenerateHandoff(paths, nowIso, callbacks) {
1603
1773
  const result = await renderHandoff({ paths, nowIso, ...callbacks });
1604
- const existing = await readMarkdownFile(paths.files.handoff);
1774
+ const existing = await readMarkdownFile2(paths.files.handoff);
1605
1775
  await writeMarkdownFile(
1606
1776
  paths.files.handoff,
1607
1777
  renderWithMarkers(existing, result.body, "handoff.md")
@@ -1615,7 +1785,7 @@ async function regenerateHandoff(paths, nowIso, callbacks) {
1615
1785
  }
1616
1786
  async function regenerateDecisions(paths, nowIso, callbacks) {
1617
1787
  const result = await renderDecisions({ paths, nowIso, ...callbacks });
1618
- const existing = await readMarkdownFile(paths.files.decisions);
1788
+ const existing = await readMarkdownFile2(paths.files.decisions);
1619
1789
  await writeMarkdownFile(
1620
1790
  paths.files.decisions,
1621
1791
  renderWithMarkers(existing, result.body, "decisions.md")
@@ -1623,7 +1793,12 @@ async function regenerateDecisions(paths, nowIso, callbacks) {
1623
1793
  return { decisionCount: result.decisionCount };
1624
1794
  }
1625
1795
  async function regenerateOrientation(paths, nowIso, callbacks) {
1626
- const result = await renderOrientation({ paths, nowIso, ...callbacks });
1796
+ const result = await renderOrientation({
1797
+ paths,
1798
+ nowIso,
1799
+ board: await glanceDefaultBoard(dirname4(paths.root), paths),
1800
+ ...callbacks
1801
+ });
1627
1802
  await writeMarkdownFile(paths.files.orientation, `${result.body}
1628
1803
  `);
1629
1804
  return {
@@ -1741,8 +1916,9 @@ read (nothing is printed on stdout).`
1741
1916
  "after",
1742
1917
  `
1743
1918
  The input is a JSON object: measure_digest (the digest of the measurement the
1744
- judgement saw), observed, cells (every lane at every stage), prose, judged_by
1745
- and axis_review. The board is measured again, and nothing is written when the
1919
+ judgement saw), observed (exactly the observations the board declares, when it
1920
+ declares any), cells (every lane at every stage), prose, judged_by and
1921
+ axis_review. The board is measured again, and nothing is written when the
1746
1922
  digest differs. Records are never written under a .basou/ directory. Exit
1747
1923
  codes: 0 when the record was written, or with --dry-run when everything
1748
1924
  checked out (nothing is written then); 1 when it was refused (nothing is
@@ -1750,6 +1926,191 @@ written; the reasons are on stderr).`
1750
1926
  ).action(async (options) => {
1751
1927
  await runBoardRecord(options);
1752
1928
  });
1929
+ board.command("guide").description(
1930
+ "Print the steps an agent follows to judge the board and record it (writes nothing, sends nothing)"
1931
+ ).option("-v, --verbose", "Show error causes").addHelpText(
1932
+ "after",
1933
+ `
1934
+ The steps are filled in with this workspace's board (${DEFAULT_BOARD_PATH},
1935
+ read only when the manifest declares the workspace's own repo private): where
1936
+ it is and its last record, the commands that measure, observe, record and show
1937
+ it, what each stage is judged by, and the record's input to fill in, with the
1938
+ previous record's state of each cell beside it. With no board declared yet, it
1939
+ says how to declare one. The text is for an agent to read; its shape may change
1940
+ at any release. Exit codes: 0 when the steps were printed; 1 when the workspace,
1941
+ its manifest or its board.yaml cannot be read, or its own repo is not declared
1942
+ private (the reason is on stderr).`
1943
+ ).action(async (options) => {
1944
+ await runBoardGuide(options);
1945
+ });
1946
+ board.command("init").description("Print a board.yaml to start a board from (writes nothing)").option("-v, --verbose", "Show error causes").addHelpText(
1947
+ "after",
1948
+ `
1949
+ It prints a declaration of the newest board_version, in the language the
1950
+ manifest declares for the workspace's own repo: a title from the workspace's
1951
+ name, the six stages with a meaning each, one lane to cut again, nothing to
1952
+ observe, no component registered, an axis of version 1, and the effort from
1953
+ the day of the first session, in this host's time zone. It writes nothing:
1954
+ save it, from the workspace's own repo, only where no board is, through a file
1955
+ of its own:
1956
+
1957
+ ${INIT_SAVE}
1958
+
1959
+ Never print it straight into ${DEFAULT_BOARD_PATH}: the shell empties that file
1960
+ before init runs, so a board already there would be lost.
1961
+
1962
+ Exit codes: 0 when it was printed; 1 when the manifest does not declare the
1963
+ workspace's own repo private (a board's records hold what the trail holds),
1964
+ when ${DEFAULT_BOARD_PATH} is already there, or when the workspace cannot be
1965
+ read (the reason is on stderr, and nothing is printed on stdout).`
1966
+ ).action(async (options) => {
1967
+ await runBoardInit(options);
1968
+ });
1969
+ }
1970
+ var INIT_SAVE = BOARD_INIT_SAVE;
1971
+ async function runBoardInit(options, ctx = {}) {
1972
+ try {
1973
+ await doRunBoardInit(options, ctx);
1974
+ } catch (error) {
1975
+ renderCliError(error, { verbose: isVerbose(options) });
1976
+ process.exitCode = 1;
1977
+ }
1978
+ }
1979
+ async function doRunBoardInit(_options, ctx) {
1980
+ const { root, paths, manifest } = await privateWorkspace(ctx, "board init");
1981
+ const cwd = ctx.cwd ?? process.cwd();
1982
+ if (await realpath3(cwd).catch(() => cwd) !== await realpath3(root).catch(() => root)) {
1983
+ throw new Error(
1984
+ `Run basou board init from the top of the workspace's own repo, where its .basou/ is: the way to save what it prints writes ${DEFAULT_BOARD_PATH} in the current directory.`
1985
+ );
1986
+ }
1987
+ const board = join4(root, DEFAULT_BOARD_PATH);
1988
+ try {
1989
+ await lstat(board);
1990
+ throw new Error(
1991
+ `${DEFAULT_BOARD_PATH} is already there, so there is no board to start: change that one (through an axis review when it changes the lanes or the stages).`
1992
+ );
1993
+ } catch (error) {
1994
+ if (!findErrorCode3(error, "ENOENT")) throw error;
1995
+ }
1996
+ const { start, timeZone, sessionsRead } = await effortStartOf(
1997
+ paths,
1998
+ ctx.nowProvider?.() ?? /* @__PURE__ */ new Date()
1999
+ );
2000
+ if (timeZone === void 0) {
2001
+ console.error(
2002
+ "basou: this host's time zone has no name, so effort.start is today in UTC and effort.time_zone is left out; write both."
2003
+ );
2004
+ } else if (!sessionsRead) {
2005
+ console.error(
2006
+ "basou: the sessions could not be read, so effort.start is today; set it to the day the work began."
2007
+ );
2008
+ } else if (!isBoardTimeZone(timeZone)) {
2009
+ console.error(
2010
+ "basou: this host's time zone has no name a board can declare, so effort.time_zone is left out; write one."
2011
+ );
2012
+ }
2013
+ const text = boardInitText({
2014
+ name: manifest.workspace.name,
2015
+ language: resolveViewLanguage(manifest),
2016
+ start,
2017
+ startIsFirstSession: timeZone !== void 0,
2018
+ // An offset such as +00:00, which some hosts name their zone, is not
2019
+ // one a board can declare.
2020
+ timeZone: timeZone !== void 0 && isBoardTimeZone(timeZone) ? timeZone : void 0
2021
+ });
2022
+ process.stdout.write(text);
2023
+ return text;
2024
+ }
2025
+ async function privateWorkspace(ctx, command) {
2026
+ const cwd = ctx.cwd ?? process.cwd();
2027
+ const root = await resolveBasouRootForCommand(
2028
+ cwd,
2029
+ command,
2030
+ ctx.portfolioConfigPath === void 0 ? {} : { portfolioConfigPath: ctx.portfolioConfigPath }
2031
+ );
2032
+ const paths = basouPaths4(root);
2033
+ await assertWorkspaceInitialized3(paths.root);
2034
+ const manifest = await readManifest4(paths);
2035
+ const own = (manifest.repos ?? []).find((repo) => repo.path === ".");
2036
+ if (own?.visibility !== "private") {
2037
+ throw new Error(
2038
+ `The board is kept at ${DEFAULT_BOARD_PATH} only in a workspace whose manifest declares its own repo (path: .) private, since a record holds what the trail holds (open tracks, time worked, model names). This one does not, so it has no judged board; basou view's /board page shows what is measured without one.`
2039
+ );
2040
+ }
2041
+ return { root, paths, manifest };
2042
+ }
2043
+ async function runBoardGuide(options, ctx = {}) {
2044
+ try {
2045
+ await doRunBoardGuide(options, ctx);
2046
+ } catch (error) {
2047
+ renderCliError(error, { verbose: isVerbose(options) });
2048
+ process.exitCode = 1;
2049
+ }
2050
+ }
2051
+ async function doRunBoardGuide(_options, ctx) {
2052
+ const { root, manifest } = await privateWorkspace(ctx, "board guide");
2053
+ const board = {
2054
+ path: join4(root, DEFAULT_BOARD_PATH),
2055
+ shown: DEFAULT_BOARD_PATH,
2056
+ given: false
2057
+ };
2058
+ let text;
2059
+ try {
2060
+ text = await readDeclaration(board);
2061
+ } catch (error) {
2062
+ if (!findErrorCode3(error, "ENOENT")) throw error;
2063
+ if (await lstat(board.path).then(
2064
+ () => true,
2065
+ () => false
2066
+ )) {
2067
+ throw new Error(
2068
+ `${DEFAULT_BOARD_PATH} is there but cannot be read (a link to nothing?). Fix it before the board is updated.`
2069
+ );
2070
+ }
2071
+ text = null;
2072
+ }
2073
+ const otherRepos = (manifest.repos ?? []).map((repo) => repo.path).filter((p) => p !== ".");
2074
+ if ([root, ...otherRepos].some((path) => displayPath3(path) !== path)) {
2075
+ throw new Error(
2076
+ "The workspace's path, or a repo path the manifest declares, holds a control character, which the guide's commands cannot carry safely. Rename it, or follow the steps by hand."
2077
+ );
2078
+ }
2079
+ const common = {
2080
+ // As basou resolves it, the root measure resolves the repos' paths against.
2081
+ anchor: root,
2082
+ otherRepos,
2083
+ language: resolveViewLanguage(manifest),
2084
+ basouCommand: ctx.basouCommand ?? [process.execPath, process.argv[1] ?? ""].filter((part) => part !== "").map(shellWord).join(" "),
2085
+ basouVersion: BASOU_CLI_VERSION,
2086
+ now: ctx.nowProvider?.() ?? /* @__PURE__ */ new Date()
2087
+ };
2088
+ let guide;
2089
+ if (text === null) {
2090
+ guide = boardGuide({ ...common, board: { status: "undeclared" } });
2091
+ } else {
2092
+ const parsed = parseBoardDeclaration(text, {
2093
+ manifestRepoPaths: (manifest.repos ?? []).map((repo) => repo.path)
2094
+ });
2095
+ if (!parsed.ok) {
2096
+ throw new Error(
2097
+ `${DEFAULT_BOARD_PATH} is not a valid board declaration, so there are no steps to print until it reads (fix it through an axis review when it changes the lanes or the stages):
2098
+ ${parsed.errors.map((e) => ` - ${displayPath3(e)}`).join("\n")}`
2099
+ );
2100
+ }
2101
+ const recordsDir = join4(dirname5(board.path), "records");
2102
+ guide = boardGuide({
2103
+ ...common,
2104
+ board: {
2105
+ status: "declared",
2106
+ declaration: parsed.declaration,
2107
+ recordCount: recordIds(await readdir2(recordsDir).catch(() => [])).length,
2108
+ previous: await readPreviousRecords(recordsDir)
2109
+ }
2110
+ });
2111
+ }
2112
+ console.log(guide.trimEnd());
2113
+ return guide;
1753
2114
  }
1754
2115
  async function runBoardMeasure(options, ctx = {}) {
1755
2116
  try {
@@ -1796,8 +2157,8 @@ Nothing was written.`, { cause: error.cause });
1796
2157
  async function checkAndRecord(options, ctx) {
1797
2158
  const cwd = ctx.cwd ?? process.cwd();
1798
2159
  const loaded = await loadBoard(options, ctx, "board record");
1799
- const beside = (...parts) => loaded.board.given ? join3(dirname3(loaded.board.shown), ...parts) : relative(cwd, join3(dirname3(loaded.board.path), ...parts));
1800
- const recordsDir = join3(dirname3(loaded.board.path), "records");
2160
+ const beside = (...parts) => loaded.board.given ? join4(dirname5(loaded.board.shown), ...parts) : relative(cwd, join4(dirname5(loaded.board.path), ...parts));
2161
+ const recordsDir = join4(dirname5(loaded.board.path), "records");
1801
2162
  await checkRecordsPlace(loaded, recordsDir, beside("records"), options.notPrivate === true);
1802
2163
  const text = await readRecordInput(options, ctx);
1803
2164
  let value;
@@ -1808,6 +2169,7 @@ async function checkAndRecord(options, ctx) {
1808
2169
  cause: error
1809
2170
  });
1810
2171
  }
2172
+ await refuseOtherWorkspaces(value, loaded.root, ctx);
1811
2173
  const parsed = parseRecordInput(value, loaded.declaration);
1812
2174
  if (!parsed.ok) {
1813
2175
  throw new RecordRefusal(
@@ -1864,11 +2226,90 @@ ${parsed.errors.map((e) => ` - ${displayPath3(e)}`).join("\n")}`
1864
2226
  diff
1865
2227
  };
1866
2228
  }
2229
+ var INPUT_KEYS = /* @__PURE__ */ new Set([
2230
+ "measure_digest",
2231
+ "observed",
2232
+ "cells",
2233
+ "prose",
2234
+ "judged_by",
2235
+ "axis_review",
2236
+ "value",
2237
+ "observed_at",
2238
+ "source",
2239
+ "error",
2240
+ "lane",
2241
+ "stage",
2242
+ "state",
2243
+ "reason",
2244
+ "summary",
2245
+ "lanes",
2246
+ "operator_turns",
2247
+ "footnotes",
2248
+ "text",
2249
+ "model",
2250
+ "self_reported",
2251
+ "triggers"
2252
+ ]);
2253
+ var SHAPED_PLACES = [
2254
+ /^measure_digest$/,
2255
+ /^observed \(a named entry\)\.observed_at$/,
2256
+ /^cells\[\d+\]\.state$/,
2257
+ /^axis_review\.triggers$/
2258
+ ];
2259
+ var NAMED_MAPS = /* @__PURE__ */ new Set(["observed", "prose.lanes"]);
2260
+ function inputTexts(value) {
2261
+ const out = [];
2262
+ const stack = [{ value, at: "" }];
2263
+ for (let next = stack.pop(); next !== void 0; next = stack.pop()) {
2264
+ const { value: v, at } = next;
2265
+ if (SHAPED_PLACES.some((place) => place.test(at))) continue;
2266
+ if (typeof v === "string" || typeof v === "number") {
2267
+ out.push({ at: at === "" ? "(top level)" : at, text: String(v) });
2268
+ } else if (Array.isArray(v)) {
2269
+ for (let i = v.length - 1; i >= 0; i--) stack.push({ value: v[i], at: `${at}[${i}]` });
2270
+ } else if (typeof v === "object" && v !== null) {
2271
+ const named = NAMED_MAPS.has(at);
2272
+ const where = at === "" ? "(top level)" : at;
2273
+ const children = [];
2274
+ for (const [k, child] of Object.entries(v)) {
2275
+ if (!named && INPUT_KEYS.has(k)) {
2276
+ children.push({ value: child, at: at === "" ? k : `${at}.${k}` });
2277
+ } else {
2278
+ out.push({ at: `${where}: a name`, text: k });
2279
+ children.push({ value: child, at: `${where} (a named entry)` });
2280
+ }
2281
+ }
2282
+ stack.push(...children.reverse());
2283
+ }
2284
+ }
2285
+ return out;
2286
+ }
2287
+ async function refuseOtherWorkspaces(input, root, ctx) {
2288
+ const found = await findForeignWorkspaceFields({
2289
+ fields: inputTexts(input),
2290
+ selfPath: root,
2291
+ configPath: ctx.portfolioConfigPath
2292
+ });
2293
+ if (found.status === "unreadable") {
2294
+ console.error(
2295
+ "basou: ~/.basou/portfolio.yaml could not be read, or does not list workspaces as basou reads them, so the record's input was not checked for the names of other registered workspaces."
2296
+ );
2297
+ return;
2298
+ }
2299
+ if (found.status === "checked" && found.at.length > 0) {
2300
+ throw new RecordRefusal(
2301
+ `The record's input names another workspace the portfolio registers, and a board holds this workspace's own work only; nothing was written:
2302
+ ${[
2303
+ ...new Set(found.at)
2304
+ ].map((where) => ` - ${displayPath3(where)}`).join("\n")}`
2305
+ );
2306
+ }
2307
+ }
1867
2308
  function hasRecords(loaded) {
1868
- return basename3(loaded.board.path) === "board.yaml";
2309
+ return basename4(loaded.board.path) === "board.yaml";
1869
2310
  }
1870
2311
  async function previousOf(loaded) {
1871
- return hasRecords(loaded) ? readPreviousRecords(join3(dirname3(loaded.board.path), "records")) : NO_PREVIOUS_RECORDS;
2312
+ return hasRecords(loaded) ? readPreviousRecords(join4(dirname5(loaded.board.path), "records")) : NO_PREVIOUS_RECORDS;
1872
2313
  }
1873
2314
  function errorCode(error) {
1874
2315
  const code = error?.code;
@@ -1876,21 +2317,21 @@ function errorCode(error) {
1876
2317
  }
1877
2318
  async function checkRecordsPlace(loaded, recordsDir, shown, notPrivate) {
1878
2319
  const { board } = loaded;
1879
- if (basename3(board.path) !== "board.yaml") {
2320
+ if (basename4(board.path) !== "board.yaml") {
1880
2321
  throw new RecordRefusal(
1881
2322
  `${displayPath3(board.shown)} is not named board.yaml: basou board record reads only a file of that name, so that the records/ beside it holds one board's records; nothing was written.`
1882
2323
  );
1883
2324
  }
1884
2325
  let dir;
1885
2326
  try {
1886
- dir = await realpath2(dirname3(board.path));
2327
+ dir = await realpath3(dirname5(board.path));
1887
2328
  } catch (error) {
1888
2329
  throw new RecordRefusal(
1889
2330
  `The directory of ${displayPath3(board.shown)} could not be resolved; nothing was written.`,
1890
2331
  { cause: error }
1891
2332
  );
1892
2333
  }
1893
- if (dir.split(sep).includes(".basou")) {
2334
+ if (dir.split(sep2).includes(".basou")) {
1894
2335
  throw new RecordRefusal(
1895
2336
  `${displayPath3(shown)} would be under a .basou/ directory, where basou board record writes nothing; nothing was written.`
1896
2337
  );
@@ -1898,8 +2339,8 @@ async function checkRecordsPlace(loaded, recordsDir, shown, notPrivate) {
1898
2339
  if (!notPrivate) {
1899
2340
  let holder;
1900
2341
  for (const repo of loaded.manifest.repos ?? []) {
1901
- const real = await realpath2(resolve3(loaded.root, repo.path)).catch(() => void 0);
1902
- if (real === void 0 || dir !== real && !dir.startsWith(`${real}${sep}`)) continue;
2342
+ const real = await realpath3(resolve3(loaded.root, repo.path)).catch(() => void 0);
2343
+ if (real === void 0 || dir !== real && !dir.startsWith(`${real}${sep2}`)) continue;
1903
2344
  if (holder === void 0 || real.length > holder.real.length) {
1904
2345
  holder = { real, private: repo.visibility === "private" };
1905
2346
  }
@@ -1999,7 +2440,7 @@ async function loadBoard(options, ctx, command) {
1999
2440
  );
2000
2441
  const paths = basouPaths4(root);
2001
2442
  await assertWorkspaceInitialized3(paths.root);
2002
- const manifest = await readManifest3(paths);
2443
+ const manifest = await readManifest4(paths);
2003
2444
  const board = boardPath(options, cwd, root, manifest);
2004
2445
  const text = await readDeclaration(board);
2005
2446
  const repoPaths = (manifest.repos ?? []).map((repo) => repo.path);
@@ -2038,7 +2479,7 @@ async function measureLiveBoard(root, ctx) {
2038
2479
  let repos = ["."];
2039
2480
  let manifestUnread = false;
2040
2481
  try {
2041
- const declared = ((await readManifest3(paths)).repos ?? []).map((repo) => repo.path);
2482
+ const declared = ((await readManifest4(paths)).repos ?? []).map((repo) => repo.path);
2042
2483
  if (declared.length > 0) repos = declared;
2043
2484
  } catch {
2044
2485
  manifestUnread = true;
@@ -2077,7 +2518,7 @@ function boardPath(options, cwd, root, manifest) {
2077
2518
  `No --board given, and the default ${DEFAULT_BOARD_PATH} is used only when the manifest declares this workspace's own repo (path: .) private. Pass --board <path to board.yaml>.`
2078
2519
  );
2079
2520
  }
2080
- return { path: join3(root, DEFAULT_BOARD_PATH), shown: DEFAULT_BOARD_PATH, given: false };
2521
+ return { path: join4(root, DEFAULT_BOARD_PATH), shown: DEFAULT_BOARD_PATH, given: false };
2081
2522
  }
2082
2523
  async function readDeclaration(board) {
2083
2524
  try {
@@ -2309,19 +2750,19 @@ function printMeasurementText(m, hasRecords2) {
2309
2750
 
2310
2751
  // src/lib/context-channel.ts
2311
2752
  import { homedir as homedir2 } from "os";
2312
- import { join as join5 } from "path";
2753
+ import { join as join6 } from "path";
2313
2754
  import {
2314
2755
  ORIENTATION_END,
2315
2756
  ORIENTATION_START,
2316
2757
  parseMarkers,
2317
- readMarkdownFile as readMarkdownFile2,
2758
+ readMarkdownFile as readMarkdownFile3,
2318
2759
  removeMarkerSection
2319
2760
  } from "@basou/core";
2320
2761
 
2321
2762
  // src/lib/durable-write.ts
2322
2763
  import { randomUUID } from "crypto";
2323
2764
  import { lstat as lstat2, open, rename, stat as stat3, unlink as unlink2 } from "fs/promises";
2324
- import { basename as basename4, dirname as dirname4, join as join4 } from "path";
2765
+ import { basename as basename5, dirname as dirname6, join as join5 } from "path";
2325
2766
  async function assertNotSymlink(targetPath) {
2326
2767
  try {
2327
2768
  const st = await lstat2(targetPath);
@@ -2336,8 +2777,8 @@ async function assertNotSymlink(targetPath) {
2336
2777
  }
2337
2778
  }
2338
2779
  async function writeFileDurable(targetPath, content) {
2339
- const dir = dirname4(targetPath);
2340
- const tmpPath = join4(dir, `.${basename4(targetPath)}.tmp.${randomUUID()}`);
2780
+ const dir = dirname6(targetPath);
2781
+ const tmpPath = join5(dir, `.${basename5(targetPath)}.tmp.${randomUUID()}`);
2341
2782
  let mode = 420;
2342
2783
  try {
2343
2784
  mode = (await stat3(targetPath)).mode & 511;
@@ -2372,7 +2813,7 @@ async function writeFileDurable(targetPath, content) {
2372
2813
  }
2373
2814
 
2374
2815
  // src/lib/context-channel.ts
2375
- var CODEX_TARGET_PATH = join5(homedir2(), ".codex", "AGENTS.md");
2816
+ var CODEX_TARGET_PATH = join6(homedir2(), ".codex", "AGENTS.md");
2376
2817
  var ORIENTATION_MARKERS = { start: ORIENTATION_START, end: ORIENTATION_END };
2377
2818
  function buildTargetBody(existing, block, markers) {
2378
2819
  const wrapped = `${markers.start}
@@ -2396,20 +2837,20 @@ ${block}${markers.end}${section.after}`;
2396
2837
  }
2397
2838
  async function backupOnce(target, existing) {
2398
2839
  const bak = `${target}.basou-bak`;
2399
- const already = await readMarkdownFile2(bak);
2840
+ const already = await readMarkdownFile3(bak);
2400
2841
  if (already !== null) return;
2401
2842
  await writeFileDurable(bak, existing ?? "");
2402
2843
  }
2403
2844
  async function syncMarkerBlock(opts) {
2404
2845
  const { target, markers, block } = opts;
2405
2846
  await assertNotSymlink(target);
2406
- const existing = await readMarkdownFile2(target);
2847
+ const existing = await readMarkdownFile3(target);
2407
2848
  const newBody = buildTargetBody(existing, block, markers);
2408
2849
  if (newBody === existing) return { action: "unchanged" };
2409
2850
  const hadBlock = existing !== null && parseMarkers(existing, markers).kind === "ok";
2410
2851
  const action = hadBlock ? "updated" : "installed";
2411
2852
  if (opts.dryRun === true) return { action };
2412
- const recheck = await readMarkdownFile2(target);
2853
+ const recheck = await readMarkdownFile3(target);
2413
2854
  if (recheck !== existing) {
2414
2855
  throw new Error(
2415
2856
  "The target changed during sync; aborting so a concurrent edit is not overwritten. Re-run the command."
@@ -2431,12 +2872,12 @@ function assertNoMarkerLine(body, markers) {
2431
2872
  async function removeMarkerBlock(opts) {
2432
2873
  const { target, markers, fileLabel } = opts;
2433
2874
  await assertNotSymlink(target);
2434
- const existing = await readMarkdownFile2(target);
2875
+ const existing = await readMarkdownFile3(target);
2435
2876
  if (existing === null) return { removed: false };
2436
2877
  const newBody = removeMarkerSection(existing, fileLabel, markers);
2437
2878
  if (newBody === existing) return { removed: false };
2438
2879
  if (opts.dryRun === true) return { removed: true };
2439
- const recheck = await readMarkdownFile2(target);
2880
+ const recheck = await readMarkdownFile3(target);
2440
2881
  if (recheck !== existing) {
2441
2882
  throw new Error(
2442
2883
  "The target changed during unsync; aborting so a concurrent edit is not overwritten. Re-run the command."
@@ -2517,7 +2958,7 @@ async function doRunChannelClear(face, options) {
2517
2958
  // src/commands/decision.ts
2518
2959
  import { readFile as readFile3 } from "fs/promises";
2519
2960
  import { homedir as homedir3 } from "os";
2520
- import { join as join6, resolve as resolve4 } from "path";
2961
+ import { join as join7, resolve as resolve4 } from "path";
2521
2962
  import {
2522
2963
  AGENT_INFRA_DIRS as AGENT_INFRA_DIRS2,
2523
2964
  acquireLock as acquireLock2,
@@ -2533,7 +2974,7 @@ import {
2533
2974
  LOCAL_CLI_EVENT_SOURCE as LOCAL_CLI_EVENT_SOURCE2,
2534
2975
  loadSessionEntries,
2535
2976
  prefixedUlid as prefixedUlid2,
2536
- readManifest as readManifest4,
2977
+ readManifest as readManifest5,
2537
2978
  replayEvents as replayEvents2,
2538
2979
  resolveSessionId,
2539
2980
  sanitizePath
@@ -2796,7 +3237,7 @@ async function runDecisionRecord(options, ctx = {}) {
2796
3237
  async function warnLinkedFilesOutsideRoots(input) {
2797
3238
  if (input.linkedFiles.length === 0) return;
2798
3239
  try {
2799
- const manifest = await readManifest4(input.paths);
3240
+ const manifest = await readManifest5(input.paths);
2800
3241
  if ((manifest.import?.source_roots?.length ?? 0) === 0) return;
2801
3242
  const scope = await classifyFilesBySourceRoot2({
2802
3243
  files: input.linkedFiles,
@@ -2862,7 +3303,7 @@ async function doRunDecisionRecord(options, ctx) {
2862
3303
  });
2863
3304
  return;
2864
3305
  }
2865
- const manifest = await readManifest4(paths);
3306
+ const manifest = await readManifest5(paths);
2866
3307
  const adHoc = await createAdHocSessionWithEvent({
2867
3308
  paths,
2868
3309
  manifest,
@@ -2926,7 +3367,7 @@ async function doRunDecisionCapture(options, ctx) {
2926
3367
  const now = ctx.nowProvider !== void 0 ? ctx.nowProvider() : /* @__PURE__ */ new Date();
2927
3368
  const occurredAt = now.toISOString();
2928
3369
  const decisionIds = decisions.map(() => prefixedUlid2("decision"));
2929
- const manifest = await readManifest4(paths);
3370
+ const manifest = await readManifest5(paths);
2930
3371
  const invocationArgs = options.file !== void 0 ? [
2931
3372
  "--file",
2932
3373
  sanitizePath(resolve4(cwd, options.file), {
@@ -3032,7 +3473,7 @@ async function doRunDecisionVoid(decisionId, options, ctx) {
3032
3473
  });
3033
3474
  return;
3034
3475
  }
3035
- const manifest = await readManifest4(paths);
3476
+ const manifest = await readManifest5(paths);
3036
3477
  const adHoc = await createAdHocSessionWithEvent({
3037
3478
  paths,
3038
3479
  manifest,
@@ -3068,7 +3509,7 @@ function isDecisionId(value) {
3068
3509
  async function decisionExists(paths, decisionId) {
3069
3510
  const entries = await loadSessionEntries(paths, { now: /* @__PURE__ */ new Date() });
3070
3511
  for (const entry of entries) {
3071
- const sessionDir = join6(paths.sessions, entry.sessionId);
3512
+ const sessionDir = join7(paths.sessions, entry.sessionId);
3072
3513
  try {
3073
3514
  for await (const ev of replayEvents2(sessionDir, {})) {
3074
3515
  if (ev.type === "decision_recorded" && ev.decision_id === decisionId) return true;
@@ -3480,7 +3921,7 @@ import {
3480
3921
  assertBasouRootSafe as assertBasouRootSafe5,
3481
3922
  basouPaths as basouPaths7,
3482
3923
  findErrorCode as findErrorCode5,
3483
- readMarkdownFile as readMarkdownFile3,
3924
+ readMarkdownFile as readMarkdownFile4,
3484
3925
  renderDecisions as renderDecisions2,
3485
3926
  renderWithMarkers as renderWithMarkers2,
3486
3927
  writeMarkdownFile as writeMarkdownFile2
@@ -3512,7 +3953,7 @@ async function doRunDecisionsGenerate(options, ctx) {
3512
3953
  onWarning: (w, sid) => printReplayWarning(w, sid),
3513
3954
  onSessionSkip: (sid, reason) => printSessionSkip(sid, reason)
3514
3955
  });
3515
- const existing = await readMarkdownFile3(paths.files.decisions);
3956
+ const existing = await readMarkdownFile4(paths.files.decisions);
3516
3957
  const finalBody = renderWithMarkers2(existing, result.body, "decisions.md");
3517
3958
  await writeMarkdownFile2(paths.files.decisions, finalBody);
3518
3959
  console.log(`Generated .basou/decisions.md (decisions: ${result.decisionCount})`);
@@ -3531,7 +3972,7 @@ async function assertWorkspaceInitialized5(basouRoot) {
3531
3972
  // src/commands/exec.ts
3532
3973
  import { mkdir as mkdir2, rm as rm2 } from "fs/promises";
3533
3974
  import { homedir as homedir4 } from "os";
3534
- import { join as join7 } from "path";
3975
+ import { join as join8 } from "path";
3535
3976
  import {
3536
3977
  acquireLock as acquireLock3,
3537
3978
  assertBasouRootSafe as assertBasouRootSafe6,
@@ -3545,7 +3986,7 @@ import {
3545
3986
  overwriteYamlFile,
3546
3987
  parseDuration,
3547
3988
  prefixedUlid as prefixedUlid3,
3548
- readManifest as readManifest5,
3989
+ readManifest as readManifest6,
3549
3990
  readYamlFile as readYamlFile2,
3550
3991
  SESSION_SCHEMA_VERSION,
3551
3992
  SessionSchema,
@@ -3572,15 +4013,15 @@ async function runExec(command, args, options, ctx = {}) {
3572
4013
  const repoRoot = await resolveRepositoryRootInPlace(cwd, "exec");
3573
4014
  const paths = basouPaths8(repoRoot);
3574
4015
  await assertBasouRootSafe6(paths.root);
3575
- const manifest = await readManifest5(paths);
4016
+ const manifest = await readManifest6(paths);
3576
4017
  await assertSessionStoreSafe2(paths);
3577
4018
  const sessionId = prefixedUlid3("ses");
3578
- const sessionDir = join7(paths.sessions, sessionId);
4019
+ const sessionDir = join8(paths.sessions, sessionId);
3579
4020
  const appendEvent = ctx.appendEvent ?? (async (_sessionDir, event) => {
3580
4021
  await coreAppendChainedEvent(paths, sessionId, event);
3581
4022
  });
3582
4023
  const startedAt = now().toISOString();
3583
- const sessionYamlPath = join7(sessionDir, "session.yaml");
4024
+ const sessionYamlPath = join8(sessionDir, "session.yaml");
3584
4025
  const session = buildInitialSession({
3585
4026
  id: sessionId,
3586
4027
  command,
@@ -3873,7 +4314,7 @@ import {
3873
4314
  assertBasouRootSafe as assertBasouRootSafe7,
3874
4315
  basouPaths as basouPaths9,
3875
4316
  findErrorCode as findErrorCode6,
3876
- readMarkdownFile as readMarkdownFile4,
4317
+ readMarkdownFile as readMarkdownFile5,
3877
4318
  renderHandoff as renderHandoff2,
3878
4319
  renderWithMarkers as renderWithMarkers3,
3879
4320
  writeMarkdownFile as writeMarkdownFile3
@@ -3906,7 +4347,7 @@ async function doRunHandoffGenerate(options, ctx) {
3906
4347
  onSessionSkip: (sid, reason) => printSessionSkip(sid, reason),
3907
4348
  onTaskSkip: (taskId, reason) => printTaskSkip(taskId, reason)
3908
4349
  });
3909
- const existing = await readMarkdownFile4(paths.files.handoff);
4350
+ const existing = await readMarkdownFile5(paths.files.handoff);
3910
4351
  const finalBody = renderWithMarkers3(existing, result.body, "handoff.md");
3911
4352
  await writeMarkdownFile3(paths.files.handoff, finalBody);
3912
4353
  console.log(
@@ -3941,7 +4382,7 @@ async function assertWorkspaceInitialized6(basouRoot) {
3941
4382
  import { execFile } from "child_process";
3942
4383
  import { open as open2, readFile as readFile4, realpath as realpath4, stat as stat4 } from "fs/promises";
3943
4384
  import { homedir as homedir7 } from "os";
3944
- import { join as join10 } from "path";
4385
+ import { join as join11 } from "path";
3945
4386
  import { fileURLToPath } from "url";
3946
4387
  import { promisify } from "util";
3947
4388
  import {
@@ -3968,7 +4409,7 @@ import {
3968
4409
  parseProtocolStamp,
3969
4410
  protocolSectionsFrom,
3970
4411
  protocolUpdateToken,
3971
- readManifest as readManifest6,
4412
+ readManifest as readManifest7,
3972
4413
  readMarkdownFile as readMarkdownFile6,
3973
4414
  recordSessionBaseline,
3974
4415
  removeClaudeSessionStartHook,
@@ -3995,12 +4436,12 @@ function commandHandlerFields(handler) {
3995
4436
  out.additionalContextLimit = handler.additionalContextLimit;
3996
4437
  return out;
3997
4438
  }
3998
- function canonicalize(value) {
3999
- if (Array.isArray(value)) return value.map(canonicalize);
4439
+ function canonicalize2(value) {
4440
+ if (Array.isArray(value)) return value.map(canonicalize2);
4000
4441
  if (typeof value === "object" && value !== null) {
4001
4442
  const src = value;
4002
4443
  const out = {};
4003
- for (const key of Object.keys(src).sort()) out[key] = canonicalize(src[key]);
4444
+ for (const key of Object.keys(src).sort()) out[key] = canonicalize2(src[key]);
4004
4445
  return out;
4005
4446
  }
4006
4447
  return value;
@@ -4019,7 +4460,7 @@ function computeCodexHookIdentityHash(input) {
4019
4460
  }
4020
4461
  const identity = { event_name: input.eventKey, hooks: [normalized] };
4021
4462
  if (input.matcher !== void 0) identity.matcher = input.matcher;
4022
- const blob = JSON.stringify(canonicalize(identity));
4463
+ const blob = JSON.stringify(canonicalize2(identity));
4023
4464
  return `sha256:${createHash("sha256").update(blob, "utf8").digest("hex")}`;
4024
4465
  }
4025
4466
  function codexHookStateKey(hooksPath, eventKey, groupIndex, handlerIndex) {
@@ -4100,148 +4541,17 @@ function describeCodexHookTrust(trust) {
4100
4541
  }
4101
4542
  }
4102
4543
 
4103
- // src/lib/foreign-workspace-warn.ts
4104
- import { realpath as realpath3 } from "fs/promises";
4105
- import { dirname as dirname5 } from "path";
4106
- import { loadPortfolioConfig as loadPortfolioConfig2, readMarkdownFile as readMarkdownFile5 } from "@basou/core";
4107
-
4108
- // src/lib/foreign-workspace-scan.ts
4109
- import { homedir as osHomedir } from "os";
4110
- import { basename as basename5, normalize, sep as sep2 } from "path";
4111
- var MIN_TOKEN_LENGTH = 4;
4112
- function stripTrailingSep(p) {
4113
- return p.length > 1 && p.endsWith(sep2) ? p.slice(0, -sep2.length) : p;
4114
- }
4115
- function toTildePath(absPath, home) {
4116
- if (absPath === home) return "~";
4117
- if (absPath.startsWith(home + sep2)) return `~${absPath.slice(home.length)}`;
4118
- return null;
4119
- }
4120
- function encodedSpelling(name) {
4121
- return name.replace(/[^a-zA-Z0-9]/g, "-");
4122
- }
4123
- function directoryNameSpellings(dir) {
4124
- const paired = /^(.+)-(planning|workspace)$/.exec(dir);
4125
- const plain = paired !== null ? [`${paired[1]}-planning`, `${paired[1]}-workspace`] : [dir, `${dir}-workspace`];
4126
- return [...plain, ...plain.map(encodedSpelling)];
4127
- }
4128
- function nameTokensFor(workspacePath, home) {
4129
- const abs = stripTrailingSep(normalize(workspacePath));
4130
- const tokens = /* @__PURE__ */ new Set([abs]);
4131
- const tilde = toTildePath(abs, home);
4132
- if (tilde !== null) tokens.add(tilde);
4133
- for (const name of directoryNameSpellings(basename5(abs))) tokens.add(name);
4134
- return [...tokens].filter((t) => t.length >= MIN_TOKEN_LENGTH);
4135
- }
4136
- function isSpellingOfSelf(workspacePath, selfPath) {
4137
- const own = new Set(directoryNameSpellings(basename5(stripTrailingSep(normalize(selfPath)))));
4138
- return directoryNameSpellings(basename5(stripTrailingSep(normalize(workspacePath)))).some(
4139
- (s) => own.has(s)
4140
- );
4141
- }
4142
- function scanForeignWorkspaceNames(input) {
4143
- if (input.text.length === 0 || input.workspacePaths.length === 0) return [];
4144
- const home = input.homedir ?? osHomedir();
4145
- const own = input.selfPath === void 0 ? [] : nameTokensFor(input.selfPath, home);
4146
- const lines = input.text.split("\n");
4147
- const hits = [];
4148
- for (const workspacePath of input.workspacePaths) {
4149
- if (input.selfPath !== void 0 && isSpellingOfSelf(workspacePath, input.selfPath)) continue;
4150
- const tokens = nameTokensFor(workspacePath, home).filter(
4151
- (token) => !own.some((ownToken) => ownToken.includes(token))
4152
- );
4153
- if (tokens.length === 0) continue;
4154
- const matched = /* @__PURE__ */ new Set();
4155
- const matchedLines = [];
4156
- for (const [index, line] of lines.entries()) {
4157
- let lineMatched = false;
4158
- for (const token of tokens) {
4159
- if (line.includes(token)) {
4160
- matched.add(token);
4161
- lineMatched = true;
4162
- }
4163
- }
4164
- if (lineMatched) matchedLines.push(index + 1);
4165
- }
4166
- if (matchedLines.length === 0) continue;
4167
- hits.push({
4168
- workspacePath,
4169
- tokens: [...matched].sort((a, b) => b.length - a.length || a.localeCompare(b)),
4170
- lines: matchedLines
4171
- });
4172
- }
4173
- return hits;
4174
- }
4175
-
4176
- // src/lib/foreign-workspace-warn.ts
4177
- var MAX_LISTED_LINES = 5;
4178
- async function canonicalize2(path) {
4179
- try {
4180
- return await realpath3(path);
4181
- } catch {
4182
- return path;
4183
- }
4184
- }
4185
- async function findForeignWorkspaceNames(args) {
4186
- let workspacePaths;
4187
- let selfPath;
4188
- try {
4189
- const workspaces = await loadPortfolioConfig2(args.configPath);
4190
- workspacePaths = await Promise.all(workspaces.map((w) => canonicalize2(w.path)));
4191
- selfPath = args.selfPath === void 0 ? void 0 : await canonicalize2(args.selfPath);
4192
- } catch {
4193
- return null;
4194
- }
4195
- const hits = scanForeignWorkspaceNames({
4196
- text: args.text,
4197
- workspacePaths,
4198
- selfPath
4199
- });
4200
- if (hits.length === 0) return null;
4201
- const lines = [...new Set(hits.flatMap((h) => h.lines))].sort((a, b) => a - b);
4202
- return { workspaceCount: hits.length, lines };
4203
- }
4204
- function describeForeignWorkspaceLines(lines) {
4205
- const listed = lines.slice(0, MAX_LISTED_LINES).join(", ");
4206
- const rest = lines.length - MAX_LISTED_LINES;
4207
- const numbers = rest > 0 ? `${listed} and ${rest} more` : listed;
4208
- return `${lines.length === 1 ? "line" : "lines"} ${numbers}`;
4209
- }
4210
- function positionForeignWorkspaceWarning(report, where) {
4211
- const subject = report.workspaceCount === 1 ? "names another registered workspace" : `names ${report.workspaceCount} other registered workspaces`;
4212
- return `basou: this workspace's position ${subject} (${where}, ${describeForeignWorkspaceLines(report.lines)}). The position is handed to the agent session running in this workspace, so those names travel with it. Advisory only: nothing was withheld.`;
4213
- }
4214
- function protocolForeignWorkspaceWarning(report) {
4215
- const subject = report.workspaceCount === 1 ? "names a registered workspace" : `names ${report.workspaceCount} registered workspaces`;
4216
- return `basou: the protocol block ${subject} (block ${describeForeignWorkspaceLines(report.lines)}, counted from the block's first line, not the file's). Standing protocols are written to the user-global CLAUDE.md, which every project on this machine loads, so a workspace-specific name there reaches every workspace's sessions. Only the basou-managed block is checked; the rest of that file is not basou's to inspect. Advisory only: nothing was withheld.`;
4217
- }
4218
- async function warnIfPositionNamesOtherWorkspaces(args) {
4219
- try {
4220
- const body = await readMarkdownFile5(args.paths.files.orientation);
4221
- if (body === null) return;
4222
- const report = await findForeignWorkspaceNames({
4223
- text: body,
4224
- selfPath: dirname5(args.paths.root),
4225
- configPath: args.configPath
4226
- });
4227
- if (report !== null) {
4228
- console.error(positionForeignWorkspaceWarning(report, args.paths.files.orientation));
4229
- }
4230
- } catch {
4231
- }
4232
- }
4233
-
4234
4544
  // src/lib/protocols-config.ts
4235
4545
  import { homedir as homedir5 } from "os";
4236
- import { isAbsolute, join as join8, resolve as resolve5 } from "path";
4546
+ import { isAbsolute, join as join9, resolve as resolve5 } from "path";
4237
4547
  import { readYamlFile as readYamlFile3 } from "@basou/core";
4238
- var DEFAULT_PROTOCOLS_CONFIG_PATH = join8(homedir5(), ".basou", "protocols.yaml");
4239
- var DEFAULT_TARGET_PATH = join8(homedir5(), ".claude", "CLAUDE.md");
4548
+ var DEFAULT_PROTOCOLS_CONFIG_PATH = join9(homedir5(), ".basou", "protocols.yaml");
4549
+ var DEFAULT_TARGET_PATH = join9(homedir5(), ".claude", "CLAUDE.md");
4240
4550
  var ALLOWED_TOP_KEYS = /* @__PURE__ */ new Set(["version", "protocols"]);
4241
4551
  var ALLOWED_ENTRY_KEYS = /* @__PURE__ */ new Set(["source", "title"]);
4242
4552
  function expandTilde(p) {
4243
4553
  if (p === "~") return homedir5();
4244
- if (p.startsWith("~/")) return join8(homedir5(), p.slice(2));
4554
+ if (p.startsWith("~/")) return join9(homedir5(), p.slice(2));
4245
4555
  return p;
4246
4556
  }
4247
4557
  function isRecord(value) {
@@ -4319,12 +4629,12 @@ import {
4319
4629
 
4320
4630
  // src/lib/hosts-config.ts
4321
4631
  import { homedir as homedir6 } from "os";
4322
- import { isAbsolute as isAbsolute2, join as join9, resolve as resolve6 } from "path";
4632
+ import { isAbsolute as isAbsolute2, join as join10, resolve as resolve6 } from "path";
4323
4633
  import { readYamlFile as readYamlFile4 } from "@basou/core";
4324
- var DEFAULT_HOSTS_CONFIG_PATH = join9(homedir6(), ".basou", "hosts.yaml");
4634
+ var DEFAULT_HOSTS_CONFIG_PATH = join10(homedir6(), ".basou", "hosts.yaml");
4325
4635
  function expandTilde2(p) {
4326
4636
  if (p === "~") return homedir6();
4327
- if (p.startsWith("~/")) return join9(homedir6(), p.slice(2));
4637
+ if (p.startsWith("~/")) return join10(homedir6(), p.slice(2));
4328
4638
  return p;
4329
4639
  }
4330
4640
  function isRecord2(value) {
@@ -4444,6 +4754,7 @@ async function renderOrientationForRoot(repositoryRoot, options, ctx, behaviour)
4444
4754
  paths,
4445
4755
  nowIso,
4446
4756
  staleness,
4757
+ board: await glanceDefaultBoard(repositoryRoot, paths),
4447
4758
  verbose: options.verbose === true,
4448
4759
  federatedRoots,
4449
4760
  onWarning: (w, sid) => printReplayWarning(w, sid),
@@ -4845,7 +5156,7 @@ async function observeSessionFromPayload(fields, pass, portfolioConfigPath) {
4845
5156
  });
4846
5157
  return;
4847
5158
  }
4848
- const manifest = await readManifest6(paths);
5159
+ const manifest = await readManifest7(paths);
4849
5160
  await recordSessionBaseline({
4850
5161
  observationsDir: paths.observations,
4851
5162
  repoRoots: observedRepoRoots(root, manifest),
@@ -4907,7 +5218,7 @@ function parseMinEdits(raw) {
4907
5218
  if (raw === void 0 || !/^\d+$/.test(raw)) return void 0;
4908
5219
  return Number(raw);
4909
5220
  }
4910
- var DEFAULT_CLAUDE_SETTINGS_PATH = join10(homedir7(), ".claude", "settings.json");
5221
+ var DEFAULT_CLAUDE_SETTINGS_PATH = join11(homedir7(), ".claude", "settings.json");
4911
5222
  function resolveCliEntry() {
4912
5223
  return fileURLToPath(import.meta.url);
4913
5224
  }
@@ -5264,7 +5575,7 @@ function describeHookMode(tiers) {
5264
5575
  const gates = tiers.review ? "capture + review" : "capture";
5265
5576
  return `${enforcement}, ${gates}`;
5266
5577
  }
5267
- var DEFAULT_CODEX_FACE_PATH = join10(homedir7(), ".codex", "AGENTS.md");
5578
+ var DEFAULT_CODEX_FACE_PATH = join11(homedir7(), ".codex", "AGENTS.md");
5268
5579
  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.`;
5269
5580
  async function faceHasLeftoverOrientationBlock(facePath) {
5270
5581
  try {
@@ -5276,8 +5587,8 @@ async function faceHasLeftoverOrientationBlock(facePath) {
5276
5587
  return false;
5277
5588
  }
5278
5589
  }
5279
- var DEFAULT_CODEX_HOOKS_PATH = join10(homedir7(), ".codex", "hooks.json");
5280
- var DEFAULT_CODEX_CONFIG_PATH = join10(homedir7(), ".codex", "config.toml");
5590
+ var DEFAULT_CODEX_HOOKS_PATH = join11(homedir7(), ".codex", "hooks.json");
5591
+ var DEFAULT_CODEX_CONFIG_PATH = join11(homedir7(), ".codex", "config.toml");
5281
5592
  async function readHooksFile(path) {
5282
5593
  let raw;
5283
5594
  try {
@@ -5535,7 +5846,7 @@ import {
5535
5846
  EVENT_SCHEMA_VERSION as EVENT_SCHEMA_VERSION4,
5536
5847
  findErrorCode as findErrorCode8,
5537
5848
  LOCAL_CLI_EVENT_SOURCE as LOCAL_CLI_EVENT_SOURCE3,
5538
- readManifest as readManifest7,
5849
+ readManifest as readManifest8,
5539
5850
  resolveSessionId as resolveSessionId2,
5540
5851
  sanitizePath as sanitizePath2
5541
5852
  } from "@basou/core";
@@ -5647,7 +5958,7 @@ ${HEREDOC_EXAMPLE}`
5647
5958
  });
5648
5959
  return;
5649
5960
  }
5650
- const manifest = await readManifest7(paths);
5961
+ const manifest = await readManifest8(paths);
5651
5962
  const adHoc = await createAdHocSessionWithEvent2({
5652
5963
  paths,
5653
5964
  manifest,
@@ -5895,7 +6206,7 @@ import {
5895
6206
  writeFileSync,
5896
6207
  writeSync
5897
6208
  } from "fs";
5898
- import { basename as basename7, dirname as dirname6, isAbsolute as isAbsolute3, join as join11, relative as relative3, resolve as resolve9 } from "path";
6209
+ import { basename as basename7, dirname as dirname7, isAbsolute as isAbsolute3, join as join12, relative as relative3, resolve as resolve9 } from "path";
5899
6210
  import {
5900
6211
  appendBasouGitignore as appendBasouGitignore2,
5901
6212
  basouPaths as basouPaths13,
@@ -5913,7 +6224,7 @@ import {
5913
6224
  planRename,
5914
6225
  planRosterAdoption,
5915
6226
  planWorkspaceView,
5916
- readManifest as readManifest8,
6227
+ readManifest as readManifest9,
5917
6228
  readMarkdownFile as readMarkdownFile7,
5918
6229
  reconcileSourceRoots,
5919
6230
  removeMarkerSection as removeMarkerSection2,
@@ -6061,7 +6372,7 @@ async function doRunProjectCheck(options, ctx) {
6061
6372
  const cwd = ctx.cwd ?? process.cwd();
6062
6373
  const repositoryRoot = await resolveBasouRootForCommand(cwd, "project check");
6063
6374
  const paths = basouPaths13(repositoryRoot);
6064
- const manifest = await readManifest8(paths);
6375
+ const manifest = await readManifest9(paths);
6065
6376
  const roster = summarizeRosterDrift({
6066
6377
  ...manifest.repos !== void 0 ? { repos: manifest.repos } : {},
6067
6378
  sourceRoots: effectiveSourceRoots(manifest)
@@ -6196,7 +6507,7 @@ async function doRunProjectSync(options, ctx) {
6196
6507
  const cwd = ctx.cwd ?? process.cwd();
6197
6508
  const repositoryRoot = await resolveBasouRootForCommand(cwd, "project sync");
6198
6509
  const paths = basouPaths13(repositoryRoot);
6199
- const manifest = await readManifest8(paths);
6510
+ const manifest = await readManifest9(paths);
6200
6511
  const hasRoster = manifest.repos !== void 0 && manifest.repos.length > 0;
6201
6512
  const reconcile = reconcileSourceRoots({
6202
6513
  ...manifest.repos !== void 0 ? { repos: manifest.repos } : {},
@@ -6276,13 +6587,13 @@ function classifySourceRoot(repositoryRoot, declaredPath) {
6276
6587
  } catch {
6277
6588
  return { path: declaredPath, kind: "unresolved" };
6278
6589
  }
6279
- return { path: declaredPath, kind: existsSync(join11(real, ".git")) ? "repo" : "non-repo" };
6590
+ return { path: declaredPath, kind: existsSync(join12(real, ".git")) ? "repo" : "non-repo" };
6280
6591
  }
6281
6592
  async function doRunProjectAdopt(options, ctx) {
6282
6593
  const cwd = ctx.cwd ?? process.cwd();
6283
6594
  const repositoryRoot = await resolveBasouRootForCommand(cwd, "project adopt");
6284
6595
  const paths = basouPaths13(repositoryRoot);
6285
- const manifest = await readManifest8(paths);
6596
+ const manifest = await readManifest9(paths);
6286
6597
  const alreadyDeclared = manifest.repos !== void 0 && manifest.repos.length > 0;
6287
6598
  const candidates = effectiveSourceRoots(manifest).map(
6288
6599
  (r) => classifySourceRoot(repositoryRoot, r)
@@ -6380,7 +6691,7 @@ async function gatherRepoWiring(repositoryRoot, entry) {
6380
6691
  } catch {
6381
6692
  return { ...base, reachable: false, instructionFiles: [] };
6382
6693
  }
6383
- if (!existsSync(join11(real, ".git"))) {
6694
+ if (!existsSync(join12(real, ".git"))) {
6384
6695
  return { ...base, reachable: false, instructionFiles: [] };
6385
6696
  }
6386
6697
  try {
@@ -6388,7 +6699,7 @@ async function gatherRepoWiring(repositoryRoot, entry) {
6388
6699
  for (const name of INSTRUCTION_FILES) {
6389
6700
  let present = true;
6390
6701
  try {
6391
- lstatSync(join11(real, name));
6702
+ lstatSync(join12(real, name));
6392
6703
  } catch {
6393
6704
  present = false;
6394
6705
  }
@@ -6404,7 +6715,7 @@ async function doRunProjectWiring(options, ctx) {
6404
6715
  const cwd = ctx.cwd ?? process.cwd();
6405
6716
  const repositoryRoot = await resolveBasouRootForCommand(cwd, "project wiring");
6406
6717
  const paths = basouPaths13(repositoryRoot);
6407
- const manifest = await readManifest8(paths);
6718
+ const manifest = await readManifest9(paths);
6408
6719
  const roster = manifest.repos ?? [];
6409
6720
  const facts = [];
6410
6721
  for (const entry of roster) facts.push(await gatherRepoWiring(repositoryRoot, entry));
@@ -6499,10 +6810,10 @@ function gatherRepoGitignore(repositoryRoot, entry) {
6499
6810
  } catch {
6500
6811
  return { ...base, reachable: false, currentLines: [] };
6501
6812
  }
6502
- if (!existsSync(join11(real, ".git"))) {
6813
+ if (!existsSync(join12(real, ".git"))) {
6503
6814
  return { ...base, reachable: false, currentLines: [] };
6504
6815
  }
6505
- return { ...base, reachable: true, currentLines: readGitignoreLines(join11(real, ".gitignore")) };
6816
+ return { ...base, reachable: true, currentLines: readGitignoreLines(join12(real, ".gitignore")) };
6506
6817
  }
6507
6818
  function hasErrorCode(error) {
6508
6819
  return error instanceof Error && typeof error.code === "string";
@@ -6516,7 +6827,7 @@ function readGitignoreLines(file) {
6516
6827
  }
6517
6828
  }
6518
6829
  function applyGitignorePlan(repositoryRoot, plan) {
6519
- const file = join11(realpathSync(resolve9(repositoryRoot, plan.path)), ".gitignore");
6830
+ const file = join12(realpathSync(resolve9(repositoryRoot, plan.path)), ".gitignore");
6520
6831
  let existing = "";
6521
6832
  try {
6522
6833
  existing = readFileSync(file, "utf8");
@@ -6537,7 +6848,7 @@ async function doRunProjectGitignore(options, ctx) {
6537
6848
  const cwd = ctx.cwd ?? process.cwd();
6538
6849
  const repositoryRoot = await resolveBasouRootForCommand(cwd, "project gitignore");
6539
6850
  const paths = basouPaths13(repositoryRoot);
6540
- const manifest = await readManifest8(paths);
6851
+ const manifest = await readManifest9(paths);
6541
6852
  const roster = manifest.repos ?? [];
6542
6853
  const facts = roster.map((entry) => gatherRepoGitignore(repositoryRoot, entry));
6543
6854
  const summary = planGitignore({ repos: facts, required: [...INSTRUCTION_FILES] });
@@ -6656,7 +6967,7 @@ function gatherRepoSymlinks(repositoryRoot, anchorReal, entry) {
6656
6967
  return { ...base, isAnchor: false, reachable: false, canonicalPresent: false, files: [] };
6657
6968
  }
6658
6969
  if (real === anchorReal) {
6659
- const anchorCanonical = join11(real, CANONICAL_FILE);
6970
+ const anchorCanonical = join12(real, CANONICAL_FILE);
6660
6971
  const anchorState = anchorCanonicalState(anchorCanonical);
6661
6972
  if (anchorState === "absent") {
6662
6973
  return { ...base, isAnchor: true, reachable: true, canonicalPresent: false, files: [] };
@@ -6676,7 +6987,7 @@ function gatherRepoSymlinks(repositoryRoot, anchorReal, entry) {
6676
6987
  anchorCanonical,
6677
6988
  "self"
6678
6989
  ).map((spec) => {
6679
- const { state, actualTarget } = inspectSymlink(join11(real, spec.name), spec.target);
6990
+ const { state, actualTarget } = inspectSymlink(join12(real, spec.name), spec.target);
6680
6991
  return {
6681
6992
  name: spec.name,
6682
6993
  expectedTarget: spec.target,
@@ -6693,16 +7004,16 @@ function gatherRepoSymlinks(repositoryRoot, anchorReal, entry) {
6693
7004
  files: anchorFiles
6694
7005
  };
6695
7006
  }
6696
- if (!existsSync(join11(real, ".git"))) {
7007
+ if (!existsSync(join12(real, ".git"))) {
6697
7008
  return { ...base, isAnchor: false, reachable: false, canonicalPresent: false, files: [] };
6698
7009
  }
6699
- const canonicalFile = isSelf ? join11(real, CANONICAL_FILE) : join11(anchorReal, "agents", basename7(real), CANONICAL_FILE);
7010
+ const canonicalFile = isSelf ? join12(real, CANONICAL_FILE) : join12(anchorReal, "agents", basename7(real), CANONICAL_FILE);
6700
7011
  if (!existsSync(canonicalFile)) {
6701
7012
  return { ...base, isAnchor: false, reachable: true, canonicalPresent: false, files: [] };
6702
7013
  }
6703
7014
  const files = expectedSymlinkTargets(real, canonicalFile, mode).map(
6704
7015
  (spec) => {
6705
- const { state, actualTarget } = inspectSymlink(join11(real, spec.name), spec.target);
7016
+ const { state, actualTarget } = inspectSymlink(join12(real, spec.name), spec.target);
6706
7017
  return {
6707
7018
  name: spec.name,
6708
7019
  expectedTarget: spec.target,
@@ -6731,9 +7042,9 @@ function applySymlinkPlan(repositoryRoot, plan) {
6731
7042
  const created = [];
6732
7043
  const failed = [];
6733
7044
  for (const { name, target } of plan.toCreate) {
6734
- const filePath = join11(real, name);
7045
+ const filePath = join12(real, name);
6735
7046
  try {
6736
- mkdirSync(dirname6(filePath), { recursive: true });
7047
+ mkdirSync(dirname7(filePath), { recursive: true });
6737
7048
  symlinkSync(target, filePath);
6738
7049
  created.push(name);
6739
7050
  } catch (error) {
@@ -6765,7 +7076,7 @@ function gatherViewSymlinks(repositoryRoot, anchorReal, roster, viewDir) {
6765
7076
  if (!existsSync(canonicalFile)) return { kind: "missing-canonical", viewName };
6766
7077
  const files = expectedSymlinkTargets(viewDir, canonicalFile, "hub").map(
6767
7078
  (spec) => {
6768
- const { state, actualTarget } = inspectSymlink(join11(viewDir, spec.name), spec.target);
7079
+ const { state, actualTarget } = inspectSymlink(join12(viewDir, spec.name), spec.target);
6769
7080
  return {
6770
7081
  name: spec.name,
6771
7082
  expectedTarget: spec.target,
@@ -6781,9 +7092,9 @@ function applyViewSymlinks(viewDir, files) {
6781
7092
  const failed = [];
6782
7093
  for (const f of files) {
6783
7094
  if (f.state !== "missing") continue;
6784
- const filePath = join11(viewDir, f.name);
7095
+ const filePath = join12(viewDir, f.name);
6785
7096
  try {
6786
- mkdirSync(dirname6(filePath), { recursive: true });
7097
+ mkdirSync(dirname7(filePath), { recursive: true });
6787
7098
  symlinkSync(f.expectedTarget, filePath);
6788
7099
  created.push(f.name);
6789
7100
  } catch (error) {
@@ -6796,7 +7107,7 @@ async function doRunProjectSymlinks(options, ctx) {
6796
7107
  const cwd = ctx.cwd ?? process.cwd();
6797
7108
  const repositoryRoot = await resolveBasouRootForCommand(cwd, "project symlinks");
6798
7109
  const paths = basouPaths13(repositoryRoot);
6799
- const manifest = await readManifest8(paths);
7110
+ const manifest = await readManifest9(paths);
6800
7111
  const roster = manifest.repos ?? [];
6801
7112
  const anchorReal = realpathSync(repositoryRoot);
6802
7113
  const facts = roster.map((entry) => gatherRepoSymlinks(repositoryRoot, anchorReal, entry));
@@ -7003,7 +7314,7 @@ function resolveViewDir(repositoryRoot, viewPath) {
7003
7314
  return realpathSync(abs);
7004
7315
  } catch {
7005
7316
  try {
7006
- return join11(realpathSync(dirname6(abs)), basename7(abs));
7317
+ return join12(realpathSync(dirname7(abs)), basename7(abs));
7007
7318
  } catch {
7008
7319
  return abs;
7009
7320
  }
@@ -7021,7 +7332,7 @@ function gatherViewRepo(repositoryRoot, viewDir, entry) {
7021
7332
  return { path: entry.path, reachable: false };
7022
7333
  }
7023
7334
  const linkName = basename7(repoReal);
7024
- const { state, actualTarget } = inspectSymlink(join11(viewDir, linkName), expectedTarget);
7335
+ const { state, actualTarget } = inspectSymlink(join12(viewDir, linkName), expectedTarget);
7025
7336
  return {
7026
7337
  path: entry.path,
7027
7338
  reachable: true,
@@ -7035,9 +7346,9 @@ function applyViewPlan(viewDir, toCreate) {
7035
7346
  const created = [];
7036
7347
  const failed = [];
7037
7348
  for (const { name, target } of toCreate) {
7038
- const filePath = join11(viewDir, name);
7349
+ const filePath = join12(viewDir, name);
7039
7350
  try {
7040
- mkdirSync(dirname6(filePath), { recursive: true });
7351
+ mkdirSync(dirname7(filePath), { recursive: true });
7041
7352
  symlinkSync(target, filePath);
7042
7353
  created.push(name);
7043
7354
  } catch (error) {
@@ -7050,7 +7361,7 @@ var TOP_LEVEL_INSTRUCTION_FILES_LOWER = new Set(
7050
7361
  INSTRUCTION_FILES.filter((f) => !f.includes("/")).map((f) => f.toLowerCase())
7051
7362
  );
7052
7363
  function classifyViewLink(viewDir, name, rosterRealpaths) {
7053
- const filePath = join11(viewDir, name);
7364
+ const filePath = join12(viewDir, name);
7054
7365
  let isLink;
7055
7366
  try {
7056
7367
  isLink = lstatSync(filePath).isSymbolicLink();
@@ -7079,7 +7390,7 @@ function classifyViewLink(viewDir, name, rosterRealpaths) {
7079
7390
  if (!isDir) {
7080
7391
  return { target, kind: existsSync(resolved) ? "non-repo" : "broken" };
7081
7392
  }
7082
- return { target, kind: existsSync(join11(resolved, ".git")) ? "repo" : "non-repo" };
7393
+ return { target, kind: existsSync(join12(resolved, ".git")) ? "repo" : "non-repo" };
7083
7394
  }
7084
7395
  function gatherExistingViewLinks(viewDir, rosterRealpaths) {
7085
7396
  let names;
@@ -7104,7 +7415,7 @@ function pruneViewLinks(viewDir, toPrune, rosterRealpaths) {
7104
7415
  const pruned = [];
7105
7416
  const failed = [];
7106
7417
  for (const { name } of toPrune) {
7107
- const filePath = join11(viewDir, name);
7418
+ const filePath = join12(viewDir, name);
7108
7419
  const c = classifyViewLink(viewDir, name, rosterRealpaths);
7109
7420
  if (c === null || c.kind !== "repo") {
7110
7421
  failed.push({
@@ -7126,7 +7437,7 @@ async function doRunProjectWorkspace(options, ctx) {
7126
7437
  const cwd = ctx.cwd ?? process.cwd();
7127
7438
  const repositoryRoot = await resolveBasouRootForCommand(cwd, "project workspace");
7128
7439
  const paths = basouPaths13(repositoryRoot);
7129
- const manifest = await readManifest8(paths);
7440
+ const manifest = await readManifest9(paths);
7130
7441
  const viewPath = manifest.workspace.view;
7131
7442
  const roster = manifest.repos ?? [];
7132
7443
  let result;
@@ -7317,10 +7628,10 @@ async function runProjectPreset(options, ctx = {}) {
7317
7628
  }
7318
7629
  }
7319
7630
  function canonicalFileFor(anchorReal, canonicalName) {
7320
- return join11(anchorReal, "agents", canonicalName, CANONICAL_FILE);
7631
+ return join12(anchorReal, "agents", canonicalName, CANONICAL_FILE);
7321
7632
  }
7322
7633
  function canonicalLabelFor(canonicalName) {
7323
- return join11("agents", canonicalName, CANONICAL_FILE);
7634
+ return join12("agents", canonicalName, CANONICAL_FILE);
7324
7635
  }
7325
7636
  async function gatherRepoPreset(repositoryRoot, anchorReal, entry) {
7326
7637
  const declared = {
@@ -7341,7 +7652,7 @@ async function gatherRepoPreset(repositoryRoot, anchorReal, entry) {
7341
7652
  if (real === anchorReal) {
7342
7653
  return { ...declared, isAnchor: true, reachable: true, canonicalPresent: false };
7343
7654
  }
7344
- if (!existsSync(join11(real, ".git"))) {
7655
+ if (!existsSync(join12(real, ".git"))) {
7345
7656
  return { ...declared, isAnchor: false, reachable: false, canonicalPresent: false };
7346
7657
  }
7347
7658
  const canonicalName = basename7(real);
@@ -7455,7 +7766,7 @@ async function applyViewPreset(anchorReal, outcome) {
7455
7766
  isLink = false;
7456
7767
  }
7457
7768
  if (isLink) throw new Error(`Canonical is a symlink in ${label}`);
7458
- if (outcome.action === "create") mkdirSync(dirname6(file), { recursive: true });
7769
+ if (outcome.action === "create") mkdirSync(dirname7(file), { recursive: true });
7459
7770
  const existing = await readMarkdownFile7(file);
7460
7771
  await writeMarkdownFile5(file, renderWithMarkers4(existing, outcome.block, label));
7461
7772
  }
@@ -7469,7 +7780,7 @@ async function applyPresetPlan(anchorReal, plan) {
7469
7780
  isLink = false;
7470
7781
  }
7471
7782
  if (isLink) throw new Error(`Canonical is a symlink in ${label}`);
7472
- if (plan.action === "create") mkdirSync(dirname6(file), { recursive: true });
7783
+ if (plan.action === "create") mkdirSync(dirname7(file), { recursive: true });
7473
7784
  const existing = await readMarkdownFile7(file);
7474
7785
  await writeMarkdownFile5(file, renderWithMarkers4(existing, plan.desiredBlock, label));
7475
7786
  }
@@ -7486,7 +7797,7 @@ async function doRunProjectPreset(options, ctx) {
7486
7797
  const cwd = ctx.cwd ?? process.cwd();
7487
7798
  const repositoryRoot = await resolveBasouRootForCommand(cwd, "project preset");
7488
7799
  const paths = basouPaths13(repositoryRoot);
7489
- const manifest = await readManifest8(paths);
7800
+ const manifest = await readManifest9(paths);
7490
7801
  const roster = manifest.repos ?? [];
7491
7802
  const anchorReal = realpathSync(repositoryRoot);
7492
7803
  const facts = [];
@@ -7723,24 +8034,24 @@ function gatherArchiveTeardown(repositoryRoot, manifest, target) {
7723
8034
  const instructionFiles = [];
7724
8035
  for (const name of INSTRUCTION_FILES) {
7725
8036
  try {
7726
- lstatSync(join11(real, name));
8037
+ lstatSync(join12(real, name));
7727
8038
  instructionFiles.push(name);
7728
8039
  } catch {
7729
8040
  }
7730
8041
  }
7731
8042
  let ignored;
7732
8043
  try {
7733
- ignored = new Set(readGitignoreLines(join11(real, ".gitignore")).map((l) => l.trim()));
8044
+ ignored = new Set(readGitignoreLines(join12(real, ".gitignore")).map((l) => l.trim()));
7734
8045
  } catch {
7735
8046
  ignored = /* @__PURE__ */ new Set();
7736
8047
  }
7737
8048
  const gitignorePatterns = INSTRUCTION_FILES.filter((p) => ignored.has(p) || ignored.has(`/${p}`));
7738
- const canonical2 = existsSync(join11(anchorReal, "agents", canonicalName, CANONICAL_FILE));
8049
+ const canonical2 = existsSync(join12(anchorReal, "agents", canonicalName, CANONICAL_FILE));
7739
8050
  let viewLink = false;
7740
8051
  const viewPath = manifest.workspace.view;
7741
8052
  if (viewPath !== void 0) {
7742
8053
  try {
7743
- lstatSync(join11(resolveViewDir(repositoryRoot, viewPath), canonicalName));
8054
+ lstatSync(join12(resolveViewDir(repositoryRoot, viewPath), canonicalName));
7744
8055
  viewLink = true;
7745
8056
  } catch {
7746
8057
  }
@@ -7754,11 +8065,11 @@ function gatherArchiveTeardown(repositoryRoot, manifest, target) {
7754
8065
  };
7755
8066
  }
7756
8067
  function teardownExpectedTargets(repoReal, anchorReal, canonicalName) {
7757
- const canonicalFile = join11(anchorReal, "agents", canonicalName, CANONICAL_FILE);
8068
+ const canonicalFile = join12(anchorReal, "agents", canonicalName, CANONICAL_FILE);
7758
8069
  return expectedSymlinkTargets(repoReal, canonicalFile);
7759
8070
  }
7760
8071
  function viewLinkPointsAt(viewDir, name, repoReal) {
7761
- const filePath = join11(viewDir, name);
8072
+ const filePath = join12(viewDir, name);
7762
8073
  try {
7763
8074
  if (!lstatSync(filePath).isSymbolicLink()) return false;
7764
8075
  const target = readlinkSync(filePath);
@@ -7769,7 +8080,7 @@ function viewLinkPointsAt(viewDir, name, repoReal) {
7769
8080
  }
7770
8081
  }
7771
8082
  function viewLinkPointsAtPath(viewDir, name, expectedRepoPath) {
7772
- const filePath = join11(viewDir, name);
8083
+ const filePath = join12(viewDir, name);
7773
8084
  try {
7774
8085
  if (!lstatSync(filePath).isSymbolicLink()) return false;
7775
8086
  const target = readlinkSync(filePath);
@@ -7820,7 +8131,7 @@ function gatherRepoTeardown(repositoryRoot, manifest, target) {
7820
8131
  if (!isAnchor) {
7821
8132
  if (repoReal !== void 0) {
7822
8133
  for (const spec of teardownExpectedTargets(repoReal, anchorReal, canonicalName)) {
7823
- const { state, actualTarget } = inspectSymlink(join11(repoReal, spec.name), spec.target);
8134
+ const { state, actualTarget } = inspectSymlink(join12(repoReal, spec.name), spec.target);
7824
8135
  if (isSelf) {
7825
8136
  if (state !== "missing")
7826
8137
  items.push({
@@ -7857,7 +8168,7 @@ function gatherRepoTeardown(repositoryRoot, manifest, target) {
7857
8168
  }
7858
8169
  let ignored;
7859
8170
  try {
7860
- ignored = new Set(readGitignoreLines(join11(repoReal, ".gitignore")).map((l) => l.trim()));
8171
+ ignored = new Set(readGitignoreLines(join12(repoReal, ".gitignore")).map((l) => l.trim()));
7861
8172
  for (const p of INSTRUCTION_FILES) {
7862
8173
  if (ignored.has(p) || ignored.has(`/${p}`)) {
7863
8174
  items.push({
@@ -7880,7 +8191,7 @@ function gatherRepoTeardown(repositoryRoot, manifest, target) {
7880
8191
  const viewPath = manifest.workspace.view;
7881
8192
  if (viewPath !== void 0) {
7882
8193
  const viewDir = resolveViewDir(repositoryRoot, viewPath);
7883
- const linkPath = join11(viewDir, canonicalName);
8194
+ const linkPath = join12(viewDir, canonicalName);
7884
8195
  let isLink = false;
7885
8196
  try {
7886
8197
  isLink = lstatSync(linkPath).isSymbolicLink();
@@ -7906,8 +8217,8 @@ function gatherRepoTeardown(repositoryRoot, manifest, target) {
7906
8217
  else items.push({ kind: "view-symlink", label: canonicalName, state: "removable" });
7907
8218
  }
7908
8219
  }
7909
- const canonicalFile = join11(anchorReal, "agents", canonicalName, CANONICAL_FILE);
7910
- const canonicalLabel = join11("agents", canonicalName, CANONICAL_FILE);
8220
+ const canonicalFile = join12(anchorReal, "agents", canonicalName, CANONICAL_FILE);
8221
+ const canonicalLabel = join12("agents", canonicalName, CANONICAL_FILE);
7911
8222
  let canonicalIsLink = false;
7912
8223
  try {
7913
8224
  canonicalIsLink = lstatSync(canonicalFile).isSymbolicLink();
@@ -8011,12 +8322,12 @@ function applyRepoTeardown(repositoryRoot, manifest, plan) {
8011
8322
  );
8012
8323
  for (const item of removable.filter((i) => i.kind === "instruction-symlink")) {
8013
8324
  const expected = expectedByName.get(item.label);
8014
- if (repoReal === null || expected === void 0 || inspectSymlink(join11(repoReal, item.label), expected).state !== "correct") {
8325
+ if (repoReal === null || expected === void 0 || inspectSymlink(join12(repoReal, item.label), expected).state !== "correct") {
8015
8326
  changed(item.label);
8016
8327
  continue;
8017
8328
  }
8018
8329
  try {
8019
- unlinkSync(join11(repoReal, item.label));
8330
+ unlinkSync(join12(repoReal, item.label));
8020
8331
  removed.push(item.label);
8021
8332
  } catch (error) {
8022
8333
  failed.push({ label: item.label, message: failureReason(error) });
@@ -8035,7 +8346,7 @@ function applyRepoTeardown(repositoryRoot, manifest, plan) {
8035
8346
  continue;
8036
8347
  }
8037
8348
  try {
8038
- unlinkSync(join11(viewDir, item.label));
8349
+ unlinkSync(join12(viewDir, item.label));
8039
8350
  removed.push(`view/${item.label}`);
8040
8351
  } catch (error) {
8041
8352
  failed.push({ label: `view/${item.label}`, message: failureReason(error) });
@@ -8043,7 +8354,7 @@ function applyRepoTeardown(repositoryRoot, manifest, plan) {
8043
8354
  }
8044
8355
  const NOFOLLOW = fsConstants.O_NOFOLLOW ?? 0;
8045
8356
  for (const item of removable.filter((i) => i.kind === "canonical-block")) {
8046
- const canonicalFile = join11(anchorReal, "agents", canonicalName, CANONICAL_FILE);
8357
+ const canonicalFile = join12(anchorReal, "agents", canonicalName, CANONICAL_FILE);
8047
8358
  try {
8048
8359
  if (lstatSync(canonicalFile).isSymbolicLink()) {
8049
8360
  changed(item.label);
@@ -8145,7 +8456,7 @@ async function doRunProjectTeardown(target, options, ctx = {}) {
8145
8456
  const cwd = ctx.cwd ?? process.cwd();
8146
8457
  const repositoryRoot = await resolveBasouRootForCommand(cwd, "project teardown");
8147
8458
  const paths = basouPaths13(repositoryRoot);
8148
- const manifest = await readManifest8(paths);
8459
+ const manifest = await readManifest9(paths);
8149
8460
  const plan = gatherRepoTeardown(repositoryRoot, manifest, target);
8150
8461
  const willApply = options.apply === true && !plan.isAnchor && plan.removableCount > 0;
8151
8462
  const { removed, failed } = willApply ? applyRepoTeardown(repositoryRoot, manifest, plan) : { removed: [], failed: [] };
@@ -8187,7 +8498,7 @@ async function doRunProjectArchive(target, options, ctx) {
8187
8498
  const cwd = ctx.cwd ?? process.cwd();
8188
8499
  const repositoryRoot = await resolveBasouRootForCommand(cwd, "project archive");
8189
8500
  const paths = basouPaths13(repositoryRoot);
8190
- const manifest = await readManifest8(paths);
8501
+ const manifest = await readManifest9(paths);
8191
8502
  const roster = manifest.repos ?? [];
8192
8503
  let targetIsAnchor = false;
8193
8504
  try {
@@ -8317,12 +8628,12 @@ function gatherRenameWiring(repositoryRoot, manifest, oldBasename) {
8317
8628
  } catch {
8318
8629
  return { canonicalDirOld: false, viewLinkOld: false };
8319
8630
  }
8320
- const canonicalDirOld = existsSync(join11(anchorReal, "agents", oldBasename));
8631
+ const canonicalDirOld = existsSync(join12(anchorReal, "agents", oldBasename));
8321
8632
  let viewLinkOld = false;
8322
8633
  const viewPath = manifest.workspace.view;
8323
8634
  if (viewPath !== void 0) {
8324
8635
  try {
8325
- lstatSync(join11(resolveViewDir(repositoryRoot, viewPath), oldBasename));
8636
+ lstatSync(join12(resolveViewDir(repositoryRoot, viewPath), oldBasename));
8326
8637
  viewLinkOld = true;
8327
8638
  } catch {
8328
8639
  }
@@ -8344,7 +8655,7 @@ async function doRunProjectRename(oldPath, newPath, options, ctx) {
8344
8655
  const cwd = ctx.cwd ?? process.cwd();
8345
8656
  const repositoryRoot = await resolveBasouRootForCommand(cwd, "project rename");
8346
8657
  const paths = basouPaths13(repositoryRoot);
8347
- const manifest = await readManifest8(paths);
8658
+ const manifest = await readManifest9(paths);
8348
8659
  const roster = manifest.repos ?? [];
8349
8660
  let oldIsAnchor = false;
8350
8661
  try {
@@ -8618,7 +8929,7 @@ async function doRunProjectDerive(options, ctx) {
8618
8929
  const cwd = ctx.cwd ?? process.cwd();
8619
8930
  const repositoryRoot = await resolveBasouRootForCommand(cwd, "project derive");
8620
8931
  const paths = basouPaths13(repositoryRoot);
8621
- const manifest = await readManifest8(paths);
8932
+ const manifest = await readManifest9(paths);
8622
8933
  if (manifest.repos === void 0 || manifest.repos.length === 0) {
8623
8934
  console.log(
8624
8935
  "# Generate project wiring in one pass (declaration \u2192 wiring)\n\n\u2139\uFE0F No repo roster declared (manifest `repos`). Declare one first with `basou project new` (new project) or `basou project adopt` (bootstrap from existing source_roots)."
@@ -8659,7 +8970,7 @@ async function doRunProjectSeedAnchor(options, ctx) {
8659
8970
  const cwd = ctx.cwd ?? process.cwd();
8660
8971
  const repositoryRoot = await resolveBasouRootForCommand(cwd, "project derive");
8661
8972
  const paths = basouPaths13(repositoryRoot);
8662
- const manifest = await readManifest8(paths);
8973
+ const manifest = await readManifest9(paths);
8663
8974
  const roster = manifest.repos ?? [];
8664
8975
  console.log("# Anchor instruction-file seed (the planning master's own AGENTS.md)");
8665
8976
  console.log("");
@@ -8667,7 +8978,7 @@ async function doRunProjectSeedAnchor(options, ctx) {
8667
8978
  console.log("\u2139\uFE0F No repo roster declared \u2014 nothing to seed.");
8668
8979
  return;
8669
8980
  }
8670
- const anchorDoc = join11(repositoryRoot, CANONICAL_FILE);
8981
+ const anchorDoc = join12(repositoryRoot, CANONICAL_FILE);
8671
8982
  if (pathPresent(anchorDoc)) {
8672
8983
  console.log(
8673
8984
  `\u2705 The anchor's own \`${CANONICAL_FILE}\` already exists \u2014 hand-maintained, left untouched.`
@@ -8729,7 +9040,7 @@ function regularFileSpokes(repoReal) {
8729
9040
  const out = [];
8730
9041
  for (const spoke of ["CLAUDE.md", ".github/copilot-instructions.md"]) {
8731
9042
  try {
8732
- const st = lstatSync(join11(repoReal, spoke));
9043
+ const st = lstatSync(join12(repoReal, spoke));
8733
9044
  if (!st.isSymbolicLink() && st.isFile()) out.push(spoke);
8734
9045
  } catch {
8735
9046
  }
@@ -8775,8 +9086,8 @@ function gatherRetrofit(repositoryRoot, anchorReal, roster, argAbs, argReal, vie
8775
9086
  };
8776
9087
  }
8777
9088
  const isAnchor = argReal === anchorReal;
8778
- const reachable = existsSync(join11(argReal, ".git"));
8779
- const canonicalFile = join11(anchorReal, "agents", canonicalName, CANONICAL_FILE);
9089
+ const reachable = existsSync(join12(argReal, ".git"));
9090
+ const canonicalFile = join12(anchorReal, "agents", canonicalName, CANONICAL_FILE);
8780
9091
  return {
8781
9092
  path,
8782
9093
  declared,
@@ -8785,15 +9096,15 @@ function gatherRetrofit(repositoryRoot, anchorReal, roster, argAbs, argReal, vie
8785
9096
  reachable,
8786
9097
  canonicalName,
8787
9098
  ...viewCanonicalName !== void 0 ? { viewCanonicalName } : {},
8788
- agentsState: inspectAgentsState(join11(argReal, CANONICAL_FILE)),
9099
+ agentsState: inspectAgentsState(join12(argReal, CANONICAL_FILE)),
8789
9100
  canonicalExists: pathPresent(canonicalFile),
8790
9101
  regularSpokes: regularFileSpokes(argReal)
8791
9102
  };
8792
9103
  }
8793
9104
  function relocateAgentsFile(repoReal, canonicalFile) {
8794
- const agentsFile = join11(repoReal, CANONICAL_FILE);
9105
+ const agentsFile = join12(repoReal, CANONICAL_FILE);
8795
9106
  try {
8796
- mkdirSync(dirname6(canonicalFile), { recursive: true });
9107
+ mkdirSync(dirname7(canonicalFile), { recursive: true });
8797
9108
  } catch (error) {
8798
9109
  return { ok: false, message: failureReason(error), partial: false };
8799
9110
  }
@@ -8853,7 +9164,7 @@ async function doRunProjectRetrofit(repo, options, ctx) {
8853
9164
  const cwd = ctx.cwd ?? process.cwd();
8854
9165
  const repositoryRoot = await resolveBasouRootForCommand(cwd, "project retrofit");
8855
9166
  const paths = basouPaths13(repositoryRoot);
8856
- const manifest = await readManifest8(paths);
9167
+ const manifest = await readManifest9(paths);
8857
9168
  const roster = manifest.repos ?? [];
8858
9169
  const anchorReal = realpathSync(repositoryRoot);
8859
9170
  const viewPath = manifest.workspace.view;
@@ -8907,7 +9218,7 @@ async function doRunProjectRetrofit(repo, options, ctx) {
8907
9218
  let failure;
8908
9219
  let partial = false;
8909
9220
  if (options.apply === true && plan.action === "relocate" && argReal !== void 0) {
8910
- const canonicalFile = join11(anchorReal, "agents", plan.canonicalName, CANONICAL_FILE);
9221
+ const canonicalFile = join12(anchorReal, "agents", plan.canonicalName, CANONICAL_FILE);
8911
9222
  const res = relocateAgentsFile(argReal, canonicalFile);
8912
9223
  if (res.ok) {
8913
9224
  applied = true;
@@ -9292,14 +9603,14 @@ import {
9292
9603
  basouPaths as basouPaths14,
9293
9604
  findErrorCode as findErrorCode10,
9294
9605
  loadPortfolioConfig as loadPortfolioConfig5,
9295
- readManifest as readManifest9
9606
+ readManifest as readManifest10
9296
9607
  } from "@basou/core";
9297
9608
  import { InvalidArgumentError as InvalidArgumentError4 } from "commander";
9298
9609
 
9299
9610
  // src/commands/refresh-watch.ts
9300
- import { readdir as readdir2, stat as stat6 } from "fs/promises";
9611
+ import { readdir as readdir3, stat as stat6 } from "fs/promises";
9301
9612
  import { homedir as homedir9 } from "os";
9302
- import { join as join12 } from "path";
9613
+ import { join as join13 } from "path";
9303
9614
  import {
9304
9615
  assertApprovalStoreSafe as assertApprovalStoreSafe2,
9305
9616
  assertLockStoreSafe as assertLockStoreSafe4,
@@ -9311,8 +9622,8 @@ var MIN_WATCH_INTERVAL_SEC = 5;
9311
9622
  var MAX_WATCH_INTERVAL_SEC = 86400;
9312
9623
  function watchedRoots(ctx) {
9313
9624
  return [
9314
- ctx.codexSessionsDir ?? join12(homedir9(), ".codex", "sessions"),
9315
- ctx.claudeProjectsDir ?? join12(homedir9(), ".claude", "projects")
9625
+ ctx.codexSessionsDir ?? join13(homedir9(), ".codex", "sessions"),
9626
+ ctx.claudeProjectsDir ?? join13(homedir9(), ".claude", "projects")
9316
9627
  ];
9317
9628
  }
9318
9629
  async function scanSourceLogs(roots) {
@@ -9320,13 +9631,13 @@ async function scanSourceLogs(roots) {
9320
9631
  const walk = async (dir) => {
9321
9632
  let entries;
9322
9633
  try {
9323
- entries = await readdir2(dir, { withFileTypes: true });
9634
+ entries = await readdir3(dir, { withFileTypes: true });
9324
9635
  } catch (error) {
9325
9636
  if (findErrorCode9(error, "ENOENT") || findErrorCode9(error, "ENOTDIR")) return;
9326
9637
  throw new Error("Failed to read a source log directory", { cause: error });
9327
9638
  }
9328
9639
  for (const entry of entries) {
9329
- const full = join12(dir, entry.name);
9640
+ const full = join13(dir, entry.name);
9330
9641
  if (entry.isDirectory()) {
9331
9642
  await walk(full);
9332
9643
  } else if (entry.isFile() && entry.name.endsWith(".jsonl")) {
@@ -9609,7 +9920,7 @@ async function warnPosition(paths, result, ctx) {
9609
9920
  }
9610
9921
  async function retiredChannelNotice(paths) {
9611
9922
  try {
9612
- const manifest = await readManifest9(paths);
9923
+ const manifest = await readManifest10(paths);
9613
9924
  if (manifest.channels?.codex !== true) return null;
9614
9925
  return "codex channel: retired \u2014 the manifest's channels.codex is ignored; a Codex session now receives this workspace's position from the SessionStart hook (see `basou hook status codex`), and nothing is written to the user-global ~/.codex/AGENTS.md";
9615
9926
  } catch {
@@ -9752,7 +10063,7 @@ import {
9752
10063
  findErrorCode as findErrorCode12,
9753
10064
  findUnbindableRepos,
9754
10065
  parseReviewRecordInput,
9755
- readManifest as readManifest10,
10066
+ readManifest as readManifest11,
9756
10067
  resolveRepoRoot,
9757
10068
  sanitizePath as sanitizePath3
9758
10069
  } from "@basou/core";
@@ -9839,7 +10150,7 @@ async function doRunReviewRecord(options, ctx) {
9839
10150
  }
9840
10151
  const now = ctx.nowProvider !== void 0 ? ctx.nowProvider() : /* @__PURE__ */ new Date();
9841
10152
  const occurredAt = now.toISOString();
9842
- const manifest = await readManifest10(paths);
10153
+ const manifest = await readManifest11(paths);
9843
10154
  const invocationArgs = options.file !== void 0 ? [
9844
10155
  "--file",
9845
10156
  sanitizePath3(resolve11(cwd, options.file), {
@@ -10179,7 +10490,7 @@ function unattachedLines(u) {
10179
10490
  // src/commands/run.ts
10180
10491
  import { mkdir as mkdir3, readFile as readFile8, rm as rm3 } from "fs/promises";
10181
10492
  import { homedir as homedir11 } from "os";
10182
- import { join as join13 } from "path";
10493
+ import { join as join14 } from "path";
10183
10494
  import {
10184
10495
  acquireLock as acquireLock5,
10185
10496
  assertBasouRootSafe as assertBasouRootSafe13,
@@ -10196,7 +10507,7 @@ import {
10196
10507
  getSnapshot as getSnapshot2,
10197
10508
  overwriteYamlFile as overwriteYamlFile2,
10198
10509
  prefixedUlid as prefixedUlid4,
10199
- readManifest as readManifest11,
10510
+ readManifest as readManifest12,
10200
10511
  readYamlFile as readYamlFile5,
10201
10512
  resolveClaudeCodeCommand,
10202
10513
  resolveCodexCommand,
@@ -10257,15 +10568,15 @@ async function runTrackedTool(args, options, ctx, adapter) {
10257
10568
  const repoRoot = await resolveRepositoryRootInPlace(cwd, "run");
10258
10569
  const paths = basouPaths18(repoRoot);
10259
10570
  await assertBasouRootSafe13(paths.root);
10260
- const manifest = await readManifest11(paths);
10571
+ const manifest = await readManifest12(paths);
10261
10572
  await assertSessionStoreSafe3(paths);
10262
10573
  const sessionId = prefixedUlid4("ses");
10263
- const sessionDir = join13(paths.sessions, sessionId);
10574
+ const sessionDir = join14(paths.sessions, sessionId);
10264
10575
  const appendEvent = ctx.appendEvent ?? (async (_sessionDir, event) => {
10265
10576
  await coreAppendChainedEvent2(paths, sessionId, event);
10266
10577
  });
10267
10578
  const startedAt = now().toISOString();
10268
- const sessionYamlPath = join13(sessionDir, "session.yaml");
10579
+ const sessionYamlPath = join14(sessionDir, "session.yaml");
10269
10580
  const session = buildInitialSession2({
10270
10581
  id: sessionId,
10271
10582
  command,
@@ -10642,7 +10953,7 @@ async function noteCodexHookStatusPreSpawn(_cwd, ctx) {
10642
10953
 
10643
10954
  // src/commands/session.ts
10644
10955
  import { readFile as readFile9 } from "fs/promises";
10645
- import { basename as basename8, isAbsolute as isAbsolute5, join as join14, relative as relative4, sep as sep3 } from "path";
10956
+ import { basename as basename8, isAbsolute as isAbsolute5, join as join15, relative as relative4, sep as sep3 } from "path";
10646
10957
  import {
10647
10958
  acquireLock as acquireLock6,
10648
10959
  appendEventToExistingSession as appendEventToExistingSession3,
@@ -10658,7 +10969,7 @@ import {
10658
10969
  loadSessionEntries as loadSessionEntries2,
10659
10970
  normalizeSessionTimestamps,
10660
10971
  readAllEvents,
10661
- readManifest as readManifest12,
10972
+ readManifest as readManifest13,
10662
10973
  readObservedDuration,
10663
10974
  readYamlFile as readYamlFile6,
10664
10975
  rechainSessionInPlace,
@@ -10774,8 +11085,8 @@ async function doRunSessionShow(idInput, options, ctx) {
10774
11085
  const paths = basouPaths19(repositoryRoot);
10775
11086
  await assertWorkspaceInitialized12(paths.root);
10776
11087
  const sessionId = await resolveSessionId3(paths, idInput);
10777
- const sessionDir = join14(paths.sessions, sessionId);
10778
- const sessionYamlPath = join14(sessionDir, "session.yaml");
11088
+ const sessionDir = join15(paths.sessions, sessionId);
11089
+ const sessionYamlPath = join15(sessionDir, "session.yaml");
10779
11090
  let session;
10780
11091
  try {
10781
11092
  const raw = await readYamlFile6(sessionYamlPath);
@@ -11085,7 +11396,7 @@ async function doRunSessionImport(options, ctx) {
11085
11396
  const repositoryRoot = await resolveRepositoryRootForSession(cwd, "import");
11086
11397
  const paths = basouPaths19(repositoryRoot);
11087
11398
  await assertWorkspaceInitialized12(paths.root);
11088
- const manifest = await readManifest12(paths);
11399
+ const manifest = await readManifest13(paths);
11089
11400
  const rawBody = await readInputFile(options.from);
11090
11401
  const json = parseJsonStrict(rawBody);
11091
11402
  const parsed = SessionImportPayloadSchema2.safeParse(json);
@@ -11471,7 +11782,7 @@ import {
11471
11782
  basouPaths as basouPaths21,
11472
11783
  buildStatusSnapshot,
11473
11784
  findErrorCode as findErrorCode15,
11474
- readManifest as readManifest13,
11785
+ readManifest as readManifest14,
11475
11786
  writeStatus
11476
11787
  } from "@basou/core";
11477
11788
  function registerStatusCommand(program2) {
@@ -11501,7 +11812,7 @@ async function doRunStatus(options, ctx) {
11501
11812
  }
11502
11813
  let manifest;
11503
11814
  try {
11504
- manifest = await readManifest13(paths);
11815
+ manifest = await readManifest14(paths);
11505
11816
  } catch (error) {
11506
11817
  if (findErrorCode15(error, "ENOENT")) {
11507
11818
  throw new Error("Workspace not initialized. Run 'basou init' first.");
@@ -11542,7 +11853,7 @@ function formatVersionGateMessage(error) {
11542
11853
 
11543
11854
  // src/commands/task.ts
11544
11855
  import { readFile as readFile10 } from "fs/promises";
11545
- import { join as join15 } from "path";
11856
+ import { join as join16 } from "path";
11546
11857
  import {
11547
11858
  archiveTask,
11548
11859
  assertBasouRootSafe as assertBasouRootSafe17,
@@ -11556,7 +11867,7 @@ import {
11556
11867
  loadSessionEntries as loadSessionEntries3,
11557
11868
  loadTaskEntries,
11558
11869
  prefixedUlid as prefixedUlid5,
11559
- readManifest as readManifest14,
11870
+ readManifest as readManifest15,
11560
11871
  readTaskFile,
11561
11872
  readTaskFileWithArchiveFallback,
11562
11873
  reconcileAllTasks,
@@ -11683,7 +11994,7 @@ async function doRunTaskNew(options, ctx) {
11683
11994
  });
11684
11995
  return;
11685
11996
  }
11686
- const manifest = await readManifest14(paths);
11997
+ const manifest = await readManifest15(paths);
11687
11998
  const result = await createTaskWithEvent({
11688
11999
  mode: "ad-hoc",
11689
12000
  paths,
@@ -11869,7 +12180,7 @@ async function doRunTaskShow(idInput, options, ctx) {
11869
12180
  const events = [];
11870
12181
  const linkedSessionIds = new Set(doc.task.task.linked_sessions);
11871
12182
  for (const s of sessions) {
11872
- const sessionDir = join15(paths.sessions, s.sessionId);
12183
+ const sessionDir = join16(paths.sessions, s.sessionId);
11873
12184
  try {
11874
12185
  for await (const ev of replayEvents3(sessionDir, {
11875
12186
  onWarning: (w) => printReplayWarning(w, s.sessionId)
@@ -12031,7 +12342,7 @@ async function doRunTaskStatus(taskIdInput, newStatusInput, options, ctx) {
12031
12342
  });
12032
12343
  return;
12033
12344
  }
12034
- const manifest = await readManifest14(paths);
12345
+ const manifest = await readManifest15(paths);
12035
12346
  const result = await updateTaskStatusWithEvent({
12036
12347
  mode: "ad-hoc",
12037
12348
  paths,
@@ -12085,7 +12396,7 @@ async function doRunTaskReconcile(options, ctx) {
12085
12396
  const paths = basouPaths22(repositoryRoot);
12086
12397
  await assertWorkspaceInitialized14(paths.root);
12087
12398
  await assertLockStoreSafe6(paths);
12088
- const manifest = await readManifest14(paths);
12399
+ const manifest = await readManifest15(paths);
12089
12400
  const nowProvider = ctx.nowProvider ?? (() => /* @__PURE__ */ new Date());
12090
12401
  const write = options.write === true;
12091
12402
  const verbose = isVerbose(options);
@@ -12265,7 +12576,7 @@ async function doRunTaskRefreshLinkage(taskIdInput, options, ctx) {
12265
12576
  const repositoryRoot = await resolveRepositoryRootForTask(cwd, "refresh-linkage");
12266
12577
  const paths = basouPaths22(repositoryRoot);
12267
12578
  await assertWorkspaceInitialized14(paths.root);
12268
- const manifest = await readManifest14(paths);
12579
+ const manifest = await readManifest15(paths);
12269
12580
  const taskId = await resolveTaskId2(paths, taskIdInput);
12270
12581
  const nowProvider = ctx.nowProvider ?? (() => /* @__PURE__ */ new Date());
12271
12582
  const write = options.write === true;
@@ -12345,7 +12656,7 @@ async function doRunTaskEdit(taskIdInput, options, ctx) {
12345
12656
  const repositoryRoot = await resolveRepositoryRootForTask(cwd, "edit");
12346
12657
  const paths = basouPaths22(repositoryRoot);
12347
12658
  await assertWorkspaceInitialized14(paths.root);
12348
- const manifest = await readManifest14(paths);
12659
+ const manifest = await readManifest15(paths);
12349
12660
  const taskId = await resolveTaskId2(paths, taskIdInput);
12350
12661
  const now = ctx.nowProvider !== void 0 ? ctx.nowProvider() : /* @__PURE__ */ new Date();
12351
12662
  const occurredAt = now.toISOString();
@@ -12401,7 +12712,7 @@ async function doRunTaskDelete(taskIdInput, options, ctx) {
12401
12712
  const repositoryRoot = await resolveRepositoryRootForTask(cwd, "delete");
12402
12713
  const paths = basouPaths22(repositoryRoot);
12403
12714
  await assertWorkspaceInitialized14(paths.root);
12404
- const manifest = await readManifest14(paths);
12715
+ const manifest = await readManifest15(paths);
12405
12716
  const taskId = await resolveTaskId2(paths, taskIdInput);
12406
12717
  if (options.yes !== true) {
12407
12718
  await confirmDestructiveAction("delete", taskId);
@@ -12446,7 +12757,7 @@ async function doRunTaskArchive(taskIdInput, options, ctx) {
12446
12757
  const repositoryRoot = await resolveRepositoryRootForTask(cwd, "archive");
12447
12758
  const paths = basouPaths22(repositoryRoot);
12448
12759
  await assertWorkspaceInitialized14(paths.root);
12449
- const manifest = await readManifest14(paths);
12760
+ const manifest = await readManifest15(paths);
12450
12761
  const taskId = await resolveTaskId2(paths, taskIdInput);
12451
12762
  if (options.yes !== true) {
12452
12763
  await confirmDestructiveAction("archive", taskId);
@@ -12764,23 +13075,23 @@ async function assertWorkspaceInitialized15(basouRoot) {
12764
13075
  // src/commands/view.ts
12765
13076
  import { spawn } from "child_process";
12766
13077
  import { createHash as createHash2 } from "crypto";
12767
- import { basename as basename11, dirname as dirname8, join as join19, resolve as resolve14 } from "path";
13078
+ import { basename as basename11, dirname as dirname9, join as join20, resolve as resolve14 } from "path";
12768
13079
  import {
12769
13080
  assertBasouRootSafe as assertBasouRootSafe19,
12770
13081
  basouPaths as basouPaths25,
12771
13082
  findErrorCode as findErrorCode19,
12772
13083
  loadPortfolioConfig as loadPortfolioConfig6,
12773
- readManifest as readManifest18
13084
+ readManifest as readManifest19
12774
13085
  } from "@basou/core";
12775
13086
  import { InvalidArgumentError as InvalidArgumentError8 } from "commander";
12776
13087
 
12777
13088
  // src/lib/portfolio-coverage.ts
12778
13089
  import { createReadStream as createReadStream2 } from "fs";
12779
- import { readdir as readdir3, stat as stat7 } from "fs/promises";
13090
+ import { readdir as readdir4, stat as stat7 } from "fs/promises";
12780
13091
  import { homedir as homedir12 } from "os";
12781
- import { basename as basename9, dirname as dirname7, join as join16 } from "path";
13092
+ import { basename as basename9, dirname as dirname8, join as join17 } from "path";
12782
13093
  import { createInterface as createInterface2 } from "readline";
12783
- import { basouPaths as basouPaths24, readManifest as readManifest15 } from "@basou/core";
13094
+ import { basouPaths as basouPaths24, readManifest as readManifest16 } from "@basou/core";
12784
13095
  function uncapturedTotal(result) {
12785
13096
  return result.groups.reduce((sum, g) => sum + g.logs, 0) + result.unplaceable;
12786
13097
  }
@@ -12790,8 +13101,8 @@ async function checkPortfolioCoverage(workspaces, ctx = {}) {
12790
13101
  ctx.portfolioConfigPath
12791
13102
  );
12792
13103
  const listedDirs = new Set([...roots].map((root) => encodeProjectDir(root)));
12793
- const claudeProjectsDir = ctx.claudeProjectsDir ?? join16(homedir12(), ".claude", "projects");
12794
- const codexSessionsDir = ctx.codexSessionsDir ?? join16(homedir12(), ".codex", "sessions");
13104
+ const claudeProjectsDir = ctx.claudeProjectsDir ?? join17(homedir12(), ".claude", "projects");
13105
+ const codexSessionsDir = ctx.codexSessionsDir ?? join17(homedir12(), ".codex", "sessions");
12795
13106
  const tally = new Tally(roots);
12796
13107
  const absentTrees = [];
12797
13108
  const claudeFiles = await listClaudeTranscripts(claudeProjectsDir);
@@ -12802,7 +13113,7 @@ async function checkPortfolioCoverage(workspaces, ctx = {}) {
12802
13113
  file,
12803
13114
  "claude-code",
12804
13115
  claudeTranscriptCwd,
12805
- listedDirs.has(basename9(dirname7(file)))
13116
+ listedDirs.has(basename9(dirname8(file)))
12806
13117
  );
12807
13118
  }
12808
13119
  }
@@ -12829,7 +13140,7 @@ async function collectDeclaredRoots(workspaces, portfolioConfigPath) {
12829
13140
  }
12830
13141
  let resolved;
12831
13142
  try {
12832
- const manifest = await readManifest15(basouPaths24(importRoot));
13143
+ const manifest = await readManifest16(basouPaths24(importRoot));
12833
13144
  resolved = resolveSourceRoots({
12834
13145
  projectFlags: [],
12835
13146
  manifest,
@@ -12929,7 +13240,7 @@ async function isDirEntry(parent, entry) {
12929
13240
  if (entry.isDirectory()) return true;
12930
13241
  if (!entry.isSymbolicLink()) return false;
12931
13242
  try {
12932
- return (await stat7(join16(parent, entry.name))).isDirectory();
13243
+ return (await stat7(join17(parent, entry.name))).isDirectory();
12933
13244
  } catch {
12934
13245
  return false;
12935
13246
  }
@@ -12937,22 +13248,22 @@ async function isDirEntry(parent, entry) {
12937
13248
  async function listClaudeTranscripts(projectsRoot) {
12938
13249
  let entries;
12939
13250
  try {
12940
- entries = await readdir3(projectsRoot, { withFileTypes: true });
13251
+ entries = await readdir4(projectsRoot, { withFileTypes: true });
12941
13252
  } catch {
12942
13253
  return void 0;
12943
13254
  }
12944
13255
  const files = [];
12945
13256
  for (const entry of entries) {
12946
13257
  if (!await isDirEntry(projectsRoot, entry)) continue;
12947
- const full = join16(projectsRoot, entry.name);
13258
+ const full = join17(projectsRoot, entry.name);
12948
13259
  let names;
12949
13260
  try {
12950
- names = await readdir3(full);
13261
+ names = await readdir4(full);
12951
13262
  } catch {
12952
13263
  continue;
12953
13264
  }
12954
13265
  for (const name of names) {
12955
- if (name.endsWith(".jsonl")) files.push(join16(full, name));
13266
+ if (name.endsWith(".jsonl")) files.push(join17(full, name));
12956
13267
  }
12957
13268
  }
12958
13269
  return files.sort();
@@ -12962,12 +13273,12 @@ async function listCodexRollouts(sessionsRoot) {
12962
13273
  const walk = async (dir) => {
12963
13274
  let entries;
12964
13275
  try {
12965
- entries = await readdir3(dir, { withFileTypes: true });
13276
+ entries = await readdir4(dir, { withFileTypes: true });
12966
13277
  } catch {
12967
13278
  return;
12968
13279
  }
12969
13280
  for (const entry of entries) {
12970
- const full = join16(dir, entry.name);
13281
+ const full = join17(dir, entry.name);
12971
13282
  if (entry.isDirectory()) await walk(full);
12972
13283
  else if (entry.isFile() && entry.name.startsWith("rollout-") && entry.name.endsWith(".jsonl")) {
12973
13284
  found.push(full);
@@ -12975,7 +13286,7 @@ async function listCodexRollouts(sessionsRoot) {
12975
13286
  }
12976
13287
  };
12977
13288
  try {
12978
- await readdir3(sessionsRoot);
13289
+ await readdir4(sessionsRoot);
12979
13290
  } catch {
12980
13291
  return void 0;
12981
13292
  }
@@ -13098,9 +13409,9 @@ function inertLines(result) {
13098
13409
  // src/lib/portfolio-safety.ts
13099
13410
  import { execFile as execFile2 } from "child_process";
13100
13411
  import { lstat as lstat3, realpath as realpath5 } from "fs/promises";
13101
- import { isAbsolute as isAbsolute6, join as join17, relative as relative5, resolve as resolve12, sep as sep4 } from "path";
13412
+ import { isAbsolute as isAbsolute6, join as join18, relative as relative5, resolve as resolve12, sep as sep4 } from "path";
13102
13413
  import { promisify as promisify2 } from "util";
13103
- import { readManifest as readManifest16 } from "@basou/core";
13414
+ import { readManifest as readManifest17 } from "@basou/core";
13104
13415
  var execFileAsync2 = promisify2(execFile2);
13105
13416
  function errorCode2(error) {
13106
13417
  return error instanceof Error ? error.code : void 0;
@@ -13122,7 +13433,7 @@ function isBasouPath(p) {
13122
13433
  async function inspectRepo(repoPath) {
13123
13434
  let hasEntry = false;
13124
13435
  try {
13125
- await lstat3(join17(repoPath, ".basou"));
13436
+ await lstat3(join18(repoPath, ".basou"));
13126
13437
  hasEntry = true;
13127
13438
  } catch (error) {
13128
13439
  if (errorCode2(error) !== "ENOENT") {
@@ -13156,7 +13467,7 @@ async function checkPortfolioSafety(workspaces) {
13156
13467
  let viewPath;
13157
13468
  let isMaster = false;
13158
13469
  try {
13159
- const manifest = await readManifest16(ws.paths);
13470
+ const manifest = await readManifest17(ws.paths);
13160
13471
  sourceRoots = manifest.import?.source_roots ?? [];
13161
13472
  viewPath = manifest.workspace.view;
13162
13473
  isMaster = true;
@@ -13276,7 +13587,7 @@ function formatSafetyReport(result) {
13276
13587
 
13277
13588
  // src/lib/view-server.ts
13278
13589
  import { createServer } from "http";
13279
- import { basename as basename10, join as join18, resolve as resolve13 } from "path";
13590
+ import { basename as basename10, join as join19, resolve as resolve13 } from "path";
13280
13591
  import {
13281
13592
  boardLivePage,
13282
13593
  boardPage,
@@ -13291,7 +13602,7 @@ import {
13291
13602
  loadSessionEntries as loadSessionEntries4,
13292
13603
  loadTaskEntries as loadTaskEntries2,
13293
13604
  readAllEvents as readAllEvents2,
13294
- readManifest as readManifest17,
13605
+ readManifest as readManifest18,
13295
13606
  readMarkdownFile as readMarkdownFile9,
13296
13607
  readSessionYaml as readSessionYaml3,
13297
13608
  readTaskFile as readTaskFile2,
@@ -15215,7 +15526,7 @@ async function captureStaleness(ws, nowIso) {
15215
15526
  async function overview(ws, nowProvider, resolveRemoteUrl) {
15216
15527
  let manifest;
15217
15528
  try {
15218
- manifest = await readManifest17(ws.paths);
15529
+ manifest = await readManifest18(ws.paths);
15219
15530
  } catch (error) {
15220
15531
  if (findErrorCode18(error, "ENOENT")) {
15221
15532
  return { initialized: false, repoRoot: ws.repoRoot };
@@ -15300,7 +15611,7 @@ async function sessionDetail(ws, sessionId) {
15300
15611
  throw error;
15301
15612
  }
15302
15613
  try {
15303
- const events = await readAllEvents2(join18(ws.paths.sessions, sessionId));
15614
+ const events = await readAllEvents2(join19(ws.paths.sessions, sessionId));
15304
15615
  return { session, events };
15305
15616
  } catch {
15306
15617
  return { session, events: [], degraded: true };
@@ -15560,12 +15871,12 @@ function sharedWhileRunning(run) {
15560
15871
  async function viewBoardOf(root, paths) {
15561
15872
  let own;
15562
15873
  try {
15563
- own = (await readManifest18(paths)).repos?.find((repo) => repo.path === ".");
15874
+ own = (await readManifest19(paths)).repos?.find((repo) => repo.path === ".");
15564
15875
  } catch {
15565
15876
  own = void 0;
15566
15877
  }
15567
15878
  if (own?.visibility !== "private") return { why: "no_board" };
15568
- return { recordsDir: join19(root, dirname8(DEFAULT_BOARD_PATH), "records") };
15879
+ return { recordsDir: join20(root, dirname9(DEFAULT_BOARD_PATH), "records") };
15569
15880
  }
15570
15881
  async function buildPortfolioDeps(workspaceFlags, ctx, cwd) {
15571
15882
  const specs = workspaceFlags.length > 0 ? workspaceFlags.map((p) => ({ path: resolve14(cwd, p) })) : await loadPortfolioConfig6(ctx.portfolioConfigPath);
@@ -15598,7 +15909,7 @@ async function buildWorkspaceEntry(repoRoot, ctx, labelOverride) {
15598
15909
  ...ctx.codexSessionsDir !== void 0 ? { codexSessionsDir: ctx.codexSessionsDir } : {}
15599
15910
  };
15600
15911
  try {
15601
- const manifest = await readManifest18(paths);
15912
+ const manifest = await readManifest19(paths);
15602
15913
  return {
15603
15914
  key: manifest.workspace.id,
15604
15915
  label: labelOverride ?? manifest.workspace.name,
@@ -15719,7 +16030,7 @@ function installExperimentalNotice(program2, names = EXPERIMENTAL_COMMANDS, writ
15719
16030
  function readBuildStamp() {
15720
16031
  if (false) return void 0;
15721
16032
  try {
15722
- return JSON.parse('{"version":"0.67.0","commit":"c4c4ebb","committedAt":"2026-10-08T23:33:17+09:00"}');
16033
+ return JSON.parse('{"version":"0.68.0","commit":"52aa4d4","committedAt":"2026-10-09T22:45:36+09:00"}');
15723
16034
  } catch {
15724
16035
  return void 0;
15725
16036
  }