@basou/cli 0.46.0 → 0.47.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 +53 -24
- package/dist/index.js.map +1 -1
- package/dist/program.js +53 -24
- package/dist/program.js.map +1 -1
- package/package.json +2 -2
package/dist/program.js
CHANGED
|
@@ -1250,7 +1250,8 @@ Input format (a JSON array; one object per decision):
|
|
|
1250
1250
|
"rationale": "Workspace protocol and a content-addressed store fit our layout.",
|
|
1251
1251
|
"alternatives": ["npm workspaces", "yarn"],
|
|
1252
1252
|
"rejected_reason": "npm hoisting caused phantom-dependency bugs",
|
|
1253
|
-
"linked_files": ["pnpm-workspace.yaml"]
|
|
1253
|
+
"linked_files": ["pnpm-workspace.yaml"],
|
|
1254
|
+
"kind": "decision"
|
|
1254
1255
|
},
|
|
1255
1256
|
{
|
|
1256
1257
|
"title": "Form-based admin editing is the next track (only 6/19 sections done)",
|
|
@@ -1259,10 +1260,14 @@ Input format (a JSON array; one object per decision):
|
|
|
1259
1260
|
}
|
|
1260
1261
|
]
|
|
1261
1262
|
|
|
1262
|
-
|
|
1263
|
-
|
|
1264
|
-
|
|
1265
|
-
|
|
1263
|
+
"title" is required. "kind" names the vessel: "track" records a strategic,
|
|
1264
|
+
UNFINISHED direction (+ why) and orientation/handoff resurface it every session
|
|
1265
|
+
until you close it with 'basou decision void <id>', while "decision" records a
|
|
1266
|
+
point-in-time call (surfaced only as the latest). Every other field is optional.
|
|
1267
|
+
|
|
1268
|
+
DEPRECATED: omitting "kind" still writes the item, as a decision, but warns --
|
|
1269
|
+
and becomes an ERROR in the next release. It has no default because getting it
|
|
1270
|
+
wrong fails silently: a track filed as a decision simply never comes back.
|
|
1266
1271
|
All decisions are written into one ad-hoc session timestamped now, so
|
|
1267
1272
|
orientation surfaces them as the latest decisions. Run from a workspace-view
|
|
1268
1273
|
directory and it resolves to the planning repo, like 'basou orient' /
|
|
@@ -1270,7 +1275,7 @@ directory and it resolves to the planning repo, like 'basou orient' /
|
|
|
1270
1275
|
|
|
1271
1276
|
Example (heredoc on stdin):
|
|
1272
1277
|
basou decision capture <<'JSON'
|
|
1273
|
-
[{ "title": "Ship the capture command", "rationale": "Close the why-capture gap" }]
|
|
1278
|
+
[{ "title": "Ship the capture command", "rationale": "Close the why-capture gap", "kind": "decision" }]
|
|
1274
1279
|
JSON
|
|
1275
1280
|
`;
|
|
1276
1281
|
async function runDecisionRecord(options, ctx = {}) {
|
|
@@ -1306,12 +1311,11 @@ async function warnLinkedFilesOutsideRoots(input) {
|
|
|
1306
1311
|
} catch {
|
|
1307
1312
|
}
|
|
1308
1313
|
}
|
|
1309
|
-
|
|
1310
|
-
|
|
1311
|
-
for (const index of markerWithoutKind) {
|
|
1314
|
+
function warnMissingKind(decisions, indices) {
|
|
1315
|
+
for (const index of indices) {
|
|
1312
1316
|
const title = (decisions[index]?.title ?? "").trim();
|
|
1313
1317
|
console.error(
|
|
1314
|
-
`basou: decision[${index}]
|
|
1318
|
+
`basou: decision[${index}] ("${title.slice(0, 40)}") declares no "kind" \u2014 it is recorded as a point-in-time decision and will NOT resurface in orient. Set "kind": "track" for an unfinished direction, or "kind": "decision" to say it is settled. Omitting it becomes an ERROR in the next release.`
|
|
1315
1319
|
);
|
|
1316
1320
|
}
|
|
1317
1321
|
}
|
|
@@ -1412,8 +1416,8 @@ async function doRunDecisionCapture(options, ctx) {
|
|
|
1412
1416
|
const paths = basouPaths4(repositoryRoot);
|
|
1413
1417
|
await assertWorkspaceInitialized2(paths.root);
|
|
1414
1418
|
const raw = await readCaptureInput(options, ctx);
|
|
1415
|
-
const { decisions,
|
|
1416
|
-
|
|
1419
|
+
const { decisions, missingKind } = parseCaptureInput(raw);
|
|
1420
|
+
warnMissingKind(decisions, missingKind);
|
|
1417
1421
|
await warnLinkedFilesOutsideRoots({
|
|
1418
1422
|
linkedFiles: decisions.flatMap((d) => d.linked_files ?? []),
|
|
1419
1423
|
cwd,
|
|
@@ -1675,13 +1679,13 @@ function parseCaptureInput(raw) {
|
|
|
1675
1679
|
throw new Error("Input array must contain at least one decision.");
|
|
1676
1680
|
}
|
|
1677
1681
|
const decisions = [];
|
|
1678
|
-
const
|
|
1682
|
+
const missingKind = [];
|
|
1679
1683
|
parsed.forEach((item, index) => {
|
|
1680
|
-
const
|
|
1684
|
+
const input = validateCaptureItem(item, index);
|
|
1681
1685
|
decisions.push(input);
|
|
1682
|
-
if (
|
|
1686
|
+
if (input.kind === void 0) missingKind.push(index);
|
|
1683
1687
|
});
|
|
1684
|
-
return { decisions,
|
|
1688
|
+
return { decisions, missingKind };
|
|
1685
1689
|
}
|
|
1686
1690
|
function validateCaptureItem(item, index) {
|
|
1687
1691
|
if (typeof item !== "object" || item === null || Array.isArray(item)) {
|
|
@@ -1703,7 +1707,7 @@ function validateCaptureItem(item, index) {
|
|
|
1703
1707
|
if (obj.kind !== "decision" && obj.kind !== "track") {
|
|
1704
1708
|
throw new Error(`decision[${index}].kind must be "decision" or "track", got '${obj.kind}'.`);
|
|
1705
1709
|
}
|
|
1706
|
-
|
|
1710
|
+
out.kind = obj.kind;
|
|
1707
1711
|
}
|
|
1708
1712
|
if (obj.rationale !== void 0) {
|
|
1709
1713
|
out.rationale = requireNonEmptyString(obj.rationale, index, "rationale");
|
|
@@ -1742,7 +1746,7 @@ function validateCaptureItem(item, index) {
|
|
|
1742
1746
|
}
|
|
1743
1747
|
});
|
|
1744
1748
|
}
|
|
1745
|
-
return
|
|
1749
|
+
return out;
|
|
1746
1750
|
}
|
|
1747
1751
|
function requireNonEmptyString(value, index, field) {
|
|
1748
1752
|
if (typeof value !== "string" || isBlank(value)) {
|
|
@@ -1793,8 +1797,13 @@ function captureItemToPayload(item) {
|
|
|
1793
1797
|
if (item.input.kind !== void 0) payload.kind = item.input.kind;
|
|
1794
1798
|
return payload;
|
|
1795
1799
|
}
|
|
1796
|
-
function
|
|
1797
|
-
|
|
1800
|
+
function previewKindMarker(kind) {
|
|
1801
|
+
if (kind === "track") return " [TRACK]";
|
|
1802
|
+
return kind === "decision" ? " [DECISION]" : " [NO KIND]";
|
|
1803
|
+
}
|
|
1804
|
+
function recordedKindMarker(kind) {
|
|
1805
|
+
if (kind === "track") return " [TRACK]";
|
|
1806
|
+
return kind === "decision" ? "" : " [NO KIND]";
|
|
1798
1807
|
}
|
|
1799
1808
|
function printCapturePreview(options, decisions) {
|
|
1800
1809
|
if (options.json === true) {
|
|
@@ -1805,7 +1814,7 @@ function printCapturePreview(options, decisions) {
|
|
|
1805
1814
|
`Would capture ${decisions.length} decision${decisions.length === 1 ? "" : "s"} (dry run; nothing written):`
|
|
1806
1815
|
);
|
|
1807
1816
|
for (const decision of decisions) {
|
|
1808
|
-
console.log(`- ${decision.title}${
|
|
1817
|
+
console.log(`- ${decision.title}${previewKindMarker(decision.kind)}`);
|
|
1809
1818
|
}
|
|
1810
1819
|
}
|
|
1811
1820
|
function printCaptureResult(options, result) {
|
|
@@ -1826,7 +1835,7 @@ function printCaptureResult(options, result) {
|
|
|
1826
1835
|
`Captured ${result.items.length} decision${result.items.length === 1 ? "" : "s"} in ad-hoc session ${sid}:`
|
|
1827
1836
|
);
|
|
1828
1837
|
for (const item of result.items) {
|
|
1829
|
-
console.log(`- ${item.decisionId}: ${item.input.title}${
|
|
1838
|
+
console.log(`- ${item.decisionId}: ${item.input.title}${recordedKindMarker(item.input.kind)}`);
|
|
1830
1839
|
}
|
|
1831
1840
|
}
|
|
1832
1841
|
function pickRichFields(options) {
|
|
@@ -12705,6 +12714,18 @@ var VIEW_HTML = `<!doctype html>
|
|
|
12705
12714
|
return el('span', { class: 'badge ok', text: 'up to date' });
|
|
12706
12715
|
}
|
|
12707
12716
|
|
|
12717
|
+
// Three states hide behind a bare 'in-flight 0': nothing ever recorded,
|
|
12718
|
+
// everything finished, and a task store that cannot be read -- where the
|
|
12719
|
+
// count is not zero but UNKNOWN. Say which one it is, as orient does.
|
|
12720
|
+
// Kept as a named top-level function with no free variables so the test suite
|
|
12721
|
+
// can lift it out of this template and run it; inlined in the card it was
|
|
12722
|
+
// unreachable from any test.
|
|
12723
|
+
function taskFlightLabel(w) {
|
|
12724
|
+
if (w.unreadableTaskCount > 0) return 'in-flight unknown (' + w.unreadableTaskCount + ' unreadable)';
|
|
12725
|
+
if (w.anyTaskEverRecorded === false) return 'no tasks recorded';
|
|
12726
|
+
return 'in-flight ' + w.inFlightCount;
|
|
12727
|
+
}
|
|
12728
|
+
|
|
12708
12729
|
function portfolioCard(w, generatedAt) {
|
|
12709
12730
|
if (!w.initialized) {
|
|
12710
12731
|
return el('div', { class: 'card pcard muted' }, [
|
|
@@ -12720,6 +12741,7 @@ var VIEW_HTML = `<!doctype html>
|
|
|
12720
12741
|
}
|
|
12721
12742
|
var pend = w.pendingApprovals || [];
|
|
12722
12743
|
var pendText = 'pending ' + pend.length + (pend.length ? ' (' + highestRisk(pend) + ')' : '');
|
|
12744
|
+
var flightText = taskFlightLabel(w);
|
|
12723
12745
|
var now = w.latestSession ? ((w.latestSession.label || '(session)') + ' [' + w.latestSession.status + ']') : '(no live sessions)';
|
|
12724
12746
|
var dec = w.latestDecision ? w.latestDecision.title : '(no decisions yet)';
|
|
12725
12747
|
var newest = (w.freshness && w.freshness.newestStartedAt) ? w.freshness.newestStartedAt : null;
|
|
@@ -12732,7 +12754,7 @@ var VIEW_HTML = `<!doctype html>
|
|
|
12732
12754
|
]),
|
|
12733
12755
|
el('div', { class: 'f', text: 'now: ' + now }),
|
|
12734
12756
|
el('div', { class: 'f', text: 'latest: ' + dec }),
|
|
12735
|
-
el('div', { class: 'f', text:
|
|
12757
|
+
el('div', { class: 'f', text: flightText + ' | ' + pendText + ' | suspect ' + w.suspectCount }),
|
|
12736
12758
|
el('div', { class: 'f muted', text: 'sessions ' + w.sessionCount + ' | newest ' + relAge(newest, generatedAt) })
|
|
12737
12759
|
]);
|
|
12738
12760
|
}
|
|
@@ -13309,6 +13331,13 @@ async function portfolioCard(ws, nowIso) {
|
|
|
13309
13331
|
sessionCount: s.sessionCount,
|
|
13310
13332
|
suspectCount: s.suspects.length,
|
|
13311
13333
|
inFlightCount: s.inFlightTasks.length,
|
|
13334
|
+
// Three states hide behind `inFlightCount: 0`: nothing ever recorded,
|
|
13335
|
+
// everything finished, and a store whose task files cannot be read (where
|
|
13336
|
+
// the count is not 0 but UNKNOWN). `orient` branches all three ways; the
|
|
13337
|
+
// card carries the two extra facts so it can too, instead of reporting a
|
|
13338
|
+
// loader blind spot as an empty record.
|
|
13339
|
+
anyTaskEverRecorded: s.anyTaskEverRecorded,
|
|
13340
|
+
unreadableTaskCount: s.unreadableTaskCount,
|
|
13312
13341
|
pendingApprovals: s.pendingApprovals.map((a) => ({
|
|
13313
13342
|
risk: a.risk,
|
|
13314
13343
|
kind: a.kind,
|
|
@@ -13776,7 +13805,7 @@ async function assertWorkspaceInitialized15(basouRoot) {
|
|
|
13776
13805
|
function readBuildStamp() {
|
|
13777
13806
|
if (false) return void 0;
|
|
13778
13807
|
try {
|
|
13779
|
-
return JSON.parse('{"version":"0.
|
|
13808
|
+
return JSON.parse('{"version":"0.47.0","commit":"831e0cb","committedAt":"2026-09-20T22:28:13+09:00"}');
|
|
13780
13809
|
} catch {
|
|
13781
13810
|
return void 0;
|
|
13782
13811
|
}
|