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