@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.d.ts +100 -2
- package/dist/index.js +208 -55
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
- package/schemas/event.schema.json +64 -0
- package/schemas/session-import.schema.json +64 -0
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((
|
|
14
|
+
return new Promise((resolve3) => {
|
|
15
15
|
const child = spawn("which", [command], { stdio: "ignore" });
|
|
16
|
-
child.on("error", () =>
|
|
17
|
-
child.on("exit", (code) =>
|
|
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
|
-
|
|
1531
|
-
|
|
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.
|
|
4049
|
+
if (args.latestDecision === void 0) {
|
|
4015
4050
|
lines.push("(no decisions recorded yet)");
|
|
4016
4051
|
} else {
|
|
4017
|
-
const last = args.
|
|
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
|
|
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 =
|
|
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
|
-
|
|
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
|
|
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
|
|
5522
|
+
import { join as join17 } from "path";
|
|
5379
5523
|
|
|
5380
5524
|
// src/stats/work-stats.ts
|
|
5381
|
-
import { join as
|
|
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(
|
|
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 =
|
|
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
|
-
|
|
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
|
|
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(
|
|
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 (
|
|
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 :
|
|
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(
|
|
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) =>
|
|
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 =
|
|
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 =
|
|
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((
|
|
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
|
-
|
|
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
|
|
6600
|
+
import { join as join19 } from "path";
|
|
6450
6601
|
function basouPaths(repositoryRoot) {
|
|
6451
|
-
const root =
|
|
6452
|
-
const approvalsBase =
|
|
6602
|
+
const root = join19(repositoryRoot, ".basou");
|
|
6603
|
+
const approvalsBase = join19(root, "approvals");
|
|
6453
6604
|
return {
|
|
6454
6605
|
root,
|
|
6455
|
-
sessions:
|
|
6456
|
-
tasks:
|
|
6606
|
+
sessions: join19(root, "sessions"),
|
|
6607
|
+
tasks: join19(root, "tasks"),
|
|
6457
6608
|
approvals: {
|
|
6458
|
-
pending:
|
|
6459
|
-
resolved:
|
|
6609
|
+
pending: join19(approvalsBase, "pending"),
|
|
6610
|
+
resolved: join19(approvalsBase, "resolved")
|
|
6460
6611
|
},
|
|
6461
|
-
locks:
|
|
6462
|
-
logs:
|
|
6463
|
-
raw:
|
|
6464
|
-
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:
|
|
6467
|
-
status:
|
|
6468
|
-
handoff:
|
|
6469
|
-
decisions:
|
|
6470
|
-
orientation:
|
|
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
|
|
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 =
|
|
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
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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
|
-
|
|
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 =
|
|
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 =
|
|
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
|
-
|
|
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,
|