@basou/cli 0.66.0 → 0.67.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/program.js CHANGED
@@ -840,6 +840,7 @@ import {
840
840
  displayPath as displayPath3,
841
841
  findErrorCode as findErrorCode3,
842
842
  measureBoard,
843
+ measureBoardLive,
843
844
  NO_PREVIOUS_RECORDS,
844
845
  parseBoardDeclaration,
845
846
  parseRecordInput,
@@ -1515,7 +1516,13 @@ async function assertWorkspaceInitialized2(basouRoot) {
1515
1516
  }
1516
1517
 
1517
1518
  // src/lib/provenance-actions.ts
1518
- async function captureImportJson(fn) {
1519
+ var capturing = Promise.resolve();
1520
+ function captureImportJson(fn) {
1521
+ const turn = capturing.then(() => captureNow(fn));
1522
+ capturing = turn.catch(() => void 0);
1523
+ return turn;
1524
+ }
1525
+ async function captureNow(fn) {
1519
1526
  const stdout = [];
1520
1527
  const originalLog = console.log;
1521
1528
  const originalError = console.error;
@@ -2024,6 +2031,41 @@ async function measureLoaded(loaded, ctx, model, previous) {
2024
2031
  previous
2025
2032
  });
2026
2033
  }
2034
+ async function measureLiveBoard(root, ctx) {
2035
+ const paths = basouPaths4(root);
2036
+ let repos = ["."];
2037
+ let manifestUnread = false;
2038
+ try {
2039
+ const declared = ((await readManifest3(paths)).repos ?? []).map((repo) => repo.path);
2040
+ if (declared.length > 0) repos = declared;
2041
+ } catch {
2042
+ manifestUnread = true;
2043
+ }
2044
+ const now = ctx.nowProvider?.() ?? /* @__PURE__ */ new Date();
2045
+ const probeCtx = { cwd: root };
2046
+ if (ctx.claudeProjectsDir !== void 0) probeCtx.claudeProjectsDir = ctx.claudeProjectsDir;
2047
+ if (ctx.codexSessionsDir !== void 0) probeCtx.codexSessionsDir = ctx.codexSessionsDir;
2048
+ const measured = await measureBoardLive({
2049
+ root,
2050
+ repos,
2051
+ paths,
2052
+ now,
2053
+ measuredWith: { basou: BASOU_CLI_VERSION, build: BASOU_BUILD?.commit ?? null },
2054
+ probeImports: () => probeStaleness({ ctx: probeCtx, paths, nowIso: now.toISOString() })
2055
+ });
2056
+ if (!manifestUnread) return measured;
2057
+ return {
2058
+ ...measured,
2059
+ complete: false,
2060
+ not_found: [
2061
+ {
2062
+ at: "repos",
2063
+ reason: "the manifest could not be read, so only the workspace's own repo was measured"
2064
+ },
2065
+ ...measured.not_found
2066
+ ]
2067
+ };
2068
+ }
2027
2069
  function boardPath(options, cwd, root, manifest) {
2028
2070
  if (options.board !== void 0)
2029
2071
  return { path: resolve3(cwd, options.board), shown: options.board, given: true };
@@ -13234,6 +13276,7 @@ function formatSafetyReport(result) {
13234
13276
  import { createServer } from "http";
13235
13277
  import { basename as basename10, join as join18, resolve as resolve13 } from "path";
13236
13278
  import {
13279
+ boardLivePage,
13237
13280
  boardPage,
13238
13281
  boardPageStrings,
13239
13282
  boardPageUnavailable,
@@ -13375,6 +13418,7 @@ var BOARD_HTML = `<!doctype html>
13375
13418
  if (k === 'text') node.textContent = attrs[k];
13376
13419
  else if (k === 'class') node.className = attrs[k];
13377
13420
  else if (k === 'onchange') node.addEventListener('change', attrs[k]);
13421
+ else if (k === 'onclick') node.addEventListener('click', attrs[k]);
13378
13422
  else node.setAttribute(k, attrs[k]);
13379
13423
  });
13380
13424
  }
@@ -13468,7 +13512,11 @@ var BOARD_HTML = `<!doctype html>
13468
13512
  clear(nav);
13469
13513
  var page = d.page;
13470
13514
  var list = page.records || [];
13471
- if (list.length === 0) return;
13515
+ if (list.length > 0) recordLinks(nav, page, list);
13516
+ nav.appendChild(el('a', { href: '/board?live=1', text: S.live.measureNow }));
13517
+ }
13518
+
13519
+ function recordLinks(nav, page, list) {
13472
13520
  var link = function (id, label) {
13473
13521
  return el('a', { href: '/board?record=' + encodeURIComponent(id), text: label });
13474
13522
  };
@@ -13949,8 +13997,12 @@ var BOARD_HTML = `<!doctype html>
13949
13997
  not_found: S.unavailable.notFound, not_json: S.unavailable.notJson,
13950
13998
  unknown_version: S.unavailable.unknownVersion, not_a_record: S.unavailable.notARecord
13951
13999
  };
13952
- var quiet = page.why === 'no_board' || page.why === 'no_records';
13953
- setStatus(fill(messages[page.why] || page.why, { record: page.id || '', version: page.version }), !quiet);
14000
+ // With no board or no record yet, draw what basou measures now instead.
14001
+ if (page.why === 'no_board' || page.why === 'no_records') {
14002
+ loadLive(page.why);
14003
+ return;
14004
+ }
14005
+ setStatus(fill(messages[page.why] || page.why, { record: page.id || '', version: page.version }), true);
13954
14006
  return;
13955
14007
  }
13956
14008
  setStatus('', false);
@@ -13959,17 +14011,153 @@ var BOARD_HTML = `<!doctype html>
13959
14011
  .forEach(function (node) { root.appendChild(node); });
13960
14012
  }
13961
14013
 
13962
- var record = new URLSearchParams(location.search).get('record');
13963
- setStatus('...', false);
13964
- fetch('/api/board' + (record ? '/' + encodeURIComponent(record) : ''))
13965
- .then(function (res) {
14014
+ // The board measured now: the sections basou measures with nothing declared
14015
+ // or judged. why is the reason there was no record to draw, or null when
14016
+ // asked for with ?live=1.
14017
+ function liveTile(k, v, sub) {
14018
+ return el('div', { class: 'tile' }, [
14019
+ el('div', { class: 'k', text: k }), el('div', { class: 'v', text: v }),
14020
+ sub ? el('div', { class: 's', text: sub }) : null
14021
+ ]);
14022
+ }
14023
+ function liveHeading(l, why) {
14024
+ var h = l.heading;
14025
+ var measured = h.complete ? S.heading.complete : fill(S.heading.incomplete, { n: h.not_found });
14026
+ var again = el('button', { type: 'button', text: S.live.remeasure, onclick: function () { loadLive(why); } });
14027
+ return el('header', null, [
14028
+ el('h1', { text: h.title }),
14029
+ el('div', { class: 'stamp' }, [fill(S.live.measuredAt, { at: when(h.measured_at) }) + ' / ' + measured + ' ', again])
14030
+ ]);
14031
+ }
14032
+ // Whether the measurement says why the value at a place was not measured.
14033
+ function missingIn(l) {
14034
+ return function (at) { return l.footnotes.not_found.some(function (n) { return n.at === at; }); };
14035
+ }
14036
+ function liveRepos(l) {
14037
+ var missing = missingIn(l);
14038
+ var labels = [S.live.repo, S.live.branch, S.live.head, S.live.lastCommit, S.live.commits, S.live.uncommitted, S.live.behindMain];
14039
+ var head = el('tr', null, labels.map(function (label, i) { return el('th', { class: i >= 4 ? 'n' : '', text: label }); }));
14040
+ var rows = l.repos.map(function (r) {
14041
+ var at = 'repos[' + r.path + ']';
14042
+ // A null the measurement says it could not measure, or one that means
14043
+ // so (a detached HEAD, no commit yet, no origin/main), drawn as '-'.
14044
+ var value = function (field, v, show) {
14045
+ if (v !== null) return show(v);
14046
+ return missing(at) || missing(at + '.' + field) ? S.tiles.notMeasured : '-';
14047
+ };
14048
+ return el('tr', null, [
14049
+ el('td', { text: r.name === null ? r.path : r.name }),
14050
+ el('td', { text: value('branch', r.branch, String) }),
14051
+ el('td', { text: value('head', r.head, function (h) { return h.slice(0, 7); }) }),
14052
+ el('td', { text: value('last_commit', r.last_commit, when) }),
14053
+ el('td', { class: 'n', text: num(r.commits) }),
14054
+ el('td', { class: 'n', text: num(r.uncommitted) }),
14055
+ el('td', { class: 'n', text: value('behind_main', r.behind_main, num) })
14056
+ ]);
14057
+ });
14058
+ return section(S.live.repos, false, [el('div', { class: 'scroll' }, [el('table', { class: 'grid' }, [head].concat(rows))])]);
14059
+ }
14060
+ function liveTrail(l) {
14061
+ var t = l.trail;
14062
+ var i = l.integrity;
14063
+ var f = l.freshness;
14064
+ var missing = missingIn(l);
14065
+ var newest = f.newest_session_at !== null ? when(f.newest_session_at)
14066
+ : missing('freshness.newest_session_at') ? S.tiles.notMeasured : S.live.noSession;
14067
+ var unimported = f.unimported === null ? S.live.unimportedNotMeasured : fill(S.live.unimported, {
14068
+ new: f.unimported.new, updated: f.unimported.updated, unverifiable: f.unimported.unverifiable
14069
+ });
14070
+ var counts = function (byKey) {
14071
+ if (byKey === null) return S.tiles.notMeasured;
14072
+ return Object.keys(byKey).map(function (k) { return k + ' ' + num(byKey[k]); }).join(' / ');
14073
+ };
14074
+ var tiles = el('div', { class: 'tiles' }, [
14075
+ liveTile(S.live.decisions, num(t.decisions_live), t.decisions_all === null ? null : fill(S.live.decisionsAll, { n: num(t.decisions_all) })),
14076
+ liveTile(S.tiles.openTracks, t.tracks_open === null ? S.tiles.notMeasured : num(t.tracks_open.length), null),
14077
+ liveTile(S.tiles.sessions, num(i.sessions), i.not_verified === null ? null : fill(S.tiles.sessionsNotVerified, { n: num(i.not_verified) })),
14078
+ liveTile(S.live.reviewGaps, num(l.review_gaps.gaps), null),
14079
+ liveTile(S.live.newestSession, newest, unimported)
14080
+ ]);
14081
+ var tracks = t.tracks_open === null ? el('p', { class: 'muted', text: S.tiles.notMeasured })
14082
+ : t.tracks_open.length === 0 ? el('p', { class: 'muted', text: S.live.noTracks })
14083
+ : el('ul', null, t.tracks_open.map(function (tr) {
14084
+ return el('li', null, [el('span', { class: 'muted', text: tr.id + ' ' }), tr.title]);
14085
+ }));
14086
+ var breakdown = el('div', { class: 'muted' }, [
14087
+ el('div', { text: fill(S.live.byStatus, { list: counts(i.by_status) }) }),
14088
+ el('div', { text: fill(S.live.byVerdict, { list: counts(l.review_gaps.by_verdict) }) })
14089
+ ]);
14090
+ return section(S.live.trail, false, [tiles, breakdown, el('h3', { text: S.live.tracks }), tracks]);
14091
+ }
14092
+ function liveComponents(l) {
14093
+ var c = l.components;
14094
+ var body = c === null ? el('p', { class: 'muted', text: S.tiles.notMeasured })
14095
+ : c.length === 0 ? el('p', { class: 'muted', text: S.live.noComponents })
14096
+ : el('ul', null, c.map(function (x) {
14097
+ return el('li', null, [el('code', { text: x.key }), el('span', { class: 'muted', text: ' ' + x.kinds.join(', ') })]);
14098
+ }));
14099
+ return section(S.live.components, false, [body]);
14100
+ }
14101
+ function liveFootnotes(l, why) {
14102
+ var f = l.footnotes;
14103
+ var items = [el('li', { text: S.live.note }), el('li', { text: S.live.judged })];
14104
+ if (why === 'no_board') items.push(el('li', { text: S.unavailable.noBoard }));
14105
+ if (f.not_found.length > 0) {
14106
+ items.push(el('li', null, [el('b', { text: S.footnotes.notMeasured }), el('ul', null, f.not_found.map(function (n) {
14107
+ return el('li', { text: n.at + ': ' + n.reason });
14108
+ }))]));
14109
+ }
14110
+ var build = f.measured_with.build === null ? '' : ' (' + f.measured_with.build + ')';
14111
+ items.push(el('li', { text: fill(S.footnotes.measuredWith, { basou: f.measured_with.basou, build: build }) }));
14112
+ return section(S.sections.footnotes, false, [el('ul', { class: 'foot' }, items)]);
14113
+ }
14114
+ var liveDrawn = 0;
14115
+ function drawLive(d, why) {
14116
+ S = d.strings;
14117
+ lang = d.language;
14118
+ document.documentElement.lang = lang;
14119
+ document.title = S.pageTitle;
14120
+ var drawn = ++liveDrawn;
14121
+ var nav = $('records');
14122
+ clear(nav);
14123
+ // Asked for with ?live=1: lead back to the records, when there are any.
14124
+ if (why === null) {
14125
+ getJson('/api/board').then(function (b) {
14126
+ var list = b && b.page && b.page.records ? b.page.records : [];
14127
+ if (drawn === liveDrawn && list.length > 0) nav.appendChild(el('a', { href: '/board', text: S.live.toRecords }));
14128
+ }).catch(function () {});
14129
+ }
14130
+ var root = $('board');
14131
+ clear(root);
14132
+ setStatus('', false);
14133
+ var l = d.live;
14134
+ [liveHeading(l, why), effort(l), liveRepos(l), liveTrail(l), liveComponents(l), liveFootnotes(l, why)]
14135
+ .forEach(function (node) { root.appendChild(node); });
14136
+ }
14137
+
14138
+ function getJson(url) {
14139
+ return fetch(url).then(function (res) {
13966
14140
  return res.json().then(function (body) {
13967
14141
  if (!res.ok) throw new Error(body && body.error ? body.error : String(res.status));
13968
14142
  return body;
13969
14143
  });
13970
- })
13971
- .then(draw)
13972
- .catch(function (err) { setStatus(S ? fill(S.loadFailed, { message: err.message }) : String(err.message), true); });
14144
+ });
14145
+ }
14146
+ function failed(err) { setStatus(S ? fill(S.loadFailed, { message: err.message }) : String(err.message), true); }
14147
+ // Measuring takes seconds, so the page says so until it is done.
14148
+ function loadLive(why) {
14149
+ setStatus(S ? S.live.measuring : '...', false);
14150
+ getJson('/api/board/live').then(function (d) { drawLive(d, why); }).catch(failed);
14151
+ }
14152
+
14153
+ var params = new URLSearchParams(location.search);
14154
+ var record = params.get('record');
14155
+ if (params.get('live') !== null) {
14156
+ loadLive(null);
14157
+ } else {
14158
+ setStatus('...', false);
14159
+ getJson('/api/board' + (record ? '/' + encodeURIComponent(record) : '')).then(draw).catch(failed);
14160
+ }
13973
14161
  })();
13974
14162
  </script>
13975
14163
  </body>
@@ -14790,7 +14978,8 @@ async function handleGet(res, pathname, deps) {
14790
14978
  ws,
14791
14979
  deps.nowProvider,
14792
14980
  remoteUrlOf(deps),
14793
- boardOf(deps)
14981
+ boardOf(deps),
14982
+ boardLiveOf(deps)
14794
14983
  )) {
14795
14984
  sendError(res, 404, "Not found");
14796
14985
  }
@@ -14804,7 +14993,8 @@ async function handleGet(res, pathname, deps) {
14804
14993
  primaryWorkspace(deps),
14805
14994
  deps.nowProvider,
14806
14995
  remoteUrlOf(deps),
14807
- boardOf(deps)
14996
+ boardOf(deps),
14997
+ boardLiveOf(deps)
14808
14998
  )) {
14809
14999
  sendError(res, 404, "Not found");
14810
15000
  }
@@ -14815,6 +15005,9 @@ async function handleGet(res, pathname, deps) {
14815
15005
  function boardOf(deps) {
14816
15006
  return deps.mode === "single" ? deps.board : void 0;
14817
15007
  }
15008
+ function boardLiveOf(deps) {
15009
+ return deps.mode === "single" ? deps.boardLive : void 0;
15010
+ }
14818
15011
  function remoteUrlOf(deps) {
14819
15012
  return deps.remoteUrlOf ?? tryRemoteUrl;
14820
15013
  }
@@ -14840,7 +15033,11 @@ async function handlePost(res, pathname, body, deps, runExclusive) {
14840
15033
  }
14841
15034
  sendError(res, 404, "Not found");
14842
15035
  }
14843
- async function handleWorkspaceGet(res, sub, ws, nowProvider, resolveRemoteUrl, board) {
15036
+ async function handleWorkspaceGet(res, sub, ws, nowProvider, resolveRemoteUrl, board, live) {
15037
+ if (live !== void 0 && sub === "board/live") {
15038
+ sendJson(res, 200, await boardLiveView(ws, live));
15039
+ return true;
15040
+ }
14844
15041
  if (board !== void 0) {
14845
15042
  if (sub === "board") {
14846
15043
  sendJson(res, 200, await boardView(ws, board, void 0));
@@ -14956,6 +15153,14 @@ async function boardView(ws, board, recordId) {
14956
15153
  const page = "why" in board ? boardPageUnavailable(board.why) : await boardPage(board.recordsDir, recordId);
14957
15154
  return { language, strings: boardPageStrings(language), page };
14958
15155
  }
15156
+ async function boardLiveView(ws, live) {
15157
+ const language = await resolveViewLanguageFromPaths(ws.paths);
15158
+ return {
15159
+ language,
15160
+ strings: boardPageStrings(language),
15161
+ live: boardLivePage(ws.label, await live())
15162
+ };
15163
+ }
14959
15164
  async function portfolio(deps) {
14960
15165
  const nowIso = deps.nowProvider().toISOString();
14961
15166
  const workspaces = await Promise.all(deps.workspaces.map((ws) => portfolioCard(ws, nowIso)));
@@ -15328,7 +15533,26 @@ async function buildSingleDeps(ctx, cwd) {
15328
15533
  mode: "single",
15329
15534
  nowProvider: nowProviderOf(ctx),
15330
15535
  ...ctx.remoteUrlOf !== void 0 ? { remoteUrlOf: ctx.remoteUrlOf } : {},
15331
- board: await viewBoardOf(repositoryRoot, paths)
15536
+ board: await viewBoardOf(repositoryRoot, paths),
15537
+ boardLive: liveBoardOf(repositoryRoot, ctx)
15538
+ };
15539
+ }
15540
+ function liveBoardOf(root, ctx) {
15541
+ return sharedWhileRunning(
15542
+ () => measureLiveBoard(root, {
15543
+ ...ctx.nowProvider !== void 0 ? { nowProvider: ctx.nowProvider } : {},
15544
+ ...ctx.claudeProjectsDir !== void 0 ? { claudeProjectsDir: ctx.claudeProjectsDir } : {},
15545
+ ...ctx.codexSessionsDir !== void 0 ? { codexSessionsDir: ctx.codexSessionsDir } : {}
15546
+ })
15547
+ );
15548
+ }
15549
+ function sharedWhileRunning(run) {
15550
+ let running = null;
15551
+ return () => {
15552
+ running ??= run().finally(() => {
15553
+ running = null;
15554
+ });
15555
+ return running;
15332
15556
  };
15333
15557
  }
15334
15558
  async function viewBoardOf(root, paths) {
@@ -15493,7 +15717,7 @@ function installExperimentalNotice(program, names = EXPERIMENTAL_COMMANDS, write
15493
15717
  function readBuildStamp() {
15494
15718
  if (false) return void 0;
15495
15719
  try {
15496
- return JSON.parse('{"version":"0.66.0","commit":"2cd6700","committedAt":"2026-10-08T13:10:32+09:00"}');
15720
+ return JSON.parse('{"version":"0.67.0","commit":"c4c4ebb","committedAt":"2026-10-08T23:33:17+09:00"}');
15497
15721
  } catch {
15498
15722
  return void 0;
15499
15723
  }