@basou/cli 0.18.0 → 0.20.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 +185 -29
- package/dist/index.js.map +1 -1
- package/dist/program.js +185 -29
- package/dist/program.js.map +1 -1
- package/package.json +2 -2
package/dist/program.js
CHANGED
|
@@ -838,6 +838,9 @@ function registerDecisionCommand(program) {
|
|
|
838
838
|
"Related file path (repeatable). Path is opaque; existence is verified at render time.",
|
|
839
839
|
collectLinkedFile,
|
|
840
840
|
[]
|
|
841
|
+
).option(
|
|
842
|
+
"--track",
|
|
843
|
+
"Record as a strategic track (an unfinished direction + why). orientation/handoff keep resurfacing open tracks until you close one with 'basou decision void'."
|
|
841
844
|
).option(
|
|
842
845
|
"--session <session_id>",
|
|
843
846
|
"Attach to an existing session; otherwise an ad-hoc session is created"
|
|
@@ -870,13 +873,22 @@ Input format (a JSON array; one object per decision):
|
|
|
870
873
|
"alternatives": ["npm workspaces", "yarn"],
|
|
871
874
|
"rejected_reason": "npm hoisting caused phantom-dependency bugs",
|
|
872
875
|
"linked_files": ["pnpm-workspace.yaml"]
|
|
876
|
+
},
|
|
877
|
+
{
|
|
878
|
+
"title": "Form-based admin editing is the next track (only 6/19 sections done)",
|
|
879
|
+
"rationale": "Raw-JSON editing is a stopgap, not the final shape; cover the rest.",
|
|
880
|
+
"kind": "track"
|
|
873
881
|
}
|
|
874
882
|
]
|
|
875
883
|
|
|
876
|
-
Only "title" is required; every other field is optional.
|
|
877
|
-
|
|
878
|
-
|
|
879
|
-
|
|
884
|
+
Only "title" is required; every other field is optional. Set "kind": "track" to
|
|
885
|
+
record a strategic, UNFINISHED direction (+ why): orientation/handoff resurface
|
|
886
|
+
open tracks every session until you close one with 'basou decision void <id>'.
|
|
887
|
+
Absent / "decision" is a point-in-time decision (surfaced only as the latest).
|
|
888
|
+
All decisions are written into one ad-hoc session timestamped now, so
|
|
889
|
+
orientation surfaces them as the latest decisions. Run from a workspace-view
|
|
890
|
+
directory and it resolves to the planning repo, like 'basou orient' /
|
|
891
|
+
'basou refresh' / 'basou note'.
|
|
880
892
|
|
|
881
893
|
Example (heredoc on stdin):
|
|
882
894
|
basou decision capture <<'JSON'
|
|
@@ -973,7 +985,7 @@ async function doRunDecisionRecord(options, ctx) {
|
|
|
973
985
|
workingDirectory: repositoryRoot,
|
|
974
986
|
invocation: {
|
|
975
987
|
command: "basou decision record",
|
|
976
|
-
args: ["--title", options.title]
|
|
988
|
+
args: options.track === true ? ["--title", options.title, "--track"] : ["--title", options.title]
|
|
977
989
|
},
|
|
978
990
|
targetEventBuilders: [
|
|
979
991
|
(sessionId, eventId) => buildDecisionEvent({
|
|
@@ -1254,7 +1266,8 @@ var CAPTURE_ALLOWED_KEYS = /* @__PURE__ */ new Set([
|
|
|
1254
1266
|
"rejected_reason",
|
|
1255
1267
|
"alternatives",
|
|
1256
1268
|
"linked_events",
|
|
1257
|
-
"linked_files"
|
|
1269
|
+
"linked_files",
|
|
1270
|
+
"kind"
|
|
1258
1271
|
]);
|
|
1259
1272
|
function parseCaptureInput(raw) {
|
|
1260
1273
|
if (raw.trim().length === 0) {
|
|
@@ -1283,7 +1296,7 @@ function validateCaptureItem(item, index) {
|
|
|
1283
1296
|
for (const key of Object.keys(obj)) {
|
|
1284
1297
|
if (!CAPTURE_ALLOWED_KEYS.has(key)) {
|
|
1285
1298
|
throw new Error(
|
|
1286
|
-
`decision[${index}]: unknown field '${key}'. Allowed: title, rationale, rejected_reason, alternatives, linked_events, linked_files.`
|
|
1299
|
+
`decision[${index}]: unknown field '${key}'. Allowed: title, rationale, rejected_reason, alternatives, linked_events, linked_files, kind.`
|
|
1287
1300
|
);
|
|
1288
1301
|
}
|
|
1289
1302
|
}
|
|
@@ -1291,6 +1304,12 @@ function validateCaptureItem(item, index) {
|
|
|
1291
1304
|
throw new Error(`decision[${index}].title must be a non-empty string.`);
|
|
1292
1305
|
}
|
|
1293
1306
|
const out = { title: obj.title };
|
|
1307
|
+
if (obj.kind !== void 0) {
|
|
1308
|
+
if (obj.kind !== "decision" && obj.kind !== "track") {
|
|
1309
|
+
throw new Error(`decision[${index}].kind must be "decision" or "track", got '${obj.kind}'.`);
|
|
1310
|
+
}
|
|
1311
|
+
if (obj.kind === "track") out.kind = "track";
|
|
1312
|
+
}
|
|
1294
1313
|
if (obj.rationale !== void 0) {
|
|
1295
1314
|
out.rationale = requireNonEmptyString(obj.rationale, index, "rationale");
|
|
1296
1315
|
}
|
|
@@ -1358,6 +1377,7 @@ function toRichFields(decision) {
|
|
|
1358
1377
|
if (decision.alternatives !== void 0) out.alternatives = [...decision.alternatives];
|
|
1359
1378
|
if (decision.linked_events !== void 0) out.linked_events = [...decision.linked_events];
|
|
1360
1379
|
if (decision.linked_files !== void 0) out.linked_files = [...decision.linked_files];
|
|
1380
|
+
if (decision.kind !== void 0) out.kind = decision.kind;
|
|
1361
1381
|
return out;
|
|
1362
1382
|
}
|
|
1363
1383
|
function buildCaptureLabel(count) {
|
|
@@ -1375,6 +1395,7 @@ function captureItemToPayload(item) {
|
|
|
1375
1395
|
payload.rejected_reason = item.input.rejected_reason;
|
|
1376
1396
|
if (item.input.linked_events !== void 0) payload.linked_events = item.input.linked_events;
|
|
1377
1397
|
if (item.input.linked_files !== void 0) payload.linked_files = item.input.linked_files;
|
|
1398
|
+
if (item.input.kind !== void 0) payload.kind = item.input.kind;
|
|
1378
1399
|
return payload;
|
|
1379
1400
|
}
|
|
1380
1401
|
function printCapturePreview(options, decisions) {
|
|
@@ -1386,7 +1407,7 @@ function printCapturePreview(options, decisions) {
|
|
|
1386
1407
|
`Would capture ${decisions.length} decision${decisions.length === 1 ? "" : "s"} (dry run; nothing written):`
|
|
1387
1408
|
);
|
|
1388
1409
|
for (const decision of decisions) {
|
|
1389
|
-
console.log(`- ${decision.title}`);
|
|
1410
|
+
console.log(`- ${decision.title}${decision.kind === "track" ? " [TRACK]" : ""}`);
|
|
1390
1411
|
}
|
|
1391
1412
|
}
|
|
1392
1413
|
function printCaptureResult(options, result) {
|
|
@@ -1407,7 +1428,9 @@ function printCaptureResult(options, result) {
|
|
|
1407
1428
|
`Captured ${result.items.length} decision${result.items.length === 1 ? "" : "s"} in ad-hoc session ${sid}:`
|
|
1408
1429
|
);
|
|
1409
1430
|
for (const item of result.items) {
|
|
1410
|
-
console.log(
|
|
1431
|
+
console.log(
|
|
1432
|
+
`- ${item.decisionId}: ${item.input.title}${item.input.kind === "track" ? " [TRACK]" : ""}`
|
|
1433
|
+
);
|
|
1411
1434
|
}
|
|
1412
1435
|
}
|
|
1413
1436
|
function pickRichFields(options) {
|
|
@@ -1423,6 +1446,7 @@ function pickRichFields(options) {
|
|
|
1423
1446
|
if (options.linkedFile !== void 0 && options.linkedFile.length > 0) {
|
|
1424
1447
|
out.linked_files = [...options.linkedFile];
|
|
1425
1448
|
}
|
|
1449
|
+
if (options.track === true) out.kind = "track";
|
|
1426
1450
|
return out;
|
|
1427
1451
|
}
|
|
1428
1452
|
function buildDecisionEvent(input) {
|
|
@@ -1439,7 +1463,8 @@ function buildDecisionEvent(input) {
|
|
|
1439
1463
|
...input.rich.alternatives !== void 0 ? { alternatives: input.rich.alternatives } : {},
|
|
1440
1464
|
...input.rich.rejected_reason !== void 0 ? { rejected_reason: input.rich.rejected_reason } : {},
|
|
1441
1465
|
...input.rich.linked_events !== void 0 ? { linked_events: input.rich.linked_events } : {},
|
|
1442
|
-
...input.rich.linked_files !== void 0 ? { linked_files: input.rich.linked_files } : {}
|
|
1466
|
+
...input.rich.linked_files !== void 0 ? { linked_files: input.rich.linked_files } : {},
|
|
1467
|
+
...input.rich.kind !== void 0 ? { kind: input.rich.kind } : {}
|
|
1443
1468
|
};
|
|
1444
1469
|
}
|
|
1445
1470
|
function buildAdHocLabel(title) {
|
|
@@ -1506,15 +1531,19 @@ function printDecisionResult(options, result) {
|
|
|
1506
1531
|
}
|
|
1507
1532
|
if (result.rich.linked_events !== void 0) payload.linked_events = result.rich.linked_events;
|
|
1508
1533
|
if (result.rich.linked_files !== void 0) payload.linked_files = result.rich.linked_files;
|
|
1534
|
+
if (result.rich.kind !== void 0) payload.kind = result.rich.kind;
|
|
1509
1535
|
console.log(JSON.stringify(payload));
|
|
1510
1536
|
return;
|
|
1511
1537
|
}
|
|
1538
|
+
const trackPrefix = result.rich.kind === "track" ? "track " : "";
|
|
1512
1539
|
const rationaleSuffix = result.rich.rationale !== void 0 ? ` (rationale: ${result.rich.rationale})` : "";
|
|
1513
1540
|
if (result.mode === "ad-hoc") {
|
|
1514
|
-
console.log(
|
|
1541
|
+
console.log(
|
|
1542
|
+
`Recorded ${trackPrefix}${result.decisionId} in ad-hoc session ${sid}${rationaleSuffix}`
|
|
1543
|
+
);
|
|
1515
1544
|
} else {
|
|
1516
1545
|
console.log(
|
|
1517
|
-
`Recorded ${result.decisionId} in session ${sid} (${result.sessionStatus})${rationaleSuffix}`
|
|
1546
|
+
`Recorded ${trackPrefix}${result.decisionId} in session ${sid} (${result.sessionStatus})${rationaleSuffix}`
|
|
1518
1547
|
);
|
|
1519
1548
|
}
|
|
1520
1549
|
}
|
|
@@ -2004,9 +2033,135 @@ async function assertWorkspaceInitialized4(basouRoot) {
|
|
|
2004
2033
|
}
|
|
2005
2034
|
}
|
|
2006
2035
|
|
|
2036
|
+
// src/commands/hook.ts
|
|
2037
|
+
import { open, readFile as readFile2, stat as stat2 } from "fs/promises";
|
|
2038
|
+
import {
|
|
2039
|
+
DEFAULT_STOP_HOOK_MIN_ACTIONS,
|
|
2040
|
+
evaluateStopHook
|
|
2041
|
+
} from "@basou/core";
|
|
2042
|
+
var MAX_TRANSCRIPT_BYTES = 8 * 1024 * 1024;
|
|
2043
|
+
function registerHookCommand(program) {
|
|
2044
|
+
const hook = program.command("hook").description(
|
|
2045
|
+
"Claude Code hook handlers (read a hook payload on stdin, emit hook JSON on stdout)"
|
|
2046
|
+
);
|
|
2047
|
+
hook.command("stop").description(
|
|
2048
|
+
"Stop-hook: when a substantive session recorded no decisions or next step, emit a non-blocking nudge to capture them. Reads the Stop hook JSON payload on stdin; never blocks and never fails the session."
|
|
2049
|
+
).option(
|
|
2050
|
+
"--min-actions <n>",
|
|
2051
|
+
`Minimum commands+edits before nudging (default ${DEFAULT_STOP_HOOK_MIN_ACTIONS})`
|
|
2052
|
+
).addHelpText("after", HOOK_STOP_HELP).action(async (options) => {
|
|
2053
|
+
const minActions = parseMinActions(options.minActions);
|
|
2054
|
+
await runHookStop(minActions !== void 0 ? { minActions } : {});
|
|
2055
|
+
});
|
|
2056
|
+
}
|
|
2057
|
+
var HOOK_STOP_HELP = `
|
|
2058
|
+
Install as a Claude Code Stop hook in ~/.claude/settings.json:
|
|
2059
|
+
{
|
|
2060
|
+
"hooks": {
|
|
2061
|
+
"Stop": [
|
|
2062
|
+
{ "hooks": [ { "type": "command", "command": "basou hook stop" } ] }
|
|
2063
|
+
]
|
|
2064
|
+
}
|
|
2065
|
+
}
|
|
2066
|
+
|
|
2067
|
+
On every turn end basou inspects the session transcript. If the session did
|
|
2068
|
+
substantive work (>= ${DEFAULT_STOP_HOOK_MIN_ACTIONS} commands + file edits by default) but ran no capture
|
|
2069
|
+
verb ('basou decision capture' / 'decision record' / 'note'), it emits a
|
|
2070
|
+
non-blocking reminder so the agent can record the why / next step. The reminder
|
|
2071
|
+
continues the conversation (Claude may act on it or stop); it never forces.
|
|
2072
|
+
The 'stop_hook_active' flag is honored so the nudge cannot loop.
|
|
2073
|
+
`;
|
|
2074
|
+
async function runHookStop(options, ctx = {}) {
|
|
2075
|
+
try {
|
|
2076
|
+
await doRunHookStop(options, ctx);
|
|
2077
|
+
} catch {
|
|
2078
|
+
}
|
|
2079
|
+
}
|
|
2080
|
+
async function doRunHookStop(options, ctx) {
|
|
2081
|
+
const readStdin = ctx.readStdin ?? defaultReadStdin;
|
|
2082
|
+
const readTranscript = ctx.readTranscript ?? readTranscriptBounded;
|
|
2083
|
+
const write = ctx.write ?? ((text) => void process.stdout.write(text));
|
|
2084
|
+
const raw = await readStdin();
|
|
2085
|
+
if (raw.trim().length === 0) return;
|
|
2086
|
+
let payload;
|
|
2087
|
+
try {
|
|
2088
|
+
payload = JSON.parse(raw);
|
|
2089
|
+
} catch {
|
|
2090
|
+
return;
|
|
2091
|
+
}
|
|
2092
|
+
if (typeof payload !== "object" || payload === null) return;
|
|
2093
|
+
const fields = payload;
|
|
2094
|
+
if (fields.stop_hook_active === true) return;
|
|
2095
|
+
const transcriptPath = typeof fields.transcript_path === "string" ? fields.transcript_path : "";
|
|
2096
|
+
if (transcriptPath.length === 0) return;
|
|
2097
|
+
let transcript;
|
|
2098
|
+
try {
|
|
2099
|
+
transcript = await readTranscript(transcriptPath);
|
|
2100
|
+
} catch {
|
|
2101
|
+
return;
|
|
2102
|
+
}
|
|
2103
|
+
const records = parseTranscript(transcript);
|
|
2104
|
+
const evaluation = evaluateStopHook({
|
|
2105
|
+
records,
|
|
2106
|
+
// stop_hook_active was already handled by the early return above.
|
|
2107
|
+
stopHookActive: false,
|
|
2108
|
+
...options.minActions !== void 0 ? { minActions: options.minActions } : {}
|
|
2109
|
+
});
|
|
2110
|
+
if (evaluation.kind !== "nudge") return;
|
|
2111
|
+
write(
|
|
2112
|
+
`${JSON.stringify({
|
|
2113
|
+
hookSpecificOutput: {
|
|
2114
|
+
hookEventName: "Stop",
|
|
2115
|
+
additionalContext: evaluation.additionalContext
|
|
2116
|
+
}
|
|
2117
|
+
})}
|
|
2118
|
+
`
|
|
2119
|
+
);
|
|
2120
|
+
}
|
|
2121
|
+
function parseTranscript(transcript) {
|
|
2122
|
+
const records = [];
|
|
2123
|
+
for (const line of transcript.split(/\r?\n/)) {
|
|
2124
|
+
if (line.trim().length === 0) continue;
|
|
2125
|
+
try {
|
|
2126
|
+
const parsed = JSON.parse(line);
|
|
2127
|
+
if (typeof parsed === "object" && parsed !== null && !Array.isArray(parsed)) {
|
|
2128
|
+
records.push(parsed);
|
|
2129
|
+
}
|
|
2130
|
+
} catch {
|
|
2131
|
+
}
|
|
2132
|
+
}
|
|
2133
|
+
return records;
|
|
2134
|
+
}
|
|
2135
|
+
async function defaultReadStdin() {
|
|
2136
|
+
if (process.stdin.isTTY === true) return "";
|
|
2137
|
+
const chunks = [];
|
|
2138
|
+
for await (const chunk of process.stdin) {
|
|
2139
|
+
chunks.push(chunk);
|
|
2140
|
+
}
|
|
2141
|
+
return Buffer.concat(chunks).toString("utf8");
|
|
2142
|
+
}
|
|
2143
|
+
async function readTranscriptBounded(path, maxBytes = MAX_TRANSCRIPT_BYTES) {
|
|
2144
|
+
const { size } = await stat2(path);
|
|
2145
|
+
if (size <= maxBytes) return readFile2(path, "utf8");
|
|
2146
|
+
const handle = await open(path, "r");
|
|
2147
|
+
try {
|
|
2148
|
+
const buffer = Buffer.alloc(maxBytes);
|
|
2149
|
+
const { bytesRead } = await handle.read(buffer, 0, maxBytes, size - maxBytes);
|
|
2150
|
+
const text = buffer.subarray(0, bytesRead).toString("utf8");
|
|
2151
|
+
const firstNewline = text.indexOf("\n");
|
|
2152
|
+
return firstNewline >= 0 ? text.slice(firstNewline + 1) : text;
|
|
2153
|
+
} finally {
|
|
2154
|
+
await handle.close();
|
|
2155
|
+
}
|
|
2156
|
+
}
|
|
2157
|
+
function parseMinActions(raw) {
|
|
2158
|
+
if (raw === void 0 || !/^\d+$/.test(raw)) return void 0;
|
|
2159
|
+
return Number(raw);
|
|
2160
|
+
}
|
|
2161
|
+
|
|
2007
2162
|
// src/commands/import.ts
|
|
2008
2163
|
import { createReadStream } from "fs";
|
|
2009
|
-
import { readdir, readFile as
|
|
2164
|
+
import { readdir, readFile as readFile3, rm, stat as stat3 } from "fs/promises";
|
|
2010
2165
|
import { homedir as homedir4 } from "os";
|
|
2011
2166
|
import { basename as basename2, dirname, join as join5, resolve as resolve4 } from "path";
|
|
2012
2167
|
import { createInterface } from "readline";
|
|
@@ -2404,7 +2559,7 @@ async function selectTranscriptFiles(projectsRoot, projectPaths, options) {
|
|
|
2404
2559
|
}
|
|
2405
2560
|
async function pathExists(file) {
|
|
2406
2561
|
try {
|
|
2407
|
-
await
|
|
2562
|
+
await stat3(file);
|
|
2408
2563
|
return true;
|
|
2409
2564
|
} catch (error) {
|
|
2410
2565
|
if (findErrorCode5(error, "ENOENT")) return false;
|
|
@@ -2413,7 +2568,7 @@ async function pathExists(file) {
|
|
|
2413
2568
|
}
|
|
2414
2569
|
async function statSize(file) {
|
|
2415
2570
|
try {
|
|
2416
|
-
return (await
|
|
2571
|
+
return (await stat3(file)).size;
|
|
2417
2572
|
} catch (error) {
|
|
2418
2573
|
if (findErrorCode5(error, "ENOENT")) return void 0;
|
|
2419
2574
|
throw error;
|
|
@@ -2499,7 +2654,7 @@ async function readFirstLine(file) {
|
|
|
2499
2654
|
async function readJsonlRecords(file) {
|
|
2500
2655
|
let buffer;
|
|
2501
2656
|
try {
|
|
2502
|
-
buffer = await
|
|
2657
|
+
buffer = await readFile3(file);
|
|
2503
2658
|
} catch (error) {
|
|
2504
2659
|
if (findErrorCode5(error, "ENOENT")) {
|
|
2505
2660
|
throw new Error("Source log not found", { cause: error });
|
|
@@ -4858,7 +5013,7 @@ function renderProjectRename(result) {
|
|
|
4858
5013
|
}
|
|
4859
5014
|
|
|
4860
5015
|
// src/commands/protocol.ts
|
|
4861
|
-
import { readFile as
|
|
5016
|
+
import { readFile as readFile4 } from "fs/promises";
|
|
4862
5017
|
import {
|
|
4863
5018
|
PROTOCOL_END,
|
|
4864
5019
|
PROTOCOL_START,
|
|
@@ -4869,7 +5024,7 @@ import {
|
|
|
4869
5024
|
|
|
4870
5025
|
// src/lib/durable-write.ts
|
|
4871
5026
|
import { randomUUID } from "crypto";
|
|
4872
|
-
import { lstat, open, rename, stat as
|
|
5027
|
+
import { lstat, open as open2, rename, stat as stat4, unlink as unlink2 } from "fs/promises";
|
|
4873
5028
|
import { basename as basename5, dirname as dirname3, join as join8 } from "path";
|
|
4874
5029
|
async function assertNotSymlink(targetPath) {
|
|
4875
5030
|
try {
|
|
@@ -4889,7 +5044,7 @@ async function writeFileDurable(targetPath, content) {
|
|
|
4889
5044
|
const tmpPath = join8(dir, `.${basename5(targetPath)}.tmp.${randomUUID()}`);
|
|
4890
5045
|
let mode = 420;
|
|
4891
5046
|
try {
|
|
4892
|
-
mode = (await
|
|
5047
|
+
mode = (await stat4(targetPath)).mode & 511;
|
|
4893
5048
|
} catch (error) {
|
|
4894
5049
|
if (!(error instanceof Error && error.code === "ENOENT")) {
|
|
4895
5050
|
throw error;
|
|
@@ -4897,7 +5052,7 @@ async function writeFileDurable(targetPath, content) {
|
|
|
4897
5052
|
}
|
|
4898
5053
|
let handle;
|
|
4899
5054
|
try {
|
|
4900
|
-
handle = await
|
|
5055
|
+
handle = await open2(tmpPath, "wx", mode);
|
|
4901
5056
|
await handle.writeFile(content, "utf8");
|
|
4902
5057
|
await handle.chmod(mode);
|
|
4903
5058
|
await handle.sync();
|
|
@@ -4910,7 +5065,7 @@ async function writeFileDurable(targetPath, content) {
|
|
|
4910
5065
|
throw error;
|
|
4911
5066
|
}
|
|
4912
5067
|
try {
|
|
4913
|
-
const dirHandle = await
|
|
5068
|
+
const dirHandle = await open2(dir, "r");
|
|
4914
5069
|
try {
|
|
4915
5070
|
await dirHandle.sync();
|
|
4916
5071
|
} finally {
|
|
@@ -5041,7 +5196,7 @@ async function readProtocolSources(entries) {
|
|
|
5041
5196
|
for (const entry of entries) {
|
|
5042
5197
|
let content;
|
|
5043
5198
|
try {
|
|
5044
|
-
content = await
|
|
5199
|
+
content = await readFile4(entry.source, "utf8");
|
|
5045
5200
|
} catch (error) {
|
|
5046
5201
|
if (error instanceof Error && error.code === "ENOENT") {
|
|
5047
5202
|
throw new Error(
|
|
@@ -5181,7 +5336,7 @@ import { assertBasouRootSafe as assertBasouRootSafe9, basouPaths as basouPaths11
|
|
|
5181
5336
|
import { InvalidArgumentError as InvalidArgumentError3 } from "commander";
|
|
5182
5337
|
|
|
5183
5338
|
// src/commands/refresh-watch.ts
|
|
5184
|
-
import { readdir as readdir2, stat as
|
|
5339
|
+
import { readdir as readdir2, stat as stat5 } from "fs/promises";
|
|
5185
5340
|
import { homedir as homedir7 } from "os";
|
|
5186
5341
|
import { join as join10 } from "path";
|
|
5187
5342
|
import { findErrorCode as findErrorCode8 } from "@basou/core";
|
|
@@ -5210,7 +5365,7 @@ async function scanSourceLogs(roots) {
|
|
|
5210
5365
|
await walk(full);
|
|
5211
5366
|
} else if (entry.isFile() && entry.name.endsWith(".jsonl")) {
|
|
5212
5367
|
try {
|
|
5213
|
-
const info = await
|
|
5368
|
+
const info = await stat5(full);
|
|
5214
5369
|
out.set(full, { mtimeMs: info.mtimeMs, size: info.size });
|
|
5215
5370
|
} catch (error) {
|
|
5216
5371
|
if (findErrorCode8(error, "ENOENT")) continue;
|
|
@@ -6128,7 +6283,7 @@ async function resolveRepositoryRootForRun(cwd) {
|
|
|
6128
6283
|
}
|
|
6129
6284
|
|
|
6130
6285
|
// src/commands/session.ts
|
|
6131
|
-
import { readFile as
|
|
6286
|
+
import { readFile as readFile5 } from "fs/promises";
|
|
6132
6287
|
import { basename as basename6, isAbsolute as isAbsolute6, join as join12, relative as relative3 } from "path";
|
|
6133
6288
|
import {
|
|
6134
6289
|
acquireLock as acquireLock6,
|
|
@@ -6573,7 +6728,7 @@ async function doRunSessionImport(options, ctx) {
|
|
|
6573
6728
|
}
|
|
6574
6729
|
async function readInputFile(path) {
|
|
6575
6730
|
try {
|
|
6576
|
-
return await
|
|
6731
|
+
return await readFile5(path, "utf8");
|
|
6577
6732
|
} catch (error) {
|
|
6578
6733
|
if (findErrorCode11(error, "ENOENT")) {
|
|
6579
6734
|
throw new Error("Import source not found", { cause: error });
|
|
@@ -6690,7 +6845,7 @@ async function doRunSessionNote(sessionIdInput, options, ctx) {
|
|
|
6690
6845
|
}
|
|
6691
6846
|
async function readNoteFile(path) {
|
|
6692
6847
|
try {
|
|
6693
|
-
return await
|
|
6848
|
+
return await readFile5(path, "utf8");
|
|
6694
6849
|
} catch (error) {
|
|
6695
6850
|
if (findErrorCode11(error, "ENOENT")) {
|
|
6696
6851
|
throw new Error("Note source not found", { cause: error });
|
|
@@ -6997,7 +7152,7 @@ async function resolveRepositoryRootForStatus(cwd) {
|
|
|
6997
7152
|
}
|
|
6998
7153
|
|
|
6999
7154
|
// src/commands/task.ts
|
|
7000
|
-
import { readFile as
|
|
7155
|
+
import { readFile as readFile6 } from "fs/promises";
|
|
7001
7156
|
import { join as join13 } from "path";
|
|
7002
7157
|
import {
|
|
7003
7158
|
archiveTask,
|
|
@@ -8013,7 +8168,7 @@ function parsePositiveInt2(raw) {
|
|
|
8013
8168
|
}
|
|
8014
8169
|
async function readDescriptionFile(path) {
|
|
8015
8170
|
try {
|
|
8016
|
-
return await
|
|
8171
|
+
return await readFile6(path, "utf8");
|
|
8017
8172
|
} catch (error) {
|
|
8018
8173
|
if (findErrorCode14(error, "ENOENT")) {
|
|
8019
8174
|
throw new Error("Description source not found", { cause: error });
|
|
@@ -9699,6 +9854,7 @@ function buildProgram() {
|
|
|
9699
9854
|
registerReviewGapsCommand(program);
|
|
9700
9855
|
registerProjectCommand(program);
|
|
9701
9856
|
registerProtocolCommand(program);
|
|
9857
|
+
registerHookCommand(program);
|
|
9702
9858
|
return program;
|
|
9703
9859
|
}
|
|
9704
9860
|
export {
|