@basou/core 0.16.0 → 0.18.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -11,10 +11,10 @@ async function resolveClaudeCodeCommand(lookup = isOnPath) {
11
11
  throw new Error("Claude Code CLI not found in PATH. Install claude-code (or claude) first.");
12
12
  }
13
13
  async function isOnPath(command) {
14
- return new Promise((resolve2) => {
14
+ return new Promise((resolve3) => {
15
15
  const child = spawn("which", [command], { stdio: "ignore" });
16
- child.on("error", () => resolve2(false));
17
- child.on("exit", (code) => resolve2(code === 0));
16
+ child.on("error", () => resolve3(false));
17
+ child.on("exit", (code) => resolve3(code === 0));
18
18
  });
19
19
  }
20
20
  function summarizeAdapterOutput(_stream, _raw) {
@@ -874,6 +874,12 @@ var DecisionRecordedEventSchema = BaseEventSchema.extend({
874
874
  linked_events: z3.array(EventIdSchema).optional(),
875
875
  linked_files: z3.array(z3.string().min(1).max(4096)).optional()
876
876
  });
877
+ var DecisionVoidedEventSchema = BaseEventSchema.extend({
878
+ type: z3.literal("decision_voided"),
879
+ decision_id: DecisionIdSchema,
880
+ reason: z3.string().nullable().optional(),
881
+ superseded_by: DecisionIdSchema.optional()
882
+ });
877
883
  var TaskCreatedEventSchema = BaseEventSchema.extend({
878
884
  type: z3.literal("task_created"),
879
885
  task_id: TaskIdSchema,
@@ -937,6 +943,7 @@ var EventSchema = z3.discriminatedUnion("type", [
937
943
  GitSnapshotEventSchema,
938
944
  FileChangedEventSchema,
939
945
  DecisionRecordedEventSchema,
946
+ DecisionVoidedEventSchema,
940
947
  TaskCreatedEventSchema,
941
948
  TaskStatusChangedEventSchema,
942
949
  TaskReconciledEventSchema,
@@ -1460,6 +1467,7 @@ async function renderDecisions(input) {
1460
1467
  if (input.onWarning !== void 0) loadOpts.onWarning = input.onWarning;
1461
1468
  const entries = await loadSessionEntries(input.paths, loadOpts);
1462
1469
  const decisions = [];
1470
+ const voids = /* @__PURE__ */ new Map();
1463
1471
  const knownEventIds = /* @__PURE__ */ new Set();
1464
1472
  for (const entry of entries) {
1465
1473
  const sessionDir = join6(input.paths.sessions, entry.sessionId);
@@ -1478,8 +1486,11 @@ async function renderDecisions(input) {
1478
1486
  alternatives: ev.alternatives,
1479
1487
  rejectedReason: ev.rejected_reason,
1480
1488
  linkedEvents: ev.linked_events,
1481
- linkedFiles: ev.linked_files
1489
+ linkedFiles: ev.linked_files,
1490
+ voided: void 0
1482
1491
  });
1492
+ } else if (ev.type === "decision_voided") {
1493
+ voids.set(ev.decision_id, { reason: ev.reason, supersededBy: ev.superseded_by });
1483
1494
  }
1484
1495
  }
1485
1496
  } catch {
@@ -1488,6 +1499,10 @@ async function renderDecisions(input) {
1488
1499
  }
1489
1500
  }
1490
1501
  }
1502
+ for (const d of decisions) {
1503
+ const v = voids.get(d.decisionId);
1504
+ if (v !== void 0) d.voided = v;
1505
+ }
1491
1506
  decisions.sort((a, b) => {
1492
1507
  const c = Date.parse(a.occurredAt) - Date.parse(b.occurredAt);
1493
1508
  return c !== 0 ? c : a.decisionId.localeCompare(b.decisionId);
@@ -1527,8 +1542,16 @@ async function formatDecisionsBody(args) {
1527
1542
  return lines.join("\n");
1528
1543
  }
1529
1544
  for (const d of args.decisions) {
1530
- lines.push(`## ${d.decisionId}: ${d.title}`);
1531
- lines.push("");
1545
+ if (d.voided !== void 0) {
1546
+ lines.push(`## ~~${d.decisionId}: ${d.title}~~ [VOIDED]`);
1547
+ lines.push("");
1548
+ const supersededBy = d.voided.supersededBy !== void 0 ? `, superseded by ${d.voided.supersededBy}` : "";
1549
+ const reason = typeof d.voided.reason === "string" && d.voided.reason.length > 0 ? `: ${d.voided.reason}` : "";
1550
+ lines.push(`- \u26A0 VOIDED${reason}${supersededBy}`);
1551
+ } else {
1552
+ lines.push(`## ${d.decisionId}: ${d.title}`);
1553
+ lines.push("");
1554
+ }
1532
1555
  const occurredDate = d.occurredAt.slice(0, 10);
1533
1556
  lines.push(`- \u6C7A\u5B9A\u65E5: ${occurredDate}`);
1534
1557
  lines.push(`- session: ${shortDecisionSessionId(d.sessionId)}`);
@@ -3853,6 +3876,7 @@ async function renderHandoff(input) {
3853
3876
  if (input.onWarning !== void 0) loadOpts.onWarning = input.onWarning;
3854
3877
  const entries = await loadSessionEntries(input.paths, loadOpts);
3855
3878
  const decisions = [];
3879
+ const voidedDecisionIds = /* @__PURE__ */ new Set();
3856
3880
  const tasksCreated = [];
3857
3881
  const tasksStatusChanged = [];
3858
3882
  let latestActivityAt = null;
@@ -3877,6 +3901,8 @@ async function renderHandoff(input) {
3877
3901
  occurredAt: ev.occurred_at,
3878
3902
  sessionId: entry.sessionId
3879
3903
  });
3904
+ } else if (ev.type === "decision_voided") {
3905
+ voidedDecisionIds.add(ev.decision_id);
3880
3906
  } else if (ev.type === "task_created") {
3881
3907
  tasksCreated.push({
3882
3908
  taskId: ev.task_id,
@@ -3902,6 +3928,14 @@ async function renderHandoff(input) {
3902
3928
  const c = Date.parse(a.occurredAt) - Date.parse(b.occurredAt);
3903
3929
  return c !== 0 ? c : a.decisionId.localeCompare(b.decisionId);
3904
3930
  });
3931
+ let latestDecision;
3932
+ for (let i = decisions.length - 1; i >= 0; i -= 1) {
3933
+ const d = decisions[i];
3934
+ if (d !== void 0 && !voidedDecisionIds.has(d.decisionId)) {
3935
+ latestDecision = d;
3936
+ break;
3937
+ }
3938
+ }
3905
3939
  tasksCreated.sort((a, b) => {
3906
3940
  const c = Date.parse(a.occurredAt) - Date.parse(b.occurredAt);
3907
3941
  return c !== 0 ? c : a.taskId.localeCompare(b.taskId);
@@ -3945,6 +3979,7 @@ async function renderHandoff(input) {
3945
3979
  latestSession,
3946
3980
  latestActivityAt,
3947
3981
  decisions,
3982
+ latestDecision,
3948
3983
  pendingApprovalsCount,
3949
3984
  suspectCount,
3950
3985
  displayedFiles,
@@ -4011,10 +4046,10 @@ function formatHandoffBody(args) {
4011
4046
  lines.push("");
4012
4047
  lines.push("## \u76F4\u8FD1\u306E\u5224\u65AD");
4013
4048
  lines.push("");
4014
- if (args.decisions.length === 0) {
4049
+ if (args.latestDecision === void 0) {
4015
4050
  lines.push("(no decisions recorded yet)");
4016
4051
  } else {
4017
- const last = args.decisions[args.decisions.length - 1];
4052
+ const last = args.latestDecision;
4018
4053
  lines.push(`- ${last.title} [${shortIdWithPrefix(last.decisionId)}]`);
4019
4054
  if (args.latestActivityAt !== null && isTrailingStale(args.latestActivityAt, last.occurredAt)) {
4020
4055
  lines.push(
@@ -4231,8 +4266,82 @@ async function resolveIdInternal(paths, input, kind, options = {}) {
4231
4266
  return matches[0];
4232
4267
  }
4233
4268
 
4269
+ // src/lib/source-root-scope.ts
4270
+ import { promises as fs } from "fs";
4271
+ import { homedir as osHomedir } from "os";
4272
+ import { basename as basename2, dirname as dirname3, isAbsolute, join as join14, normalize, relative, resolve as resolve2 } from "path";
4273
+ var AGENT_INFRA_DIRS = ["~/.claude", "~/.codex", "~/.basou"];
4274
+ async function realpathBestEffort(absPath) {
4275
+ let current = normalize(absPath);
4276
+ const tail = [];
4277
+ for (let guard = 0; guard < 4096; guard += 1) {
4278
+ try {
4279
+ const real = await fs.realpath(current);
4280
+ return tail.length > 0 ? join14(real, ...tail.reverse()) : real;
4281
+ } catch (error) {
4282
+ const code = error?.code;
4283
+ if (code !== "ENOENT" && code !== "ENOTDIR") {
4284
+ return normalize(absPath);
4285
+ }
4286
+ const parent = dirname3(current);
4287
+ if (parent === current) return normalize(absPath);
4288
+ tail.push(basename2(current));
4289
+ current = parent;
4290
+ }
4291
+ }
4292
+ return normalize(absPath);
4293
+ }
4294
+ function expandTilde(p, homedir4) {
4295
+ if (p === "~") return homedir4;
4296
+ if (p.startsWith("~/")) return join14(homedir4, p.slice(2));
4297
+ return p;
4298
+ }
4299
+ function toAbsolute(p, workingDirAbs, homedir4) {
4300
+ const expanded = expandTilde(p, homedir4);
4301
+ if (isAbsolute(expanded)) return normalize(expanded);
4302
+ return normalize(resolve2(workingDirAbs, expanded));
4303
+ }
4304
+ function isUnder(child, parent) {
4305
+ if (child === parent) return true;
4306
+ const rel = relative(parent, child);
4307
+ return rel !== "" && !rel.startsWith("..") && !isAbsolute(rel);
4308
+ }
4309
+ async function classifyFilesBySourceRoot(input) {
4310
+ const inRoot = [];
4311
+ const outOfRoot = [];
4312
+ if (input.files.length === 0) return { inRoot, outOfRoot };
4313
+ const homedir4 = input.homedir ?? osHomedir();
4314
+ const workingDirAbs = toAbsolute(input.workingDirectory, homedir4, homedir4);
4315
+ const declared = input.sourceRoots && input.sourceRoots.length > 0 ? [...input.sourceRoots] : ["."];
4316
+ const rootsAbs = [];
4317
+ for (const r of declared) {
4318
+ const expanded = expandTilde(r, homedir4);
4319
+ const abs = isAbsolute(expanded) ? normalize(expanded) : normalize(resolve2(input.masterRoot, expanded));
4320
+ rootsAbs.push(await realpathBestEffort(abs));
4321
+ }
4322
+ for (const e of input.extraInRoot ?? []) {
4323
+ const expanded = expandTilde(e, homedir4);
4324
+ const abs = isAbsolute(expanded) ? normalize(expanded) : normalize(resolve2(homedir4, expanded));
4325
+ rootsAbs.push(await realpathBestEffort(abs));
4326
+ }
4327
+ if (rootsAbs.length === 0) {
4328
+ return { inRoot: [...input.files], outOfRoot };
4329
+ }
4330
+ for (const file of input.files) {
4331
+ try {
4332
+ const abs = toAbsolute(file, workingDirAbs, homedir4);
4333
+ const real = await realpathBestEffort(abs);
4334
+ const within = rootsAbs.some((root) => isUnder(real, root));
4335
+ (within ? inRoot : outOfRoot).push(file);
4336
+ } catch {
4337
+ inRoot.push(file);
4338
+ }
4339
+ }
4340
+ return { inRoot, outOfRoot };
4341
+ }
4342
+
4234
4343
  // src/orientation/orientation-renderer.ts
4235
- import { join as join14 } from "path";
4344
+ import { dirname as dirname4, join as join15 } from "path";
4236
4345
 
4237
4346
  // src/storage/manifest.ts
4238
4347
  import { lstat as lstat3 } from "fs/promises";
@@ -4393,6 +4502,7 @@ async function summarizeOrientation(input) {
4393
4502
  }
4394
4503
  ) : await loadSessionEntries(input.paths, loadOpts);
4395
4504
  const decisions = [];
4505
+ const voidedDecisionIds = /* @__PURE__ */ new Set();
4396
4506
  let latestActivityAt = null;
4397
4507
  let latestNote = null;
4398
4508
  const noteActivity = (iso) => {
@@ -4401,7 +4511,7 @@ async function summarizeOrientation(input) {
4401
4511
  }
4402
4512
  };
4403
4513
  for (const entry of entries) {
4404
- const sessionDir = join14(entry.sourceRoot.sessions, entry.sessionId);
4514
+ const sessionDir = join15(entry.sourceRoot.sessions, entry.sessionId);
4405
4515
  const counted = entry.session.session.status !== "archived";
4406
4516
  if (counted) noteActivity(entry.session.session.ended_at ?? entry.session.session.started_at);
4407
4517
  try {
@@ -4416,6 +4526,8 @@ async function summarizeOrientation(input) {
4416
4526
  sessionId: entry.sessionId,
4417
4527
  host: entry.host
4418
4528
  });
4529
+ } else if (ev.type === "decision_voided") {
4530
+ voidedDecisionIds.add(ev.decision_id);
4419
4531
  }
4420
4532
  if (counted && ev.type === "note_added" && ev.kind === "next_step") {
4421
4533
  if (latestNote === null || Date.parse(ev.occurred_at) > Date.parse(latestNote.occurredAt)) {
@@ -4437,7 +4549,14 @@ async function summarizeOrientation(input) {
4437
4549
  const c = Date.parse(a.occurredAt) - Date.parse(b.occurredAt);
4438
4550
  return c !== 0 ? c : a.decisionId.localeCompare(b.decisionId);
4439
4551
  });
4440
- const latestDecision = decisions[decisions.length - 1];
4552
+ let latestDecision;
4553
+ for (let i = decisions.length - 1; i >= 0; i -= 1) {
4554
+ const d = decisions[i];
4555
+ if (d !== void 0 && !voidedDecisionIds.has(d.decisionId)) {
4556
+ latestDecision = d;
4557
+ break;
4558
+ }
4559
+ }
4441
4560
  const taskLoadOpts = {};
4442
4561
  if (input.onTaskSkip !== void 0) taskLoadOpts.onSkip = input.onTaskSkip;
4443
4562
  const taskEntries = await loadTaskEntries(input.paths, taskLoadOpts);
@@ -4499,8 +4618,24 @@ async function summarizeOrientation(input) {
4499
4618
  }
4500
4619
  const latestFiles = latestEntry?.session.session.related_files ?? [];
4501
4620
  const uniqueFiles = new Set(latestFiles);
4502
- const displayed = [...uniqueFiles].sort().slice(0, limit);
4621
+ const sortedFiles = [...uniqueFiles].sort();
4622
+ const displayed = sortedFiles.slice(0, limit);
4503
4623
  const overflow = Math.max(0, uniqueFiles.size - limit);
4624
+ let outOfRoot = [];
4625
+ if (latestEntry !== void 0 && latestEntry.host === null && sortedFiles.length > 0 && sourceRoots !== null && sourceRoots.length > 0) {
4626
+ try {
4627
+ const scope = await classifyFilesBySourceRoot({
4628
+ files: sortedFiles,
4629
+ workingDirectory: latestEntry.session.session.working_directory,
4630
+ sourceRoots,
4631
+ masterRoot: dirname4(input.paths.root),
4632
+ extraInRoot: AGENT_INFRA_DIRS
4633
+ });
4634
+ outOfRoot = scope.outOfRoot;
4635
+ } catch {
4636
+ outOfRoot = [];
4637
+ }
4638
+ }
4504
4639
  const hosts = [
4505
4640
  ...new Set(entries.map((e) => e.host).filter((h) => h !== null))
4506
4641
  ].sort();
@@ -4511,7 +4646,7 @@ async function summarizeOrientation(input) {
4511
4646
  latestDecision: latestDecision ?? null,
4512
4647
  decisionCount: decisions.length,
4513
4648
  latestNote,
4514
- relatedFiles: { displayed, overflow },
4649
+ relatedFiles: { displayed, overflow, outOfRoot },
4515
4650
  inFlightTasks,
4516
4651
  plannedTasks,
4517
4652
  pendingApprovals,
@@ -4594,6 +4729,15 @@ function formatOrientationBody(summary, opts) {
4594
4729
  const shown = summary.relatedFiles.displayed.join(", ");
4595
4730
  const more = summary.relatedFiles.overflow > 0 ? ` (... +${summary.relatedFiles.overflow} more)` : "";
4596
4731
  lines.push(`- \u76F4\u8FD1\u306E\u5909\u66F4\u30D5\u30A1\u30A4\u30EB: ${shown}${more}`);
4732
+ if (summary.relatedFiles.outOfRoot.length > 0) {
4733
+ const OUT_OF_ROOT_DISPLAY = 10;
4734
+ const out = summary.relatedFiles.outOfRoot;
4735
+ const shownOut = out.slice(0, OUT_OF_ROOT_DISPLAY).join(", ");
4736
+ const outMore = out.length > OUT_OF_ROOT_DISPLAY ? ` (... +${out.length - OUT_OF_ROOT_DISPLAY} more)` : "";
4737
+ lines.push(
4738
+ ` - \u26A0 source_roots \u5916 ${out.length} \u4EF6 (\u5225\u30D7\u30ED\u30B8\u30A7\u30AF\u30C8\u306E\u53EF\u80FD\u6027): ${shownOut}${outMore}`
4739
+ );
4740
+ }
4597
4741
  } else {
4598
4742
  lines.push("- \u76F4\u8FD1\u306E\u5909\u66F4\u30D5\u30A1\u30A4\u30EB: (none recorded)");
4599
4743
  }
@@ -5375,10 +5519,10 @@ function planWorkspaceView(facts, existing = [], rosterNames = []) {
5375
5519
  }
5376
5520
 
5377
5521
  // src/report/report-renderer.ts
5378
- import { join as join16 } from "path";
5522
+ import { join as join17 } from "path";
5379
5523
 
5380
5524
  // src/stats/work-stats.ts
5381
- import { join as join15 } from "path";
5525
+ import { join as join16 } from "path";
5382
5526
  function resolveTimeZone(timeZone) {
5383
5527
  if (timeZone !== void 0 && timeZone.length > 0) return timeZone;
5384
5528
  return Intl.DateTimeFormat().resolvedOptions().timeZone;
@@ -5409,7 +5553,7 @@ async function computeWorkStats(input) {
5409
5553
  const events = [];
5410
5554
  let eventsUnreadable = false;
5411
5555
  try {
5412
- for await (const ev of replayEvents(join15(input.paths.sessions, entry.sessionId), {
5556
+ for await (const ev of replayEvents(join16(input.paths.sessions, entry.sessionId), {
5413
5557
  onWarning: (w) => input.onWarning?.(w, entry.sessionId)
5414
5558
  })) {
5415
5559
  events.push(ev);
@@ -5695,14 +5839,17 @@ async function renderReport(input) {
5695
5839
  const stats = await computeWorkStats(statsInput);
5696
5840
  const statsBySession = new Map(stats.sessions.map((s) => [s.sessionId, s]));
5697
5841
  const decisions = [];
5842
+ const voidedDecisionIds = /* @__PURE__ */ new Set();
5698
5843
  for (const entry of entries) {
5699
- const sessionDir = join16(input.paths.sessions, entry.sessionId);
5844
+ const sessionDir = join17(input.paths.sessions, entry.sessionId);
5700
5845
  try {
5701
5846
  for await (const ev of replayEvents(sessionDir, {
5702
5847
  onWarning: (w) => input.onWarning?.(w, entry.sessionId)
5703
5848
  })) {
5704
5849
  if (ev.type === "decision_recorded") {
5705
5850
  decisions.push({ id: ev.decision_id, title: ev.title, occurredAt: ev.occurred_at });
5851
+ } else if (ev.type === "decision_voided") {
5852
+ voidedDecisionIds.add(ev.decision_id);
5706
5853
  }
5707
5854
  }
5708
5855
  } catch {
@@ -5711,6 +5858,9 @@ async function renderReport(input) {
5711
5858
  }
5712
5859
  }
5713
5860
  }
5861
+ for (const d of decisions) {
5862
+ if (voidedDecisionIds.has(d.id)) d.voided = true;
5863
+ }
5714
5864
  decisions.sort((a, b) => {
5715
5865
  const c = Date.parse(a.occurredAt) - Date.parse(b.occurredAt);
5716
5866
  return c !== 0 ? c : a.id.localeCompare(b.id);
@@ -5883,7 +6033,8 @@ function formatReportBody(data) {
5883
6033
  lines.push("");
5884
6034
  }
5885
6035
  for (const d of shown) {
5886
- lines.push(`- ${d.occurredAt.slice(0, 10)} \xB7 ${d.title}`);
6036
+ const voidedTag = d.voided === true ? " (voided)" : "";
6037
+ lines.push(`- ${d.occurredAt.slice(0, 10)} \xB7 ${d.title}${voidedTag}`);
5887
6038
  }
5888
6039
  }
5889
6040
  lines.push("");
@@ -5977,7 +6128,7 @@ function formatInt(n) {
5977
6128
  // src/review/review-gaps.ts
5978
6129
  import { existsSync, realpathSync } from "fs";
5979
6130
  import { homedir as homedir2 } from "os";
5980
- import { basename as basename2, isAbsolute, join as join17 } from "path";
6131
+ import { basename as basename3, isAbsolute as isAbsolute2, join as join18 } from "path";
5981
6132
  function stripQuotes(s) {
5982
6133
  if (s.length >= 2 && (s[0] === '"' && s.at(-1) === '"' || s[0] === "'" && s.at(-1) === "'")) {
5983
6134
  return s.slice(1, -1);
@@ -6001,7 +6152,7 @@ var repoRootCache = /* @__PURE__ */ new Map();
6001
6152
  function isRepoRoot(realPath) {
6002
6153
  const cached = repoRootCache.get(realPath);
6003
6154
  if (cached !== void 0) return cached;
6004
- const result = existsSync(join17(realPath, ".git"));
6155
+ const result = existsSync(join18(realPath, ".git"));
6005
6156
  repoRootCache.set(realPath, result);
6006
6157
  return result;
6007
6158
  }
@@ -6010,7 +6161,7 @@ function normalizeRepoPath(p) {
6010
6161
  let s = stripQuotes(p.trim()).replace(/\/+$/, "");
6011
6162
  if (s.length === 0 || s === "~") return null;
6012
6163
  if (s.startsWith("~/")) s = homedir2() + s.slice(1);
6013
- if (isAbsolute(s)) {
6164
+ if (isAbsolute2(s)) {
6014
6165
  const real = resolveRealpath(s);
6015
6166
  if (real !== null) {
6016
6167
  return isRepoRoot(real) ? real : null;
@@ -6024,7 +6175,7 @@ function normalizeRepoPath(p) {
6024
6175
  }
6025
6176
  function normalizeRepoKey(p) {
6026
6177
  const full = normalizeRepoPath(p);
6027
- return full === null ? null : basename2(full);
6178
+ return full === null ? null : basename3(full);
6028
6179
  }
6029
6180
  function inspectCommand(args) {
6030
6181
  const a = args.join(" ");
@@ -6038,7 +6189,7 @@ function inspectCommand(args) {
6038
6189
  let m;
6039
6190
  while ((m = re.exec(a)) !== null) {
6040
6191
  const f = m[1];
6041
- if (f !== void 0) files.add(basename2(f));
6192
+ if (f !== void 0) files.add(basename3(f));
6042
6193
  }
6043
6194
  }
6044
6195
  return { files: [...files], examinedDiff };
@@ -6055,7 +6206,7 @@ function commitFiles(args) {
6055
6206
  const a = args.join(" ");
6056
6207
  const add = a.match(/git add\s+([^&|;]+)/);
6057
6208
  if (!add?.[1]) return [];
6058
- return add[1].split(/\s+/).filter((t) => /\.[A-Za-z]/.test(t) && !t.startsWith("-")).map((t) => basename2(t));
6209
+ return add[1].split(/\s+/).filter((t) => /\.[A-Za-z]/.test(t) && !t.startsWith("-")).map((t) => basename3(t));
6059
6210
  }
6060
6211
  var REVIEW_SOURCE = "codex-import";
6061
6212
  var DEFAULT_WINDOW_HOURS = 24;
@@ -6071,7 +6222,7 @@ async function findReviewGaps(input) {
6071
6222
  const workUnits = /* @__PURE__ */ new Map();
6072
6223
  const unknownCommits = /* @__PURE__ */ new Map();
6073
6224
  for (const entry of entries) {
6074
- const sessionDir = join17(input.paths.sessions, entry.sessionId);
6225
+ const sessionDir = join18(input.paths.sessions, entry.sessionId);
6075
6226
  const isReview = entry.session.session.source.kind === REVIEW_SOURCE;
6076
6227
  const reviewRepos = /* @__PURE__ */ new Map();
6077
6228
  let reviewEnd = null;
@@ -6120,7 +6271,7 @@ async function findReviewGaps(input) {
6120
6271
  let newestCommit = null;
6121
6272
  for (const [sessionId, byRepo] of workUnits) {
6122
6273
  for (const [repoPath, commits] of byRepo) {
6123
- const label = basename2(repoPath);
6274
+ const label = basename3(repoPath);
6124
6275
  if (scope !== null && !scope.includes(label)) continue;
6125
6276
  const times = commits.map((c) => c.at).sort((a, b) => a - b);
6126
6277
  const first = times[0] ?? null;
@@ -6279,7 +6430,7 @@ var ChildProcessRunner = class {
6279
6430
  if (killTimer !== null) clearTimeout(killTimer);
6280
6431
  options.signal?.removeEventListener("abort", onAbort);
6281
6432
  };
6282
- return new Promise((resolve2, reject) => {
6433
+ return new Promise((resolve3, reject) => {
6283
6434
  child.once("error", (error) => {
6284
6435
  if (settled) return;
6285
6436
  settled = true;
@@ -6291,7 +6442,7 @@ var ChildProcessRunner = class {
6291
6442
  settled = true;
6292
6443
  cleanup();
6293
6444
  const ended_at = /* @__PURE__ */ new Date();
6294
- resolve2({
6445
+ resolve3({
6295
6446
  command: snapshotCommand,
6296
6447
  args: snapshotArgs,
6297
6448
  cwd: snapshotCwd,
@@ -6446,28 +6597,28 @@ function serializeJsonSchema(schema) {
6446
6597
 
6447
6598
  // src/storage/basou-dir.ts
6448
6599
  import { lstat as lstat4, mkdir as mkdir4 } from "fs/promises";
6449
- import { join as join18 } from "path";
6600
+ import { join as join19 } from "path";
6450
6601
  function basouPaths(repositoryRoot) {
6451
- const root = join18(repositoryRoot, ".basou");
6452
- const approvalsBase = join18(root, "approvals");
6602
+ const root = join19(repositoryRoot, ".basou");
6603
+ const approvalsBase = join19(root, "approvals");
6453
6604
  return {
6454
6605
  root,
6455
- sessions: join18(root, "sessions"),
6456
- tasks: join18(root, "tasks"),
6606
+ sessions: join19(root, "sessions"),
6607
+ tasks: join19(root, "tasks"),
6457
6608
  approvals: {
6458
- pending: join18(approvalsBase, "pending"),
6459
- resolved: join18(approvalsBase, "resolved")
6609
+ pending: join19(approvalsBase, "pending"),
6610
+ resolved: join19(approvalsBase, "resolved")
6460
6611
  },
6461
- locks: join18(root, "locks"),
6462
- logs: join18(root, "logs"),
6463
- raw: join18(root, "raw"),
6464
- tmp: join18(root, "tmp"),
6612
+ locks: join19(root, "locks"),
6613
+ logs: join19(root, "logs"),
6614
+ raw: join19(root, "raw"),
6615
+ tmp: join19(root, "tmp"),
6465
6616
  files: {
6466
- manifest: join18(root, "manifest.yaml"),
6467
- status: join18(root, "status.json"),
6468
- handoff: join18(root, "handoff.md"),
6469
- decisions: join18(root, "decisions.md"),
6470
- orientation: join18(root, "orientation.md")
6617
+ manifest: join19(root, "manifest.yaml"),
6618
+ status: join19(root, "status.json"),
6619
+ handoff: join19(root, "handoff.md"),
6620
+ decisions: join19(root, "decisions.md"),
6621
+ orientation: join19(root, "orientation.md")
6471
6622
  }
6472
6623
  };
6473
6624
  }
@@ -6524,12 +6675,12 @@ function hasErrorCode4(error) {
6524
6675
 
6525
6676
  // src/storage/gitignore.ts
6526
6677
  import { readFile as readFile8, writeFile as writeFile2 } from "fs/promises";
6527
- import { join as join19 } from "path";
6678
+ import { join as join20 } from "path";
6528
6679
  var MARKER = "# Basou - default ignore";
6529
6680
  var BASOU_GITIGNORE_BLOCK = "# Basou - default ignore\n.basou/logs/\n.basou/raw/\n.basou/tmp/\n.basou/locks/\n.basou/status.json\n.basou/orientation.md\n.basou/sessions/*/events.jsonl\n.basou/sessions/*/artifacts/\n.basou/approvals/pending/\n.basou/approvals/resolved/\n\n# Basou - default commit\n# .basou/manifest.yaml\n# .basou/handoff.md\n# .basou/decisions.md\n# .basou/tasks/\n# .basou/sessions/*/session.yaml\n# .basou/sessions/*/transcript.md\n# .basou/sessions/*/changed-files.json\n";
6530
6681
  var BASOU_GITIGNORE_BLOCK_LOCAL_ONLY = "# Basou - default ignore\n# Local-only: basou's trail is never committed (personal/local state,\n# regenerable by re-importing from the agents' own logs). Recommended for\n# monitored repos and any workspace kept out of version control.\n.basou/\n";
6531
6682
  async function appendBasouGitignore(repositoryRoot, options = {}) {
6532
- const gitignorePath = join19(repositoryRoot, ".gitignore");
6683
+ const gitignorePath = join20(repositoryRoot, ".gitignore");
6533
6684
  let body;
6534
6685
  let existed;
6535
6686
  try {
@@ -6702,7 +6853,7 @@ function hasErrorCode6(error) {
6702
6853
  // src/storage/session-import.ts
6703
6854
  import { mkdir as mkdir5, readFile as readFile10, rm as rm2 } from "fs/promises";
6704
6855
  import { homedir as homedir3 } from "os";
6705
- import { join as join20 } from "path";
6856
+ import { join as join21 } from "path";
6706
6857
  async function importSessionFromJson(paths, manifest, payload, options) {
6707
6858
  if (options.taskIdOverride !== void 0 && !TaskIdSchema.safeParse(options.taskIdOverride).success) {
6708
6859
  throw new Error(`Invalid task_id: ${options.taskIdOverride}`);
@@ -6727,7 +6878,7 @@ async function importSessionFromJson(paths, manifest, payload, options) {
6727
6878
  pathSanitizeReport
6728
6879
  };
6729
6880
  }
6730
- const sessionDir = join20(paths.sessions, newSessionId);
6881
+ const sessionDir = join21(paths.sessions, newSessionId);
6731
6882
  try {
6732
6883
  await mkdir5(sessionDir, { recursive: true });
6733
6884
  } catch (error) {
@@ -6741,7 +6892,7 @@ async function importSessionFromJson(paths, manifest, payload, options) {
6741
6892
  throw error;
6742
6893
  }
6743
6894
  try {
6744
- const sessionYamlPath = join20(sessionDir, "session.yaml");
6895
+ const sessionYamlPath = join21(sessionDir, "session.yaml");
6745
6896
  await linkYamlFile(sessionYamlPath, withIntegrity(sessionRecord, chainResult));
6746
6897
  } catch (error) {
6747
6898
  await rm2(sessionDir, { recursive: true, force: true }).catch(() => void 0);
@@ -6909,7 +7060,7 @@ function reuseDerivedIds(priorDerived, freshDerived, sessionId) {
6909
7060
  async function reimportPreservingId(paths, manifest, priorSessionId, freshPayload, options = {}) {
6910
7061
  const sessionId = priorSessionId;
6911
7062
  const importSource = freshPayload.session.source.kind;
6912
- const sessionDir = join20(paths.sessions, priorSessionId);
7063
+ const sessionDir = join21(paths.sessions, priorSessionId);
6913
7064
  const lock = options.dryRun === true ? null : await acquireLock(paths, "session", priorSessionId);
6914
7065
  try {
6915
7066
  const priorVerdict = await verifyEventsChain(paths, priorSessionId);
@@ -6951,7 +7102,7 @@ async function reimportPreservingId(paths, manifest, priorSessionId, freshPayloa
6951
7102
  };
6952
7103
  const updatedRecord = { schema_version: "0.1.0", session: preservedInner };
6953
7104
  if (options.dryRun !== true) {
6954
- const eventsPath = join20(sessionDir, "events.jsonl");
7105
+ const eventsPath = join21(sessionDir, "events.jsonl");
6955
7106
  let priorEventsRaw = null;
6956
7107
  try {
6957
7108
  priorEventsRaw = await readFile10(eventsPath);
@@ -6963,7 +7114,7 @@ async function reimportPreservingId(paths, manifest, priorSessionId, freshPayloa
6963
7114
  const chainResult = await writeEventsBulk(sessionDir, mergedEvents, { chain: true });
6964
7115
  try {
6965
7116
  await overwriteYamlFile(
6966
- join20(sessionDir, "session.yaml"),
7117
+ join21(sessionDir, "session.yaml"),
6967
7118
  withIntegrity(updatedRecord, chainResult)
6968
7119
  );
6969
7120
  } catch (error) {
@@ -6987,7 +7138,7 @@ async function reimportPreservingId(paths, manifest, priorSessionId, freshPayloa
6987
7138
  }
6988
7139
  }
6989
7140
  async function rechainSessionInPlace(paths, sessionId, options = {}) {
6990
- const sessionDir = join20(paths.sessions, sessionId);
7141
+ const sessionDir = join21(paths.sessions, sessionId);
6991
7142
  let lock;
6992
7143
  try {
6993
7144
  lock = await acquireLock(paths, "session", sessionId);
@@ -7020,7 +7171,7 @@ async function rechainSessionInPlace(paths, sessionId, options = {}) {
7020
7171
  if (verdict.status !== "unchained") {
7021
7172
  return { status: "skipped", reason: "tampered" };
7022
7173
  }
7023
- const eventsPath = join20(sessionDir, "events.jsonl");
7174
+ const eventsPath = join21(sessionDir, "events.jsonl");
7024
7175
  let priorRaw;
7025
7176
  try {
7026
7177
  priorRaw = await readFile10(eventsPath);
@@ -7068,7 +7219,7 @@ async function rechainSessionInPlace(paths, sessionId, options = {}) {
7068
7219
  }
7069
7220
  try {
7070
7221
  await overwriteYamlFile(
7071
- join20(sessionDir, "session.yaml"),
7222
+ join21(sessionDir, "session.yaml"),
7072
7223
  withIntegrity(record, { headHash: chainResult.headHash, count: chainResult.count })
7073
7224
  );
7074
7225
  } catch (error) {
@@ -7085,6 +7236,7 @@ async function rechainSessionInPlace(paths, sessionId, options = {}) {
7085
7236
  var BASOU_CORE_VERSION = "0.1.0";
7086
7237
  export {
7087
7238
  ACTIVE_GAP_CAP_MS,
7239
+ AGENT_INFRA_DIRS,
7088
7240
  ApprovalIdSchema,
7089
7241
  ApprovalSchema,
7090
7242
  ApprovalStatusSchema,
@@ -7135,6 +7287,7 @@ export {
7135
7287
  buildStatusSnapshot,
7136
7288
  chainEvents,
7137
7289
  chainRawJsonLines,
7290
+ classifyFilesBySourceRoot,
7138
7291
  classifySuspect,
7139
7292
  claudeCodeAdapterMetadata,
7140
7293
  claudeTranscriptToImportPayload,