@basou/cli 0.65.0 → 0.67.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 +434 -332
- package/dist/index.js.map +1 -1
- package/dist/program.js +433 -333
- package/dist/program.js.map +1 -1
- package/package.json +2 -2
package/dist/program.js
CHANGED
|
@@ -14,7 +14,7 @@ import {
|
|
|
14
14
|
assertBasouRootSafe,
|
|
15
15
|
assertLockStoreSafe,
|
|
16
16
|
assertSessionDirSafe,
|
|
17
|
-
basouPaths,
|
|
17
|
+
basouPaths as basouPaths2,
|
|
18
18
|
EVENT_SCHEMA_VERSION,
|
|
19
19
|
enumerateApprovals,
|
|
20
20
|
findErrorCode,
|
|
@@ -28,8 +28,7 @@ import {
|
|
|
28
28
|
prefixedUlid,
|
|
29
29
|
readSessionYaml,
|
|
30
30
|
readYamlFile,
|
|
31
|
-
replayEvents
|
|
32
|
-
resolveRepositoryRoot
|
|
31
|
+
replayEvents
|
|
33
32
|
} from "@basou/core";
|
|
34
33
|
|
|
35
34
|
// src/lib/error-render.ts
|
|
@@ -154,6 +153,150 @@ function printTaskSkip(taskId, reason) {
|
|
|
154
153
|
console.error(`Skipped ${shortTaskId(taskId)}: ${reason}`);
|
|
155
154
|
}
|
|
156
155
|
|
|
156
|
+
// src/lib/repo-root.ts
|
|
157
|
+
import { realpath, stat } from "fs/promises";
|
|
158
|
+
import { basename, resolve } from "path";
|
|
159
|
+
import {
|
|
160
|
+
basouPaths,
|
|
161
|
+
defaultPortfolioConfigPath,
|
|
162
|
+
loadPortfolioConfig,
|
|
163
|
+
readManifest,
|
|
164
|
+
resolveBasouRepositoryRoot,
|
|
165
|
+
resolveRepositoryRoot
|
|
166
|
+
} from "@basou/core";
|
|
167
|
+
async function resolveBasouRootForCommand(cwd, commandName, opts = {}) {
|
|
168
|
+
let root;
|
|
169
|
+
try {
|
|
170
|
+
root = await resolveBasouRepositoryRoot(cwd, {
|
|
171
|
+
onRedirect: ({ via, root: root2 }) => {
|
|
172
|
+
if (opts.quiet !== true) console.error(`Resolved workspace view to ${root2} (via ${via}).`);
|
|
173
|
+
}
|
|
174
|
+
});
|
|
175
|
+
} catch (error) {
|
|
176
|
+
if (error instanceof Error && error.message === "Not a git repository") {
|
|
177
|
+
throw new Error(
|
|
178
|
+
`Not a git repository. Run 'git init' first, then re-run 'basou ${commandName}'.`,
|
|
179
|
+
{ cause: error }
|
|
180
|
+
);
|
|
181
|
+
}
|
|
182
|
+
throw error;
|
|
183
|
+
}
|
|
184
|
+
if (!await hasBasouStore(root)) {
|
|
185
|
+
const master = await resolveMemberToMaster(
|
|
186
|
+
root,
|
|
187
|
+
opts.portfolioConfigPath ?? defaultPortfolioConfigPath()
|
|
188
|
+
);
|
|
189
|
+
if (master !== void 0) {
|
|
190
|
+
if (opts.quiet !== true) {
|
|
191
|
+
console.error(
|
|
192
|
+
`Resolved portfolio member to ${master.root} (via portfolio: ${master.label}).`
|
|
193
|
+
);
|
|
194
|
+
}
|
|
195
|
+
return master.root;
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
return root;
|
|
199
|
+
}
|
|
200
|
+
async function resolveRepositoryRootInPlace(cwd, commandName) {
|
|
201
|
+
try {
|
|
202
|
+
return await resolveRepositoryRoot(cwd);
|
|
203
|
+
} catch (error) {
|
|
204
|
+
if (!(error instanceof Error) || error.message !== "Not a git repository") throw error;
|
|
205
|
+
const links = await describeWorkspaceView(cwd);
|
|
206
|
+
if (links !== void 0) {
|
|
207
|
+
throw new Error(
|
|
208
|
+
`Not a git repository: this is a workspace view (it links ${links}). Run 'basou ${commandName}' inside a repository instead.`,
|
|
209
|
+
{ cause: error }
|
|
210
|
+
);
|
|
211
|
+
}
|
|
212
|
+
throw new Error(
|
|
213
|
+
`Not a git repository. Run 'git init' first, then re-run 'basou ${commandName}'.`,
|
|
214
|
+
{ cause: error }
|
|
215
|
+
);
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
async function describeWorkspaceView(dir) {
|
|
219
|
+
let via;
|
|
220
|
+
try {
|
|
221
|
+
await resolveBasouRepositoryRoot(dir, {
|
|
222
|
+
onRedirect: (info) => {
|
|
223
|
+
via = info.via;
|
|
224
|
+
}
|
|
225
|
+
});
|
|
226
|
+
} catch (error) {
|
|
227
|
+
if (error instanceof Error && error.message.startsWith("Ambiguous workspace view:")) {
|
|
228
|
+
return "more than one repository with a .basou store";
|
|
229
|
+
}
|
|
230
|
+
return void 0;
|
|
231
|
+
}
|
|
232
|
+
return via;
|
|
233
|
+
}
|
|
234
|
+
async function hasBasouStore(root) {
|
|
235
|
+
try {
|
|
236
|
+
return (await stat(basouPaths(root).root)).isDirectory();
|
|
237
|
+
} catch {
|
|
238
|
+
return false;
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
async function resolveMemberToMaster(repoRoot, configPath) {
|
|
242
|
+
let workspaces;
|
|
243
|
+
try {
|
|
244
|
+
workspaces = await loadPortfolioConfig(configPath);
|
|
245
|
+
} catch (error) {
|
|
246
|
+
if (!(error instanceof Error) || !error.message.startsWith("No portfolio config at")) {
|
|
247
|
+
const detail = error instanceof Error ? error.message : String(error);
|
|
248
|
+
console.error(`Ignoring ~/.basou/portfolio.yaml: ${detail}`);
|
|
249
|
+
}
|
|
250
|
+
return void 0;
|
|
251
|
+
}
|
|
252
|
+
const memberReal = await realpathOrNull(repoRoot);
|
|
253
|
+
if (memberReal === null) return void 0;
|
|
254
|
+
const claimants = /* @__PURE__ */ new Map();
|
|
255
|
+
const seenMaster = /* @__PURE__ */ new Set();
|
|
256
|
+
for (const ws of workspaces) {
|
|
257
|
+
const masterReal = await realpathOrNull(ws.path);
|
|
258
|
+
if (masterReal === null) continue;
|
|
259
|
+
if (masterReal === memberReal) continue;
|
|
260
|
+
if (seenMaster.has(masterReal)) continue;
|
|
261
|
+
seenMaster.add(masterReal);
|
|
262
|
+
let manifest;
|
|
263
|
+
try {
|
|
264
|
+
manifest = await readManifest(basouPaths(masterReal));
|
|
265
|
+
} catch (error) {
|
|
266
|
+
if (error instanceof Error && error.message !== "YAML file not found") {
|
|
267
|
+
console.error(
|
|
268
|
+
`Skipping portfolio workspace '${ws.label ?? basename(masterReal)}': could not read its manifest (${error.message}).`
|
|
269
|
+
);
|
|
270
|
+
}
|
|
271
|
+
continue;
|
|
272
|
+
}
|
|
273
|
+
const sourceRoots = manifest.import?.source_roots ?? ["."];
|
|
274
|
+
for (const sr of sourceRoots) {
|
|
275
|
+
const real = await realpathOrNull(resolve(masterReal, sr));
|
|
276
|
+
if (real !== null && real === memberReal) {
|
|
277
|
+
claimants.set(masterReal, { root: masterReal, label: ws.label ?? basename(masterReal) });
|
|
278
|
+
break;
|
|
279
|
+
}
|
|
280
|
+
}
|
|
281
|
+
}
|
|
282
|
+
const matched = [...claimants.values()];
|
|
283
|
+
if (matched.length === 1) return matched[0];
|
|
284
|
+
if (matched.length > 1) {
|
|
285
|
+
const names = matched.map((c) => c.label).join(", ");
|
|
286
|
+
throw new Error(
|
|
287
|
+
`This repository is declared as a source root by ${matched.length} portfolio workspaces (${names}). Disambiguate in ~/.basou/portfolio.yaml so only one aggregates it.`
|
|
288
|
+
);
|
|
289
|
+
}
|
|
290
|
+
return void 0;
|
|
291
|
+
}
|
|
292
|
+
async function realpathOrNull(p) {
|
|
293
|
+
try {
|
|
294
|
+
return await realpath(p);
|
|
295
|
+
} catch {
|
|
296
|
+
return null;
|
|
297
|
+
}
|
|
298
|
+
}
|
|
299
|
+
|
|
157
300
|
// src/commands/approval.ts
|
|
158
301
|
var APPR_PREFIX = "appr_";
|
|
159
302
|
var SHORT_ID_BASE_LEN = 6;
|
|
@@ -190,8 +333,8 @@ async function runApprovalList(options, ctx = {}) {
|
|
|
190
333
|
}
|
|
191
334
|
async function doRunApprovalList(options, ctx) {
|
|
192
335
|
const cwd = ctx.cwd ?? process.cwd();
|
|
193
|
-
const repositoryRoot = await
|
|
194
|
-
const paths =
|
|
336
|
+
const repositoryRoot = await resolveBasouRootForCommand(cwd, "approval list");
|
|
337
|
+
const paths = basouPaths2(repositoryRoot);
|
|
195
338
|
await assertWorkspaceInitialized(paths.root);
|
|
196
339
|
const ids = await enumerateApprovals(paths);
|
|
197
340
|
for (const entry of ids.unfollowed) {
|
|
@@ -263,8 +406,8 @@ async function runApprovalShow(idInput, options, ctx = {}) {
|
|
|
263
406
|
}
|
|
264
407
|
async function doRunApprovalShow(idInput, options, ctx) {
|
|
265
408
|
const cwd = ctx.cwd ?? process.cwd();
|
|
266
|
-
const repositoryRoot = await
|
|
267
|
-
const paths =
|
|
409
|
+
const repositoryRoot = await resolveBasouRootForCommand(cwd, "approval show");
|
|
410
|
+
const paths = basouPaths2(repositoryRoot);
|
|
268
411
|
await assertWorkspaceInitialized(paths.root);
|
|
269
412
|
const { id, unfollowed } = await resolveApprovalId(paths, idInput);
|
|
270
413
|
for (const entry2 of unfollowed) console.error(`Warning: ${describeUnfollowed(entry2)}`);
|
|
@@ -329,8 +472,8 @@ async function doRunApprovalResolve(idInput, options, ctx, decision) {
|
|
|
329
472
|
}
|
|
330
473
|
}
|
|
331
474
|
const cwd = ctx.cwd ?? process.cwd();
|
|
332
|
-
const repositoryRoot = await
|
|
333
|
-
const paths =
|
|
475
|
+
const repositoryRoot = await resolveBasouRootForCommand(cwd, `approval ${decision}`);
|
|
476
|
+
const paths = basouPaths2(repositoryRoot);
|
|
334
477
|
await assertWorkspaceInitialized(paths.root);
|
|
335
478
|
const { id, location } = await resolveApprovalId(paths, idInput);
|
|
336
479
|
if (location === "resolved") {
|
|
@@ -651,19 +794,6 @@ function truncate(value, maxLength) {
|
|
|
651
794
|
if (value.length <= maxLength) return value;
|
|
652
795
|
return `${value.slice(0, maxLength - 3)}...`;
|
|
653
796
|
}
|
|
654
|
-
async function resolveRepositoryRootForApproval(cwd, subcmd) {
|
|
655
|
-
try {
|
|
656
|
-
return await resolveRepositoryRoot(cwd);
|
|
657
|
-
} catch (error) {
|
|
658
|
-
if (error instanceof Error && error.message === "Not a git repository") {
|
|
659
|
-
throw new Error(
|
|
660
|
-
`Not a git repository. Run 'git init' first, then re-run 'basou approval ${subcmd}'.`,
|
|
661
|
-
{ cause: error }
|
|
662
|
-
);
|
|
663
|
-
}
|
|
664
|
-
throw error;
|
|
665
|
-
}
|
|
666
|
-
}
|
|
667
797
|
async function assertWorkspaceInitialized(basouRoot) {
|
|
668
798
|
try {
|
|
669
799
|
await assertBasouRootSafe(basouRoot);
|
|
@@ -710,6 +840,7 @@ import {
|
|
|
710
840
|
displayPath as displayPath3,
|
|
711
841
|
findErrorCode as findErrorCode3,
|
|
712
842
|
measureBoard,
|
|
843
|
+
measureBoardLive,
|
|
713
844
|
NO_PREVIOUS_RECORDS,
|
|
714
845
|
parseBoardDeclaration,
|
|
715
846
|
parseRecordInput,
|
|
@@ -733,16 +864,16 @@ import {
|
|
|
733
864
|
|
|
734
865
|
// src/commands/import.ts
|
|
735
866
|
import { createReadStream } from "fs";
|
|
736
|
-
import { readdir, readFile, rm, stat } from "fs/promises";
|
|
867
|
+
import { readdir, readFile, rm, stat as stat2 } from "fs/promises";
|
|
737
868
|
import { homedir } from "os";
|
|
738
|
-
import { basename, dirname as dirname2, join as join2, resolve } from "path";
|
|
869
|
+
import { basename as basename2, dirname as dirname2, join as join2, resolve as resolve2 } from "path";
|
|
739
870
|
import { createInterface } from "readline";
|
|
740
871
|
import {
|
|
741
872
|
AGENT_INFRA_DIRS,
|
|
742
873
|
assertBasouRootSafe as assertBasouRootSafe2,
|
|
743
874
|
assertLockStoreSafe as assertLockStoreSafe2,
|
|
744
875
|
assertSessionStoreSafe,
|
|
745
|
-
basouPaths as
|
|
876
|
+
basouPaths as basouPaths3,
|
|
746
877
|
CLAUDE_IMPORT_SOURCE,
|
|
747
878
|
CODEX_IMPORT_SOURCE,
|
|
748
879
|
classifyFilesBySourceRoot,
|
|
@@ -753,11 +884,10 @@ import {
|
|
|
753
884
|
findErrorCode as findErrorCode2,
|
|
754
885
|
importSessionFromJson,
|
|
755
886
|
observedFilesOf,
|
|
756
|
-
readManifest,
|
|
887
|
+
readManifest as readManifest2,
|
|
757
888
|
readSessionObservation,
|
|
758
889
|
readSessionYaml as readSessionYaml2,
|
|
759
890
|
reimportPreservingId,
|
|
760
|
-
resolveRepositoryRoot as resolveRepositoryRoot2,
|
|
761
891
|
SESSION_IMPORT_SCHEMA_VERSION,
|
|
762
892
|
SessionImportPayloadSchema
|
|
763
893
|
} from "@basou/core";
|
|
@@ -827,10 +957,10 @@ function resolveSourceRoots(args) {
|
|
|
827
957
|
const { projectFlags, manifest, repoRoot, cwd } = args;
|
|
828
958
|
let resolved;
|
|
829
959
|
if (projectFlags.length > 0) {
|
|
830
|
-
resolved = projectFlags.map((p) =>
|
|
960
|
+
resolved = projectFlags.map((p) => resolve2(cwd, p));
|
|
831
961
|
} else {
|
|
832
962
|
const roots = manifest.import?.source_roots;
|
|
833
|
-
resolved = roots !== void 0 && roots.length > 0 ? roots.map((r) =>
|
|
963
|
+
resolved = roots !== void 0 && roots.length > 0 ? roots.map((r) => resolve2(repoRoot, r)) : [repoRoot];
|
|
834
964
|
}
|
|
835
965
|
return [...new Set(resolved)];
|
|
836
966
|
}
|
|
@@ -848,7 +978,7 @@ async function doRunImportClaudeCode(options, ctx) {
|
|
|
848
978
|
const files = await selectTranscriptFiles(projectsRoot, projectPaths, options);
|
|
849
979
|
const projectSet = new Set(projectPaths);
|
|
850
980
|
const candidates = files.map((file) => {
|
|
851
|
-
const externalId =
|
|
981
|
+
const externalId = basename2(file, ".jsonl");
|
|
852
982
|
return {
|
|
853
983
|
externalId,
|
|
854
984
|
sourcePath: file,
|
|
@@ -923,10 +1053,10 @@ function assertSelector(options) {
|
|
|
923
1053
|
}
|
|
924
1054
|
async function resolveImportTarget(ctx) {
|
|
925
1055
|
const cwd = ctx.cwd ?? process.cwd();
|
|
926
|
-
const repositoryRoot = await
|
|
927
|
-
const paths =
|
|
1056
|
+
const repositoryRoot = await resolveBasouRootForCommand(cwd, "import");
|
|
1057
|
+
const paths = basouPaths3(repositoryRoot);
|
|
928
1058
|
await assertWorkspaceInitialized2(paths.root);
|
|
929
|
-
const manifest = await
|
|
1059
|
+
const manifest = await readManifest2(paths);
|
|
930
1060
|
return { repositoryRoot, paths, manifest };
|
|
931
1061
|
}
|
|
932
1062
|
async function importDerivedSessions(paths, manifest, options, sourceKind, candidates, projectPaths, boundaryDeclared) {
|
|
@@ -1173,7 +1303,7 @@ async function selectTranscriptFiles(projectsRoot, projectPaths, options) {
|
|
|
1173
1303
|
}
|
|
1174
1304
|
async function pathExists(file) {
|
|
1175
1305
|
try {
|
|
1176
|
-
await
|
|
1306
|
+
await stat2(file);
|
|
1177
1307
|
return true;
|
|
1178
1308
|
} catch (error) {
|
|
1179
1309
|
if (findErrorCode2(error, "ENOENT")) return false;
|
|
@@ -1182,7 +1312,7 @@ async function pathExists(file) {
|
|
|
1182
1312
|
}
|
|
1183
1313
|
async function statSize(file) {
|
|
1184
1314
|
try {
|
|
1185
|
-
return (await
|
|
1315
|
+
return (await stat2(file)).size;
|
|
1186
1316
|
} catch (error) {
|
|
1187
1317
|
if (findErrorCode2(error, "ENOENT")) return void 0;
|
|
1188
1318
|
throw error;
|
|
@@ -1374,18 +1504,6 @@ function shortId2(id) {
|
|
|
1374
1504
|
}
|
|
1375
1505
|
return id.slice(0, SHORT_ID_LEN2);
|
|
1376
1506
|
}
|
|
1377
|
-
async function resolveRepositoryRootForImport(cwd) {
|
|
1378
|
-
try {
|
|
1379
|
-
return await resolveRepositoryRoot2(cwd);
|
|
1380
|
-
} catch (error) {
|
|
1381
|
-
if (error instanceof Error && error.message === "Not a git repository") {
|
|
1382
|
-
throw new Error("Not a git repository. Run 'git init' first, then re-run 'basou import'.", {
|
|
1383
|
-
cause: error
|
|
1384
|
-
});
|
|
1385
|
-
}
|
|
1386
|
-
throw error;
|
|
1387
|
-
}
|
|
1388
|
-
}
|
|
1389
1507
|
async function assertWorkspaceInitialized2(basouRoot) {
|
|
1390
1508
|
try {
|
|
1391
1509
|
await assertBasouRootSafe2(basouRoot);
|
|
@@ -1398,7 +1516,13 @@ async function assertWorkspaceInitialized2(basouRoot) {
|
|
|
1398
1516
|
}
|
|
1399
1517
|
|
|
1400
1518
|
// src/lib/provenance-actions.ts
|
|
1401
|
-
|
|
1519
|
+
var capturing = Promise.resolve();
|
|
1520
|
+
function captureImportJson(fn) {
|
|
1521
|
+
const turn = capturing.then(() => captureNow(fn));
|
|
1522
|
+
capturing = turn.catch(() => void 0);
|
|
1523
|
+
return turn;
|
|
1524
|
+
}
|
|
1525
|
+
async function captureNow(fn) {
|
|
1402
1526
|
const stdout = [];
|
|
1403
1527
|
const originalLog = console.log;
|
|
1404
1528
|
const originalError = console.error;
|
|
@@ -1578,111 +1702,6 @@ async function probeStaleness(args) {
|
|
|
1578
1702
|
}
|
|
1579
1703
|
}
|
|
1580
1704
|
|
|
1581
|
-
// src/lib/repo-root.ts
|
|
1582
|
-
import { realpath, stat as stat2 } from "fs/promises";
|
|
1583
|
-
import { basename as basename2, resolve as resolve2 } from "path";
|
|
1584
|
-
import {
|
|
1585
|
-
basouPaths as basouPaths3,
|
|
1586
|
-
defaultPortfolioConfigPath,
|
|
1587
|
-
loadPortfolioConfig,
|
|
1588
|
-
readManifest as readManifest2,
|
|
1589
|
-
resolveBasouRepositoryRoot
|
|
1590
|
-
} from "@basou/core";
|
|
1591
|
-
async function resolveBasouRootForCommand(cwd, commandName, opts = {}) {
|
|
1592
|
-
let root;
|
|
1593
|
-
try {
|
|
1594
|
-
root = await resolveBasouRepositoryRoot(cwd, {
|
|
1595
|
-
onRedirect: ({ via, root: root2 }) => console.error(`Resolved workspace view to ${root2} (via ${via}).`)
|
|
1596
|
-
});
|
|
1597
|
-
} catch (error) {
|
|
1598
|
-
if (error instanceof Error && error.message === "Not a git repository") {
|
|
1599
|
-
throw new Error(
|
|
1600
|
-
`Not a git repository. Run 'git init' first, then re-run 'basou ${commandName}'.`,
|
|
1601
|
-
{ cause: error }
|
|
1602
|
-
);
|
|
1603
|
-
}
|
|
1604
|
-
throw error;
|
|
1605
|
-
}
|
|
1606
|
-
if (!await hasBasouStore(root)) {
|
|
1607
|
-
const master = await resolveMemberToMaster(
|
|
1608
|
-
root,
|
|
1609
|
-
opts.portfolioConfigPath ?? defaultPortfolioConfigPath()
|
|
1610
|
-
);
|
|
1611
|
-
if (master !== void 0) {
|
|
1612
|
-
console.error(
|
|
1613
|
-
`Resolved portfolio member to ${master.root} (via portfolio: ${master.label}).`
|
|
1614
|
-
);
|
|
1615
|
-
return master.root;
|
|
1616
|
-
}
|
|
1617
|
-
}
|
|
1618
|
-
return root;
|
|
1619
|
-
}
|
|
1620
|
-
async function hasBasouStore(root) {
|
|
1621
|
-
try {
|
|
1622
|
-
return (await stat2(basouPaths3(root).root)).isDirectory();
|
|
1623
|
-
} catch {
|
|
1624
|
-
return false;
|
|
1625
|
-
}
|
|
1626
|
-
}
|
|
1627
|
-
async function resolveMemberToMaster(repoRoot, configPath) {
|
|
1628
|
-
let workspaces;
|
|
1629
|
-
try {
|
|
1630
|
-
workspaces = await loadPortfolioConfig(configPath);
|
|
1631
|
-
} catch (error) {
|
|
1632
|
-
if (!(error instanceof Error) || !error.message.startsWith("No portfolio config at")) {
|
|
1633
|
-
const detail = error instanceof Error ? error.message : String(error);
|
|
1634
|
-
console.error(`Ignoring ~/.basou/portfolio.yaml: ${detail}`);
|
|
1635
|
-
}
|
|
1636
|
-
return void 0;
|
|
1637
|
-
}
|
|
1638
|
-
const memberReal = await realpathOrNull(repoRoot);
|
|
1639
|
-
if (memberReal === null) return void 0;
|
|
1640
|
-
const claimants = /* @__PURE__ */ new Map();
|
|
1641
|
-
const seenMaster = /* @__PURE__ */ new Set();
|
|
1642
|
-
for (const ws of workspaces) {
|
|
1643
|
-
const masterReal = await realpathOrNull(ws.path);
|
|
1644
|
-
if (masterReal === null) continue;
|
|
1645
|
-
if (masterReal === memberReal) continue;
|
|
1646
|
-
if (seenMaster.has(masterReal)) continue;
|
|
1647
|
-
seenMaster.add(masterReal);
|
|
1648
|
-
let manifest;
|
|
1649
|
-
try {
|
|
1650
|
-
manifest = await readManifest2(basouPaths3(masterReal));
|
|
1651
|
-
} catch (error) {
|
|
1652
|
-
if (error instanceof Error && error.message !== "YAML file not found") {
|
|
1653
|
-
console.error(
|
|
1654
|
-
`Skipping portfolio workspace '${ws.label ?? basename2(masterReal)}': could not read its manifest (${error.message}).`
|
|
1655
|
-
);
|
|
1656
|
-
}
|
|
1657
|
-
continue;
|
|
1658
|
-
}
|
|
1659
|
-
const sourceRoots = manifest.import?.source_roots ?? ["."];
|
|
1660
|
-
for (const sr of sourceRoots) {
|
|
1661
|
-
const real = await realpathOrNull(resolve2(masterReal, sr));
|
|
1662
|
-
if (real !== null && real === memberReal) {
|
|
1663
|
-
claimants.set(masterReal, { root: masterReal, label: ws.label ?? basename2(masterReal) });
|
|
1664
|
-
break;
|
|
1665
|
-
}
|
|
1666
|
-
}
|
|
1667
|
-
}
|
|
1668
|
-
const matched = [...claimants.values()];
|
|
1669
|
-
if (matched.length === 1) return matched[0];
|
|
1670
|
-
if (matched.length > 1) {
|
|
1671
|
-
const names = matched.map((c) => c.label).join(", ");
|
|
1672
|
-
throw new Error(
|
|
1673
|
-
`This repository is declared as a source root by ${matched.length} portfolio workspaces (${names}). Disambiguate in ~/.basou/portfolio.yaml so only one aggregates it.`
|
|
1674
|
-
);
|
|
1675
|
-
}
|
|
1676
|
-
return void 0;
|
|
1677
|
-
}
|
|
1678
|
-
async function realpathOrNull(p) {
|
|
1679
|
-
try {
|
|
1680
|
-
return await realpath(p);
|
|
1681
|
-
} catch {
|
|
1682
|
-
return null;
|
|
1683
|
-
}
|
|
1684
|
-
}
|
|
1685
|
-
|
|
1686
1705
|
// src/commands/board.ts
|
|
1687
1706
|
var DEFAULT_BOARD_PATH = "board/board.yaml";
|
|
1688
1707
|
function registerBoardCommand(program) {
|
|
@@ -2012,6 +2031,41 @@ async function measureLoaded(loaded, ctx, model, previous) {
|
|
|
2012
2031
|
previous
|
|
2013
2032
|
});
|
|
2014
2033
|
}
|
|
2034
|
+
async function measureLiveBoard(root, ctx) {
|
|
2035
|
+
const paths = basouPaths4(root);
|
|
2036
|
+
let repos = ["."];
|
|
2037
|
+
let manifestUnread = false;
|
|
2038
|
+
try {
|
|
2039
|
+
const declared = ((await readManifest3(paths)).repos ?? []).map((repo) => repo.path);
|
|
2040
|
+
if (declared.length > 0) repos = declared;
|
|
2041
|
+
} catch {
|
|
2042
|
+
manifestUnread = true;
|
|
2043
|
+
}
|
|
2044
|
+
const now = ctx.nowProvider?.() ?? /* @__PURE__ */ new Date();
|
|
2045
|
+
const probeCtx = { cwd: root };
|
|
2046
|
+
if (ctx.claudeProjectsDir !== void 0) probeCtx.claudeProjectsDir = ctx.claudeProjectsDir;
|
|
2047
|
+
if (ctx.codexSessionsDir !== void 0) probeCtx.codexSessionsDir = ctx.codexSessionsDir;
|
|
2048
|
+
const measured = await measureBoardLive({
|
|
2049
|
+
root,
|
|
2050
|
+
repos,
|
|
2051
|
+
paths,
|
|
2052
|
+
now,
|
|
2053
|
+
measuredWith: { basou: BASOU_CLI_VERSION, build: BASOU_BUILD?.commit ?? null },
|
|
2054
|
+
probeImports: () => probeStaleness({ ctx: probeCtx, paths, nowIso: now.toISOString() })
|
|
2055
|
+
});
|
|
2056
|
+
if (!manifestUnread) return measured;
|
|
2057
|
+
return {
|
|
2058
|
+
...measured,
|
|
2059
|
+
complete: false,
|
|
2060
|
+
not_found: [
|
|
2061
|
+
{
|
|
2062
|
+
at: "repos",
|
|
2063
|
+
reason: "the manifest could not be read, so only the workspace's own repo was measured"
|
|
2064
|
+
},
|
|
2065
|
+
...measured.not_found
|
|
2066
|
+
]
|
|
2067
|
+
};
|
|
2068
|
+
}
|
|
2015
2069
|
function boardPath(options, cwd, root, manifest) {
|
|
2016
2070
|
if (options.board !== void 0)
|
|
2017
2071
|
return { path: resolve3(cwd, options.board), shown: options.board, given: true };
|
|
@@ -2479,7 +2533,6 @@ import {
|
|
|
2479
2533
|
prefixedUlid as prefixedUlid2,
|
|
2480
2534
|
readManifest as readManifest4,
|
|
2481
2535
|
replayEvents as replayEvents2,
|
|
2482
|
-
resolveRepositoryRoot as resolveRepositoryRoot3,
|
|
2483
2536
|
resolveSessionId,
|
|
2484
2537
|
sanitizePath
|
|
2485
2538
|
} from "@basou/core";
|
|
@@ -2762,7 +2815,7 @@ async function warnLinkedFilesOutsideRoots(input) {
|
|
|
2762
2815
|
}
|
|
2763
2816
|
async function doRunDecisionRecord(options, ctx) {
|
|
2764
2817
|
const cwd = ctx.cwd ?? process.cwd();
|
|
2765
|
-
const repositoryRoot = await
|
|
2818
|
+
const repositoryRoot = await resolveBasouRootForCommand(cwd, "decision record");
|
|
2766
2819
|
const paths = basouPaths6(repositoryRoot);
|
|
2767
2820
|
await assertWorkspaceInitialized4(paths.root);
|
|
2768
2821
|
const now = ctx.nowProvider !== void 0 ? ctx.nowProvider() : /* @__PURE__ */ new Date();
|
|
@@ -3409,19 +3462,6 @@ function printDecisionResult(options, result) {
|
|
|
3409
3462
|
);
|
|
3410
3463
|
}
|
|
3411
3464
|
}
|
|
3412
|
-
async function resolveRepositoryRootForDecision(cwd) {
|
|
3413
|
-
try {
|
|
3414
|
-
return await resolveRepositoryRoot3(cwd);
|
|
3415
|
-
} catch (error) {
|
|
3416
|
-
if (error instanceof Error && error.message === "Not a git repository") {
|
|
3417
|
-
throw new Error(
|
|
3418
|
-
"Not a git repository. Run 'git init' first, then re-run 'basou decision record'.",
|
|
3419
|
-
{ cause: error }
|
|
3420
|
-
);
|
|
3421
|
-
}
|
|
3422
|
-
throw error;
|
|
3423
|
-
}
|
|
3424
|
-
}
|
|
3425
3465
|
async function assertWorkspaceInitialized4(basouRoot) {
|
|
3426
3466
|
try {
|
|
3427
3467
|
await assertBasouRootSafe4(basouRoot);
|
|
@@ -3441,7 +3481,6 @@ import {
|
|
|
3441
3481
|
readMarkdownFile as readMarkdownFile3,
|
|
3442
3482
|
renderDecisions as renderDecisions2,
|
|
3443
3483
|
renderWithMarkers as renderWithMarkers2,
|
|
3444
|
-
resolveRepositoryRoot as resolveRepositoryRoot4,
|
|
3445
3484
|
writeMarkdownFile as writeMarkdownFile2
|
|
3446
3485
|
} from "@basou/core";
|
|
3447
3486
|
function registerDecisionsCommand(program) {
|
|
@@ -3461,7 +3500,7 @@ async function runDecisionsGenerate(options, ctx = {}) {
|
|
|
3461
3500
|
async function doRunDecisionsGenerate(options, ctx) {
|
|
3462
3501
|
void options;
|
|
3463
3502
|
const cwd = ctx.cwd ?? process.cwd();
|
|
3464
|
-
const repositoryRoot = await
|
|
3503
|
+
const repositoryRoot = await resolveBasouRootForCommand(cwd, "decisions generate");
|
|
3465
3504
|
const paths = basouPaths7(repositoryRoot);
|
|
3466
3505
|
await assertWorkspaceInitialized5(paths.root);
|
|
3467
3506
|
const nowIso = (ctx.nowProvider?.() ?? /* @__PURE__ */ new Date()).toISOString();
|
|
@@ -3476,19 +3515,6 @@ async function doRunDecisionsGenerate(options, ctx) {
|
|
|
3476
3515
|
await writeMarkdownFile2(paths.files.decisions, finalBody);
|
|
3477
3516
|
console.log(`Generated .basou/decisions.md (decisions: ${result.decisionCount})`);
|
|
3478
3517
|
}
|
|
3479
|
-
async function resolveRepositoryRootForDecisions(cwd) {
|
|
3480
|
-
try {
|
|
3481
|
-
return await resolveRepositoryRoot4(cwd);
|
|
3482
|
-
} catch (error) {
|
|
3483
|
-
if (error instanceof Error && error.message === "Not a git repository") {
|
|
3484
|
-
throw new Error(
|
|
3485
|
-
"Not a git repository. Run 'git init' first, then re-run 'basou decisions generate'.",
|
|
3486
|
-
{ cause: error }
|
|
3487
|
-
);
|
|
3488
|
-
}
|
|
3489
|
-
throw error;
|
|
3490
|
-
}
|
|
3491
|
-
}
|
|
3492
3518
|
async function assertWorkspaceInitialized5(basouRoot) {
|
|
3493
3519
|
try {
|
|
3494
3520
|
await assertBasouRootSafe5(basouRoot);
|
|
@@ -3519,7 +3545,6 @@ import {
|
|
|
3519
3545
|
prefixedUlid as prefixedUlid3,
|
|
3520
3546
|
readManifest as readManifest5,
|
|
3521
3547
|
readYamlFile as readYamlFile2,
|
|
3522
|
-
resolveRepositoryRoot as resolveRepositoryRoot5,
|
|
3523
3548
|
SESSION_SCHEMA_VERSION,
|
|
3524
3549
|
SessionSchema,
|
|
3525
3550
|
sanitizeWorkingDirectory,
|
|
@@ -3542,7 +3567,7 @@ async function runExec(command, args, options, ctx = {}) {
|
|
|
3542
3567
|
const now = ctx.now ?? (() => /* @__PURE__ */ new Date());
|
|
3543
3568
|
const cwd = options.cwd ?? process.cwd();
|
|
3544
3569
|
const timeout_ms = options.timeout !== void 0 ? parseDuration(options.timeout) : void 0;
|
|
3545
|
-
const repoRoot = await
|
|
3570
|
+
const repoRoot = await resolveRepositoryRootInPlace(cwd, "exec");
|
|
3546
3571
|
const paths = basouPaths8(repoRoot);
|
|
3547
3572
|
await assertBasouRootSafe6(paths.root);
|
|
3548
3573
|
const manifest = await readManifest5(paths);
|
|
@@ -3840,18 +3865,6 @@ async function finalizeSessionAsFailed(paths, sessionDir, sessionId, appendEvent
|
|
|
3840
3865
|
s.session.invocation.exit_code = null;
|
|
3841
3866
|
});
|
|
3842
3867
|
}
|
|
3843
|
-
async function resolveRepositoryRootForExec(cwd) {
|
|
3844
|
-
try {
|
|
3845
|
-
return await resolveRepositoryRoot5(cwd);
|
|
3846
|
-
} catch (error) {
|
|
3847
|
-
if (error instanceof Error && error.message === "Not a git repository") {
|
|
3848
|
-
throw new Error("Not a git repository. Run 'git init' first, then re-run 'basou exec'.", {
|
|
3849
|
-
cause: error
|
|
3850
|
-
});
|
|
3851
|
-
}
|
|
3852
|
-
throw error;
|
|
3853
|
-
}
|
|
3854
|
-
}
|
|
3855
3868
|
|
|
3856
3869
|
// src/commands/handoff.ts
|
|
3857
3870
|
import {
|
|
@@ -5439,7 +5452,6 @@ import {
|
|
|
5439
5452
|
appendBasouGitignore,
|
|
5440
5453
|
createManifest,
|
|
5441
5454
|
ensureBasouDirectory,
|
|
5442
|
-
resolveRepositoryRoot as resolveRepositoryRoot6,
|
|
5443
5455
|
writeManifest
|
|
5444
5456
|
} from "@basou/core";
|
|
5445
5457
|
function collectValue(value, previous) {
|
|
@@ -5471,7 +5483,7 @@ async function runInit(options, ctx = {}) {
|
|
|
5471
5483
|
}
|
|
5472
5484
|
async function doRunInit(options, ctx) {
|
|
5473
5485
|
const cwd = ctx.cwd ?? process.cwd();
|
|
5474
|
-
const repositoryRoot = await
|
|
5486
|
+
const repositoryRoot = await resolveRepositoryRootInPlace(cwd, "init");
|
|
5475
5487
|
const workspaceName = options.name ?? basename6(repositoryRoot);
|
|
5476
5488
|
if (options.repoUrl !== void 0) {
|
|
5477
5489
|
console.error(
|
|
@@ -5507,18 +5519,6 @@ function renderGitignoreWarning(error, verbose) {
|
|
|
5507
5519
|
if (label !== void 0) console.error(`Caused by: ${label}`);
|
|
5508
5520
|
}
|
|
5509
5521
|
}
|
|
5510
|
-
async function resolveRepositoryRootForInit(cwd) {
|
|
5511
|
-
try {
|
|
5512
|
-
return await resolveRepositoryRoot6(cwd);
|
|
5513
|
-
} catch (error) {
|
|
5514
|
-
if (error instanceof Error && error.message === "Not a git repository") {
|
|
5515
|
-
throw new Error("Not a git repository. Run 'git init' first, then re-run 'basou init'.", {
|
|
5516
|
-
cause: error
|
|
5517
|
-
});
|
|
5518
|
-
}
|
|
5519
|
-
throw error;
|
|
5520
|
-
}
|
|
5521
|
-
}
|
|
5522
5522
|
|
|
5523
5523
|
// src/commands/note.ts
|
|
5524
5524
|
import { readFile as readFile5 } from "fs/promises";
|
|
@@ -5918,7 +5918,6 @@ import {
|
|
|
5918
5918
|
renderAnchorStarter,
|
|
5919
5919
|
renderViewPresetBlock,
|
|
5920
5920
|
renderWithMarkers as renderWithMarkers4,
|
|
5921
|
-
resolveRepositoryRoot as resolveRepositoryRoot7,
|
|
5922
5921
|
safeSimpleGit,
|
|
5923
5922
|
seedMarkers,
|
|
5924
5923
|
summarizePresetPlan,
|
|
@@ -8464,19 +8463,6 @@ async function runProjectNew(repos, options, ctx = {}) {
|
|
|
8464
8463
|
process.exitCode = 1;
|
|
8465
8464
|
}
|
|
8466
8465
|
}
|
|
8467
|
-
async function resolveRepositoryRootForNew(cwd) {
|
|
8468
|
-
try {
|
|
8469
|
-
return await resolveRepositoryRoot7(cwd);
|
|
8470
|
-
} catch (error) {
|
|
8471
|
-
if (error instanceof Error && error.message === "Not a git repository") {
|
|
8472
|
-
throw new Error(
|
|
8473
|
-
"Not a git repository. Run 'git init' first, then re-run 'basou project new'.",
|
|
8474
|
-
{ cause: error }
|
|
8475
|
-
);
|
|
8476
|
-
}
|
|
8477
|
-
throw error;
|
|
8478
|
-
}
|
|
8479
|
-
}
|
|
8480
8466
|
function validateProjectName(name) {
|
|
8481
8467
|
if (!/^[A-Za-z0-9][A-Za-z0-9._-]*$/.test(name)) {
|
|
8482
8468
|
throw new Error(
|
|
@@ -8487,7 +8473,7 @@ function validateProjectName(name) {
|
|
|
8487
8473
|
}
|
|
8488
8474
|
async function doRunProjectNew(repos, options, ctx) {
|
|
8489
8475
|
const cwd = ctx.cwd ?? process.cwd();
|
|
8490
|
-
const repositoryRoot = await
|
|
8476
|
+
const repositoryRoot = await resolveRepositoryRootInPlace(cwd, "project new");
|
|
8491
8477
|
const workspaceName = basename7(repositoryRoot);
|
|
8492
8478
|
const productName = options.projectName !== void 0 ? validateProjectName(options.projectName) : void 0;
|
|
8493
8479
|
const viewStem = productName ?? workspaceName;
|
|
@@ -9694,7 +9680,6 @@ import {
|
|
|
9694
9680
|
basouPaths as basouPaths15,
|
|
9695
9681
|
findErrorCode as findErrorCode11,
|
|
9696
9682
|
renderReport,
|
|
9697
|
-
resolveRepositoryRoot as resolveRepositoryRoot8,
|
|
9698
9683
|
writeMarkdownFile as writeMarkdownFile6
|
|
9699
9684
|
} from "@basou/core";
|
|
9700
9685
|
function registerReportCommand(program) {
|
|
@@ -9715,7 +9700,7 @@ async function runReportGenerate(options, ctx = {}) {
|
|
|
9715
9700
|
}
|
|
9716
9701
|
async function doRunReportGenerate(options, ctx) {
|
|
9717
9702
|
const cwd = ctx.cwd ?? process.cwd();
|
|
9718
|
-
const repositoryRoot = await
|
|
9703
|
+
const repositoryRoot = await resolveBasouRootForCommand(cwd, "report generate");
|
|
9719
9704
|
const paths = basouPaths15(repositoryRoot);
|
|
9720
9705
|
await assertWorkspaceInitialized10(paths.root);
|
|
9721
9706
|
const nowIso = (ctx.nowProvider?.() ?? /* @__PURE__ */ new Date()).toISOString();
|
|
@@ -9741,19 +9726,6 @@ async function doRunReportGenerate(options, ctx) {
|
|
|
9741
9726
|
console.log(result.body);
|
|
9742
9727
|
}
|
|
9743
9728
|
}
|
|
9744
|
-
async function resolveRepositoryRootForReport(cwd) {
|
|
9745
|
-
try {
|
|
9746
|
-
return await resolveRepositoryRoot8(cwd);
|
|
9747
|
-
} catch (error) {
|
|
9748
|
-
if (error instanceof Error && error.message === "Not a git repository") {
|
|
9749
|
-
throw new Error(
|
|
9750
|
-
"Not a git repository. Run 'git init' first, then re-run 'basou report generate'.",
|
|
9751
|
-
{ cause: error }
|
|
9752
|
-
);
|
|
9753
|
-
}
|
|
9754
|
-
throw error;
|
|
9755
|
-
}
|
|
9756
|
-
}
|
|
9757
9729
|
async function assertWorkspaceInitialized10(basouRoot) {
|
|
9758
9730
|
try {
|
|
9759
9731
|
await assertBasouRootSafe11(basouRoot);
|
|
@@ -10226,7 +10198,6 @@ import {
|
|
|
10226
10198
|
readYamlFile as readYamlFile5,
|
|
10227
10199
|
resolveClaudeCodeCommand,
|
|
10228
10200
|
resolveCodexCommand,
|
|
10229
|
-
resolveRepositoryRoot as resolveRepositoryRoot9,
|
|
10230
10201
|
SESSION_SCHEMA_VERSION as SESSION_SCHEMA_VERSION2,
|
|
10231
10202
|
SessionSchema as SessionSchema2,
|
|
10232
10203
|
sanitizeRelatedFiles,
|
|
@@ -10281,7 +10252,7 @@ async function runTrackedTool(args, options, ctx, adapter) {
|
|
|
10281
10252
|
const { command } = await adapter.resolveCommand();
|
|
10282
10253
|
const childArgs = adapter.transformArgs ? adapter.transformArgs(args) : args;
|
|
10283
10254
|
const cwd = options.cwd ?? process.cwd();
|
|
10284
|
-
const repoRoot = await
|
|
10255
|
+
const repoRoot = await resolveRepositoryRootInPlace(cwd, "run");
|
|
10285
10256
|
const paths = basouPaths18(repoRoot);
|
|
10286
10257
|
await assertBasouRootSafe13(paths.root);
|
|
10287
10258
|
const manifest = await readManifest11(paths);
|
|
@@ -10650,18 +10621,6 @@ async function finalizeSessionAsFailed2(paths, sessionDir, sessionId, appendEven
|
|
|
10650
10621
|
s.session.invocation.exit_code = null;
|
|
10651
10622
|
});
|
|
10652
10623
|
}
|
|
10653
|
-
async function resolveRepositoryRootForRun(cwd) {
|
|
10654
|
-
try {
|
|
10655
|
-
return await resolveRepositoryRoot9(cwd);
|
|
10656
|
-
} catch (error) {
|
|
10657
|
-
if (error instanceof Error && error.message === "Not a git repository") {
|
|
10658
|
-
throw new Error("Not a git repository. Run 'git init' first, then re-run 'basou run'.", {
|
|
10659
|
-
cause: error
|
|
10660
|
-
});
|
|
10661
|
-
}
|
|
10662
|
-
throw error;
|
|
10663
|
-
}
|
|
10664
|
-
}
|
|
10665
10624
|
async function noteCodexHookStatusPreSpawn(_cwd, ctx) {
|
|
10666
10625
|
const hooksPath = ctx.codexHooksPath ?? DEFAULT_CODEX_HOOKS_PATH;
|
|
10667
10626
|
let location;
|
|
@@ -11388,8 +11347,7 @@ import {
|
|
|
11388
11347
|
assertBasouRootSafe as assertBasouRootSafe15,
|
|
11389
11348
|
basouPaths as basouPaths20,
|
|
11390
11349
|
computeWorkStats,
|
|
11391
|
-
findErrorCode as findErrorCode14
|
|
11392
|
-
resolveRepositoryRoot as resolveRepositoryRoot10
|
|
11350
|
+
findErrorCode as findErrorCode14
|
|
11393
11351
|
} from "@basou/core";
|
|
11394
11352
|
function registerStatsCommand(program) {
|
|
11395
11353
|
program.command("stats").description("Report how much the AI worked (output volume + time proxies) across sessions").option("--by-source", "Break the totals down by session source kind").option("--by-day", "Break billable time and volume down by calendar day").option("--json", "Output the full stats as JSON").option("-v, --verbose", "Show error causes").action(async (options) => {
|
|
@@ -11406,7 +11364,7 @@ async function runStats(options, ctx = {}) {
|
|
|
11406
11364
|
}
|
|
11407
11365
|
async function doRunStats(options, ctx) {
|
|
11408
11366
|
const cwd = ctx.cwd ?? process.cwd();
|
|
11409
|
-
const repositoryRoot = await
|
|
11367
|
+
const repositoryRoot = await resolveBasouRootForCommand(cwd, "stats");
|
|
11410
11368
|
const paths = basouPaths20(repositoryRoot);
|
|
11411
11369
|
await assertWorkspaceInitialized13(paths.root);
|
|
11412
11370
|
const now = ctx.nowProvider?.() ?? /* @__PURE__ */ new Date();
|
|
@@ -11494,18 +11452,6 @@ function formatFloorMs(ms) {
|
|
|
11494
11452
|
function formatInt(n) {
|
|
11495
11453
|
return n.toLocaleString("en-US");
|
|
11496
11454
|
}
|
|
11497
|
-
async function resolveRepositoryRootForStats(cwd) {
|
|
11498
|
-
try {
|
|
11499
|
-
return await resolveRepositoryRoot10(cwd);
|
|
11500
|
-
} catch (error) {
|
|
11501
|
-
if (error instanceof Error && error.message === "Not a git repository") {
|
|
11502
|
-
throw new Error("Not a git repository. Run 'git init' first, then re-run 'basou stats'.", {
|
|
11503
|
-
cause: error
|
|
11504
|
-
});
|
|
11505
|
-
}
|
|
11506
|
-
throw error;
|
|
11507
|
-
}
|
|
11508
|
-
}
|
|
11509
11455
|
async function assertWorkspaceInitialized13(basouRoot) {
|
|
11510
11456
|
try {
|
|
11511
11457
|
await assertBasouRootSafe15(basouRoot);
|
|
@@ -11524,7 +11470,6 @@ import {
|
|
|
11524
11470
|
buildStatusSnapshot,
|
|
11525
11471
|
findErrorCode as findErrorCode15,
|
|
11526
11472
|
readManifest as readManifest13,
|
|
11527
|
-
resolveRepositoryRoot as resolveRepositoryRoot11,
|
|
11528
11473
|
writeStatus
|
|
11529
11474
|
} from "@basou/core";
|
|
11530
11475
|
function registerStatusCommand(program) {
|
|
@@ -11542,7 +11487,7 @@ async function runStatus(options, ctx = {}) {
|
|
|
11542
11487
|
}
|
|
11543
11488
|
async function doRunStatus(options, ctx) {
|
|
11544
11489
|
const cwd = ctx.cwd ?? process.cwd();
|
|
11545
|
-
const repositoryRoot = await
|
|
11490
|
+
const repositoryRoot = await resolveBasouRootForCommand(cwd, "status");
|
|
11546
11491
|
const paths = basouPaths21(repositoryRoot);
|
|
11547
11492
|
try {
|
|
11548
11493
|
await assertBasouRootSafe16(paths.root);
|
|
@@ -11580,18 +11525,6 @@ function renderTextStatus(s) {
|
|
|
11580
11525
|
const present = Object.values(dp).filter((v) => v === true).length;
|
|
11581
11526
|
console.log(`Subdirectories present: ${present}/${total}`);
|
|
11582
11527
|
}
|
|
11583
|
-
async function resolveRepositoryRootForStatus(cwd) {
|
|
11584
|
-
try {
|
|
11585
|
-
return await resolveRepositoryRoot11(cwd);
|
|
11586
|
-
} catch (error) {
|
|
11587
|
-
if (error instanceof Error && error.message === "Not a git repository") {
|
|
11588
|
-
throw new Error("Not a git repository. Run 'git init' first, then re-run 'basou status'.", {
|
|
11589
|
-
cause: error
|
|
11590
|
-
});
|
|
11591
|
-
}
|
|
11592
|
-
throw error;
|
|
11593
|
-
}
|
|
11594
|
-
}
|
|
11595
11528
|
function formatVersionGateMessage(error) {
|
|
11596
11529
|
const issues = error.issues;
|
|
11597
11530
|
if (!Array.isArray(issues)) return void 0;
|
|
@@ -12733,7 +12666,6 @@ import {
|
|
|
12733
12666
|
basouPaths as basouPaths23,
|
|
12734
12667
|
enumerateSessionEntries as enumerateSessionEntries3,
|
|
12735
12668
|
findErrorCode as findErrorCode17,
|
|
12736
|
-
resolveRepositoryRoot as resolveRepositoryRoot12,
|
|
12737
12669
|
resolveSessionId as resolveSessionId5,
|
|
12738
12670
|
verifyEventsChain
|
|
12739
12671
|
} from "@basou/core";
|
|
@@ -12762,7 +12694,7 @@ async function doRunVerify(options, ctx) {
|
|
|
12762
12694
|
throw new Error("Specify either --session <id> or --all, not both");
|
|
12763
12695
|
}
|
|
12764
12696
|
const cwd = ctx.cwd ?? process.cwd();
|
|
12765
|
-
const repositoryRoot = await
|
|
12697
|
+
const repositoryRoot = await resolveBasouRootForCommand(cwd, "verify");
|
|
12766
12698
|
const paths = basouPaths23(repositoryRoot);
|
|
12767
12699
|
await assertWorkspaceInitialized15(paths.root);
|
|
12768
12700
|
const sessionIds = options.session !== void 0 ? [await resolveSessionId5(paths, options.session, { allowNotDirectory: true })] : await enumerateAllSessionNames2(paths);
|
|
@@ -12816,18 +12748,6 @@ async function enumerateAllSessionNames2(paths) {
|
|
|
12816
12748
|
const { dirs, notDirectories } = await enumerateSessionEntries3(paths);
|
|
12817
12749
|
return [...dirs, ...notDirectories].sort();
|
|
12818
12750
|
}
|
|
12819
|
-
async function resolveRepositoryRootForVerify(cwd) {
|
|
12820
|
-
try {
|
|
12821
|
-
return await resolveRepositoryRoot12(cwd);
|
|
12822
|
-
} catch (error) {
|
|
12823
|
-
if (error instanceof Error && error.message === "Not a git repository") {
|
|
12824
|
-
throw new Error("Not a git repository. Run 'git init' first, then re-run 'basou verify'.", {
|
|
12825
|
-
cause: error
|
|
12826
|
-
});
|
|
12827
|
-
}
|
|
12828
|
-
throw error;
|
|
12829
|
-
}
|
|
12830
|
-
}
|
|
12831
12751
|
async function assertWorkspaceInitialized15(basouRoot) {
|
|
12832
12752
|
try {
|
|
12833
12753
|
await assertBasouRootSafe18(basouRoot);
|
|
@@ -12848,8 +12768,7 @@ import {
|
|
|
12848
12768
|
basouPaths as basouPaths25,
|
|
12849
12769
|
findErrorCode as findErrorCode19,
|
|
12850
12770
|
loadPortfolioConfig as loadPortfolioConfig6,
|
|
12851
|
-
readManifest as readManifest18
|
|
12852
|
-
resolveRepositoryRoot as resolveRepositoryRoot14
|
|
12771
|
+
readManifest as readManifest18
|
|
12853
12772
|
} from "@basou/core";
|
|
12854
12773
|
import { InvalidArgumentError as InvalidArgumentError8 } from "commander";
|
|
12855
12774
|
|
|
@@ -12859,12 +12778,15 @@ import { readdir as readdir3, stat as stat7 } from "fs/promises";
|
|
|
12859
12778
|
import { homedir as homedir12 } from "os";
|
|
12860
12779
|
import { basename as basename9, dirname as dirname7, join as join16 } from "path";
|
|
12861
12780
|
import { createInterface as createInterface2 } from "readline";
|
|
12862
|
-
import { basouPaths as basouPaths24, readManifest as readManifest15
|
|
12781
|
+
import { basouPaths as basouPaths24, readManifest as readManifest15 } from "@basou/core";
|
|
12863
12782
|
function uncapturedTotal(result) {
|
|
12864
12783
|
return result.groups.reduce((sum, g) => sum + g.logs, 0) + result.unplaceable;
|
|
12865
12784
|
}
|
|
12866
12785
|
async function checkPortfolioCoverage(workspaces, ctx = {}) {
|
|
12867
|
-
const { roots, inertWorkspaces } = await collectDeclaredRoots(
|
|
12786
|
+
const { roots, inertWorkspaces } = await collectDeclaredRoots(
|
|
12787
|
+
workspaces,
|
|
12788
|
+
ctx.portfolioConfigPath
|
|
12789
|
+
);
|
|
12868
12790
|
const listedDirs = new Set([...roots].map((root) => encodeProjectDir(root)));
|
|
12869
12791
|
const claudeProjectsDir = ctx.claudeProjectsDir ?? join16(homedir12(), ".claude", "projects");
|
|
12870
12792
|
const codexSessionsDir = ctx.codexSessionsDir ?? join16(homedir12(), ".codex", "sessions");
|
|
@@ -12889,13 +12811,16 @@ async function checkPortfolioCoverage(workspaces, ctx = {}) {
|
|
|
12889
12811
|
}
|
|
12890
12812
|
return tally.finish(absentTrees, inertWorkspaces);
|
|
12891
12813
|
}
|
|
12892
|
-
async function collectDeclaredRoots(workspaces) {
|
|
12814
|
+
async function collectDeclaredRoots(workspaces, portfolioConfigPath) {
|
|
12893
12815
|
const roots = /* @__PURE__ */ new Set();
|
|
12894
12816
|
const inertWorkspaces = [];
|
|
12895
12817
|
for (const ws of workspaces) {
|
|
12896
12818
|
let importRoot;
|
|
12897
12819
|
try {
|
|
12898
|
-
importRoot = await
|
|
12820
|
+
importRoot = await resolveBasouRootForCommand(ws.repoRoot, "import", {
|
|
12821
|
+
quiet: true,
|
|
12822
|
+
...portfolioConfigPath !== void 0 ? { portfolioConfigPath } : {}
|
|
12823
|
+
});
|
|
12899
12824
|
} catch {
|
|
12900
12825
|
inertWorkspaces.push({ path: ws.repoRoot, reason: "not_a_git_repo" });
|
|
12901
12826
|
continue;
|
|
@@ -13351,6 +13276,7 @@ function formatSafetyReport(result) {
|
|
|
13351
13276
|
import { createServer } from "http";
|
|
13352
13277
|
import { basename as basename10, join as join18, resolve as resolve13 } from "path";
|
|
13353
13278
|
import {
|
|
13279
|
+
boardLivePage,
|
|
13354
13280
|
boardPage,
|
|
13355
13281
|
boardPageStrings,
|
|
13356
13282
|
boardPageUnavailable,
|
|
@@ -13492,6 +13418,7 @@ var BOARD_HTML = `<!doctype html>
|
|
|
13492
13418
|
if (k === 'text') node.textContent = attrs[k];
|
|
13493
13419
|
else if (k === 'class') node.className = attrs[k];
|
|
13494
13420
|
else if (k === 'onchange') node.addEventListener('change', attrs[k]);
|
|
13421
|
+
else if (k === 'onclick') node.addEventListener('click', attrs[k]);
|
|
13495
13422
|
else node.setAttribute(k, attrs[k]);
|
|
13496
13423
|
});
|
|
13497
13424
|
}
|
|
@@ -13585,7 +13512,11 @@ var BOARD_HTML = `<!doctype html>
|
|
|
13585
13512
|
clear(nav);
|
|
13586
13513
|
var page = d.page;
|
|
13587
13514
|
var list = page.records || [];
|
|
13588
|
-
if (list.length
|
|
13515
|
+
if (list.length > 0) recordLinks(nav, page, list);
|
|
13516
|
+
nav.appendChild(el('a', { href: '/board?live=1', text: S.live.measureNow }));
|
|
13517
|
+
}
|
|
13518
|
+
|
|
13519
|
+
function recordLinks(nav, page, list) {
|
|
13589
13520
|
var link = function (id, label) {
|
|
13590
13521
|
return el('a', { href: '/board?record=' + encodeURIComponent(id), text: label });
|
|
13591
13522
|
};
|
|
@@ -14066,8 +13997,12 @@ var BOARD_HTML = `<!doctype html>
|
|
|
14066
13997
|
not_found: S.unavailable.notFound, not_json: S.unavailable.notJson,
|
|
14067
13998
|
unknown_version: S.unavailable.unknownVersion, not_a_record: S.unavailable.notARecord
|
|
14068
13999
|
};
|
|
14069
|
-
|
|
14070
|
-
|
|
14000
|
+
// With no board or no record yet, draw what basou measures now instead.
|
|
14001
|
+
if (page.why === 'no_board' || page.why === 'no_records') {
|
|
14002
|
+
loadLive(page.why);
|
|
14003
|
+
return;
|
|
14004
|
+
}
|
|
14005
|
+
setStatus(fill(messages[page.why] || page.why, { record: page.id || '', version: page.version }), true);
|
|
14071
14006
|
return;
|
|
14072
14007
|
}
|
|
14073
14008
|
setStatus('', false);
|
|
@@ -14076,17 +14011,153 @@ var BOARD_HTML = `<!doctype html>
|
|
|
14076
14011
|
.forEach(function (node) { root.appendChild(node); });
|
|
14077
14012
|
}
|
|
14078
14013
|
|
|
14079
|
-
|
|
14080
|
-
|
|
14081
|
-
|
|
14082
|
-
|
|
14014
|
+
// The board measured now: the sections basou measures with nothing declared
|
|
14015
|
+
// or judged. why is the reason there was no record to draw, or null when
|
|
14016
|
+
// asked for with ?live=1.
|
|
14017
|
+
function liveTile(k, v, sub) {
|
|
14018
|
+
return el('div', { class: 'tile' }, [
|
|
14019
|
+
el('div', { class: 'k', text: k }), el('div', { class: 'v', text: v }),
|
|
14020
|
+
sub ? el('div', { class: 's', text: sub }) : null
|
|
14021
|
+
]);
|
|
14022
|
+
}
|
|
14023
|
+
function liveHeading(l, why) {
|
|
14024
|
+
var h = l.heading;
|
|
14025
|
+
var measured = h.complete ? S.heading.complete : fill(S.heading.incomplete, { n: h.not_found });
|
|
14026
|
+
var again = el('button', { type: 'button', text: S.live.remeasure, onclick: function () { loadLive(why); } });
|
|
14027
|
+
return el('header', null, [
|
|
14028
|
+
el('h1', { text: h.title }),
|
|
14029
|
+
el('div', { class: 'stamp' }, [fill(S.live.measuredAt, { at: when(h.measured_at) }) + ' / ' + measured + ' ', again])
|
|
14030
|
+
]);
|
|
14031
|
+
}
|
|
14032
|
+
// Whether the measurement says why the value at a place was not measured.
|
|
14033
|
+
function missingIn(l) {
|
|
14034
|
+
return function (at) { return l.footnotes.not_found.some(function (n) { return n.at === at; }); };
|
|
14035
|
+
}
|
|
14036
|
+
function liveRepos(l) {
|
|
14037
|
+
var missing = missingIn(l);
|
|
14038
|
+
var labels = [S.live.repo, S.live.branch, S.live.head, S.live.lastCommit, S.live.commits, S.live.uncommitted, S.live.behindMain];
|
|
14039
|
+
var head = el('tr', null, labels.map(function (label, i) { return el('th', { class: i >= 4 ? 'n' : '', text: label }); }));
|
|
14040
|
+
var rows = l.repos.map(function (r) {
|
|
14041
|
+
var at = 'repos[' + r.path + ']';
|
|
14042
|
+
// A null the measurement says it could not measure, or one that means
|
|
14043
|
+
// so (a detached HEAD, no commit yet, no origin/main), drawn as '-'.
|
|
14044
|
+
var value = function (field, v, show) {
|
|
14045
|
+
if (v !== null) return show(v);
|
|
14046
|
+
return missing(at) || missing(at + '.' + field) ? S.tiles.notMeasured : '-';
|
|
14047
|
+
};
|
|
14048
|
+
return el('tr', null, [
|
|
14049
|
+
el('td', { text: r.name === null ? r.path : r.name }),
|
|
14050
|
+
el('td', { text: value('branch', r.branch, String) }),
|
|
14051
|
+
el('td', { text: value('head', r.head, function (h) { return h.slice(0, 7); }) }),
|
|
14052
|
+
el('td', { text: value('last_commit', r.last_commit, when) }),
|
|
14053
|
+
el('td', { class: 'n', text: num(r.commits) }),
|
|
14054
|
+
el('td', { class: 'n', text: num(r.uncommitted) }),
|
|
14055
|
+
el('td', { class: 'n', text: value('behind_main', r.behind_main, num) })
|
|
14056
|
+
]);
|
|
14057
|
+
});
|
|
14058
|
+
return section(S.live.repos, false, [el('div', { class: 'scroll' }, [el('table', { class: 'grid' }, [head].concat(rows))])]);
|
|
14059
|
+
}
|
|
14060
|
+
function liveTrail(l) {
|
|
14061
|
+
var t = l.trail;
|
|
14062
|
+
var i = l.integrity;
|
|
14063
|
+
var f = l.freshness;
|
|
14064
|
+
var missing = missingIn(l);
|
|
14065
|
+
var newest = f.newest_session_at !== null ? when(f.newest_session_at)
|
|
14066
|
+
: missing('freshness.newest_session_at') ? S.tiles.notMeasured : S.live.noSession;
|
|
14067
|
+
var unimported = f.unimported === null ? S.live.unimportedNotMeasured : fill(S.live.unimported, {
|
|
14068
|
+
new: f.unimported.new, updated: f.unimported.updated, unverifiable: f.unimported.unverifiable
|
|
14069
|
+
});
|
|
14070
|
+
var counts = function (byKey) {
|
|
14071
|
+
if (byKey === null) return S.tiles.notMeasured;
|
|
14072
|
+
return Object.keys(byKey).map(function (k) { return k + ' ' + num(byKey[k]); }).join(' / ');
|
|
14073
|
+
};
|
|
14074
|
+
var tiles = el('div', { class: 'tiles' }, [
|
|
14075
|
+
liveTile(S.live.decisions, num(t.decisions_live), t.decisions_all === null ? null : fill(S.live.decisionsAll, { n: num(t.decisions_all) })),
|
|
14076
|
+
liveTile(S.tiles.openTracks, t.tracks_open === null ? S.tiles.notMeasured : num(t.tracks_open.length), null),
|
|
14077
|
+
liveTile(S.tiles.sessions, num(i.sessions), i.not_verified === null ? null : fill(S.tiles.sessionsNotVerified, { n: num(i.not_verified) })),
|
|
14078
|
+
liveTile(S.live.reviewGaps, num(l.review_gaps.gaps), null),
|
|
14079
|
+
liveTile(S.live.newestSession, newest, unimported)
|
|
14080
|
+
]);
|
|
14081
|
+
var tracks = t.tracks_open === null ? el('p', { class: 'muted', text: S.tiles.notMeasured })
|
|
14082
|
+
: t.tracks_open.length === 0 ? el('p', { class: 'muted', text: S.live.noTracks })
|
|
14083
|
+
: el('ul', null, t.tracks_open.map(function (tr) {
|
|
14084
|
+
return el('li', null, [el('span', { class: 'muted', text: tr.id + ' ' }), tr.title]);
|
|
14085
|
+
}));
|
|
14086
|
+
var breakdown = el('div', { class: 'muted' }, [
|
|
14087
|
+
el('div', { text: fill(S.live.byStatus, { list: counts(i.by_status) }) }),
|
|
14088
|
+
el('div', { text: fill(S.live.byVerdict, { list: counts(l.review_gaps.by_verdict) }) })
|
|
14089
|
+
]);
|
|
14090
|
+
return section(S.live.trail, false, [tiles, breakdown, el('h3', { text: S.live.tracks }), tracks]);
|
|
14091
|
+
}
|
|
14092
|
+
function liveComponents(l) {
|
|
14093
|
+
var c = l.components;
|
|
14094
|
+
var body = c === null ? el('p', { class: 'muted', text: S.tiles.notMeasured })
|
|
14095
|
+
: c.length === 0 ? el('p', { class: 'muted', text: S.live.noComponents })
|
|
14096
|
+
: el('ul', null, c.map(function (x) {
|
|
14097
|
+
return el('li', null, [el('code', { text: x.key }), el('span', { class: 'muted', text: ' ' + x.kinds.join(', ') })]);
|
|
14098
|
+
}));
|
|
14099
|
+
return section(S.live.components, false, [body]);
|
|
14100
|
+
}
|
|
14101
|
+
function liveFootnotes(l, why) {
|
|
14102
|
+
var f = l.footnotes;
|
|
14103
|
+
var items = [el('li', { text: S.live.note }), el('li', { text: S.live.judged })];
|
|
14104
|
+
if (why === 'no_board') items.push(el('li', { text: S.unavailable.noBoard }));
|
|
14105
|
+
if (f.not_found.length > 0) {
|
|
14106
|
+
items.push(el('li', null, [el('b', { text: S.footnotes.notMeasured }), el('ul', null, f.not_found.map(function (n) {
|
|
14107
|
+
return el('li', { text: n.at + ': ' + n.reason });
|
|
14108
|
+
}))]));
|
|
14109
|
+
}
|
|
14110
|
+
var build = f.measured_with.build === null ? '' : ' (' + f.measured_with.build + ')';
|
|
14111
|
+
items.push(el('li', { text: fill(S.footnotes.measuredWith, { basou: f.measured_with.basou, build: build }) }));
|
|
14112
|
+
return section(S.sections.footnotes, false, [el('ul', { class: 'foot' }, items)]);
|
|
14113
|
+
}
|
|
14114
|
+
var liveDrawn = 0;
|
|
14115
|
+
function drawLive(d, why) {
|
|
14116
|
+
S = d.strings;
|
|
14117
|
+
lang = d.language;
|
|
14118
|
+
document.documentElement.lang = lang;
|
|
14119
|
+
document.title = S.pageTitle;
|
|
14120
|
+
var drawn = ++liveDrawn;
|
|
14121
|
+
var nav = $('records');
|
|
14122
|
+
clear(nav);
|
|
14123
|
+
// Asked for with ?live=1: lead back to the records, when there are any.
|
|
14124
|
+
if (why === null) {
|
|
14125
|
+
getJson('/api/board').then(function (b) {
|
|
14126
|
+
var list = b && b.page && b.page.records ? b.page.records : [];
|
|
14127
|
+
if (drawn === liveDrawn && list.length > 0) nav.appendChild(el('a', { href: '/board', text: S.live.toRecords }));
|
|
14128
|
+
}).catch(function () {});
|
|
14129
|
+
}
|
|
14130
|
+
var root = $('board');
|
|
14131
|
+
clear(root);
|
|
14132
|
+
setStatus('', false);
|
|
14133
|
+
var l = d.live;
|
|
14134
|
+
[liveHeading(l, why), effort(l), liveRepos(l), liveTrail(l), liveComponents(l), liveFootnotes(l, why)]
|
|
14135
|
+
.forEach(function (node) { root.appendChild(node); });
|
|
14136
|
+
}
|
|
14137
|
+
|
|
14138
|
+
function getJson(url) {
|
|
14139
|
+
return fetch(url).then(function (res) {
|
|
14083
14140
|
return res.json().then(function (body) {
|
|
14084
14141
|
if (!res.ok) throw new Error(body && body.error ? body.error : String(res.status));
|
|
14085
14142
|
return body;
|
|
14086
14143
|
});
|
|
14087
|
-
})
|
|
14088
|
-
|
|
14089
|
-
|
|
14144
|
+
});
|
|
14145
|
+
}
|
|
14146
|
+
function failed(err) { setStatus(S ? fill(S.loadFailed, { message: err.message }) : String(err.message), true); }
|
|
14147
|
+
// Measuring takes seconds, so the page says so until it is done.
|
|
14148
|
+
function loadLive(why) {
|
|
14149
|
+
setStatus(S ? S.live.measuring : '...', false);
|
|
14150
|
+
getJson('/api/board/live').then(function (d) { drawLive(d, why); }).catch(failed);
|
|
14151
|
+
}
|
|
14152
|
+
|
|
14153
|
+
var params = new URLSearchParams(location.search);
|
|
14154
|
+
var record = params.get('record');
|
|
14155
|
+
if (params.get('live') !== null) {
|
|
14156
|
+
loadLive(null);
|
|
14157
|
+
} else {
|
|
14158
|
+
setStatus('...', false);
|
|
14159
|
+
getJson('/api/board' + (record ? '/' + encodeURIComponent(record) : '')).then(draw).catch(failed);
|
|
14160
|
+
}
|
|
14090
14161
|
})();
|
|
14091
14162
|
</script>
|
|
14092
14163
|
</body>
|
|
@@ -14907,7 +14978,8 @@ async function handleGet(res, pathname, deps) {
|
|
|
14907
14978
|
ws,
|
|
14908
14979
|
deps.nowProvider,
|
|
14909
14980
|
remoteUrlOf(deps),
|
|
14910
|
-
boardOf(deps)
|
|
14981
|
+
boardOf(deps),
|
|
14982
|
+
boardLiveOf(deps)
|
|
14911
14983
|
)) {
|
|
14912
14984
|
sendError(res, 404, "Not found");
|
|
14913
14985
|
}
|
|
@@ -14921,7 +14993,8 @@ async function handleGet(res, pathname, deps) {
|
|
|
14921
14993
|
primaryWorkspace(deps),
|
|
14922
14994
|
deps.nowProvider,
|
|
14923
14995
|
remoteUrlOf(deps),
|
|
14924
|
-
boardOf(deps)
|
|
14996
|
+
boardOf(deps),
|
|
14997
|
+
boardLiveOf(deps)
|
|
14925
14998
|
)) {
|
|
14926
14999
|
sendError(res, 404, "Not found");
|
|
14927
15000
|
}
|
|
@@ -14932,6 +15005,9 @@ async function handleGet(res, pathname, deps) {
|
|
|
14932
15005
|
function boardOf(deps) {
|
|
14933
15006
|
return deps.mode === "single" ? deps.board : void 0;
|
|
14934
15007
|
}
|
|
15008
|
+
function boardLiveOf(deps) {
|
|
15009
|
+
return deps.mode === "single" ? deps.boardLive : void 0;
|
|
15010
|
+
}
|
|
14935
15011
|
function remoteUrlOf(deps) {
|
|
14936
15012
|
return deps.remoteUrlOf ?? tryRemoteUrl;
|
|
14937
15013
|
}
|
|
@@ -14957,7 +15033,11 @@ async function handlePost(res, pathname, body, deps, runExclusive) {
|
|
|
14957
15033
|
}
|
|
14958
15034
|
sendError(res, 404, "Not found");
|
|
14959
15035
|
}
|
|
14960
|
-
async function handleWorkspaceGet(res, sub, ws, nowProvider, resolveRemoteUrl, board) {
|
|
15036
|
+
async function handleWorkspaceGet(res, sub, ws, nowProvider, resolveRemoteUrl, board, live) {
|
|
15037
|
+
if (live !== void 0 && sub === "board/live") {
|
|
15038
|
+
sendJson(res, 200, await boardLiveView(ws, live));
|
|
15039
|
+
return true;
|
|
15040
|
+
}
|
|
14961
15041
|
if (board !== void 0) {
|
|
14962
15042
|
if (sub === "board") {
|
|
14963
15043
|
sendJson(res, 200, await boardView(ws, board, void 0));
|
|
@@ -15009,9 +15089,13 @@ async function handleWorkspaceGet(res, sub, ws, nowProvider, resolveRemoteUrl, b
|
|
|
15009
15089
|
}
|
|
15010
15090
|
return false;
|
|
15011
15091
|
}
|
|
15092
|
+
var IMPORT_SUBS = /* @__PURE__ */ new Set(["import/claude-code", "import/codex"]);
|
|
15012
15093
|
async function handleWorkspacePost(res, sub, ws, body, deps, runExclusive) {
|
|
15013
15094
|
const nowIso = deps.nowProvider().toISOString();
|
|
15014
15095
|
const actionOptions = readActionOptions(body);
|
|
15096
|
+
if (!ws.initialized && (sub === "refresh" || IMPORT_SUBS.has(sub))) {
|
|
15097
|
+
throw new Error("Workspace not initialized. Run 'basou init' first.");
|
|
15098
|
+
}
|
|
15015
15099
|
if (sub === "refresh") {
|
|
15016
15100
|
const result = await runExclusive(
|
|
15017
15101
|
() => refreshAll({ options: actionOptions, ctx: ws.importCtx, paths: ws.paths, nowIso })
|
|
@@ -15069,6 +15153,14 @@ async function boardView(ws, board, recordId) {
|
|
|
15069
15153
|
const page = "why" in board ? boardPageUnavailable(board.why) : await boardPage(board.recordsDir, recordId);
|
|
15070
15154
|
return { language, strings: boardPageStrings(language), page };
|
|
15071
15155
|
}
|
|
15156
|
+
async function boardLiveView(ws, live) {
|
|
15157
|
+
const language = await resolveViewLanguageFromPaths(ws.paths);
|
|
15158
|
+
return {
|
|
15159
|
+
language,
|
|
15160
|
+
strings: boardPageStrings(language),
|
|
15161
|
+
live: boardLivePage(ws.label, await live())
|
|
15162
|
+
};
|
|
15163
|
+
}
|
|
15072
15164
|
async function portfolio(deps) {
|
|
15073
15165
|
const nowIso = deps.nowProvider().toISOString();
|
|
15074
15166
|
const workspaces = await Promise.all(deps.workspaces.map((ws) => portfolioCard(ws, nowIso)));
|
|
@@ -15388,7 +15480,8 @@ async function doRunView(options, ctx) {
|
|
|
15388
15480
|
if (isWholeRegistry) {
|
|
15389
15481
|
const coverage = await checkPortfolioCoverage(deps.workspaces, {
|
|
15390
15482
|
...ctx.claudeProjectsDir !== void 0 ? { claudeProjectsDir: ctx.claudeProjectsDir } : {},
|
|
15391
|
-
...ctx.codexSessionsDir !== void 0 ? { codexSessionsDir: ctx.codexSessionsDir } : {}
|
|
15483
|
+
...ctx.codexSessionsDir !== void 0 ? { codexSessionsDir: ctx.codexSessionsDir } : {},
|
|
15484
|
+
...ctx.portfolioConfigPath !== void 0 ? { portfolioConfigPath: ctx.portfolioConfigPath } : {}
|
|
15392
15485
|
});
|
|
15393
15486
|
console.log("");
|
|
15394
15487
|
for (const line of formatCoverageReport(coverage)) console.log(line);
|
|
@@ -15431,7 +15524,7 @@ async function doRunView(options, ctx) {
|
|
|
15431
15524
|
}
|
|
15432
15525
|
}
|
|
15433
15526
|
async function buildSingleDeps(ctx, cwd) {
|
|
15434
|
-
const repositoryRoot = await
|
|
15527
|
+
const repositoryRoot = await resolveBasouRootForCommand(cwd, "view");
|
|
15435
15528
|
const paths = basouPaths25(repositoryRoot);
|
|
15436
15529
|
await assertWorkspaceInitialized16(paths.root);
|
|
15437
15530
|
const entry = await buildWorkspaceEntry(repositoryRoot, ctx);
|
|
@@ -15440,7 +15533,26 @@ async function buildSingleDeps(ctx, cwd) {
|
|
|
15440
15533
|
mode: "single",
|
|
15441
15534
|
nowProvider: nowProviderOf(ctx),
|
|
15442
15535
|
...ctx.remoteUrlOf !== void 0 ? { remoteUrlOf: ctx.remoteUrlOf } : {},
|
|
15443
|
-
board: await viewBoardOf(repositoryRoot, paths)
|
|
15536
|
+
board: await viewBoardOf(repositoryRoot, paths),
|
|
15537
|
+
boardLive: liveBoardOf(repositoryRoot, ctx)
|
|
15538
|
+
};
|
|
15539
|
+
}
|
|
15540
|
+
function liveBoardOf(root, ctx) {
|
|
15541
|
+
return sharedWhileRunning(
|
|
15542
|
+
() => measureLiveBoard(root, {
|
|
15543
|
+
...ctx.nowProvider !== void 0 ? { nowProvider: ctx.nowProvider } : {},
|
|
15544
|
+
...ctx.claudeProjectsDir !== void 0 ? { claudeProjectsDir: ctx.claudeProjectsDir } : {},
|
|
15545
|
+
...ctx.codexSessionsDir !== void 0 ? { codexSessionsDir: ctx.codexSessionsDir } : {}
|
|
15546
|
+
})
|
|
15547
|
+
);
|
|
15548
|
+
}
|
|
15549
|
+
function sharedWhileRunning(run) {
|
|
15550
|
+
let running = null;
|
|
15551
|
+
return () => {
|
|
15552
|
+
running ??= run().finally(() => {
|
|
15553
|
+
running = null;
|
|
15554
|
+
});
|
|
15555
|
+
return running;
|
|
15444
15556
|
};
|
|
15445
15557
|
}
|
|
15446
15558
|
async function viewBoardOf(root, paths) {
|
|
@@ -15562,18 +15674,6 @@ function waitForShutdown(signal) {
|
|
|
15562
15674
|
}
|
|
15563
15675
|
});
|
|
15564
15676
|
}
|
|
15565
|
-
async function resolveRepositoryRootForView(cwd) {
|
|
15566
|
-
try {
|
|
15567
|
-
return await resolveRepositoryRoot14(cwd);
|
|
15568
|
-
} catch (error) {
|
|
15569
|
-
if (error instanceof Error && error.message === "Not a git repository") {
|
|
15570
|
-
throw new Error("Not a git repository. Run 'git init' first, then re-run 'basou view'.", {
|
|
15571
|
-
cause: error
|
|
15572
|
-
});
|
|
15573
|
-
}
|
|
15574
|
-
throw error;
|
|
15575
|
-
}
|
|
15576
|
-
}
|
|
15577
15677
|
async function assertWorkspaceInitialized16(basouRoot) {
|
|
15578
15678
|
try {
|
|
15579
15679
|
await assertBasouRootSafe19(basouRoot);
|
|
@@ -15617,7 +15717,7 @@ function installExperimentalNotice(program, names = EXPERIMENTAL_COMMANDS, write
|
|
|
15617
15717
|
function readBuildStamp() {
|
|
15618
15718
|
if (false) return void 0;
|
|
15619
15719
|
try {
|
|
15620
|
-
return JSON.parse('{"version":"0.
|
|
15720
|
+
return JSON.parse('{"version":"0.67.0","commit":"c4c4ebb","committedAt":"2026-10-08T23:33:17+09:00"}');
|
|
15621
15721
|
} catch {
|
|
15622
15722
|
return void 0;
|
|
15623
15723
|
}
|