@basou/cli 0.44.0 → 0.45.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 +421 -152
- package/dist/index.js.map +1 -1
- package/dist/program.d.ts +33 -1
- package/dist/program.js +423 -152
- package/dist/program.js.map +1 -1
- package/package.json +2 -2
package/dist/program.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
// src/program.ts
|
|
2
2
|
import { createRequire } from "module";
|
|
3
|
+
import * as basouCore from "@basou/core";
|
|
3
4
|
import { Command } from "commander";
|
|
4
5
|
|
|
5
6
|
// src/commands/approval.ts
|
|
@@ -16,6 +17,7 @@ import {
|
|
|
16
17
|
enumerateApprovals,
|
|
17
18
|
findErrorCode,
|
|
18
19
|
isLazyExpired,
|
|
20
|
+
LOCAL_CLI_EVENT_SOURCE,
|
|
19
21
|
linkYamlFile,
|
|
20
22
|
loadApproval,
|
|
21
23
|
prefixedUlid,
|
|
@@ -369,7 +371,7 @@ async function doRunApprovalResolve(idInput, options, ctx, decision) {
|
|
|
369
371
|
id: eventId,
|
|
370
372
|
session_id: approval.session_id,
|
|
371
373
|
occurred_at: occurredAt,
|
|
372
|
-
source:
|
|
374
|
+
source: LOCAL_CLI_EVENT_SOURCE,
|
|
373
375
|
type: "approval_approved",
|
|
374
376
|
approval_id: approval.id,
|
|
375
377
|
resolver: "local-cli",
|
|
@@ -382,7 +384,7 @@ async function doRunApprovalResolve(idInput, options, ctx, decision) {
|
|
|
382
384
|
id: eventId,
|
|
383
385
|
session_id: approval.session_id,
|
|
384
386
|
occurred_at: occurredAt,
|
|
385
|
-
source:
|
|
387
|
+
source: LOCAL_CLI_EVENT_SOURCE,
|
|
386
388
|
type: "approval_rejected",
|
|
387
389
|
approval_id: approval.id,
|
|
388
390
|
resolver: "local-cli",
|
|
@@ -861,12 +863,13 @@ import {
|
|
|
861
863
|
acquireLock as acquireLock2,
|
|
862
864
|
appendEventToExistingSession,
|
|
863
865
|
assertBasouRootSafe as assertBasouRootSafe2,
|
|
864
|
-
basouPaths as
|
|
866
|
+
basouPaths as basouPaths4,
|
|
865
867
|
classifyFilesBySourceRoot,
|
|
866
868
|
createAdHocSessionWithEvent,
|
|
867
869
|
EVENT_SCHEMA_VERSION as EVENT_SCHEMA_VERSION2,
|
|
868
870
|
findErrorCode as findErrorCode2,
|
|
869
871
|
isValidPrefixedId,
|
|
872
|
+
LOCAL_CLI_EVENT_SOURCE as LOCAL_CLI_EVENT_SOURCE2,
|
|
870
873
|
loadSessionEntries,
|
|
871
874
|
prefixedUlid as prefixedUlid2,
|
|
872
875
|
readManifest as readManifest2,
|
|
@@ -875,7 +878,7 @@ import {
|
|
|
875
878
|
resolveSessionId,
|
|
876
879
|
sanitizePath
|
|
877
880
|
} from "@basou/core";
|
|
878
|
-
import { InvalidArgumentError } from "commander";
|
|
881
|
+
import { InvalidArgumentError as InvalidArgumentError2 } from "commander";
|
|
879
882
|
|
|
880
883
|
// src/lib/repo-root.ts
|
|
881
884
|
import { realpath, stat as stat2 } from "fs/promises";
|
|
@@ -1035,11 +1038,165 @@ async function realpathOrNull(p) {
|
|
|
1035
1038
|
}
|
|
1036
1039
|
}
|
|
1037
1040
|
|
|
1041
|
+
// src/commands/decision-gaps.ts
|
|
1042
|
+
import {
|
|
1043
|
+
basouPaths as basouPaths3,
|
|
1044
|
+
findDecisionGaps
|
|
1045
|
+
} from "@basou/core";
|
|
1046
|
+
import { InvalidArgumentError } from "commander";
|
|
1047
|
+
var DEFAULT_GAP_LIMIT = 20;
|
|
1048
|
+
function parseLimit(value) {
|
|
1049
|
+
const n = /^\d+$/u.test(value.trim()) ? Number(value.trim()) : Number.NaN;
|
|
1050
|
+
if (!Number.isInteger(n) || n < 0) {
|
|
1051
|
+
throw new InvalidArgumentError("--limit must be a non-negative integer.");
|
|
1052
|
+
}
|
|
1053
|
+
return n;
|
|
1054
|
+
}
|
|
1055
|
+
function parseSince(value, now = /* @__PURE__ */ new Date()) {
|
|
1056
|
+
const duration = /^(\d+)([dhm])$/u.exec(value.trim());
|
|
1057
|
+
if (duration !== null) {
|
|
1058
|
+
const n = Number(duration[1]);
|
|
1059
|
+
const ms = { d: 864e5, h: 36e5, m: 6e4 }[duration[2]];
|
|
1060
|
+
return new Date(now.getTime() - n * ms).toISOString();
|
|
1061
|
+
}
|
|
1062
|
+
if (/^\d+$/u.test(value.trim())) {
|
|
1063
|
+
throw new InvalidArgumentError("--since needs a unit (7d, 36h, 90m) or a full ISO timestamp.");
|
|
1064
|
+
}
|
|
1065
|
+
const parsed = Date.parse(value);
|
|
1066
|
+
if (Number.isNaN(parsed)) {
|
|
1067
|
+
throw new InvalidArgumentError(
|
|
1068
|
+
"--since must be an ISO timestamp or a duration (7d, 36h, 90m)."
|
|
1069
|
+
);
|
|
1070
|
+
}
|
|
1071
|
+
return new Date(parsed).toISOString();
|
|
1072
|
+
}
|
|
1073
|
+
function registerDecisionGapsCommand(decision) {
|
|
1074
|
+
decision.command("gaps").description("List recorded decisions that no task carries (read-only, advisory)").option(
|
|
1075
|
+
"--since <when>",
|
|
1076
|
+
"Only decisions recorded at or after this ISO instant or duration back (7d, 36h)"
|
|
1077
|
+
).option(
|
|
1078
|
+
"--limit <n>",
|
|
1079
|
+
`Maximum entries to list; the rest are counted (default ${DEFAULT_GAP_LIMIT}, 0 = no limit)`,
|
|
1080
|
+
parseLimit
|
|
1081
|
+
).option("--json", "Output the result as JSON").option("-v, --verbose", "Show error causes").action(async (opts) => {
|
|
1082
|
+
await runDecisionGaps(opts);
|
|
1083
|
+
});
|
|
1084
|
+
}
|
|
1085
|
+
async function runDecisionGaps(options, ctx = {}) {
|
|
1086
|
+
try {
|
|
1087
|
+
await doRunDecisionGaps(options, ctx);
|
|
1088
|
+
} catch (error) {
|
|
1089
|
+
renderCliError(error, { verbose: isVerbose(options) });
|
|
1090
|
+
process.exitCode = 1;
|
|
1091
|
+
}
|
|
1092
|
+
}
|
|
1093
|
+
async function doRunDecisionGaps(options, ctx) {
|
|
1094
|
+
const cwd = ctx.cwd ?? process.cwd();
|
|
1095
|
+
const repositoryRoot = await resolveBasouRootForCommand(cwd, "decision gaps");
|
|
1096
|
+
const paths = basouPaths3(repositoryRoot);
|
|
1097
|
+
const now = ctx.nowProvider?.() ?? /* @__PURE__ */ new Date();
|
|
1098
|
+
const limit = options.limit ?? DEFAULT_GAP_LIMIT;
|
|
1099
|
+
const summary = await findDecisionGaps({
|
|
1100
|
+
paths,
|
|
1101
|
+
nowIso: now.toISOString(),
|
|
1102
|
+
...options.since !== void 0 ? { start: parseSince(options.since, now) } : {},
|
|
1103
|
+
...limit > 0 ? { limit } : {},
|
|
1104
|
+
onWarning: (w, sid) => printReplayWarning(w, sid),
|
|
1105
|
+
onSessionSkip: (sid, reason) => printSessionSkip(sid, reason)
|
|
1106
|
+
});
|
|
1107
|
+
if (options.json === true) {
|
|
1108
|
+
console.log(JSON.stringify(summary));
|
|
1109
|
+
} else {
|
|
1110
|
+
console.log(renderDecisionGaps(summary));
|
|
1111
|
+
}
|
|
1112
|
+
return summary;
|
|
1113
|
+
}
|
|
1114
|
+
function relAge(iso, now) {
|
|
1115
|
+
const ms = now.getTime() - Date.parse(iso);
|
|
1116
|
+
if (!Number.isFinite(ms)) return "(unknown)";
|
|
1117
|
+
if (ms < 0) return "clock ahead";
|
|
1118
|
+
const days = Math.floor(ms / 864e5);
|
|
1119
|
+
if (days >= 1) return `${days}d ago`;
|
|
1120
|
+
const hours = Math.floor(ms / 36e5);
|
|
1121
|
+
if (hours >= 1) return `${hours}h ago`;
|
|
1122
|
+
return `${Math.max(1, Math.floor(ms / 6e4))}m ago`;
|
|
1123
|
+
}
|
|
1124
|
+
var GRAPHEMES = new Intl.Segmenter(void 0, { granularity: "grapheme" });
|
|
1125
|
+
function oneLine(value, max) {
|
|
1126
|
+
const flat = [...GRAPHEMES.segment(value.replace(/\s+/gu, " ").trim())].map((s) => s.segment);
|
|
1127
|
+
return flat.length > max ? `${flat.slice(0, max - 1).join("")}\u2026` : flat.join("");
|
|
1128
|
+
}
|
|
1129
|
+
function gapLine(d, now) {
|
|
1130
|
+
return `- ${oneLine(d.title, 100)}
|
|
1131
|
+
${relAge(d.recordedAt, now)} \xB7 ${d.decisionId}`;
|
|
1132
|
+
}
|
|
1133
|
+
function incompleteLine(summary) {
|
|
1134
|
+
const { sessions, tasks, unknownReferences } = summary.incomplete;
|
|
1135
|
+
const parts = [];
|
|
1136
|
+
if (sessions > 0) {
|
|
1137
|
+
parts.push(
|
|
1138
|
+
`${sessions} session${sessions === 1 ? "" : "s"} could not be read in full, so a decision recorded there may be missing from these numbers, and a \`decision void\` recorded there may not have been applied`
|
|
1139
|
+
);
|
|
1140
|
+
}
|
|
1141
|
+
if (tasks > 0) {
|
|
1142
|
+
parts.push(
|
|
1143
|
+
`${tasks} task file${tasks === 1 ? "" : "s"} could not be read, so a decision one of them carries is listed above as if nothing did`
|
|
1144
|
+
);
|
|
1145
|
+
}
|
|
1146
|
+
if (unknownReferences > 0) {
|
|
1147
|
+
parts.push(
|
|
1148
|
+
`${unknownReferences} decision id${unknownReferences === 1 ? "" : "s"} named by a task ${unknownReferences === 1 ? "matches" : "match"} no decision in this store, and ${unknownReferences === 1 ? "was" : "were"} not counted as carrying anything`
|
|
1149
|
+
);
|
|
1150
|
+
}
|
|
1151
|
+
return parts.length === 0 ? null : `\u26A0\uFE0F ${parts.join("; ")}.`;
|
|
1152
|
+
}
|
|
1153
|
+
function renderDecisionGaps(summary) {
|
|
1154
|
+
const now = new Date(summary.generatedAt);
|
|
1155
|
+
const { excluded, scope } = summary;
|
|
1156
|
+
const lines = ["# Decision gaps", ""];
|
|
1157
|
+
if (summary.populationCount === 0) {
|
|
1158
|
+
lines.push(
|
|
1159
|
+
`No decision recorded since ${scope.start} by \`basou decision capture\` or \`basou decision record\` is still open, so there is nothing to check yet. This list fills as decisions are recorded.`
|
|
1160
|
+
);
|
|
1161
|
+
} else if (summary.gaps.length === 0) {
|
|
1162
|
+
lines.push(
|
|
1163
|
+
`\u2705 Within what was checked, each of the ${summary.populationCount} open decision${summary.populationCount === 1 ? "" : "s"} in scope has a task carrying it.`
|
|
1164
|
+
);
|
|
1165
|
+
} else {
|
|
1166
|
+
const total = summary.gaps.length + summary.truncated;
|
|
1167
|
+
lines.push(`\u26A0\uFE0F Open decisions no task carries: ${total} of ${summary.populationCount} in scope`);
|
|
1168
|
+
lines.push("");
|
|
1169
|
+
for (const d of summary.gaps) lines.push(gapLine(d, now));
|
|
1170
|
+
if (summary.truncated > 0) {
|
|
1171
|
+
lines.push(` ... +${summary.truncated} more (--limit 0 to list them all, or --json)`);
|
|
1172
|
+
}
|
|
1173
|
+
}
|
|
1174
|
+
lines.push("");
|
|
1175
|
+
lines.push("## Scope");
|
|
1176
|
+
lines.push(
|
|
1177
|
+
`- Checked: decisions recorded at or after ${scope.start} with source \`${scope.source}\` (i.e. recorded by running basou, not derived from a transcript by an importer), still open, and not a track.`
|
|
1178
|
+
);
|
|
1179
|
+
lines.push(
|
|
1180
|
+
`- Not checked: ${excluded.byStart} recorded earlier, then ${excluded.bySource} recorded by something other than basou itself, then ${excluded.track} tracks (already shown every session by \`basou orient\` until closed), then ${excluded.voided} closed with \`basou decision void\`.`
|
|
1181
|
+
);
|
|
1182
|
+
lines.push(
|
|
1183
|
+
`- Task files read: ${summary.tasksScanned} (live and archived); ${summary.carried} of the decisions in scope are carried by one.`
|
|
1184
|
+
);
|
|
1185
|
+
const incomplete = incompleteLine(summary);
|
|
1186
|
+
if (incomplete !== null) lines.push(`- ${incomplete}`);
|
|
1187
|
+
lines.push("");
|
|
1188
|
+
lines.push(
|
|
1189
|
+
"Note: read-only advisory. A task carries a decision when it names that decision's full id; an abbreviated id names no one decision, because a captured batch shares a millisecond and its ids differ only at the end. It does not read what a decision means, and it does not enforce."
|
|
1190
|
+
);
|
|
1191
|
+
return lines.join("\n");
|
|
1192
|
+
}
|
|
1193
|
+
|
|
1038
1194
|
// src/commands/decision.ts
|
|
1039
1195
|
var LABEL_TITLE_MAX = 80;
|
|
1040
1196
|
var LABEL_TRUNCATE_HEAD = LABEL_TITLE_MAX - 3;
|
|
1041
1197
|
function registerDecisionCommand(program) {
|
|
1042
1198
|
const decision = program.command("decision").description("Record human-authored decisions as events");
|
|
1199
|
+
registerDecisionGapsCommand(decision);
|
|
1043
1200
|
decision.command("record").description("Record a decision_recorded event").requiredOption("--title <text>", "Decision title", parseTitle).option("--rationale <text>", "Rationale for the decision", parseRationale).option(
|
|
1044
1201
|
"--rejected-reason <text>",
|
|
1045
1202
|
"Reason rejected alternatives were not chosen",
|
|
@@ -1161,7 +1318,7 @@ function warnTrackMarkerWithoutKind(decisions, markerWithoutKind) {
|
|
|
1161
1318
|
async function doRunDecisionRecord(options, ctx) {
|
|
1162
1319
|
const cwd = ctx.cwd ?? process.cwd();
|
|
1163
1320
|
const repositoryRoot = await resolveRepositoryRootForDecision(cwd);
|
|
1164
|
-
const paths =
|
|
1321
|
+
const paths = basouPaths4(repositoryRoot);
|
|
1165
1322
|
await assertWorkspaceInitialized2(paths.root);
|
|
1166
1323
|
const now = ctx.nowProvider !== void 0 ? ctx.nowProvider() : /* @__PURE__ */ new Date();
|
|
1167
1324
|
const occurredAt = now.toISOString();
|
|
@@ -1252,7 +1409,7 @@ async function runDecisionCapture(options, ctx = {}) {
|
|
|
1252
1409
|
async function doRunDecisionCapture(options, ctx) {
|
|
1253
1410
|
const cwd = ctx.cwd ?? process.cwd();
|
|
1254
1411
|
const repositoryRoot = await resolveBasouRootForCommand(cwd, "decision capture");
|
|
1255
|
-
const paths =
|
|
1412
|
+
const paths = basouPaths4(repositoryRoot);
|
|
1256
1413
|
await assertWorkspaceInitialized2(paths.root);
|
|
1257
1414
|
const raw = await readCaptureInput(options, ctx);
|
|
1258
1415
|
const { decisions, markerWithoutKind } = parseCaptureInput(raw);
|
|
@@ -1334,7 +1491,7 @@ async function doRunDecisionVoid(decisionId, options, ctx) {
|
|
|
1334
1491
|
}
|
|
1335
1492
|
const cwd = ctx.cwd ?? process.cwd();
|
|
1336
1493
|
const repositoryRoot = await resolveBasouRootForCommand(cwd, "decision void");
|
|
1337
|
-
const paths =
|
|
1494
|
+
const paths = basouPaths4(repositoryRoot);
|
|
1338
1495
|
await assertWorkspaceInitialized2(paths.root);
|
|
1339
1496
|
if (!await decisionExists(paths, decisionId)) {
|
|
1340
1497
|
throw new Error(
|
|
@@ -1428,7 +1585,7 @@ function buildDecisionVoidedEvent(input) {
|
|
|
1428
1585
|
id: input.eventId,
|
|
1429
1586
|
session_id: input.sessionId,
|
|
1430
1587
|
occurred_at: input.occurredAt,
|
|
1431
|
-
source:
|
|
1588
|
+
source: LOCAL_CLI_EVENT_SOURCE2,
|
|
1432
1589
|
type: "decision_voided",
|
|
1433
1590
|
decision_id: input.decisionId,
|
|
1434
1591
|
...input.reason !== void 0 ? { reason: input.reason } : {},
|
|
@@ -1460,7 +1617,7 @@ function printVoidResult(options, result) {
|
|
|
1460
1617
|
}
|
|
1461
1618
|
function parseReason(raw) {
|
|
1462
1619
|
if (raw.trim().length === 0) {
|
|
1463
|
-
throw new
|
|
1620
|
+
throw new InvalidArgumentError2("--reason must not be empty");
|
|
1464
1621
|
}
|
|
1465
1622
|
return raw;
|
|
1466
1623
|
}
|
|
@@ -1694,7 +1851,7 @@ function buildDecisionEvent(input) {
|
|
|
1694
1851
|
id: input.eventId,
|
|
1695
1852
|
session_id: input.sessionId,
|
|
1696
1853
|
occurred_at: input.occurredAt,
|
|
1697
|
-
source:
|
|
1854
|
+
source: LOCAL_CLI_EVENT_SOURCE2,
|
|
1698
1855
|
type: "decision_recorded",
|
|
1699
1856
|
decision_id: input.decisionId,
|
|
1700
1857
|
title: input.title,
|
|
@@ -1712,25 +1869,25 @@ function buildAdHocLabel(title) {
|
|
|
1712
1869
|
}
|
|
1713
1870
|
function parseTitle(raw) {
|
|
1714
1871
|
if (isBlank(raw)) {
|
|
1715
|
-
throw new
|
|
1872
|
+
throw new InvalidArgumentError2("Title must not be empty");
|
|
1716
1873
|
}
|
|
1717
1874
|
return raw;
|
|
1718
1875
|
}
|
|
1719
1876
|
function parseRationale(raw) {
|
|
1720
1877
|
if (isBlank(raw)) {
|
|
1721
|
-
throw new
|
|
1878
|
+
throw new InvalidArgumentError2("Rationale must not be empty");
|
|
1722
1879
|
}
|
|
1723
1880
|
return raw;
|
|
1724
1881
|
}
|
|
1725
1882
|
function parseRejectedReason(raw) {
|
|
1726
1883
|
if (isBlank(raw)) {
|
|
1727
|
-
throw new
|
|
1884
|
+
throw new InvalidArgumentError2("Rejected reason must not be empty");
|
|
1728
1885
|
}
|
|
1729
1886
|
return raw;
|
|
1730
1887
|
}
|
|
1731
1888
|
function collectAlternative(value, prev) {
|
|
1732
1889
|
if (isBlank(value)) {
|
|
1733
|
-
throw new
|
|
1890
|
+
throw new InvalidArgumentError2("Alternative must not be empty");
|
|
1734
1891
|
}
|
|
1735
1892
|
return prev.concat(value);
|
|
1736
1893
|
}
|
|
@@ -1739,16 +1896,16 @@ function isValidEventId(value) {
|
|
|
1739
1896
|
}
|
|
1740
1897
|
function collectLinkedEvent(value, prev) {
|
|
1741
1898
|
if (!isValidEventId(value)) {
|
|
1742
|
-
throw new
|
|
1899
|
+
throw new InvalidArgumentError2(`Linked event id must match evt_<ULID>, got '${value}'`);
|
|
1743
1900
|
}
|
|
1744
1901
|
return prev.concat(value);
|
|
1745
1902
|
}
|
|
1746
1903
|
function collectLinkedFile(value, prev) {
|
|
1747
1904
|
if (isBlank(value)) {
|
|
1748
|
-
throw new
|
|
1905
|
+
throw new InvalidArgumentError2("Linked file path must not be empty");
|
|
1749
1906
|
}
|
|
1750
1907
|
if (value.length > 4096) {
|
|
1751
|
-
throw new
|
|
1908
|
+
throw new InvalidArgumentError2("Linked file path exceeds 4096 chars");
|
|
1752
1909
|
}
|
|
1753
1910
|
return prev.concat(value);
|
|
1754
1911
|
}
|
|
@@ -1813,7 +1970,7 @@ async function assertWorkspaceInitialized2(basouRoot) {
|
|
|
1813
1970
|
// src/commands/decisions.ts
|
|
1814
1971
|
import {
|
|
1815
1972
|
assertBasouRootSafe as assertBasouRootSafe3,
|
|
1816
|
-
basouPaths as
|
|
1973
|
+
basouPaths as basouPaths5,
|
|
1817
1974
|
findErrorCode as findErrorCode3,
|
|
1818
1975
|
readMarkdownFile as readMarkdownFile2,
|
|
1819
1976
|
renderDecisions,
|
|
@@ -1839,7 +1996,7 @@ async function doRunDecisionsGenerate(options, ctx) {
|
|
|
1839
1996
|
void options;
|
|
1840
1997
|
const cwd = ctx.cwd ?? process.cwd();
|
|
1841
1998
|
const repositoryRoot = await resolveRepositoryRootForDecisions(cwd);
|
|
1842
|
-
const paths =
|
|
1999
|
+
const paths = basouPaths5(repositoryRoot);
|
|
1843
2000
|
await assertWorkspaceInitialized3(paths.root);
|
|
1844
2001
|
const nowIso = (ctx.nowProvider?.() ?? /* @__PURE__ */ new Date()).toISOString();
|
|
1845
2002
|
const result = await renderDecisions({
|
|
@@ -1884,7 +2041,7 @@ import { join as join6 } from "path";
|
|
|
1884
2041
|
import {
|
|
1885
2042
|
acquireLock as acquireLock3,
|
|
1886
2043
|
assertBasouRootSafe as assertBasouRootSafe4,
|
|
1887
|
-
basouPaths as
|
|
2044
|
+
basouPaths as basouPaths6,
|
|
1888
2045
|
ChildProcessRunner,
|
|
1889
2046
|
appendChainedEvent as coreAppendChainedEvent,
|
|
1890
2047
|
EVENT_SCHEMA_VERSION as EVENT_SCHEMA_VERSION3,
|
|
@@ -1896,6 +2053,7 @@ import {
|
|
|
1896
2053
|
readManifest as readManifest3,
|
|
1897
2054
|
readYamlFile as readYamlFile3,
|
|
1898
2055
|
resolveRepositoryRoot as resolveRepositoryRoot4,
|
|
2056
|
+
SESSION_SCHEMA_VERSION,
|
|
1899
2057
|
SessionSchema,
|
|
1900
2058
|
sanitizeWorkingDirectory,
|
|
1901
2059
|
writeObservedDuration,
|
|
@@ -1918,7 +2076,7 @@ async function runExec(command, args, options, ctx = {}) {
|
|
|
1918
2076
|
const cwd = options.cwd ?? process.cwd();
|
|
1919
2077
|
const timeout_ms = options.timeout !== void 0 ? parseDuration(options.timeout) : void 0;
|
|
1920
2078
|
const repoRoot = await resolveRepositoryRootForExec(cwd);
|
|
1921
|
-
const paths =
|
|
2079
|
+
const paths = basouPaths6(repoRoot);
|
|
1922
2080
|
await assertBasouRootSafe4(paths.root);
|
|
1923
2081
|
const manifest = await readManifest3(paths);
|
|
1924
2082
|
const sessionId = prefixedUlid3("ses");
|
|
@@ -2131,7 +2289,7 @@ function normalizeGitSnapshotSkipMessage(error) {
|
|
|
2131
2289
|
function buildInitialSession(input) {
|
|
2132
2290
|
const cmdline = [input.command, ...input.args].join(" ");
|
|
2133
2291
|
return {
|
|
2134
|
-
schema_version:
|
|
2292
|
+
schema_version: SESSION_SCHEMA_VERSION,
|
|
2135
2293
|
session: {
|
|
2136
2294
|
id: input.id,
|
|
2137
2295
|
label: `basou exec ${cmdline} (${input.startedAt})`,
|
|
@@ -2216,12 +2374,11 @@ async function resolveRepositoryRootForExec(cwd) {
|
|
|
2216
2374
|
// src/commands/handoff.ts
|
|
2217
2375
|
import {
|
|
2218
2376
|
assertBasouRootSafe as assertBasouRootSafe5,
|
|
2219
|
-
basouPaths as
|
|
2377
|
+
basouPaths as basouPaths7,
|
|
2220
2378
|
findErrorCode as findErrorCode4,
|
|
2221
2379
|
readMarkdownFile as readMarkdownFile3,
|
|
2222
2380
|
renderHandoff,
|
|
2223
2381
|
renderWithMarkers as renderWithMarkers2,
|
|
2224
|
-
resolveRepositoryRoot as resolveRepositoryRoot5,
|
|
2225
2382
|
writeMarkdownFile as writeMarkdownFile2
|
|
2226
2383
|
} from "@basou/core";
|
|
2227
2384
|
function registerHandoffCommand(program) {
|
|
@@ -2242,7 +2399,7 @@ async function doRunHandoffGenerate(options, ctx) {
|
|
|
2242
2399
|
void options;
|
|
2243
2400
|
const cwd = ctx.cwd ?? process.cwd();
|
|
2244
2401
|
const repositoryRoot = await resolveRepositoryRootForHandoff(cwd);
|
|
2245
|
-
const paths =
|
|
2402
|
+
const paths = basouPaths7(repositoryRoot);
|
|
2246
2403
|
await assertWorkspaceInitialized4(paths.root);
|
|
2247
2404
|
const nowIso = (ctx.nowProvider?.() ?? /* @__PURE__ */ new Date()).toISOString();
|
|
2248
2405
|
const result = await renderHandoff({
|
|
@@ -2261,7 +2418,7 @@ async function doRunHandoffGenerate(options, ctx) {
|
|
|
2261
2418
|
}
|
|
2262
2419
|
async function resolveRepositoryRootForHandoff(cwd) {
|
|
2263
2420
|
try {
|
|
2264
|
-
return await
|
|
2421
|
+
return await resolveBasouRootForCommand(cwd, "handoff generate");
|
|
2265
2422
|
} catch (error) {
|
|
2266
2423
|
if (error instanceof Error && error.message === "Not a git repository") {
|
|
2267
2424
|
throw new Error(
|
|
@@ -2284,10 +2441,12 @@ async function assertWorkspaceInitialized4(basouRoot) {
|
|
|
2284
2441
|
}
|
|
2285
2442
|
|
|
2286
2443
|
// src/commands/hook.ts
|
|
2444
|
+
import { execFile } from "child_process";
|
|
2287
2445
|
import { open as open2, readFile as readFile3, realpath as realpath3, stat as stat4 } from "fs/promises";
|
|
2288
2446
|
import { homedir as homedir7 } from "os";
|
|
2289
2447
|
import { join as join9 } from "path";
|
|
2290
2448
|
import { fileURLToPath } from "url";
|
|
2449
|
+
import { promisify } from "util";
|
|
2291
2450
|
import {
|
|
2292
2451
|
buildSessionStartHookCommand,
|
|
2293
2452
|
buildStopHookCommand,
|
|
@@ -2558,7 +2717,7 @@ async function warnIfPositionNamesOtherWorkspaces(args) {
|
|
|
2558
2717
|
// src/commands/orient.ts
|
|
2559
2718
|
import {
|
|
2560
2719
|
assertBasouRootSafe as assertBasouRootSafe7,
|
|
2561
|
-
basouPaths as
|
|
2720
|
+
basouPaths as basouPaths9,
|
|
2562
2721
|
findErrorCode as findErrorCode6,
|
|
2563
2722
|
renderOrientation as renderOrientation2,
|
|
2564
2723
|
writeMarkdownFile as writeMarkdownFile4
|
|
@@ -2639,7 +2798,7 @@ import { createInterface } from "readline";
|
|
|
2639
2798
|
import {
|
|
2640
2799
|
AGENT_INFRA_DIRS as AGENT_INFRA_DIRS2,
|
|
2641
2800
|
assertBasouRootSafe as assertBasouRootSafe6,
|
|
2642
|
-
basouPaths as
|
|
2801
|
+
basouPaths as basouPaths8,
|
|
2643
2802
|
CLAUDE_IMPORT_SOURCE,
|
|
2644
2803
|
CODEX_IMPORT_SOURCE,
|
|
2645
2804
|
classifyFilesBySourceRoot as classifyFilesBySourceRoot2,
|
|
@@ -2651,7 +2810,7 @@ import {
|
|
|
2651
2810
|
readManifest as readManifest4,
|
|
2652
2811
|
readSessionYaml as readSessionYaml2,
|
|
2653
2812
|
reimportPreservingId,
|
|
2654
|
-
resolveRepositoryRoot as
|
|
2813
|
+
resolveRepositoryRoot as resolveRepositoryRoot5,
|
|
2655
2814
|
SESSION_IMPORT_SCHEMA_VERSION,
|
|
2656
2815
|
SessionImportPayloadSchema
|
|
2657
2816
|
} from "@basou/core";
|
|
@@ -2798,7 +2957,7 @@ function assertSelector(options) {
|
|
|
2798
2957
|
async function resolveImportTarget(ctx) {
|
|
2799
2958
|
const cwd = ctx.cwd ?? process.cwd();
|
|
2800
2959
|
const repositoryRoot = await resolveRepositoryRootForImport(cwd);
|
|
2801
|
-
const paths =
|
|
2960
|
+
const paths = basouPaths8(repositoryRoot);
|
|
2802
2961
|
await assertWorkspaceInitialized5(paths.root);
|
|
2803
2962
|
const manifest = await readManifest4(paths);
|
|
2804
2963
|
return { repositoryRoot, paths, manifest };
|
|
@@ -2841,7 +3000,9 @@ async function importDerivedSessions(paths, manifest, options, sourceKind, candi
|
|
|
2841
3000
|
throw new Error("Invalid import payload", { cause: parsed.error });
|
|
2842
3001
|
}
|
|
2843
3002
|
if (parsed.data.schema_version !== SESSION_IMPORT_SCHEMA_VERSION) {
|
|
2844
|
-
throw new Error(
|
|
3003
|
+
throw new Error(
|
|
3004
|
+
`Unsupported import schema_version: ${parsed.data.schema_version} (expected ${SESSION_IMPORT_SCHEMA_VERSION}). The envelope's shape is published as @basou/core/schemas/session-import.schema.json; this version may move at a minor release, so a producer should read it from the installed @basou/core rather than pinning it.`
|
|
3005
|
+
);
|
|
2845
3006
|
}
|
|
2846
3007
|
return parsed.data;
|
|
2847
3008
|
};
|
|
@@ -3234,7 +3395,7 @@ function shortId2(id) {
|
|
|
3234
3395
|
}
|
|
3235
3396
|
async function resolveRepositoryRootForImport(cwd) {
|
|
3236
3397
|
try {
|
|
3237
|
-
return await
|
|
3398
|
+
return await resolveRepositoryRoot5(cwd);
|
|
3238
3399
|
} catch (error) {
|
|
3239
3400
|
if (error instanceof Error && error.message === "Not a git repository") {
|
|
3240
3401
|
throw new Error("Not a git repository. Run 'git init' first, then re-run 'basou import'.", {
|
|
@@ -3467,7 +3628,7 @@ async function warnIfPositionNamesOtherWorkspaces2(result, ctx) {
|
|
|
3467
3628
|
});
|
|
3468
3629
|
if (report !== null) {
|
|
3469
3630
|
console.error(
|
|
3470
|
-
positionForeignWorkspaceWarning(report,
|
|
3631
|
+
positionForeignWorkspaceWarning(report, basouPaths9(result.workspaceRoot).files.orientation)
|
|
3471
3632
|
);
|
|
3472
3633
|
}
|
|
3473
3634
|
}
|
|
@@ -3477,7 +3638,7 @@ async function renderOrientationForCwd(options, ctx) {
|
|
|
3477
3638
|
return renderOrientationForRoot(repositoryRoot, options, ctx, { write: true });
|
|
3478
3639
|
}
|
|
3479
3640
|
async function renderOrientationForRoot(repositoryRoot, options, ctx, behaviour) {
|
|
3480
|
-
const paths =
|
|
3641
|
+
const paths = basouPaths9(repositoryRoot);
|
|
3481
3642
|
await assertWorkspaceInitialized6(paths.root);
|
|
3482
3643
|
const nowIso = (ctx.nowProvider?.() ?? /* @__PURE__ */ new Date()).toISOString();
|
|
3483
3644
|
const probeCtx = { cwd: repositoryRoot };
|
|
@@ -3491,7 +3652,7 @@ async function renderOrientationForRoot(repositoryRoot, options, ctx, behaviour)
|
|
|
3491
3652
|
try {
|
|
3492
3653
|
const hosts = await loadHostsConfig(ctx.hostsConfigPath);
|
|
3493
3654
|
if (hosts !== null) {
|
|
3494
|
-
federatedRoots = hosts.map((h) => ({ paths:
|
|
3655
|
+
federatedRoots = hosts.map((h) => ({ paths: basouPaths9(h.path), host: h.label }));
|
|
3495
3656
|
}
|
|
3496
3657
|
} catch (error) {
|
|
3497
3658
|
console.error(
|
|
@@ -3535,6 +3696,7 @@ async function assertWorkspaceInitialized6(basouRoot) {
|
|
|
3535
3696
|
|
|
3536
3697
|
// src/commands/hook.ts
|
|
3537
3698
|
var MAX_TRANSCRIPT_BYTES = 8 * 1024 * 1024;
|
|
3699
|
+
var execFileAsync = promisify(execFile);
|
|
3538
3700
|
function registerHookCommand(program) {
|
|
3539
3701
|
const hook = program.command("hook").description(
|
|
3540
3702
|
"Hook handlers for AI coding tools (Claude Code, Codex): read a hook payload on stdin, emit the tool's hook output on stdout"
|
|
@@ -3984,6 +4146,90 @@ async function doRunHookStatus(options) {
|
|
|
3984
4146
|
review: / --require-review\b/.test(command)
|
|
3985
4147
|
});
|
|
3986
4148
|
console.log(`basou Stop hook: registered, ${mode}.`);
|
|
4149
|
+
await reportHookEntryBuild(command);
|
|
4150
|
+
}
|
|
4151
|
+
async function reportHookEntryBuild(command) {
|
|
4152
|
+
console.log(` this basou is: ${BASOU_VERSION_LINE}`);
|
|
4153
|
+
const entry = extractHookEntryPath(command);
|
|
4154
|
+
if (entry === void 0) {
|
|
4155
|
+
console.log(
|
|
4156
|
+
" runs: (registered by alias, not by path) \u2014 which build that resolves to depends on the hook's PATH, so this cannot tell you."
|
|
4157
|
+
);
|
|
4158
|
+
return;
|
|
4159
|
+
}
|
|
4160
|
+
try {
|
|
4161
|
+
const { stdout } = await execFileAsync(process.execPath, [entry, "--version"], {
|
|
4162
|
+
timeout: 1e4
|
|
4163
|
+
});
|
|
4164
|
+
const reported = stdout.trim();
|
|
4165
|
+
console.log(` the hook runs: ${entry}`);
|
|
4166
|
+
console.log(` that build is: ${reported}`);
|
|
4167
|
+
if (!reported.includes("(build ")) {
|
|
4168
|
+
console.log(
|
|
4169
|
+
" note: that build predates build stamping, so what it reports is its package.json rather than itself \u2014 it cannot tell you which build it is. Update it (rebuild a source checkout, or reinstall the package) to find out."
|
|
4170
|
+
);
|
|
4171
|
+
}
|
|
4172
|
+
} catch {
|
|
4173
|
+
console.log(` the hook runs: ${entry}`);
|
|
4174
|
+
console.log(
|
|
4175
|
+
" that build is: could not be executed \u2014 the hook's wrapper fails open, so it is silently doing nothing."
|
|
4176
|
+
);
|
|
4177
|
+
}
|
|
4178
|
+
}
|
|
4179
|
+
function tokenizeShellCommand(command) {
|
|
4180
|
+
const tokens = [];
|
|
4181
|
+
let current = "";
|
|
4182
|
+
let started = false;
|
|
4183
|
+
let quote;
|
|
4184
|
+
for (let i = 0; i < command.length; i++) {
|
|
4185
|
+
const ch = command[i];
|
|
4186
|
+
if (quote === "'") {
|
|
4187
|
+
if (ch === "'") quote = void 0;
|
|
4188
|
+
else current += ch;
|
|
4189
|
+
continue;
|
|
4190
|
+
}
|
|
4191
|
+
if (quote === '"') {
|
|
4192
|
+
if (ch === '"') quote = void 0;
|
|
4193
|
+
else if (ch === "\\" && i + 1 < command.length) current += command[++i];
|
|
4194
|
+
else current += ch;
|
|
4195
|
+
continue;
|
|
4196
|
+
}
|
|
4197
|
+
if (ch === "'" || ch === '"') {
|
|
4198
|
+
quote = ch;
|
|
4199
|
+
started = true;
|
|
4200
|
+
continue;
|
|
4201
|
+
}
|
|
4202
|
+
if (ch === "\\" && i + 1 < command.length) {
|
|
4203
|
+
current += command[++i];
|
|
4204
|
+
started = true;
|
|
4205
|
+
continue;
|
|
4206
|
+
}
|
|
4207
|
+
if (/\s/.test(ch)) {
|
|
4208
|
+
if (started) tokens.push(current);
|
|
4209
|
+
current = "";
|
|
4210
|
+
started = false;
|
|
4211
|
+
continue;
|
|
4212
|
+
}
|
|
4213
|
+
current += ch;
|
|
4214
|
+
started = true;
|
|
4215
|
+
}
|
|
4216
|
+
if (quote !== void 0) return void 0;
|
|
4217
|
+
if (started) tokens.push(current);
|
|
4218
|
+
return tokens;
|
|
4219
|
+
}
|
|
4220
|
+
function extractHookEntryPath(command) {
|
|
4221
|
+
const tokens = tokenizeShellCommand(command);
|
|
4222
|
+
if (tokens === void 0) return void 0;
|
|
4223
|
+
let index = 0;
|
|
4224
|
+
while (index < tokens.length && /^[A-Za-z_][A-Za-z0-9_]*=/.test(tokens[index])) index++;
|
|
4225
|
+
const interpreter = tokens[index];
|
|
4226
|
+
if (interpreter === void 0) return void 0;
|
|
4227
|
+
const base = interpreter.replace(/\\/g, "/").split("/").pop() ?? "";
|
|
4228
|
+
if (base !== "node" && base !== "node.exe") return void 0;
|
|
4229
|
+
index++;
|
|
4230
|
+
while (index < tokens.length && tokens[index].startsWith("-")) index++;
|
|
4231
|
+
const entry = tokens[index];
|
|
4232
|
+
return entry === void 0 || entry === "" ? void 0 : entry;
|
|
3987
4233
|
}
|
|
3988
4234
|
function describeHookMode(tiers) {
|
|
3989
4235
|
const enforcement = tiers.block ? "blocking (opt-in enforcement)" : "advisory (non-blocking)";
|
|
@@ -4140,6 +4386,7 @@ async function doRunCodexHookStatus(options) {
|
|
|
4140
4386
|
console.log(
|
|
4141
4387
|
`basou Codex SessionStart hook: registered in ${hooksPath} (matcher: ${matcher}); speaks only for workspaces registered in ~/.basou/portfolio.yaml.`
|
|
4142
4388
|
);
|
|
4389
|
+
await reportHookEntryBuild(location.command);
|
|
4143
4390
|
await reportCodexHookState(hooksPath, parsed, options);
|
|
4144
4391
|
}
|
|
4145
4392
|
async function codexHookTrustFor(hooksPath, location, configPath) {
|
|
@@ -4179,7 +4426,7 @@ import {
|
|
|
4179
4426
|
appendBasouGitignore,
|
|
4180
4427
|
createManifest,
|
|
4181
4428
|
ensureBasouDirectory,
|
|
4182
|
-
resolveRepositoryRoot as
|
|
4429
|
+
resolveRepositoryRoot as resolveRepositoryRoot6,
|
|
4183
4430
|
writeManifest
|
|
4184
4431
|
} from "@basou/core";
|
|
4185
4432
|
function collectValue(value, previous) {
|
|
@@ -4249,7 +4496,7 @@ function renderGitignoreWarning(error, verbose) {
|
|
|
4249
4496
|
}
|
|
4250
4497
|
async function resolveRepositoryRootForInit(cwd) {
|
|
4251
4498
|
try {
|
|
4252
|
-
return await
|
|
4499
|
+
return await resolveRepositoryRoot6(cwd);
|
|
4253
4500
|
} catch (error) {
|
|
4254
4501
|
if (error instanceof Error && error.message === "Not a git repository") {
|
|
4255
4502
|
throw new Error("Not a git repository. Run 'git init' first, then re-run 'basou init'.", {
|
|
@@ -4265,14 +4512,15 @@ import {
|
|
|
4265
4512
|
acquireLock as acquireLock4,
|
|
4266
4513
|
appendEventToExistingSession as appendEventToExistingSession2,
|
|
4267
4514
|
assertBasouRootSafe as assertBasouRootSafe8,
|
|
4268
|
-
basouPaths as
|
|
4515
|
+
basouPaths as basouPaths10,
|
|
4269
4516
|
createAdHocSessionWithEvent as createAdHocSessionWithEvent2,
|
|
4270
4517
|
EVENT_SCHEMA_VERSION as EVENT_SCHEMA_VERSION4,
|
|
4271
4518
|
findErrorCode as findErrorCode7,
|
|
4519
|
+
LOCAL_CLI_EVENT_SOURCE as LOCAL_CLI_EVENT_SOURCE3,
|
|
4272
4520
|
readManifest as readManifest5,
|
|
4273
4521
|
resolveSessionId as resolveSessionId2
|
|
4274
4522
|
} from "@basou/core";
|
|
4275
|
-
import { InvalidArgumentError as
|
|
4523
|
+
import { InvalidArgumentError as InvalidArgumentError3 } from "commander";
|
|
4276
4524
|
var NOTE_SUBCOMMAND_LOOKALIKES = /* @__PURE__ */ new Set([
|
|
4277
4525
|
"list",
|
|
4278
4526
|
"ls",
|
|
@@ -4319,7 +4567,7 @@ async function doRunNote(body, options, ctx) {
|
|
|
4319
4567
|
}
|
|
4320
4568
|
const cwd = ctx.cwd ?? process.cwd();
|
|
4321
4569
|
const repositoryRoot = await resolveBasouRootForCommand(cwd, "note");
|
|
4322
|
-
const paths =
|
|
4570
|
+
const paths = basouPaths10(repositoryRoot);
|
|
4323
4571
|
await assertWorkspaceInitialized7(paths.root);
|
|
4324
4572
|
const now = ctx.nowProvider !== void 0 ? ctx.nowProvider() : /* @__PURE__ */ new Date();
|
|
4325
4573
|
const occurredAt = now.toISOString();
|
|
@@ -4376,7 +4624,7 @@ function buildNoteEvent(input) {
|
|
|
4376
4624
|
id: input.eventId,
|
|
4377
4625
|
session_id: input.sessionId,
|
|
4378
4626
|
occurred_at: input.occurredAt,
|
|
4379
|
-
source:
|
|
4627
|
+
source: LOCAL_CLI_EVENT_SOURCE3,
|
|
4380
4628
|
type: "note_added",
|
|
4381
4629
|
body: input.body,
|
|
4382
4630
|
// `basou note` is the resume-hint command; mark it so orientation surfaces
|
|
@@ -4385,13 +4633,13 @@ function buildNoteEvent(input) {
|
|
|
4385
4633
|
};
|
|
4386
4634
|
}
|
|
4387
4635
|
function buildAdHocLabel2(body) {
|
|
4388
|
-
const
|
|
4389
|
-
const truncated =
|
|
4636
|
+
const oneLine3 = body.replace(/\s+/g, " ").trim();
|
|
4637
|
+
const truncated = oneLine3.length > LABEL_BODY_MAX ? `${oneLine3.slice(0, LABEL_TRUNCATE_HEAD2)}...` : oneLine3;
|
|
4390
4638
|
return `Ad-hoc note: ${truncated}`;
|
|
4391
4639
|
}
|
|
4392
4640
|
function parseBody(raw) {
|
|
4393
4641
|
if (raw.trim().length === 0) {
|
|
4394
|
-
throw new
|
|
4642
|
+
throw new InvalidArgumentError3("Note body must not be empty");
|
|
4395
4643
|
}
|
|
4396
4644
|
return raw;
|
|
4397
4645
|
}
|
|
@@ -4542,7 +4790,7 @@ import {
|
|
|
4542
4790
|
import { basename as basename6, dirname as dirname4, isAbsolute as isAbsolute3, join as join11, relative as relative2, resolve as resolve7 } from "path";
|
|
4543
4791
|
import {
|
|
4544
4792
|
appendBasouGitignore as appendBasouGitignore2,
|
|
4545
|
-
basouPaths as
|
|
4793
|
+
basouPaths as basouPaths11,
|
|
4546
4794
|
classifyRetrofit,
|
|
4547
4795
|
createManifest as createManifest2,
|
|
4548
4796
|
ensureBasouDirectory as ensureBasouDirectory2,
|
|
@@ -4564,7 +4812,7 @@ import {
|
|
|
4564
4812
|
renderAnchorStarter,
|
|
4565
4813
|
renderViewPresetBlock,
|
|
4566
4814
|
renderWithMarkers as renderWithMarkers4,
|
|
4567
|
-
resolveRepositoryRoot as
|
|
4815
|
+
resolveRepositoryRoot as resolveRepositoryRoot7,
|
|
4568
4816
|
safeSimpleGit,
|
|
4569
4817
|
seedMarkers,
|
|
4570
4818
|
summarizePresetPlan,
|
|
@@ -4705,7 +4953,7 @@ function preservedUnknownLines(fields) {
|
|
|
4705
4953
|
async function doRunProjectCheck(options, ctx) {
|
|
4706
4954
|
const cwd = ctx.cwd ?? process.cwd();
|
|
4707
4955
|
const repositoryRoot = await resolveBasouRootForCommand(cwd, "project check");
|
|
4708
|
-
const paths =
|
|
4956
|
+
const paths = basouPaths11(repositoryRoot);
|
|
4709
4957
|
const manifest = await readManifest6(paths);
|
|
4710
4958
|
const roster = summarizeRosterDrift({
|
|
4711
4959
|
...manifest.repos !== void 0 ? { repos: manifest.repos } : {},
|
|
@@ -4840,7 +5088,7 @@ async function runProjectSync(options, ctx = {}) {
|
|
|
4840
5088
|
async function doRunProjectSync(options, ctx) {
|
|
4841
5089
|
const cwd = ctx.cwd ?? process.cwd();
|
|
4842
5090
|
const repositoryRoot = await resolveBasouRootForCommand(cwd, "project sync");
|
|
4843
|
-
const paths =
|
|
5091
|
+
const paths = basouPaths11(repositoryRoot);
|
|
4844
5092
|
const manifest = await readManifest6(paths);
|
|
4845
5093
|
const hasRoster = manifest.repos !== void 0 && manifest.repos.length > 0;
|
|
4846
5094
|
const reconcile = reconcileSourceRoots({
|
|
@@ -4926,7 +5174,7 @@ function classifySourceRoot(repositoryRoot, declaredPath) {
|
|
|
4926
5174
|
async function doRunProjectAdopt(options, ctx) {
|
|
4927
5175
|
const cwd = ctx.cwd ?? process.cwd();
|
|
4928
5176
|
const repositoryRoot = await resolveBasouRootForCommand(cwd, "project adopt");
|
|
4929
|
-
const paths =
|
|
5177
|
+
const paths = basouPaths11(repositoryRoot);
|
|
4930
5178
|
const manifest = await readManifest6(paths);
|
|
4931
5179
|
const alreadyDeclared = manifest.repos !== void 0 && manifest.repos.length > 0;
|
|
4932
5180
|
const candidates = effectiveSourceRoots(manifest).map(
|
|
@@ -5048,7 +5296,7 @@ async function gatherRepoWiring(repositoryRoot, entry) {
|
|
|
5048
5296
|
async function doRunProjectWiring(options, ctx) {
|
|
5049
5297
|
const cwd = ctx.cwd ?? process.cwd();
|
|
5050
5298
|
const repositoryRoot = await resolveBasouRootForCommand(cwd, "project wiring");
|
|
5051
|
-
const paths =
|
|
5299
|
+
const paths = basouPaths11(repositoryRoot);
|
|
5052
5300
|
const manifest = await readManifest6(paths);
|
|
5053
5301
|
const roster = manifest.repos ?? [];
|
|
5054
5302
|
const facts = [];
|
|
@@ -5181,7 +5429,7 @@ function applyGitignorePlan(repositoryRoot, plan) {
|
|
|
5181
5429
|
async function doRunProjectGitignore(options, ctx) {
|
|
5182
5430
|
const cwd = ctx.cwd ?? process.cwd();
|
|
5183
5431
|
const repositoryRoot = await resolveBasouRootForCommand(cwd, "project gitignore");
|
|
5184
|
-
const paths =
|
|
5432
|
+
const paths = basouPaths11(repositoryRoot);
|
|
5185
5433
|
const manifest = await readManifest6(paths);
|
|
5186
5434
|
const roster = manifest.repos ?? [];
|
|
5187
5435
|
const facts = roster.map((entry) => gatherRepoGitignore(repositoryRoot, entry));
|
|
@@ -5440,7 +5688,7 @@ function applyViewSymlinks(viewDir, files) {
|
|
|
5440
5688
|
async function doRunProjectSymlinks(options, ctx) {
|
|
5441
5689
|
const cwd = ctx.cwd ?? process.cwd();
|
|
5442
5690
|
const repositoryRoot = await resolveBasouRootForCommand(cwd, "project symlinks");
|
|
5443
|
-
const paths =
|
|
5691
|
+
const paths = basouPaths11(repositoryRoot);
|
|
5444
5692
|
const manifest = await readManifest6(paths);
|
|
5445
5693
|
const roster = manifest.repos ?? [];
|
|
5446
5694
|
const anchorReal = realpathSync(repositoryRoot);
|
|
@@ -5770,7 +6018,7 @@ function pruneViewLinks(viewDir, toPrune, rosterRealpaths) {
|
|
|
5770
6018
|
async function doRunProjectWorkspace(options, ctx) {
|
|
5771
6019
|
const cwd = ctx.cwd ?? process.cwd();
|
|
5772
6020
|
const repositoryRoot = await resolveBasouRootForCommand(cwd, "project workspace");
|
|
5773
|
-
const paths =
|
|
6021
|
+
const paths = basouPaths11(repositoryRoot);
|
|
5774
6022
|
const manifest = await readManifest6(paths);
|
|
5775
6023
|
const viewPath = manifest.workspace.view;
|
|
5776
6024
|
const roster = manifest.repos ?? [];
|
|
@@ -6130,7 +6378,7 @@ function presetFailureReason(error) {
|
|
|
6130
6378
|
async function doRunProjectPreset(options, ctx) {
|
|
6131
6379
|
const cwd = ctx.cwd ?? process.cwd();
|
|
6132
6380
|
const repositoryRoot = await resolveBasouRootForCommand(cwd, "project preset");
|
|
6133
|
-
const paths =
|
|
6381
|
+
const paths = basouPaths11(repositoryRoot);
|
|
6134
6382
|
const manifest = await readManifest6(paths);
|
|
6135
6383
|
const roster = manifest.repos ?? [];
|
|
6136
6384
|
const anchorReal = realpathSync(repositoryRoot);
|
|
@@ -6789,7 +7037,7 @@ function renderProjectTeardown(result) {
|
|
|
6789
7037
|
async function doRunProjectTeardown(target, options, ctx = {}) {
|
|
6790
7038
|
const cwd = ctx.cwd ?? process.cwd();
|
|
6791
7039
|
const repositoryRoot = await resolveBasouRootForCommand(cwd, "project teardown");
|
|
6792
|
-
const paths =
|
|
7040
|
+
const paths = basouPaths11(repositoryRoot);
|
|
6793
7041
|
const manifest = await readManifest6(paths);
|
|
6794
7042
|
const plan = gatherRepoTeardown(repositoryRoot, manifest, target);
|
|
6795
7043
|
const willApply = options.apply === true && !plan.isAnchor && plan.removableCount > 0;
|
|
@@ -6831,7 +7079,7 @@ function buildArchivedManifest(manifest, plan, updatedAt) {
|
|
|
6831
7079
|
async function doRunProjectArchive(target, options, ctx) {
|
|
6832
7080
|
const cwd = ctx.cwd ?? process.cwd();
|
|
6833
7081
|
const repositoryRoot = await resolveBasouRootForCommand(cwd, "project archive");
|
|
6834
|
-
const paths =
|
|
7082
|
+
const paths = basouPaths11(repositoryRoot);
|
|
6835
7083
|
const manifest = await readManifest6(paths);
|
|
6836
7084
|
const roster = manifest.repos ?? [];
|
|
6837
7085
|
let targetIsAnchor = false;
|
|
@@ -6988,7 +7236,7 @@ function buildRenamedManifest(manifest, plan, updatedAt) {
|
|
|
6988
7236
|
async function doRunProjectRename(oldPath, newPath, options, ctx) {
|
|
6989
7237
|
const cwd = ctx.cwd ?? process.cwd();
|
|
6990
7238
|
const repositoryRoot = await resolveBasouRootForCommand(cwd, "project rename");
|
|
6991
|
-
const paths =
|
|
7239
|
+
const paths = basouPaths11(repositoryRoot);
|
|
6992
7240
|
const manifest = await readManifest6(paths);
|
|
6993
7241
|
const roster = manifest.repos ?? [];
|
|
6994
7242
|
let oldIsAnchor = false;
|
|
@@ -7112,7 +7360,7 @@ async function runProjectNew(repos, options, ctx = {}) {
|
|
|
7112
7360
|
}
|
|
7113
7361
|
async function resolveRepositoryRootForNew(cwd) {
|
|
7114
7362
|
try {
|
|
7115
|
-
return await
|
|
7363
|
+
return await resolveRepositoryRoot7(cwd);
|
|
7116
7364
|
} catch (error) {
|
|
7117
7365
|
if (error instanceof Error && error.message === "Not a git repository") {
|
|
7118
7366
|
throw new Error(
|
|
@@ -7164,7 +7412,7 @@ async function doRunProjectNew(repos, options, ctx) {
|
|
|
7164
7412
|
const roster = rosterPaths.map((path) => ({ path }));
|
|
7165
7413
|
const viewPath = options.view === false ? null : options.view ?? `../${viewStem}-workspace`;
|
|
7166
7414
|
const sourceRoots = [...rosterPaths, ...viewPath !== null ? [viewPath] : []];
|
|
7167
|
-
const paths =
|
|
7415
|
+
const paths = basouPaths11(repositoryRoot);
|
|
7168
7416
|
const existed = existsSync2(paths.files.manifest);
|
|
7169
7417
|
const manifest = createManifest2({
|
|
7170
7418
|
workspaceName,
|
|
@@ -7275,7 +7523,7 @@ async function runProjectDerive(options, ctx = {}) {
|
|
|
7275
7523
|
async function doRunProjectDerive(options, ctx) {
|
|
7276
7524
|
const cwd = ctx.cwd ?? process.cwd();
|
|
7277
7525
|
const repositoryRoot = await resolveBasouRootForCommand(cwd, "project derive");
|
|
7278
|
-
const paths =
|
|
7526
|
+
const paths = basouPaths11(repositoryRoot);
|
|
7279
7527
|
const manifest = await readManifest6(paths);
|
|
7280
7528
|
if (manifest.repos === void 0 || manifest.repos.length === 0) {
|
|
7281
7529
|
console.log(
|
|
@@ -7316,7 +7564,7 @@ async function doRunProjectDerive(options, ctx) {
|
|
|
7316
7564
|
async function doRunProjectSeedAnchor(options, ctx) {
|
|
7317
7565
|
const cwd = ctx.cwd ?? process.cwd();
|
|
7318
7566
|
const repositoryRoot = await resolveBasouRootForCommand(cwd, "project derive");
|
|
7319
|
-
const paths =
|
|
7567
|
+
const paths = basouPaths11(repositoryRoot);
|
|
7320
7568
|
const manifest = await readManifest6(paths);
|
|
7321
7569
|
const roster = manifest.repos ?? [];
|
|
7322
7570
|
console.log("# Anchor instruction-file seed (the planning master's own AGENTS.md)");
|
|
@@ -7402,7 +7650,7 @@ function pathPresent(p) {
|
|
|
7402
7650
|
return false;
|
|
7403
7651
|
}
|
|
7404
7652
|
}
|
|
7405
|
-
function gatherRetrofit(repositoryRoot, anchorReal, roster,
|
|
7653
|
+
function gatherRetrofit(repositoryRoot, anchorReal, roster, argAbs, argReal, viewCanonicalName) {
|
|
7406
7654
|
const declaredEntry = roster.find((entry) => {
|
|
7407
7655
|
const entryAbs = resolve7(repositoryRoot, entry.path);
|
|
7408
7656
|
if (argReal !== void 0) {
|
|
@@ -7510,7 +7758,7 @@ async function applyViewRetrofit(anchorReal, outcome) {
|
|
|
7510
7758
|
async function doRunProjectRetrofit(repo, options, ctx) {
|
|
7511
7759
|
const cwd = ctx.cwd ?? process.cwd();
|
|
7512
7760
|
const repositoryRoot = await resolveBasouRootForCommand(cwd, "project retrofit");
|
|
7513
|
-
const paths =
|
|
7761
|
+
const paths = basouPaths11(repositoryRoot);
|
|
7514
7762
|
const manifest = await readManifest6(paths);
|
|
7515
7763
|
const roster = manifest.repos ?? [];
|
|
7516
7764
|
const anchorReal = realpathSync(repositoryRoot);
|
|
@@ -7556,7 +7804,6 @@ async function doRunProjectRetrofit(repo, options, ctx) {
|
|
|
7556
7804
|
repositoryRoot,
|
|
7557
7805
|
anchorReal,
|
|
7558
7806
|
roster,
|
|
7559
|
-
repo,
|
|
7560
7807
|
argAbs,
|
|
7561
7808
|
argReal,
|
|
7562
7809
|
viewCanonicalName
|
|
@@ -7987,11 +8234,11 @@ async function doRunProtocolUnsync(options) {
|
|
|
7987
8234
|
// src/commands/refresh.ts
|
|
7988
8235
|
import {
|
|
7989
8236
|
assertBasouRootSafe as assertBasouRootSafe9,
|
|
7990
|
-
basouPaths as
|
|
8237
|
+
basouPaths as basouPaths12,
|
|
7991
8238
|
findErrorCode as findErrorCode9,
|
|
7992
8239
|
readManifest as readManifest7
|
|
7993
8240
|
} from "@basou/core";
|
|
7994
|
-
import { InvalidArgumentError as
|
|
8241
|
+
import { InvalidArgumentError as InvalidArgumentError4 } from "commander";
|
|
7995
8242
|
|
|
7996
8243
|
// src/commands/refresh-watch.ts
|
|
7997
8244
|
import { readdir as readdir2, stat as stat5 } from "fs/promises";
|
|
@@ -8118,7 +8365,7 @@ function collectPath2(value, previous) {
|
|
|
8118
8365
|
function parseInterval(value) {
|
|
8119
8366
|
const seconds = Number(value);
|
|
8120
8367
|
if (!Number.isInteger(seconds) || seconds < MIN_WATCH_INTERVAL_SEC || seconds > MAX_WATCH_INTERVAL_SEC) {
|
|
8121
|
-
throw new
|
|
8368
|
+
throw new InvalidArgumentError4(
|
|
8122
8369
|
`--interval must be an integer between ${MIN_WATCH_INTERVAL_SEC} and ${MAX_WATCH_INTERVAL_SEC} (seconds).`
|
|
8123
8370
|
);
|
|
8124
8371
|
}
|
|
@@ -8229,7 +8476,7 @@ async function doRunRefreshWatch(options, ctx) {
|
|
|
8229
8476
|
if (options.force === true) throw new Error("--watch cannot be combined with --force.");
|
|
8230
8477
|
const cwd = ctx.cwd ?? process.cwd();
|
|
8231
8478
|
const repositoryRoot = await resolveBasouRootForCommand(cwd, "refresh");
|
|
8232
|
-
const paths =
|
|
8479
|
+
const paths = basouPaths12(repositoryRoot);
|
|
8233
8480
|
await assertWorkspaceInitialized8(paths.root);
|
|
8234
8481
|
const intervalMs = (options.interval ?? DEFAULT_WATCH_INTERVAL_SEC) * 1e3;
|
|
8235
8482
|
const controller = new AbortController();
|
|
@@ -8257,7 +8504,7 @@ async function doRunRefreshWatch(options, ctx) {
|
|
|
8257
8504
|
async function computeRefresh(options, ctx) {
|
|
8258
8505
|
const cwd = ctx.cwd ?? process.cwd();
|
|
8259
8506
|
const repositoryRoot = await resolveBasouRootForCommand(cwd, "refresh");
|
|
8260
|
-
const paths =
|
|
8507
|
+
const paths = basouPaths12(repositoryRoot);
|
|
8261
8508
|
await assertWorkspaceInitialized8(paths.root);
|
|
8262
8509
|
const nowIso = (ctx.nowProvider?.() ?? /* @__PURE__ */ new Date()).toISOString();
|
|
8263
8510
|
const result = await refreshAll({
|
|
@@ -8366,10 +8613,10 @@ async function assertWorkspaceInitialized8(basouRoot) {
|
|
|
8366
8613
|
import { isAbsolute as isAbsolute5, resolve as resolve9 } from "path";
|
|
8367
8614
|
import {
|
|
8368
8615
|
assertBasouRootSafe as assertBasouRootSafe10,
|
|
8369
|
-
basouPaths as
|
|
8616
|
+
basouPaths as basouPaths13,
|
|
8370
8617
|
findErrorCode as findErrorCode10,
|
|
8371
8618
|
renderReport,
|
|
8372
|
-
resolveRepositoryRoot as
|
|
8619
|
+
resolveRepositoryRoot as resolveRepositoryRoot8,
|
|
8373
8620
|
writeMarkdownFile as writeMarkdownFile6
|
|
8374
8621
|
} from "@basou/core";
|
|
8375
8622
|
function registerReportCommand(program) {
|
|
@@ -8391,7 +8638,7 @@ async function runReportGenerate(options, ctx = {}) {
|
|
|
8391
8638
|
async function doRunReportGenerate(options, ctx) {
|
|
8392
8639
|
const cwd = ctx.cwd ?? process.cwd();
|
|
8393
8640
|
const repositoryRoot = await resolveRepositoryRootForReport(cwd);
|
|
8394
|
-
const paths =
|
|
8641
|
+
const paths = basouPaths13(repositoryRoot);
|
|
8395
8642
|
await assertWorkspaceInitialized9(paths.root);
|
|
8396
8643
|
const nowIso = (ctx.nowProvider?.() ?? /* @__PURE__ */ new Date()).toISOString();
|
|
8397
8644
|
const result = await renderReport({
|
|
@@ -8418,7 +8665,7 @@ async function doRunReportGenerate(options, ctx) {
|
|
|
8418
8665
|
}
|
|
8419
8666
|
async function resolveRepositoryRootForReport(cwd) {
|
|
8420
8667
|
try {
|
|
8421
|
-
return await
|
|
8668
|
+
return await resolveRepositoryRoot8(cwd);
|
|
8422
8669
|
} catch (error) {
|
|
8423
8670
|
if (error instanceof Error && error.message === "Not a git repository") {
|
|
8424
8671
|
throw new Error(
|
|
@@ -8446,7 +8693,7 @@ import { homedir as homedir10 } from "os";
|
|
|
8446
8693
|
import { resolve as resolve10 } from "path";
|
|
8447
8694
|
import {
|
|
8448
8695
|
assertBasouRootSafe as assertBasouRootSafe11,
|
|
8449
|
-
basouPaths as
|
|
8696
|
+
basouPaths as basouPaths14,
|
|
8450
8697
|
buildReviewRecordedEvent,
|
|
8451
8698
|
buildReviewRecordLabel,
|
|
8452
8699
|
createAdHocSessionWithEvent as createAdHocSessionWithEvent3,
|
|
@@ -8528,7 +8775,7 @@ async function runReviewRecord(options, ctx = {}) {
|
|
|
8528
8775
|
async function doRunReviewRecord(options, ctx) {
|
|
8529
8776
|
const cwd = ctx.cwd ?? process.cwd();
|
|
8530
8777
|
const repositoryRoot = await resolveBasouRootForCommand(cwd, "review record");
|
|
8531
|
-
const paths =
|
|
8778
|
+
const paths = basouPaths14(repositoryRoot);
|
|
8532
8779
|
await assertWorkspaceInitialized10(paths.root);
|
|
8533
8780
|
const raw = await readReviewInput(options, ctx);
|
|
8534
8781
|
const review = parseReviewRecordInput(raw);
|
|
@@ -8675,17 +8922,17 @@ async function assertWorkspaceInitialized10(basouRoot) {
|
|
|
8675
8922
|
|
|
8676
8923
|
// src/commands/review-gaps.ts
|
|
8677
8924
|
import {
|
|
8678
|
-
basouPaths as
|
|
8925
|
+
basouPaths as basouPaths15,
|
|
8679
8926
|
findReviewGaps
|
|
8680
8927
|
} from "@basou/core";
|
|
8681
|
-
import { InvalidArgumentError as
|
|
8928
|
+
import { InvalidArgumentError as InvalidArgumentError5 } from "commander";
|
|
8682
8929
|
function collectRepo(value, previous) {
|
|
8683
8930
|
return [...previous, value];
|
|
8684
8931
|
}
|
|
8685
8932
|
function parseWindow(value) {
|
|
8686
8933
|
const hours = Number(value);
|
|
8687
8934
|
if (!Number.isInteger(hours) || hours <= 0) {
|
|
8688
|
-
throw new
|
|
8935
|
+
throw new InvalidArgumentError5("--window must be a positive integer (hours).");
|
|
8689
8936
|
}
|
|
8690
8937
|
return hours;
|
|
8691
8938
|
}
|
|
@@ -8716,7 +8963,7 @@ async function runReviewGaps(options, ctx = {}) {
|
|
|
8716
8963
|
async function doRunReviewGaps(options, ctx) {
|
|
8717
8964
|
const cwd = ctx.cwd ?? process.cwd();
|
|
8718
8965
|
const repositoryRoot = await resolveBasouRootForCommand(cwd, "review-gaps");
|
|
8719
|
-
const paths =
|
|
8966
|
+
const paths = basouPaths15(repositoryRoot);
|
|
8720
8967
|
const nowIso = (ctx.nowProvider?.() ?? /* @__PURE__ */ new Date()).toISOString();
|
|
8721
8968
|
const summary = await findReviewGaps({
|
|
8722
8969
|
paths,
|
|
@@ -8733,7 +8980,7 @@ async function doRunReviewGaps(options, ctx) {
|
|
|
8733
8980
|
}
|
|
8734
8981
|
return summary;
|
|
8735
8982
|
}
|
|
8736
|
-
function
|
|
8983
|
+
function relAge2(iso, now) {
|
|
8737
8984
|
if (iso === null) return "(unknown)";
|
|
8738
8985
|
const ms = now.getTime() - Date.parse(iso);
|
|
8739
8986
|
if (!Number.isFinite(ms) || ms < 0) return "just now";
|
|
@@ -8746,11 +8993,11 @@ function relAge(iso, now) {
|
|
|
8746
8993
|
function flatten(value) {
|
|
8747
8994
|
return value.replace(/\s+/gu, " ").trim();
|
|
8748
8995
|
}
|
|
8749
|
-
var
|
|
8996
|
+
var GRAPHEMES2 = new Intl.Segmenter(void 0, { granularity: "grapheme" });
|
|
8750
8997
|
function graphemes(value) {
|
|
8751
|
-
return [...
|
|
8998
|
+
return [...GRAPHEMES2.segment(value)].map((s) => s.segment);
|
|
8752
8999
|
}
|
|
8753
|
-
function
|
|
9000
|
+
function oneLine2(value, max) {
|
|
8754
9001
|
const flat = graphemes(flatten(value));
|
|
8755
9002
|
return flat.length > max ? `${flat.slice(0, max - 1).join("")}\u2026` : flat.join("");
|
|
8756
9003
|
}
|
|
@@ -8763,7 +9010,7 @@ var SELF_REPORTS_SHOWN = 3;
|
|
|
8763
9010
|
function selfReportSuffix(u, stillCounted) {
|
|
8764
9011
|
if (u.selfReports.length === 0) return "";
|
|
8765
9012
|
const parts = u.selfReports.slice(0, SELF_REPORTS_SHOWN).map(
|
|
8766
|
-
(r) => `${
|
|
9013
|
+
(r) => `${oneLine2(r.reviewer, 40)}${claimedCommits(r.commits)}${r.recordedAfterCommit ? " (recorded after the commit)" : ""}`
|
|
8767
9014
|
);
|
|
8768
9015
|
const rest = u.selfReports.length - parts.length;
|
|
8769
9016
|
if (rest > 0) parts.push(`+${rest} more`);
|
|
@@ -8776,7 +9023,7 @@ function unobservedOutcomeSuffix(u) {
|
|
|
8776
9023
|
return ` \xB7 ${scope}exited with no recorded status \u2014 landing assumed, not observed`;
|
|
8777
9024
|
}
|
|
8778
9025
|
function unitLine(u, now) {
|
|
8779
|
-
const when =
|
|
9026
|
+
const when = relAge2(u.lastCommitAt, now);
|
|
8780
9027
|
const head = `- ${u.repo} ${when} (${u.commitCount} commit${u.commitCount === 1 ? "" : "s"})`;
|
|
8781
9028
|
if (u.verdict === "near_unbound") {
|
|
8782
9029
|
const ids = u.reviews.map((r) => r.sessionId.slice(0, 14)).join(", ");
|
|
@@ -8785,7 +9032,7 @@ function unitLine(u, now) {
|
|
|
8785
9032
|
return `${head} \u2014 no bound cross-model review${selfReportSuffix(u, true)}${unobservedOutcomeSuffix(u)}`;
|
|
8786
9033
|
}
|
|
8787
9034
|
function candidateLine(u, now) {
|
|
8788
|
-
const when =
|
|
9035
|
+
const when = relAge2(u.lastCommitAt, now);
|
|
8789
9036
|
const cite = u.reviews.map((r) => `${r.sessionId.slice(0, 14)}${r.examinedDiff ? "(diff)" : ""}`).join(", ");
|
|
8790
9037
|
return `- ${u.repo} ${when} (${u.commitCount} commit${u.commitCount === 1 ? "" : "s"}) \u2014 review trace: ${cite}${selfReportSuffix(u, false)}${unobservedOutcomeSuffix(u)}`;
|
|
8791
9038
|
}
|
|
@@ -8821,7 +9068,7 @@ function renderReviewGaps(summary) {
|
|
|
8821
9068
|
);
|
|
8822
9069
|
for (const u of summary.unknowns) {
|
|
8823
9070
|
lines.push(
|
|
8824
|
-
`- ${
|
|
9071
|
+
`- ${relAge2(u.lastCommitAt, now)} (${u.commitCount} commit${u.commitCount === 1 ? "" : "s"}) [${u.sessionId}]`
|
|
8825
9072
|
);
|
|
8826
9073
|
}
|
|
8827
9074
|
lines.push("");
|
|
@@ -8834,7 +9081,7 @@ function renderReviewGaps(summary) {
|
|
|
8834
9081
|
}
|
|
8835
9082
|
lines.push("");
|
|
8836
9083
|
lines.push(
|
|
8837
|
-
`Note: read-only advisory. Only captured commits are in scope (newest captured commit: ${summary.newestCommitAt === null ? "none" :
|
|
9084
|
+
`Note: read-only advisory. Only captured commits are in scope (newest captured commit: ${summary.newestCommitAt === null ? "none" : relAge2(summary.newestCommitAt, now)}). It never auto-judges that a review "happened", and temporal proximity alone is not a pass. It does not enforce.`
|
|
8838
9085
|
);
|
|
8839
9086
|
if (summary.gaps.some((u) => u.selfReports.length > 0)) {
|
|
8840
9087
|
lines.push(
|
|
@@ -8884,7 +9131,7 @@ import { join as join14 } from "path";
|
|
|
8884
9131
|
import {
|
|
8885
9132
|
acquireLock as acquireLock5,
|
|
8886
9133
|
assertBasouRootSafe as assertBasouRootSafe12,
|
|
8887
|
-
basouPaths as
|
|
9134
|
+
basouPaths as basouPaths16,
|
|
8888
9135
|
ChildProcessRunner as ChildProcessRunner2,
|
|
8889
9136
|
claudeCodeAdapterMetadata,
|
|
8890
9137
|
codexAdapterMetadata,
|
|
@@ -8900,7 +9147,8 @@ import {
|
|
|
8900
9147
|
readYamlFile as readYamlFile6,
|
|
8901
9148
|
resolveClaudeCodeCommand,
|
|
8902
9149
|
resolveCodexCommand,
|
|
8903
|
-
resolveRepositoryRoot as
|
|
9150
|
+
resolveRepositoryRoot as resolveRepositoryRoot9,
|
|
9151
|
+
SESSION_SCHEMA_VERSION as SESSION_SCHEMA_VERSION2,
|
|
8904
9152
|
SessionSchema as SessionSchema2,
|
|
8905
9153
|
sanitizeRelatedFiles,
|
|
8906
9154
|
sanitizeWorkingDirectory as sanitizeWorkingDirectory2,
|
|
@@ -8955,7 +9203,7 @@ async function runTrackedTool(args, options, ctx, adapter) {
|
|
|
8955
9203
|
const childArgs = adapter.transformArgs ? adapter.transformArgs(args) : args;
|
|
8956
9204
|
const cwd = options.cwd ?? process.cwd();
|
|
8957
9205
|
const repoRoot = await resolveRepositoryRootForRun(cwd);
|
|
8958
|
-
const paths =
|
|
9206
|
+
const paths = basouPaths16(repoRoot);
|
|
8959
9207
|
await assertBasouRootSafe12(paths.root);
|
|
8960
9208
|
const manifest = await readManifest9(paths);
|
|
8961
9209
|
const sessionId = prefixedUlid4("ses");
|
|
@@ -9239,7 +9487,7 @@ function normalizeFileChangedSkipMessage(error) {
|
|
|
9239
9487
|
function buildInitialSession2(input) {
|
|
9240
9488
|
const cmdline = [input.command, ...input.args].join(" ");
|
|
9241
9489
|
return {
|
|
9242
|
-
schema_version:
|
|
9490
|
+
schema_version: SESSION_SCHEMA_VERSION2,
|
|
9243
9491
|
session: {
|
|
9244
9492
|
id: input.id,
|
|
9245
9493
|
label: `basou run ${cmdline} (${input.startedAt})`,
|
|
@@ -9310,7 +9558,7 @@ async function finalizeSessionAsFailed2(paths, sessionDir, sessionId, appendEven
|
|
|
9310
9558
|
}
|
|
9311
9559
|
async function resolveRepositoryRootForRun(cwd) {
|
|
9312
9560
|
try {
|
|
9313
|
-
return await
|
|
9561
|
+
return await resolveRepositoryRoot9(cwd);
|
|
9314
9562
|
} catch (error) {
|
|
9315
9563
|
if (error instanceof Error && error.message === "Not a git repository") {
|
|
9316
9564
|
throw new Error("Not a git repository. Run 'git init' first, then re-run 'basou run'.", {
|
|
@@ -9344,11 +9592,12 @@ import {
|
|
|
9344
9592
|
acquireLock as acquireLock6,
|
|
9345
9593
|
appendEventToExistingSession as appendEventToExistingSession3,
|
|
9346
9594
|
assertBasouRootSafe as assertBasouRootSafe13,
|
|
9347
|
-
basouPaths as
|
|
9595
|
+
basouPaths as basouPaths17,
|
|
9348
9596
|
EVENT_SCHEMA_VERSION as EVENT_SCHEMA_VERSION6,
|
|
9349
9597
|
enumerateSessionDirs as enumerateSessionDirs2,
|
|
9350
9598
|
findErrorCode as findErrorCode12,
|
|
9351
9599
|
importSessionFromJson as importSessionFromJson2,
|
|
9600
|
+
LOCAL_CLI_EVENT_SOURCE as LOCAL_CLI_EVENT_SOURCE4,
|
|
9352
9601
|
loadSessionEntries as loadSessionEntries2,
|
|
9353
9602
|
readAllEvents,
|
|
9354
9603
|
readManifest as readManifest10,
|
|
@@ -9363,7 +9612,7 @@ import {
|
|
|
9363
9612
|
SessionStatusSchema,
|
|
9364
9613
|
sessionWorkStatsFromEvents
|
|
9365
9614
|
} from "@basou/core";
|
|
9366
|
-
import { InvalidArgumentError as
|
|
9615
|
+
import { InvalidArgumentError as InvalidArgumentError6 } from "commander";
|
|
9367
9616
|
|
|
9368
9617
|
// src/lib/format-duration.ts
|
|
9369
9618
|
import { formatDurationMs } from "@basou/core";
|
|
@@ -9412,7 +9661,7 @@ async function runSessionList(options, ctx = {}) {
|
|
|
9412
9661
|
async function doRunSessionList(options, ctx) {
|
|
9413
9662
|
const cwd = ctx.cwd ?? process.cwd();
|
|
9414
9663
|
const repositoryRoot = await resolveRepositoryRootForSession(cwd, "list");
|
|
9415
|
-
const paths =
|
|
9664
|
+
const paths = basouPaths17(repositoryRoot);
|
|
9416
9665
|
await assertWorkspaceInitialized11(paths.root);
|
|
9417
9666
|
const now = /* @__PURE__ */ new Date();
|
|
9418
9667
|
const records = (await loadSessionEntries2(paths, {
|
|
@@ -9464,7 +9713,7 @@ async function runSessionShow(idInput, options, ctx = {}) {
|
|
|
9464
9713
|
async function doRunSessionShow(idInput, options, ctx) {
|
|
9465
9714
|
const cwd = ctx.cwd ?? process.cwd();
|
|
9466
9715
|
const repositoryRoot = await resolveRepositoryRootForSession(cwd, "show");
|
|
9467
|
-
const paths =
|
|
9716
|
+
const paths = basouPaths17(repositoryRoot);
|
|
9468
9717
|
await assertWorkspaceInitialized11(paths.root);
|
|
9469
9718
|
const sessionId = await resolveSessionId3(paths, idInput);
|
|
9470
9719
|
const sessionDir = join15(paths.sessions, sessionId);
|
|
@@ -9776,7 +10025,7 @@ async function runSessionImport(options, ctx = {}) {
|
|
|
9776
10025
|
async function doRunSessionImport(options, ctx) {
|
|
9777
10026
|
const cwd = ctx.cwd ?? process.cwd();
|
|
9778
10027
|
const repositoryRoot = await resolveRepositoryRootForSession(cwd, "import");
|
|
9779
|
-
const paths =
|
|
10028
|
+
const paths = basouPaths17(repositoryRoot);
|
|
9780
10029
|
await assertWorkspaceInitialized11(paths.root);
|
|
9781
10030
|
const manifest = await readManifest10(paths);
|
|
9782
10031
|
const rawBody = await readInputFile(options.from);
|
|
@@ -9786,7 +10035,9 @@ async function doRunSessionImport(options, ctx) {
|
|
|
9786
10035
|
throw new Error("Invalid import payload", { cause: parsed.error });
|
|
9787
10036
|
}
|
|
9788
10037
|
if (parsed.data.schema_version !== SESSION_IMPORT_SCHEMA_VERSION2) {
|
|
9789
|
-
throw new Error(
|
|
10038
|
+
throw new Error(
|
|
10039
|
+
`Unsupported import schema_version: ${parsed.data.schema_version} (expected ${SESSION_IMPORT_SCHEMA_VERSION2}). The envelope's shape is published as @basou/core/schemas/session-import.schema.json; this version may move at a minor release, so a producer should read it from the installed @basou/core rather than pinning it.`
|
|
10040
|
+
);
|
|
9790
10041
|
}
|
|
9791
10042
|
const importOptions2 = { dryRun: options.dryRun === true };
|
|
9792
10043
|
if (options.label !== void 0) importOptions2.labelOverride = options.label;
|
|
@@ -9825,19 +10076,19 @@ function parseJsonStrict(body) {
|
|
|
9825
10076
|
}
|
|
9826
10077
|
function parseImportFormat(raw) {
|
|
9827
10078
|
if (raw !== "json") {
|
|
9828
|
-
throw new
|
|
10079
|
+
throw new InvalidArgumentError6(`Unsupported format: ${raw}. Valid values: json`);
|
|
9829
10080
|
}
|
|
9830
10081
|
return "json";
|
|
9831
10082
|
}
|
|
9832
10083
|
function parseLabelOverride(raw) {
|
|
9833
10084
|
if (raw.length === 0) {
|
|
9834
|
-
throw new
|
|
10085
|
+
throw new InvalidArgumentError6("Label must not be empty");
|
|
9835
10086
|
}
|
|
9836
10087
|
return raw;
|
|
9837
10088
|
}
|
|
9838
10089
|
function parseTaskIdOverride(raw) {
|
|
9839
10090
|
if (raw.length === 0) {
|
|
9840
|
-
throw new
|
|
10091
|
+
throw new InvalidArgumentError6("Task id is empty");
|
|
9841
10092
|
}
|
|
9842
10093
|
return raw;
|
|
9843
10094
|
}
|
|
@@ -9890,7 +10141,7 @@ async function doRunSessionNote(sessionIdInput, options, ctx) {
|
|
|
9890
10141
|
}
|
|
9891
10142
|
const cwd = ctx.cwd ?? process.cwd();
|
|
9892
10143
|
const repositoryRoot = await resolveRepositoryRootForSession(cwd, "note");
|
|
9893
|
-
const paths =
|
|
10144
|
+
const paths = basouPaths17(repositoryRoot);
|
|
9894
10145
|
await assertWorkspaceInitialized11(paths.root);
|
|
9895
10146
|
const sessionId = await resolveSessionId3(paths, sessionIdInput);
|
|
9896
10147
|
const body = hasBody ? options.body : await readNoteFile(options.fromFile);
|
|
@@ -9910,7 +10161,7 @@ async function doRunSessionNote(sessionIdInput, options, ctx) {
|
|
|
9910
10161
|
id: eventId,
|
|
9911
10162
|
session_id: sesId,
|
|
9912
10163
|
occurred_at: occurredAt,
|
|
9913
|
-
source:
|
|
10164
|
+
source: LOCAL_CLI_EVENT_SOURCE4,
|
|
9914
10165
|
type: "note_added",
|
|
9915
10166
|
body
|
|
9916
10167
|
})
|
|
@@ -9935,7 +10186,7 @@ async function readNoteFile(path) {
|
|
|
9935
10186
|
}
|
|
9936
10187
|
function parseNoteBodyOption(raw) {
|
|
9937
10188
|
if (raw.length === 0) {
|
|
9938
|
-
throw new
|
|
10189
|
+
throw new InvalidArgumentError6("--body must not be empty");
|
|
9939
10190
|
}
|
|
9940
10191
|
return raw;
|
|
9941
10192
|
}
|
|
@@ -9972,7 +10223,7 @@ async function doRunSessionRechain(options, ctx) {
|
|
|
9972
10223
|
}
|
|
9973
10224
|
const cwd = ctx.cwd ?? process.cwd();
|
|
9974
10225
|
const repositoryRoot = await resolveRepositoryRootForSession(cwd, "rechain");
|
|
9975
|
-
const paths =
|
|
10226
|
+
const paths = basouPaths17(repositoryRoot);
|
|
9976
10227
|
await assertWorkspaceInitialized11(paths.root);
|
|
9977
10228
|
const sessionIds = options.session !== void 0 ? [await resolveSessionId3(paths, options.session)] : await enumerateSessionDirs2(paths);
|
|
9978
10229
|
const dryRun = options.dryRun === true;
|
|
@@ -10027,10 +10278,10 @@ function renderRechainRow(row, dryRun) {
|
|
|
10027
10278
|
// src/commands/stats.ts
|
|
10028
10279
|
import {
|
|
10029
10280
|
assertBasouRootSafe as assertBasouRootSafe14,
|
|
10030
|
-
basouPaths as
|
|
10281
|
+
basouPaths as basouPaths18,
|
|
10031
10282
|
computeWorkStats,
|
|
10032
10283
|
findErrorCode as findErrorCode13,
|
|
10033
|
-
resolveRepositoryRoot as
|
|
10284
|
+
resolveRepositoryRoot as resolveRepositoryRoot10
|
|
10034
10285
|
} from "@basou/core";
|
|
10035
10286
|
function registerStatsCommand(program) {
|
|
10036
10287
|
program.command("stats").description("Report how much the AI worked (output volume + time proxies) across sessions").option("--by-source", "Break the totals down by session source kind").option("--by-day", "Break billable time and volume down by calendar day").option("--json", "Output the full stats as JSON").option("-v, --verbose", "Show error causes").action(async (options) => {
|
|
@@ -10048,7 +10299,7 @@ async function runStats(options, ctx = {}) {
|
|
|
10048
10299
|
async function doRunStats(options, ctx) {
|
|
10049
10300
|
const cwd = ctx.cwd ?? process.cwd();
|
|
10050
10301
|
const repositoryRoot = await resolveRepositoryRootForStats(cwd);
|
|
10051
|
-
const paths =
|
|
10302
|
+
const paths = basouPaths18(repositoryRoot);
|
|
10052
10303
|
await assertWorkspaceInitialized12(paths.root);
|
|
10053
10304
|
const now = ctx.nowProvider?.() ?? /* @__PURE__ */ new Date();
|
|
10054
10305
|
const result = await computeWorkStats({
|
|
@@ -10137,7 +10388,7 @@ function formatInt(n) {
|
|
|
10137
10388
|
}
|
|
10138
10389
|
async function resolveRepositoryRootForStats(cwd) {
|
|
10139
10390
|
try {
|
|
10140
|
-
return await
|
|
10391
|
+
return await resolveRepositoryRoot10(cwd);
|
|
10141
10392
|
} catch (error) {
|
|
10142
10393
|
if (error instanceof Error && error.message === "Not a git repository") {
|
|
10143
10394
|
throw new Error("Not a git repository. Run 'git init' first, then re-run 'basou stats'.", {
|
|
@@ -10161,11 +10412,11 @@ async function assertWorkspaceInitialized12(basouRoot) {
|
|
|
10161
10412
|
// src/commands/status.ts
|
|
10162
10413
|
import {
|
|
10163
10414
|
assertBasouRootSafe as assertBasouRootSafe15,
|
|
10164
|
-
basouPaths as
|
|
10415
|
+
basouPaths as basouPaths19,
|
|
10165
10416
|
buildStatusSnapshot,
|
|
10166
10417
|
findErrorCode as findErrorCode14,
|
|
10167
10418
|
readManifest as readManifest11,
|
|
10168
|
-
resolveRepositoryRoot as
|
|
10419
|
+
resolveRepositoryRoot as resolveRepositoryRoot11,
|
|
10169
10420
|
writeStatus
|
|
10170
10421
|
} from "@basou/core";
|
|
10171
10422
|
function registerStatusCommand(program) {
|
|
@@ -10184,7 +10435,7 @@ async function runStatus(options, ctx = {}) {
|
|
|
10184
10435
|
async function doRunStatus(options, ctx) {
|
|
10185
10436
|
const cwd = ctx.cwd ?? process.cwd();
|
|
10186
10437
|
const repositoryRoot = await resolveRepositoryRootForStatus(cwd);
|
|
10187
|
-
const paths =
|
|
10438
|
+
const paths = basouPaths19(repositoryRoot);
|
|
10188
10439
|
try {
|
|
10189
10440
|
await assertBasouRootSafe15(paths.root);
|
|
10190
10441
|
} catch (error) {
|
|
@@ -10223,7 +10474,7 @@ function renderTextStatus(s) {
|
|
|
10223
10474
|
}
|
|
10224
10475
|
async function resolveRepositoryRootForStatus(cwd) {
|
|
10225
10476
|
try {
|
|
10226
|
-
return await
|
|
10477
|
+
return await resolveRepositoryRoot11(cwd);
|
|
10227
10478
|
} catch (error) {
|
|
10228
10479
|
if (error instanceof Error && error.message === "Not a git repository") {
|
|
10229
10480
|
throw new Error("Not a git repository. Run 'git init' first, then re-run 'basou status'.", {
|
|
@@ -10252,7 +10503,7 @@ import { join as join16 } from "path";
|
|
|
10252
10503
|
import {
|
|
10253
10504
|
archiveTask,
|
|
10254
10505
|
assertBasouRootSafe as assertBasouRootSafe16,
|
|
10255
|
-
basouPaths as
|
|
10506
|
+
basouPaths as basouPaths20,
|
|
10256
10507
|
createTaskWithEvent,
|
|
10257
10508
|
deleteTask,
|
|
10258
10509
|
editTask,
|
|
@@ -10274,7 +10525,7 @@ import {
|
|
|
10274
10525
|
TaskWriteAfterEventError,
|
|
10275
10526
|
updateTaskStatusWithEvent
|
|
10276
10527
|
} from "@basou/core";
|
|
10277
|
-
import { InvalidArgumentError as
|
|
10528
|
+
import { InvalidArgumentError as InvalidArgumentError7 } from "commander";
|
|
10278
10529
|
var STATUS_VALUES3 = TaskStatusSchema.options;
|
|
10279
10530
|
function registerTaskCommand(program) {
|
|
10280
10531
|
const task = program.command("task").description("Manage Basou tasks (purpose units that span sessions)");
|
|
@@ -10353,7 +10604,7 @@ async function doRunTaskNew(options, ctx) {
|
|
|
10353
10604
|
}
|
|
10354
10605
|
const cwd = ctx.cwd ?? process.cwd();
|
|
10355
10606
|
const repositoryRoot = await resolveRepositoryRootForTask(cwd, "new");
|
|
10356
|
-
const paths =
|
|
10607
|
+
const paths = basouPaths20(repositoryRoot);
|
|
10357
10608
|
await assertWorkspaceInitialized13(paths.root);
|
|
10358
10609
|
const description = options.description !== void 0 ? options.description : options.fromFile !== void 0 ? await readDescriptionFile(options.fromFile) : "";
|
|
10359
10610
|
const now = ctx.nowProvider !== void 0 ? ctx.nowProvider() : /* @__PURE__ */ new Date();
|
|
@@ -10462,7 +10713,7 @@ async function runTaskList(options, ctx = {}) {
|
|
|
10462
10713
|
async function doRunTaskList(options, ctx) {
|
|
10463
10714
|
const cwd = ctx.cwd ?? process.cwd();
|
|
10464
10715
|
const repositoryRoot = await resolveRepositoryRootForTask(cwd, "list");
|
|
10465
|
-
const paths =
|
|
10716
|
+
const paths = basouPaths20(repositoryRoot);
|
|
10466
10717
|
await assertWorkspaceInitialized13(paths.root);
|
|
10467
10718
|
const entries = await loadTaskEntries(paths, {
|
|
10468
10719
|
onSkip: (id, reason) => printTaskSkip(id, reason)
|
|
@@ -10566,7 +10817,7 @@ async function runTaskShow(idInput, options, ctx = {}) {
|
|
|
10566
10817
|
async function doRunTaskShow(idInput, options, ctx) {
|
|
10567
10818
|
const cwd = ctx.cwd ?? process.cwd();
|
|
10568
10819
|
const repositoryRoot = await resolveRepositoryRootForTask(cwd, "show");
|
|
10569
|
-
const paths =
|
|
10820
|
+
const paths = basouPaths20(repositoryRoot);
|
|
10570
10821
|
await assertWorkspaceInitialized13(paths.root);
|
|
10571
10822
|
const taskId = await resolveTaskId2(paths, idInput, { includeArchived: true });
|
|
10572
10823
|
const { doc, archived } = await readTaskFileWithArchiveFallback(paths, taskId);
|
|
@@ -10710,7 +10961,7 @@ async function doRunTaskStatus(taskIdInput, newStatusInput, options, ctx) {
|
|
|
10710
10961
|
const newStatus = parseTaskStatusPositional(newStatusInput);
|
|
10711
10962
|
const cwd = ctx.cwd ?? process.cwd();
|
|
10712
10963
|
const repositoryRoot = await resolveRepositoryRootForTask(cwd, "status");
|
|
10713
|
-
const paths =
|
|
10964
|
+
const paths = basouPaths20(repositoryRoot);
|
|
10714
10965
|
await assertWorkspaceInitialized13(paths.root);
|
|
10715
10966
|
const taskId = await resolveTaskId2(paths, taskIdInput);
|
|
10716
10967
|
const now = ctx.nowProvider !== void 0 ? ctx.nowProvider() : /* @__PURE__ */ new Date();
|
|
@@ -10787,7 +11038,7 @@ async function runTaskReconcile(options, ctx = {}) {
|
|
|
10787
11038
|
async function doRunTaskReconcile(options, ctx) {
|
|
10788
11039
|
const cwd = ctx.cwd ?? process.cwd();
|
|
10789
11040
|
const repositoryRoot = await resolveRepositoryRootForTask(cwd, "reconcile");
|
|
10790
|
-
const paths =
|
|
11041
|
+
const paths = basouPaths20(repositoryRoot);
|
|
10791
11042
|
await assertWorkspaceInitialized13(paths.root);
|
|
10792
11043
|
const manifest = await readManifest12(paths);
|
|
10793
11044
|
const nowProvider = ctx.nowProvider ?? (() => /* @__PURE__ */ new Date());
|
|
@@ -10967,7 +11218,7 @@ async function doRunTaskRefreshLinkage(taskIdInput, options, ctx) {
|
|
|
10967
11218
|
}
|
|
10968
11219
|
const cwd = ctx.cwd ?? process.cwd();
|
|
10969
11220
|
const repositoryRoot = await resolveRepositoryRootForTask(cwd, "refresh-linkage");
|
|
10970
|
-
const paths =
|
|
11221
|
+
const paths = basouPaths20(repositoryRoot);
|
|
10971
11222
|
await assertWorkspaceInitialized13(paths.root);
|
|
10972
11223
|
const manifest = await readManifest12(paths);
|
|
10973
11224
|
const taskId = await resolveTaskId2(paths, taskIdInput);
|
|
@@ -11047,7 +11298,7 @@ async function doRunTaskEdit(taskIdInput, options, ctx) {
|
|
|
11047
11298
|
}
|
|
11048
11299
|
const cwd = ctx.cwd ?? process.cwd();
|
|
11049
11300
|
const repositoryRoot = await resolveRepositoryRootForTask(cwd, "edit");
|
|
11050
|
-
const paths =
|
|
11301
|
+
const paths = basouPaths20(repositoryRoot);
|
|
11051
11302
|
await assertWorkspaceInitialized13(paths.root);
|
|
11052
11303
|
const manifest = await readManifest12(paths);
|
|
11053
11304
|
const taskId = await resolveTaskId2(paths, taskIdInput);
|
|
@@ -11103,7 +11354,7 @@ async function doRunTaskDelete(taskIdInput, options, ctx) {
|
|
|
11103
11354
|
}
|
|
11104
11355
|
const cwd = ctx.cwd ?? process.cwd();
|
|
11105
11356
|
const repositoryRoot = await resolveRepositoryRootForTask(cwd, "delete");
|
|
11106
|
-
const paths =
|
|
11357
|
+
const paths = basouPaths20(repositoryRoot);
|
|
11107
11358
|
await assertWorkspaceInitialized13(paths.root);
|
|
11108
11359
|
const manifest = await readManifest12(paths);
|
|
11109
11360
|
const taskId = await resolveTaskId2(paths, taskIdInput);
|
|
@@ -11148,7 +11399,7 @@ async function doRunTaskArchive(taskIdInput, options, ctx) {
|
|
|
11148
11399
|
}
|
|
11149
11400
|
const cwd = ctx.cwd ?? process.cwd();
|
|
11150
11401
|
const repositoryRoot = await resolveRepositoryRootForTask(cwd, "archive");
|
|
11151
|
-
const paths =
|
|
11402
|
+
const paths = basouPaths20(repositoryRoot);
|
|
11152
11403
|
await assertWorkspaceInitialized13(paths.root);
|
|
11153
11404
|
const manifest = await readManifest12(paths);
|
|
11154
11405
|
const taskId = await resolveTaskId2(paths, taskIdInput);
|
|
@@ -11203,20 +11454,20 @@ async function readSingleLineFromStdin() {
|
|
|
11203
11454
|
}
|
|
11204
11455
|
function parseTitle2(raw) {
|
|
11205
11456
|
if (raw.length === 0) {
|
|
11206
|
-
throw new
|
|
11457
|
+
throw new InvalidArgumentError7("Title must not be empty");
|
|
11207
11458
|
}
|
|
11208
11459
|
return raw;
|
|
11209
11460
|
}
|
|
11210
11461
|
function parseLabel(raw) {
|
|
11211
11462
|
if (raw.length === 0) {
|
|
11212
|
-
throw new
|
|
11463
|
+
throw new InvalidArgumentError7("Label must not be empty");
|
|
11213
11464
|
}
|
|
11214
11465
|
return raw;
|
|
11215
11466
|
}
|
|
11216
11467
|
function parseInitialTaskStatus(raw) {
|
|
11217
11468
|
const result = TaskStatusSchema.safeParse(raw);
|
|
11218
11469
|
if (!result.success) {
|
|
11219
|
-
throw new
|
|
11470
|
+
throw new InvalidArgumentError7(
|
|
11220
11471
|
`Initial task status must be one of: ${STATUS_VALUES3.join(", ")}`
|
|
11221
11472
|
);
|
|
11222
11473
|
}
|
|
@@ -11225,7 +11476,7 @@ function parseInitialTaskStatus(raw) {
|
|
|
11225
11476
|
var ISO_DATE_RE = /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(?:\.\d+)?(?:Z|[+-]\d{2}:\d{2})$/;
|
|
11226
11477
|
function parseIsoTimestampOption(raw) {
|
|
11227
11478
|
if (!ISO_DATE_RE.test(raw) || Number.isNaN(Date.parse(raw))) {
|
|
11228
|
-
throw new
|
|
11479
|
+
throw new InvalidArgumentError7(
|
|
11229
11480
|
"Invalid --completed-at value; expected ISO-8601 timestamp like 2026-05-10T12:34:56+09:00"
|
|
11230
11481
|
);
|
|
11231
11482
|
}
|
|
@@ -11234,7 +11485,7 @@ function parseIsoTimestampOption(raw) {
|
|
|
11234
11485
|
function parseTaskStatusFilter(raw) {
|
|
11235
11486
|
const result = TaskStatusSchema.safeParse(raw);
|
|
11236
11487
|
if (!result.success) {
|
|
11237
|
-
throw new
|
|
11488
|
+
throw new InvalidArgumentError7(
|
|
11238
11489
|
`Invalid task status: ${raw}. Valid values: ${STATUS_VALUES3.join(", ")}`
|
|
11239
11490
|
);
|
|
11240
11491
|
}
|
|
@@ -11249,14 +11500,14 @@ function parseTaskStatusPositional(raw) {
|
|
|
11249
11500
|
}
|
|
11250
11501
|
function parseDescriptionOption(raw) {
|
|
11251
11502
|
if (raw.length === 0) {
|
|
11252
|
-
throw new
|
|
11503
|
+
throw new InvalidArgumentError7("Description must not be empty");
|
|
11253
11504
|
}
|
|
11254
11505
|
return raw;
|
|
11255
11506
|
}
|
|
11256
11507
|
function parsePositiveInt2(raw) {
|
|
11257
11508
|
const n = Number.parseInt(raw, 10);
|
|
11258
11509
|
if (!Number.isInteger(n) || n < 1 || raw.trim() !== String(n)) {
|
|
11259
|
-
throw new
|
|
11510
|
+
throw new InvalidArgumentError7(`Invalid number: ${raw}`);
|
|
11260
11511
|
}
|
|
11261
11512
|
return n;
|
|
11262
11513
|
}
|
|
@@ -11369,10 +11620,10 @@ function maxLen3(values, floor) {
|
|
|
11369
11620
|
// src/commands/verify.ts
|
|
11370
11621
|
import {
|
|
11371
11622
|
assertBasouRootSafe as assertBasouRootSafe17,
|
|
11372
|
-
basouPaths as
|
|
11623
|
+
basouPaths as basouPaths21,
|
|
11373
11624
|
enumerateSessionDirs as enumerateSessionDirs3,
|
|
11374
11625
|
findErrorCode as findErrorCode16,
|
|
11375
|
-
resolveRepositoryRoot as
|
|
11626
|
+
resolveRepositoryRoot as resolveRepositoryRoot12,
|
|
11376
11627
|
resolveSessionId as resolveSessionId5,
|
|
11377
11628
|
verifyEventsChain
|
|
11378
11629
|
} from "@basou/core";
|
|
@@ -11395,7 +11646,7 @@ async function doRunVerify(options, ctx) {
|
|
|
11395
11646
|
}
|
|
11396
11647
|
const cwd = ctx.cwd ?? process.cwd();
|
|
11397
11648
|
const repositoryRoot = await resolveRepositoryRootForVerify(cwd);
|
|
11398
|
-
const paths =
|
|
11649
|
+
const paths = basouPaths21(repositoryRoot);
|
|
11399
11650
|
await assertWorkspaceInitialized14(paths.root);
|
|
11400
11651
|
const sessionIds = options.session !== void 0 ? [await resolveSessionId5(paths, options.session)] : await enumerateSessionDirs3(paths);
|
|
11401
11652
|
const rows = [];
|
|
@@ -11443,7 +11694,7 @@ function renderVerdict(row) {
|
|
|
11443
11694
|
}
|
|
11444
11695
|
async function resolveRepositoryRootForVerify(cwd) {
|
|
11445
11696
|
try {
|
|
11446
|
-
return await
|
|
11697
|
+
return await resolveRepositoryRoot12(cwd);
|
|
11447
11698
|
} catch (error) {
|
|
11448
11699
|
if (error instanceof Error && error.message === "Not a git repository") {
|
|
11449
11700
|
throw new Error("Not a git repository. Run 'git init' first, then re-run 'basou verify'.", {
|
|
@@ -11470,12 +11721,12 @@ import { createHash as createHash2 } from "crypto";
|
|
|
11470
11721
|
import { basename as basename10, resolve as resolve13 } from "path";
|
|
11471
11722
|
import {
|
|
11472
11723
|
assertBasouRootSafe as assertBasouRootSafe18,
|
|
11473
|
-
basouPaths as
|
|
11724
|
+
basouPaths as basouPaths23,
|
|
11474
11725
|
findErrorCode as findErrorCode18,
|
|
11475
11726
|
readManifest as readManifest16,
|
|
11476
|
-
resolveRepositoryRoot as
|
|
11727
|
+
resolveRepositoryRoot as resolveRepositoryRoot14
|
|
11477
11728
|
} from "@basou/core";
|
|
11478
|
-
import { InvalidArgumentError as
|
|
11729
|
+
import { InvalidArgumentError as InvalidArgumentError8 } from "commander";
|
|
11479
11730
|
|
|
11480
11731
|
// src/lib/portfolio-coverage.ts
|
|
11481
11732
|
import { createReadStream as createReadStream2 } from "fs";
|
|
@@ -11483,7 +11734,7 @@ import { readdir as readdir3, stat as stat6 } from "fs/promises";
|
|
|
11483
11734
|
import { homedir as homedir12 } from "os";
|
|
11484
11735
|
import { basename as basename8, dirname as dirname5, join as join17 } from "path";
|
|
11485
11736
|
import { createInterface as createInterface2 } from "readline";
|
|
11486
|
-
import { basouPaths as
|
|
11737
|
+
import { basouPaths as basouPaths22, readManifest as readManifest13, resolveRepositoryRoot as resolveRepositoryRoot13 } from "@basou/core";
|
|
11487
11738
|
function uncapturedTotal(result) {
|
|
11488
11739
|
return result.groups.reduce((sum, g) => sum + g.logs, 0) + result.unplaceable;
|
|
11489
11740
|
}
|
|
@@ -11519,14 +11770,14 @@ async function collectDeclaredRoots(workspaces) {
|
|
|
11519
11770
|
for (const ws of workspaces) {
|
|
11520
11771
|
let importRoot;
|
|
11521
11772
|
try {
|
|
11522
|
-
importRoot = await
|
|
11773
|
+
importRoot = await resolveRepositoryRoot13(ws.repoRoot);
|
|
11523
11774
|
} catch {
|
|
11524
11775
|
inertWorkspaces.push({ path: ws.repoRoot, reason: "not_a_git_repo" });
|
|
11525
11776
|
continue;
|
|
11526
11777
|
}
|
|
11527
11778
|
let resolved;
|
|
11528
11779
|
try {
|
|
11529
|
-
const manifest = await readManifest13(
|
|
11780
|
+
const manifest = await readManifest13(basouPaths22(importRoot));
|
|
11530
11781
|
resolved = resolveSourceRoots({
|
|
11531
11782
|
projectFlags: [],
|
|
11532
11783
|
manifest,
|
|
@@ -11793,12 +12044,12 @@ function inertLines(result) {
|
|
|
11793
12044
|
}
|
|
11794
12045
|
|
|
11795
12046
|
// src/lib/portfolio-safety.ts
|
|
11796
|
-
import { execFile } from "child_process";
|
|
12047
|
+
import { execFile as execFile2 } from "child_process";
|
|
11797
12048
|
import { lstat as lstat2, realpath as realpath4 } from "fs/promises";
|
|
11798
12049
|
import { isAbsolute as isAbsolute7, join as join18, relative as relative4, resolve as resolve11 } from "path";
|
|
11799
|
-
import { promisify } from "util";
|
|
12050
|
+
import { promisify as promisify2 } from "util";
|
|
11800
12051
|
import { readManifest as readManifest14 } from "@basou/core";
|
|
11801
|
-
var
|
|
12052
|
+
var execFileAsync2 = promisify2(execFile2);
|
|
11802
12053
|
function errorCode(error) {
|
|
11803
12054
|
return error instanceof Error ? error.code : void 0;
|
|
11804
12055
|
}
|
|
@@ -11830,7 +12081,7 @@ async function inspectRepo(repoPath) {
|
|
|
11830
12081
|
}
|
|
11831
12082
|
}
|
|
11832
12083
|
try {
|
|
11833
|
-
const { stdout } = await
|
|
12084
|
+
const { stdout } = await execFileAsync2("git", ["-C", repoPath, "ls-files", "-z"]);
|
|
11834
12085
|
const tracked = stdout.split("\0").some((f) => f.length > 0 && isBasouPath(f));
|
|
11835
12086
|
if (tracked) {
|
|
11836
12087
|
return {
|
|
@@ -13163,7 +13414,7 @@ var DEFAULT_PORT = 4319;
|
|
|
13163
13414
|
function parsePort(value) {
|
|
13164
13415
|
const port = Number.parseInt(value, 10);
|
|
13165
13416
|
if (!Number.isInteger(port) || port < 1 || port > 65535) {
|
|
13166
|
-
throw new
|
|
13417
|
+
throw new InvalidArgumentError8("Port must be an integer between 1 and 65535.");
|
|
13167
13418
|
}
|
|
13168
13419
|
return port;
|
|
13169
13420
|
}
|
|
@@ -13249,7 +13500,7 @@ async function doRunView(options, ctx) {
|
|
|
13249
13500
|
}
|
|
13250
13501
|
async function buildSingleDeps(ctx, cwd) {
|
|
13251
13502
|
const repositoryRoot = await resolveRepositoryRootForView(cwd);
|
|
13252
|
-
const paths =
|
|
13503
|
+
const paths = basouPaths23(repositoryRoot);
|
|
13253
13504
|
await assertWorkspaceInitialized15(paths.root);
|
|
13254
13505
|
const entry = await buildWorkspaceEntry(repositoryRoot, ctx);
|
|
13255
13506
|
return {
|
|
@@ -13283,7 +13534,7 @@ async function buildPortfolioDeps(workspaceFlags, ctx, cwd) {
|
|
|
13283
13534
|
};
|
|
13284
13535
|
}
|
|
13285
13536
|
async function buildWorkspaceEntry(repoRoot, ctx, labelOverride) {
|
|
13286
|
-
const paths =
|
|
13537
|
+
const paths = basouPaths23(repoRoot);
|
|
13287
13538
|
const importCtx = {
|
|
13288
13539
|
cwd: repoRoot,
|
|
13289
13540
|
...ctx.claudeProjectsDir !== void 0 ? { claudeProjectsDir: ctx.claudeProjectsDir } : {},
|
|
@@ -13370,7 +13621,7 @@ function waitForShutdown(signal) {
|
|
|
13370
13621
|
}
|
|
13371
13622
|
async function resolveRepositoryRootForView(cwd) {
|
|
13372
13623
|
try {
|
|
13373
|
-
return await
|
|
13624
|
+
return await resolveRepositoryRoot14(cwd);
|
|
13374
13625
|
} catch (error) {
|
|
13375
13626
|
if (error instanceof Error && error.message === "Not a git repository") {
|
|
13376
13627
|
throw new Error("Not a git repository. Run 'git init' first, then re-run 'basou view'.", {
|
|
@@ -13392,12 +13643,30 @@ async function assertWorkspaceInitialized15(basouRoot) {
|
|
|
13392
13643
|
}
|
|
13393
13644
|
|
|
13394
13645
|
// src/program.ts
|
|
13646
|
+
function readBuildStamp() {
|
|
13647
|
+
if (false) return void 0;
|
|
13648
|
+
try {
|
|
13649
|
+
return JSON.parse('{"version":"0.45.0","commit":"6b04208","committedAt":"2026-09-19T01:07:28+09:00"}');
|
|
13650
|
+
} catch {
|
|
13651
|
+
return void 0;
|
|
13652
|
+
}
|
|
13653
|
+
}
|
|
13395
13654
|
var require2 = createRequire(import.meta.url);
|
|
13396
13655
|
var pkg = require2("../package.json");
|
|
13397
|
-
var
|
|
13656
|
+
var BASOU_BUILD = readBuildStamp();
|
|
13657
|
+
var BASOU_CLI_VERSION = BASOU_BUILD?.version ?? pkg.version;
|
|
13658
|
+
var BASOU_VERSION_LINE = buildVersionLine();
|
|
13659
|
+
function buildVersionLine() {
|
|
13660
|
+
if (BASOU_BUILD === void 0) return `${pkg.version} (source)`;
|
|
13661
|
+
const coreBuild = basouCore.BASOU_CORE_BUILD;
|
|
13662
|
+
const self = `${BASOU_BUILD.version} (build ${BASOU_BUILD.commit}, ${BASOU_BUILD.committedAt})`;
|
|
13663
|
+
if (coreBuild === void 0) return `${self}; core build unknown (not stamped)`;
|
|
13664
|
+
if (coreBuild.commit === BASOU_BUILD.commit) return self;
|
|
13665
|
+
return `${self}; core build ${coreBuild.commit}, ${coreBuild.committedAt}`;
|
|
13666
|
+
}
|
|
13398
13667
|
function buildProgram() {
|
|
13399
13668
|
const program = new Command();
|
|
13400
|
-
program.name("basou").description("A harness for steering AI coding agents").version(
|
|
13669
|
+
program.name("basou").description("A harness for steering AI coding agents").version(BASOU_VERSION_LINE).enablePositionalOptions();
|
|
13401
13670
|
registerInitCommand(program);
|
|
13402
13671
|
registerStatusCommand(program);
|
|
13403
13672
|
registerStatsCommand(program);
|
|
@@ -13426,7 +13695,9 @@ function buildProgram() {
|
|
|
13426
13695
|
return program;
|
|
13427
13696
|
}
|
|
13428
13697
|
export {
|
|
13698
|
+
BASOU_BUILD,
|
|
13429
13699
|
BASOU_CLI_VERSION,
|
|
13700
|
+
BASOU_VERSION_LINE,
|
|
13430
13701
|
buildProgram
|
|
13431
13702
|
};
|
|
13432
13703
|
//# sourceMappingURL=program.js.map
|