@basou/cli 0.65.0 → 0.66.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/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 resolveRepositoryRootForApproval(cwd, "list");
194
- const paths = basouPaths(repositoryRoot);
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 resolveRepositoryRootForApproval(cwd, "show");
267
- const paths = basouPaths(repositoryRoot);
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 resolveRepositoryRootForApproval(cwd, decision);
333
- const paths = basouPaths(repositoryRoot);
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);
@@ -733,16 +863,16 @@ import {
733
863
 
734
864
  // src/commands/import.ts
735
865
  import { createReadStream } from "fs";
736
- import { readdir, readFile, rm, stat } from "fs/promises";
866
+ import { readdir, readFile, rm, stat as stat2 } from "fs/promises";
737
867
  import { homedir } from "os";
738
- import { basename, dirname as dirname2, join as join2, resolve } from "path";
868
+ import { basename as basename2, dirname as dirname2, join as join2, resolve as resolve2 } from "path";
739
869
  import { createInterface } from "readline";
740
870
  import {
741
871
  AGENT_INFRA_DIRS,
742
872
  assertBasouRootSafe as assertBasouRootSafe2,
743
873
  assertLockStoreSafe as assertLockStoreSafe2,
744
874
  assertSessionStoreSafe,
745
- basouPaths as basouPaths2,
875
+ basouPaths as basouPaths3,
746
876
  CLAUDE_IMPORT_SOURCE,
747
877
  CODEX_IMPORT_SOURCE,
748
878
  classifyFilesBySourceRoot,
@@ -753,11 +883,10 @@ import {
753
883
  findErrorCode as findErrorCode2,
754
884
  importSessionFromJson,
755
885
  observedFilesOf,
756
- readManifest,
886
+ readManifest as readManifest2,
757
887
  readSessionObservation,
758
888
  readSessionYaml as readSessionYaml2,
759
889
  reimportPreservingId,
760
- resolveRepositoryRoot as resolveRepositoryRoot2,
761
890
  SESSION_IMPORT_SCHEMA_VERSION,
762
891
  SessionImportPayloadSchema
763
892
  } from "@basou/core";
@@ -827,10 +956,10 @@ function resolveSourceRoots(args) {
827
956
  const { projectFlags, manifest, repoRoot, cwd } = args;
828
957
  let resolved;
829
958
  if (projectFlags.length > 0) {
830
- resolved = projectFlags.map((p) => resolve(cwd, p));
959
+ resolved = projectFlags.map((p) => resolve2(cwd, p));
831
960
  } else {
832
961
  const roots = manifest.import?.source_roots;
833
- resolved = roots !== void 0 && roots.length > 0 ? roots.map((r) => resolve(repoRoot, r)) : [repoRoot];
962
+ resolved = roots !== void 0 && roots.length > 0 ? roots.map((r) => resolve2(repoRoot, r)) : [repoRoot];
834
963
  }
835
964
  return [...new Set(resolved)];
836
965
  }
@@ -848,7 +977,7 @@ async function doRunImportClaudeCode(options, ctx) {
848
977
  const files = await selectTranscriptFiles(projectsRoot, projectPaths, options);
849
978
  const projectSet = new Set(projectPaths);
850
979
  const candidates = files.map((file) => {
851
- const externalId = basename(file, ".jsonl");
980
+ const externalId = basename2(file, ".jsonl");
852
981
  return {
853
982
  externalId,
854
983
  sourcePath: file,
@@ -923,10 +1052,10 @@ function assertSelector(options) {
923
1052
  }
924
1053
  async function resolveImportTarget(ctx) {
925
1054
  const cwd = ctx.cwd ?? process.cwd();
926
- const repositoryRoot = await resolveRepositoryRootForImport(cwd);
927
- const paths = basouPaths2(repositoryRoot);
1055
+ const repositoryRoot = await resolveBasouRootForCommand(cwd, "import");
1056
+ const paths = basouPaths3(repositoryRoot);
928
1057
  await assertWorkspaceInitialized2(paths.root);
929
- const manifest = await readManifest(paths);
1058
+ const manifest = await readManifest2(paths);
930
1059
  return { repositoryRoot, paths, manifest };
931
1060
  }
932
1061
  async function importDerivedSessions(paths, manifest, options, sourceKind, candidates, projectPaths, boundaryDeclared) {
@@ -1173,7 +1302,7 @@ async function selectTranscriptFiles(projectsRoot, projectPaths, options) {
1173
1302
  }
1174
1303
  async function pathExists(file) {
1175
1304
  try {
1176
- await stat(file);
1305
+ await stat2(file);
1177
1306
  return true;
1178
1307
  } catch (error) {
1179
1308
  if (findErrorCode2(error, "ENOENT")) return false;
@@ -1182,7 +1311,7 @@ async function pathExists(file) {
1182
1311
  }
1183
1312
  async function statSize(file) {
1184
1313
  try {
1185
- return (await stat(file)).size;
1314
+ return (await stat2(file)).size;
1186
1315
  } catch (error) {
1187
1316
  if (findErrorCode2(error, "ENOENT")) return void 0;
1188
1317
  throw error;
@@ -1374,18 +1503,6 @@ function shortId2(id) {
1374
1503
  }
1375
1504
  return id.slice(0, SHORT_ID_LEN2);
1376
1505
  }
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
1506
  async function assertWorkspaceInitialized2(basouRoot) {
1390
1507
  try {
1391
1508
  await assertBasouRootSafe2(basouRoot);
@@ -1578,111 +1695,6 @@ async function probeStaleness(args) {
1578
1695
  }
1579
1696
  }
1580
1697
 
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
1698
  // src/commands/board.ts
1687
1699
  var DEFAULT_BOARD_PATH = "board/board.yaml";
1688
1700
  function registerBoardCommand(program) {
@@ -2479,7 +2491,6 @@ import {
2479
2491
  prefixedUlid as prefixedUlid2,
2480
2492
  readManifest as readManifest4,
2481
2493
  replayEvents as replayEvents2,
2482
- resolveRepositoryRoot as resolveRepositoryRoot3,
2483
2494
  resolveSessionId,
2484
2495
  sanitizePath
2485
2496
  } from "@basou/core";
@@ -2762,7 +2773,7 @@ async function warnLinkedFilesOutsideRoots(input) {
2762
2773
  }
2763
2774
  async function doRunDecisionRecord(options, ctx) {
2764
2775
  const cwd = ctx.cwd ?? process.cwd();
2765
- const repositoryRoot = await resolveRepositoryRootForDecision(cwd);
2776
+ const repositoryRoot = await resolveBasouRootForCommand(cwd, "decision record");
2766
2777
  const paths = basouPaths6(repositoryRoot);
2767
2778
  await assertWorkspaceInitialized4(paths.root);
2768
2779
  const now = ctx.nowProvider !== void 0 ? ctx.nowProvider() : /* @__PURE__ */ new Date();
@@ -3409,19 +3420,6 @@ function printDecisionResult(options, result) {
3409
3420
  );
3410
3421
  }
3411
3422
  }
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
3423
  async function assertWorkspaceInitialized4(basouRoot) {
3426
3424
  try {
3427
3425
  await assertBasouRootSafe4(basouRoot);
@@ -3441,7 +3439,6 @@ import {
3441
3439
  readMarkdownFile as readMarkdownFile3,
3442
3440
  renderDecisions as renderDecisions2,
3443
3441
  renderWithMarkers as renderWithMarkers2,
3444
- resolveRepositoryRoot as resolveRepositoryRoot4,
3445
3442
  writeMarkdownFile as writeMarkdownFile2
3446
3443
  } from "@basou/core";
3447
3444
  function registerDecisionsCommand(program) {
@@ -3461,7 +3458,7 @@ async function runDecisionsGenerate(options, ctx = {}) {
3461
3458
  async function doRunDecisionsGenerate(options, ctx) {
3462
3459
  void options;
3463
3460
  const cwd = ctx.cwd ?? process.cwd();
3464
- const repositoryRoot = await resolveRepositoryRootForDecisions(cwd);
3461
+ const repositoryRoot = await resolveBasouRootForCommand(cwd, "decisions generate");
3465
3462
  const paths = basouPaths7(repositoryRoot);
3466
3463
  await assertWorkspaceInitialized5(paths.root);
3467
3464
  const nowIso = (ctx.nowProvider?.() ?? /* @__PURE__ */ new Date()).toISOString();
@@ -3476,19 +3473,6 @@ async function doRunDecisionsGenerate(options, ctx) {
3476
3473
  await writeMarkdownFile2(paths.files.decisions, finalBody);
3477
3474
  console.log(`Generated .basou/decisions.md (decisions: ${result.decisionCount})`);
3478
3475
  }
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
3476
  async function assertWorkspaceInitialized5(basouRoot) {
3493
3477
  try {
3494
3478
  await assertBasouRootSafe5(basouRoot);
@@ -3519,7 +3503,6 @@ import {
3519
3503
  prefixedUlid as prefixedUlid3,
3520
3504
  readManifest as readManifest5,
3521
3505
  readYamlFile as readYamlFile2,
3522
- resolveRepositoryRoot as resolveRepositoryRoot5,
3523
3506
  SESSION_SCHEMA_VERSION,
3524
3507
  SessionSchema,
3525
3508
  sanitizeWorkingDirectory,
@@ -3542,7 +3525,7 @@ async function runExec(command, args, options, ctx = {}) {
3542
3525
  const now = ctx.now ?? (() => /* @__PURE__ */ new Date());
3543
3526
  const cwd = options.cwd ?? process.cwd();
3544
3527
  const timeout_ms = options.timeout !== void 0 ? parseDuration(options.timeout) : void 0;
3545
- const repoRoot = await resolveRepositoryRootForExec(cwd);
3528
+ const repoRoot = await resolveRepositoryRootInPlace(cwd, "exec");
3546
3529
  const paths = basouPaths8(repoRoot);
3547
3530
  await assertBasouRootSafe6(paths.root);
3548
3531
  const manifest = await readManifest5(paths);
@@ -3840,18 +3823,6 @@ async function finalizeSessionAsFailed(paths, sessionDir, sessionId, appendEvent
3840
3823
  s.session.invocation.exit_code = null;
3841
3824
  });
3842
3825
  }
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
3826
 
3856
3827
  // src/commands/handoff.ts
3857
3828
  import {
@@ -5439,7 +5410,6 @@ import {
5439
5410
  appendBasouGitignore,
5440
5411
  createManifest,
5441
5412
  ensureBasouDirectory,
5442
- resolveRepositoryRoot as resolveRepositoryRoot6,
5443
5413
  writeManifest
5444
5414
  } from "@basou/core";
5445
5415
  function collectValue(value, previous) {
@@ -5471,7 +5441,7 @@ async function runInit(options, ctx = {}) {
5471
5441
  }
5472
5442
  async function doRunInit(options, ctx) {
5473
5443
  const cwd = ctx.cwd ?? process.cwd();
5474
- const repositoryRoot = await resolveRepositoryRootForInit(cwd);
5444
+ const repositoryRoot = await resolveRepositoryRootInPlace(cwd, "init");
5475
5445
  const workspaceName = options.name ?? basename6(repositoryRoot);
5476
5446
  if (options.repoUrl !== void 0) {
5477
5447
  console.error(
@@ -5507,18 +5477,6 @@ function renderGitignoreWarning(error, verbose) {
5507
5477
  if (label !== void 0) console.error(`Caused by: ${label}`);
5508
5478
  }
5509
5479
  }
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
5480
 
5523
5481
  // src/commands/note.ts
5524
5482
  import { readFile as readFile5 } from "fs/promises";
@@ -5918,7 +5876,6 @@ import {
5918
5876
  renderAnchorStarter,
5919
5877
  renderViewPresetBlock,
5920
5878
  renderWithMarkers as renderWithMarkers4,
5921
- resolveRepositoryRoot as resolveRepositoryRoot7,
5922
5879
  safeSimpleGit,
5923
5880
  seedMarkers,
5924
5881
  summarizePresetPlan,
@@ -8464,19 +8421,6 @@ async function runProjectNew(repos, options, ctx = {}) {
8464
8421
  process.exitCode = 1;
8465
8422
  }
8466
8423
  }
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
8424
  function validateProjectName(name) {
8481
8425
  if (!/^[A-Za-z0-9][A-Za-z0-9._-]*$/.test(name)) {
8482
8426
  throw new Error(
@@ -8487,7 +8431,7 @@ function validateProjectName(name) {
8487
8431
  }
8488
8432
  async function doRunProjectNew(repos, options, ctx) {
8489
8433
  const cwd = ctx.cwd ?? process.cwd();
8490
- const repositoryRoot = await resolveRepositoryRootForNew(cwd);
8434
+ const repositoryRoot = await resolveRepositoryRootInPlace(cwd, "project new");
8491
8435
  const workspaceName = basename7(repositoryRoot);
8492
8436
  const productName = options.projectName !== void 0 ? validateProjectName(options.projectName) : void 0;
8493
8437
  const viewStem = productName ?? workspaceName;
@@ -9694,7 +9638,6 @@ import {
9694
9638
  basouPaths as basouPaths15,
9695
9639
  findErrorCode as findErrorCode11,
9696
9640
  renderReport,
9697
- resolveRepositoryRoot as resolveRepositoryRoot8,
9698
9641
  writeMarkdownFile as writeMarkdownFile6
9699
9642
  } from "@basou/core";
9700
9643
  function registerReportCommand(program) {
@@ -9715,7 +9658,7 @@ async function runReportGenerate(options, ctx = {}) {
9715
9658
  }
9716
9659
  async function doRunReportGenerate(options, ctx) {
9717
9660
  const cwd = ctx.cwd ?? process.cwd();
9718
- const repositoryRoot = await resolveRepositoryRootForReport(cwd);
9661
+ const repositoryRoot = await resolveBasouRootForCommand(cwd, "report generate");
9719
9662
  const paths = basouPaths15(repositoryRoot);
9720
9663
  await assertWorkspaceInitialized10(paths.root);
9721
9664
  const nowIso = (ctx.nowProvider?.() ?? /* @__PURE__ */ new Date()).toISOString();
@@ -9741,19 +9684,6 @@ async function doRunReportGenerate(options, ctx) {
9741
9684
  console.log(result.body);
9742
9685
  }
9743
9686
  }
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
9687
  async function assertWorkspaceInitialized10(basouRoot) {
9758
9688
  try {
9759
9689
  await assertBasouRootSafe11(basouRoot);
@@ -10226,7 +10156,6 @@ import {
10226
10156
  readYamlFile as readYamlFile5,
10227
10157
  resolveClaudeCodeCommand,
10228
10158
  resolveCodexCommand,
10229
- resolveRepositoryRoot as resolveRepositoryRoot9,
10230
10159
  SESSION_SCHEMA_VERSION as SESSION_SCHEMA_VERSION2,
10231
10160
  SessionSchema as SessionSchema2,
10232
10161
  sanitizeRelatedFiles,
@@ -10281,7 +10210,7 @@ async function runTrackedTool(args, options, ctx, adapter) {
10281
10210
  const { command } = await adapter.resolveCommand();
10282
10211
  const childArgs = adapter.transformArgs ? adapter.transformArgs(args) : args;
10283
10212
  const cwd = options.cwd ?? process.cwd();
10284
- const repoRoot = await resolveRepositoryRootForRun(cwd);
10213
+ const repoRoot = await resolveRepositoryRootInPlace(cwd, "run");
10285
10214
  const paths = basouPaths18(repoRoot);
10286
10215
  await assertBasouRootSafe13(paths.root);
10287
10216
  const manifest = await readManifest11(paths);
@@ -10650,18 +10579,6 @@ async function finalizeSessionAsFailed2(paths, sessionDir, sessionId, appendEven
10650
10579
  s.session.invocation.exit_code = null;
10651
10580
  });
10652
10581
  }
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
10582
  async function noteCodexHookStatusPreSpawn(_cwd, ctx) {
10666
10583
  const hooksPath = ctx.codexHooksPath ?? DEFAULT_CODEX_HOOKS_PATH;
10667
10584
  let location;
@@ -11388,8 +11305,7 @@ import {
11388
11305
  assertBasouRootSafe as assertBasouRootSafe15,
11389
11306
  basouPaths as basouPaths20,
11390
11307
  computeWorkStats,
11391
- findErrorCode as findErrorCode14,
11392
- resolveRepositoryRoot as resolveRepositoryRoot10
11308
+ findErrorCode as findErrorCode14
11393
11309
  } from "@basou/core";
11394
11310
  function registerStatsCommand(program) {
11395
11311
  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 +11322,7 @@ async function runStats(options, ctx = {}) {
11406
11322
  }
11407
11323
  async function doRunStats(options, ctx) {
11408
11324
  const cwd = ctx.cwd ?? process.cwd();
11409
- const repositoryRoot = await resolveRepositoryRootForStats(cwd);
11325
+ const repositoryRoot = await resolveBasouRootForCommand(cwd, "stats");
11410
11326
  const paths = basouPaths20(repositoryRoot);
11411
11327
  await assertWorkspaceInitialized13(paths.root);
11412
11328
  const now = ctx.nowProvider?.() ?? /* @__PURE__ */ new Date();
@@ -11494,18 +11410,6 @@ function formatFloorMs(ms) {
11494
11410
  function formatInt(n) {
11495
11411
  return n.toLocaleString("en-US");
11496
11412
  }
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
11413
  async function assertWorkspaceInitialized13(basouRoot) {
11510
11414
  try {
11511
11415
  await assertBasouRootSafe15(basouRoot);
@@ -11524,7 +11428,6 @@ import {
11524
11428
  buildStatusSnapshot,
11525
11429
  findErrorCode as findErrorCode15,
11526
11430
  readManifest as readManifest13,
11527
- resolveRepositoryRoot as resolveRepositoryRoot11,
11528
11431
  writeStatus
11529
11432
  } from "@basou/core";
11530
11433
  function registerStatusCommand(program) {
@@ -11542,7 +11445,7 @@ async function runStatus(options, ctx = {}) {
11542
11445
  }
11543
11446
  async function doRunStatus(options, ctx) {
11544
11447
  const cwd = ctx.cwd ?? process.cwd();
11545
- const repositoryRoot = await resolveRepositoryRootForStatus(cwd);
11448
+ const repositoryRoot = await resolveBasouRootForCommand(cwd, "status");
11546
11449
  const paths = basouPaths21(repositoryRoot);
11547
11450
  try {
11548
11451
  await assertBasouRootSafe16(paths.root);
@@ -11580,18 +11483,6 @@ function renderTextStatus(s) {
11580
11483
  const present = Object.values(dp).filter((v) => v === true).length;
11581
11484
  console.log(`Subdirectories present: ${present}/${total}`);
11582
11485
  }
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
11486
  function formatVersionGateMessage(error) {
11596
11487
  const issues = error.issues;
11597
11488
  if (!Array.isArray(issues)) return void 0;
@@ -12733,7 +12624,6 @@ import {
12733
12624
  basouPaths as basouPaths23,
12734
12625
  enumerateSessionEntries as enumerateSessionEntries3,
12735
12626
  findErrorCode as findErrorCode17,
12736
- resolveRepositoryRoot as resolveRepositoryRoot12,
12737
12627
  resolveSessionId as resolveSessionId5,
12738
12628
  verifyEventsChain
12739
12629
  } from "@basou/core";
@@ -12762,7 +12652,7 @@ async function doRunVerify(options, ctx) {
12762
12652
  throw new Error("Specify either --session <id> or --all, not both");
12763
12653
  }
12764
12654
  const cwd = ctx.cwd ?? process.cwd();
12765
- const repositoryRoot = await resolveRepositoryRootForVerify(cwd);
12655
+ const repositoryRoot = await resolveBasouRootForCommand(cwd, "verify");
12766
12656
  const paths = basouPaths23(repositoryRoot);
12767
12657
  await assertWorkspaceInitialized15(paths.root);
12768
12658
  const sessionIds = options.session !== void 0 ? [await resolveSessionId5(paths, options.session, { allowNotDirectory: true })] : await enumerateAllSessionNames2(paths);
@@ -12816,18 +12706,6 @@ async function enumerateAllSessionNames2(paths) {
12816
12706
  const { dirs, notDirectories } = await enumerateSessionEntries3(paths);
12817
12707
  return [...dirs, ...notDirectories].sort();
12818
12708
  }
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
12709
  async function assertWorkspaceInitialized15(basouRoot) {
12832
12710
  try {
12833
12711
  await assertBasouRootSafe18(basouRoot);
@@ -12848,8 +12726,7 @@ import {
12848
12726
  basouPaths as basouPaths25,
12849
12727
  findErrorCode as findErrorCode19,
12850
12728
  loadPortfolioConfig as loadPortfolioConfig6,
12851
- readManifest as readManifest18,
12852
- resolveRepositoryRoot as resolveRepositoryRoot14
12729
+ readManifest as readManifest18
12853
12730
  } from "@basou/core";
12854
12731
  import { InvalidArgumentError as InvalidArgumentError8 } from "commander";
12855
12732
 
@@ -12859,12 +12736,15 @@ import { readdir as readdir3, stat as stat7 } from "fs/promises";
12859
12736
  import { homedir as homedir12 } from "os";
12860
12737
  import { basename as basename9, dirname as dirname7, join as join16 } from "path";
12861
12738
  import { createInterface as createInterface2 } from "readline";
12862
- import { basouPaths as basouPaths24, readManifest as readManifest15, resolveRepositoryRoot as resolveRepositoryRoot13 } from "@basou/core";
12739
+ import { basouPaths as basouPaths24, readManifest as readManifest15 } from "@basou/core";
12863
12740
  function uncapturedTotal(result) {
12864
12741
  return result.groups.reduce((sum, g) => sum + g.logs, 0) + result.unplaceable;
12865
12742
  }
12866
12743
  async function checkPortfolioCoverage(workspaces, ctx = {}) {
12867
- const { roots, inertWorkspaces } = await collectDeclaredRoots(workspaces);
12744
+ const { roots, inertWorkspaces } = await collectDeclaredRoots(
12745
+ workspaces,
12746
+ ctx.portfolioConfigPath
12747
+ );
12868
12748
  const listedDirs = new Set([...roots].map((root) => encodeProjectDir(root)));
12869
12749
  const claudeProjectsDir = ctx.claudeProjectsDir ?? join16(homedir12(), ".claude", "projects");
12870
12750
  const codexSessionsDir = ctx.codexSessionsDir ?? join16(homedir12(), ".codex", "sessions");
@@ -12889,13 +12769,16 @@ async function checkPortfolioCoverage(workspaces, ctx = {}) {
12889
12769
  }
12890
12770
  return tally.finish(absentTrees, inertWorkspaces);
12891
12771
  }
12892
- async function collectDeclaredRoots(workspaces) {
12772
+ async function collectDeclaredRoots(workspaces, portfolioConfigPath) {
12893
12773
  const roots = /* @__PURE__ */ new Set();
12894
12774
  const inertWorkspaces = [];
12895
12775
  for (const ws of workspaces) {
12896
12776
  let importRoot;
12897
12777
  try {
12898
- importRoot = await resolveRepositoryRoot13(ws.repoRoot);
12778
+ importRoot = await resolveBasouRootForCommand(ws.repoRoot, "import", {
12779
+ quiet: true,
12780
+ ...portfolioConfigPath !== void 0 ? { portfolioConfigPath } : {}
12781
+ });
12899
12782
  } catch {
12900
12783
  inertWorkspaces.push({ path: ws.repoRoot, reason: "not_a_git_repo" });
12901
12784
  continue;
@@ -15009,9 +14892,13 @@ async function handleWorkspaceGet(res, sub, ws, nowProvider, resolveRemoteUrl, b
15009
14892
  }
15010
14893
  return false;
15011
14894
  }
14895
+ var IMPORT_SUBS = /* @__PURE__ */ new Set(["import/claude-code", "import/codex"]);
15012
14896
  async function handleWorkspacePost(res, sub, ws, body, deps, runExclusive) {
15013
14897
  const nowIso = deps.nowProvider().toISOString();
15014
14898
  const actionOptions = readActionOptions(body);
14899
+ if (!ws.initialized && (sub === "refresh" || IMPORT_SUBS.has(sub))) {
14900
+ throw new Error("Workspace not initialized. Run 'basou init' first.");
14901
+ }
15015
14902
  if (sub === "refresh") {
15016
14903
  const result = await runExclusive(
15017
14904
  () => refreshAll({ options: actionOptions, ctx: ws.importCtx, paths: ws.paths, nowIso })
@@ -15388,7 +15275,8 @@ async function doRunView(options, ctx) {
15388
15275
  if (isWholeRegistry) {
15389
15276
  const coverage = await checkPortfolioCoverage(deps.workspaces, {
15390
15277
  ...ctx.claudeProjectsDir !== void 0 ? { claudeProjectsDir: ctx.claudeProjectsDir } : {},
15391
- ...ctx.codexSessionsDir !== void 0 ? { codexSessionsDir: ctx.codexSessionsDir } : {}
15278
+ ...ctx.codexSessionsDir !== void 0 ? { codexSessionsDir: ctx.codexSessionsDir } : {},
15279
+ ...ctx.portfolioConfigPath !== void 0 ? { portfolioConfigPath: ctx.portfolioConfigPath } : {}
15392
15280
  });
15393
15281
  console.log("");
15394
15282
  for (const line of formatCoverageReport(coverage)) console.log(line);
@@ -15431,7 +15319,7 @@ async function doRunView(options, ctx) {
15431
15319
  }
15432
15320
  }
15433
15321
  async function buildSingleDeps(ctx, cwd) {
15434
- const repositoryRoot = await resolveRepositoryRootForView(cwd);
15322
+ const repositoryRoot = await resolveBasouRootForCommand(cwd, "view");
15435
15323
  const paths = basouPaths25(repositoryRoot);
15436
15324
  await assertWorkspaceInitialized16(paths.root);
15437
15325
  const entry = await buildWorkspaceEntry(repositoryRoot, ctx);
@@ -15562,18 +15450,6 @@ function waitForShutdown(signal) {
15562
15450
  }
15563
15451
  });
15564
15452
  }
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
15453
  async function assertWorkspaceInitialized16(basouRoot) {
15578
15454
  try {
15579
15455
  await assertBasouRootSafe19(basouRoot);
@@ -15617,7 +15493,7 @@ function installExperimentalNotice(program, names = EXPERIMENTAL_COMMANDS, write
15617
15493
  function readBuildStamp() {
15618
15494
  if (false) return void 0;
15619
15495
  try {
15620
- return JSON.parse('{"version":"0.65.0","commit":"a93e199","committedAt":"2026-10-08T07:48:03+09:00"}');
15496
+ return JSON.parse('{"version":"0.66.0","commit":"2cd6700","committedAt":"2026-10-08T13:10:32+09:00"}');
15621
15497
  } catch {
15622
15498
  return void 0;
15623
15499
  }