@basou/cli 0.17.0 → 0.18.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 +288 -75
- package/dist/index.js.map +1 -1
- package/dist/program.js +288 -75
- package/dist/program.js.map +1 -1
- package/package.json +2 -2
package/dist/program.js
CHANGED
|
@@ -635,17 +635,21 @@ function printNoApprovals(options) {
|
|
|
635
635
|
// src/commands/decision.ts
|
|
636
636
|
import { readFile } from "fs/promises";
|
|
637
637
|
import { homedir as homedir2 } from "os";
|
|
638
|
-
import { resolve as resolve3 } from "path";
|
|
638
|
+
import { join as join3, resolve as resolve3 } from "path";
|
|
639
639
|
import {
|
|
640
|
+
AGENT_INFRA_DIRS,
|
|
640
641
|
acquireLock as acquireLock2,
|
|
641
642
|
appendEventToExistingSession,
|
|
642
643
|
assertBasouRootSafe as assertBasouRootSafe2,
|
|
643
644
|
basouPaths as basouPaths3,
|
|
645
|
+
classifyFilesBySourceRoot,
|
|
644
646
|
createAdHocSessionWithEvent,
|
|
645
647
|
findErrorCode as findErrorCode2,
|
|
646
648
|
isValidPrefixedId,
|
|
649
|
+
loadSessionEntries,
|
|
647
650
|
prefixedUlid as prefixedUlid2,
|
|
648
651
|
readManifest as readManifest2,
|
|
652
|
+
replayEvents as replayEvents2,
|
|
649
653
|
resolveRepositoryRoot as resolveRepositoryRoot2,
|
|
650
654
|
resolveSessionId,
|
|
651
655
|
sanitizePath
|
|
@@ -845,6 +849,17 @@ function registerDecisionCommand(program) {
|
|
|
845
849
|
).option("--file <path>", "Read the JSON array from a file instead of stdin").option("--dry-run", "Validate and preview the decisions without writing them").option("--json", "Output the result as JSON").option("-v, --verbose", "Show error causes").addHelpText("after", CAPTURE_HELP).action(async (options) => {
|
|
846
850
|
await runDecisionCapture(options);
|
|
847
851
|
});
|
|
852
|
+
decision.command("void").description(
|
|
853
|
+
"Void (or supersede) a recorded decision. Append-only: the original is kept but struck in decisions.md and skipped as orientation's latest direction. Use when a decision was wrong or recorded in the wrong project."
|
|
854
|
+
).argument("<decision_id>", "The decision to void (its decision_ ULID)").option("--reason <text>", "Why the decision is voided", parseReason).option(
|
|
855
|
+
"--superseded-by <decision_id>",
|
|
856
|
+
"The decision that replaces this one (records a supersede rather than a plain void)"
|
|
857
|
+
).option(
|
|
858
|
+
"--session <session_id>",
|
|
859
|
+
"Attach to an existing session; otherwise an ad-hoc session is created"
|
|
860
|
+
).option("--json", "Output the result as JSON").option("-v, --verbose", "Show error causes").action(async (decisionId, options) => {
|
|
861
|
+
await runDecisionVoid(decisionId, options);
|
|
862
|
+
});
|
|
848
863
|
}
|
|
849
864
|
var CAPTURE_HELP = `
|
|
850
865
|
Input format (a JSON array; one object per decision):
|
|
@@ -879,6 +894,28 @@ async function runDecisionRecord(options, ctx = {}) {
|
|
|
879
894
|
process.exitCode = 1;
|
|
880
895
|
}
|
|
881
896
|
}
|
|
897
|
+
async function warnLinkedFilesOutsideRoots(input) {
|
|
898
|
+
if (input.linkedFiles.length === 0) return;
|
|
899
|
+
try {
|
|
900
|
+
const manifest = await readManifest2(input.paths);
|
|
901
|
+
if ((manifest.import?.source_roots?.length ?? 0) === 0) return;
|
|
902
|
+
const scope = await classifyFilesBySourceRoot({
|
|
903
|
+
files: input.linkedFiles,
|
|
904
|
+
workingDirectory: input.cwd,
|
|
905
|
+
sourceRoots: manifest.import?.source_roots,
|
|
906
|
+
masterRoot: input.repositoryRoot,
|
|
907
|
+
extraInRoot: AGENT_INFRA_DIRS
|
|
908
|
+
});
|
|
909
|
+
if (scope.outOfRoot.length === 0) return;
|
|
910
|
+
const PATH_SAMPLE = 5;
|
|
911
|
+
const sample = scope.outOfRoot.slice(0, PATH_SAMPLE).join(", ");
|
|
912
|
+
const more = scope.outOfRoot.length > PATH_SAMPLE ? ` (... +${scope.outOfRoot.length - PATH_SAMPLE} more)` : "";
|
|
913
|
+
console.error(
|
|
914
|
+
`basou: ${scope.outOfRoot.length} linked file(s) resolve outside this project's source_roots: ${sample}${more} \u2014 this decision may belong to another project.`
|
|
915
|
+
);
|
|
916
|
+
} catch {
|
|
917
|
+
}
|
|
918
|
+
}
|
|
882
919
|
async function doRunDecisionRecord(options, ctx) {
|
|
883
920
|
const cwd = ctx.cwd ?? process.cwd();
|
|
884
921
|
const repositoryRoot = await resolveRepositoryRootForDecision(cwd);
|
|
@@ -888,6 +925,12 @@ async function doRunDecisionRecord(options, ctx) {
|
|
|
888
925
|
const occurredAt = now.toISOString();
|
|
889
926
|
const decisionId = prefixedUlid2("decision");
|
|
890
927
|
const rich = pickRichFields(options);
|
|
928
|
+
await warnLinkedFilesOutsideRoots({
|
|
929
|
+
linkedFiles: rich.linked_files ?? [],
|
|
930
|
+
cwd,
|
|
931
|
+
paths,
|
|
932
|
+
repositoryRoot
|
|
933
|
+
});
|
|
891
934
|
if (options.session !== void 0) {
|
|
892
935
|
const sessionId = await resolveSessionId(paths, options.session);
|
|
893
936
|
const sesId = sessionId;
|
|
@@ -971,6 +1014,12 @@ async function doRunDecisionCapture(options, ctx) {
|
|
|
971
1014
|
await assertWorkspaceInitialized2(paths.root);
|
|
972
1015
|
const raw = await readCaptureInput(options, ctx);
|
|
973
1016
|
const decisions = parseCaptureInput(raw);
|
|
1017
|
+
await warnLinkedFilesOutsideRoots({
|
|
1018
|
+
linkedFiles: decisions.flatMap((d) => d.linked_files ?? []),
|
|
1019
|
+
cwd,
|
|
1020
|
+
paths,
|
|
1021
|
+
repositoryRoot
|
|
1022
|
+
});
|
|
974
1023
|
if (options.dryRun === true) {
|
|
975
1024
|
printCapturePreview(options, decisions);
|
|
976
1025
|
return;
|
|
@@ -1017,6 +1066,161 @@ async function doRunDecisionCapture(options, ctx) {
|
|
|
1017
1066
|
}))
|
|
1018
1067
|
});
|
|
1019
1068
|
}
|
|
1069
|
+
async function runDecisionVoid(decisionId, options, ctx = {}) {
|
|
1070
|
+
try {
|
|
1071
|
+
await doRunDecisionVoid(decisionId, options, ctx);
|
|
1072
|
+
} catch (error) {
|
|
1073
|
+
renderCliError(error, {
|
|
1074
|
+
verbose: isVerbose(options),
|
|
1075
|
+
classifiers: [failedToFinalizeClassifier]
|
|
1076
|
+
});
|
|
1077
|
+
process.exitCode = 1;
|
|
1078
|
+
}
|
|
1079
|
+
}
|
|
1080
|
+
async function doRunDecisionVoid(decisionId, options, ctx) {
|
|
1081
|
+
if (!isDecisionId(decisionId)) {
|
|
1082
|
+
throw new Error(`Invalid decision id: ${decisionId} (expected a decision_<ULID>).`);
|
|
1083
|
+
}
|
|
1084
|
+
if (options.supersededBy !== void 0 && !isDecisionId(options.supersededBy)) {
|
|
1085
|
+
throw new Error(
|
|
1086
|
+
`Invalid --superseded-by id: ${options.supersededBy} (expected a decision_<ULID>).`
|
|
1087
|
+
);
|
|
1088
|
+
}
|
|
1089
|
+
if (options.supersededBy === decisionId) {
|
|
1090
|
+
throw new Error("A decision cannot supersede itself.");
|
|
1091
|
+
}
|
|
1092
|
+
const cwd = ctx.cwd ?? process.cwd();
|
|
1093
|
+
const repositoryRoot = await resolveBasouRootForCommand(cwd, "decision void");
|
|
1094
|
+
const paths = basouPaths3(repositoryRoot);
|
|
1095
|
+
await assertWorkspaceInitialized2(paths.root);
|
|
1096
|
+
if (!await decisionExists(paths, decisionId)) {
|
|
1097
|
+
throw new Error(
|
|
1098
|
+
`Decision ${decisionId} not found in this workspace. Run 'basou decisions generate' or check the id.`
|
|
1099
|
+
);
|
|
1100
|
+
}
|
|
1101
|
+
const now = ctx.nowProvider !== void 0 ? ctx.nowProvider() : /* @__PURE__ */ new Date();
|
|
1102
|
+
const occurredAt = now.toISOString();
|
|
1103
|
+
const reason = options.reason;
|
|
1104
|
+
const supersededBy = options.supersededBy;
|
|
1105
|
+
if (options.session !== void 0) {
|
|
1106
|
+
const sessionId = await resolveSessionId(paths, options.session);
|
|
1107
|
+
const sessionLock = await acquireLock2(paths, "session", sessionId);
|
|
1108
|
+
let result;
|
|
1109
|
+
try {
|
|
1110
|
+
result = await appendEventToExistingSession({
|
|
1111
|
+
paths,
|
|
1112
|
+
sessionId,
|
|
1113
|
+
eventBuilder: (eventId) => buildDecisionVoidedEvent({
|
|
1114
|
+
eventId,
|
|
1115
|
+
sessionId,
|
|
1116
|
+
decisionId,
|
|
1117
|
+
occurredAt,
|
|
1118
|
+
reason,
|
|
1119
|
+
supersededBy
|
|
1120
|
+
})
|
|
1121
|
+
});
|
|
1122
|
+
} finally {
|
|
1123
|
+
await sessionLock.release();
|
|
1124
|
+
}
|
|
1125
|
+
printVoidResult(options, {
|
|
1126
|
+
mode: "attached",
|
|
1127
|
+
sessionId,
|
|
1128
|
+
decisionId,
|
|
1129
|
+
eventId: result.eventId,
|
|
1130
|
+
sessionStatus: result.sessionStatus,
|
|
1131
|
+
reason,
|
|
1132
|
+
supersededBy
|
|
1133
|
+
});
|
|
1134
|
+
return;
|
|
1135
|
+
}
|
|
1136
|
+
const manifest = await readManifest2(paths);
|
|
1137
|
+
const adHoc = await createAdHocSessionWithEvent({
|
|
1138
|
+
paths,
|
|
1139
|
+
manifest,
|
|
1140
|
+
label: `Ad-hoc decision void: ${decisionId}`,
|
|
1141
|
+
occurredAt,
|
|
1142
|
+
sessionSource: "human",
|
|
1143
|
+
workingDirectory: repositoryRoot,
|
|
1144
|
+
invocation: { command: "basou decision void", args: [decisionId] },
|
|
1145
|
+
targetEventBuilders: [
|
|
1146
|
+
(sessionId, eventId) => buildDecisionVoidedEvent({
|
|
1147
|
+
eventId,
|
|
1148
|
+
sessionId,
|
|
1149
|
+
decisionId,
|
|
1150
|
+
occurredAt,
|
|
1151
|
+
reason,
|
|
1152
|
+
supersededBy
|
|
1153
|
+
})
|
|
1154
|
+
]
|
|
1155
|
+
});
|
|
1156
|
+
printVoidResult(options, {
|
|
1157
|
+
mode: "ad-hoc",
|
|
1158
|
+
sessionId: adHoc.sessionId,
|
|
1159
|
+
decisionId,
|
|
1160
|
+
eventId: adHoc.targetEventIds[0],
|
|
1161
|
+
sessionStatus: "completed",
|
|
1162
|
+
reason,
|
|
1163
|
+
supersededBy
|
|
1164
|
+
});
|
|
1165
|
+
}
|
|
1166
|
+
function isDecisionId(value) {
|
|
1167
|
+
return value.startsWith("decision_") && isValidPrefixedId(value);
|
|
1168
|
+
}
|
|
1169
|
+
async function decisionExists(paths, decisionId) {
|
|
1170
|
+
const entries = await loadSessionEntries(paths, { now: /* @__PURE__ */ new Date() });
|
|
1171
|
+
for (const entry of entries) {
|
|
1172
|
+
const sessionDir = join3(paths.sessions, entry.sessionId);
|
|
1173
|
+
try {
|
|
1174
|
+
for await (const ev of replayEvents2(sessionDir, {})) {
|
|
1175
|
+
if (ev.type === "decision_recorded" && ev.decision_id === decisionId) return true;
|
|
1176
|
+
}
|
|
1177
|
+
} catch {
|
|
1178
|
+
}
|
|
1179
|
+
}
|
|
1180
|
+
return false;
|
|
1181
|
+
}
|
|
1182
|
+
function buildDecisionVoidedEvent(input) {
|
|
1183
|
+
return {
|
|
1184
|
+
schema_version: "0.1.0",
|
|
1185
|
+
id: input.eventId,
|
|
1186
|
+
session_id: input.sessionId,
|
|
1187
|
+
occurred_at: input.occurredAt,
|
|
1188
|
+
source: "local-cli",
|
|
1189
|
+
type: "decision_voided",
|
|
1190
|
+
decision_id: input.decisionId,
|
|
1191
|
+
...input.reason !== void 0 ? { reason: input.reason } : {},
|
|
1192
|
+
...input.supersededBy !== void 0 ? { superseded_by: input.supersededBy } : {}
|
|
1193
|
+
};
|
|
1194
|
+
}
|
|
1195
|
+
function printVoidResult(options, result) {
|
|
1196
|
+
if (options.json === true) {
|
|
1197
|
+
console.log(
|
|
1198
|
+
JSON.stringify({
|
|
1199
|
+
event_id: result.eventId,
|
|
1200
|
+
session_id: result.sessionId,
|
|
1201
|
+
decision_id: result.decisionId,
|
|
1202
|
+
session_status: result.sessionStatus,
|
|
1203
|
+
mode: result.mode,
|
|
1204
|
+
...result.reason !== void 0 ? { reason: result.reason } : {},
|
|
1205
|
+
...result.supersededBy !== void 0 ? { superseded_by: result.supersededBy } : {}
|
|
1206
|
+
})
|
|
1207
|
+
);
|
|
1208
|
+
return;
|
|
1209
|
+
}
|
|
1210
|
+
const sid = shortSessionId(result.sessionId);
|
|
1211
|
+
const tail = result.supersededBy !== void 0 ? ` (superseded by ${result.supersededBy})` : "";
|
|
1212
|
+
if (result.mode === "ad-hoc") {
|
|
1213
|
+
console.log(`Voided ${result.decisionId} in ad-hoc session ${sid}${tail}`);
|
|
1214
|
+
} else {
|
|
1215
|
+
console.log(`Voided ${result.decisionId} in session ${sid} (${result.sessionStatus})${tail}`);
|
|
1216
|
+
}
|
|
1217
|
+
}
|
|
1218
|
+
function parseReason(raw) {
|
|
1219
|
+
if (raw.trim().length === 0) {
|
|
1220
|
+
throw new InvalidArgumentError("--reason must not be empty");
|
|
1221
|
+
}
|
|
1222
|
+
return raw;
|
|
1223
|
+
}
|
|
1020
1224
|
async function readCaptureInput(options, ctx) {
|
|
1021
1225
|
if (options.file !== void 0) {
|
|
1022
1226
|
try {
|
|
@@ -1408,7 +1612,7 @@ async function assertWorkspaceInitialized3(basouRoot) {
|
|
|
1408
1612
|
// src/commands/exec.ts
|
|
1409
1613
|
import { mkdir } from "fs/promises";
|
|
1410
1614
|
import { homedir as homedir3 } from "os";
|
|
1411
|
-
import { join as
|
|
1615
|
+
import { join as join4 } from "path";
|
|
1412
1616
|
import {
|
|
1413
1617
|
acquireLock as acquireLock3,
|
|
1414
1618
|
assertBasouRootSafe as assertBasouRootSafe4,
|
|
@@ -1448,13 +1652,13 @@ async function runExec(command, args, options, ctx = {}) {
|
|
|
1448
1652
|
await assertBasouRootSafe4(paths.root);
|
|
1449
1653
|
const manifest = await readManifest3(paths);
|
|
1450
1654
|
const sessionId = prefixedUlid3("ses");
|
|
1451
|
-
const sessionDir =
|
|
1655
|
+
const sessionDir = join4(paths.sessions, sessionId);
|
|
1452
1656
|
await mkdir(sessionDir, { recursive: true });
|
|
1453
1657
|
const appendEvent = ctx.appendEvent ?? (async (_sessionDir, event) => {
|
|
1454
1658
|
await coreAppendChainedEvent(paths, sessionId, event);
|
|
1455
1659
|
});
|
|
1456
1660
|
const startedAt = now().toISOString();
|
|
1457
|
-
const sessionYamlPath =
|
|
1661
|
+
const sessionYamlPath = join4(sessionDir, "session.yaml");
|
|
1458
1662
|
const session = buildInitialSession({
|
|
1459
1663
|
id: sessionId,
|
|
1460
1664
|
command,
|
|
@@ -1804,15 +2008,15 @@ async function assertWorkspaceInitialized4(basouRoot) {
|
|
|
1804
2008
|
import { createReadStream } from "fs";
|
|
1805
2009
|
import { readdir, readFile as readFile2, rm, stat as stat2 } from "fs/promises";
|
|
1806
2010
|
import { homedir as homedir4 } from "os";
|
|
1807
|
-
import { basename as basename2, dirname, join as
|
|
2011
|
+
import { basename as basename2, dirname, join as join5, resolve as resolve4 } from "path";
|
|
1808
2012
|
import { createInterface } from "readline";
|
|
1809
2013
|
import {
|
|
1810
|
-
AGENT_INFRA_DIRS,
|
|
2014
|
+
AGENT_INFRA_DIRS as AGENT_INFRA_DIRS2,
|
|
1811
2015
|
assertBasouRootSafe as assertBasouRootSafe6,
|
|
1812
2016
|
basouPaths as basouPaths7,
|
|
1813
2017
|
CLAUDE_IMPORT_SOURCE,
|
|
1814
2018
|
CODEX_IMPORT_SOURCE,
|
|
1815
|
-
classifyFilesBySourceRoot,
|
|
2019
|
+
classifyFilesBySourceRoot as classifyFilesBySourceRoot2,
|
|
1816
2020
|
claudeTranscriptToImportPayload,
|
|
1817
2021
|
codexRolloutToImportPayload,
|
|
1818
2022
|
enumerateSessionDirs,
|
|
@@ -1890,7 +2094,7 @@ async function doRunImportClaudeCode(options, ctx) {
|
|
|
1890
2094
|
repoRoot: repositoryRoot,
|
|
1891
2095
|
cwd: ctx.cwd ?? process.cwd()
|
|
1892
2096
|
});
|
|
1893
|
-
const projectsRoot = ctx.claudeProjectsDir ??
|
|
2097
|
+
const projectsRoot = ctx.claudeProjectsDir ?? join5(homedir4(), ".claude", "projects");
|
|
1894
2098
|
const files = await selectTranscriptFiles(projectsRoot, projectPaths, options);
|
|
1895
2099
|
const projectSet = new Set(projectPaths);
|
|
1896
2100
|
const candidates = files.map((file) => {
|
|
@@ -1929,7 +2133,7 @@ async function doRunImportCodex(options, ctx) {
|
|
|
1929
2133
|
repoRoot: repositoryRoot,
|
|
1930
2134
|
cwd: ctx.cwd ?? process.cwd()
|
|
1931
2135
|
});
|
|
1932
|
-
const sessionsRoot = ctx.codexSessionsDir ??
|
|
2136
|
+
const sessionsRoot = ctx.codexSessionsDir ?? join5(homedir4(), ".codex", "sessions");
|
|
1933
2137
|
const rollouts = await discoverCodexRollouts(sessionsRoot, projectPaths, options);
|
|
1934
2138
|
const candidates = rollouts.map(({ file, externalId }) => ({
|
|
1935
2139
|
externalId,
|
|
@@ -1980,12 +2184,12 @@ async function importDerivedSessions(paths, manifest, options, sourceKind, candi
|
|
|
1980
2184
|
const noteCrossProject = async (externalId, payload) => {
|
|
1981
2185
|
if (!crossProjectCheck) return;
|
|
1982
2186
|
try {
|
|
1983
|
-
const scope = await
|
|
2187
|
+
const scope = await classifyFilesBySourceRoot2({
|
|
1984
2188
|
files: payload.session.related_files ?? [],
|
|
1985
2189
|
workingDirectory: payload.session.working_directory,
|
|
1986
2190
|
sourceRoots: projectPaths,
|
|
1987
2191
|
masterRoot: dirname(paths.root),
|
|
1988
|
-
extraInRoot:
|
|
2192
|
+
extraInRoot: AGENT_INFRA_DIRS2
|
|
1989
2193
|
});
|
|
1990
2194
|
if (scope.outOfRoot.length > 0) crossProject.push({ externalId, outOfRoot: scope.outOfRoot });
|
|
1991
2195
|
} catch {
|
|
@@ -2058,7 +2262,7 @@ async function importDerivedSessions(paths, manifest, options, sourceKind, candi
|
|
|
2058
2262
|
if (priors.length > 0 && options.force === true) {
|
|
2059
2263
|
if (options.dryRun !== true) {
|
|
2060
2264
|
for (const { sessionId } of priors) {
|
|
2061
|
-
await rm(
|
|
2265
|
+
await rm(join5(paths.sessions, sessionId), { recursive: true, force: true });
|
|
2062
2266
|
}
|
|
2063
2267
|
}
|
|
2064
2268
|
counts.replaced++;
|
|
@@ -2169,7 +2373,7 @@ async function selectTranscriptFiles(projectsRoot, projectPaths, options) {
|
|
|
2169
2373
|
if (options.session !== void 0) {
|
|
2170
2374
|
const matches = [];
|
|
2171
2375
|
for (const projectPath of projectPaths) {
|
|
2172
|
-
const file =
|
|
2376
|
+
const file = join5(projectsRoot, encodeProjectDir(projectPath), `${options.session}.jsonl`);
|
|
2173
2377
|
if (await pathExists(file)) matches.push(file);
|
|
2174
2378
|
}
|
|
2175
2379
|
if (matches.length === 0) {
|
|
@@ -2180,7 +2384,7 @@ async function selectTranscriptFiles(projectsRoot, projectPaths, options) {
|
|
|
2180
2384
|
const files = [];
|
|
2181
2385
|
let anyDirFound = false;
|
|
2182
2386
|
for (const projectPath of projectPaths) {
|
|
2183
|
-
const transcriptDir =
|
|
2387
|
+
const transcriptDir = join5(projectsRoot, encodeProjectDir(projectPath));
|
|
2184
2388
|
let entries;
|
|
2185
2389
|
try {
|
|
2186
2390
|
entries = await readdir(transcriptDir);
|
|
@@ -2190,7 +2394,7 @@ async function selectTranscriptFiles(projectsRoot, projectPaths, options) {
|
|
|
2190
2394
|
}
|
|
2191
2395
|
anyDirFound = true;
|
|
2192
2396
|
for (const name of entries) {
|
|
2193
|
-
if (name.endsWith(".jsonl")) files.push(
|
|
2397
|
+
if (name.endsWith(".jsonl")) files.push(join5(transcriptDir, name));
|
|
2194
2398
|
}
|
|
2195
2399
|
}
|
|
2196
2400
|
if (!anyDirFound) {
|
|
@@ -2247,7 +2451,7 @@ async function findRolloutFiles(sessionsRoot) {
|
|
|
2247
2451
|
throw new Error("Failed to read Codex sessions directory", { cause: error });
|
|
2248
2452
|
}
|
|
2249
2453
|
for (const entry of entries) {
|
|
2250
|
-
const full =
|
|
2454
|
+
const full = join5(dir, entry.name);
|
|
2251
2455
|
if (entry.isDirectory()) {
|
|
2252
2456
|
await walk(full, false);
|
|
2253
2457
|
} else if (entry.isFile() && entry.name.startsWith("rollout-") && entry.name.endsWith(".jsonl")) {
|
|
@@ -2671,12 +2875,12 @@ import {
|
|
|
2671
2875
|
|
|
2672
2876
|
// src/lib/hosts-config.ts
|
|
2673
2877
|
import { homedir as homedir5 } from "os";
|
|
2674
|
-
import { isAbsolute as isAbsolute2, join as
|
|
2878
|
+
import { isAbsolute as isAbsolute2, join as join6, resolve as resolve6 } from "path";
|
|
2675
2879
|
import { readYamlFile as readYamlFile4 } from "@basou/core";
|
|
2676
|
-
var DEFAULT_HOSTS_CONFIG_PATH =
|
|
2880
|
+
var DEFAULT_HOSTS_CONFIG_PATH = join6(homedir5(), ".basou", "hosts.yaml");
|
|
2677
2881
|
function expandTilde2(p) {
|
|
2678
2882
|
if (p === "~") return homedir5();
|
|
2679
|
-
if (p.startsWith("~/")) return
|
|
2883
|
+
if (p.startsWith("~/")) return join6(homedir5(), p.slice(2));
|
|
2680
2884
|
return p;
|
|
2681
2885
|
}
|
|
2682
2886
|
function isRecord2(value) {
|
|
@@ -2990,7 +3194,7 @@ import {
|
|
|
2990
3194
|
unlinkSync,
|
|
2991
3195
|
writeFileSync
|
|
2992
3196
|
} from "fs";
|
|
2993
|
-
import { basename as basename4, dirname as dirname2, isAbsolute as isAbsolute3, join as
|
|
3197
|
+
import { basename as basename4, dirname as dirname2, isAbsolute as isAbsolute3, join as join7, relative as relative2, resolve as resolve7 } from "path";
|
|
2994
3198
|
import {
|
|
2995
3199
|
basouPaths as basouPaths10,
|
|
2996
3200
|
GENERATED_END,
|
|
@@ -3249,7 +3453,7 @@ function classifySourceRoot(repositoryRoot, declaredPath) {
|
|
|
3249
3453
|
} catch {
|
|
3250
3454
|
return { path: declaredPath, kind: "unresolved" };
|
|
3251
3455
|
}
|
|
3252
|
-
return { path: declaredPath, kind: existsSync(
|
|
3456
|
+
return { path: declaredPath, kind: existsSync(join7(real, ".git")) ? "repo" : "non-repo" };
|
|
3253
3457
|
}
|
|
3254
3458
|
async function doRunProjectAdopt(options, ctx) {
|
|
3255
3459
|
const cwd = ctx.cwd ?? process.cwd();
|
|
@@ -3348,7 +3552,7 @@ async function gatherRepoWiring(repositoryRoot, entry) {
|
|
|
3348
3552
|
} catch {
|
|
3349
3553
|
return { ...base, reachable: false, instructionFiles: [] };
|
|
3350
3554
|
}
|
|
3351
|
-
if (!existsSync(
|
|
3555
|
+
if (!existsSync(join7(real, ".git"))) {
|
|
3352
3556
|
return { ...base, reachable: false, instructionFiles: [] };
|
|
3353
3557
|
}
|
|
3354
3558
|
try {
|
|
@@ -3356,7 +3560,7 @@ async function gatherRepoWiring(repositoryRoot, entry) {
|
|
|
3356
3560
|
for (const name of INSTRUCTION_FILES) {
|
|
3357
3561
|
let present = true;
|
|
3358
3562
|
try {
|
|
3359
|
-
lstatSync(
|
|
3563
|
+
lstatSync(join7(real, name));
|
|
3360
3564
|
} catch {
|
|
3361
3565
|
present = false;
|
|
3362
3566
|
}
|
|
@@ -3453,10 +3657,10 @@ function gatherRepoGitignore(repositoryRoot, entry) {
|
|
|
3453
3657
|
} catch {
|
|
3454
3658
|
return { ...base, reachable: false, currentLines: [] };
|
|
3455
3659
|
}
|
|
3456
|
-
if (!existsSync(
|
|
3660
|
+
if (!existsSync(join7(real, ".git"))) {
|
|
3457
3661
|
return { ...base, reachable: false, currentLines: [] };
|
|
3458
3662
|
}
|
|
3459
|
-
return { ...base, reachable: true, currentLines: readGitignoreLines(
|
|
3663
|
+
return { ...base, reachable: true, currentLines: readGitignoreLines(join7(real, ".gitignore")) };
|
|
3460
3664
|
}
|
|
3461
3665
|
function hasErrorCode(error) {
|
|
3462
3666
|
return error instanceof Error && typeof error.code === "string";
|
|
@@ -3470,7 +3674,7 @@ function readGitignoreLines(file) {
|
|
|
3470
3674
|
}
|
|
3471
3675
|
}
|
|
3472
3676
|
function applyGitignorePlan(repositoryRoot, plan) {
|
|
3473
|
-
const file =
|
|
3677
|
+
const file = join7(realpathSync(resolve7(repositoryRoot, plan.path)), ".gitignore");
|
|
3474
3678
|
let existing = "";
|
|
3475
3679
|
try {
|
|
3476
3680
|
existing = readFileSync(file, "utf8");
|
|
@@ -3589,16 +3793,16 @@ function gatherRepoSymlinks(repositoryRoot, anchorReal, entry) {
|
|
|
3589
3793
|
if (real === anchorReal) {
|
|
3590
3794
|
return { ...base, isAnchor: true, reachable: true, canonicalPresent: false, files: [] };
|
|
3591
3795
|
}
|
|
3592
|
-
if (!existsSync(
|
|
3796
|
+
if (!existsSync(join7(real, ".git"))) {
|
|
3593
3797
|
return { ...base, isAnchor: false, reachable: false, canonicalPresent: false, files: [] };
|
|
3594
3798
|
}
|
|
3595
|
-
const canonicalFile =
|
|
3799
|
+
const canonicalFile = join7(anchorReal, "agents", basename4(real), CANONICAL_FILE);
|
|
3596
3800
|
if (!existsSync(canonicalFile)) {
|
|
3597
3801
|
return { ...base, isAnchor: false, reachable: true, canonicalPresent: false, files: [] };
|
|
3598
3802
|
}
|
|
3599
3803
|
const files = expectedSymlinkTargets(real, canonicalFile).map(
|
|
3600
3804
|
(spec) => {
|
|
3601
|
-
const { state, actualTarget } = inspectSymlink(
|
|
3805
|
+
const { state, actualTarget } = inspectSymlink(join7(real, spec.name), spec.target);
|
|
3602
3806
|
return {
|
|
3603
3807
|
name: spec.name,
|
|
3604
3808
|
expectedTarget: spec.target,
|
|
@@ -3627,7 +3831,7 @@ function applySymlinkPlan(repositoryRoot, plan) {
|
|
|
3627
3831
|
const created = [];
|
|
3628
3832
|
const failed = [];
|
|
3629
3833
|
for (const { name, target } of plan.toCreate) {
|
|
3630
|
-
const filePath =
|
|
3834
|
+
const filePath = join7(real, name);
|
|
3631
3835
|
try {
|
|
3632
3836
|
mkdirSync(dirname2(filePath), { recursive: true });
|
|
3633
3837
|
symlinkSync(target, filePath);
|
|
@@ -3769,7 +3973,7 @@ function resolveViewDir(repositoryRoot, viewPath) {
|
|
|
3769
3973
|
return realpathSync(abs);
|
|
3770
3974
|
} catch {
|
|
3771
3975
|
try {
|
|
3772
|
-
return
|
|
3976
|
+
return join7(realpathSync(dirname2(abs)), basename4(abs));
|
|
3773
3977
|
} catch {
|
|
3774
3978
|
return abs;
|
|
3775
3979
|
}
|
|
@@ -3787,7 +3991,7 @@ function gatherViewRepo(repositoryRoot, viewDir, entry) {
|
|
|
3787
3991
|
return { path: entry.path, reachable: false };
|
|
3788
3992
|
}
|
|
3789
3993
|
const linkName = basename4(repoReal);
|
|
3790
|
-
const { state, actualTarget } = inspectSymlink(
|
|
3994
|
+
const { state, actualTarget } = inspectSymlink(join7(viewDir, linkName), expectedTarget);
|
|
3791
3995
|
return {
|
|
3792
3996
|
path: entry.path,
|
|
3793
3997
|
reachable: true,
|
|
@@ -3801,7 +4005,7 @@ function applyViewPlan(viewDir, toCreate) {
|
|
|
3801
4005
|
const created = [];
|
|
3802
4006
|
const failed = [];
|
|
3803
4007
|
for (const { name, target } of toCreate) {
|
|
3804
|
-
const filePath =
|
|
4008
|
+
const filePath = join7(viewDir, name);
|
|
3805
4009
|
try {
|
|
3806
4010
|
mkdirSync(dirname2(filePath), { recursive: true });
|
|
3807
4011
|
symlinkSync(target, filePath);
|
|
@@ -3816,7 +4020,7 @@ var TOP_LEVEL_INSTRUCTION_FILES_LOWER = new Set(
|
|
|
3816
4020
|
INSTRUCTION_FILES.filter((f) => !f.includes("/")).map((f) => f.toLowerCase())
|
|
3817
4021
|
);
|
|
3818
4022
|
function classifyViewLink(viewDir, name, rosterRealpaths) {
|
|
3819
|
-
const filePath =
|
|
4023
|
+
const filePath = join7(viewDir, name);
|
|
3820
4024
|
let isLink;
|
|
3821
4025
|
try {
|
|
3822
4026
|
isLink = lstatSync(filePath).isSymbolicLink();
|
|
@@ -3845,7 +4049,7 @@ function classifyViewLink(viewDir, name, rosterRealpaths) {
|
|
|
3845
4049
|
if (!isDir) {
|
|
3846
4050
|
return { target, kind: existsSync(resolved) ? "non-repo" : "broken" };
|
|
3847
4051
|
}
|
|
3848
|
-
return { target, kind: existsSync(
|
|
4052
|
+
return { target, kind: existsSync(join7(resolved, ".git")) ? "repo" : "non-repo" };
|
|
3849
4053
|
}
|
|
3850
4054
|
function gatherExistingViewLinks(viewDir, rosterRealpaths) {
|
|
3851
4055
|
let names;
|
|
@@ -3870,7 +4074,7 @@ function pruneViewLinks(viewDir, toPrune, rosterRealpaths) {
|
|
|
3870
4074
|
const pruned = [];
|
|
3871
4075
|
const failed = [];
|
|
3872
4076
|
for (const { name } of toPrune) {
|
|
3873
|
-
const filePath =
|
|
4077
|
+
const filePath = join7(viewDir, name);
|
|
3874
4078
|
const c = classifyViewLink(viewDir, name, rosterRealpaths);
|
|
3875
4079
|
if (c === null || c.kind !== "repo") {
|
|
3876
4080
|
failed.push({
|
|
@@ -4081,10 +4285,10 @@ async function runProjectPreset(options, ctx = {}) {
|
|
|
4081
4285
|
}
|
|
4082
4286
|
}
|
|
4083
4287
|
function canonicalFileFor(anchorReal, canonicalName) {
|
|
4084
|
-
return
|
|
4288
|
+
return join7(anchorReal, "agents", canonicalName, CANONICAL_FILE);
|
|
4085
4289
|
}
|
|
4086
4290
|
function canonicalLabelFor(canonicalName) {
|
|
4087
|
-
return
|
|
4291
|
+
return join7("agents", canonicalName, CANONICAL_FILE);
|
|
4088
4292
|
}
|
|
4089
4293
|
async function gatherRepoPreset(repositoryRoot, anchorReal, entry) {
|
|
4090
4294
|
const declared = {
|
|
@@ -4102,7 +4306,7 @@ async function gatherRepoPreset(repositoryRoot, anchorReal, entry) {
|
|
|
4102
4306
|
if (real === anchorReal) {
|
|
4103
4307
|
return { ...declared, isAnchor: true, reachable: true, canonicalPresent: false };
|
|
4104
4308
|
}
|
|
4105
|
-
if (!existsSync(
|
|
4309
|
+
if (!existsSync(join7(real, ".git"))) {
|
|
4106
4310
|
return { ...declared, isAnchor: false, reachable: false, canonicalPresent: false };
|
|
4107
4311
|
}
|
|
4108
4312
|
const canonicalName = basename4(real);
|
|
@@ -4333,24 +4537,24 @@ function gatherArchiveTeardown(repositoryRoot, manifest, target) {
|
|
|
4333
4537
|
const instructionFiles = [];
|
|
4334
4538
|
for (const name of INSTRUCTION_FILES) {
|
|
4335
4539
|
try {
|
|
4336
|
-
lstatSync(
|
|
4540
|
+
lstatSync(join7(real, name));
|
|
4337
4541
|
instructionFiles.push(name);
|
|
4338
4542
|
} catch {
|
|
4339
4543
|
}
|
|
4340
4544
|
}
|
|
4341
4545
|
let ignored;
|
|
4342
4546
|
try {
|
|
4343
|
-
ignored = new Set(readGitignoreLines(
|
|
4547
|
+
ignored = new Set(readGitignoreLines(join7(real, ".gitignore")).map((l) => l.trim()));
|
|
4344
4548
|
} catch {
|
|
4345
4549
|
ignored = /* @__PURE__ */ new Set();
|
|
4346
4550
|
}
|
|
4347
4551
|
const gitignorePatterns = INSTRUCTION_FILES.filter((p) => ignored.has(p) || ignored.has(`/${p}`));
|
|
4348
|
-
const canonical2 = existsSync(
|
|
4552
|
+
const canonical2 = existsSync(join7(anchorReal, "agents", canonicalName, CANONICAL_FILE));
|
|
4349
4553
|
let viewLink = false;
|
|
4350
4554
|
const viewPath = manifest.workspace.view;
|
|
4351
4555
|
if (viewPath !== void 0) {
|
|
4352
4556
|
try {
|
|
4353
|
-
lstatSync(
|
|
4557
|
+
lstatSync(join7(resolveViewDir(repositoryRoot, viewPath), canonicalName));
|
|
4354
4558
|
viewLink = true;
|
|
4355
4559
|
} catch {
|
|
4356
4560
|
}
|
|
@@ -4512,12 +4716,12 @@ function gatherRenameWiring(repositoryRoot, manifest, oldBasename) {
|
|
|
4512
4716
|
} catch {
|
|
4513
4717
|
return { canonicalDirOld: false, viewLinkOld: false };
|
|
4514
4718
|
}
|
|
4515
|
-
const canonicalDirOld = existsSync(
|
|
4719
|
+
const canonicalDirOld = existsSync(join7(anchorReal, "agents", oldBasename));
|
|
4516
4720
|
let viewLinkOld = false;
|
|
4517
4721
|
const viewPath = manifest.workspace.view;
|
|
4518
4722
|
if (viewPath !== void 0) {
|
|
4519
4723
|
try {
|
|
4520
|
-
lstatSync(
|
|
4724
|
+
lstatSync(join7(resolveViewDir(repositoryRoot, viewPath), oldBasename));
|
|
4521
4725
|
viewLinkOld = true;
|
|
4522
4726
|
} catch {
|
|
4523
4727
|
}
|
|
@@ -4666,7 +4870,7 @@ import {
|
|
|
4666
4870
|
// src/lib/durable-write.ts
|
|
4667
4871
|
import { randomUUID } from "crypto";
|
|
4668
4872
|
import { lstat, open, rename, stat as stat3, unlink as unlink2 } from "fs/promises";
|
|
4669
|
-
import { basename as basename5, dirname as dirname3, join as
|
|
4873
|
+
import { basename as basename5, dirname as dirname3, join as join8 } from "path";
|
|
4670
4874
|
async function assertNotSymlink(targetPath) {
|
|
4671
4875
|
try {
|
|
4672
4876
|
const st = await lstat(targetPath);
|
|
@@ -4682,7 +4886,7 @@ async function assertNotSymlink(targetPath) {
|
|
|
4682
4886
|
}
|
|
4683
4887
|
async function writeFileDurable(targetPath, content) {
|
|
4684
4888
|
const dir = dirname3(targetPath);
|
|
4685
|
-
const tmpPath =
|
|
4889
|
+
const tmpPath = join8(dir, `.${basename5(targetPath)}.tmp.${randomUUID()}`);
|
|
4686
4890
|
let mode = 420;
|
|
4687
4891
|
try {
|
|
4688
4892
|
mode = (await stat3(targetPath)).mode & 511;
|
|
@@ -4718,15 +4922,15 @@ async function writeFileDurable(targetPath, content) {
|
|
|
4718
4922
|
|
|
4719
4923
|
// src/lib/protocols-config.ts
|
|
4720
4924
|
import { homedir as homedir6 } from "os";
|
|
4721
|
-
import { isAbsolute as isAbsolute4, join as
|
|
4925
|
+
import { isAbsolute as isAbsolute4, join as join9, resolve as resolve8 } from "path";
|
|
4722
4926
|
import { readYamlFile as readYamlFile5 } from "@basou/core";
|
|
4723
|
-
var DEFAULT_PROTOCOLS_CONFIG_PATH =
|
|
4724
|
-
var DEFAULT_TARGET_PATH =
|
|
4927
|
+
var DEFAULT_PROTOCOLS_CONFIG_PATH = join9(homedir6(), ".basou", "protocols.yaml");
|
|
4928
|
+
var DEFAULT_TARGET_PATH = join9(homedir6(), ".claude", "CLAUDE.md");
|
|
4725
4929
|
var ALLOWED_TOP_KEYS = /* @__PURE__ */ new Set(["version", "protocols"]);
|
|
4726
4930
|
var ALLOWED_ENTRY_KEYS = /* @__PURE__ */ new Set(["source", "title"]);
|
|
4727
4931
|
function expandTilde3(p) {
|
|
4728
4932
|
if (p === "~") return homedir6();
|
|
4729
|
-
if (p.startsWith("~/")) return
|
|
4933
|
+
if (p.startsWith("~/")) return join9(homedir6(), p.slice(2));
|
|
4730
4934
|
return p;
|
|
4731
4935
|
}
|
|
4732
4936
|
function isRecord3(value) {
|
|
@@ -4979,15 +5183,15 @@ import { InvalidArgumentError as InvalidArgumentError3 } from "commander";
|
|
|
4979
5183
|
// src/commands/refresh-watch.ts
|
|
4980
5184
|
import { readdir as readdir2, stat as stat4 } from "fs/promises";
|
|
4981
5185
|
import { homedir as homedir7 } from "os";
|
|
4982
|
-
import { join as
|
|
5186
|
+
import { join as join10 } from "path";
|
|
4983
5187
|
import { findErrorCode as findErrorCode8 } from "@basou/core";
|
|
4984
5188
|
var DEFAULT_WATCH_INTERVAL_SEC = 30;
|
|
4985
5189
|
var MIN_WATCH_INTERVAL_SEC = 5;
|
|
4986
5190
|
var MAX_WATCH_INTERVAL_SEC = 86400;
|
|
4987
5191
|
function watchedRoots(ctx) {
|
|
4988
5192
|
return [
|
|
4989
|
-
ctx.codexSessionsDir ??
|
|
4990
|
-
ctx.claudeProjectsDir ??
|
|
5193
|
+
ctx.codexSessionsDir ?? join10(homedir7(), ".codex", "sessions"),
|
|
5194
|
+
ctx.claudeProjectsDir ?? join10(homedir7(), ".claude", "projects")
|
|
4991
5195
|
];
|
|
4992
5196
|
}
|
|
4993
5197
|
async function scanSourceLogs(roots) {
|
|
@@ -5001,7 +5205,7 @@ async function scanSourceLogs(roots) {
|
|
|
5001
5205
|
throw new Error("Failed to read a source log directory", { cause: error });
|
|
5002
5206
|
}
|
|
5003
5207
|
for (const entry of entries) {
|
|
5004
|
-
const full =
|
|
5208
|
+
const full = join10(dir, entry.name);
|
|
5005
5209
|
if (entry.isDirectory()) {
|
|
5006
5210
|
await walk(full);
|
|
5007
5211
|
} else if (entry.isFile() && entry.name.endsWith(".jsonl")) {
|
|
@@ -5523,7 +5727,7 @@ function renderReviewGaps(summary) {
|
|
|
5523
5727
|
// src/commands/run.ts
|
|
5524
5728
|
import { mkdir as mkdir2 } from "fs/promises";
|
|
5525
5729
|
import { homedir as homedir8 } from "os";
|
|
5526
|
-
import { join as
|
|
5730
|
+
import { join as join11 } from "path";
|
|
5527
5731
|
import {
|
|
5528
5732
|
acquireLock as acquireLock5,
|
|
5529
5733
|
assertBasouRootSafe as assertBasouRootSafe11,
|
|
@@ -5576,13 +5780,13 @@ async function runClaudeCode(args, options, ctx = {}) {
|
|
|
5576
5780
|
await assertBasouRootSafe11(paths.root);
|
|
5577
5781
|
const manifest = await readManifest7(paths);
|
|
5578
5782
|
const sessionId = prefixedUlid4("ses");
|
|
5579
|
-
const sessionDir =
|
|
5783
|
+
const sessionDir = join11(paths.sessions, sessionId);
|
|
5580
5784
|
await mkdir2(sessionDir, { recursive: true });
|
|
5581
5785
|
const appendEvent = ctx.appendEvent ?? (async (_sessionDir, event) => {
|
|
5582
5786
|
await coreAppendChainedEvent2(paths, sessionId, event);
|
|
5583
5787
|
});
|
|
5584
5788
|
const startedAt = now().toISOString();
|
|
5585
|
-
const sessionYamlPath =
|
|
5789
|
+
const sessionYamlPath = join11(sessionDir, "session.yaml");
|
|
5586
5790
|
const session = buildInitialSession2({
|
|
5587
5791
|
id: sessionId,
|
|
5588
5792
|
command,
|
|
@@ -5925,7 +6129,7 @@ async function resolveRepositoryRootForRun(cwd) {
|
|
|
5925
6129
|
|
|
5926
6130
|
// src/commands/session.ts
|
|
5927
6131
|
import { readFile as readFile4 } from "fs/promises";
|
|
5928
|
-
import { basename as basename6, isAbsolute as isAbsolute6, join as
|
|
6132
|
+
import { basename as basename6, isAbsolute as isAbsolute6, join as join12, relative as relative3 } from "path";
|
|
5929
6133
|
import {
|
|
5930
6134
|
acquireLock as acquireLock6,
|
|
5931
6135
|
appendEventToExistingSession as appendEventToExistingSession3,
|
|
@@ -5934,7 +6138,7 @@ import {
|
|
|
5934
6138
|
enumerateSessionDirs as enumerateSessionDirs2,
|
|
5935
6139
|
findErrorCode as findErrorCode11,
|
|
5936
6140
|
importSessionFromJson as importSessionFromJson2,
|
|
5937
|
-
loadSessionEntries,
|
|
6141
|
+
loadSessionEntries as loadSessionEntries2,
|
|
5938
6142
|
readAllEvents,
|
|
5939
6143
|
readManifest as readManifest8,
|
|
5940
6144
|
readYamlFile as readYamlFile7,
|
|
@@ -5998,7 +6202,7 @@ async function doRunSessionList(options, ctx) {
|
|
|
5998
6202
|
const paths = basouPaths15(repositoryRoot);
|
|
5999
6203
|
await assertWorkspaceInitialized10(paths.root);
|
|
6000
6204
|
const now = /* @__PURE__ */ new Date();
|
|
6001
|
-
const records = (await
|
|
6205
|
+
const records = (await loadSessionEntries2(paths, {
|
|
6002
6206
|
now,
|
|
6003
6207
|
onWarning: (w, sid) => printReplayWarning(w, sid),
|
|
6004
6208
|
onSkip: (sid, reason) => printSessionListSkip(sid, reason)
|
|
@@ -6050,8 +6254,8 @@ async function doRunSessionShow(idInput, options, ctx) {
|
|
|
6050
6254
|
const paths = basouPaths15(repositoryRoot);
|
|
6051
6255
|
await assertWorkspaceInitialized10(paths.root);
|
|
6052
6256
|
const sessionId = await resolveSessionId3(paths, idInput);
|
|
6053
|
-
const sessionDir =
|
|
6054
|
-
const sessionYamlPath =
|
|
6257
|
+
const sessionDir = join12(paths.sessions, sessionId);
|
|
6258
|
+
const sessionYamlPath = join12(sessionDir, "session.yaml");
|
|
6055
6259
|
let session;
|
|
6056
6260
|
try {
|
|
6057
6261
|
const raw = await readYamlFile7(sessionYamlPath);
|
|
@@ -6226,6 +6430,11 @@ function eventVariantSummary(ev) {
|
|
|
6226
6430
|
return `approval=${ev.approval_id}`;
|
|
6227
6431
|
case "decision_recorded":
|
|
6228
6432
|
return ev.title;
|
|
6433
|
+
case "decision_voided": {
|
|
6434
|
+
const sup = ev.superseded_by !== void 0 ? ` superseded by ${ev.superseded_by}` : "";
|
|
6435
|
+
const reason = typeof ev.reason === "string" && ev.reason.length > 0 ? `: ${ev.reason}` : "";
|
|
6436
|
+
return `voided ${ev.decision_id}${reason}${sup}`;
|
|
6437
|
+
}
|
|
6229
6438
|
case "task_created":
|
|
6230
6439
|
return ev.title;
|
|
6231
6440
|
case "task_status_changed":
|
|
@@ -6789,7 +6998,7 @@ async function resolveRepositoryRootForStatus(cwd) {
|
|
|
6789
6998
|
|
|
6790
6999
|
// src/commands/task.ts
|
|
6791
7000
|
import { readFile as readFile5 } from "fs/promises";
|
|
6792
|
-
import { join as
|
|
7001
|
+
import { join as join13 } from "path";
|
|
6793
7002
|
import {
|
|
6794
7003
|
archiveTask,
|
|
6795
7004
|
assertBasouRootSafe as assertBasouRootSafe15,
|
|
@@ -6799,7 +7008,7 @@ import {
|
|
|
6799
7008
|
editTask,
|
|
6800
7009
|
enumerateArchivedTaskIds,
|
|
6801
7010
|
findErrorCode as findErrorCode14,
|
|
6802
|
-
loadSessionEntries as
|
|
7011
|
+
loadSessionEntries as loadSessionEntries3,
|
|
6803
7012
|
loadTaskEntries,
|
|
6804
7013
|
prefixedUlid as prefixedUlid5,
|
|
6805
7014
|
readManifest as readManifest10,
|
|
@@ -6808,7 +7017,7 @@ import {
|
|
|
6808
7017
|
reconcileAllTasks,
|
|
6809
7018
|
reconcileTask,
|
|
6810
7019
|
refreshTaskLinkedSessions,
|
|
6811
|
-
replayEvents as
|
|
7020
|
+
replayEvents as replayEvents3,
|
|
6812
7021
|
resolveRepositoryRoot as resolveRepositoryRoot12,
|
|
6813
7022
|
resolveSessionId as resolveSessionId4,
|
|
6814
7023
|
resolveTaskId as resolveTaskId2,
|
|
@@ -7112,13 +7321,13 @@ async function doRunTaskShow(idInput, options, ctx) {
|
|
|
7112
7321
|
await assertWorkspaceInitialized12(paths.root);
|
|
7113
7322
|
const taskId = await resolveTaskId2(paths, idInput, { includeArchived: true });
|
|
7114
7323
|
const { doc, archived } = await readTaskFileWithArchiveFallback(paths, taskId);
|
|
7115
|
-
const sessions = await
|
|
7324
|
+
const sessions = await loadSessionEntries3(paths, { now: /* @__PURE__ */ new Date() });
|
|
7116
7325
|
const events = [];
|
|
7117
7326
|
const linkedSessionIds = new Set(doc.task.task.linked_sessions);
|
|
7118
7327
|
for (const s of sessions) {
|
|
7119
|
-
const sessionDir =
|
|
7328
|
+
const sessionDir = join13(paths.sessions, s.sessionId);
|
|
7120
7329
|
try {
|
|
7121
|
-
for await (const ev of
|
|
7330
|
+
for await (const ev of replayEvents3(sessionDir, {
|
|
7122
7331
|
onWarning: (w) => printReplayWarning(w, s.sessionId)
|
|
7123
7332
|
})) {
|
|
7124
7333
|
if ((ev.type === "task_created" || ev.type === "task_status_changed" || ev.type === "task_reconciled" || ev.type === "task_linkage_refreshed" || ev.type === "task_deleted" || ev.type === "task_archived") && ev.task_id === taskId) {
|
|
@@ -8032,7 +8241,7 @@ import { InvalidArgumentError as InvalidArgumentError7 } from "commander";
|
|
|
8032
8241
|
// src/lib/portfolio-safety.ts
|
|
8033
8242
|
import { execFile } from "child_process";
|
|
8034
8243
|
import { lstat as lstat2, realpath as realpath2 } from "fs/promises";
|
|
8035
|
-
import { isAbsolute as isAbsolute7, join as
|
|
8244
|
+
import { isAbsolute as isAbsolute7, join as join14, relative as relative4, resolve as resolve10 } from "path";
|
|
8036
8245
|
import { promisify } from "util";
|
|
8037
8246
|
import { readManifest as readManifest11 } from "@basou/core";
|
|
8038
8247
|
var execFileAsync = promisify(execFile);
|
|
@@ -8056,7 +8265,7 @@ function isBasouPath(p) {
|
|
|
8056
8265
|
async function inspectRepo(repoPath) {
|
|
8057
8266
|
let hasEntry = false;
|
|
8058
8267
|
try {
|
|
8059
|
-
await lstat2(
|
|
8268
|
+
await lstat2(join14(repoPath, ".basou"));
|
|
8060
8269
|
hasEntry = true;
|
|
8061
8270
|
} catch (error) {
|
|
8062
8271
|
if (errorCode(error) !== "ENOENT") {
|
|
@@ -8157,14 +8366,14 @@ function formatSafetyReport(result) {
|
|
|
8157
8366
|
|
|
8158
8367
|
// src/lib/view-server.ts
|
|
8159
8368
|
import { createServer } from "http";
|
|
8160
|
-
import { join as
|
|
8369
|
+
import { join as join15 } from "path";
|
|
8161
8370
|
import {
|
|
8162
8371
|
computeWorkStats as computeWorkStats2,
|
|
8163
8372
|
enumerateApprovals as enumerateApprovals2,
|
|
8164
8373
|
findErrorCode as findErrorCode16,
|
|
8165
8374
|
isLazyExpired as isLazyExpired2,
|
|
8166
8375
|
loadApproval as loadApproval2,
|
|
8167
|
-
loadSessionEntries as
|
|
8376
|
+
loadSessionEntries as loadSessionEntries4,
|
|
8168
8377
|
loadTaskEntries as loadTaskEntries2,
|
|
8169
8378
|
readAllEvents as readAllEvents2,
|
|
8170
8379
|
readManifest as readManifest12,
|
|
@@ -8709,6 +8918,10 @@ var VIEW_HTML = `<!doctype html>
|
|
|
8709
8918
|
}
|
|
8710
8919
|
if (ev.type === 'file_changed') return ev.path + ' [' + ev.change_type + ']';
|
|
8711
8920
|
if (ev.type === 'decision_recorded') return ev.title || '';
|
|
8921
|
+
if (ev.type === 'decision_voided') {
|
|
8922
|
+
var vs = ev.superseded_by ? ' superseded by ' + ev.superseded_by : '';
|
|
8923
|
+
return 'voided ' + ev.decision_id + (ev.reason ? ': ' + ev.reason : '') + vs;
|
|
8924
|
+
}
|
|
8712
8925
|
return '';
|
|
8713
8926
|
}
|
|
8714
8927
|
|
|
@@ -9086,7 +9299,7 @@ async function overview(ws, nowProvider) {
|
|
|
9086
9299
|
};
|
|
9087
9300
|
}
|
|
9088
9301
|
async function sessionsList(ws, nowProvider) {
|
|
9089
|
-
const entries = await
|
|
9302
|
+
const entries = await loadSessionEntries4(ws.paths, { now: nowProvider() });
|
|
9090
9303
|
const sessions = entries.map((entry) => ({
|
|
9091
9304
|
sessionId: entry.sessionId,
|
|
9092
9305
|
label: entry.session.session.label ?? null,
|
|
@@ -9112,7 +9325,7 @@ async function sessionDetail(ws, sessionId) {
|
|
|
9112
9325
|
throw error;
|
|
9113
9326
|
}
|
|
9114
9327
|
try {
|
|
9115
|
-
const events = await readAllEvents2(
|
|
9328
|
+
const events = await readAllEvents2(join15(ws.paths.sessions, sessionId));
|
|
9116
9329
|
return { session, events };
|
|
9117
9330
|
} catch {
|
|
9118
9331
|
return { session, events: [], degraded: true };
|