@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 +646 -335
- package/dist/index.js.map +1 -1
- package/dist/program.js +646 -335
- package/dist/program.js.map +1 -1
- package/package.json +2 -2
package/dist/program.js
CHANGED
|
@@ -828,33 +828,190 @@ function printNoApprovals(options) {
|
|
|
828
828
|
}
|
|
829
829
|
|
|
830
830
|
// src/commands/board.ts
|
|
831
|
-
import { lstat, readFile as readFile2, realpath as
|
|
832
|
-
import { basename as
|
|
831
|
+
import { lstat, readdir as readdir2, readFile as readFile2, realpath as realpath3 } from "fs/promises";
|
|
832
|
+
import { basename as basename4, dirname as dirname5, join as join4, relative, resolve as resolve3, sep as sep2 } from "path";
|
|
833
833
|
import {
|
|
834
834
|
assertBasouRootSafe as assertBasouRootSafe3,
|
|
835
|
+
BOARD_INIT_SAVE,
|
|
835
836
|
basouPaths as basouPaths4,
|
|
837
|
+
boardGuide,
|
|
838
|
+
boardInitText,
|
|
836
839
|
buildRecord,
|
|
837
840
|
byCodePoint,
|
|
838
841
|
diffCells,
|
|
839
842
|
diffObserved,
|
|
840
843
|
displayPath as displayPath3,
|
|
844
|
+
effortStartOf,
|
|
841
845
|
findErrorCode as findErrorCode3,
|
|
846
|
+
isBoardTimeZone,
|
|
842
847
|
measureBoard,
|
|
843
848
|
measureBoardLive,
|
|
844
849
|
NO_PREVIOUS_RECORDS,
|
|
845
850
|
parseBoardDeclaration,
|
|
846
851
|
parseRecordInput,
|
|
847
|
-
readManifest as
|
|
852
|
+
readManifest as readManifest4,
|
|
848
853
|
readPreviousRecords,
|
|
854
|
+
recordIds,
|
|
855
|
+
resolveViewLanguage,
|
|
856
|
+
shellWord,
|
|
849
857
|
writeRecord
|
|
850
858
|
} from "@basou/core";
|
|
851
859
|
|
|
860
|
+
// src/lib/foreign-workspace-warn.ts
|
|
861
|
+
import { realpath as realpath2 } from "fs/promises";
|
|
862
|
+
import { dirname } from "path";
|
|
863
|
+
import {
|
|
864
|
+
isPositionBoardLine,
|
|
865
|
+
loadPortfolioConfig as loadPortfolioConfig2,
|
|
866
|
+
PortfolioConfigMissingError,
|
|
867
|
+
readMarkdownFile
|
|
868
|
+
} from "@basou/core";
|
|
869
|
+
|
|
870
|
+
// src/lib/foreign-workspace-scan.ts
|
|
871
|
+
import { homedir as osHomedir } from "os";
|
|
872
|
+
import { basename as basename2, normalize, sep } from "path";
|
|
873
|
+
var MIN_TOKEN_LENGTH = 4;
|
|
874
|
+
function stripTrailingSep(p) {
|
|
875
|
+
return p.length > 1 && p.endsWith(sep) ? p.slice(0, -sep.length) : p;
|
|
876
|
+
}
|
|
877
|
+
function toTildePath(absPath, home) {
|
|
878
|
+
if (absPath === home) return "~";
|
|
879
|
+
if (absPath.startsWith(home + sep)) return `~${absPath.slice(home.length)}`;
|
|
880
|
+
return null;
|
|
881
|
+
}
|
|
882
|
+
function encodedSpelling(name) {
|
|
883
|
+
return name.replace(/[^a-zA-Z0-9]/g, "-");
|
|
884
|
+
}
|
|
885
|
+
function directoryNameSpellings(dir) {
|
|
886
|
+
const paired = /^(.+)-(planning|workspace)$/.exec(dir);
|
|
887
|
+
const plain = paired !== null ? [`${paired[1]}-planning`, `${paired[1]}-workspace`] : [dir, `${dir}-workspace`];
|
|
888
|
+
return [...plain, ...plain.map(encodedSpelling)];
|
|
889
|
+
}
|
|
890
|
+
function nameTokensFor(workspacePath, home) {
|
|
891
|
+
const abs = stripTrailingSep(normalize(workspacePath));
|
|
892
|
+
const tokens = /* @__PURE__ */ new Set([abs]);
|
|
893
|
+
const tilde = toTildePath(abs, home);
|
|
894
|
+
if (tilde !== null) tokens.add(tilde);
|
|
895
|
+
for (const name of directoryNameSpellings(basename2(abs))) tokens.add(name);
|
|
896
|
+
return [...tokens].filter((t) => t.length >= MIN_TOKEN_LENGTH);
|
|
897
|
+
}
|
|
898
|
+
function isSpellingOfSelf(workspacePath, selfPath) {
|
|
899
|
+
const own = new Set(directoryNameSpellings(basename2(stripTrailingSep(normalize(selfPath)))));
|
|
900
|
+
return directoryNameSpellings(basename2(stripTrailingSep(normalize(workspacePath)))).some(
|
|
901
|
+
(s) => own.has(s)
|
|
902
|
+
);
|
|
903
|
+
}
|
|
904
|
+
function scanForeignWorkspaceNames(input) {
|
|
905
|
+
if (input.text.length === 0 || input.workspacePaths.length === 0) return [];
|
|
906
|
+
const home = input.homedir ?? osHomedir();
|
|
907
|
+
const own = input.selfPath === void 0 ? [] : nameTokensFor(input.selfPath, home);
|
|
908
|
+
const lines = input.text.split("\n");
|
|
909
|
+
const hits = [];
|
|
910
|
+
for (const workspacePath of input.workspacePaths) {
|
|
911
|
+
if (input.selfPath !== void 0 && isSpellingOfSelf(workspacePath, input.selfPath)) continue;
|
|
912
|
+
const tokens = nameTokensFor(workspacePath, home).filter(
|
|
913
|
+
(token) => !own.some((ownToken) => ownToken.includes(token))
|
|
914
|
+
);
|
|
915
|
+
if (tokens.length === 0) continue;
|
|
916
|
+
const matched = /* @__PURE__ */ new Set();
|
|
917
|
+
const matchedLines = [];
|
|
918
|
+
for (const [index, line] of lines.entries()) {
|
|
919
|
+
let lineMatched = false;
|
|
920
|
+
for (const token of tokens) {
|
|
921
|
+
if (line.includes(token)) {
|
|
922
|
+
matched.add(token);
|
|
923
|
+
lineMatched = true;
|
|
924
|
+
}
|
|
925
|
+
}
|
|
926
|
+
if (lineMatched) matchedLines.push(index + 1);
|
|
927
|
+
}
|
|
928
|
+
if (matchedLines.length === 0) continue;
|
|
929
|
+
hits.push({
|
|
930
|
+
workspacePath,
|
|
931
|
+
tokens: [...matched].sort((a, b) => b.length - a.length || a.localeCompare(b)),
|
|
932
|
+
lines: matchedLines
|
|
933
|
+
});
|
|
934
|
+
}
|
|
935
|
+
return hits;
|
|
936
|
+
}
|
|
937
|
+
|
|
938
|
+
// src/lib/foreign-workspace-warn.ts
|
|
939
|
+
var MAX_LISTED_LINES = 5;
|
|
940
|
+
async function canonicalize(path) {
|
|
941
|
+
try {
|
|
942
|
+
return await realpath2(path);
|
|
943
|
+
} catch {
|
|
944
|
+
return path;
|
|
945
|
+
}
|
|
946
|
+
}
|
|
947
|
+
async function findForeignWorkspaceNames(args) {
|
|
948
|
+
let workspacePaths;
|
|
949
|
+
let selfPath;
|
|
950
|
+
try {
|
|
951
|
+
const workspaces = await loadPortfolioConfig2(args.configPath);
|
|
952
|
+
workspacePaths = await Promise.all(workspaces.map((w) => canonicalize(w.path)));
|
|
953
|
+
selfPath = args.selfPath === void 0 ? void 0 : await canonicalize(args.selfPath);
|
|
954
|
+
} catch {
|
|
955
|
+
return null;
|
|
956
|
+
}
|
|
957
|
+
const text = args.text.split("\n").map((line) => isPositionBoardLine(line) ? "" : line).join("\n");
|
|
958
|
+
const hits = scanForeignWorkspaceNames({ text, workspacePaths, selfPath });
|
|
959
|
+
if (hits.length === 0) return null;
|
|
960
|
+
const lines = [...new Set(hits.flatMap((h) => h.lines))].sort((a, b) => a - b);
|
|
961
|
+
return { workspaceCount: hits.length, lines };
|
|
962
|
+
}
|
|
963
|
+
async function findForeignWorkspaceFields(args) {
|
|
964
|
+
let workspacePaths;
|
|
965
|
+
let selfPath;
|
|
966
|
+
try {
|
|
967
|
+
const workspaces = await loadPortfolioConfig2(args.configPath);
|
|
968
|
+
workspacePaths = await Promise.all(workspaces.map((w) => canonicalize(w.path)));
|
|
969
|
+
selfPath = await canonicalize(args.selfPath);
|
|
970
|
+
} catch (error) {
|
|
971
|
+
return error instanceof PortfolioConfigMissingError ? { status: "no_registry" } : { status: "unreadable" };
|
|
972
|
+
}
|
|
973
|
+
const at = args.fields.filter(
|
|
974
|
+
(field) => scanForeignWorkspaceNames({ text: field.text, workspacePaths, selfPath }).length > 0
|
|
975
|
+
).map((field) => field.at);
|
|
976
|
+
return { status: "checked", at };
|
|
977
|
+
}
|
|
978
|
+
function describeForeignWorkspaceLines(lines) {
|
|
979
|
+
const listed = lines.slice(0, MAX_LISTED_LINES).join(", ");
|
|
980
|
+
const rest = lines.length - MAX_LISTED_LINES;
|
|
981
|
+
const numbers = rest > 0 ? `${listed} and ${rest} more` : listed;
|
|
982
|
+
return `${lines.length === 1 ? "line" : "lines"} ${numbers}`;
|
|
983
|
+
}
|
|
984
|
+
function positionForeignWorkspaceWarning(report, where) {
|
|
985
|
+
const subject = report.workspaceCount === 1 ? "names another registered workspace" : `names ${report.workspaceCount} other registered workspaces`;
|
|
986
|
+
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.`;
|
|
987
|
+
}
|
|
988
|
+
function protocolForeignWorkspaceWarning(report) {
|
|
989
|
+
const subject = report.workspaceCount === 1 ? "names a registered workspace" : `names ${report.workspaceCount} registered workspaces`;
|
|
990
|
+
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.`;
|
|
991
|
+
}
|
|
992
|
+
async function warnIfPositionNamesOtherWorkspaces(args) {
|
|
993
|
+
try {
|
|
994
|
+
const body = await readMarkdownFile(args.paths.files.orientation);
|
|
995
|
+
if (body === null) return;
|
|
996
|
+
const report = await findForeignWorkspaceNames({
|
|
997
|
+
text: body,
|
|
998
|
+
selfPath: dirname(args.paths.root),
|
|
999
|
+
configPath: args.configPath
|
|
1000
|
+
});
|
|
1001
|
+
if (report !== null) {
|
|
1002
|
+
console.error(positionForeignWorkspaceWarning(report, args.paths.files.orientation));
|
|
1003
|
+
}
|
|
1004
|
+
} catch {
|
|
1005
|
+
}
|
|
1006
|
+
}
|
|
1007
|
+
|
|
852
1008
|
// src/lib/provenance-actions.ts
|
|
1009
|
+
import { dirname as dirname4 } from "path";
|
|
853
1010
|
import {
|
|
854
1011
|
assertApprovalStoreSafe,
|
|
855
1012
|
assertLockStoreSafe as assertLockStoreSafe3,
|
|
856
1013
|
assertTaskStoreSafe,
|
|
857
|
-
readMarkdownFile,
|
|
1014
|
+
readMarkdownFile as readMarkdownFile2,
|
|
858
1015
|
renderDecisions,
|
|
859
1016
|
renderHandoff,
|
|
860
1017
|
renderOrientation,
|
|
@@ -866,7 +1023,7 @@ import {
|
|
|
866
1023
|
import { createReadStream } from "fs";
|
|
867
1024
|
import { readdir, readFile, rm, stat as stat2 } from "fs/promises";
|
|
868
1025
|
import { homedir } from "os";
|
|
869
|
-
import { basename as
|
|
1026
|
+
import { basename as basename3, dirname as dirname3, join as join2, resolve as resolve2 } from "path";
|
|
870
1027
|
import { createInterface } from "readline";
|
|
871
1028
|
import {
|
|
872
1029
|
AGENT_INFRA_DIRS,
|
|
@@ -893,7 +1050,7 @@ import {
|
|
|
893
1050
|
} from "@basou/core";
|
|
894
1051
|
|
|
895
1052
|
// src/lib/observation-warn.ts
|
|
896
|
-
import { dirname } from "path";
|
|
1053
|
+
import { dirname as dirname2 } from "path";
|
|
897
1054
|
import { assertObservationsDirSafe, displayPath } from "@basou/core";
|
|
898
1055
|
async function warnIfObservationsRefused(paths) {
|
|
899
1056
|
try {
|
|
@@ -901,7 +1058,7 @@ async function warnIfObservationsRefused(paths) {
|
|
|
901
1058
|
} catch (error) {
|
|
902
1059
|
const reason = error instanceof Error ? error.message : String(error);
|
|
903
1060
|
console.error(
|
|
904
|
-
`basou: ${reason} (in ${displayPath(
|
|
1061
|
+
`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.`
|
|
905
1062
|
);
|
|
906
1063
|
}
|
|
907
1064
|
}
|
|
@@ -978,7 +1135,7 @@ async function doRunImportClaudeCode(options, ctx) {
|
|
|
978
1135
|
const files = await selectTranscriptFiles(projectsRoot, projectPaths, options);
|
|
979
1136
|
const projectSet = new Set(projectPaths);
|
|
980
1137
|
const candidates = files.map((file) => {
|
|
981
|
-
const externalId =
|
|
1138
|
+
const externalId = basename3(file, ".jsonl");
|
|
982
1139
|
return {
|
|
983
1140
|
externalId,
|
|
984
1141
|
sourcePath: file,
|
|
@@ -1077,7 +1234,7 @@ async function importDerivedSessions(paths, manifest, options, sourceKind, candi
|
|
|
1077
1234
|
files: payload.session.related_files ?? [],
|
|
1078
1235
|
workingDirectory: payload.session.working_directory,
|
|
1079
1236
|
sourceRoots: projectPaths,
|
|
1080
|
-
masterRoot:
|
|
1237
|
+
masterRoot: dirname3(paths.root),
|
|
1081
1238
|
extraInRoot: AGENT_INFRA_DIRS
|
|
1082
1239
|
});
|
|
1083
1240
|
if (scope.outOfRoot.length > 0) crossProject.push({ externalId, outOfRoot: scope.outOfRoot });
|
|
@@ -1515,6 +1672,19 @@ async function assertWorkspaceInitialized2(basouRoot) {
|
|
|
1515
1672
|
}
|
|
1516
1673
|
}
|
|
1517
1674
|
|
|
1675
|
+
// src/lib/board-glance.ts
|
|
1676
|
+
import { join as join3 } from "path";
|
|
1677
|
+
import { glanceBoard, readManifest as readManifest3 } from "@basou/core";
|
|
1678
|
+
async function glanceDefaultBoard(root, paths) {
|
|
1679
|
+
try {
|
|
1680
|
+
const own = (await readManifest3(paths)).repos?.find((repo) => repo.path === ".");
|
|
1681
|
+
if (own?.visibility !== "private") return null;
|
|
1682
|
+
} catch {
|
|
1683
|
+
return null;
|
|
1684
|
+
}
|
|
1685
|
+
return glanceBoard(join3(root, DEFAULT_BOARD_PATH));
|
|
1686
|
+
}
|
|
1687
|
+
|
|
1518
1688
|
// src/lib/provenance-actions.ts
|
|
1519
1689
|
var capturing = Promise.resolve();
|
|
1520
1690
|
function captureImportJson(fn) {
|
|
@@ -1599,7 +1769,7 @@ function importCodex(options, ctx) {
|
|
|
1599
1769
|
}
|
|
1600
1770
|
async function regenerateHandoff(paths, nowIso, callbacks) {
|
|
1601
1771
|
const result = await renderHandoff({ paths, nowIso, ...callbacks });
|
|
1602
|
-
const existing = await
|
|
1772
|
+
const existing = await readMarkdownFile2(paths.files.handoff);
|
|
1603
1773
|
await writeMarkdownFile(
|
|
1604
1774
|
paths.files.handoff,
|
|
1605
1775
|
renderWithMarkers(existing, result.body, "handoff.md")
|
|
@@ -1613,7 +1783,7 @@ async function regenerateHandoff(paths, nowIso, callbacks) {
|
|
|
1613
1783
|
}
|
|
1614
1784
|
async function regenerateDecisions(paths, nowIso, callbacks) {
|
|
1615
1785
|
const result = await renderDecisions({ paths, nowIso, ...callbacks });
|
|
1616
|
-
const existing = await
|
|
1786
|
+
const existing = await readMarkdownFile2(paths.files.decisions);
|
|
1617
1787
|
await writeMarkdownFile(
|
|
1618
1788
|
paths.files.decisions,
|
|
1619
1789
|
renderWithMarkers(existing, result.body, "decisions.md")
|
|
@@ -1621,7 +1791,12 @@ async function regenerateDecisions(paths, nowIso, callbacks) {
|
|
|
1621
1791
|
return { decisionCount: result.decisionCount };
|
|
1622
1792
|
}
|
|
1623
1793
|
async function regenerateOrientation(paths, nowIso, callbacks) {
|
|
1624
|
-
const result = await renderOrientation({
|
|
1794
|
+
const result = await renderOrientation({
|
|
1795
|
+
paths,
|
|
1796
|
+
nowIso,
|
|
1797
|
+
board: await glanceDefaultBoard(dirname4(paths.root), paths),
|
|
1798
|
+
...callbacks
|
|
1799
|
+
});
|
|
1625
1800
|
await writeMarkdownFile(paths.files.orientation, `${result.body}
|
|
1626
1801
|
`);
|
|
1627
1802
|
return {
|
|
@@ -1739,8 +1914,9 @@ read (nothing is printed on stdout).`
|
|
|
1739
1914
|
"after",
|
|
1740
1915
|
`
|
|
1741
1916
|
The input is a JSON object: measure_digest (the digest of the measurement the
|
|
1742
|
-
judgement saw), observed
|
|
1743
|
-
|
|
1917
|
+
judgement saw), observed (exactly the observations the board declares, when it
|
|
1918
|
+
declares any), cells (every lane at every stage), prose, judged_by and
|
|
1919
|
+
axis_review. The board is measured again, and nothing is written when the
|
|
1744
1920
|
digest differs. Records are never written under a .basou/ directory. Exit
|
|
1745
1921
|
codes: 0 when the record was written, or with --dry-run when everything
|
|
1746
1922
|
checked out (nothing is written then); 1 when it was refused (nothing is
|
|
@@ -1748,6 +1924,191 @@ written; the reasons are on stderr).`
|
|
|
1748
1924
|
).action(async (options) => {
|
|
1749
1925
|
await runBoardRecord(options);
|
|
1750
1926
|
});
|
|
1927
|
+
board.command("guide").description(
|
|
1928
|
+
"Print the steps an agent follows to judge the board and record it (writes nothing, sends nothing)"
|
|
1929
|
+
).option("-v, --verbose", "Show error causes").addHelpText(
|
|
1930
|
+
"after",
|
|
1931
|
+
`
|
|
1932
|
+
The steps are filled in with this workspace's board (${DEFAULT_BOARD_PATH},
|
|
1933
|
+
read only when the manifest declares the workspace's own repo private): where
|
|
1934
|
+
it is and its last record, the commands that measure, observe, record and show
|
|
1935
|
+
it, what each stage is judged by, and the record's input to fill in, with the
|
|
1936
|
+
previous record's state of each cell beside it. With no board declared yet, it
|
|
1937
|
+
says how to declare one. The text is for an agent to read; its shape may change
|
|
1938
|
+
at any release. Exit codes: 0 when the steps were printed; 1 when the workspace,
|
|
1939
|
+
its manifest or its board.yaml cannot be read, or its own repo is not declared
|
|
1940
|
+
private (the reason is on stderr).`
|
|
1941
|
+
).action(async (options) => {
|
|
1942
|
+
await runBoardGuide(options);
|
|
1943
|
+
});
|
|
1944
|
+
board.command("init").description("Print a board.yaml to start a board from (writes nothing)").option("-v, --verbose", "Show error causes").addHelpText(
|
|
1945
|
+
"after",
|
|
1946
|
+
`
|
|
1947
|
+
It prints a declaration of the newest board_version, in the language the
|
|
1948
|
+
manifest declares for the workspace's own repo: a title from the workspace's
|
|
1949
|
+
name, the six stages with a meaning each, one lane to cut again, nothing to
|
|
1950
|
+
observe, no component registered, an axis of version 1, and the effort from
|
|
1951
|
+
the day of the first session, in this host's time zone. It writes nothing:
|
|
1952
|
+
save it, from the workspace's own repo, only where no board is, through a file
|
|
1953
|
+
of its own:
|
|
1954
|
+
|
|
1955
|
+
${INIT_SAVE}
|
|
1956
|
+
|
|
1957
|
+
Never print it straight into ${DEFAULT_BOARD_PATH}: the shell empties that file
|
|
1958
|
+
before init runs, so a board already there would be lost.
|
|
1959
|
+
|
|
1960
|
+
Exit codes: 0 when it was printed; 1 when the manifest does not declare the
|
|
1961
|
+
workspace's own repo private (a board's records hold what the trail holds),
|
|
1962
|
+
when ${DEFAULT_BOARD_PATH} is already there, or when the workspace cannot be
|
|
1963
|
+
read (the reason is on stderr, and nothing is printed on stdout).`
|
|
1964
|
+
).action(async (options) => {
|
|
1965
|
+
await runBoardInit(options);
|
|
1966
|
+
});
|
|
1967
|
+
}
|
|
1968
|
+
var INIT_SAVE = BOARD_INIT_SAVE;
|
|
1969
|
+
async function runBoardInit(options, ctx = {}) {
|
|
1970
|
+
try {
|
|
1971
|
+
await doRunBoardInit(options, ctx);
|
|
1972
|
+
} catch (error) {
|
|
1973
|
+
renderCliError(error, { verbose: isVerbose(options) });
|
|
1974
|
+
process.exitCode = 1;
|
|
1975
|
+
}
|
|
1976
|
+
}
|
|
1977
|
+
async function doRunBoardInit(_options, ctx) {
|
|
1978
|
+
const { root, paths, manifest } = await privateWorkspace(ctx, "board init");
|
|
1979
|
+
const cwd = ctx.cwd ?? process.cwd();
|
|
1980
|
+
if (await realpath3(cwd).catch(() => cwd) !== await realpath3(root).catch(() => root)) {
|
|
1981
|
+
throw new Error(
|
|
1982
|
+
`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.`
|
|
1983
|
+
);
|
|
1984
|
+
}
|
|
1985
|
+
const board = join4(root, DEFAULT_BOARD_PATH);
|
|
1986
|
+
try {
|
|
1987
|
+
await lstat(board);
|
|
1988
|
+
throw new Error(
|
|
1989
|
+
`${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).`
|
|
1990
|
+
);
|
|
1991
|
+
} catch (error) {
|
|
1992
|
+
if (!findErrorCode3(error, "ENOENT")) throw error;
|
|
1993
|
+
}
|
|
1994
|
+
const { start, timeZone, sessionsRead } = await effortStartOf(
|
|
1995
|
+
paths,
|
|
1996
|
+
ctx.nowProvider?.() ?? /* @__PURE__ */ new Date()
|
|
1997
|
+
);
|
|
1998
|
+
if (timeZone === void 0) {
|
|
1999
|
+
console.error(
|
|
2000
|
+
"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."
|
|
2001
|
+
);
|
|
2002
|
+
} else if (!sessionsRead) {
|
|
2003
|
+
console.error(
|
|
2004
|
+
"basou: the sessions could not be read, so effort.start is today; set it to the day the work began."
|
|
2005
|
+
);
|
|
2006
|
+
} else if (!isBoardTimeZone(timeZone)) {
|
|
2007
|
+
console.error(
|
|
2008
|
+
"basou: this host's time zone has no name a board can declare, so effort.time_zone is left out; write one."
|
|
2009
|
+
);
|
|
2010
|
+
}
|
|
2011
|
+
const text = boardInitText({
|
|
2012
|
+
name: manifest.workspace.name,
|
|
2013
|
+
language: resolveViewLanguage(manifest),
|
|
2014
|
+
start,
|
|
2015
|
+
startIsFirstSession: timeZone !== void 0,
|
|
2016
|
+
// An offset such as +00:00, which some hosts name their zone, is not
|
|
2017
|
+
// one a board can declare.
|
|
2018
|
+
timeZone: timeZone !== void 0 && isBoardTimeZone(timeZone) ? timeZone : void 0
|
|
2019
|
+
});
|
|
2020
|
+
process.stdout.write(text);
|
|
2021
|
+
return text;
|
|
2022
|
+
}
|
|
2023
|
+
async function privateWorkspace(ctx, command) {
|
|
2024
|
+
const cwd = ctx.cwd ?? process.cwd();
|
|
2025
|
+
const root = await resolveBasouRootForCommand(
|
|
2026
|
+
cwd,
|
|
2027
|
+
command,
|
|
2028
|
+
ctx.portfolioConfigPath === void 0 ? {} : { portfolioConfigPath: ctx.portfolioConfigPath }
|
|
2029
|
+
);
|
|
2030
|
+
const paths = basouPaths4(root);
|
|
2031
|
+
await assertWorkspaceInitialized3(paths.root);
|
|
2032
|
+
const manifest = await readManifest4(paths);
|
|
2033
|
+
const own = (manifest.repos ?? []).find((repo) => repo.path === ".");
|
|
2034
|
+
if (own?.visibility !== "private") {
|
|
2035
|
+
throw new Error(
|
|
2036
|
+
`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.`
|
|
2037
|
+
);
|
|
2038
|
+
}
|
|
2039
|
+
return { root, paths, manifest };
|
|
2040
|
+
}
|
|
2041
|
+
async function runBoardGuide(options, ctx = {}) {
|
|
2042
|
+
try {
|
|
2043
|
+
await doRunBoardGuide(options, ctx);
|
|
2044
|
+
} catch (error) {
|
|
2045
|
+
renderCliError(error, { verbose: isVerbose(options) });
|
|
2046
|
+
process.exitCode = 1;
|
|
2047
|
+
}
|
|
2048
|
+
}
|
|
2049
|
+
async function doRunBoardGuide(_options, ctx) {
|
|
2050
|
+
const { root, manifest } = await privateWorkspace(ctx, "board guide");
|
|
2051
|
+
const board = {
|
|
2052
|
+
path: join4(root, DEFAULT_BOARD_PATH),
|
|
2053
|
+
shown: DEFAULT_BOARD_PATH,
|
|
2054
|
+
given: false
|
|
2055
|
+
};
|
|
2056
|
+
let text;
|
|
2057
|
+
try {
|
|
2058
|
+
text = await readDeclaration(board);
|
|
2059
|
+
} catch (error) {
|
|
2060
|
+
if (!findErrorCode3(error, "ENOENT")) throw error;
|
|
2061
|
+
if (await lstat(board.path).then(
|
|
2062
|
+
() => true,
|
|
2063
|
+
() => false
|
|
2064
|
+
)) {
|
|
2065
|
+
throw new Error(
|
|
2066
|
+
`${DEFAULT_BOARD_PATH} is there but cannot be read (a link to nothing?). Fix it before the board is updated.`
|
|
2067
|
+
);
|
|
2068
|
+
}
|
|
2069
|
+
text = null;
|
|
2070
|
+
}
|
|
2071
|
+
const otherRepos = (manifest.repos ?? []).map((repo) => repo.path).filter((p) => p !== ".");
|
|
2072
|
+
if ([root, ...otherRepos].some((path) => displayPath3(path) !== path)) {
|
|
2073
|
+
throw new Error(
|
|
2074
|
+
"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."
|
|
2075
|
+
);
|
|
2076
|
+
}
|
|
2077
|
+
const common = {
|
|
2078
|
+
// As basou resolves it, the root measure resolves the repos' paths against.
|
|
2079
|
+
anchor: root,
|
|
2080
|
+
otherRepos,
|
|
2081
|
+
language: resolveViewLanguage(manifest),
|
|
2082
|
+
basouCommand: ctx.basouCommand ?? [process.execPath, process.argv[1] ?? ""].filter((part) => part !== "").map(shellWord).join(" "),
|
|
2083
|
+
basouVersion: BASOU_CLI_VERSION,
|
|
2084
|
+
now: ctx.nowProvider?.() ?? /* @__PURE__ */ new Date()
|
|
2085
|
+
};
|
|
2086
|
+
let guide;
|
|
2087
|
+
if (text === null) {
|
|
2088
|
+
guide = boardGuide({ ...common, board: { status: "undeclared" } });
|
|
2089
|
+
} else {
|
|
2090
|
+
const parsed = parseBoardDeclaration(text, {
|
|
2091
|
+
manifestRepoPaths: (manifest.repos ?? []).map((repo) => repo.path)
|
|
2092
|
+
});
|
|
2093
|
+
if (!parsed.ok) {
|
|
2094
|
+
throw new Error(
|
|
2095
|
+
`${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):
|
|
2096
|
+
${parsed.errors.map((e) => ` - ${displayPath3(e)}`).join("\n")}`
|
|
2097
|
+
);
|
|
2098
|
+
}
|
|
2099
|
+
const recordsDir = join4(dirname5(board.path), "records");
|
|
2100
|
+
guide = boardGuide({
|
|
2101
|
+
...common,
|
|
2102
|
+
board: {
|
|
2103
|
+
status: "declared",
|
|
2104
|
+
declaration: parsed.declaration,
|
|
2105
|
+
recordCount: recordIds(await readdir2(recordsDir).catch(() => [])).length,
|
|
2106
|
+
previous: await readPreviousRecords(recordsDir)
|
|
2107
|
+
}
|
|
2108
|
+
});
|
|
2109
|
+
}
|
|
2110
|
+
console.log(guide.trimEnd());
|
|
2111
|
+
return guide;
|
|
1751
2112
|
}
|
|
1752
2113
|
async function runBoardMeasure(options, ctx = {}) {
|
|
1753
2114
|
try {
|
|
@@ -1794,8 +2155,8 @@ Nothing was written.`, { cause: error.cause });
|
|
|
1794
2155
|
async function checkAndRecord(options, ctx) {
|
|
1795
2156
|
const cwd = ctx.cwd ?? process.cwd();
|
|
1796
2157
|
const loaded = await loadBoard(options, ctx, "board record");
|
|
1797
|
-
const beside = (...parts) => loaded.board.given ?
|
|
1798
|
-
const recordsDir =
|
|
2158
|
+
const beside = (...parts) => loaded.board.given ? join4(dirname5(loaded.board.shown), ...parts) : relative(cwd, join4(dirname5(loaded.board.path), ...parts));
|
|
2159
|
+
const recordsDir = join4(dirname5(loaded.board.path), "records");
|
|
1799
2160
|
await checkRecordsPlace(loaded, recordsDir, beside("records"), options.notPrivate === true);
|
|
1800
2161
|
const text = await readRecordInput(options, ctx);
|
|
1801
2162
|
let value;
|
|
@@ -1806,6 +2167,7 @@ async function checkAndRecord(options, ctx) {
|
|
|
1806
2167
|
cause: error
|
|
1807
2168
|
});
|
|
1808
2169
|
}
|
|
2170
|
+
await refuseOtherWorkspaces(value, loaded.root, ctx);
|
|
1809
2171
|
const parsed = parseRecordInput(value, loaded.declaration);
|
|
1810
2172
|
if (!parsed.ok) {
|
|
1811
2173
|
throw new RecordRefusal(
|
|
@@ -1862,11 +2224,90 @@ ${parsed.errors.map((e) => ` - ${displayPath3(e)}`).join("\n")}`
|
|
|
1862
2224
|
diff
|
|
1863
2225
|
};
|
|
1864
2226
|
}
|
|
2227
|
+
var INPUT_KEYS = /* @__PURE__ */ new Set([
|
|
2228
|
+
"measure_digest",
|
|
2229
|
+
"observed",
|
|
2230
|
+
"cells",
|
|
2231
|
+
"prose",
|
|
2232
|
+
"judged_by",
|
|
2233
|
+
"axis_review",
|
|
2234
|
+
"value",
|
|
2235
|
+
"observed_at",
|
|
2236
|
+
"source",
|
|
2237
|
+
"error",
|
|
2238
|
+
"lane",
|
|
2239
|
+
"stage",
|
|
2240
|
+
"state",
|
|
2241
|
+
"reason",
|
|
2242
|
+
"summary",
|
|
2243
|
+
"lanes",
|
|
2244
|
+
"operator_turns",
|
|
2245
|
+
"footnotes",
|
|
2246
|
+
"text",
|
|
2247
|
+
"model",
|
|
2248
|
+
"self_reported",
|
|
2249
|
+
"triggers"
|
|
2250
|
+
]);
|
|
2251
|
+
var SHAPED_PLACES = [
|
|
2252
|
+
/^measure_digest$/,
|
|
2253
|
+
/^observed \(a named entry\)\.observed_at$/,
|
|
2254
|
+
/^cells\[\d+\]\.state$/,
|
|
2255
|
+
/^axis_review\.triggers$/
|
|
2256
|
+
];
|
|
2257
|
+
var NAMED_MAPS = /* @__PURE__ */ new Set(["observed", "prose.lanes"]);
|
|
2258
|
+
function inputTexts(value) {
|
|
2259
|
+
const out = [];
|
|
2260
|
+
const stack = [{ value, at: "" }];
|
|
2261
|
+
for (let next = stack.pop(); next !== void 0; next = stack.pop()) {
|
|
2262
|
+
const { value: v, at } = next;
|
|
2263
|
+
if (SHAPED_PLACES.some((place) => place.test(at))) continue;
|
|
2264
|
+
if (typeof v === "string" || typeof v === "number") {
|
|
2265
|
+
out.push({ at: at === "" ? "(top level)" : at, text: String(v) });
|
|
2266
|
+
} else if (Array.isArray(v)) {
|
|
2267
|
+
for (let i = v.length - 1; i >= 0; i--) stack.push({ value: v[i], at: `${at}[${i}]` });
|
|
2268
|
+
} else if (typeof v === "object" && v !== null) {
|
|
2269
|
+
const named = NAMED_MAPS.has(at);
|
|
2270
|
+
const where = at === "" ? "(top level)" : at;
|
|
2271
|
+
const children = [];
|
|
2272
|
+
for (const [k, child] of Object.entries(v)) {
|
|
2273
|
+
if (!named && INPUT_KEYS.has(k)) {
|
|
2274
|
+
children.push({ value: child, at: at === "" ? k : `${at}.${k}` });
|
|
2275
|
+
} else {
|
|
2276
|
+
out.push({ at: `${where}: a name`, text: k });
|
|
2277
|
+
children.push({ value: child, at: `${where} (a named entry)` });
|
|
2278
|
+
}
|
|
2279
|
+
}
|
|
2280
|
+
stack.push(...children.reverse());
|
|
2281
|
+
}
|
|
2282
|
+
}
|
|
2283
|
+
return out;
|
|
2284
|
+
}
|
|
2285
|
+
async function refuseOtherWorkspaces(input, root, ctx) {
|
|
2286
|
+
const found = await findForeignWorkspaceFields({
|
|
2287
|
+
fields: inputTexts(input),
|
|
2288
|
+
selfPath: root,
|
|
2289
|
+
configPath: ctx.portfolioConfigPath
|
|
2290
|
+
});
|
|
2291
|
+
if (found.status === "unreadable") {
|
|
2292
|
+
console.error(
|
|
2293
|
+
"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."
|
|
2294
|
+
);
|
|
2295
|
+
return;
|
|
2296
|
+
}
|
|
2297
|
+
if (found.status === "checked" && found.at.length > 0) {
|
|
2298
|
+
throw new RecordRefusal(
|
|
2299
|
+
`The record's input names another workspace the portfolio registers, and a board holds this workspace's own work only; nothing was written:
|
|
2300
|
+
${[
|
|
2301
|
+
...new Set(found.at)
|
|
2302
|
+
].map((where) => ` - ${displayPath3(where)}`).join("\n")}`
|
|
2303
|
+
);
|
|
2304
|
+
}
|
|
2305
|
+
}
|
|
1865
2306
|
function hasRecords(loaded) {
|
|
1866
|
-
return
|
|
2307
|
+
return basename4(loaded.board.path) === "board.yaml";
|
|
1867
2308
|
}
|
|
1868
2309
|
async function previousOf(loaded) {
|
|
1869
|
-
return hasRecords(loaded) ? readPreviousRecords(
|
|
2310
|
+
return hasRecords(loaded) ? readPreviousRecords(join4(dirname5(loaded.board.path), "records")) : NO_PREVIOUS_RECORDS;
|
|
1870
2311
|
}
|
|
1871
2312
|
function errorCode(error) {
|
|
1872
2313
|
const code = error?.code;
|
|
@@ -1874,21 +2315,21 @@ function errorCode(error) {
|
|
|
1874
2315
|
}
|
|
1875
2316
|
async function checkRecordsPlace(loaded, recordsDir, shown, notPrivate) {
|
|
1876
2317
|
const { board } = loaded;
|
|
1877
|
-
if (
|
|
2318
|
+
if (basename4(board.path) !== "board.yaml") {
|
|
1878
2319
|
throw new RecordRefusal(
|
|
1879
2320
|
`${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.`
|
|
1880
2321
|
);
|
|
1881
2322
|
}
|
|
1882
2323
|
let dir;
|
|
1883
2324
|
try {
|
|
1884
|
-
dir = await
|
|
2325
|
+
dir = await realpath3(dirname5(board.path));
|
|
1885
2326
|
} catch (error) {
|
|
1886
2327
|
throw new RecordRefusal(
|
|
1887
2328
|
`The directory of ${displayPath3(board.shown)} could not be resolved; nothing was written.`,
|
|
1888
2329
|
{ cause: error }
|
|
1889
2330
|
);
|
|
1890
2331
|
}
|
|
1891
|
-
if (dir.split(
|
|
2332
|
+
if (dir.split(sep2).includes(".basou")) {
|
|
1892
2333
|
throw new RecordRefusal(
|
|
1893
2334
|
`${displayPath3(shown)} would be under a .basou/ directory, where basou board record writes nothing; nothing was written.`
|
|
1894
2335
|
);
|
|
@@ -1896,8 +2337,8 @@ async function checkRecordsPlace(loaded, recordsDir, shown, notPrivate) {
|
|
|
1896
2337
|
if (!notPrivate) {
|
|
1897
2338
|
let holder;
|
|
1898
2339
|
for (const repo of loaded.manifest.repos ?? []) {
|
|
1899
|
-
const real = await
|
|
1900
|
-
if (real === void 0 || dir !== real && !dir.startsWith(`${real}${
|
|
2340
|
+
const real = await realpath3(resolve3(loaded.root, repo.path)).catch(() => void 0);
|
|
2341
|
+
if (real === void 0 || dir !== real && !dir.startsWith(`${real}${sep2}`)) continue;
|
|
1901
2342
|
if (holder === void 0 || real.length > holder.real.length) {
|
|
1902
2343
|
holder = { real, private: repo.visibility === "private" };
|
|
1903
2344
|
}
|
|
@@ -1997,7 +2438,7 @@ async function loadBoard(options, ctx, command) {
|
|
|
1997
2438
|
);
|
|
1998
2439
|
const paths = basouPaths4(root);
|
|
1999
2440
|
await assertWorkspaceInitialized3(paths.root);
|
|
2000
|
-
const manifest = await
|
|
2441
|
+
const manifest = await readManifest4(paths);
|
|
2001
2442
|
const board = boardPath(options, cwd, root, manifest);
|
|
2002
2443
|
const text = await readDeclaration(board);
|
|
2003
2444
|
const repoPaths = (manifest.repos ?? []).map((repo) => repo.path);
|
|
@@ -2036,7 +2477,7 @@ async function measureLiveBoard(root, ctx) {
|
|
|
2036
2477
|
let repos = ["."];
|
|
2037
2478
|
let manifestUnread = false;
|
|
2038
2479
|
try {
|
|
2039
|
-
const declared = ((await
|
|
2480
|
+
const declared = ((await readManifest4(paths)).repos ?? []).map((repo) => repo.path);
|
|
2040
2481
|
if (declared.length > 0) repos = declared;
|
|
2041
2482
|
} catch {
|
|
2042
2483
|
manifestUnread = true;
|
|
@@ -2075,7 +2516,7 @@ function boardPath(options, cwd, root, manifest) {
|
|
|
2075
2516
|
`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>.`
|
|
2076
2517
|
);
|
|
2077
2518
|
}
|
|
2078
|
-
return { path:
|
|
2519
|
+
return { path: join4(root, DEFAULT_BOARD_PATH), shown: DEFAULT_BOARD_PATH, given: false };
|
|
2079
2520
|
}
|
|
2080
2521
|
async function readDeclaration(board) {
|
|
2081
2522
|
try {
|
|
@@ -2307,19 +2748,19 @@ function printMeasurementText(m, hasRecords2) {
|
|
|
2307
2748
|
|
|
2308
2749
|
// src/lib/context-channel.ts
|
|
2309
2750
|
import { homedir as homedir2 } from "os";
|
|
2310
|
-
import { join as
|
|
2751
|
+
import { join as join6 } from "path";
|
|
2311
2752
|
import {
|
|
2312
2753
|
ORIENTATION_END,
|
|
2313
2754
|
ORIENTATION_START,
|
|
2314
2755
|
parseMarkers,
|
|
2315
|
-
readMarkdownFile as
|
|
2756
|
+
readMarkdownFile as readMarkdownFile3,
|
|
2316
2757
|
removeMarkerSection
|
|
2317
2758
|
} from "@basou/core";
|
|
2318
2759
|
|
|
2319
2760
|
// src/lib/durable-write.ts
|
|
2320
2761
|
import { randomUUID } from "crypto";
|
|
2321
2762
|
import { lstat as lstat2, open, rename, stat as stat3, unlink as unlink2 } from "fs/promises";
|
|
2322
|
-
import { basename as
|
|
2763
|
+
import { basename as basename5, dirname as dirname6, join as join5 } from "path";
|
|
2323
2764
|
async function assertNotSymlink(targetPath) {
|
|
2324
2765
|
try {
|
|
2325
2766
|
const st = await lstat2(targetPath);
|
|
@@ -2334,8 +2775,8 @@ async function assertNotSymlink(targetPath) {
|
|
|
2334
2775
|
}
|
|
2335
2776
|
}
|
|
2336
2777
|
async function writeFileDurable(targetPath, content) {
|
|
2337
|
-
const dir =
|
|
2338
|
-
const tmpPath =
|
|
2778
|
+
const dir = dirname6(targetPath);
|
|
2779
|
+
const tmpPath = join5(dir, `.${basename5(targetPath)}.tmp.${randomUUID()}`);
|
|
2339
2780
|
let mode = 420;
|
|
2340
2781
|
try {
|
|
2341
2782
|
mode = (await stat3(targetPath)).mode & 511;
|
|
@@ -2370,7 +2811,7 @@ async function writeFileDurable(targetPath, content) {
|
|
|
2370
2811
|
}
|
|
2371
2812
|
|
|
2372
2813
|
// src/lib/context-channel.ts
|
|
2373
|
-
var CODEX_TARGET_PATH =
|
|
2814
|
+
var CODEX_TARGET_PATH = join6(homedir2(), ".codex", "AGENTS.md");
|
|
2374
2815
|
var ORIENTATION_MARKERS = { start: ORIENTATION_START, end: ORIENTATION_END };
|
|
2375
2816
|
function buildTargetBody(existing, block, markers) {
|
|
2376
2817
|
const wrapped = `${markers.start}
|
|
@@ -2394,20 +2835,20 @@ ${block}${markers.end}${section.after}`;
|
|
|
2394
2835
|
}
|
|
2395
2836
|
async function backupOnce(target, existing) {
|
|
2396
2837
|
const bak = `${target}.basou-bak`;
|
|
2397
|
-
const already = await
|
|
2838
|
+
const already = await readMarkdownFile3(bak);
|
|
2398
2839
|
if (already !== null) return;
|
|
2399
2840
|
await writeFileDurable(bak, existing ?? "");
|
|
2400
2841
|
}
|
|
2401
2842
|
async function syncMarkerBlock(opts) {
|
|
2402
2843
|
const { target, markers, block } = opts;
|
|
2403
2844
|
await assertNotSymlink(target);
|
|
2404
|
-
const existing = await
|
|
2845
|
+
const existing = await readMarkdownFile3(target);
|
|
2405
2846
|
const newBody = buildTargetBody(existing, block, markers);
|
|
2406
2847
|
if (newBody === existing) return { action: "unchanged" };
|
|
2407
2848
|
const hadBlock = existing !== null && parseMarkers(existing, markers).kind === "ok";
|
|
2408
2849
|
const action = hadBlock ? "updated" : "installed";
|
|
2409
2850
|
if (opts.dryRun === true) return { action };
|
|
2410
|
-
const recheck = await
|
|
2851
|
+
const recheck = await readMarkdownFile3(target);
|
|
2411
2852
|
if (recheck !== existing) {
|
|
2412
2853
|
throw new Error(
|
|
2413
2854
|
"The target changed during sync; aborting so a concurrent edit is not overwritten. Re-run the command."
|
|
@@ -2429,12 +2870,12 @@ function assertNoMarkerLine(body, markers) {
|
|
|
2429
2870
|
async function removeMarkerBlock(opts) {
|
|
2430
2871
|
const { target, markers, fileLabel } = opts;
|
|
2431
2872
|
await assertNotSymlink(target);
|
|
2432
|
-
const existing = await
|
|
2873
|
+
const existing = await readMarkdownFile3(target);
|
|
2433
2874
|
if (existing === null) return { removed: false };
|
|
2434
2875
|
const newBody = removeMarkerSection(existing, fileLabel, markers);
|
|
2435
2876
|
if (newBody === existing) return { removed: false };
|
|
2436
2877
|
if (opts.dryRun === true) return { removed: true };
|
|
2437
|
-
const recheck = await
|
|
2878
|
+
const recheck = await readMarkdownFile3(target);
|
|
2438
2879
|
if (recheck !== existing) {
|
|
2439
2880
|
throw new Error(
|
|
2440
2881
|
"The target changed during unsync; aborting so a concurrent edit is not overwritten. Re-run the command."
|
|
@@ -2515,7 +2956,7 @@ async function doRunChannelClear(face, options) {
|
|
|
2515
2956
|
// src/commands/decision.ts
|
|
2516
2957
|
import { readFile as readFile3 } from "fs/promises";
|
|
2517
2958
|
import { homedir as homedir3 } from "os";
|
|
2518
|
-
import { join as
|
|
2959
|
+
import { join as join7, resolve as resolve4 } from "path";
|
|
2519
2960
|
import {
|
|
2520
2961
|
AGENT_INFRA_DIRS as AGENT_INFRA_DIRS2,
|
|
2521
2962
|
acquireLock as acquireLock2,
|
|
@@ -2531,7 +2972,7 @@ import {
|
|
|
2531
2972
|
LOCAL_CLI_EVENT_SOURCE as LOCAL_CLI_EVENT_SOURCE2,
|
|
2532
2973
|
loadSessionEntries,
|
|
2533
2974
|
prefixedUlid as prefixedUlid2,
|
|
2534
|
-
readManifest as
|
|
2975
|
+
readManifest as readManifest5,
|
|
2535
2976
|
replayEvents as replayEvents2,
|
|
2536
2977
|
resolveSessionId,
|
|
2537
2978
|
sanitizePath
|
|
@@ -2794,7 +3235,7 @@ async function runDecisionRecord(options, ctx = {}) {
|
|
|
2794
3235
|
async function warnLinkedFilesOutsideRoots(input) {
|
|
2795
3236
|
if (input.linkedFiles.length === 0) return;
|
|
2796
3237
|
try {
|
|
2797
|
-
const manifest = await
|
|
3238
|
+
const manifest = await readManifest5(input.paths);
|
|
2798
3239
|
if ((manifest.import?.source_roots?.length ?? 0) === 0) return;
|
|
2799
3240
|
const scope = await classifyFilesBySourceRoot2({
|
|
2800
3241
|
files: input.linkedFiles,
|
|
@@ -2860,7 +3301,7 @@ async function doRunDecisionRecord(options, ctx) {
|
|
|
2860
3301
|
});
|
|
2861
3302
|
return;
|
|
2862
3303
|
}
|
|
2863
|
-
const manifest = await
|
|
3304
|
+
const manifest = await readManifest5(paths);
|
|
2864
3305
|
const adHoc = await createAdHocSessionWithEvent({
|
|
2865
3306
|
paths,
|
|
2866
3307
|
manifest,
|
|
@@ -2924,7 +3365,7 @@ async function doRunDecisionCapture(options, ctx) {
|
|
|
2924
3365
|
const now = ctx.nowProvider !== void 0 ? ctx.nowProvider() : /* @__PURE__ */ new Date();
|
|
2925
3366
|
const occurredAt = now.toISOString();
|
|
2926
3367
|
const decisionIds = decisions.map(() => prefixedUlid2("decision"));
|
|
2927
|
-
const manifest = await
|
|
3368
|
+
const manifest = await readManifest5(paths);
|
|
2928
3369
|
const invocationArgs = options.file !== void 0 ? [
|
|
2929
3370
|
"--file",
|
|
2930
3371
|
sanitizePath(resolve4(cwd, options.file), {
|
|
@@ -3030,7 +3471,7 @@ async function doRunDecisionVoid(decisionId, options, ctx) {
|
|
|
3030
3471
|
});
|
|
3031
3472
|
return;
|
|
3032
3473
|
}
|
|
3033
|
-
const manifest = await
|
|
3474
|
+
const manifest = await readManifest5(paths);
|
|
3034
3475
|
const adHoc = await createAdHocSessionWithEvent({
|
|
3035
3476
|
paths,
|
|
3036
3477
|
manifest,
|
|
@@ -3066,7 +3507,7 @@ function isDecisionId(value) {
|
|
|
3066
3507
|
async function decisionExists(paths, decisionId) {
|
|
3067
3508
|
const entries = await loadSessionEntries(paths, { now: /* @__PURE__ */ new Date() });
|
|
3068
3509
|
for (const entry of entries) {
|
|
3069
|
-
const sessionDir =
|
|
3510
|
+
const sessionDir = join7(paths.sessions, entry.sessionId);
|
|
3070
3511
|
try {
|
|
3071
3512
|
for await (const ev of replayEvents2(sessionDir, {})) {
|
|
3072
3513
|
if (ev.type === "decision_recorded" && ev.decision_id === decisionId) return true;
|
|
@@ -3478,7 +3919,7 @@ import {
|
|
|
3478
3919
|
assertBasouRootSafe as assertBasouRootSafe5,
|
|
3479
3920
|
basouPaths as basouPaths7,
|
|
3480
3921
|
findErrorCode as findErrorCode5,
|
|
3481
|
-
readMarkdownFile as
|
|
3922
|
+
readMarkdownFile as readMarkdownFile4,
|
|
3482
3923
|
renderDecisions as renderDecisions2,
|
|
3483
3924
|
renderWithMarkers as renderWithMarkers2,
|
|
3484
3925
|
writeMarkdownFile as writeMarkdownFile2
|
|
@@ -3510,7 +3951,7 @@ async function doRunDecisionsGenerate(options, ctx) {
|
|
|
3510
3951
|
onWarning: (w, sid) => printReplayWarning(w, sid),
|
|
3511
3952
|
onSessionSkip: (sid, reason) => printSessionSkip(sid, reason)
|
|
3512
3953
|
});
|
|
3513
|
-
const existing = await
|
|
3954
|
+
const existing = await readMarkdownFile4(paths.files.decisions);
|
|
3514
3955
|
const finalBody = renderWithMarkers2(existing, result.body, "decisions.md");
|
|
3515
3956
|
await writeMarkdownFile2(paths.files.decisions, finalBody);
|
|
3516
3957
|
console.log(`Generated .basou/decisions.md (decisions: ${result.decisionCount})`);
|
|
@@ -3529,7 +3970,7 @@ async function assertWorkspaceInitialized5(basouRoot) {
|
|
|
3529
3970
|
// src/commands/exec.ts
|
|
3530
3971
|
import { mkdir as mkdir2, rm as rm2 } from "fs/promises";
|
|
3531
3972
|
import { homedir as homedir4 } from "os";
|
|
3532
|
-
import { join as
|
|
3973
|
+
import { join as join8 } from "path";
|
|
3533
3974
|
import {
|
|
3534
3975
|
acquireLock as acquireLock3,
|
|
3535
3976
|
assertBasouRootSafe as assertBasouRootSafe6,
|
|
@@ -3543,7 +3984,7 @@ import {
|
|
|
3543
3984
|
overwriteYamlFile,
|
|
3544
3985
|
parseDuration,
|
|
3545
3986
|
prefixedUlid as prefixedUlid3,
|
|
3546
|
-
readManifest as
|
|
3987
|
+
readManifest as readManifest6,
|
|
3547
3988
|
readYamlFile as readYamlFile2,
|
|
3548
3989
|
SESSION_SCHEMA_VERSION,
|
|
3549
3990
|
SessionSchema,
|
|
@@ -3570,15 +4011,15 @@ async function runExec(command, args, options, ctx = {}) {
|
|
|
3570
4011
|
const repoRoot = await resolveRepositoryRootInPlace(cwd, "exec");
|
|
3571
4012
|
const paths = basouPaths8(repoRoot);
|
|
3572
4013
|
await assertBasouRootSafe6(paths.root);
|
|
3573
|
-
const manifest = await
|
|
4014
|
+
const manifest = await readManifest6(paths);
|
|
3574
4015
|
await assertSessionStoreSafe2(paths);
|
|
3575
4016
|
const sessionId = prefixedUlid3("ses");
|
|
3576
|
-
const sessionDir =
|
|
4017
|
+
const sessionDir = join8(paths.sessions, sessionId);
|
|
3577
4018
|
const appendEvent = ctx.appendEvent ?? (async (_sessionDir, event) => {
|
|
3578
4019
|
await coreAppendChainedEvent(paths, sessionId, event);
|
|
3579
4020
|
});
|
|
3580
4021
|
const startedAt = now().toISOString();
|
|
3581
|
-
const sessionYamlPath =
|
|
4022
|
+
const sessionYamlPath = join8(sessionDir, "session.yaml");
|
|
3582
4023
|
const session = buildInitialSession({
|
|
3583
4024
|
id: sessionId,
|
|
3584
4025
|
command,
|
|
@@ -3871,7 +4312,7 @@ import {
|
|
|
3871
4312
|
assertBasouRootSafe as assertBasouRootSafe7,
|
|
3872
4313
|
basouPaths as basouPaths9,
|
|
3873
4314
|
findErrorCode as findErrorCode6,
|
|
3874
|
-
readMarkdownFile as
|
|
4315
|
+
readMarkdownFile as readMarkdownFile5,
|
|
3875
4316
|
renderHandoff as renderHandoff2,
|
|
3876
4317
|
renderWithMarkers as renderWithMarkers3,
|
|
3877
4318
|
writeMarkdownFile as writeMarkdownFile3
|
|
@@ -3904,7 +4345,7 @@ async function doRunHandoffGenerate(options, ctx) {
|
|
|
3904
4345
|
onSessionSkip: (sid, reason) => printSessionSkip(sid, reason),
|
|
3905
4346
|
onTaskSkip: (taskId, reason) => printTaskSkip(taskId, reason)
|
|
3906
4347
|
});
|
|
3907
|
-
const existing = await
|
|
4348
|
+
const existing = await readMarkdownFile5(paths.files.handoff);
|
|
3908
4349
|
const finalBody = renderWithMarkers3(existing, result.body, "handoff.md");
|
|
3909
4350
|
await writeMarkdownFile3(paths.files.handoff, finalBody);
|
|
3910
4351
|
console.log(
|
|
@@ -3939,7 +4380,7 @@ async function assertWorkspaceInitialized6(basouRoot) {
|
|
|
3939
4380
|
import { execFile } from "child_process";
|
|
3940
4381
|
import { open as open2, readFile as readFile4, realpath as realpath4, stat as stat4 } from "fs/promises";
|
|
3941
4382
|
import { homedir as homedir7 } from "os";
|
|
3942
|
-
import { join as
|
|
4383
|
+
import { join as join11 } from "path";
|
|
3943
4384
|
import { fileURLToPath } from "url";
|
|
3944
4385
|
import { promisify } from "util";
|
|
3945
4386
|
import {
|
|
@@ -3966,7 +4407,7 @@ import {
|
|
|
3966
4407
|
parseProtocolStamp,
|
|
3967
4408
|
protocolSectionsFrom,
|
|
3968
4409
|
protocolUpdateToken,
|
|
3969
|
-
readManifest as
|
|
4410
|
+
readManifest as readManifest7,
|
|
3970
4411
|
readMarkdownFile as readMarkdownFile6,
|
|
3971
4412
|
recordSessionBaseline,
|
|
3972
4413
|
removeClaudeSessionStartHook,
|
|
@@ -3993,12 +4434,12 @@ function commandHandlerFields(handler) {
|
|
|
3993
4434
|
out.additionalContextLimit = handler.additionalContextLimit;
|
|
3994
4435
|
return out;
|
|
3995
4436
|
}
|
|
3996
|
-
function
|
|
3997
|
-
if (Array.isArray(value)) return value.map(
|
|
4437
|
+
function canonicalize2(value) {
|
|
4438
|
+
if (Array.isArray(value)) return value.map(canonicalize2);
|
|
3998
4439
|
if (typeof value === "object" && value !== null) {
|
|
3999
4440
|
const src = value;
|
|
4000
4441
|
const out = {};
|
|
4001
|
-
for (const key of Object.keys(src).sort()) out[key] =
|
|
4442
|
+
for (const key of Object.keys(src).sort()) out[key] = canonicalize2(src[key]);
|
|
4002
4443
|
return out;
|
|
4003
4444
|
}
|
|
4004
4445
|
return value;
|
|
@@ -4017,7 +4458,7 @@ function computeCodexHookIdentityHash(input) {
|
|
|
4017
4458
|
}
|
|
4018
4459
|
const identity = { event_name: input.eventKey, hooks: [normalized] };
|
|
4019
4460
|
if (input.matcher !== void 0) identity.matcher = input.matcher;
|
|
4020
|
-
const blob = JSON.stringify(
|
|
4461
|
+
const blob = JSON.stringify(canonicalize2(identity));
|
|
4021
4462
|
return `sha256:${createHash("sha256").update(blob, "utf8").digest("hex")}`;
|
|
4022
4463
|
}
|
|
4023
4464
|
function codexHookStateKey(hooksPath, eventKey, groupIndex, handlerIndex) {
|
|
@@ -4098,148 +4539,17 @@ function describeCodexHookTrust(trust) {
|
|
|
4098
4539
|
}
|
|
4099
4540
|
}
|
|
4100
4541
|
|
|
4101
|
-
// src/lib/foreign-workspace-warn.ts
|
|
4102
|
-
import { realpath as realpath3 } from "fs/promises";
|
|
4103
|
-
import { dirname as dirname5 } from "path";
|
|
4104
|
-
import { loadPortfolioConfig as loadPortfolioConfig2, readMarkdownFile as readMarkdownFile5 } from "@basou/core";
|
|
4105
|
-
|
|
4106
|
-
// src/lib/foreign-workspace-scan.ts
|
|
4107
|
-
import { homedir as osHomedir } from "os";
|
|
4108
|
-
import { basename as basename5, normalize, sep as sep2 } from "path";
|
|
4109
|
-
var MIN_TOKEN_LENGTH = 4;
|
|
4110
|
-
function stripTrailingSep(p) {
|
|
4111
|
-
return p.length > 1 && p.endsWith(sep2) ? p.slice(0, -sep2.length) : p;
|
|
4112
|
-
}
|
|
4113
|
-
function toTildePath(absPath, home) {
|
|
4114
|
-
if (absPath === home) return "~";
|
|
4115
|
-
if (absPath.startsWith(home + sep2)) return `~${absPath.slice(home.length)}`;
|
|
4116
|
-
return null;
|
|
4117
|
-
}
|
|
4118
|
-
function encodedSpelling(name) {
|
|
4119
|
-
return name.replace(/[^a-zA-Z0-9]/g, "-");
|
|
4120
|
-
}
|
|
4121
|
-
function directoryNameSpellings(dir) {
|
|
4122
|
-
const paired = /^(.+)-(planning|workspace)$/.exec(dir);
|
|
4123
|
-
const plain = paired !== null ? [`${paired[1]}-planning`, `${paired[1]}-workspace`] : [dir, `${dir}-workspace`];
|
|
4124
|
-
return [...plain, ...plain.map(encodedSpelling)];
|
|
4125
|
-
}
|
|
4126
|
-
function nameTokensFor(workspacePath, home) {
|
|
4127
|
-
const abs = stripTrailingSep(normalize(workspacePath));
|
|
4128
|
-
const tokens = /* @__PURE__ */ new Set([abs]);
|
|
4129
|
-
const tilde = toTildePath(abs, home);
|
|
4130
|
-
if (tilde !== null) tokens.add(tilde);
|
|
4131
|
-
for (const name of directoryNameSpellings(basename5(abs))) tokens.add(name);
|
|
4132
|
-
return [...tokens].filter((t) => t.length >= MIN_TOKEN_LENGTH);
|
|
4133
|
-
}
|
|
4134
|
-
function isSpellingOfSelf(workspacePath, selfPath) {
|
|
4135
|
-
const own = new Set(directoryNameSpellings(basename5(stripTrailingSep(normalize(selfPath)))));
|
|
4136
|
-
return directoryNameSpellings(basename5(stripTrailingSep(normalize(workspacePath)))).some(
|
|
4137
|
-
(s) => own.has(s)
|
|
4138
|
-
);
|
|
4139
|
-
}
|
|
4140
|
-
function scanForeignWorkspaceNames(input) {
|
|
4141
|
-
if (input.text.length === 0 || input.workspacePaths.length === 0) return [];
|
|
4142
|
-
const home = input.homedir ?? osHomedir();
|
|
4143
|
-
const own = input.selfPath === void 0 ? [] : nameTokensFor(input.selfPath, home);
|
|
4144
|
-
const lines = input.text.split("\n");
|
|
4145
|
-
const hits = [];
|
|
4146
|
-
for (const workspacePath of input.workspacePaths) {
|
|
4147
|
-
if (input.selfPath !== void 0 && isSpellingOfSelf(workspacePath, input.selfPath)) continue;
|
|
4148
|
-
const tokens = nameTokensFor(workspacePath, home).filter(
|
|
4149
|
-
(token) => !own.some((ownToken) => ownToken.includes(token))
|
|
4150
|
-
);
|
|
4151
|
-
if (tokens.length === 0) continue;
|
|
4152
|
-
const matched = /* @__PURE__ */ new Set();
|
|
4153
|
-
const matchedLines = [];
|
|
4154
|
-
for (const [index, line] of lines.entries()) {
|
|
4155
|
-
let lineMatched = false;
|
|
4156
|
-
for (const token of tokens) {
|
|
4157
|
-
if (line.includes(token)) {
|
|
4158
|
-
matched.add(token);
|
|
4159
|
-
lineMatched = true;
|
|
4160
|
-
}
|
|
4161
|
-
}
|
|
4162
|
-
if (lineMatched) matchedLines.push(index + 1);
|
|
4163
|
-
}
|
|
4164
|
-
if (matchedLines.length === 0) continue;
|
|
4165
|
-
hits.push({
|
|
4166
|
-
workspacePath,
|
|
4167
|
-
tokens: [...matched].sort((a, b) => b.length - a.length || a.localeCompare(b)),
|
|
4168
|
-
lines: matchedLines
|
|
4169
|
-
});
|
|
4170
|
-
}
|
|
4171
|
-
return hits;
|
|
4172
|
-
}
|
|
4173
|
-
|
|
4174
|
-
// src/lib/foreign-workspace-warn.ts
|
|
4175
|
-
var MAX_LISTED_LINES = 5;
|
|
4176
|
-
async function canonicalize2(path) {
|
|
4177
|
-
try {
|
|
4178
|
-
return await realpath3(path);
|
|
4179
|
-
} catch {
|
|
4180
|
-
return path;
|
|
4181
|
-
}
|
|
4182
|
-
}
|
|
4183
|
-
async function findForeignWorkspaceNames(args) {
|
|
4184
|
-
let workspacePaths;
|
|
4185
|
-
let selfPath;
|
|
4186
|
-
try {
|
|
4187
|
-
const workspaces = await loadPortfolioConfig2(args.configPath);
|
|
4188
|
-
workspacePaths = await Promise.all(workspaces.map((w) => canonicalize2(w.path)));
|
|
4189
|
-
selfPath = args.selfPath === void 0 ? void 0 : await canonicalize2(args.selfPath);
|
|
4190
|
-
} catch {
|
|
4191
|
-
return null;
|
|
4192
|
-
}
|
|
4193
|
-
const hits = scanForeignWorkspaceNames({
|
|
4194
|
-
text: args.text,
|
|
4195
|
-
workspacePaths,
|
|
4196
|
-
selfPath
|
|
4197
|
-
});
|
|
4198
|
-
if (hits.length === 0) return null;
|
|
4199
|
-
const lines = [...new Set(hits.flatMap((h) => h.lines))].sort((a, b) => a - b);
|
|
4200
|
-
return { workspaceCount: hits.length, lines };
|
|
4201
|
-
}
|
|
4202
|
-
function describeForeignWorkspaceLines(lines) {
|
|
4203
|
-
const listed = lines.slice(0, MAX_LISTED_LINES).join(", ");
|
|
4204
|
-
const rest = lines.length - MAX_LISTED_LINES;
|
|
4205
|
-
const numbers = rest > 0 ? `${listed} and ${rest} more` : listed;
|
|
4206
|
-
return `${lines.length === 1 ? "line" : "lines"} ${numbers}`;
|
|
4207
|
-
}
|
|
4208
|
-
function positionForeignWorkspaceWarning(report, where) {
|
|
4209
|
-
const subject = report.workspaceCount === 1 ? "names another registered workspace" : `names ${report.workspaceCount} other registered workspaces`;
|
|
4210
|
-
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.`;
|
|
4211
|
-
}
|
|
4212
|
-
function protocolForeignWorkspaceWarning(report) {
|
|
4213
|
-
const subject = report.workspaceCount === 1 ? "names a registered workspace" : `names ${report.workspaceCount} registered workspaces`;
|
|
4214
|
-
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.`;
|
|
4215
|
-
}
|
|
4216
|
-
async function warnIfPositionNamesOtherWorkspaces(args) {
|
|
4217
|
-
try {
|
|
4218
|
-
const body = await readMarkdownFile5(args.paths.files.orientation);
|
|
4219
|
-
if (body === null) return;
|
|
4220
|
-
const report = await findForeignWorkspaceNames({
|
|
4221
|
-
text: body,
|
|
4222
|
-
selfPath: dirname5(args.paths.root),
|
|
4223
|
-
configPath: args.configPath
|
|
4224
|
-
});
|
|
4225
|
-
if (report !== null) {
|
|
4226
|
-
console.error(positionForeignWorkspaceWarning(report, args.paths.files.orientation));
|
|
4227
|
-
}
|
|
4228
|
-
} catch {
|
|
4229
|
-
}
|
|
4230
|
-
}
|
|
4231
|
-
|
|
4232
4542
|
// src/lib/protocols-config.ts
|
|
4233
4543
|
import { homedir as homedir5 } from "os";
|
|
4234
|
-
import { isAbsolute, join as
|
|
4544
|
+
import { isAbsolute, join as join9, resolve as resolve5 } from "path";
|
|
4235
4545
|
import { readYamlFile as readYamlFile3 } from "@basou/core";
|
|
4236
|
-
var DEFAULT_PROTOCOLS_CONFIG_PATH =
|
|
4237
|
-
var DEFAULT_TARGET_PATH =
|
|
4546
|
+
var DEFAULT_PROTOCOLS_CONFIG_PATH = join9(homedir5(), ".basou", "protocols.yaml");
|
|
4547
|
+
var DEFAULT_TARGET_PATH = join9(homedir5(), ".claude", "CLAUDE.md");
|
|
4238
4548
|
var ALLOWED_TOP_KEYS = /* @__PURE__ */ new Set(["version", "protocols"]);
|
|
4239
4549
|
var ALLOWED_ENTRY_KEYS = /* @__PURE__ */ new Set(["source", "title"]);
|
|
4240
4550
|
function expandTilde(p) {
|
|
4241
4551
|
if (p === "~") return homedir5();
|
|
4242
|
-
if (p.startsWith("~/")) return
|
|
4552
|
+
if (p.startsWith("~/")) return join9(homedir5(), p.slice(2));
|
|
4243
4553
|
return p;
|
|
4244
4554
|
}
|
|
4245
4555
|
function isRecord(value) {
|
|
@@ -4317,12 +4627,12 @@ import {
|
|
|
4317
4627
|
|
|
4318
4628
|
// src/lib/hosts-config.ts
|
|
4319
4629
|
import { homedir as homedir6 } from "os";
|
|
4320
|
-
import { isAbsolute as isAbsolute2, join as
|
|
4630
|
+
import { isAbsolute as isAbsolute2, join as join10, resolve as resolve6 } from "path";
|
|
4321
4631
|
import { readYamlFile as readYamlFile4 } from "@basou/core";
|
|
4322
|
-
var DEFAULT_HOSTS_CONFIG_PATH =
|
|
4632
|
+
var DEFAULT_HOSTS_CONFIG_PATH = join10(homedir6(), ".basou", "hosts.yaml");
|
|
4323
4633
|
function expandTilde2(p) {
|
|
4324
4634
|
if (p === "~") return homedir6();
|
|
4325
|
-
if (p.startsWith("~/")) return
|
|
4635
|
+
if (p.startsWith("~/")) return join10(homedir6(), p.slice(2));
|
|
4326
4636
|
return p;
|
|
4327
4637
|
}
|
|
4328
4638
|
function isRecord2(value) {
|
|
@@ -4442,6 +4752,7 @@ async function renderOrientationForRoot(repositoryRoot, options, ctx, behaviour)
|
|
|
4442
4752
|
paths,
|
|
4443
4753
|
nowIso,
|
|
4444
4754
|
staleness,
|
|
4755
|
+
board: await glanceDefaultBoard(repositoryRoot, paths),
|
|
4445
4756
|
verbose: options.verbose === true,
|
|
4446
4757
|
federatedRoots,
|
|
4447
4758
|
onWarning: (w, sid) => printReplayWarning(w, sid),
|
|
@@ -4843,7 +5154,7 @@ async function observeSessionFromPayload(fields, pass, portfolioConfigPath) {
|
|
|
4843
5154
|
});
|
|
4844
5155
|
return;
|
|
4845
5156
|
}
|
|
4846
|
-
const manifest = await
|
|
5157
|
+
const manifest = await readManifest7(paths);
|
|
4847
5158
|
await recordSessionBaseline({
|
|
4848
5159
|
observationsDir: paths.observations,
|
|
4849
5160
|
repoRoots: observedRepoRoots(root, manifest),
|
|
@@ -4905,7 +5216,7 @@ function parseMinEdits(raw) {
|
|
|
4905
5216
|
if (raw === void 0 || !/^\d+$/.test(raw)) return void 0;
|
|
4906
5217
|
return Number(raw);
|
|
4907
5218
|
}
|
|
4908
|
-
var DEFAULT_CLAUDE_SETTINGS_PATH =
|
|
5219
|
+
var DEFAULT_CLAUDE_SETTINGS_PATH = join11(homedir7(), ".claude", "settings.json");
|
|
4909
5220
|
function resolveCliEntry() {
|
|
4910
5221
|
return fileURLToPath(import.meta.url);
|
|
4911
5222
|
}
|
|
@@ -5262,7 +5573,7 @@ function describeHookMode(tiers) {
|
|
|
5262
5573
|
const gates = tiers.review ? "capture + review" : "capture";
|
|
5263
5574
|
return `${enforcement}, ${gates}`;
|
|
5264
5575
|
}
|
|
5265
|
-
var DEFAULT_CODEX_FACE_PATH =
|
|
5576
|
+
var DEFAULT_CODEX_FACE_PATH = join11(homedir7(), ".codex", "AGENTS.md");
|
|
5266
5577
|
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.`;
|
|
5267
5578
|
async function faceHasLeftoverOrientationBlock(facePath) {
|
|
5268
5579
|
try {
|
|
@@ -5274,8 +5585,8 @@ async function faceHasLeftoverOrientationBlock(facePath) {
|
|
|
5274
5585
|
return false;
|
|
5275
5586
|
}
|
|
5276
5587
|
}
|
|
5277
|
-
var DEFAULT_CODEX_HOOKS_PATH =
|
|
5278
|
-
var DEFAULT_CODEX_CONFIG_PATH =
|
|
5588
|
+
var DEFAULT_CODEX_HOOKS_PATH = join11(homedir7(), ".codex", "hooks.json");
|
|
5589
|
+
var DEFAULT_CODEX_CONFIG_PATH = join11(homedir7(), ".codex", "config.toml");
|
|
5279
5590
|
async function readHooksFile(path) {
|
|
5280
5591
|
let raw;
|
|
5281
5592
|
try {
|
|
@@ -5533,7 +5844,7 @@ import {
|
|
|
5533
5844
|
EVENT_SCHEMA_VERSION as EVENT_SCHEMA_VERSION4,
|
|
5534
5845
|
findErrorCode as findErrorCode8,
|
|
5535
5846
|
LOCAL_CLI_EVENT_SOURCE as LOCAL_CLI_EVENT_SOURCE3,
|
|
5536
|
-
readManifest as
|
|
5847
|
+
readManifest as readManifest8,
|
|
5537
5848
|
resolveSessionId as resolveSessionId2,
|
|
5538
5849
|
sanitizePath as sanitizePath2
|
|
5539
5850
|
} from "@basou/core";
|
|
@@ -5645,7 +5956,7 @@ ${HEREDOC_EXAMPLE}`
|
|
|
5645
5956
|
});
|
|
5646
5957
|
return;
|
|
5647
5958
|
}
|
|
5648
|
-
const manifest = await
|
|
5959
|
+
const manifest = await readManifest8(paths);
|
|
5649
5960
|
const adHoc = await createAdHocSessionWithEvent2({
|
|
5650
5961
|
paths,
|
|
5651
5962
|
manifest,
|
|
@@ -5893,7 +6204,7 @@ import {
|
|
|
5893
6204
|
writeFileSync,
|
|
5894
6205
|
writeSync
|
|
5895
6206
|
} from "fs";
|
|
5896
|
-
import { basename as basename7, dirname as
|
|
6207
|
+
import { basename as basename7, dirname as dirname7, isAbsolute as isAbsolute3, join as join12, relative as relative3, resolve as resolve9 } from "path";
|
|
5897
6208
|
import {
|
|
5898
6209
|
appendBasouGitignore as appendBasouGitignore2,
|
|
5899
6210
|
basouPaths as basouPaths13,
|
|
@@ -5911,7 +6222,7 @@ import {
|
|
|
5911
6222
|
planRename,
|
|
5912
6223
|
planRosterAdoption,
|
|
5913
6224
|
planWorkspaceView,
|
|
5914
|
-
readManifest as
|
|
6225
|
+
readManifest as readManifest9,
|
|
5915
6226
|
readMarkdownFile as readMarkdownFile7,
|
|
5916
6227
|
reconcileSourceRoots,
|
|
5917
6228
|
removeMarkerSection as removeMarkerSection2,
|
|
@@ -6059,7 +6370,7 @@ async function doRunProjectCheck(options, ctx) {
|
|
|
6059
6370
|
const cwd = ctx.cwd ?? process.cwd();
|
|
6060
6371
|
const repositoryRoot = await resolveBasouRootForCommand(cwd, "project check");
|
|
6061
6372
|
const paths = basouPaths13(repositoryRoot);
|
|
6062
|
-
const manifest = await
|
|
6373
|
+
const manifest = await readManifest9(paths);
|
|
6063
6374
|
const roster = summarizeRosterDrift({
|
|
6064
6375
|
...manifest.repos !== void 0 ? { repos: manifest.repos } : {},
|
|
6065
6376
|
sourceRoots: effectiveSourceRoots(manifest)
|
|
@@ -6194,7 +6505,7 @@ async function doRunProjectSync(options, ctx) {
|
|
|
6194
6505
|
const cwd = ctx.cwd ?? process.cwd();
|
|
6195
6506
|
const repositoryRoot = await resolveBasouRootForCommand(cwd, "project sync");
|
|
6196
6507
|
const paths = basouPaths13(repositoryRoot);
|
|
6197
|
-
const manifest = await
|
|
6508
|
+
const manifest = await readManifest9(paths);
|
|
6198
6509
|
const hasRoster = manifest.repos !== void 0 && manifest.repos.length > 0;
|
|
6199
6510
|
const reconcile = reconcileSourceRoots({
|
|
6200
6511
|
...manifest.repos !== void 0 ? { repos: manifest.repos } : {},
|
|
@@ -6274,13 +6585,13 @@ function classifySourceRoot(repositoryRoot, declaredPath) {
|
|
|
6274
6585
|
} catch {
|
|
6275
6586
|
return { path: declaredPath, kind: "unresolved" };
|
|
6276
6587
|
}
|
|
6277
|
-
return { path: declaredPath, kind: existsSync(
|
|
6588
|
+
return { path: declaredPath, kind: existsSync(join12(real, ".git")) ? "repo" : "non-repo" };
|
|
6278
6589
|
}
|
|
6279
6590
|
async function doRunProjectAdopt(options, ctx) {
|
|
6280
6591
|
const cwd = ctx.cwd ?? process.cwd();
|
|
6281
6592
|
const repositoryRoot = await resolveBasouRootForCommand(cwd, "project adopt");
|
|
6282
6593
|
const paths = basouPaths13(repositoryRoot);
|
|
6283
|
-
const manifest = await
|
|
6594
|
+
const manifest = await readManifest9(paths);
|
|
6284
6595
|
const alreadyDeclared = manifest.repos !== void 0 && manifest.repos.length > 0;
|
|
6285
6596
|
const candidates = effectiveSourceRoots(manifest).map(
|
|
6286
6597
|
(r) => classifySourceRoot(repositoryRoot, r)
|
|
@@ -6378,7 +6689,7 @@ async function gatherRepoWiring(repositoryRoot, entry) {
|
|
|
6378
6689
|
} catch {
|
|
6379
6690
|
return { ...base, reachable: false, instructionFiles: [] };
|
|
6380
6691
|
}
|
|
6381
|
-
if (!existsSync(
|
|
6692
|
+
if (!existsSync(join12(real, ".git"))) {
|
|
6382
6693
|
return { ...base, reachable: false, instructionFiles: [] };
|
|
6383
6694
|
}
|
|
6384
6695
|
try {
|
|
@@ -6386,7 +6697,7 @@ async function gatherRepoWiring(repositoryRoot, entry) {
|
|
|
6386
6697
|
for (const name of INSTRUCTION_FILES) {
|
|
6387
6698
|
let present = true;
|
|
6388
6699
|
try {
|
|
6389
|
-
lstatSync(
|
|
6700
|
+
lstatSync(join12(real, name));
|
|
6390
6701
|
} catch {
|
|
6391
6702
|
present = false;
|
|
6392
6703
|
}
|
|
@@ -6402,7 +6713,7 @@ async function doRunProjectWiring(options, ctx) {
|
|
|
6402
6713
|
const cwd = ctx.cwd ?? process.cwd();
|
|
6403
6714
|
const repositoryRoot = await resolveBasouRootForCommand(cwd, "project wiring");
|
|
6404
6715
|
const paths = basouPaths13(repositoryRoot);
|
|
6405
|
-
const manifest = await
|
|
6716
|
+
const manifest = await readManifest9(paths);
|
|
6406
6717
|
const roster = manifest.repos ?? [];
|
|
6407
6718
|
const facts = [];
|
|
6408
6719
|
for (const entry of roster) facts.push(await gatherRepoWiring(repositoryRoot, entry));
|
|
@@ -6497,10 +6808,10 @@ function gatherRepoGitignore(repositoryRoot, entry) {
|
|
|
6497
6808
|
} catch {
|
|
6498
6809
|
return { ...base, reachable: false, currentLines: [] };
|
|
6499
6810
|
}
|
|
6500
|
-
if (!existsSync(
|
|
6811
|
+
if (!existsSync(join12(real, ".git"))) {
|
|
6501
6812
|
return { ...base, reachable: false, currentLines: [] };
|
|
6502
6813
|
}
|
|
6503
|
-
return { ...base, reachable: true, currentLines: readGitignoreLines(
|
|
6814
|
+
return { ...base, reachable: true, currentLines: readGitignoreLines(join12(real, ".gitignore")) };
|
|
6504
6815
|
}
|
|
6505
6816
|
function hasErrorCode(error) {
|
|
6506
6817
|
return error instanceof Error && typeof error.code === "string";
|
|
@@ -6514,7 +6825,7 @@ function readGitignoreLines(file) {
|
|
|
6514
6825
|
}
|
|
6515
6826
|
}
|
|
6516
6827
|
function applyGitignorePlan(repositoryRoot, plan) {
|
|
6517
|
-
const file =
|
|
6828
|
+
const file = join12(realpathSync(resolve9(repositoryRoot, plan.path)), ".gitignore");
|
|
6518
6829
|
let existing = "";
|
|
6519
6830
|
try {
|
|
6520
6831
|
existing = readFileSync(file, "utf8");
|
|
@@ -6535,7 +6846,7 @@ async function doRunProjectGitignore(options, ctx) {
|
|
|
6535
6846
|
const cwd = ctx.cwd ?? process.cwd();
|
|
6536
6847
|
const repositoryRoot = await resolveBasouRootForCommand(cwd, "project gitignore");
|
|
6537
6848
|
const paths = basouPaths13(repositoryRoot);
|
|
6538
|
-
const manifest = await
|
|
6849
|
+
const manifest = await readManifest9(paths);
|
|
6539
6850
|
const roster = manifest.repos ?? [];
|
|
6540
6851
|
const facts = roster.map((entry) => gatherRepoGitignore(repositoryRoot, entry));
|
|
6541
6852
|
const summary = planGitignore({ repos: facts, required: [...INSTRUCTION_FILES] });
|
|
@@ -6654,7 +6965,7 @@ function gatherRepoSymlinks(repositoryRoot, anchorReal, entry) {
|
|
|
6654
6965
|
return { ...base, isAnchor: false, reachable: false, canonicalPresent: false, files: [] };
|
|
6655
6966
|
}
|
|
6656
6967
|
if (real === anchorReal) {
|
|
6657
|
-
const anchorCanonical =
|
|
6968
|
+
const anchorCanonical = join12(real, CANONICAL_FILE);
|
|
6658
6969
|
const anchorState = anchorCanonicalState(anchorCanonical);
|
|
6659
6970
|
if (anchorState === "absent") {
|
|
6660
6971
|
return { ...base, isAnchor: true, reachable: true, canonicalPresent: false, files: [] };
|
|
@@ -6674,7 +6985,7 @@ function gatherRepoSymlinks(repositoryRoot, anchorReal, entry) {
|
|
|
6674
6985
|
anchorCanonical,
|
|
6675
6986
|
"self"
|
|
6676
6987
|
).map((spec) => {
|
|
6677
|
-
const { state, actualTarget } = inspectSymlink(
|
|
6988
|
+
const { state, actualTarget } = inspectSymlink(join12(real, spec.name), spec.target);
|
|
6678
6989
|
return {
|
|
6679
6990
|
name: spec.name,
|
|
6680
6991
|
expectedTarget: spec.target,
|
|
@@ -6691,16 +7002,16 @@ function gatherRepoSymlinks(repositoryRoot, anchorReal, entry) {
|
|
|
6691
7002
|
files: anchorFiles
|
|
6692
7003
|
};
|
|
6693
7004
|
}
|
|
6694
|
-
if (!existsSync(
|
|
7005
|
+
if (!existsSync(join12(real, ".git"))) {
|
|
6695
7006
|
return { ...base, isAnchor: false, reachable: false, canonicalPresent: false, files: [] };
|
|
6696
7007
|
}
|
|
6697
|
-
const canonicalFile = isSelf ?
|
|
7008
|
+
const canonicalFile = isSelf ? join12(real, CANONICAL_FILE) : join12(anchorReal, "agents", basename7(real), CANONICAL_FILE);
|
|
6698
7009
|
if (!existsSync(canonicalFile)) {
|
|
6699
7010
|
return { ...base, isAnchor: false, reachable: true, canonicalPresent: false, files: [] };
|
|
6700
7011
|
}
|
|
6701
7012
|
const files = expectedSymlinkTargets(real, canonicalFile, mode).map(
|
|
6702
7013
|
(spec) => {
|
|
6703
|
-
const { state, actualTarget } = inspectSymlink(
|
|
7014
|
+
const { state, actualTarget } = inspectSymlink(join12(real, spec.name), spec.target);
|
|
6704
7015
|
return {
|
|
6705
7016
|
name: spec.name,
|
|
6706
7017
|
expectedTarget: spec.target,
|
|
@@ -6729,9 +7040,9 @@ function applySymlinkPlan(repositoryRoot, plan) {
|
|
|
6729
7040
|
const created = [];
|
|
6730
7041
|
const failed = [];
|
|
6731
7042
|
for (const { name, target } of plan.toCreate) {
|
|
6732
|
-
const filePath =
|
|
7043
|
+
const filePath = join12(real, name);
|
|
6733
7044
|
try {
|
|
6734
|
-
mkdirSync(
|
|
7045
|
+
mkdirSync(dirname7(filePath), { recursive: true });
|
|
6735
7046
|
symlinkSync(target, filePath);
|
|
6736
7047
|
created.push(name);
|
|
6737
7048
|
} catch (error) {
|
|
@@ -6763,7 +7074,7 @@ function gatherViewSymlinks(repositoryRoot, anchorReal, roster, viewDir) {
|
|
|
6763
7074
|
if (!existsSync(canonicalFile)) return { kind: "missing-canonical", viewName };
|
|
6764
7075
|
const files = expectedSymlinkTargets(viewDir, canonicalFile, "hub").map(
|
|
6765
7076
|
(spec) => {
|
|
6766
|
-
const { state, actualTarget } = inspectSymlink(
|
|
7077
|
+
const { state, actualTarget } = inspectSymlink(join12(viewDir, spec.name), spec.target);
|
|
6767
7078
|
return {
|
|
6768
7079
|
name: spec.name,
|
|
6769
7080
|
expectedTarget: spec.target,
|
|
@@ -6779,9 +7090,9 @@ function applyViewSymlinks(viewDir, files) {
|
|
|
6779
7090
|
const failed = [];
|
|
6780
7091
|
for (const f of files) {
|
|
6781
7092
|
if (f.state !== "missing") continue;
|
|
6782
|
-
const filePath =
|
|
7093
|
+
const filePath = join12(viewDir, f.name);
|
|
6783
7094
|
try {
|
|
6784
|
-
mkdirSync(
|
|
7095
|
+
mkdirSync(dirname7(filePath), { recursive: true });
|
|
6785
7096
|
symlinkSync(f.expectedTarget, filePath);
|
|
6786
7097
|
created.push(f.name);
|
|
6787
7098
|
} catch (error) {
|
|
@@ -6794,7 +7105,7 @@ async function doRunProjectSymlinks(options, ctx) {
|
|
|
6794
7105
|
const cwd = ctx.cwd ?? process.cwd();
|
|
6795
7106
|
const repositoryRoot = await resolveBasouRootForCommand(cwd, "project symlinks");
|
|
6796
7107
|
const paths = basouPaths13(repositoryRoot);
|
|
6797
|
-
const manifest = await
|
|
7108
|
+
const manifest = await readManifest9(paths);
|
|
6798
7109
|
const roster = manifest.repos ?? [];
|
|
6799
7110
|
const anchorReal = realpathSync(repositoryRoot);
|
|
6800
7111
|
const facts = roster.map((entry) => gatherRepoSymlinks(repositoryRoot, anchorReal, entry));
|
|
@@ -7001,7 +7312,7 @@ function resolveViewDir(repositoryRoot, viewPath) {
|
|
|
7001
7312
|
return realpathSync(abs);
|
|
7002
7313
|
} catch {
|
|
7003
7314
|
try {
|
|
7004
|
-
return
|
|
7315
|
+
return join12(realpathSync(dirname7(abs)), basename7(abs));
|
|
7005
7316
|
} catch {
|
|
7006
7317
|
return abs;
|
|
7007
7318
|
}
|
|
@@ -7019,7 +7330,7 @@ function gatherViewRepo(repositoryRoot, viewDir, entry) {
|
|
|
7019
7330
|
return { path: entry.path, reachable: false };
|
|
7020
7331
|
}
|
|
7021
7332
|
const linkName = basename7(repoReal);
|
|
7022
|
-
const { state, actualTarget } = inspectSymlink(
|
|
7333
|
+
const { state, actualTarget } = inspectSymlink(join12(viewDir, linkName), expectedTarget);
|
|
7023
7334
|
return {
|
|
7024
7335
|
path: entry.path,
|
|
7025
7336
|
reachable: true,
|
|
@@ -7033,9 +7344,9 @@ function applyViewPlan(viewDir, toCreate) {
|
|
|
7033
7344
|
const created = [];
|
|
7034
7345
|
const failed = [];
|
|
7035
7346
|
for (const { name, target } of toCreate) {
|
|
7036
|
-
const filePath =
|
|
7347
|
+
const filePath = join12(viewDir, name);
|
|
7037
7348
|
try {
|
|
7038
|
-
mkdirSync(
|
|
7349
|
+
mkdirSync(dirname7(filePath), { recursive: true });
|
|
7039
7350
|
symlinkSync(target, filePath);
|
|
7040
7351
|
created.push(name);
|
|
7041
7352
|
} catch (error) {
|
|
@@ -7048,7 +7359,7 @@ var TOP_LEVEL_INSTRUCTION_FILES_LOWER = new Set(
|
|
|
7048
7359
|
INSTRUCTION_FILES.filter((f) => !f.includes("/")).map((f) => f.toLowerCase())
|
|
7049
7360
|
);
|
|
7050
7361
|
function classifyViewLink(viewDir, name, rosterRealpaths) {
|
|
7051
|
-
const filePath =
|
|
7362
|
+
const filePath = join12(viewDir, name);
|
|
7052
7363
|
let isLink;
|
|
7053
7364
|
try {
|
|
7054
7365
|
isLink = lstatSync(filePath).isSymbolicLink();
|
|
@@ -7077,7 +7388,7 @@ function classifyViewLink(viewDir, name, rosterRealpaths) {
|
|
|
7077
7388
|
if (!isDir) {
|
|
7078
7389
|
return { target, kind: existsSync(resolved) ? "non-repo" : "broken" };
|
|
7079
7390
|
}
|
|
7080
|
-
return { target, kind: existsSync(
|
|
7391
|
+
return { target, kind: existsSync(join12(resolved, ".git")) ? "repo" : "non-repo" };
|
|
7081
7392
|
}
|
|
7082
7393
|
function gatherExistingViewLinks(viewDir, rosterRealpaths) {
|
|
7083
7394
|
let names;
|
|
@@ -7102,7 +7413,7 @@ function pruneViewLinks(viewDir, toPrune, rosterRealpaths) {
|
|
|
7102
7413
|
const pruned = [];
|
|
7103
7414
|
const failed = [];
|
|
7104
7415
|
for (const { name } of toPrune) {
|
|
7105
|
-
const filePath =
|
|
7416
|
+
const filePath = join12(viewDir, name);
|
|
7106
7417
|
const c = classifyViewLink(viewDir, name, rosterRealpaths);
|
|
7107
7418
|
if (c === null || c.kind !== "repo") {
|
|
7108
7419
|
failed.push({
|
|
@@ -7124,7 +7435,7 @@ async function doRunProjectWorkspace(options, ctx) {
|
|
|
7124
7435
|
const cwd = ctx.cwd ?? process.cwd();
|
|
7125
7436
|
const repositoryRoot = await resolveBasouRootForCommand(cwd, "project workspace");
|
|
7126
7437
|
const paths = basouPaths13(repositoryRoot);
|
|
7127
|
-
const manifest = await
|
|
7438
|
+
const manifest = await readManifest9(paths);
|
|
7128
7439
|
const viewPath = manifest.workspace.view;
|
|
7129
7440
|
const roster = manifest.repos ?? [];
|
|
7130
7441
|
let result;
|
|
@@ -7315,10 +7626,10 @@ async function runProjectPreset(options, ctx = {}) {
|
|
|
7315
7626
|
}
|
|
7316
7627
|
}
|
|
7317
7628
|
function canonicalFileFor(anchorReal, canonicalName) {
|
|
7318
|
-
return
|
|
7629
|
+
return join12(anchorReal, "agents", canonicalName, CANONICAL_FILE);
|
|
7319
7630
|
}
|
|
7320
7631
|
function canonicalLabelFor(canonicalName) {
|
|
7321
|
-
return
|
|
7632
|
+
return join12("agents", canonicalName, CANONICAL_FILE);
|
|
7322
7633
|
}
|
|
7323
7634
|
async function gatherRepoPreset(repositoryRoot, anchorReal, entry) {
|
|
7324
7635
|
const declared = {
|
|
@@ -7339,7 +7650,7 @@ async function gatherRepoPreset(repositoryRoot, anchorReal, entry) {
|
|
|
7339
7650
|
if (real === anchorReal) {
|
|
7340
7651
|
return { ...declared, isAnchor: true, reachable: true, canonicalPresent: false };
|
|
7341
7652
|
}
|
|
7342
|
-
if (!existsSync(
|
|
7653
|
+
if (!existsSync(join12(real, ".git"))) {
|
|
7343
7654
|
return { ...declared, isAnchor: false, reachable: false, canonicalPresent: false };
|
|
7344
7655
|
}
|
|
7345
7656
|
const canonicalName = basename7(real);
|
|
@@ -7453,7 +7764,7 @@ async function applyViewPreset(anchorReal, outcome) {
|
|
|
7453
7764
|
isLink = false;
|
|
7454
7765
|
}
|
|
7455
7766
|
if (isLink) throw new Error(`Canonical is a symlink in ${label}`);
|
|
7456
|
-
if (outcome.action === "create") mkdirSync(
|
|
7767
|
+
if (outcome.action === "create") mkdirSync(dirname7(file), { recursive: true });
|
|
7457
7768
|
const existing = await readMarkdownFile7(file);
|
|
7458
7769
|
await writeMarkdownFile5(file, renderWithMarkers4(existing, outcome.block, label));
|
|
7459
7770
|
}
|
|
@@ -7467,7 +7778,7 @@ async function applyPresetPlan(anchorReal, plan) {
|
|
|
7467
7778
|
isLink = false;
|
|
7468
7779
|
}
|
|
7469
7780
|
if (isLink) throw new Error(`Canonical is a symlink in ${label}`);
|
|
7470
|
-
if (plan.action === "create") mkdirSync(
|
|
7781
|
+
if (plan.action === "create") mkdirSync(dirname7(file), { recursive: true });
|
|
7471
7782
|
const existing = await readMarkdownFile7(file);
|
|
7472
7783
|
await writeMarkdownFile5(file, renderWithMarkers4(existing, plan.desiredBlock, label));
|
|
7473
7784
|
}
|
|
@@ -7484,7 +7795,7 @@ async function doRunProjectPreset(options, ctx) {
|
|
|
7484
7795
|
const cwd = ctx.cwd ?? process.cwd();
|
|
7485
7796
|
const repositoryRoot = await resolveBasouRootForCommand(cwd, "project preset");
|
|
7486
7797
|
const paths = basouPaths13(repositoryRoot);
|
|
7487
|
-
const manifest = await
|
|
7798
|
+
const manifest = await readManifest9(paths);
|
|
7488
7799
|
const roster = manifest.repos ?? [];
|
|
7489
7800
|
const anchorReal = realpathSync(repositoryRoot);
|
|
7490
7801
|
const facts = [];
|
|
@@ -7721,24 +8032,24 @@ function gatherArchiveTeardown(repositoryRoot, manifest, target) {
|
|
|
7721
8032
|
const instructionFiles = [];
|
|
7722
8033
|
for (const name of INSTRUCTION_FILES) {
|
|
7723
8034
|
try {
|
|
7724
|
-
lstatSync(
|
|
8035
|
+
lstatSync(join12(real, name));
|
|
7725
8036
|
instructionFiles.push(name);
|
|
7726
8037
|
} catch {
|
|
7727
8038
|
}
|
|
7728
8039
|
}
|
|
7729
8040
|
let ignored;
|
|
7730
8041
|
try {
|
|
7731
|
-
ignored = new Set(readGitignoreLines(
|
|
8042
|
+
ignored = new Set(readGitignoreLines(join12(real, ".gitignore")).map((l) => l.trim()));
|
|
7732
8043
|
} catch {
|
|
7733
8044
|
ignored = /* @__PURE__ */ new Set();
|
|
7734
8045
|
}
|
|
7735
8046
|
const gitignorePatterns = INSTRUCTION_FILES.filter((p) => ignored.has(p) || ignored.has(`/${p}`));
|
|
7736
|
-
const canonical2 = existsSync(
|
|
8047
|
+
const canonical2 = existsSync(join12(anchorReal, "agents", canonicalName, CANONICAL_FILE));
|
|
7737
8048
|
let viewLink = false;
|
|
7738
8049
|
const viewPath = manifest.workspace.view;
|
|
7739
8050
|
if (viewPath !== void 0) {
|
|
7740
8051
|
try {
|
|
7741
|
-
lstatSync(
|
|
8052
|
+
lstatSync(join12(resolveViewDir(repositoryRoot, viewPath), canonicalName));
|
|
7742
8053
|
viewLink = true;
|
|
7743
8054
|
} catch {
|
|
7744
8055
|
}
|
|
@@ -7752,11 +8063,11 @@ function gatherArchiveTeardown(repositoryRoot, manifest, target) {
|
|
|
7752
8063
|
};
|
|
7753
8064
|
}
|
|
7754
8065
|
function teardownExpectedTargets(repoReal, anchorReal, canonicalName) {
|
|
7755
|
-
const canonicalFile =
|
|
8066
|
+
const canonicalFile = join12(anchorReal, "agents", canonicalName, CANONICAL_FILE);
|
|
7756
8067
|
return expectedSymlinkTargets(repoReal, canonicalFile);
|
|
7757
8068
|
}
|
|
7758
8069
|
function viewLinkPointsAt(viewDir, name, repoReal) {
|
|
7759
|
-
const filePath =
|
|
8070
|
+
const filePath = join12(viewDir, name);
|
|
7760
8071
|
try {
|
|
7761
8072
|
if (!lstatSync(filePath).isSymbolicLink()) return false;
|
|
7762
8073
|
const target = readlinkSync(filePath);
|
|
@@ -7767,7 +8078,7 @@ function viewLinkPointsAt(viewDir, name, repoReal) {
|
|
|
7767
8078
|
}
|
|
7768
8079
|
}
|
|
7769
8080
|
function viewLinkPointsAtPath(viewDir, name, expectedRepoPath) {
|
|
7770
|
-
const filePath =
|
|
8081
|
+
const filePath = join12(viewDir, name);
|
|
7771
8082
|
try {
|
|
7772
8083
|
if (!lstatSync(filePath).isSymbolicLink()) return false;
|
|
7773
8084
|
const target = readlinkSync(filePath);
|
|
@@ -7818,7 +8129,7 @@ function gatherRepoTeardown(repositoryRoot, manifest, target) {
|
|
|
7818
8129
|
if (!isAnchor) {
|
|
7819
8130
|
if (repoReal !== void 0) {
|
|
7820
8131
|
for (const spec of teardownExpectedTargets(repoReal, anchorReal, canonicalName)) {
|
|
7821
|
-
const { state, actualTarget } = inspectSymlink(
|
|
8132
|
+
const { state, actualTarget } = inspectSymlink(join12(repoReal, spec.name), spec.target);
|
|
7822
8133
|
if (isSelf) {
|
|
7823
8134
|
if (state !== "missing")
|
|
7824
8135
|
items.push({
|
|
@@ -7855,7 +8166,7 @@ function gatherRepoTeardown(repositoryRoot, manifest, target) {
|
|
|
7855
8166
|
}
|
|
7856
8167
|
let ignored;
|
|
7857
8168
|
try {
|
|
7858
|
-
ignored = new Set(readGitignoreLines(
|
|
8169
|
+
ignored = new Set(readGitignoreLines(join12(repoReal, ".gitignore")).map((l) => l.trim()));
|
|
7859
8170
|
for (const p of INSTRUCTION_FILES) {
|
|
7860
8171
|
if (ignored.has(p) || ignored.has(`/${p}`)) {
|
|
7861
8172
|
items.push({
|
|
@@ -7878,7 +8189,7 @@ function gatherRepoTeardown(repositoryRoot, manifest, target) {
|
|
|
7878
8189
|
const viewPath = manifest.workspace.view;
|
|
7879
8190
|
if (viewPath !== void 0) {
|
|
7880
8191
|
const viewDir = resolveViewDir(repositoryRoot, viewPath);
|
|
7881
|
-
const linkPath =
|
|
8192
|
+
const linkPath = join12(viewDir, canonicalName);
|
|
7882
8193
|
let isLink = false;
|
|
7883
8194
|
try {
|
|
7884
8195
|
isLink = lstatSync(linkPath).isSymbolicLink();
|
|
@@ -7904,8 +8215,8 @@ function gatherRepoTeardown(repositoryRoot, manifest, target) {
|
|
|
7904
8215
|
else items.push({ kind: "view-symlink", label: canonicalName, state: "removable" });
|
|
7905
8216
|
}
|
|
7906
8217
|
}
|
|
7907
|
-
const canonicalFile =
|
|
7908
|
-
const canonicalLabel =
|
|
8218
|
+
const canonicalFile = join12(anchorReal, "agents", canonicalName, CANONICAL_FILE);
|
|
8219
|
+
const canonicalLabel = join12("agents", canonicalName, CANONICAL_FILE);
|
|
7909
8220
|
let canonicalIsLink = false;
|
|
7910
8221
|
try {
|
|
7911
8222
|
canonicalIsLink = lstatSync(canonicalFile).isSymbolicLink();
|
|
@@ -8009,12 +8320,12 @@ function applyRepoTeardown(repositoryRoot, manifest, plan) {
|
|
|
8009
8320
|
);
|
|
8010
8321
|
for (const item of removable.filter((i) => i.kind === "instruction-symlink")) {
|
|
8011
8322
|
const expected = expectedByName.get(item.label);
|
|
8012
|
-
if (repoReal === null || expected === void 0 || inspectSymlink(
|
|
8323
|
+
if (repoReal === null || expected === void 0 || inspectSymlink(join12(repoReal, item.label), expected).state !== "correct") {
|
|
8013
8324
|
changed(item.label);
|
|
8014
8325
|
continue;
|
|
8015
8326
|
}
|
|
8016
8327
|
try {
|
|
8017
|
-
unlinkSync(
|
|
8328
|
+
unlinkSync(join12(repoReal, item.label));
|
|
8018
8329
|
removed.push(item.label);
|
|
8019
8330
|
} catch (error) {
|
|
8020
8331
|
failed.push({ label: item.label, message: failureReason(error) });
|
|
@@ -8033,7 +8344,7 @@ function applyRepoTeardown(repositoryRoot, manifest, plan) {
|
|
|
8033
8344
|
continue;
|
|
8034
8345
|
}
|
|
8035
8346
|
try {
|
|
8036
|
-
unlinkSync(
|
|
8347
|
+
unlinkSync(join12(viewDir, item.label));
|
|
8037
8348
|
removed.push(`view/${item.label}`);
|
|
8038
8349
|
} catch (error) {
|
|
8039
8350
|
failed.push({ label: `view/${item.label}`, message: failureReason(error) });
|
|
@@ -8041,7 +8352,7 @@ function applyRepoTeardown(repositoryRoot, manifest, plan) {
|
|
|
8041
8352
|
}
|
|
8042
8353
|
const NOFOLLOW = fsConstants.O_NOFOLLOW ?? 0;
|
|
8043
8354
|
for (const item of removable.filter((i) => i.kind === "canonical-block")) {
|
|
8044
|
-
const canonicalFile =
|
|
8355
|
+
const canonicalFile = join12(anchorReal, "agents", canonicalName, CANONICAL_FILE);
|
|
8045
8356
|
try {
|
|
8046
8357
|
if (lstatSync(canonicalFile).isSymbolicLink()) {
|
|
8047
8358
|
changed(item.label);
|
|
@@ -8143,7 +8454,7 @@ async function doRunProjectTeardown(target, options, ctx = {}) {
|
|
|
8143
8454
|
const cwd = ctx.cwd ?? process.cwd();
|
|
8144
8455
|
const repositoryRoot = await resolveBasouRootForCommand(cwd, "project teardown");
|
|
8145
8456
|
const paths = basouPaths13(repositoryRoot);
|
|
8146
|
-
const manifest = await
|
|
8457
|
+
const manifest = await readManifest9(paths);
|
|
8147
8458
|
const plan = gatherRepoTeardown(repositoryRoot, manifest, target);
|
|
8148
8459
|
const willApply = options.apply === true && !plan.isAnchor && plan.removableCount > 0;
|
|
8149
8460
|
const { removed, failed } = willApply ? applyRepoTeardown(repositoryRoot, manifest, plan) : { removed: [], failed: [] };
|
|
@@ -8185,7 +8496,7 @@ async function doRunProjectArchive(target, options, ctx) {
|
|
|
8185
8496
|
const cwd = ctx.cwd ?? process.cwd();
|
|
8186
8497
|
const repositoryRoot = await resolveBasouRootForCommand(cwd, "project archive");
|
|
8187
8498
|
const paths = basouPaths13(repositoryRoot);
|
|
8188
|
-
const manifest = await
|
|
8499
|
+
const manifest = await readManifest9(paths);
|
|
8189
8500
|
const roster = manifest.repos ?? [];
|
|
8190
8501
|
let targetIsAnchor = false;
|
|
8191
8502
|
try {
|
|
@@ -8315,12 +8626,12 @@ function gatherRenameWiring(repositoryRoot, manifest, oldBasename) {
|
|
|
8315
8626
|
} catch {
|
|
8316
8627
|
return { canonicalDirOld: false, viewLinkOld: false };
|
|
8317
8628
|
}
|
|
8318
|
-
const canonicalDirOld = existsSync(
|
|
8629
|
+
const canonicalDirOld = existsSync(join12(anchorReal, "agents", oldBasename));
|
|
8319
8630
|
let viewLinkOld = false;
|
|
8320
8631
|
const viewPath = manifest.workspace.view;
|
|
8321
8632
|
if (viewPath !== void 0) {
|
|
8322
8633
|
try {
|
|
8323
|
-
lstatSync(
|
|
8634
|
+
lstatSync(join12(resolveViewDir(repositoryRoot, viewPath), oldBasename));
|
|
8324
8635
|
viewLinkOld = true;
|
|
8325
8636
|
} catch {
|
|
8326
8637
|
}
|
|
@@ -8342,7 +8653,7 @@ async function doRunProjectRename(oldPath, newPath, options, ctx) {
|
|
|
8342
8653
|
const cwd = ctx.cwd ?? process.cwd();
|
|
8343
8654
|
const repositoryRoot = await resolveBasouRootForCommand(cwd, "project rename");
|
|
8344
8655
|
const paths = basouPaths13(repositoryRoot);
|
|
8345
|
-
const manifest = await
|
|
8656
|
+
const manifest = await readManifest9(paths);
|
|
8346
8657
|
const roster = manifest.repos ?? [];
|
|
8347
8658
|
let oldIsAnchor = false;
|
|
8348
8659
|
try {
|
|
@@ -8616,7 +8927,7 @@ async function doRunProjectDerive(options, ctx) {
|
|
|
8616
8927
|
const cwd = ctx.cwd ?? process.cwd();
|
|
8617
8928
|
const repositoryRoot = await resolveBasouRootForCommand(cwd, "project derive");
|
|
8618
8929
|
const paths = basouPaths13(repositoryRoot);
|
|
8619
|
-
const manifest = await
|
|
8930
|
+
const manifest = await readManifest9(paths);
|
|
8620
8931
|
if (manifest.repos === void 0 || manifest.repos.length === 0) {
|
|
8621
8932
|
console.log(
|
|
8622
8933
|
"# 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)."
|
|
@@ -8657,7 +8968,7 @@ async function doRunProjectSeedAnchor(options, ctx) {
|
|
|
8657
8968
|
const cwd = ctx.cwd ?? process.cwd();
|
|
8658
8969
|
const repositoryRoot = await resolveBasouRootForCommand(cwd, "project derive");
|
|
8659
8970
|
const paths = basouPaths13(repositoryRoot);
|
|
8660
|
-
const manifest = await
|
|
8971
|
+
const manifest = await readManifest9(paths);
|
|
8661
8972
|
const roster = manifest.repos ?? [];
|
|
8662
8973
|
console.log("# Anchor instruction-file seed (the planning master's own AGENTS.md)");
|
|
8663
8974
|
console.log("");
|
|
@@ -8665,7 +8976,7 @@ async function doRunProjectSeedAnchor(options, ctx) {
|
|
|
8665
8976
|
console.log("\u2139\uFE0F No repo roster declared \u2014 nothing to seed.");
|
|
8666
8977
|
return;
|
|
8667
8978
|
}
|
|
8668
|
-
const anchorDoc =
|
|
8979
|
+
const anchorDoc = join12(repositoryRoot, CANONICAL_FILE);
|
|
8669
8980
|
if (pathPresent(anchorDoc)) {
|
|
8670
8981
|
console.log(
|
|
8671
8982
|
`\u2705 The anchor's own \`${CANONICAL_FILE}\` already exists \u2014 hand-maintained, left untouched.`
|
|
@@ -8727,7 +9038,7 @@ function regularFileSpokes(repoReal) {
|
|
|
8727
9038
|
const out = [];
|
|
8728
9039
|
for (const spoke of ["CLAUDE.md", ".github/copilot-instructions.md"]) {
|
|
8729
9040
|
try {
|
|
8730
|
-
const st = lstatSync(
|
|
9041
|
+
const st = lstatSync(join12(repoReal, spoke));
|
|
8731
9042
|
if (!st.isSymbolicLink() && st.isFile()) out.push(spoke);
|
|
8732
9043
|
} catch {
|
|
8733
9044
|
}
|
|
@@ -8773,8 +9084,8 @@ function gatherRetrofit(repositoryRoot, anchorReal, roster, argAbs, argReal, vie
|
|
|
8773
9084
|
};
|
|
8774
9085
|
}
|
|
8775
9086
|
const isAnchor = argReal === anchorReal;
|
|
8776
|
-
const reachable = existsSync(
|
|
8777
|
-
const canonicalFile =
|
|
9087
|
+
const reachable = existsSync(join12(argReal, ".git"));
|
|
9088
|
+
const canonicalFile = join12(anchorReal, "agents", canonicalName, CANONICAL_FILE);
|
|
8778
9089
|
return {
|
|
8779
9090
|
path,
|
|
8780
9091
|
declared,
|
|
@@ -8783,15 +9094,15 @@ function gatherRetrofit(repositoryRoot, anchorReal, roster, argAbs, argReal, vie
|
|
|
8783
9094
|
reachable,
|
|
8784
9095
|
canonicalName,
|
|
8785
9096
|
...viewCanonicalName !== void 0 ? { viewCanonicalName } : {},
|
|
8786
|
-
agentsState: inspectAgentsState(
|
|
9097
|
+
agentsState: inspectAgentsState(join12(argReal, CANONICAL_FILE)),
|
|
8787
9098
|
canonicalExists: pathPresent(canonicalFile),
|
|
8788
9099
|
regularSpokes: regularFileSpokes(argReal)
|
|
8789
9100
|
};
|
|
8790
9101
|
}
|
|
8791
9102
|
function relocateAgentsFile(repoReal, canonicalFile) {
|
|
8792
|
-
const agentsFile =
|
|
9103
|
+
const agentsFile = join12(repoReal, CANONICAL_FILE);
|
|
8793
9104
|
try {
|
|
8794
|
-
mkdirSync(
|
|
9105
|
+
mkdirSync(dirname7(canonicalFile), { recursive: true });
|
|
8795
9106
|
} catch (error) {
|
|
8796
9107
|
return { ok: false, message: failureReason(error), partial: false };
|
|
8797
9108
|
}
|
|
@@ -8851,7 +9162,7 @@ async function doRunProjectRetrofit(repo, options, ctx) {
|
|
|
8851
9162
|
const cwd = ctx.cwd ?? process.cwd();
|
|
8852
9163
|
const repositoryRoot = await resolveBasouRootForCommand(cwd, "project retrofit");
|
|
8853
9164
|
const paths = basouPaths13(repositoryRoot);
|
|
8854
|
-
const manifest = await
|
|
9165
|
+
const manifest = await readManifest9(paths);
|
|
8855
9166
|
const roster = manifest.repos ?? [];
|
|
8856
9167
|
const anchorReal = realpathSync(repositoryRoot);
|
|
8857
9168
|
const viewPath = manifest.workspace.view;
|
|
@@ -8905,7 +9216,7 @@ async function doRunProjectRetrofit(repo, options, ctx) {
|
|
|
8905
9216
|
let failure;
|
|
8906
9217
|
let partial = false;
|
|
8907
9218
|
if (options.apply === true && plan.action === "relocate" && argReal !== void 0) {
|
|
8908
|
-
const canonicalFile =
|
|
9219
|
+
const canonicalFile = join12(anchorReal, "agents", plan.canonicalName, CANONICAL_FILE);
|
|
8909
9220
|
const res = relocateAgentsFile(argReal, canonicalFile);
|
|
8910
9221
|
if (res.ok) {
|
|
8911
9222
|
applied = true;
|
|
@@ -9290,14 +9601,14 @@ import {
|
|
|
9290
9601
|
basouPaths as basouPaths14,
|
|
9291
9602
|
findErrorCode as findErrorCode10,
|
|
9292
9603
|
loadPortfolioConfig as loadPortfolioConfig5,
|
|
9293
|
-
readManifest as
|
|
9604
|
+
readManifest as readManifest10
|
|
9294
9605
|
} from "@basou/core";
|
|
9295
9606
|
import { InvalidArgumentError as InvalidArgumentError4 } from "commander";
|
|
9296
9607
|
|
|
9297
9608
|
// src/commands/refresh-watch.ts
|
|
9298
|
-
import { readdir as
|
|
9609
|
+
import { readdir as readdir3, stat as stat6 } from "fs/promises";
|
|
9299
9610
|
import { homedir as homedir9 } from "os";
|
|
9300
|
-
import { join as
|
|
9611
|
+
import { join as join13 } from "path";
|
|
9301
9612
|
import {
|
|
9302
9613
|
assertApprovalStoreSafe as assertApprovalStoreSafe2,
|
|
9303
9614
|
assertLockStoreSafe as assertLockStoreSafe4,
|
|
@@ -9309,8 +9620,8 @@ var MIN_WATCH_INTERVAL_SEC = 5;
|
|
|
9309
9620
|
var MAX_WATCH_INTERVAL_SEC = 86400;
|
|
9310
9621
|
function watchedRoots(ctx) {
|
|
9311
9622
|
return [
|
|
9312
|
-
ctx.codexSessionsDir ??
|
|
9313
|
-
ctx.claudeProjectsDir ??
|
|
9623
|
+
ctx.codexSessionsDir ?? join13(homedir9(), ".codex", "sessions"),
|
|
9624
|
+
ctx.claudeProjectsDir ?? join13(homedir9(), ".claude", "projects")
|
|
9314
9625
|
];
|
|
9315
9626
|
}
|
|
9316
9627
|
async function scanSourceLogs(roots) {
|
|
@@ -9318,13 +9629,13 @@ async function scanSourceLogs(roots) {
|
|
|
9318
9629
|
const walk = async (dir) => {
|
|
9319
9630
|
let entries;
|
|
9320
9631
|
try {
|
|
9321
|
-
entries = await
|
|
9632
|
+
entries = await readdir3(dir, { withFileTypes: true });
|
|
9322
9633
|
} catch (error) {
|
|
9323
9634
|
if (findErrorCode9(error, "ENOENT") || findErrorCode9(error, "ENOTDIR")) return;
|
|
9324
9635
|
throw new Error("Failed to read a source log directory", { cause: error });
|
|
9325
9636
|
}
|
|
9326
9637
|
for (const entry of entries) {
|
|
9327
|
-
const full =
|
|
9638
|
+
const full = join13(dir, entry.name);
|
|
9328
9639
|
if (entry.isDirectory()) {
|
|
9329
9640
|
await walk(full);
|
|
9330
9641
|
} else if (entry.isFile() && entry.name.endsWith(".jsonl")) {
|
|
@@ -9607,7 +9918,7 @@ async function warnPosition(paths, result, ctx) {
|
|
|
9607
9918
|
}
|
|
9608
9919
|
async function retiredChannelNotice(paths) {
|
|
9609
9920
|
try {
|
|
9610
|
-
const manifest = await
|
|
9921
|
+
const manifest = await readManifest10(paths);
|
|
9611
9922
|
if (manifest.channels?.codex !== true) return null;
|
|
9612
9923
|
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";
|
|
9613
9924
|
} catch {
|
|
@@ -9750,7 +10061,7 @@ import {
|
|
|
9750
10061
|
findErrorCode as findErrorCode12,
|
|
9751
10062
|
findUnbindableRepos,
|
|
9752
10063
|
parseReviewRecordInput,
|
|
9753
|
-
readManifest as
|
|
10064
|
+
readManifest as readManifest11,
|
|
9754
10065
|
resolveRepoRoot,
|
|
9755
10066
|
sanitizePath as sanitizePath3
|
|
9756
10067
|
} from "@basou/core";
|
|
@@ -9837,7 +10148,7 @@ async function doRunReviewRecord(options, ctx) {
|
|
|
9837
10148
|
}
|
|
9838
10149
|
const now = ctx.nowProvider !== void 0 ? ctx.nowProvider() : /* @__PURE__ */ new Date();
|
|
9839
10150
|
const occurredAt = now.toISOString();
|
|
9840
|
-
const manifest = await
|
|
10151
|
+
const manifest = await readManifest11(paths);
|
|
9841
10152
|
const invocationArgs = options.file !== void 0 ? [
|
|
9842
10153
|
"--file",
|
|
9843
10154
|
sanitizePath3(resolve11(cwd, options.file), {
|
|
@@ -10177,7 +10488,7 @@ function unattachedLines(u) {
|
|
|
10177
10488
|
// src/commands/run.ts
|
|
10178
10489
|
import { mkdir as mkdir3, readFile as readFile8, rm as rm3 } from "fs/promises";
|
|
10179
10490
|
import { homedir as homedir11 } from "os";
|
|
10180
|
-
import { join as
|
|
10491
|
+
import { join as join14 } from "path";
|
|
10181
10492
|
import {
|
|
10182
10493
|
acquireLock as acquireLock5,
|
|
10183
10494
|
assertBasouRootSafe as assertBasouRootSafe13,
|
|
@@ -10194,7 +10505,7 @@ import {
|
|
|
10194
10505
|
getSnapshot as getSnapshot2,
|
|
10195
10506
|
overwriteYamlFile as overwriteYamlFile2,
|
|
10196
10507
|
prefixedUlid as prefixedUlid4,
|
|
10197
|
-
readManifest as
|
|
10508
|
+
readManifest as readManifest12,
|
|
10198
10509
|
readYamlFile as readYamlFile5,
|
|
10199
10510
|
resolveClaudeCodeCommand,
|
|
10200
10511
|
resolveCodexCommand,
|
|
@@ -10255,15 +10566,15 @@ async function runTrackedTool(args, options, ctx, adapter) {
|
|
|
10255
10566
|
const repoRoot = await resolveRepositoryRootInPlace(cwd, "run");
|
|
10256
10567
|
const paths = basouPaths18(repoRoot);
|
|
10257
10568
|
await assertBasouRootSafe13(paths.root);
|
|
10258
|
-
const manifest = await
|
|
10569
|
+
const manifest = await readManifest12(paths);
|
|
10259
10570
|
await assertSessionStoreSafe3(paths);
|
|
10260
10571
|
const sessionId = prefixedUlid4("ses");
|
|
10261
|
-
const sessionDir =
|
|
10572
|
+
const sessionDir = join14(paths.sessions, sessionId);
|
|
10262
10573
|
const appendEvent = ctx.appendEvent ?? (async (_sessionDir, event) => {
|
|
10263
10574
|
await coreAppendChainedEvent2(paths, sessionId, event);
|
|
10264
10575
|
});
|
|
10265
10576
|
const startedAt = now().toISOString();
|
|
10266
|
-
const sessionYamlPath =
|
|
10577
|
+
const sessionYamlPath = join14(sessionDir, "session.yaml");
|
|
10267
10578
|
const session = buildInitialSession2({
|
|
10268
10579
|
id: sessionId,
|
|
10269
10580
|
command,
|
|
@@ -10640,7 +10951,7 @@ async function noteCodexHookStatusPreSpawn(_cwd, ctx) {
|
|
|
10640
10951
|
|
|
10641
10952
|
// src/commands/session.ts
|
|
10642
10953
|
import { readFile as readFile9 } from "fs/promises";
|
|
10643
|
-
import { basename as basename8, isAbsolute as isAbsolute5, join as
|
|
10954
|
+
import { basename as basename8, isAbsolute as isAbsolute5, join as join15, relative as relative4, sep as sep3 } from "path";
|
|
10644
10955
|
import {
|
|
10645
10956
|
acquireLock as acquireLock6,
|
|
10646
10957
|
appendEventToExistingSession as appendEventToExistingSession3,
|
|
@@ -10656,7 +10967,7 @@ import {
|
|
|
10656
10967
|
loadSessionEntries as loadSessionEntries2,
|
|
10657
10968
|
normalizeSessionTimestamps,
|
|
10658
10969
|
readAllEvents,
|
|
10659
|
-
readManifest as
|
|
10970
|
+
readManifest as readManifest13,
|
|
10660
10971
|
readObservedDuration,
|
|
10661
10972
|
readYamlFile as readYamlFile6,
|
|
10662
10973
|
rechainSessionInPlace,
|
|
@@ -10772,8 +11083,8 @@ async function doRunSessionShow(idInput, options, ctx) {
|
|
|
10772
11083
|
const paths = basouPaths19(repositoryRoot);
|
|
10773
11084
|
await assertWorkspaceInitialized12(paths.root);
|
|
10774
11085
|
const sessionId = await resolveSessionId3(paths, idInput);
|
|
10775
|
-
const sessionDir =
|
|
10776
|
-
const sessionYamlPath =
|
|
11086
|
+
const sessionDir = join15(paths.sessions, sessionId);
|
|
11087
|
+
const sessionYamlPath = join15(sessionDir, "session.yaml");
|
|
10777
11088
|
let session;
|
|
10778
11089
|
try {
|
|
10779
11090
|
const raw = await readYamlFile6(sessionYamlPath);
|
|
@@ -11083,7 +11394,7 @@ async function doRunSessionImport(options, ctx) {
|
|
|
11083
11394
|
const repositoryRoot = await resolveRepositoryRootForSession(cwd, "import");
|
|
11084
11395
|
const paths = basouPaths19(repositoryRoot);
|
|
11085
11396
|
await assertWorkspaceInitialized12(paths.root);
|
|
11086
|
-
const manifest = await
|
|
11397
|
+
const manifest = await readManifest13(paths);
|
|
11087
11398
|
const rawBody = await readInputFile(options.from);
|
|
11088
11399
|
const json = parseJsonStrict(rawBody);
|
|
11089
11400
|
const parsed = SessionImportPayloadSchema2.safeParse(json);
|
|
@@ -11469,7 +11780,7 @@ import {
|
|
|
11469
11780
|
basouPaths as basouPaths21,
|
|
11470
11781
|
buildStatusSnapshot,
|
|
11471
11782
|
findErrorCode as findErrorCode15,
|
|
11472
|
-
readManifest as
|
|
11783
|
+
readManifest as readManifest14,
|
|
11473
11784
|
writeStatus
|
|
11474
11785
|
} from "@basou/core";
|
|
11475
11786
|
function registerStatusCommand(program) {
|
|
@@ -11499,7 +11810,7 @@ async function doRunStatus(options, ctx) {
|
|
|
11499
11810
|
}
|
|
11500
11811
|
let manifest;
|
|
11501
11812
|
try {
|
|
11502
|
-
manifest = await
|
|
11813
|
+
manifest = await readManifest14(paths);
|
|
11503
11814
|
} catch (error) {
|
|
11504
11815
|
if (findErrorCode15(error, "ENOENT")) {
|
|
11505
11816
|
throw new Error("Workspace not initialized. Run 'basou init' first.");
|
|
@@ -11540,7 +11851,7 @@ function formatVersionGateMessage(error) {
|
|
|
11540
11851
|
|
|
11541
11852
|
// src/commands/task.ts
|
|
11542
11853
|
import { readFile as readFile10 } from "fs/promises";
|
|
11543
|
-
import { join as
|
|
11854
|
+
import { join as join16 } from "path";
|
|
11544
11855
|
import {
|
|
11545
11856
|
archiveTask,
|
|
11546
11857
|
assertBasouRootSafe as assertBasouRootSafe17,
|
|
@@ -11554,7 +11865,7 @@ import {
|
|
|
11554
11865
|
loadSessionEntries as loadSessionEntries3,
|
|
11555
11866
|
loadTaskEntries,
|
|
11556
11867
|
prefixedUlid as prefixedUlid5,
|
|
11557
|
-
readManifest as
|
|
11868
|
+
readManifest as readManifest15,
|
|
11558
11869
|
readTaskFile,
|
|
11559
11870
|
readTaskFileWithArchiveFallback,
|
|
11560
11871
|
reconcileAllTasks,
|
|
@@ -11681,7 +11992,7 @@ async function doRunTaskNew(options, ctx) {
|
|
|
11681
11992
|
});
|
|
11682
11993
|
return;
|
|
11683
11994
|
}
|
|
11684
|
-
const manifest = await
|
|
11995
|
+
const manifest = await readManifest15(paths);
|
|
11685
11996
|
const result = await createTaskWithEvent({
|
|
11686
11997
|
mode: "ad-hoc",
|
|
11687
11998
|
paths,
|
|
@@ -11867,7 +12178,7 @@ async function doRunTaskShow(idInput, options, ctx) {
|
|
|
11867
12178
|
const events = [];
|
|
11868
12179
|
const linkedSessionIds = new Set(doc.task.task.linked_sessions);
|
|
11869
12180
|
for (const s of sessions) {
|
|
11870
|
-
const sessionDir =
|
|
12181
|
+
const sessionDir = join16(paths.sessions, s.sessionId);
|
|
11871
12182
|
try {
|
|
11872
12183
|
for await (const ev of replayEvents3(sessionDir, {
|
|
11873
12184
|
onWarning: (w) => printReplayWarning(w, s.sessionId)
|
|
@@ -12029,7 +12340,7 @@ async function doRunTaskStatus(taskIdInput, newStatusInput, options, ctx) {
|
|
|
12029
12340
|
});
|
|
12030
12341
|
return;
|
|
12031
12342
|
}
|
|
12032
|
-
const manifest = await
|
|
12343
|
+
const manifest = await readManifest15(paths);
|
|
12033
12344
|
const result = await updateTaskStatusWithEvent({
|
|
12034
12345
|
mode: "ad-hoc",
|
|
12035
12346
|
paths,
|
|
@@ -12083,7 +12394,7 @@ async function doRunTaskReconcile(options, ctx) {
|
|
|
12083
12394
|
const paths = basouPaths22(repositoryRoot);
|
|
12084
12395
|
await assertWorkspaceInitialized14(paths.root);
|
|
12085
12396
|
await assertLockStoreSafe6(paths);
|
|
12086
|
-
const manifest = await
|
|
12397
|
+
const manifest = await readManifest15(paths);
|
|
12087
12398
|
const nowProvider = ctx.nowProvider ?? (() => /* @__PURE__ */ new Date());
|
|
12088
12399
|
const write = options.write === true;
|
|
12089
12400
|
const verbose = isVerbose(options);
|
|
@@ -12263,7 +12574,7 @@ async function doRunTaskRefreshLinkage(taskIdInput, options, ctx) {
|
|
|
12263
12574
|
const repositoryRoot = await resolveRepositoryRootForTask(cwd, "refresh-linkage");
|
|
12264
12575
|
const paths = basouPaths22(repositoryRoot);
|
|
12265
12576
|
await assertWorkspaceInitialized14(paths.root);
|
|
12266
|
-
const manifest = await
|
|
12577
|
+
const manifest = await readManifest15(paths);
|
|
12267
12578
|
const taskId = await resolveTaskId2(paths, taskIdInput);
|
|
12268
12579
|
const nowProvider = ctx.nowProvider ?? (() => /* @__PURE__ */ new Date());
|
|
12269
12580
|
const write = options.write === true;
|
|
@@ -12343,7 +12654,7 @@ async function doRunTaskEdit(taskIdInput, options, ctx) {
|
|
|
12343
12654
|
const repositoryRoot = await resolveRepositoryRootForTask(cwd, "edit");
|
|
12344
12655
|
const paths = basouPaths22(repositoryRoot);
|
|
12345
12656
|
await assertWorkspaceInitialized14(paths.root);
|
|
12346
|
-
const manifest = await
|
|
12657
|
+
const manifest = await readManifest15(paths);
|
|
12347
12658
|
const taskId = await resolveTaskId2(paths, taskIdInput);
|
|
12348
12659
|
const now = ctx.nowProvider !== void 0 ? ctx.nowProvider() : /* @__PURE__ */ new Date();
|
|
12349
12660
|
const occurredAt = now.toISOString();
|
|
@@ -12399,7 +12710,7 @@ async function doRunTaskDelete(taskIdInput, options, ctx) {
|
|
|
12399
12710
|
const repositoryRoot = await resolveRepositoryRootForTask(cwd, "delete");
|
|
12400
12711
|
const paths = basouPaths22(repositoryRoot);
|
|
12401
12712
|
await assertWorkspaceInitialized14(paths.root);
|
|
12402
|
-
const manifest = await
|
|
12713
|
+
const manifest = await readManifest15(paths);
|
|
12403
12714
|
const taskId = await resolveTaskId2(paths, taskIdInput);
|
|
12404
12715
|
if (options.yes !== true) {
|
|
12405
12716
|
await confirmDestructiveAction("delete", taskId);
|
|
@@ -12444,7 +12755,7 @@ async function doRunTaskArchive(taskIdInput, options, ctx) {
|
|
|
12444
12755
|
const repositoryRoot = await resolveRepositoryRootForTask(cwd, "archive");
|
|
12445
12756
|
const paths = basouPaths22(repositoryRoot);
|
|
12446
12757
|
await assertWorkspaceInitialized14(paths.root);
|
|
12447
|
-
const manifest = await
|
|
12758
|
+
const manifest = await readManifest15(paths);
|
|
12448
12759
|
const taskId = await resolveTaskId2(paths, taskIdInput);
|
|
12449
12760
|
if (options.yes !== true) {
|
|
12450
12761
|
await confirmDestructiveAction("archive", taskId);
|
|
@@ -12762,23 +13073,23 @@ async function assertWorkspaceInitialized15(basouRoot) {
|
|
|
12762
13073
|
// src/commands/view.ts
|
|
12763
13074
|
import { spawn } from "child_process";
|
|
12764
13075
|
import { createHash as createHash2 } from "crypto";
|
|
12765
|
-
import { basename as basename11, dirname as
|
|
13076
|
+
import { basename as basename11, dirname as dirname9, join as join20, resolve as resolve14 } from "path";
|
|
12766
13077
|
import {
|
|
12767
13078
|
assertBasouRootSafe as assertBasouRootSafe19,
|
|
12768
13079
|
basouPaths as basouPaths25,
|
|
12769
13080
|
findErrorCode as findErrorCode19,
|
|
12770
13081
|
loadPortfolioConfig as loadPortfolioConfig6,
|
|
12771
|
-
readManifest as
|
|
13082
|
+
readManifest as readManifest19
|
|
12772
13083
|
} from "@basou/core";
|
|
12773
13084
|
import { InvalidArgumentError as InvalidArgumentError8 } from "commander";
|
|
12774
13085
|
|
|
12775
13086
|
// src/lib/portfolio-coverage.ts
|
|
12776
13087
|
import { createReadStream as createReadStream2 } from "fs";
|
|
12777
|
-
import { readdir as
|
|
13088
|
+
import { readdir as readdir4, stat as stat7 } from "fs/promises";
|
|
12778
13089
|
import { homedir as homedir12 } from "os";
|
|
12779
|
-
import { basename as basename9, dirname as
|
|
13090
|
+
import { basename as basename9, dirname as dirname8, join as join17 } from "path";
|
|
12780
13091
|
import { createInterface as createInterface2 } from "readline";
|
|
12781
|
-
import { basouPaths as basouPaths24, readManifest as
|
|
13092
|
+
import { basouPaths as basouPaths24, readManifest as readManifest16 } from "@basou/core";
|
|
12782
13093
|
function uncapturedTotal(result) {
|
|
12783
13094
|
return result.groups.reduce((sum, g) => sum + g.logs, 0) + result.unplaceable;
|
|
12784
13095
|
}
|
|
@@ -12788,8 +13099,8 @@ async function checkPortfolioCoverage(workspaces, ctx = {}) {
|
|
|
12788
13099
|
ctx.portfolioConfigPath
|
|
12789
13100
|
);
|
|
12790
13101
|
const listedDirs = new Set([...roots].map((root) => encodeProjectDir(root)));
|
|
12791
|
-
const claudeProjectsDir = ctx.claudeProjectsDir ??
|
|
12792
|
-
const codexSessionsDir = ctx.codexSessionsDir ??
|
|
13102
|
+
const claudeProjectsDir = ctx.claudeProjectsDir ?? join17(homedir12(), ".claude", "projects");
|
|
13103
|
+
const codexSessionsDir = ctx.codexSessionsDir ?? join17(homedir12(), ".codex", "sessions");
|
|
12793
13104
|
const tally = new Tally(roots);
|
|
12794
13105
|
const absentTrees = [];
|
|
12795
13106
|
const claudeFiles = await listClaudeTranscripts(claudeProjectsDir);
|
|
@@ -12800,7 +13111,7 @@ async function checkPortfolioCoverage(workspaces, ctx = {}) {
|
|
|
12800
13111
|
file,
|
|
12801
13112
|
"claude-code",
|
|
12802
13113
|
claudeTranscriptCwd,
|
|
12803
|
-
listedDirs.has(basename9(
|
|
13114
|
+
listedDirs.has(basename9(dirname8(file)))
|
|
12804
13115
|
);
|
|
12805
13116
|
}
|
|
12806
13117
|
}
|
|
@@ -12827,7 +13138,7 @@ async function collectDeclaredRoots(workspaces, portfolioConfigPath) {
|
|
|
12827
13138
|
}
|
|
12828
13139
|
let resolved;
|
|
12829
13140
|
try {
|
|
12830
|
-
const manifest = await
|
|
13141
|
+
const manifest = await readManifest16(basouPaths24(importRoot));
|
|
12831
13142
|
resolved = resolveSourceRoots({
|
|
12832
13143
|
projectFlags: [],
|
|
12833
13144
|
manifest,
|
|
@@ -12927,7 +13238,7 @@ async function isDirEntry(parent, entry) {
|
|
|
12927
13238
|
if (entry.isDirectory()) return true;
|
|
12928
13239
|
if (!entry.isSymbolicLink()) return false;
|
|
12929
13240
|
try {
|
|
12930
|
-
return (await stat7(
|
|
13241
|
+
return (await stat7(join17(parent, entry.name))).isDirectory();
|
|
12931
13242
|
} catch {
|
|
12932
13243
|
return false;
|
|
12933
13244
|
}
|
|
@@ -12935,22 +13246,22 @@ async function isDirEntry(parent, entry) {
|
|
|
12935
13246
|
async function listClaudeTranscripts(projectsRoot) {
|
|
12936
13247
|
let entries;
|
|
12937
13248
|
try {
|
|
12938
|
-
entries = await
|
|
13249
|
+
entries = await readdir4(projectsRoot, { withFileTypes: true });
|
|
12939
13250
|
} catch {
|
|
12940
13251
|
return void 0;
|
|
12941
13252
|
}
|
|
12942
13253
|
const files = [];
|
|
12943
13254
|
for (const entry of entries) {
|
|
12944
13255
|
if (!await isDirEntry(projectsRoot, entry)) continue;
|
|
12945
|
-
const full =
|
|
13256
|
+
const full = join17(projectsRoot, entry.name);
|
|
12946
13257
|
let names;
|
|
12947
13258
|
try {
|
|
12948
|
-
names = await
|
|
13259
|
+
names = await readdir4(full);
|
|
12949
13260
|
} catch {
|
|
12950
13261
|
continue;
|
|
12951
13262
|
}
|
|
12952
13263
|
for (const name of names) {
|
|
12953
|
-
if (name.endsWith(".jsonl")) files.push(
|
|
13264
|
+
if (name.endsWith(".jsonl")) files.push(join17(full, name));
|
|
12954
13265
|
}
|
|
12955
13266
|
}
|
|
12956
13267
|
return files.sort();
|
|
@@ -12960,12 +13271,12 @@ async function listCodexRollouts(sessionsRoot) {
|
|
|
12960
13271
|
const walk = async (dir) => {
|
|
12961
13272
|
let entries;
|
|
12962
13273
|
try {
|
|
12963
|
-
entries = await
|
|
13274
|
+
entries = await readdir4(dir, { withFileTypes: true });
|
|
12964
13275
|
} catch {
|
|
12965
13276
|
return;
|
|
12966
13277
|
}
|
|
12967
13278
|
for (const entry of entries) {
|
|
12968
|
-
const full =
|
|
13279
|
+
const full = join17(dir, entry.name);
|
|
12969
13280
|
if (entry.isDirectory()) await walk(full);
|
|
12970
13281
|
else if (entry.isFile() && entry.name.startsWith("rollout-") && entry.name.endsWith(".jsonl")) {
|
|
12971
13282
|
found.push(full);
|
|
@@ -12973,7 +13284,7 @@ async function listCodexRollouts(sessionsRoot) {
|
|
|
12973
13284
|
}
|
|
12974
13285
|
};
|
|
12975
13286
|
try {
|
|
12976
|
-
await
|
|
13287
|
+
await readdir4(sessionsRoot);
|
|
12977
13288
|
} catch {
|
|
12978
13289
|
return void 0;
|
|
12979
13290
|
}
|
|
@@ -13096,9 +13407,9 @@ function inertLines(result) {
|
|
|
13096
13407
|
// src/lib/portfolio-safety.ts
|
|
13097
13408
|
import { execFile as execFile2 } from "child_process";
|
|
13098
13409
|
import { lstat as lstat3, realpath as realpath5 } from "fs/promises";
|
|
13099
|
-
import { isAbsolute as isAbsolute6, join as
|
|
13410
|
+
import { isAbsolute as isAbsolute6, join as join18, relative as relative5, resolve as resolve12, sep as sep4 } from "path";
|
|
13100
13411
|
import { promisify as promisify2 } from "util";
|
|
13101
|
-
import { readManifest as
|
|
13412
|
+
import { readManifest as readManifest17 } from "@basou/core";
|
|
13102
13413
|
var execFileAsync2 = promisify2(execFile2);
|
|
13103
13414
|
function errorCode2(error) {
|
|
13104
13415
|
return error instanceof Error ? error.code : void 0;
|
|
@@ -13120,7 +13431,7 @@ function isBasouPath(p) {
|
|
|
13120
13431
|
async function inspectRepo(repoPath) {
|
|
13121
13432
|
let hasEntry = false;
|
|
13122
13433
|
try {
|
|
13123
|
-
await lstat3(
|
|
13434
|
+
await lstat3(join18(repoPath, ".basou"));
|
|
13124
13435
|
hasEntry = true;
|
|
13125
13436
|
} catch (error) {
|
|
13126
13437
|
if (errorCode2(error) !== "ENOENT") {
|
|
@@ -13154,7 +13465,7 @@ async function checkPortfolioSafety(workspaces) {
|
|
|
13154
13465
|
let viewPath;
|
|
13155
13466
|
let isMaster = false;
|
|
13156
13467
|
try {
|
|
13157
|
-
const manifest = await
|
|
13468
|
+
const manifest = await readManifest17(ws.paths);
|
|
13158
13469
|
sourceRoots = manifest.import?.source_roots ?? [];
|
|
13159
13470
|
viewPath = manifest.workspace.view;
|
|
13160
13471
|
isMaster = true;
|
|
@@ -13274,7 +13585,7 @@ function formatSafetyReport(result) {
|
|
|
13274
13585
|
|
|
13275
13586
|
// src/lib/view-server.ts
|
|
13276
13587
|
import { createServer } from "http";
|
|
13277
|
-
import { basename as basename10, join as
|
|
13588
|
+
import { basename as basename10, join as join19, resolve as resolve13 } from "path";
|
|
13278
13589
|
import {
|
|
13279
13590
|
boardLivePage,
|
|
13280
13591
|
boardPage,
|
|
@@ -13289,7 +13600,7 @@ import {
|
|
|
13289
13600
|
loadSessionEntries as loadSessionEntries4,
|
|
13290
13601
|
loadTaskEntries as loadTaskEntries2,
|
|
13291
13602
|
readAllEvents as readAllEvents2,
|
|
13292
|
-
readManifest as
|
|
13603
|
+
readManifest as readManifest18,
|
|
13293
13604
|
readMarkdownFile as readMarkdownFile9,
|
|
13294
13605
|
readSessionYaml as readSessionYaml3,
|
|
13295
13606
|
readTaskFile as readTaskFile2,
|
|
@@ -15213,7 +15524,7 @@ async function captureStaleness(ws, nowIso) {
|
|
|
15213
15524
|
async function overview(ws, nowProvider, resolveRemoteUrl) {
|
|
15214
15525
|
let manifest;
|
|
15215
15526
|
try {
|
|
15216
|
-
manifest = await
|
|
15527
|
+
manifest = await readManifest18(ws.paths);
|
|
15217
15528
|
} catch (error) {
|
|
15218
15529
|
if (findErrorCode18(error, "ENOENT")) {
|
|
15219
15530
|
return { initialized: false, repoRoot: ws.repoRoot };
|
|
@@ -15298,7 +15609,7 @@ async function sessionDetail(ws, sessionId) {
|
|
|
15298
15609
|
throw error;
|
|
15299
15610
|
}
|
|
15300
15611
|
try {
|
|
15301
|
-
const events = await readAllEvents2(
|
|
15612
|
+
const events = await readAllEvents2(join19(ws.paths.sessions, sessionId));
|
|
15302
15613
|
return { session, events };
|
|
15303
15614
|
} catch {
|
|
15304
15615
|
return { session, events: [], degraded: true };
|
|
@@ -15558,12 +15869,12 @@ function sharedWhileRunning(run) {
|
|
|
15558
15869
|
async function viewBoardOf(root, paths) {
|
|
15559
15870
|
let own;
|
|
15560
15871
|
try {
|
|
15561
|
-
own = (await
|
|
15872
|
+
own = (await readManifest19(paths)).repos?.find((repo) => repo.path === ".");
|
|
15562
15873
|
} catch {
|
|
15563
15874
|
own = void 0;
|
|
15564
15875
|
}
|
|
15565
15876
|
if (own?.visibility !== "private") return { why: "no_board" };
|
|
15566
|
-
return { recordsDir:
|
|
15877
|
+
return { recordsDir: join20(root, dirname9(DEFAULT_BOARD_PATH), "records") };
|
|
15567
15878
|
}
|
|
15568
15879
|
async function buildPortfolioDeps(workspaceFlags, ctx, cwd) {
|
|
15569
15880
|
const specs = workspaceFlags.length > 0 ? workspaceFlags.map((p) => ({ path: resolve14(cwd, p) })) : await loadPortfolioConfig6(ctx.portfolioConfigPath);
|
|
@@ -15596,7 +15907,7 @@ async function buildWorkspaceEntry(repoRoot, ctx, labelOverride) {
|
|
|
15596
15907
|
...ctx.codexSessionsDir !== void 0 ? { codexSessionsDir: ctx.codexSessionsDir } : {}
|
|
15597
15908
|
};
|
|
15598
15909
|
try {
|
|
15599
|
-
const manifest = await
|
|
15910
|
+
const manifest = await readManifest19(paths);
|
|
15600
15911
|
return {
|
|
15601
15912
|
key: manifest.workspace.id,
|
|
15602
15913
|
label: labelOverride ?? manifest.workspace.name,
|
|
@@ -15717,7 +16028,7 @@ function installExperimentalNotice(program, names = EXPERIMENTAL_COMMANDS, write
|
|
|
15717
16028
|
function readBuildStamp() {
|
|
15718
16029
|
if (false) return void 0;
|
|
15719
16030
|
try {
|
|
15720
|
-
return JSON.parse('{"version":"0.
|
|
16031
|
+
return JSON.parse('{"version":"0.68.0","commit":"52aa4d4","committedAt":"2026-10-09T22:45:36+09:00"}');
|
|
15721
16032
|
} catch {
|
|
15722
16033
|
return void 0;
|
|
15723
16034
|
}
|