@basou/core 0.29.0 → 0.30.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 +260 -8
- package/dist/index.js +239 -12
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
- package/schemas/event.schema.json +119 -0
- package/schemas/session-import.schema.json +120 -0
- package/schemas/session.schema.json +1 -0
package/dist/index.js
CHANGED
|
@@ -1,5 +1,14 @@
|
|
|
1
|
-
// src/adapters/
|
|
1
|
+
// src/adapters/command-lookup.ts
|
|
2
2
|
import { spawn } from "child_process";
|
|
3
|
+
async function isOnPath(command) {
|
|
4
|
+
return new Promise((resolve3) => {
|
|
5
|
+
const child = spawn("which", [command], { stdio: "ignore" });
|
|
6
|
+
child.on("error", () => resolve3(false));
|
|
7
|
+
child.on("exit", (code) => resolve3(code === 0));
|
|
8
|
+
});
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
// src/adapters/claude-code/claude-code-adapter.ts
|
|
3
12
|
var claudeCodeAdapterMetadata = {
|
|
4
13
|
kind: "claude-code-adapter",
|
|
5
14
|
version: "0.1.0"
|
|
@@ -10,13 +19,6 @@ async function resolveClaudeCodeCommand(lookup = isOnPath) {
|
|
|
10
19
|
}
|
|
11
20
|
throw new Error("Claude Code CLI not found in PATH. Install claude-code (or claude) first.");
|
|
12
21
|
}
|
|
13
|
-
async function isOnPath(command) {
|
|
14
|
-
return new Promise((resolve3) => {
|
|
15
|
-
const child = spawn("which", [command], { stdio: "ignore" });
|
|
16
|
-
child.on("error", () => resolve3(false));
|
|
17
|
-
child.on("exit", (code) => resolve3(code === 0));
|
|
18
|
-
});
|
|
19
|
-
}
|
|
20
22
|
function summarizeAdapterOutput(_stream, _raw) {
|
|
21
23
|
throw new Error("adapter_output summary is not implemented in this release");
|
|
22
24
|
}
|
|
@@ -33,6 +35,7 @@ function shellQuote(value) {
|
|
|
33
35
|
function buildStopHookCommand(options) {
|
|
34
36
|
const flags = [];
|
|
35
37
|
if (options.block === true) flags.push("--block");
|
|
38
|
+
if (options.requireReview === true) flags.push("--require-review");
|
|
36
39
|
if (options.minEdits !== void 0) flags.push(`--min-edits ${options.minEdits}`);
|
|
37
40
|
const suffix = flags.length > 0 ? ` ${flags.join(" ")}` : "";
|
|
38
41
|
return `node ${shellQuote(options.cliEntry)} hook stop${suffix} 2>/dev/null || true`;
|
|
@@ -218,6 +221,14 @@ var CAPTURE_VERB = /(?:decision\s+(?:capture|record)|note)\b/;
|
|
|
218
221
|
var CAPTURE_COMMAND_PATTERN = new RegExp(
|
|
219
222
|
`(?:^|[\\n;&|(])\\s*${CAPTURE_INVOCATION.source}\\s+${CAPTURE_VERB.source}`
|
|
220
223
|
);
|
|
224
|
+
var SHIP_ACT_PATTERN = /(?:^|[\n;&|(])\s*(?:git\s+push|git\s+merge|gh\s+pr\s+(?:create|merge))(?![-\w])/;
|
|
225
|
+
var DRY_RUN_PUSH_PATTERN = /(?:^|[\n;&|(])\s*git\s+push\b[^\n;&|()]*?\s-(?:-dry-run|n)\b/;
|
|
226
|
+
function isShipAct(command) {
|
|
227
|
+
return SHIP_ACT_PATTERN.test(command) && !DRY_RUN_PUSH_PATTERN.test(command);
|
|
228
|
+
}
|
|
229
|
+
var REVIEW_RECORD_PATTERN = new RegExp(
|
|
230
|
+
`(?:^|[\\n;&|(])\\s*${CAPTURE_INVOCATION.source}\\s+review\\s+record\\b`
|
|
231
|
+
);
|
|
221
232
|
var FILE_EDIT_TOOLS = /* @__PURE__ */ new Set(["Edit", "Write", "NotebookEdit"]);
|
|
222
233
|
function evaluateStopHook(input) {
|
|
223
234
|
const minEdits = input.minEdits ?? DEFAULT_STOP_HOOK_MIN_EDITS;
|
|
@@ -225,6 +236,7 @@ function evaluateStopHook(input) {
|
|
|
225
236
|
return {
|
|
226
237
|
kind: "silent",
|
|
227
238
|
reason: "stop_hook_active",
|
|
239
|
+
review: { fires: false, reason: "stop_hook_active" },
|
|
228
240
|
commandCount: 0,
|
|
229
241
|
fileCount: 0,
|
|
230
242
|
decisionPointCount: 0
|
|
@@ -233,6 +245,8 @@ function evaluateStopHook(input) {
|
|
|
233
245
|
let commandCount = 0;
|
|
234
246
|
let fileCount = 0;
|
|
235
247
|
let captured = false;
|
|
248
|
+
let shipped = false;
|
|
249
|
+
let reviewed = false;
|
|
236
250
|
for (const record of input.records) {
|
|
237
251
|
if (readString2(record.type) !== "assistant") continue;
|
|
238
252
|
for (const tool of toolUsesOf2(record)) {
|
|
@@ -242,7 +256,11 @@ function evaluateStopHook(input) {
|
|
|
242
256
|
commandCount += 1;
|
|
243
257
|
const toolInput = isObject2(tool.input) ? tool.input : void 0;
|
|
244
258
|
const command = toolInput !== void 0 ? readString2(toolInput.command) : void 0;
|
|
245
|
-
if (command !== void 0
|
|
259
|
+
if (command !== void 0) {
|
|
260
|
+
if (CAPTURE_COMMAND_PATTERN.test(command)) captured = true;
|
|
261
|
+
if (isShipAct(command)) shipped = true;
|
|
262
|
+
if (REVIEW_RECORD_PATTERN.test(command)) reviewed = true;
|
|
263
|
+
}
|
|
246
264
|
} else if (FILE_EDIT_TOOLS.has(name)) {
|
|
247
265
|
fileCount += 1;
|
|
248
266
|
}
|
|
@@ -250,14 +268,21 @@ function evaluateStopHook(input) {
|
|
|
250
268
|
}
|
|
251
269
|
const decisionPointCount = countUncapturedDecisionPoints(input.records);
|
|
252
270
|
const counts = { commandCount, fileCount, decisionPointCount };
|
|
271
|
+
const review = evaluateReviewGate({ shipped, reviewed, fileCount, minEdits });
|
|
253
272
|
if (captured) {
|
|
254
|
-
return { kind: "silent", reason: "already_captured", ...counts };
|
|
273
|
+
return { kind: "silent", reason: "already_captured", review, ...counts };
|
|
255
274
|
}
|
|
256
275
|
const substantive = fileCount >= minEdits || decisionPointCount > 0;
|
|
257
276
|
if (!substantive) {
|
|
258
|
-
return { kind: "silent", reason: "not_substantive", ...counts };
|
|
277
|
+
return { kind: "silent", reason: "not_substantive", review, ...counts };
|
|
259
278
|
}
|
|
260
|
-
return { kind: "nudge", additionalContext: renderNudge(counts), ...counts };
|
|
279
|
+
return { kind: "nudge", additionalContext: renderNudge(counts), review, ...counts };
|
|
280
|
+
}
|
|
281
|
+
function evaluateReviewGate(input) {
|
|
282
|
+
if (!input.shipped) return { fires: false, reason: "no_ship_act" };
|
|
283
|
+
if (input.fileCount < input.minEdits) return { fires: false, reason: "not_substantive_code" };
|
|
284
|
+
if (input.reviewed) return { fires: false, reason: "already_reviewed" };
|
|
285
|
+
return { fires: true, additionalContext: renderReviewNudge() };
|
|
261
286
|
}
|
|
262
287
|
function renderNudge(counts) {
|
|
263
288
|
const did = [];
|
|
@@ -280,6 +305,14 @@ function renderNudge(counts) {
|
|
|
280
305
|
"If nothing is worth capturing, just stop \u2014 do not invent decisions."
|
|
281
306
|
].join("\n");
|
|
282
307
|
}
|
|
308
|
+
function renderReviewNudge() {
|
|
309
|
+
return [
|
|
310
|
+
"This session shipped code (a push / PR / merge) after substantive edits but recorded no review.",
|
|
311
|
+
"An adversarial / second-opinion review before shipping is the discipline here. If a review ran, record it now so it lands on the durable trail:",
|
|
312
|
+
' - run `basou review record` and pipe a JSON object: { "reviewer": "...", "target": "...", with optional "verdict" / "findings" / "blocked" } (an explicit "blocked": [] records that you blocked nothing).',
|
|
313
|
+
"If no review ran, that is the gap this is meant to catch \u2014 review before relying on this. If a review genuinely was not warranted, just stop \u2014 do not fabricate a review record."
|
|
314
|
+
].join("\n");
|
|
315
|
+
}
|
|
283
316
|
function readString2(value) {
|
|
284
317
|
return typeof value === "string" && value.length > 0 ? value : void 0;
|
|
285
318
|
}
|
|
@@ -606,6 +639,16 @@ function toolUses(record) {
|
|
|
606
639
|
return result;
|
|
607
640
|
}
|
|
608
641
|
|
|
642
|
+
// src/adapters/codex/codex-adapter.ts
|
|
643
|
+
var codexAdapterMetadata = {
|
|
644
|
+
kind: "codex-adapter",
|
|
645
|
+
version: "0.1.0"
|
|
646
|
+
};
|
|
647
|
+
async function resolveCodexCommand(lookup = isOnPath) {
|
|
648
|
+
if (await lookup("codex")) return { command: "codex" };
|
|
649
|
+
throw new Error("Codex CLI not found in PATH. Install codex first.");
|
|
650
|
+
}
|
|
651
|
+
|
|
609
652
|
// src/adapters/codex/rollout-importer.ts
|
|
610
653
|
var CODEX_IMPORT_SOURCE = "codex-import";
|
|
611
654
|
function codexRolloutToImportPayload(records, options) {
|
|
@@ -1196,6 +1239,25 @@ var NoteAddedEventSchema = BaseEventSchema.extend({
|
|
|
1196
1239
|
// surface. Optional so pre-existing note_added events remain valid.
|
|
1197
1240
|
kind: z3.enum(["note", "next_step"]).optional()
|
|
1198
1241
|
});
|
|
1242
|
+
var ReviewFindingSchema = z3.object({
|
|
1243
|
+
title: z3.string().min(1),
|
|
1244
|
+
severity: z3.enum(["high", "medium", "low"]).optional(),
|
|
1245
|
+
location: z3.string().min(1).optional(),
|
|
1246
|
+
summary: z3.string().min(1).optional()
|
|
1247
|
+
});
|
|
1248
|
+
var ReviewBlockedSchema = z3.object({
|
|
1249
|
+
title: z3.string().min(1),
|
|
1250
|
+
reason: z3.enum(["spec-deviation", "design-reversal"]),
|
|
1251
|
+
why: z3.string().min(1).optional()
|
|
1252
|
+
});
|
|
1253
|
+
var ReviewRecordedEventSchema = BaseEventSchema.extend({
|
|
1254
|
+
type: z3.literal("review_recorded"),
|
|
1255
|
+
reviewer: z3.string().min(1),
|
|
1256
|
+
target: z3.string().min(1),
|
|
1257
|
+
verdict: z3.enum(["pass", "needs-attention", "fail"]).optional(),
|
|
1258
|
+
findings: z3.array(ReviewFindingSchema).optional(),
|
|
1259
|
+
blocked: z3.array(ReviewBlockedSchema).optional()
|
|
1260
|
+
});
|
|
1199
1261
|
var AdapterOutputEventSchema = BaseEventSchema.extend({
|
|
1200
1262
|
type: z3.literal("adapter_output"),
|
|
1201
1263
|
stream: z3.enum(["stdout", "stderr"]),
|
|
@@ -1223,6 +1285,7 @@ var EventSchema = z3.discriminatedUnion("type", [
|
|
|
1223
1285
|
TaskDeletedEventSchema,
|
|
1224
1286
|
TaskArchivedEventSchema,
|
|
1225
1287
|
NoteAddedEventSchema,
|
|
1288
|
+
ReviewRecordedEventSchema,
|
|
1226
1289
|
AdapterOutputEventSchema
|
|
1227
1290
|
]);
|
|
1228
1291
|
|
|
@@ -1521,6 +1584,7 @@ var SessionStatusSchema = z4.enum([
|
|
|
1521
1584
|
var SessionSourceKindSchema = z4.enum([
|
|
1522
1585
|
"claude-code-adapter",
|
|
1523
1586
|
"claude-code-import",
|
|
1587
|
+
"codex-adapter",
|
|
1524
1588
|
"codex-import",
|
|
1525
1589
|
"human",
|
|
1526
1590
|
"import",
|
|
@@ -6867,6 +6931,159 @@ async function findReviewGaps(input) {
|
|
|
6867
6931
|
};
|
|
6868
6932
|
}
|
|
6869
6933
|
|
|
6934
|
+
// src/review/review-record.ts
|
|
6935
|
+
var VALID_VERDICTS = /* @__PURE__ */ new Set(["pass", "needs-attention", "fail"]);
|
|
6936
|
+
var VALID_SEVERITIES = /* @__PURE__ */ new Set(["high", "medium", "low"]);
|
|
6937
|
+
var VALID_BLOCK_REASONS = /* @__PURE__ */ new Set(["spec-deviation", "design-reversal"]);
|
|
6938
|
+
var ALLOWED_KEYS = /* @__PURE__ */ new Set([
|
|
6939
|
+
"reviewer",
|
|
6940
|
+
"target",
|
|
6941
|
+
"verdict",
|
|
6942
|
+
"findings",
|
|
6943
|
+
"blocked"
|
|
6944
|
+
]);
|
|
6945
|
+
var ALLOWED_FINDING_KEYS = /* @__PURE__ */ new Set([
|
|
6946
|
+
"title",
|
|
6947
|
+
"severity",
|
|
6948
|
+
"location",
|
|
6949
|
+
"summary"
|
|
6950
|
+
]);
|
|
6951
|
+
var ALLOWED_BLOCKED_KEYS = /* @__PURE__ */ new Set(["title", "reason", "why"]);
|
|
6952
|
+
var REVIEW_RECORD_NO_INPUT_HINT = "No input: pipe a JSON object describing the review to stdin or pass --file <path>.";
|
|
6953
|
+
function parseReviewRecordInput(raw) {
|
|
6954
|
+
if (raw.trim().length === 0) {
|
|
6955
|
+
throw new Error(REVIEW_RECORD_NO_INPUT_HINT);
|
|
6956
|
+
}
|
|
6957
|
+
let parsed;
|
|
6958
|
+
try {
|
|
6959
|
+
parsed = JSON.parse(raw);
|
|
6960
|
+
} catch (error) {
|
|
6961
|
+
const detail = error instanceof Error ? error.message : String(error);
|
|
6962
|
+
throw new Error(`Input is not valid JSON: ${detail}`);
|
|
6963
|
+
}
|
|
6964
|
+
if (typeof parsed !== "object" || parsed === null || Array.isArray(parsed)) {
|
|
6965
|
+
throw new Error("Input must be a single JSON object describing one review.");
|
|
6966
|
+
}
|
|
6967
|
+
const obj = parsed;
|
|
6968
|
+
for (const key of Object.keys(obj)) {
|
|
6969
|
+
if (!ALLOWED_KEYS.has(key)) {
|
|
6970
|
+
throw new Error(
|
|
6971
|
+
`Unknown field '${key}'. Allowed: reviewer, target, verdict, findings, blocked.`
|
|
6972
|
+
);
|
|
6973
|
+
}
|
|
6974
|
+
}
|
|
6975
|
+
const reviewer = requireNonEmptyString(obj.reviewer, "reviewer");
|
|
6976
|
+
const target = requireNonEmptyString(obj.target, "target");
|
|
6977
|
+
const out = { reviewer, target };
|
|
6978
|
+
if (obj.verdict !== void 0) {
|
|
6979
|
+
if (typeof obj.verdict !== "string" || !VALID_VERDICTS.has(obj.verdict)) {
|
|
6980
|
+
throw new Error(`verdict must be one of pass, needs-attention, fail, got '${obj.verdict}'.`);
|
|
6981
|
+
}
|
|
6982
|
+
out.verdict = obj.verdict;
|
|
6983
|
+
}
|
|
6984
|
+
if (obj.findings !== void 0) {
|
|
6985
|
+
out.findings = parseFindings(obj.findings);
|
|
6986
|
+
}
|
|
6987
|
+
if (obj.blocked !== void 0) {
|
|
6988
|
+
out.blocked = parseBlocked(obj.blocked);
|
|
6989
|
+
}
|
|
6990
|
+
return out;
|
|
6991
|
+
}
|
|
6992
|
+
function parseFindings(value) {
|
|
6993
|
+
if (!Array.isArray(value)) {
|
|
6994
|
+
throw new Error("findings must be an array of objects.");
|
|
6995
|
+
}
|
|
6996
|
+
return value.map((item, i) => {
|
|
6997
|
+
if (typeof item !== "object" || item === null || Array.isArray(item)) {
|
|
6998
|
+
throw new Error(`findings[${i}] must be a JSON object.`);
|
|
6999
|
+
}
|
|
7000
|
+
const obj = item;
|
|
7001
|
+
for (const key of Object.keys(obj)) {
|
|
7002
|
+
if (!ALLOWED_FINDING_KEYS.has(key)) {
|
|
7003
|
+
throw new Error(
|
|
7004
|
+
`findings[${i}]: unknown field '${key}'. Allowed: title, severity, location, summary.`
|
|
7005
|
+
);
|
|
7006
|
+
}
|
|
7007
|
+
}
|
|
7008
|
+
const finding = {
|
|
7009
|
+
title: requireNonEmptyString(obj.title, `findings[${i}].title`)
|
|
7010
|
+
};
|
|
7011
|
+
if (obj.severity !== void 0) {
|
|
7012
|
+
if (typeof obj.severity !== "string" || !VALID_SEVERITIES.has(obj.severity)) {
|
|
7013
|
+
throw new Error(
|
|
7014
|
+
`findings[${i}].severity must be one of high, medium, low, got '${obj.severity}'.`
|
|
7015
|
+
);
|
|
7016
|
+
}
|
|
7017
|
+
finding.severity = obj.severity;
|
|
7018
|
+
}
|
|
7019
|
+
if (obj.location !== void 0) {
|
|
7020
|
+
finding.location = requireNonEmptyString(obj.location, `findings[${i}].location`);
|
|
7021
|
+
}
|
|
7022
|
+
if (obj.summary !== void 0) {
|
|
7023
|
+
finding.summary = requireNonEmptyString(obj.summary, `findings[${i}].summary`);
|
|
7024
|
+
}
|
|
7025
|
+
return finding;
|
|
7026
|
+
});
|
|
7027
|
+
}
|
|
7028
|
+
function parseBlocked(value) {
|
|
7029
|
+
if (!Array.isArray(value)) {
|
|
7030
|
+
throw new Error("blocked must be an array of objects.");
|
|
7031
|
+
}
|
|
7032
|
+
return value.map((item, i) => {
|
|
7033
|
+
if (typeof item !== "object" || item === null || Array.isArray(item)) {
|
|
7034
|
+
throw new Error(`blocked[${i}] must be a JSON object.`);
|
|
7035
|
+
}
|
|
7036
|
+
const obj = item;
|
|
7037
|
+
for (const key of Object.keys(obj)) {
|
|
7038
|
+
if (!ALLOWED_BLOCKED_KEYS.has(key)) {
|
|
7039
|
+
throw new Error(`blocked[${i}]: unknown field '${key}'. Allowed: title, reason, why.`);
|
|
7040
|
+
}
|
|
7041
|
+
}
|
|
7042
|
+
if (typeof obj.reason !== "string" || !VALID_BLOCK_REASONS.has(obj.reason)) {
|
|
7043
|
+
throw new Error(
|
|
7044
|
+
`blocked[${i}].reason must be one of spec-deviation, design-reversal, got '${obj.reason}'.`
|
|
7045
|
+
);
|
|
7046
|
+
}
|
|
7047
|
+
const blocked = {
|
|
7048
|
+
title: requireNonEmptyString(obj.title, `blocked[${i}].title`),
|
|
7049
|
+
reason: obj.reason
|
|
7050
|
+
};
|
|
7051
|
+
if (obj.why !== void 0) {
|
|
7052
|
+
blocked.why = requireNonEmptyString(obj.why, `blocked[${i}].why`);
|
|
7053
|
+
}
|
|
7054
|
+
return blocked;
|
|
7055
|
+
});
|
|
7056
|
+
}
|
|
7057
|
+
function requireNonEmptyString(value, field) {
|
|
7058
|
+
if (typeof value !== "string" || value.trim().length === 0) {
|
|
7059
|
+
throw new Error(`${field} must be a non-empty string.`);
|
|
7060
|
+
}
|
|
7061
|
+
return value;
|
|
7062
|
+
}
|
|
7063
|
+
function buildReviewRecordedEvent(input) {
|
|
7064
|
+
const { review } = input;
|
|
7065
|
+
return {
|
|
7066
|
+
schema_version: "0.1.0",
|
|
7067
|
+
id: input.eventId,
|
|
7068
|
+
session_id: input.sessionId,
|
|
7069
|
+
occurred_at: input.occurredAt,
|
|
7070
|
+
source: "local-cli",
|
|
7071
|
+
type: "review_recorded",
|
|
7072
|
+
reviewer: review.reviewer,
|
|
7073
|
+
target: review.target,
|
|
7074
|
+
...review.verdict !== void 0 ? { verdict: review.verdict } : {},
|
|
7075
|
+
...review.findings !== void 0 ? { findings: review.findings } : {},
|
|
7076
|
+
...review.blocked !== void 0 ? { blocked: review.blocked } : {}
|
|
7077
|
+
};
|
|
7078
|
+
}
|
|
7079
|
+
var LABEL_FRAGMENT_MAX = 40;
|
|
7080
|
+
function buildReviewRecordLabel(review) {
|
|
7081
|
+
return `Ad-hoc review: ${truncate(review.reviewer)} -> ${truncate(review.target)}`;
|
|
7082
|
+
}
|
|
7083
|
+
function truncate(value) {
|
|
7084
|
+
return value.length > LABEL_FRAGMENT_MAX ? `${value.slice(0, LABEL_FRAGMENT_MAX - 3)}...` : value;
|
|
7085
|
+
}
|
|
7086
|
+
|
|
6870
7087
|
// src/runtime/child-process-runner.ts
|
|
6871
7088
|
import { spawn as spawn2 } from "child_process";
|
|
6872
7089
|
var DEFAULT_KILL_GRACE_MS = 5e3;
|
|
@@ -7249,6 +7466,8 @@ var GENERATED_START = "<!-- BASOU:GENERATED:START -->";
|
|
|
7249
7466
|
var GENERATED_END = "<!-- BASOU:GENERATED:END -->";
|
|
7250
7467
|
var PROTOCOL_START = "<!-- BASOU:PROTOCOLS:START -->";
|
|
7251
7468
|
var PROTOCOL_END = "<!-- BASOU:PROTOCOLS:END -->";
|
|
7469
|
+
var ORIENTATION_START = "<!-- BASOU:ORIENTATION:START -->";
|
|
7470
|
+
var ORIENTATION_END = "<!-- BASOU:ORIENTATION:END -->";
|
|
7252
7471
|
var DEFAULT_MARKERS = { start: GENERATED_START, end: GENERATED_END };
|
|
7253
7472
|
async function readMarkdownFile(filePath) {
|
|
7254
7473
|
try {
|
|
@@ -7773,8 +7992,11 @@ export {
|
|
|
7773
7992
|
IsoTimestampSchema,
|
|
7774
7993
|
JSON_SCHEMA_VERSION,
|
|
7775
7994
|
ManifestSchema,
|
|
7995
|
+
ORIENTATION_END,
|
|
7996
|
+
ORIENTATION_START,
|
|
7776
7997
|
PROTOCOL_END,
|
|
7777
7998
|
PROTOCOL_START,
|
|
7999
|
+
REVIEW_RECORD_NO_INPUT_HINT,
|
|
7778
8000
|
RiskLevelSchema,
|
|
7779
8001
|
STOP_HOOK_TIMEOUT_SECONDS,
|
|
7780
8002
|
STUCK_THRESHOLD_MS,
|
|
@@ -7803,6 +8025,8 @@ export {
|
|
|
7803
8025
|
assertBasouRootSafe,
|
|
7804
8026
|
basouPaths,
|
|
7805
8027
|
buildJsonSchemas,
|
|
8028
|
+
buildReviewRecordLabel,
|
|
8029
|
+
buildReviewRecordedEvent,
|
|
7806
8030
|
buildStatusSnapshot,
|
|
7807
8031
|
buildStopHookCommand,
|
|
7808
8032
|
chainEvents,
|
|
@@ -7812,6 +8036,7 @@ export {
|
|
|
7812
8036
|
classifySuspect,
|
|
7813
8037
|
claudeCodeAdapterMetadata,
|
|
7814
8038
|
claudeTranscriptToImportPayload,
|
|
8039
|
+
codexAdapterMetadata,
|
|
7815
8040
|
codexRolloutToImportPayload,
|
|
7816
8041
|
computeWorkStats,
|
|
7817
8042
|
createAdHocSessionWithEvent,
|
|
@@ -7853,6 +8078,7 @@ export {
|
|
|
7853
8078
|
overwriteYamlFile,
|
|
7854
8079
|
parseDuration,
|
|
7855
8080
|
parseMarkers,
|
|
8081
|
+
parseReviewRecordInput,
|
|
7856
8082
|
pathBasename,
|
|
7857
8083
|
planArchive,
|
|
7858
8084
|
planGitignore,
|
|
@@ -7885,6 +8111,7 @@ export {
|
|
|
7885
8111
|
replayEvents,
|
|
7886
8112
|
resolveBasouRepositoryRoot,
|
|
7887
8113
|
resolveClaudeCodeCommand,
|
|
8114
|
+
resolveCodexCommand,
|
|
7888
8115
|
resolveRepositoryRoot,
|
|
7889
8116
|
resolveSessionId,
|
|
7890
8117
|
resolveTaskId,
|