@nanopm/cli 0.1.19 → 0.1.21

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 CHANGED
@@ -801,16 +801,16 @@ function paragraphs(text2) {
801
801
  return (text2 ?? "").split(/\n\s*\n/).map((p) => p.trim().replace(/\s+/g, " ")).filter(Boolean);
802
802
  }
803
803
  function wholeParagraphs(text2, max, most = Infinity) {
804
- let out = "";
804
+ let out2 = "";
805
805
  for (const p of paragraphs(text2).slice(0, most)) {
806
- const room = max - out.length - (out ? 2 : 0);
806
+ const room = max - out2.length - (out2 ? 2 : 0);
807
807
  const kept = p.length <= room ? p : wholeSentences(p, room);
808
- if (/[.!?]$/.test(kept) || kept === p) out = out ? `${out}
808
+ if (/[.!?]$/.test(kept) || kept === p) out2 = out2 ? `${out2}
809
809
 
810
810
  ${kept}` : kept;
811
811
  if (kept !== p) break;
812
812
  }
813
- return out || null;
813
+ return out2 || null;
814
814
  }
815
815
  var CODE_PATTERNS = [
816
816
  [/\b\d+\s?px\b/, "a size in pixels"],
@@ -1279,13 +1279,13 @@ function doubtLine(m, f) {
1279
1279
  }
1280
1280
  function problemStory(m, f) {
1281
1281
  const story = [m.situation, m.progress, m.obstacle, m.rationale].map(sentence).filter(Boolean).join(" ");
1282
- const out = { story, doubt: doubtLine(m, f), test: testLine(m) };
1282
+ const out2 = { story, doubt: doubtLine(m, f), test: testLine(m) };
1283
1283
  if (m.knowledge === "hypothesis" && m.assumptions.length) {
1284
1284
  const parts = m.assumptions.map((a) => lower(a).replace(/[.]$/, ""));
1285
1285
  const joined = parts.length === 1 ? parts[0] : `${parts.slice(0, -1).join("; ")}, and ${parts[parts.length - 1]}`;
1286
- out.assumes = follow("This assumes", joined);
1286
+ out2.assumes = follow("This assumes", joined);
1287
1287
  }
1288
- return out;
1288
+ return out2;
1289
1289
  }
1290
1290
 
1291
1291
  // ../shared/src/signals.ts
@@ -1602,15 +1602,15 @@ function stripTree(n) {
1602
1602
  return row;
1603
1603
  }
1604
1604
  function flattenNeedTree(roots) {
1605
- const out = [];
1605
+ const out2 = [];
1606
1606
  const walk = (list) => {
1607
1607
  for (const n of list) {
1608
- out.push(n);
1608
+ out2.push(n);
1609
1609
  walk(n.children);
1610
1610
  }
1611
1611
  };
1612
1612
  walk(roots);
1613
- return out;
1613
+ return out2;
1614
1614
  }
1615
1615
  function needCounts(roots) {
1616
1616
  const live = flattenNeedTree(roots).filter((n) => n.status !== "dropped");
@@ -1629,10 +1629,10 @@ function personaName(title, slug) {
1629
1629
  function threadSentence(who, path) {
1630
1630
  const [need, difficulty, precise] = path.map((p) => lowerFirst(p.label.trim().replace(/[.!?;:,]+$/, "")));
1631
1631
  if (!need) return "";
1632
- let out = `${who.trim()} wants to ${need}`;
1633
- if (difficulty) out += `, but ${difficulty}`;
1634
- if (precise) out += `: ${precise}`;
1635
- return `${out}.`;
1632
+ let out2 = `${who.trim()} wants to ${need}`;
1633
+ if (difficulty) out2 += `, but ${difficulty}`;
1634
+ if (precise) out2 += `: ${precise}`;
1635
+ return `${out2}.`;
1636
1636
  }
1637
1637
  function lowerFirst(text2) {
1638
1638
  if (!text2) return text2;
@@ -1643,14 +1643,14 @@ function lowerFirst(text2) {
1643
1643
  function needsRunConflict(active, wanted, ctx) {
1644
1644
  const whose = (r) => r.persona ?? ctx.primary;
1645
1645
  const persona = whose(wanted);
1646
- for (const run of active) {
1647
- if (whose(run) !== persona) continue;
1646
+ for (const run2 of active) {
1647
+ if (whose(run2) !== persona) continue;
1648
1648
  const name = persona ? ctx.name(persona) : "this persona";
1649
- if (run.scope === "map") return `${name}'s whole map is already being drawn; this waits for it.`;
1650
- if (wanted.scope === "map") return `NanoPM is digging into ${run.scope} on ${name}'s map; map it again once that is done.`;
1649
+ if (run2.scope === "map") return `${name}'s whole map is already being drawn; this waits for it.`;
1650
+ if (wanted.scope === "map") return `NanoPM is digging into ${run2.scope} on ${name}'s map; map it again once that is done.`;
1651
1651
  const up = (ref) => ctx.lineage(ref).map((r) => r.toUpperCase());
1652
- if (up(wanted.scope).includes(run.scope.toUpperCase()) || up(run.scope).includes(wanted.scope.toUpperCase())) {
1653
- return `NanoPM is already digging into ${run.scope}, which covers this; it waits for that run.`;
1652
+ if (up(wanted.scope).includes(run2.scope.toUpperCase()) || up(run2.scope).includes(wanted.scope.toUpperCase())) {
1653
+ return `NanoPM is already digging into ${run2.scope}, which covers this; it waits for that run.`;
1654
1654
  }
1655
1655
  }
1656
1656
  return null;
@@ -2034,9 +2034,9 @@ function placeRanks(crew, pinned) {
2034
2034
  if (slots[i] !== null) continue;
2035
2035
  slots[i] = loose[next++] ?? null;
2036
2036
  }
2037
- const out = /* @__PURE__ */ new Map();
2038
- slots.forEach((id, i) => id && out.set(id, i + 1));
2039
- return out;
2037
+ const out2 = /* @__PURE__ */ new Map();
2038
+ slots.forEach((id, i) => id && out2.set(id, i + 1));
2039
+ return out2;
2040
2040
  }
2041
2041
  function settlePriorities(inOrder) {
2042
2042
  const p = inOrder.map((r) => r.priority);
@@ -2084,14 +2084,14 @@ function changelogText(c) {
2084
2084
  return [line(c.headline), ...c.points.map(line)].filter(Boolean).join("\n");
2085
2085
  }
2086
2086
  function pointsWithin(points, room) {
2087
- const out = [];
2087
+ const out2 = [];
2088
2088
  let used = 0;
2089
2089
  for (const p of points.map((x) => x.trim().replace(/\s+/g, " ")).filter(Boolean)) {
2090
2090
  if (used + p.length + 1 > room) break;
2091
- out.push(p);
2091
+ out2.push(p);
2092
2092
  used += p.length + 1;
2093
2093
  }
2094
- return out;
2094
+ return out2;
2095
2095
  }
2096
2096
  var descriptionText = (text2) => wholeParagraphs(text2, DESCRIPTION_MAX2, DESCRIPTION_PARAGRAPHS2);
2097
2097
  function parseChangelog(text2) {
@@ -2151,10 +2151,10 @@ var HORIZONS = ["now", "next", "later"];
2151
2151
  var HORIZON_WORD = { now: "Now", next: "Next", later: "Later" };
2152
2152
  var BOARD_FILL = { now: 3, next: 4 };
2153
2153
  function boardOrder(branches) {
2154
- const out = [];
2154
+ const out2 = [];
2155
2155
  const longest = Math.max(0, ...branches.map((b) => b.length));
2156
- for (let i = 0; i < longest; i += 1) for (const b of branches) if (i < b.length) out.push(b[i]);
2157
- return out;
2156
+ for (let i = 0; i < longest; i += 1) for (const b of branches) if (i < b.length) out2.push(b[i]);
2157
+ return out2;
2158
2158
  }
2159
2159
  function placeNewCards(existing, fresh) {
2160
2160
  const count2 = { now: 0, next: 0, later: 0 };
@@ -2163,14 +2163,14 @@ function placeNewCards(existing, fresh) {
2163
2163
  count2[c.horizon] += 1;
2164
2164
  last[c.horizon] = Math.max(last[c.horizon], c.place);
2165
2165
  }
2166
- const out = /* @__PURE__ */ new Map();
2166
+ const out2 = /* @__PURE__ */ new Map();
2167
2167
  for (const id of fresh) {
2168
2168
  const horizon = count2.now < BOARD_FILL.now ? "now" : count2.next < BOARD_FILL.next ? "next" : "later";
2169
2169
  count2[horizon] += 1;
2170
2170
  last[horizon] += 1;
2171
- out.set(id, { horizon, place: last[horizon] });
2171
+ out2.set(id, { horizon, place: last[horizon] });
2172
2172
  }
2173
- return out;
2173
+ return out2;
2174
2174
  }
2175
2175
  var SOLUTION_COLUMNS = "id, ref, opportunity_id, problem_id, title, description, changelog, steps, size, assumption, reasoning, sources, rank, status, horizon, place, placed_by, superseded_by, job_id, created_at, specced_at";
2176
2176
  function boardOf(all, solutions) {
@@ -2214,12 +2214,12 @@ function solutionsRunOf(params) {
2214
2214
  return { depth: p.depth === "deep" ? "deep" : "quick", leaves };
2215
2215
  }
2216
2216
  function leavesAtWork(params) {
2217
- const out = /* @__PURE__ */ new Map();
2217
+ const out2 = /* @__PURE__ */ new Map();
2218
2218
  for (const p of params) {
2219
- const run = solutionsRunOf(p);
2220
- for (const leaf of run?.leaves ?? []) if (out.get(leaf) !== "deep") out.set(leaf, run.depth);
2219
+ const run2 = solutionsRunOf(p);
2220
+ for (const leaf of run2?.leaves ?? []) if (out2.get(leaf) !== "deep") out2.set(leaf, run2.depth);
2221
2221
  }
2222
- return out;
2222
+ return out2;
2223
2223
  }
2224
2224
  function brainstormPlan(map, ref, deep, busy = /* @__PURE__ */ new Set()) {
2225
2225
  const wanted = ref.trim().toUpperCase();
@@ -2929,7 +2929,7 @@ function whoFirst(sentence2) {
2929
2929
  }
2930
2930
  var safePath = (file) => !file.startsWith("/") && !file.split(/[\\/]/).includes("..");
2931
2931
  function checkProposals(answer, fileExists) {
2932
- const out = {};
2932
+ const out2 = {};
2933
2933
  for (const lever of OUTCOME_LEVERS) {
2934
2934
  const kept = [];
2935
2935
  const seen = /* @__PURE__ */ new Set();
@@ -2945,9 +2945,9 @@ function checkProposals(answer, fileExists) {
2945
2945
  const found = p.found && safePath(p.found.file) && fileExists(p.found.file) ? { file: p.found.file, line: p.found.line, note: p.found.note } : null;
2946
2946
  kept.push({ sentence: sentence2, found });
2947
2947
  }
2948
- if (kept.length) out[lever] = kept;
2948
+ if (kept.length) out2[lever] = kept;
2949
2949
  }
2950
- return out;
2950
+ return out2;
2951
2951
  }
2952
2952
 
2953
2953
  // ../shared/src/reword.ts
@@ -3696,6 +3696,8 @@ function sandboxSettingsFor(home, grantedTools, network = [], answerFile) {
3696
3696
  enabled: true,
3697
3697
  allowUnsandboxedCommands: false,
3698
3698
  failIfUnavailable: true,
3699
+ // No local ports (spec 77 §6): Claude Code's allowLocalBinding binds every interface and opens
3700
+ // every localhost port to the shell — the founder's databases, proxies, debug ports.
3699
3701
  network: { allowedDomains: network.filter((h) => !/(^|\.)github(usercontent)?\.com$/.test(h)) }
3700
3702
  };
3701
3703
  }
@@ -4218,11 +4220,11 @@ import postgres from "postgres";
4218
4220
 
4219
4221
  // ../connectors/src/sql-text.ts
4220
4222
  function stripLiterals(sql) {
4221
- const out = [];
4223
+ const out2 = [];
4222
4224
  let i = 0;
4223
4225
  const n = sql.length;
4224
4226
  const blank = (from, to) => {
4225
- for (let k = from; k < to; k += 1) out.push(sql[k] === "\n" ? "\n" : " ");
4227
+ for (let k = from; k < to; k += 1) out2.push(sql[k] === "\n" ? "\n" : " ");
4226
4228
  };
4227
4229
  while (i < n) {
4228
4230
  const c = sql[i];
@@ -4284,10 +4286,10 @@ function stripLiterals(sql) {
4284
4286
  i = k;
4285
4287
  continue;
4286
4288
  }
4287
- out.push(c);
4289
+ out2.push(c);
4288
4290
  i += 1;
4289
4291
  }
4290
- return out.join("");
4292
+ return out2.join("");
4291
4293
  }
4292
4294
  function refuseUnlessSingleStatement(sql) {
4293
4295
  const trimmed = sql.trim();
@@ -4749,7 +4751,7 @@ async function readCapped(response, maxBytes) {
4749
4751
  const reader = response.body?.getReader();
4750
4752
  if (!reader) return { body: await response.text(), truncated: false };
4751
4753
  const decoder = new TextDecoder("utf-8", { fatal: false });
4752
- let out = "";
4754
+ let out2 = "";
4753
4755
  let bytes = 0;
4754
4756
  let truncated = false;
4755
4757
  for (; ; ) {
@@ -4757,16 +4759,16 @@ async function readCapped(response, maxBytes) {
4757
4759
  if (done) break;
4758
4760
  bytes += value.byteLength;
4759
4761
  if (bytes > maxBytes) {
4760
- out += decoder.decode(value.subarray(0, Math.max(0, value.byteLength - (bytes - maxBytes))), { stream: true });
4762
+ out2 += decoder.decode(value.subarray(0, Math.max(0, value.byteLength - (bytes - maxBytes))), { stream: true });
4761
4763
  truncated = true;
4762
4764
  await reader.cancel().catch(() => {
4763
4765
  });
4764
4766
  break;
4765
4767
  }
4766
- out += decoder.decode(value, { stream: true });
4768
+ out2 += decoder.decode(value, { stream: true });
4767
4769
  }
4768
- out += decoder.decode();
4769
- return { body: out, truncated };
4770
+ out2 += decoder.decode();
4771
+ return { body: out2, truncated };
4770
4772
  }
4771
4773
  async function defaultLookup(host) {
4772
4774
  const records = await dnsLookup(host, { all: true });
@@ -4899,11 +4901,11 @@ async function queryPosthog(key, host, projectId, hogql, opts = {}) {
4899
4901
  const results = data.results ?? [];
4900
4902
  const kept = results.slice(0, maxRows);
4901
4903
  const rows2 = kept.map((row) => {
4902
- const out = {};
4904
+ const out2 = {};
4903
4905
  columns2.forEach((column, i) => {
4904
- out[column] = Array.isArray(row) ? row[i] : void 0;
4906
+ out2[column] = Array.isArray(row) ? row[i] : void 0;
4905
4907
  });
4906
- return out;
4908
+ return out2;
4907
4909
  });
4908
4910
  return {
4909
4911
  rows: rows2,
@@ -5175,16 +5177,16 @@ function playSearchUrl(term, country) {
5175
5177
  return url.toString();
5176
5178
  }
5177
5179
  function blobs(html) {
5178
- const out = [];
5180
+ const out2 = [];
5179
5181
  for (const match of html.matchAll(/AF_initDataCallback\((\{[\s\S]*?\})\);<\/script>/g)) {
5180
5182
  const data = /data:\s*(\[[\s\S]*\])\s*,\s*sideChannel/.exec(match[1]);
5181
5183
  if (!data) continue;
5182
5184
  try {
5183
- out.push(JSON.parse(data[1]));
5185
+ out2.push(JSON.parse(data[1]));
5184
5186
  } catch {
5185
5187
  }
5186
5188
  }
5187
- return out;
5189
+ return out2;
5188
5190
  }
5189
5191
  function cards(node, found = []) {
5190
5192
  if (!Array.isArray(node)) return found;
@@ -5675,8 +5677,8 @@ async function pullRequestState(cwd, number) {
5675
5677
  }
5676
5678
  async function openPullRequest(cwd, pr) {
5677
5679
  try {
5678
- const out = await gh(cwd, ["pr", "create", "--draft", "--head", pr.branch, "--base", pr.base, "--title", pr.title, "--body", pr.body]);
5679
- const url = reviewUrlFrom(out) ?? out.split("\n").at(-1) ?? "";
5680
+ const out2 = await gh(cwd, ["pr", "create", "--draft", "--head", pr.branch, "--base", pr.base, "--title", pr.title, "--body", pr.body]);
5681
+ const url = reviewUrlFrom(out2) ?? out2.split("\n").at(-1) ?? "";
5680
5682
  return { ok: true, url, number: Number(/\/pull\/(\d+)/.exec(url)?.[1] ?? 0) };
5681
5683
  } catch (err) {
5682
5684
  return { ok: false, reason: errorMessage(err).split("\n").filter(Boolean).slice(-2).join(" ") || "gh pr create failed" };
@@ -5693,8 +5695,8 @@ async function git2(cwd, args) {
5693
5695
  }
5694
5696
  async function commitsSince(cwd, since) {
5695
5697
  const branch = await defaultBranch(cwd);
5696
- const out = await git2(cwd, ["log", branch, `--since=${since.toISOString()}`, "--format=%cI"]);
5697
- const stamps = out.split("\n").filter(Boolean);
5698
+ const out2 = await git2(cwd, ["log", branch, `--since=${since.toISOString()}`, "--format=%cI"]);
5699
+ const stamps = out2.split("\n").filter(Boolean);
5698
5700
  return { count: stamps.length, newestAt: stamps[0] ? new Date(stamps[0]) : null };
5699
5701
  }
5700
5702
  async function reviewDeploys(client, daemonId, now = /* @__PURE__ */ new Date()) {
@@ -5824,16 +5826,16 @@ var NEEDS_THE_REQUESTERS_CLONE = ["write_repo", "run_commands"];
5824
5826
  var NEEDS_THE_REQUESTERS_IDENTITY = /* @__PURE__ */ new Set(["want", "start"]);
5825
5827
  async function clonelessSkills(skillsDir = findSkillsDir()) {
5826
5828
  const names = readdirSync(skillsDir, { withFileTypes: true }).filter((d) => d.isDirectory() && existsSync2(join7(skillsDir, d.name, "skill.json"))).map((d) => d.name);
5827
- const out = [];
5829
+ const out2 = [];
5828
5830
  for (const name of names) {
5829
5831
  try {
5830
5832
  const { manifest } = await loadSkill(name, skillsDir);
5831
5833
  if (NEEDS_THE_REQUESTERS_IDENTITY.has(name)) continue;
5832
- if (!manifest.capabilities.some((c) => NEEDS_THE_REQUESTERS_CLONE.includes(c))) out.push(name);
5834
+ if (!manifest.capabilities.some((c) => NEEDS_THE_REQUESTERS_CLONE.includes(c))) out2.push(name);
5833
5835
  } catch {
5834
5836
  }
5835
5837
  }
5836
- return out.sort();
5838
+ return out2.sort();
5837
5839
  }
5838
5840
 
5839
5841
  // ../daemon/src/problem-mapping-harness.ts
@@ -6356,15 +6358,15 @@ async function readDocs(cwd, opts = {}) {
6356
6358
  };
6357
6359
  await walk(cwd, 0);
6358
6360
  found.sort((a, b) => Number(WRITTEN.test(b)) - Number(WRITTEN.test(a)) || a.length - b.length);
6359
- const out = [];
6361
+ const out2 = [];
6360
6362
  for (const path of found.slice(0, DOCS_MAX)) {
6361
6363
  try {
6362
6364
  if ((await stat(path)).size > 4e5) continue;
6363
- out.push({ path: relative(cwd, path), text: (await readFile5(path, "utf8")).slice(0, DOC_CHARS) });
6365
+ out2.push({ path: relative(cwd, path), text: (await readFile5(path, "utf8")).slice(0, DOC_CHARS) });
6364
6366
  } catch {
6365
6367
  }
6366
6368
  }
6367
- return out;
6369
+ return out2;
6368
6370
  }
6369
6371
 
6370
6372
  // ../daemon/src/persona-job.ts
@@ -7962,18 +7964,18 @@ function droppedAt(rows2, parent, byId, current, target) {
7962
7964
  });
7963
7965
  }
7964
7966
  function sourcesOf(ids, heard, read6, today) {
7965
- const out = [];
7967
+ const out2 = [];
7966
7968
  for (const id of ids) {
7967
7969
  const h = heard.find((q) => q.id === id);
7968
7970
  if (h) {
7969
- out.push({ kind: h.kind, ref: h.ref, quote: clip2(h.quote, NEED_QUOTE_MAX), read_at: today });
7971
+ out2.push({ kind: h.kind, ref: h.ref, quote: clip2(h.quote, NEED_QUOTE_MAX), read_at: today });
7970
7972
  continue;
7971
7973
  }
7972
7974
  const w = read6.find((f) => f.id === id && f.read);
7973
- if (w) out.push({ kind: "web", ref: w.page, quote: clip2(w.quote, NEED_QUOTE_MAX), read_at: w.published ?? today });
7975
+ if (w) out2.push({ kind: "web", ref: w.page, quote: clip2(w.quote, NEED_QUOTE_MAX), read_at: w.published ?? today });
7974
7976
  }
7975
7977
  const seen = /* @__PURE__ */ new Set();
7976
- return out.filter((s) => seen.has(`${s.ref}|${s.quote}`) ? false : (seen.add(`${s.ref}|${s.quote}`), true)).slice(0, NEED_SOURCES_MAX);
7978
+ return out2.filter((s) => seen.has(`${s.ref}|${s.quote}`) ? false : (seen.add(`${s.ref}|${s.quote}`), true)).slice(0, NEED_SOURCES_MAX);
7977
7979
  }
7978
7980
  function checkHeard(quotes, material) {
7979
7981
  const kept = [];
@@ -8033,7 +8035,7 @@ function descendants(rows2, root2) {
8033
8035
  for (let hops = 0; n && n.status === "replaced" && n.replaced_by && hops < 50; hops += 1) n = byId.get(n.replaced_by);
8034
8036
  return n && n.status !== "replaced" ? n : null;
8035
8037
  };
8036
- const out = [root2];
8038
+ const out2 = [root2];
8037
8039
  const ids = /* @__PURE__ */ new Set([root2.id]);
8038
8040
  for (let grew = true; grew; ) {
8039
8041
  grew = false;
@@ -8042,12 +8044,12 @@ function descendants(rows2, root2) {
8042
8044
  const p = current(r.parent_id);
8043
8045
  if (p && ids.has(p.id)) {
8044
8046
  ids.add(r.id);
8045
- out.push(r);
8047
+ out2.push(r);
8046
8048
  grew = true;
8047
8049
  }
8048
8050
  }
8049
8051
  }
8050
- return out;
8052
+ return out2;
8051
8053
  }
8052
8054
  function pathOf2(rows2, node) {
8053
8055
  const found = needTree(rows2).flatMap(function walk(n) {
@@ -8347,18 +8349,18 @@ function snapshot(input) {
8347
8349
  for (const n of input.nodes) byPersona.set(n.persona, [...byPersona.get(n.persona) ?? [], n]);
8348
8350
  let lines = 0;
8349
8351
  for (const [persona, rows2] of byPersona) {
8350
- const out = [];
8352
+ const out2 = [];
8351
8353
  const walk = (list, under) => {
8352
8354
  for (const n of list) {
8353
8355
  if (n.status === "dropped" || n.outside_mission || lines >= NEEDS_LINES_MAX) continue;
8354
8356
  lines += 1;
8355
8357
  cites.set(n.ref.toUpperCase(), { kind: "needs", ref: n.ref, note: clip3(n.label, 140) });
8356
- out.push({ ref: n.ref, level: n.level, line: n.label, statement: n.statement, under });
8358
+ out2.push({ ref: n.ref, level: n.level, line: n.label, statement: n.statement, under });
8357
8359
  walk(n.children, n.ref);
8358
8360
  }
8359
8361
  };
8360
8362
  walk(needTree(rows2), null);
8361
- if (out.length) needsMaps.push({ persona, lines: out });
8363
+ if (out2.length) needsMaps.push({ persona, lines: out2 });
8362
8364
  }
8363
8365
  if (ask7.note) cites.set("NOTE", { kind: "founder", ref: "note", note: clip3(ask7.note, 140) });
8364
8366
  const tree = flattenTree(liveTree(input.mine));
@@ -8496,22 +8498,22 @@ async function deepRun(ctx, mine) {
8496
8498
  const full = { ...m, explorers };
8497
8499
  const synthesize = async () => {
8498
8500
  const cost = await role6({ ...ctx, role: "synthesizer", tools: "none", say: "Making one map of what was found\u2026", prompt: JSON.stringify(full) });
8499
- const out = SynthesizedMap.parse(JSON.parse(cost.text));
8501
+ const out2 = SynthesizedMap.parse(JSON.parse(cost.text));
8500
8502
  await logStep(
8501
8503
  ctx,
8502
8504
  "opportunities.synthesized",
8503
8505
  "making one map",
8504
- `The synthesizer made one map: ${mapLine(out.opportunities)}; kept ${out.keep.length} as they were, removed ${out.removed.length}${out.reconsider.length ? `, ${out.reconsider.length} of yours worth reconsidering` : ""}.`,
8506
+ `The synthesizer made one map: ${mapLine(out2.opportunities)}; kept ${out2.keep.length} as they were, removed ${out2.removed.length}${out2.reconsider.length ? `, ${out2.reconsider.length} of yours worth reconsidering` : ""}.`,
8505
8507
  cost
8506
8508
  );
8507
- return out;
8509
+ return out2;
8508
8510
  };
8509
8511
  const prioritize = async (synth2, again) => {
8510
8512
  const cost = await role6({ ...ctx, role: "prioritizer", tools: "none", say: "Ordering the map\u2026", prompt: JSON.stringify({ objective: m.objective, business: m.business, personas: m.personas, note: m.note, map: mapForPrioritizer(synth2, mine) }) });
8511
- const out = PrioritizedMap.parse(JSON.parse(cost.text));
8512
- const n = (p) => out.rows.filter((r) => r.priority === p).length;
8513
- await logStep(ctx, "opportunities.prioritized", again ? "the revision (the order)" : "ordering", `The prioritizer ordered ${out.rows.length}: ${n("high")} high, ${n("medium")} medium, ${n("low")} low.`, cost);
8514
- return out;
8513
+ const out2 = PrioritizedMap.parse(JSON.parse(cost.text));
8514
+ const n = (p) => out2.rows.filter((r) => r.priority === p).length;
8515
+ await logStep(ctx, "opportunities.prioritized", again ? "the revision (the order)" : "ordering", `The prioritizer ordered ${out2.rows.length}: ${n("high")} high, ${n("medium")} medium, ${n("low")} low.`, cost);
8516
+ return out2;
8515
8517
  };
8516
8518
  let synth = await synthesize();
8517
8519
  const reading = beside(readEvidence(ctx, synth.opportunities));
@@ -8630,8 +8632,8 @@ function revisionLine(m) {
8630
8632
  }
8631
8633
  async function readEvidence(ctx, rows2) {
8632
8634
  const said = ctx.known.material.users_said.filter((s) => ctx.known.cites.get(s.id.toUpperCase())?.kind === "user");
8633
- const out = /* @__PURE__ */ new Map();
8634
- if (!said.length || !rows2.length) return out;
8635
+ const out2 = /* @__PURE__ */ new Map();
8636
+ if (!said.length || !rows2.length) return out2;
8635
8637
  const cost = await role6({
8636
8638
  ...ctx,
8637
8639
  role: "evidence",
@@ -8649,19 +8651,19 @@ async function readEvidence(ctx, rows2) {
8649
8651
  dropped += 1;
8650
8652
  continue;
8651
8653
  }
8652
- const list = out.get(m.key) ?? [];
8654
+ const list = out2.get(m.key) ?? [];
8653
8655
  if (!list.some((x) => x.id === text2.id && x.quote === m.quote)) list.push({ id: text2.id, quote: m.quote.trim() });
8654
- out.set(m.key, list);
8656
+ out2.set(m.key, list);
8655
8657
  }
8656
- const quotes = [...out.values()].reduce((n, l) => n + l.length, 0);
8658
+ const quotes = [...out2.values()].reduce((n, l) => n + l.length, 0);
8657
8659
  await logStep(
8658
8660
  ctx,
8659
8661
  "opportunities.evidenced",
8660
8662
  "reading the evidence (alongside)",
8661
- `The evidence reader matched what users said to ${out.size} problem${out.size === 1 ? "" : "s"}, ${quotes} quote${quotes === 1 ? "" : "s"} checked word for word${dropped ? `; ${dropped} not found in the words, left out` : ""}.${read6.notes ? ` ${wholeSentences(read6.notes, 400)}` : ""}`,
8663
+ `The evidence reader matched what users said to ${out2.size} problem${out2.size === 1 ? "" : "s"}, ${quotes} quote${quotes === 1 ? "" : "s"} checked word for word${dropped ? `; ${dropped} not found in the words, left out` : ""}.${read6.notes ? ` ${wholeSentences(read6.notes, 400)}` : ""}`,
8662
8664
  cost
8663
8665
  );
8664
- return out;
8666
+ return out2;
8665
8667
  }
8666
8668
  function withEvidence(rows2, evidence2) {
8667
8669
  return rows2.map((r) => {
@@ -8866,10 +8868,10 @@ function existingTree(rows2) {
8866
8868
  const ancestors = (r) => {
8867
8869
  const known = memo.get(r.id);
8868
8870
  if (known) return known;
8869
- const out = [];
8870
- for (let p = parentOf2(r); p && !out.includes(p) && out.length < 50; p = parentOf2(p)) out.push(p);
8871
- memo.set(r.id, out);
8872
- return out;
8871
+ const out2 = [];
8872
+ for (let p = parentOf2(r); p && !out2.includes(p) && out2.length < 50; p = parentOf2(p)) out2.push(p);
8873
+ memo.set(r.id, out2);
8874
+ return out2;
8873
8875
  };
8874
8876
  return { parentOf: parentOf2, ancestors };
8875
8877
  }
@@ -8954,9 +8956,9 @@ function structure(pool, gone, drawn, tree, liveByRef, asideByRef) {
8954
8956
  if (!at || seen.has(k)) return -1;
8955
8957
  seen.add(k);
8956
8958
  const d = "key" in at ? measure(at.key, seen) : 0;
8957
- const out = d < 0 ? -1 : d + 1;
8958
- depthNew.set(k, out);
8959
- return out;
8959
+ const out2 = d < 0 ? -1 : d + 1;
8960
+ depthNew.set(k, out2);
8961
+ return out2;
8960
8962
  };
8961
8963
  for (const r of pool) {
8962
8964
  if (late.some((l) => l.key === r.key)) continue;
@@ -8966,9 +8968,9 @@ function structure(pool, gone, drawn, tree, liveByRef, asideByRef) {
8966
8968
  }
8967
8969
  function retireable(rows2) {
8968
8970
  const tree = existingTree(rows2);
8969
- const out = new Set(rows2.filter((r) => isLive(r) && r.author_kind === "nanopm" && !isFounders(r)).map((r) => r.id));
8970
- for (const f of rows2.filter((r) => isLive(r) && isFounders(r))) for (const a of tree.ancestors(f)) out.delete(a.id);
8971
- return [...out];
8971
+ const out2 = new Set(rows2.filter((r) => isLive(r) && r.author_kind === "nanopm" && !isFounders(r)).map((r) => r.id));
8972
+ for (const f of rows2.filter((r) => isLive(r) && isFounders(r))) for (const a of tree.ancestors(f)) out2.delete(a.id);
8973
+ return [...out2];
8972
8974
  }
8973
8975
  function critiqueWording(o, v, reworked) {
8974
8976
  if (v?.verdict !== "pass" || reworked.has(o.key)) return o;
@@ -9416,7 +9418,7 @@ async function flashBranch(ctx, w, n, places) {
9416
9418
  const name = clip3(themeName(w.branch), 60);
9417
9419
  const material = flashMaterial(ctx.known, w);
9418
9420
  const call2 = { role: "flash", parse: FlashAnswer, dir: `flash-${n + 1}` };
9419
- const out = [];
9421
+ const out2 = [];
9420
9422
  try {
9421
9423
  const first = await ask2(ctx, { ...call2, prompt: material, say: `Finding solutions to \u201C${name}\u201D\u2026` });
9422
9424
  const gated = w.leaves.map((leaf) => {
@@ -9429,7 +9431,7 @@ async function flashBranch(ctx, w, n, places) {
9429
9431
  `\u201C${name}\u201D: ${gated.reduce((t, g) => t + g.written.length, 0)} solutions for ${w.leaves.length} problem${w.leaves.length === 1 ? "" : "s"}${again.length ? `; ${again.length} to write again` : ""}.${first.answer.notes ? ` ${wholeSentences(first.answer.notes, 400)}` : ""}`,
9430
9432
  first.cost
9431
9433
  );
9432
- for (const g of gated.filter((x) => !x.refused.length)) out.push(await publish4(ctx, g.leaf, g.kept, places.get(g.leaf.id)));
9434
+ for (const g of gated.filter((x) => !x.refused.length)) out2.push(await publish4(ctx, g.leaf, g.kept, places.get(g.leaf.id)));
9433
9435
  if (again.length) {
9434
9436
  const refused = again.map((g) => ({ ref: g.leaf.ref, why: g.refused, you_wrote: g.written }));
9435
9437
  const second = await ask2(ctx, { ...call2, prompt: { ...material, answer: again.map((g) => g.leaf.ref), refused }, say: `Writing again what would be refused on \u201C${name}\u201D\u2026` });
@@ -9437,11 +9439,11 @@ async function flashBranch(ctx, w, n, places) {
9437
9439
  for (const g of again) {
9438
9440
  const re = gate(ctx, answersFor(second.answer.leaves, g.leaf), g.leaf);
9439
9441
  if (!re.kept.length) {
9440
- out.push({ leaf: g.leaf, failed: re.refused.join("; ") });
9442
+ out2.push({ leaf: g.leaf, failed: re.refused.join("; ") });
9441
9443
  continue;
9442
9444
  }
9443
9445
  for (const why of re.refused) await ctx.journal("solutions.refused", `${g.leaf.ref}: ${why}; not recorded.`);
9444
- out.push(await publish4(ctx, g.leaf, re.kept, places.get(g.leaf.id)));
9446
+ out2.push(await publish4(ctx, g.leaf, re.kept, places.get(g.leaf.id)));
9445
9447
  }
9446
9448
  }
9447
9449
  } catch (error) {
@@ -9450,13 +9452,13 @@ async function flashBranch(ctx, w, n, places) {
9450
9452
  throw error;
9451
9453
  }
9452
9454
  const why = clip3(error instanceof Error ? error.message : String(error), 300);
9453
- for (const leaf of w.leaves) if (!out.some((o) => o.leaf === leaf)) out.push({ leaf, failed: why });
9455
+ for (const leaf of w.leaves) if (!out2.some((o) => o.leaf === leaf)) out2.push({ leaf, failed: why });
9454
9456
  }
9455
- const failed2 = out.filter((o) => "failed" in o);
9457
+ const failed2 = out2.filter((o) => "failed" in o);
9456
9458
  if (failed2.length) {
9457
9459
  await ctx.journal("solutions.failed", `\u201C${name}\u201D could not finish: ${failed2.map((f) => `${f.leaf.ref} \u2014 ${f.failed}`).join("; ")}. The other branches were not held.`);
9458
9460
  }
9459
- return out;
9461
+ return out2;
9460
9462
  }
9461
9463
  function answersFor(answered2, leaf) {
9462
9464
  return answered2.find((a) => a.ref.trim().toUpperCase() === leaf.ref.toUpperCase())?.solutions ?? [];
@@ -9571,10 +9573,10 @@ function uniqueKeys(list) {
9571
9573
  }
9572
9574
  function revised2(first, r, cut) {
9573
9575
  const rework = new Map(r.rework.filter((x) => !cut.has(x.key)).map((x) => [x.key, x]));
9574
- const out = first.map((s) => rework.get(s.key) ?? s);
9575
- const fresh = uniqueKeys([...out, ...r.add, ...r.rework.filter((x) => !first.some((s) => s.key === x.key))]).slice(out.length);
9576
+ const out2 = first.map((s) => rework.get(s.key) ?? s);
9577
+ const fresh = uniqueKeys([...out2, ...r.add, ...r.rework.filter((x) => !first.some((s) => s.key === x.key))]).slice(out2.length);
9576
9578
  const dropped = new Set(r.drop.map((d) => d.key));
9577
- const kept = [...out, ...fresh].filter((s) => !dropped.has(s.key));
9579
+ const kept = [...out2, ...fresh].filter((s) => !dropped.has(s.key));
9578
9580
  const at = (key) => r.order.includes(key) ? r.order.indexOf(key) : Infinity;
9579
9581
  return r.order.length ? [...kept].sort((a, b) => at(a.key) - at(b.key)) : kept;
9580
9582
  }
@@ -9631,14 +9633,14 @@ function draftFields(w) {
9631
9633
  };
9632
9634
  }
9633
9635
  function resolve3(ctx, ids) {
9634
- const out = [];
9636
+ const out2 = [];
9635
9637
  for (const raw of ids) {
9636
9638
  const id = raw.trim().toUpperCase();
9637
9639
  const source = ctx.known.cites.get(id);
9638
9640
  if (!source) ctx.known.unresolved.add(raw.trim());
9639
- else if (!out.some((s) => s.kind === source.kind && s.ref === source.ref)) out.push(source);
9641
+ else if (!out2.some((s) => s.kind === source.kind && s.ref === source.ref)) out2.push(source);
9640
9642
  }
9641
- return out.length ? out.slice(0, OPPORTUNITY_SOURCES_MAX) : [{ kind: "inference", ref: "nanopm", note: null }];
9643
+ return out2.length ? out2.slice(0, OPPORTUNITY_SOURCES_MAX) : [{ kind: "inference", ref: "nanopm", note: null }];
9642
9644
  }
9643
9645
  async function publish4(ctx, leaf, drafts, place) {
9644
9646
  ctx.spec.signal.throwIfAborted();
@@ -9829,7 +9831,7 @@ async function git3(cwd, args) {
9829
9831
  return stdout.trim();
9830
9832
  }
9831
9833
  async function listTrackedFiles(cwd) {
9832
- const lines = (args) => git3(cwd, args).then((out) => out ? out.split("\n") : [], () => []);
9834
+ const lines = (args) => git3(cwd, args).then((out2) => out2 ? out2.split("\n") : [], () => []);
9833
9835
  const [files, wiki] = await Promise.all([lines(["ls-files"]), lines(["ls-files", "--others", "--ignored", "--exclude-standard", "--", WIKI_DIR])]);
9834
9836
  return { files, wiki };
9835
9837
  }
@@ -10261,16 +10263,16 @@ async function countingLines(cwd) {
10261
10263
  }
10262
10264
  function pickLines(raw) {
10263
10265
  const perFile = /* @__PURE__ */ new Map();
10264
- const out = [];
10266
+ const out2 = [];
10265
10267
  for (const line of raw) {
10266
- if (!line.trim() || out.length >= COUNTING_LINES_MAX) continue;
10268
+ if (!line.trim() || out2.length >= COUNTING_LINES_MAX) continue;
10267
10269
  const file = line.split(":", 1)[0];
10268
10270
  const n = perFile.get(file) ?? 0;
10269
10271
  if (n >= PER_FILE) continue;
10270
10272
  perFile.set(file, n + 1);
10271
- out.push(line.length > 200 ? `${line.slice(0, 199)}\u2026` : line);
10273
+ out2.push(line.length > 200 ? `${line.slice(0, 199)}\u2026` : line);
10272
10274
  }
10273
- return out;
10275
+ return out2;
10274
10276
  }
10275
10277
  async function killRecorded(runDir) {
10276
10278
  try {
@@ -11205,6 +11207,56 @@ function usageTokens(usage) {
11205
11207
  return usage.inputTokens + (usage.cacheWriteTokens ?? 0) + usage.outputTokens;
11206
11208
  }
11207
11209
 
11210
+ // ../daemon/src/strays.ts
11211
+ import { execFile as execFile7 } from "child_process";
11212
+ import { realpathSync as realpathSync2 } from "fs";
11213
+ import { promisify as promisify7 } from "util";
11214
+ var run = promisify7(execFile7);
11215
+ function strayPids(lsof, dir, orphans = null, self = process.pid) {
11216
+ const root2 = dir.replace(/\/+$/, "");
11217
+ const pids = [];
11218
+ let pid = 0;
11219
+ for (const line of lsof.split("\n")) {
11220
+ if (line.startsWith("p")) pid = Number(line.slice(1));
11221
+ else if (line.startsWith("n") && pid && pid !== self && (!orphans || orphans.has(pid))) {
11222
+ const at = line.slice(1);
11223
+ if (at === root2 || at.startsWith(`${root2}/`)) pids.push(pid);
11224
+ }
11225
+ }
11226
+ return [...new Set(pids)];
11227
+ }
11228
+ function orphansOf(ps) {
11229
+ return new Set(
11230
+ ps.split("\n").map((l) => l.trim().split(/\s+/).map(Number)).filter(([pid, ppid]) => pid && ppid === 1).map(([pid]) => pid)
11231
+ );
11232
+ }
11233
+ var out = (e) => ({ stdout: String(e.stdout ?? "") });
11234
+ async function stopStrays(dir) {
11235
+ let root2 = dir;
11236
+ try {
11237
+ root2 = realpathSync2(dir);
11238
+ } catch {
11239
+ return [];
11240
+ }
11241
+ const [{ stdout: lsof }, { stdout: ps }] = await Promise.all([
11242
+ run("lsof", ["-w", "-a", "-d", "cwd", "-Fpn"], { maxBuffer: 16 * 1024 * 1024, timeout: 1e4 }).catch(out),
11243
+ run("ps", ["-A", "-o", "pid=,ppid="]).catch(out)
11244
+ ]);
11245
+ const pids = strayPids(lsof, root2, orphansOf(ps));
11246
+ const signal = (s) => pids.forEach((pid) => {
11247
+ try {
11248
+ process.kill(pid, s);
11249
+ } catch {
11250
+ }
11251
+ });
11252
+ signal("SIGTERM");
11253
+ if (pids.length) {
11254
+ await new Promise((r) => setTimeout(r, 2e3));
11255
+ signal("SIGKILL");
11256
+ }
11257
+ return pids;
11258
+ }
11259
+
11208
11260
  // ../daemon/src/build-job.ts
11209
11261
  var PLAN_TOOL = "mcp__nanopm__build_plan";
11210
11262
  var ASK_TOOL = "mcp__nanopm__ask_user";
@@ -11271,31 +11323,36 @@ async function runBuildJob(input) {
11271
11323
  };
11272
11324
  const session2 = async (prompt, resume3) => {
11273
11325
  let end = { stop: failed("the agent produced no result") };
11274
- for await (const event of adapter.run({ ...spec2, prompt, resumeSessionId: resume3 ? spec2.sessionId : void 0 })) {
11275
- await input.onEvent(event);
11276
- if (event.type === "tool_result" && event.tool === PLAN_TOOL && !event.isError && !plan) {
11277
- plan = stepsOf(event.input);
11278
- const understood = event.input?.understood;
11279
- if (plan) await events.write("plan", { steps: plan, ...typeof understood === "string" && understood.trim() ? { understood: understood.trim().slice(0, BUILD_UNDERSTOOD_MAX) } : {} });
11280
- } else if (event.type === "tool_result" && event.tool === STEP_TOOL && !event.isError) {
11281
- const { step, summary } = event.input ?? {};
11282
- const index = Number(step) - 1;
11283
- if (Number.isInteger(index) && index >= 0 && !done.has(index)) {
11284
- const says = plan?.[index] ?? `step ${index + 1}`;
11285
- const commit = await commitAll(worktree, `${solution.ref}: ${says}`, summary);
11286
- done.add(index);
11287
- await events.write("step", { step: index + 1, says, commit: commit?.sha ?? null, files: commit?.files ?? 0 });
11326
+ try {
11327
+ for await (const event of adapter.run({ ...spec2, prompt, resumeSessionId: resume3 ? spec2.sessionId : void 0 })) {
11328
+ await input.onEvent(event);
11329
+ if (event.type === "tool_result" && event.tool === PLAN_TOOL && !event.isError && !plan) {
11330
+ plan = stepsOf(event.input);
11331
+ const understood = event.input?.understood;
11332
+ if (plan) await events.write("plan", { steps: plan, ...typeof understood === "string" && understood.trim() ? { understood: understood.trim().slice(0, BUILD_UNDERSTOOD_MAX) } : {} });
11333
+ } else if (event.type === "tool_result" && event.tool === STEP_TOOL && !event.isError) {
11334
+ const { step, summary } = event.input ?? {};
11335
+ const index = Number(step) - 1;
11336
+ if (Number.isInteger(index) && index >= 0 && !done.has(index)) {
11337
+ const says = plan?.[index] ?? `step ${index + 1}`;
11338
+ const commit = await commitAll(worktree, `${solution.ref}: ${says}`, summary);
11339
+ done.add(index);
11340
+ await events.write("step", { step: index + 1, says, commit: commit?.sha ?? null, files: commit?.files ?? 0 });
11341
+ }
11342
+ } else if (event.type === "finished") {
11343
+ usage = sumUsage(usage, event.usage);
11344
+ said = event.result;
11345
+ end = null;
11346
+ } else if (event.type === "deferred" && event.tool === ASK_TOOL) {
11347
+ usage = sumUsage(usage, event.usage);
11348
+ end = { park: { ask: askOf(event.input) } };
11349
+ } else if (event.type === "failed") {
11350
+ end = event.code === "rate_limit" ? { park: { resetsAt: event.resetsAt } } : { stop: { status: "failed", error: { code: event.code, message: event.message } } };
11288
11351
  }
11289
- } else if (event.type === "finished") {
11290
- usage = sumUsage(usage, event.usage);
11291
- said = event.result;
11292
- end = null;
11293
- } else if (event.type === "deferred" && event.tool === ASK_TOOL) {
11294
- usage = sumUsage(usage, event.usage);
11295
- end = { park: { ask: askOf(event.input) } };
11296
- } else if (event.type === "failed") {
11297
- end = event.code === "rate_limit" ? { park: { resetsAt: event.resetsAt } } : { stop: { status: "failed", error: { code: event.code, message: event.message } } };
11298
11352
  }
11353
+ } finally {
11354
+ const stopped = await stopStrays(worktree.dir);
11355
+ if (stopped.length) await log("build.strays", `stopped ${stopped.length} process${stopped.length === 1 ? "" : "es"} the build left running`);
11299
11356
  }
11300
11357
  return end;
11301
11358
  };
@@ -12336,11 +12393,11 @@ function domainAllowed(host, allowlist) {
12336
12393
  return false;
12337
12394
  }
12338
12395
  function allowlistFor(baseDomains, confirmedDomains) {
12339
- const out = /* @__PURE__ */ new Set();
12396
+ const out2 = /* @__PURE__ */ new Set();
12340
12397
  for (const d of [...baseDomains, ...confirmedDomains]) {
12341
- if (d && !refuseHost(d)) out.add(d.toLowerCase());
12398
+ if (d && !refuseHost(d)) out2.add(d.toLowerCase());
12342
12399
  }
12343
- return [...out];
12400
+ return [...out2];
12344
12401
  }
12345
12402
  function refuseFetch(url, allowlist, open = false) {
12346
12403
  const host = hostOf(url);
@@ -12901,14 +12958,14 @@ ${renderMap()}`,
12901
12958
  const act = (change, asked, why) => talkAct(ctx, round2 ??= talkRound(ctx), change, asked, why);
12902
12959
  const actInTurn = async (ref, changes, asked, why) => {
12903
12960
  if (!changes.length) return failure("Say what changes.");
12904
- const out = [];
12961
+ const out2 = [];
12905
12962
  let at = ref;
12906
12963
  for (const change of changes) {
12907
12964
  const done = await act({ ...change, ref: at }, asked, why);
12908
- out.push(done);
12965
+ out2.push(done);
12909
12966
  if (done.target) at = done.target;
12910
12967
  }
12911
- return json(out);
12968
+ return json(out2);
12912
12969
  };
12913
12970
  const F2 = TalkField;
12914
12971
  const ASKED = z21.string().max(300).nullable().describe('The founder\u2019s own words that ask for this change, copied exactly from their message (a few words are enough: "lower it"). Null when it is your own idea: then it is only suggested, and waits for their yes.');
@@ -13947,9 +14004,9 @@ async function startMemoryServer(env = process.env) {
13947
14004
  import { readFile as readFile12 } from "fs/promises";
13948
14005
 
13949
14006
  // src/project.ts
13950
- import { execFile as execFile7 } from "child_process";
14007
+ import { execFile as execFile8 } from "child_process";
13951
14008
  import { basename as basename2 } from "path";
13952
- import { promisify as promisify7 } from "util";
14009
+ import { promisify as promisify8 } from "util";
13953
14010
 
13954
14011
  // src/local.ts
13955
14012
  import { readFile as readFile11 } from "fs/promises";
@@ -13967,7 +14024,7 @@ function useColor() {
13967
14024
  }
13968
14025
 
13969
14026
  // src/project.ts
13970
- var execFileAsync7 = promisify7(execFile7);
14027
+ var execFileAsync7 = promisify8(execFile8);
13971
14028
  async function detectRepo(cwd) {
13972
14029
  let root2;
13973
14030
  try {
@@ -14164,8 +14221,8 @@ function nanoFrame(mood, tick, paint) {
14164
14221
  const dot = mood === "working" ? DOTS[Math.floor(tick / 3) % DOTS.length] : mood === "happy" ? "\u02D9" : " ";
14165
14222
  return `${paint(ANSI.red, face)}${paint(ANSI.red, dot)}`;
14166
14223
  }
14167
- function plainRenderer(out) {
14168
- return { settle: out, live: () => void 0, pause: () => void 0, resume: () => void 0, stop: () => void 0 };
14224
+ function plainRenderer(out2) {
14225
+ return { settle: out2, live: () => void 0, pause: () => void 0, resume: () => void 0, stop: () => void 0 };
14169
14226
  }
14170
14227
  function liveRenderer(stream, opts = {}) {
14171
14228
  const paint = painter(opts.color ?? Boolean(stream.isTTY && !process.env.NO_COLOR));
@@ -14453,10 +14510,10 @@ function scriptedAnswerer(entries) {
14453
14510
  return entry.answer;
14454
14511
  };
14455
14512
  }
14456
- function answersFromTerminal(out) {
14513
+ function answersFromTerminal(out2) {
14457
14514
  return async (q, signal) => {
14458
14515
  if (!process.stdin.isTTY) {
14459
- out("(no terminal to answer from; skipping)");
14516
+ out2("(no terminal to answer from; skipping)");
14460
14517
  return "skip";
14461
14518
  }
14462
14519
  const rl = createInterface2({ input: process.stdin, output: process.stdout });
@@ -14477,17 +14534,17 @@ function verboseWanted(flag, interactive = Boolean(process.stdout.isTTY)) {
14477
14534
  if (process.env.NANOPM_VERBOSE === "1") return true;
14478
14535
  return !interactive;
14479
14536
  }
14480
- function rendererFor(out) {
14481
- if (!out && process.stdout.isTTY) {
14537
+ function rendererFor(out2) {
14538
+ if (!out2 && process.stdout.isTTY) {
14482
14539
  const renderer = liveRenderer(process.stdout);
14483
14540
  return { renderer, out: (l) => renderer.settle(l) };
14484
14541
  }
14485
- const plain = out ?? ((l) => console.log(l));
14542
+ const plain = out2 ?? ((l) => console.log(l));
14486
14543
  return { renderer: plainRenderer(plain), out: plain };
14487
14544
  }
14488
14545
  var TERMINAL = /* @__PURE__ */ new Set(["succeeded", "failed", "cancelled"]);
14489
14546
  async function runSkill(skill, opts) {
14490
- const { renderer, out } = rendererFor(opts.out);
14547
+ const { renderer, out: out2 } = rendererFor(opts.out);
14491
14548
  const err = opts.err ?? ((l) => console.error(l));
14492
14549
  const pollMs = opts.pollMs ?? 1e3;
14493
14550
  const auth = await authenticate();
@@ -14501,10 +14558,10 @@ async function runSkill(skill, opts) {
14501
14558
  target_daemon_id: daemonId
14502
14559
  }).select("id").single();
14503
14560
  if (error || !job) throw new Error(`could not queue the job: ${error?.message}`);
14504
- out(`queued ${skill} as job ${job.id}`);
14505
- const answerer = opts.answersFile ? await answersFromFile(opts.answersFile) : answersFromTerminal(out);
14561
+ out2(`queued ${skill} as job ${job.id}`);
14562
+ const answerer = opts.answersFile ? await answersFromFile(opts.answersFile) : answersFromTerminal(out2);
14506
14563
  const link = opts.slug ? conversationUrl(opts.slug, job.id) : void 0;
14507
- return streamJob(auth.client, job.id, { pollMs, out, renderer, cancelSignal: opts.cancelSignal, answerer, link, verbose: verboseWanted(opts.verbose) });
14564
+ return streamJob(auth.client, job.id, { pollMs, out: out2, renderer, cancelSignal: opts.cancelSignal, answerer, link, verbose: verboseWanted(opts.verbose) });
14508
14565
  }
14509
14566
  async function streamJob(client, jobId, opts) {
14510
14567
  const renderer = opts.renderer ?? plainRenderer(opts.out);
@@ -14620,7 +14677,7 @@ async function answeredElsewhere(client, jobId, seq, signal, pollMs) {
14620
14677
 
14621
14678
  // src/commands/ask.ts
14622
14679
  async function ask4(question, opts) {
14623
- const out = opts.out ?? ((l) => console.log(l));
14680
+ const out2 = opts.out ?? ((l) => console.log(l));
14624
14681
  if (!question.trim()) throw new Error("ask needs a question");
14625
14682
  if (opts.all && opts.projectId) throw new Error("--all asks every project; drop --project, or drop --all");
14626
14683
  const auth = await authenticate();
@@ -14629,17 +14686,17 @@ async function ask4(question, opts) {
14629
14686
  let worst = 0;
14630
14687
  for (const project2 of await memberProjects(auth.client, auth.userId, opts.cwd)) {
14631
14688
  if (opts.cancelSignal?.aborted) break;
14632
- out(`
14689
+ out2(`
14633
14690
  ${project2.name}`);
14634
- const code = await askOne(auth, project2.id, question, daemonId, opts, out);
14691
+ const code = await askOne(auth, project2.id, question, daemonId, opts, out2);
14635
14692
  worst = Math.max(worst, code);
14636
14693
  }
14637
14694
  return worst;
14638
14695
  }
14639
14696
  const project = await resolveProject(auth.client, { projectId: opts.projectId, cwd: opts.cwd });
14640
- return askOne(auth, project.id, question, daemonId, opts, out);
14697
+ return askOne(auth, project.id, question, daemonId, opts, out2);
14641
14698
  }
14642
- async function askOne(auth, projectId, question, daemonId, opts, out) {
14699
+ async function askOne(auth, projectId, question, daemonId, opts, out2) {
14643
14700
  const { data: job, error } = await auth.client.from("jobs").insert({
14644
14701
  project_id: projectId,
14645
14702
  skill: "ask",
@@ -14652,7 +14709,7 @@ async function askOne(auth, projectId, question, daemonId, opts, out) {
14652
14709
  pollMs: 700,
14653
14710
  out: (line) => {
14654
14711
  if (line.startsWith("\u203A") || line.startsWith("\u25CF") || line.startsWith("\u2713") || line.startsWith(" \u21BA")) return;
14655
- out(line);
14712
+ out2(line);
14656
14713
  },
14657
14714
  cancelSignal: opts.cancelSignal,
14658
14715
  verbose: false
@@ -14660,17 +14717,17 @@ async function askOne(auth, projectId, question, daemonId, opts, out) {
14660
14717
  }
14661
14718
 
14662
14719
  // src/commands/daemon-service.ts
14663
- import { execFile as execFile8 } from "child_process";
14720
+ import { execFile as execFile9 } from "child_process";
14664
14721
  import { existsSync as existsSync6 } from "fs";
14665
14722
  import { mkdir as mkdir7, readFile as readFile14, rm as rm6, writeFile as writeFile7 } from "fs/promises";
14666
14723
  import { homedir as homedir5 } from "os";
14667
14724
  import { join as join16 } from "path";
14668
- import { promisify as promisify8 } from "util";
14725
+ import { promisify as promisify9 } from "util";
14669
14726
 
14670
14727
  // package.json
14671
14728
  var package_default = {
14672
14729
  name: "@nanopm/cli",
14673
- version: "0.1.19",
14730
+ version: "0.1.21",
14674
14731
  description: "An autonomous product manager for builders who run several small products. Runs on your machine, drives your own coding harness.",
14675
14732
  type: "module",
14676
14733
  license: "MIT",
@@ -14719,7 +14776,7 @@ var package_default = {
14719
14776
  var VERSION = package_default.version;
14720
14777
 
14721
14778
  // src/commands/daemon-service.ts
14722
- var execFileAsync8 = promisify8(execFile8);
14779
+ var execFileAsync8 = promisify9(execFile9);
14723
14780
  var LAUNCHD_LABEL = "pm.nano.daemon";
14724
14781
  function plistPath() {
14725
14782
  return join16(homedir5(), "Library", "LaunchAgents", `${LAUNCHD_LABEL}.plist`);
@@ -14761,7 +14818,7 @@ function requireMac() {
14761
14818
  throw new Error("`nanopm daemon install` supports macOS in block 1; on other systems run `nanopm daemon run` in a terminal or a systemd user unit");
14762
14819
  }
14763
14820
  }
14764
- async function installDaemon(out = (l) => console.log(l), opts = {}) {
14821
+ async function installDaemon(out2 = (l) => console.log(l), opts = {}) {
14765
14822
  requireMac();
14766
14823
  if (!await loadCredentials()) throw new Error("not logged in; run `nanopm login` first");
14767
14824
  const auth = await authenticate();
@@ -14772,16 +14829,16 @@ async function installDaemon(out = (l) => console.log(l), opts = {}) {
14772
14829
  await found(detection);
14773
14830
  throw new Error("Claude Code is not installed on this machine (`claude` not found on PATH)");
14774
14831
  }
14775
- out(`Claude Code ${detection.version} found; checking its login\u2026`);
14832
+ out2(`Claude Code ${detection.version} found; checking its login\u2026`);
14776
14833
  const ping = await adapter.ping();
14777
14834
  if (!ping.ok) {
14778
14835
  await found({ ...detection, authenticated: false });
14779
14836
  throw new Error(`Claude Code is installed but not usable headless: ${ping.message}`);
14780
14837
  }
14781
- out("Claude Code answers.");
14838
+ out2("Claude Code answers.");
14782
14839
  const web = await adapter.probeWebSearch();
14783
14840
  await rememberWebSearch(web.ok);
14784
- out(web.ok ? "Claude's own search answers; searches go through your subscription." : `Claude's search is not available on this machine (${web.message}); searches use DuckDuckGo.`);
14841
+ out2(web.ok ? "Claude's own search answers; searches go through your subscription." : `Claude's search is not available on this machine (${web.message}); searches use DuckDuckGo.`);
14785
14842
  const bin = process.argv[1];
14786
14843
  if (!bin) throw new Error("cannot locate the nanopm binary");
14787
14844
  const home = homedir5();
@@ -14794,10 +14851,10 @@ async function installDaemon(out = (l) => console.log(l), opts = {}) {
14794
14851
  const target = guiTarget();
14795
14852
  await execFileAsync8("launchctl", ["bootout", target, plistPath()]).catch(() => void 0);
14796
14853
  await execFileAsync8("launchctl", ["bootstrap", target, plistPath()]);
14797
- out(`Installed ${LAUNCHD_LABEL}; the daemon starts now and at every login. Logs: ${daemonLogPath()}`);
14854
+ out2(`Installed ${LAUNCHD_LABEL}; the daemon starts now and at every login. Logs: ${daemonLogPath()}`);
14798
14855
  const shared = opts.shared ?? await localShared();
14799
- out(shared ? "This machine is shared with your projects: it also runs scheduled reviews and partners' jobs." : "This machine runs your jobs only. `nanopm daemon install --share` to offer it to your projects.");
14800
- if (opts.next !== false) out(`Next, in a repo: nanopm onboard \xB7 or continue at ${webUrl()}/new`);
14856
+ out2(shared ? "This machine is shared with your projects: it also runs scheduled reviews and partners' jobs." : "This machine runs your jobs only. `nanopm daemon install --share` to offer it to your projects.");
14857
+ if (opts.next !== false) out2(`Next, in a repo: nanopm onboard \xB7 or continue at ${webUrl()}/new`);
14801
14858
  }
14802
14859
  function guiTarget() {
14803
14860
  return `gui/${process.getuid?.() ?? 501}`;
@@ -14807,34 +14864,34 @@ async function restartDaemon() {
14807
14864
  await execFileAsync8("launchctl", ["kickstart", "-k", `${guiTarget()}/${LAUNCHD_LABEL}`]);
14808
14865
  return true;
14809
14866
  }
14810
- async function uninstallDaemon(out = (l) => console.log(l)) {
14867
+ async function uninstallDaemon(out2 = (l) => console.log(l)) {
14811
14868
  requireMac();
14812
14869
  const target = guiTarget();
14813
14870
  await execFileAsync8("launchctl", ["bootout", target, plistPath()]).catch(() => void 0);
14814
14871
  await rm6(plistPath(), { force: true });
14815
- out(`Removed ${LAUNCHD_LABEL}.`);
14872
+ out2(`Removed ${LAUNCHD_LABEL}.`);
14816
14873
  }
14817
- async function daemonStatus(out = (l) => console.log(l)) {
14874
+ async function daemonStatus(out2 = (l) => console.log(l)) {
14818
14875
  const id = await localDaemonId();
14819
14876
  const installed = process.platform === "darwin" && existsSync6(plistPath());
14820
- out(`launchd: ${installed ? "installed" : "not installed"}`);
14877
+ out2(`launchd: ${installed ? "installed" : "not installed"}`);
14821
14878
  if (!id) {
14822
- out("daemon: never registered on this machine");
14879
+ out2("daemon: never registered on this machine");
14823
14880
  return;
14824
14881
  }
14825
14882
  const auth = await authenticate();
14826
14883
  const { data } = await auth.client.from("daemons").select("hostname, status, last_seen_at, version, harnesses, shared").eq("id", id).maybeSingle();
14827
14884
  if (!data) {
14828
- out(`daemon: ${id} is not in the account any more`);
14885
+ out2(`daemon: ${id} is not in the account any more`);
14829
14886
  return;
14830
14887
  }
14831
14888
  const seen = data.last_seen_at ? `${Math.round((Date.now() - new Date(data.last_seen_at).getTime()) / 1e3)}s ago` : "never";
14832
- out(`daemon: ${data.hostname} \xB7 ${data.status} \xB7 last seen ${seen} \xB7 nanopm ${data.version}`);
14833
- out(data.shared ? "shared with your projects: runs scheduled reviews and partners' jobs too" : "runs your jobs only \xB7 `nanopm daemon install --share` to offer it to your projects");
14889
+ out2(`daemon: ${data.hostname} \xB7 ${data.status} \xB7 last seen ${seen} \xB7 nanopm ${data.version}`);
14890
+ out2(data.shared ? "shared with your projects: runs scheduled reviews and partners' jobs too" : "runs your jobs only \xB7 `nanopm daemon install --share` to offer it to your projects");
14834
14891
  const { count: count2 } = await auth.client.from("jobs").select("id", { count: "exact", head: true }).eq("status", "queued");
14835
- out(`queued jobs: ${count2 ?? 0}`);
14892
+ out2(`queued jobs: ${count2 ?? 0}`);
14836
14893
  const { data: running } = await auth.client.from("jobs").select("id, skill").eq("assigned_daemon_id", id).in("status", ["running", "waiting"]);
14837
- for (const j of running ?? []) out(`running: ${j.skill} ${j.id}`);
14894
+ for (const j of running ?? []) out2(`running: ${j.skill} ${j.id}`);
14838
14895
  }
14839
14896
  async function daemonLogs(lines = 50) {
14840
14897
  try {
@@ -14891,22 +14948,22 @@ function renderActivityLog(jobs, rows2, opts) {
14891
14948
  const key = row.job_id ?? "";
14892
14949
  byJob.set(key, [...byJob.get(key) ?? [], row]);
14893
14950
  }
14894
- const out = [];
14951
+ const out2 = [];
14895
14952
  const ordered = [...jobs].sort((a, b) => a.created_at.localeCompare(b.created_at));
14896
14953
  for (const job of ordered) {
14897
14954
  const jobRows = byJob.get(job.id) ?? [];
14898
- out.push(paint(ANSI.bold, jobHeader(job)));
14899
- if (job.note) out.push(` ${job.note}`);
14900
- for (const row of jobRows) out.push(renderRow(row, paint));
14901
- out.push("");
14955
+ out2.push(paint(ANSI.bold, jobHeader(job)));
14956
+ if (job.note) out2.push(` ${job.note}`);
14957
+ for (const row of jobRows) out2.push(renderRow(row, paint));
14958
+ out2.push("");
14902
14959
  byJob.delete(job.id);
14903
14960
  }
14904
14961
  const loose = [...byJob.values()].flat();
14905
14962
  if (loose.length) {
14906
- out.push(paint(ANSI.bold, "Outside any job"));
14907
- for (const row of loose) out.push(renderRow(row, paint));
14963
+ out2.push(paint(ANSI.bold, "Outside any job"));
14964
+ for (const row of loose) out2.push(renderRow(row, paint));
14908
14965
  }
14909
- return out.join("\n").trimEnd();
14966
+ return out2.join("\n").trimEnd();
14910
14967
  }
14911
14968
  function jobHeader(job) {
14912
14969
  const parts = [job.skill];
@@ -15008,17 +15065,17 @@ async function correctionNote(client, jobId) {
15008
15065
  import { setTimeout as sleep4 } from "timers/promises";
15009
15066
 
15010
15067
  // src/browser.ts
15011
- import { execFile as execFile9 } from "child_process";
15068
+ import { execFile as execFile10 } from "child_process";
15012
15069
  function openBrowser(url, interactive = Boolean(process.stdout.isTTY)) {
15013
15070
  if (!interactive) return false;
15014
15071
  const cmd = process.platform === "darwin" ? "open" : process.platform === "win32" ? "start" : "xdg-open";
15015
- execFile9(cmd, [url], () => void 0);
15072
+ execFile10(cmd, [url], () => void 0);
15016
15073
  return true;
15017
15074
  }
15018
15075
 
15019
15076
  // src/commands/login.ts
15020
15077
  async function login(opts = {}) {
15021
- const out = opts.out ?? ((l) => console.log(l));
15078
+ const out2 = opts.out ?? ((l) => console.log(l));
15022
15079
  const web = (opts.web ?? webUrl()).replace(/\/+$/, "");
15023
15080
  const config = supabaseConfigFromEnv();
15024
15081
  let code = generateDeviceCode();
@@ -15037,8 +15094,8 @@ async function login(opts = {}) {
15037
15094
  throw new Error(`nano.pm refused to start a login (${res.status}): ${await res.text()}`);
15038
15095
  }
15039
15096
  const url = `${web}/device?code=${code}`;
15040
- out(`Your code: ${formatDeviceCode(code)}`);
15041
- out(`Confirm it at ${url}`);
15097
+ out2(`Your code: ${formatDeviceCode(code)}`);
15098
+ out2(`Confirm it at ${url}`);
15042
15099
  if (!opts.noBrowser) openBrowser(url);
15043
15100
  const deadline = Date.now() + DEVICE_CODE_TTL_MS;
15044
15101
  const pollMs = opts.pollMs ?? 2e3;
@@ -15069,58 +15126,58 @@ async function login(opts = {}) {
15069
15126
  expiresAt: data.session.expires_at ?? session2.expires_at
15070
15127
  });
15071
15128
  const { data: profile } = await client.from("users").select("display_name, github_login").eq("id", data.session.user.id).maybeSingle();
15072
- out(`Logged in as ${profile?.github_login ?? profile?.display_name ?? data.session.user.email ?? data.session.user.id}.`);
15129
+ out2(`Logged in as ${profile?.github_login ?? profile?.display_name ?? data.session.user.email ?? data.session.user.id}.`);
15073
15130
  if (!opts.noDaemon) {
15074
15131
  try {
15075
- await installDaemon(out, { next: false });
15132
+ await installDaemon(out2, { next: false });
15076
15133
  } catch (err) {
15077
- out(`could not start me in the background: ${err instanceof Error ? err.message : String(err)}`);
15078
- out("");
15079
- out("Your account is linked, but I cannot work on this machine yet. Fix the line above, then run: nanopm daemon install. The page in your browser moves on by itself.");
15134
+ out2(`could not start me in the background: ${err instanceof Error ? err.message : String(err)}`);
15135
+ out2("");
15136
+ out2("Your account is linked, but I cannot work on this machine yet. Fix the line above, then run: nanopm daemon install. The page in your browser moves on by itself.");
15080
15137
  return;
15081
15138
  }
15082
15139
  }
15083
- out("");
15084
- out(`Your machine is linked. Continue at ${web}/new`);
15140
+ out2("");
15141
+ out2(`Your machine is linked. Continue at ${web}/new`);
15085
15142
  }
15086
- async function logout(out = (l) => console.log(l)) {
15143
+ async function logout(out2 = (l) => console.log(l)) {
15087
15144
  const stored = await loadCredentials();
15088
15145
  if (!stored) {
15089
- out("Not logged in.");
15146
+ out2("Not logged in.");
15090
15147
  return;
15091
15148
  }
15092
15149
  const client = createNanopmClient({ url: stored.supabaseUrl, anonKey: stored.anonKey });
15093
15150
  const { error } = await client.auth.setSession({ access_token: stored.accessToken, refresh_token: stored.refreshToken });
15094
15151
  if (!error) await client.auth.signOut({ scope: "local" });
15095
15152
  await deleteCredentials();
15096
- out("Logged out. The daemon on this machine will stop taking jobs at its next start.");
15153
+ out2("Logged out. The daemon on this machine will stop taking jobs at its next start.");
15097
15154
  }
15098
15155
 
15099
15156
  // src/commands/update.ts
15100
- async function update(opts, out = (l) => console.log(l)) {
15157
+ async function update(opts, out2 = (l) => console.log(l)) {
15101
15158
  if (opts.auto !== void 0) {
15102
15159
  if (opts.auto !== "on" && opts.auto !== "off") throw new Error("--auto takes on or off");
15103
15160
  await setAutoUpdate(opts.auto === "on");
15104
- out(opts.auto === "on" ? "Automatic updates are on: NanoPM updates itself when it is idle." : "Automatic updates are off. `nanopm update` still updates when you ask.");
15161
+ out2(opts.auto === "on" ? "Automatic updates are on: NanoPM updates itself when it is idle." : "Automatic updates are off. `nanopm update` still updates when you ask.");
15105
15162
  return;
15106
15163
  }
15107
15164
  if (installKind() !== "installer") {
15108
- out(`This NanoPM (${VERSION}) was not installed with the install line, so it does not update itself.`);
15109
- out("From a clone of nanopm2: git pull && pnpm build && nanopm daemon install");
15110
- out("Installed with npm: npm i -g @nanopm/cli@latest && nanopm daemon install");
15165
+ out2(`This NanoPM (${VERSION}) was not installed with the install line, so it does not update itself.`);
15166
+ out2("From a clone of nanopm2: git pull && pnpm build && nanopm daemon install");
15167
+ out2("Installed with npm: npm i -g @nanopm/cli@latest && nanopm daemon install");
15111
15168
  return;
15112
15169
  }
15113
15170
  const latest = await latestVersion();
15114
15171
  if (!latest) throw new Error("could not reach the npm registry; try again in a moment");
15115
15172
  if (!newer(latest, VERSION)) {
15116
- out(`Already up to date (${VERSION}).`);
15173
+ out2(`Already up to date (${VERSION}).`);
15117
15174
  return;
15118
15175
  }
15119
- out(`Updating ${VERSION} \u2192 ${latest}\u2026`);
15176
+ out2(`Updating ${VERSION} \u2192 ${latest}\u2026`);
15120
15177
  const done = await installVersion(latest);
15121
15178
  if (!done.ok) throw new Error(`${done.why}
15122
15179
  The install line does the same: curl -fsSL https://nanopm.app/install | sh`);
15123
- out(await restartDaemon() ? `Updated ${VERSION} \u2192 ${latest}; the daemon restarted on it.` : `Updated ${VERSION} \u2192 ${latest}.`);
15180
+ out2(await restartDaemon() ? `Updated ${VERSION} \u2192 ${latest}; the daemon restarted on it.` : `Updated ${VERSION} \u2192 ${latest}.`);
15124
15181
  }
15125
15182
 
15126
15183
  // src/commands/autonomy.ts
@@ -15188,51 +15245,51 @@ async function verifyCommand(command, opts) {
15188
15245
  }
15189
15246
 
15190
15247
  // src/commands/build.ts
15191
- import { execFile as execFile10 } from "child_process";
15248
+ import { execFile as execFile11 } from "child_process";
15192
15249
  import { existsSync as existsSync7 } from "fs";
15193
15250
  import { homedir as homedir6 } from "os";
15194
- import { promisify as promisify9 } from "util";
15251
+ import { promisify as promisify10 } from "util";
15195
15252
 
15196
15253
  // src/format/opportunities.ts
15197
15254
  function renderOpportunities(rows2, lens, opts) {
15198
15255
  const paint = painter(opts.color);
15199
15256
  const width = Math.max(40, columns());
15200
- const out = [];
15257
+ const out2 = [];
15201
15258
  if (!lens) return "No objective yet: the map is drawn for it. Say what matters most right now on Your business, then `nanopm opportunities run`.";
15202
- out.push(`${paint(ANSI.bold, "Opportunities")} ${paint(ANSI.dim, `${lens.lever} \u2014 ${lens.sentence}`)}`);
15203
- out.push(paint(ANSI.dim, lens.definition ? `Counts: ${lens.definition}` : "What it counts is not defined yet: drawn against the lever and your sentence."));
15204
- if (opts.previous) out.push(paint(ANSI.amber, "Drawn for your previous objective. `nanopm opportunities run` draws it for this one."));
15259
+ out2.push(`${paint(ANSI.bold, "Opportunities")} ${paint(ANSI.dim, `${lens.lever} \u2014 ${lens.sentence}`)}`);
15260
+ out2.push(paint(ANSI.dim, lens.definition ? `Counts: ${lens.definition}` : "What it counts is not defined yet: drawn against the lever and your sentence."));
15261
+ if (opts.previous) out2.push(paint(ANSI.amber, "Drawn for your previous objective. `nanopm opportunities run` draws it for this one."));
15205
15262
  if (!rows2.length) {
15206
- out.push("", "No map yet. `nanopm opportunities run` draws it from what NanoPM knows; add --deep to check it against your users, code and data.");
15207
- return out.join("\n");
15263
+ out2.push("", "No map yet. `nanopm opportunities run` draws it from what NanoPM knows; add --deep to check it against your users, code and data.");
15264
+ return out2.join("\n");
15208
15265
  }
15209
15266
  const tree = liveTree(rows2);
15210
15267
  const live = rows2.filter(isLive);
15211
15268
  const evidenced = live.filter((r) => r.evidence_status === "evidence_backed").length;
15212
- out.push(paint(ANSI.dim, `${live.length} on the map: ${evidenced} evidence-backed, ${live.length - evidenced} hypotheses \u2014 what users or their behaviour have not shown yet.`));
15269
+ out2.push(paint(ANSI.dim, `${live.length} on the map: ${evidenced} evidence-backed, ${live.length - evidenced} hypotheses \u2014 what users or their behaviour have not shown yet.`));
15213
15270
  const who = (r) => r.personas.map((p) => opts.names?.get(p) ?? p).join(", ");
15214
15271
  const one = (r, place) => {
15215
15272
  const indent = " ".repeat(r.depth - 1);
15216
15273
  const yours = r.status === "confirmed" ? paint(ANSI.green, " yours") : "";
15217
- out.push("", `${indent}${paint(ANSI.bold, place.padStart(2))} ${r.depth === 1 ? paint(ANSI.bold, r.problem) : r.problem}${yours} ${paint(ANSI.dim, r.ref)}`);
15274
+ out2.push("", `${indent}${paint(ANSI.bold, place.padStart(2))} ${r.depth === 1 ? paint(ANSI.bold, r.problem) : r.problem}${yours} ${paint(ANSI.dim, r.ref)}`);
15218
15275
  const pad = `${indent} `;
15219
15276
  const facts = [PRIORITY_WORD[r.priority], EVIDENCE_STATUS_WORD[r.evidence_status], NEXT_ACTION_WORD[r.next_action], who(r)].filter(Boolean).join(" \xB7 ");
15220
- out.push(`${pad}${paint(ANSI.dim, facts)}`);
15277
+ out2.push(`${pad}${paint(ANSI.dim, facts)}`);
15221
15278
  const gist = r.description ? paragraphs(r.description)[0] : null;
15222
- if (gist) for (const l of wrap(gist, width - pad.length)) out.push(`${pad}${l}`);
15223
- if (r.learn) for (const l of wrap(`To learn more: ${r.learn}`, width - pad.length)) out.push(`${pad}${paint(ANSI.dim, l)}`);
15224
- if (r.founder_rank !== null && r.now_rank) out.push(`${pad}${paint(ANSI.dim, `Its place is yours; NanoPM would now put it at #${r.now_rank}.`)}`);
15225
- if (r.reconsider) for (const l of wrap(`Worth reconsidering: ${r.reconsider}`, width - pad.length)) out.push(`${pad}${paint(ANSI.amber, l)}`);
15279
+ if (gist) for (const l of wrap(gist, width - pad.length)) out2.push(`${pad}${l}`);
15280
+ if (r.learn) for (const l of wrap(`To learn more: ${r.learn}`, width - pad.length)) out2.push(`${pad}${paint(ANSI.dim, l)}`);
15281
+ if (r.founder_rank !== null && r.now_rank) out2.push(`${pad}${paint(ANSI.dim, `Its place is yours; NanoPM would now put it at #${r.now_rank}.`)}`);
15282
+ if (r.reconsider) for (const l of wrap(`Worth reconsidering: ${r.reconsider}`, width - pad.length)) out2.push(`${pad}${paint(ANSI.amber, l)}`);
15226
15283
  for (const c of r.children) one(c, `${place}.${c.rank}`);
15227
15284
  };
15228
15285
  for (const r of tree) one(r, String(r.rank));
15229
15286
  const aside = rows2.filter((r) => r.status === "set_aside");
15230
15287
  if (aside.length) {
15231
- out.push("", paint(ANSI.bold, `Set aside (${aside.length})`));
15232
- for (const r of aside) out.push(` ${paint(ANSI.dim, r.ref.padEnd(8))} ${r.problem}${r.set_aside_reason ? paint(ANSI.dim, ` \u2014 ${r.set_aside_reason}`) : ""}`);
15288
+ out2.push("", paint(ANSI.bold, `Set aside (${aside.length})`));
15289
+ for (const r of aside) out2.push(` ${paint(ANSI.dim, r.ref.padEnd(8))} ${r.problem}${r.set_aside_reason ? paint(ANSI.dim, ` \u2014 ${r.set_aside_reason}`) : ""}`);
15233
15290
  }
15234
- out.push("", paint(ANSI.dim, '`nanopm opportunities confirm <ref>`, `set-aside <ref> "why"`, `move <ref> <place>`, `reword <ref> "\u2026"`, `brainstorm <ref> [--deep]`, `run [--deep] [--note "\u2026"] [--use signals,data,repo,market] [--skip \u2026]`.'));
15235
- return out.join("\n");
15291
+ out2.push("", paint(ANSI.dim, '`nanopm opportunities confirm <ref>`, `set-aside <ref> "why"`, `move <ref> <place>`, `reword <ref> "\u2026"`, `brainstorm <ref> [--deep]`, `run [--deep] [--note "\u2026"] [--use signals,data,repo,market] [--skip \u2026]`.'));
15292
+ return out2.join("\n");
15236
15293
  }
15237
15294
 
15238
15295
  // src/format/needs.ts
@@ -15240,12 +15297,12 @@ var MARK = { observed: "\u25CF", stated: "\u2013", inferred: "\u25CB" };
15240
15297
  function renderNeeds(persona, tree, opts) {
15241
15298
  const paint = painter(opts.color);
15242
15299
  const width = Math.max(40, columns());
15243
- const out = [];
15300
+ const out2 = [];
15244
15301
  const counts = needCounts(tree);
15245
- out.push(`${paint(ANSI.bold, `${persona.name}\u2019s needs`)} ${paint(ANSI.dim, `${counts.needs} needs \xB7 ${counts.difficulties + counts.precise} difficulties`)}`);
15302
+ out2.push(`${paint(ANSI.bold, `${persona.name}\u2019s needs`)} ${paint(ANSI.dim, `${counts.needs} needs \xB7 ${counts.difficulties + counts.precise} difficulties`)}`);
15246
15303
  if (!tree.length) {
15247
- out.push("", `Nothing mapped yet. \`nanopm needs --refresh ${persona.slug}\` maps it in depth; add --short for a quick pass.`);
15248
- return out.join("\n");
15304
+ out2.push("", `Nothing mapped yet. \`nanopm needs --refresh ${persona.slug}\` maps it in depth; add --short for a quick pass.`);
15305
+ return out2.join("\n");
15249
15306
  }
15250
15307
  const line = (n, depth) => {
15251
15308
  const indent = " ".repeat(depth);
@@ -15254,17 +15311,17 @@ function renderNeeds(persona, tree, opts) {
15254
15311
  const state = n.status === "dropped" ? paint(ANSI.dim, " dropped") : n.status === "confirmed" || n.author_kind === "user" ? "" : paint(ANSI.amber, " to review");
15255
15312
  const label = n.status === "dropped" ? paint(ANSI.dim, n.label) : n.level === 1 ? paint(ANSI.bold, n.label) : n.label;
15256
15313
  const body = wrap(`${label}${where}${state}${n.proposal ? paint(ANSI.amber, " \xB7 NanoPM suggests a new wording") : ""}`, width - indent.length - 12);
15257
- out.push(`${indent}${lead}${paint(ANSI.dim, n.ref.padEnd(8))} ${body[0] ?? ""}`);
15258
- for (const more of body.slice(1)) out.push(`${indent} ${more}`);
15314
+ out2.push(`${indent}${lead}${paint(ANSI.dim, n.ref.padEnd(8))} ${body[0] ?? ""}`);
15315
+ for (const more of body.slice(1)) out2.push(`${indent} ${more}`);
15259
15316
  for (const c of n.children) line(c, depth + 1);
15260
15317
  };
15261
15318
  for (const need of tree) {
15262
- out.push("");
15319
+ out2.push("");
15263
15320
  line(need, 0);
15264
15321
  }
15265
- out.push("", paint(ANSI.dim, `\u25CF observed \u2013 stated by you \u25CB NanoPM\u2019s guess. \`nanopm needs confirm <ref>\`, \`nanopm needs drop <ref> "why"\`, \`nanopm needs dig <ref>\`.`));
15266
- if (opts.others?.length) out.push(paint(ANSI.dim, `Other personas: ${opts.others.join(", ")}.`));
15267
- return out.join("\n");
15322
+ out2.push("", paint(ANSI.dim, `\u25CF observed \u2013 stated by you \u25CB NanoPM\u2019s guess. \`nanopm needs confirm <ref>\`, \`nanopm needs drop <ref> "why"\`, \`nanopm needs dig <ref>\`.`));
15323
+ if (opts.others?.length) out2.push(paint(ANSI.dim, `Other personas: ${opts.others.join(", ")}.`));
15324
+ return out2.join("\n");
15268
15325
  }
15269
15326
 
15270
15327
  // src/commands/needs.ts
@@ -15464,11 +15521,11 @@ async function brainstorm(ref, opts) {
15464
15521
  const minutes = deep ? DEEP_MINUTES * n : FLASH_MINUTES;
15465
15522
  const what = plan.problem.children.length ? `the ${n} narrower problem${n === 1 ? "" : "s"} under ${plan.problem.ref}` : `${plan.problem.ref} \u201C${clip(plan.problem.problem, 60)}\u201D`;
15466
15523
  const how = deep ? `${DEPTH_WORD.deep}${n > 1 ? ", one after the other" : ""}` : `${DEPTH_WORD.quick}, a first pass`;
15467
- const out = [`${deep ? "Going deeper on" : "Brainstorming"} ${what}: ${how}, about ${minutes} minutes on ${daemon.hostname}.`];
15468
- if (plan.problem.children.length) for (const l of plan.leaves) out.push(` ${l.ref.padEnd(8)} ${clip(l.problem, Math.max(30, columns() - 11))}`);
15469
- if (plan.busy) out.push(`${plan.busy} left out: Nano is already finding solutions to ${plan.busy === 1 ? "it" : "them"}.`);
15470
- out.push("The solutions land on `nanopm roadmap`.");
15471
- return out.join("\n");
15524
+ const out2 = [`${deep ? "Going deeper on" : "Brainstorming"} ${what}: ${how}, about ${minutes} minutes on ${daemon.hostname}.`];
15525
+ if (plan.problem.children.length) for (const l of plan.leaves) out2.push(` ${l.ref.padEnd(8)} ${clip(l.problem, Math.max(30, columns() - 11))}`);
15526
+ if (plan.busy) out2.push(`${plan.busy} left out: Nano is already finding solutions to ${plan.busy === 1 ? "it" : "them"}.`);
15527
+ out2.push("The solutions land on `nanopm roadmap`.");
15528
+ return out2.join("\n");
15472
15529
  }
15473
15530
 
15474
15531
  // src/format/roadmap.ts
@@ -15484,11 +15541,11 @@ var WORK_KINDS = /* @__PURE__ */ new Set(["branch", "git", "url", "job"]);
15484
15541
  function renderRoadmapItem(item, opts, problem, builtBy) {
15485
15542
  const paint = painter(opts.color);
15486
15543
  const shape2 = SHAPE[item.status] ?? { mark: "\xB7", colour: "", label: item.status };
15487
- const out = [`${paint(shape2.colour, shape2.mark)} ${paint(ANSI.bold, item.ref)} ${item.title}`];
15488
- if (problem) out.push(paint(ANSI.dim, ` answers ${problem.ref} ${clip(problem.statement, Math.max(30, columns() - 20))}`));
15489
- out.push("");
15490
- for (const line of wrap(item.reasoning, Math.max(40, columns() - 4))) out.push(` ${line}`);
15491
- out.push("");
15544
+ const out2 = [`${paint(shape2.colour, shape2.mark)} ${paint(ANSI.bold, item.ref)} ${item.title}`];
15545
+ if (problem) out2.push(paint(ANSI.dim, ` answers ${problem.ref} ${clip(problem.statement, Math.max(30, columns() - 20))}`));
15546
+ out2.push("");
15547
+ for (const line of wrap(item.reasoning, Math.max(40, columns() - 4))) out2.push(` ${line}`);
15548
+ out2.push("");
15492
15549
  const facts = [
15493
15550
  ["status", shape2.label + (item.outcome ? ` \xB7 ${item.outcome}${item.outcome_note ? `: ${item.outcome_note}` : ""}` : "")],
15494
15551
  ["rank", String(item.rank)],
@@ -15496,15 +15553,15 @@ function renderRoadmapItem(item, opts, problem, builtBy) {
15496
15553
  ["bets on", item.assumption ?? "\u2014"],
15497
15554
  ["we'll know", item.eval ?? "\u2014"]
15498
15555
  ];
15499
- for (const [k, v] of facts) out.push(paint(ANSI.dim, ` ${k.padEnd(9)} ${v}`));
15556
+ for (const [k, v] of facts) out2.push(paint(ANSI.dim, ` ${k.padEnd(9)} ${v}`));
15500
15557
  const sources2 = item.sources ?? [];
15501
15558
  const restsOn = sources2.filter((s) => !WORK_KINDS.has(s.kind));
15502
15559
  if (restsOn.length) {
15503
- out.push(paint(ANSI.dim, ` ${"rests on".padEnd(9)} ${restsOn.map((s) => `${s.kind}:${s.ref}`).join(", ")}`));
15560
+ out2.push(paint(ANSI.dim, ` ${"rests on".padEnd(9)} ${restsOn.map((s) => `${s.kind}:${s.ref}`).join(", ")}`));
15504
15561
  }
15505
15562
  const where = renderWhere(sources2);
15506
- if (where) out.push(paint(ANSI.dim, ` ${"where".padEnd(9)} ${where}${builtBy ? ` \xB7 built by ${builtBy}` : ""}`));
15507
- return out.join("\n");
15563
+ if (where) out2.push(paint(ANSI.dim, ` ${"where".padEnd(9)} ${where}${builtBy ? ` \xB7 built by ${builtBy}` : ""}`));
15564
+ return out2.join("\n");
15508
15565
  }
15509
15566
  function renderWhere(sources2) {
15510
15567
  const branch = sources2.find((s) => s.kind === "branch")?.ref;
@@ -15529,18 +15586,18 @@ var VERB = {
15529
15586
  function renderDecisions(rows2, opts) {
15530
15587
  const paint = painter(opts.color);
15531
15588
  if (!rows2.length) return "No decisions yet.";
15532
- const out = [];
15589
+ const out2 = [];
15533
15590
  for (const row of rows2) {
15534
15591
  const when = row.created_at.slice(0, 10);
15535
15592
  const verb = VERB[row.kind] ?? { text: row.kind, colour: ANSI.blue };
15536
15593
  const about = row.solutions ? `${paint(ANSI.dim, row.solutions.ref)} ${row.solutions.title}` : row.problems ? paint(ANSI.dim, row.problems.ref) : row.market_entries ? `${paint(ANSI.dim, row.market_entries.ref)} ${row.market_entries.competitor ?? ""}`.trimEnd() : "";
15537
15594
  const by = row.by ? paint(ANSI.dim, ` \xB7 by ${row.by}`) : "";
15538
- out.push(`${paint(ANSI.dim, when)} ${paint(verb.colour, verb.text)} ${about}${by}`.trimEnd());
15595
+ out2.push(`${paint(ANSI.dim, when)} ${paint(verb.colour, verb.text)} ${about}${by}`.trimEnd());
15539
15596
  if (row.body) {
15540
- for (const line of wrap(row.body, 72)) out.push(paint(ANSI.dim, ` ${line}`));
15597
+ for (const line of wrap(row.body, 72)) out2.push(paint(ANSI.dim, ` ${line}`));
15541
15598
  }
15542
15599
  }
15543
- return out.join("\n");
15600
+ return out2.join("\n");
15544
15601
  }
15545
15602
  var HUE = ["\x1B[34m", "\x1B[35m", "\x1B[36m", "\x1B[32m", "\x1B[95m"];
15546
15603
  function themesOf(tree) {
@@ -15553,7 +15610,7 @@ function renderSolutionsBoard(board, opts) {
15553
15610
  const paint = painter(opts.color);
15554
15611
  const width = Math.max(40, columns());
15555
15612
  const themes = themesOf(board.tree);
15556
- const out = [`${paint(ANSI.bold, "Roadmap")}${board.objective ? ` ${paint(ANSI.dim, `${board.objective.lever} \u2014 ${board.objective.sentence}`)}` : ""}`];
15613
+ const out2 = [`${paint(ANSI.bold, "Roadmap")}${board.objective ? ` ${paint(ANSI.dim, `${board.objective.lever} \u2014 ${board.objective.sentence}`)}` : ""}`];
15557
15614
  const chips = board.tree.flatMap((b) => {
15558
15615
  const n = board.cards.filter((c) => c.branch.id === b.id).length;
15559
15616
  const t = themes.get(b.id);
@@ -15565,89 +15622,89 @@ function renderSolutionsBoard(board, opts) {
15565
15622
  else lines.push(chip);
15566
15623
  return lines;
15567
15624
  }, []);
15568
- out.push("", ...legend);
15569
- if (!board.cards.some((c) => c.pick.placed_by === "founder")) out.push(paint(ANSI.dim, "Nano placed these in the order of your opportunity map. Move them where you want them."));
15625
+ out2.push("", ...legend);
15626
+ if (!board.cards.some((c) => c.pick.placed_by === "founder")) out2.push(paint(ANSI.dim, "Nano placed these in the order of your opportunity map. Move them where you want them."));
15570
15627
  const refWidth = Math.max(5, ...board.cards.map((c) => c.pick.ref.length));
15571
15628
  const gutter = " ".repeat(refWidth + 4);
15572
15629
  const titleWidth = Math.min(Math.max(...board.cards.map((c) => c.pick.title.length)), width - gutter.length - 8);
15573
15630
  for (const h of HORIZONS) {
15574
15631
  const column = board.cards.filter((c) => c.horizon === h);
15575
- out.push("", `${paint(ANSI.bold, HORIZON_WORD[h])} ${paint(ANSI.dim, String(column.length))}`);
15576
- if (!column.length) out.push(paint(ANSI.dim, ` Move a card here: nanopm roadmap move <ref> ${h}`));
15632
+ out2.push("", `${paint(ANSI.bold, HORIZON_WORD[h])} ${paint(ANSI.dim, String(column.length))}`);
15633
+ if (!column.length) out2.push(paint(ANSI.dim, ` Move a card here: nanopm roadmap move <ref> ${h}`));
15577
15634
  for (const c of column) {
15578
15635
  const t = themes.get(c.branch.id);
15579
- out.push(` ${paint(ANSI.dim, c.pick.ref.padEnd(refWidth))} ${paint(t.colour, "\u25CF")} ${paint(ANSI.dim, t.name)}`);
15580
- out.push(`${gutter}${clip(c.pick.title, titleWidth).padEnd(titleWidth)} ${sizeWord(c.pick.size, paint)}`.trimEnd());
15636
+ out2.push(` ${paint(ANSI.dim, c.pick.ref.padEnd(refWidth))} ${paint(t.colour, "\u25CF")} ${paint(ANSI.dim, t.name)}`);
15637
+ out2.push(`${gutter}${clip(c.pick.title, titleWidth).padEnd(titleWidth)} ${sizeWord(c.pick.size, paint)}`.trimEnd());
15581
15638
  const depth = board.working.get(c.leaf.id);
15582
- if (depth) out.push(`${gutter}${paint(ANSI.blue, "\u25CF")} ${paint(ANSI.dim, WORK_WORD[depth])}`);
15639
+ if (depth) out2.push(`${gutter}${paint(ANSI.blue, "\u25CF")} ${paint(ANSI.dim, WORK_WORD[depth])}`);
15583
15640
  }
15584
15641
  }
15585
- if (board.earlier) out.push("", paint(ANSI.bold, "On the problem list"), "", board.earlier);
15586
- out.push(
15642
+ if (board.earlier) out2.push("", paint(ANSI.bold, "On the problem list"), "", board.earlier);
15643
+ out2.push(
15587
15644
  "",
15588
15645
  paint(ANSI.dim, 'nanopm roadmap <ref> \xB7 nanopm roadmap move <ref> now|next|later [position] \xB7 nanopm reject <ref> ["why"]'),
15589
15646
  paint(ANSI.dim, "nanopm opportunities brainstorm <ref> [--deep]: solutions to another problem, or deeper ones")
15590
15647
  );
15591
- return out.join("\n");
15648
+ return out2.join("\n");
15592
15649
  }
15593
15650
  function renderSolutionSheet(s, at, opts) {
15594
15651
  const paint = painter(opts.color);
15595
15652
  const width = Math.max(40, columns());
15596
15653
  const theme = at.branch ? themesOf(at.tree).get(at.branch.id) : void 0;
15597
- const out = [];
15598
- if (theme) out.push(`${paint(theme.colour, "\u25CF")} ${paint(ANSI.dim, theme.name)}`);
15654
+ const out2 = [];
15655
+ if (theme) out2.push(`${paint(theme.colour, "\u25CF")} ${paint(ANSI.dim, theme.name)}`);
15599
15656
  const size = sizeWord(s.size, paint);
15600
- out.push(`${paint(ANSI.dim, s.ref)} ${paint(ANSI.bold, s.title)}${size ? ` ${size}` : ""}`);
15657
+ out2.push(`${paint(ANSI.dim, s.ref)} ${paint(ANSI.bold, s.title)}${size ? ` ${size}` : ""}`);
15601
15658
  const pad = " ".repeat(12);
15602
- const heading = (text2) => out.push("", ` ${paint(ANSI.bold, text2)}`);
15659
+ const heading = (text2) => out2.push("", ` ${paint(ANSI.bold, text2)}`);
15603
15660
  heading("Problem we are solving");
15604
15661
  if (at.leaf) {
15605
15662
  const solves = wrap(at.leaf.problem, width - 6 - at.leaf.ref.length - 2);
15606
15663
  solves[solves.length - 1] += ` ${paint(ANSI.dim, at.leaf.ref)}`;
15607
- for (const line of solves) out.push(` ${line}`);
15664
+ for (const line of solves) out2.push(` ${line}`);
15608
15665
  if (at.branch && at.branch.id !== at.leaf.id) {
15609
- for (const line of wrap(`Part of: ${at.branch.problem} ${at.branch.ref}`, width - 6)) out.push(` ${paint(ANSI.dim, line)}`);
15666
+ for (const line of wrap(`Part of: ${at.branch.problem} ${at.branch.ref}`, width - 6)) out2.push(` ${paint(ANSI.dim, line)}`);
15610
15667
  }
15611
15668
  } else {
15612
- out.push(paint(ANSI.dim, " The problem it answered is no longer on the map."));
15669
+ out2.push(paint(ANSI.dim, " The problem it answered is no longer on the map."));
15613
15670
  }
15614
15671
  const described = paragraphs(s.description);
15615
15672
  if (described.length) {
15616
15673
  heading("Proposed solution");
15617
15674
  described.forEach((p, i) => {
15618
- if (i) out.push("");
15619
- for (const line of wrap(p, width - 6)) out.push(` ${line}`);
15675
+ if (i) out2.push("");
15676
+ for (const line of wrap(p, width - 6)) out2.push(` ${line}`);
15620
15677
  });
15621
15678
  }
15622
15679
  const changelog = parseChangelog(s.changelog);
15623
15680
  if (changelog) {
15624
15681
  heading("Future changelog");
15625
- for (const line of wrap(changelog.headline, width - 6)) out.push(` ${paint(ANSI.bold, line)}`);
15626
- for (const point of changelog.points) wrap(point, width - 8).forEach((line, i) => out.push(` ${i ? " " : "\u2022"} ${line}`));
15682
+ for (const line of wrap(changelog.headline, width - 6)) out2.push(` ${paint(ANSI.bold, line)}`);
15683
+ for (const point of changelog.points) wrap(point, width - 8).forEach((line, i) => out2.push(` ${i ? " " : "\u2022"} ${line}`));
15627
15684
  }
15628
15685
  if (s.steps.length) {
15629
15686
  heading("Plan");
15630
15687
  s.steps.forEach((step, i) => {
15631
15688
  const who = STEP_WHO_WORD[step.who] ?? step.who;
15632
- wrap(step.do, width - pad.length - 8).forEach((line, j) => out.push(`${j ? pad : ` \u2610 ${who.padEnd(6)}`}${line}`));
15633
- if (step.checks) out.push(pad + paint(ANSI.amber, i === 0 ? "checks the problem first" : "checks the problem"));
15689
+ wrap(step.do, width - pad.length - 8).forEach((line, j) => out2.push(`${j ? pad : ` \u2610 ${who.padEnd(6)}`}${line}`));
15690
+ if (step.checks) out2.push(pad + paint(ANSI.amber, i === 0 ? "checks the problem first" : "checks the problem"));
15634
15691
  });
15635
15692
  }
15636
15693
  const card = at.leaf ? at.cards.find((c) => c.leaf.id === at.leaf.id) : void 0;
15637
15694
  const others = card ? card.others.filter((o) => o.id !== s.id) : [];
15638
- out.push("");
15639
- if (s.status === "rejected") out.push(paint(ANSI.dim, " Thrown out."));
15695
+ out2.push("");
15696
+ if (s.status === "rejected") out2.push(paint(ANSI.dim, " Thrown out."));
15640
15697
  else if (card?.pick.id === s.id) {
15641
15698
  const column = at.cards.filter((c) => c.horizon === card.horizon);
15642
15699
  const yours = s.placed_by === "founder" ? ", where you put it" : "";
15643
- out.push(paint(ANSI.dim, ` On the board: ${HORIZON_WORD[card.horizon]}, ${column.indexOf(card) + 1} of ${column.length}${yours}.`));
15644
- } else out.push(paint(ANSI.dim, card ? ` Not on the board: ${card.pick.ref} is this problem's card.` : " Not on the board."));
15645
- if (others.length) out.push(paint(ANSI.dim, ` Other solutions to this problem: ${others.map((o) => o.ref).join(", ")}.`));
15700
+ out2.push(paint(ANSI.dim, ` On the board: ${HORIZON_WORD[card.horizon]}, ${column.indexOf(card) + 1} of ${column.length}${yours}.`));
15701
+ } else out2.push(paint(ANSI.dim, card ? ` Not on the board: ${card.pick.ref} is this problem's card.` : " Not on the board."));
15702
+ if (others.length) out2.push(paint(ANSI.dim, ` Other solutions to this problem: ${others.map((o) => o.ref).join(", ")}.`));
15646
15703
  if (s.status === "proposed") {
15647
15704
  const move = card?.pick.id === s.id ? `nanopm roadmap move ${s.ref} now|next|later [position] \xB7 ` : "";
15648
- out.push(paint(ANSI.dim, ` ${move}nanopm reject ${s.ref} ["why"]`));
15705
+ out2.push(paint(ANSI.dim, ` ${move}nanopm reject ${s.ref} ["why"]`));
15649
15706
  }
15650
- return out.join("\n");
15707
+ return out2.join("\n");
15651
15708
  }
15652
15709
 
15653
15710
  // src/format/problems.ts
@@ -15677,23 +15734,23 @@ function renderProblems(problems2, duplicates, opts) {
15677
15734
  ].join("\n");
15678
15735
  }
15679
15736
  const refWidth = Math.max(...problems2.map((p) => p.ref.length));
15680
- const out = [];
15737
+ const out2 = [];
15681
15738
  const map = groupProblemMap(problems2.map((p) => ({ ...p, parentRef: p.parent_ref })));
15682
15739
  const ordered = [...map.branches.flatMap((b) => [b.parent, ...b.children]), ...map.unplaced];
15683
15740
  for (const problem of ordered) {
15684
15741
  const shape2 = SHAPE2[problem.status] ?? { mark: "\xB7", colour: ANSI.dim, label: problem.status };
15685
15742
  const score = renderImportance(problem, opts);
15686
15743
  const head = `${paint(shape2.colour, shape2.mark)} ${paint(ANSI.dim, problem.ref.padEnd(refWidth))} ${score} ${problem.parent_ref ? paint(ANSI.dim, "\u21B3 ") : ""}${clip(firstSentence(problem.statement), Math.max(40, columns() - refWidth - 14))}`;
15687
- out.push(head);
15688
- if (problem.mapping) out.push(paint(ANSI.dim, ` ${problem.mapping.level === 1 ? "Need" : "Difficulty"} \xB7 ${problem.mapping.knowledge} \xB7 ${problem.mapping.origin === "expert_inference" ? "expert inference" : "source analysis"}`));
15744
+ out2.push(head);
15745
+ if (problem.mapping) out2.push(paint(ANSI.dim, ` ${problem.mapping.level === 1 ? "Need" : "Difficulty"} \xB7 ${problem.mapping.knowledge} \xB7 ${problem.mapping.origin === "expert_inference" ? "expert inference" : "source analysis"}`));
15689
15746
  const indent = ` ${" ".repeat(refWidth)} `;
15690
15747
  const tail = problem.proofs > 0 ? `${problem.proofs} proof${problem.proofs === 1 ? "" : "s"}${problem.proof_kinds?.length ? ` \xB7 ${problem.proof_kinds.join(", ")}` : ""}` : problem.importance_note ? `no proof yet \xB7 ${problem.importance_note}` : "no proof yet";
15691
- out.push(paint(ANSI.dim, indent + clip(tail, Math.max(40, columns() - indent.length))));
15748
+ out2.push(paint(ANSI.dim, indent + clip(tail, Math.max(40, columns() - indent.length))));
15692
15749
  }
15693
15750
  const unproven = problems2.filter((p) => p.proofs === 0).length;
15694
15751
  if (unproven) {
15695
- out.push("");
15696
- out.push(
15752
+ out2.push("");
15753
+ out2.push(
15697
15754
  paint(
15698
15755
  ANSI.dim,
15699
15756
  `${unproven} of ${problems2.length} rest on judgement, not proof. A tilde marks those.`
@@ -15701,45 +15758,45 @@ function renderProblems(problems2, duplicates, opts) {
15701
15758
  );
15702
15759
  }
15703
15760
  if (duplicates.length) {
15704
- out.push("");
15761
+ out2.push("");
15705
15762
  for (const pair of duplicates.slice(0, 3)) {
15706
15763
  const why = pair.flagged ? `NanoPM is not sure ${pair.a} and ${pair.b} are different problems` : `${pair.a} and ${pair.b} read alike`;
15707
- out.push(paint(ANSI.amber, `? ${why}`));
15764
+ out2.push(paint(ANSI.amber, `? ${why}`));
15708
15765
  }
15709
- out.push(paint(ANSI.dim, ` nanopm decisions why \xB7 nanopm problems merge ${duplicates[0].b} ${duplicates[0].a}`));
15766
+ out2.push(paint(ANSI.dim, ` nanopm decisions why \xB7 nanopm problems merge ${duplicates[0].b} ${duplicates[0].a}`));
15710
15767
  }
15711
15768
  if (opts.manualMerges !== void 0) {
15712
- out.push("");
15713
- out.push(
15769
+ out2.push("");
15770
+ out2.push(
15714
15771
  paint(
15715
15772
  ANSI.dim,
15716
15773
  `${opts.manualMerges} merge${opts.manualMerges === 1 ? "" : "s"} you have had to make by hand.`
15717
15774
  )
15718
15775
  );
15719
15776
  }
15720
- out.push("");
15721
- out.push(paint(ANSI.dim, " nanopm problems <ref> \xB7 nanopm problems merge <from> <into> \xB7 nanopm problems weight <ref> <0-100>"));
15722
- return out.join("\n");
15777
+ out2.push("");
15778
+ out2.push(paint(ANSI.dim, " nanopm problems <ref> \xB7 nanopm problems merge <from> <into> \xB7 nanopm problems weight <ref> <0-100>"));
15779
+ return out2.join("\n");
15723
15780
  }
15724
15781
  function renderProblem(problem, evidence2, addressedBy, opts) {
15725
15782
  const paint = painter(opts.color);
15726
15783
  const shape2 = SHAPE2[problem.status] ?? { mark: "\xB7", colour: ANSI.dim, label: problem.status };
15727
15784
  const width = columns();
15728
- const out = [];
15729
- out.push(`${paint(shape2.colour, shape2.mark)} ${paint(ANSI.dim, problem.ref)} ${paint(ANSI.bold, shape2.label)}`);
15730
- out.push("");
15731
- for (const line of wrap(problem.statement, width)) out.push(line);
15732
- out.push("");
15785
+ const out2 = [];
15786
+ out2.push(`${paint(shape2.colour, shape2.mark)} ${paint(ANSI.dim, problem.ref)} ${paint(ANSI.bold, shape2.label)}`);
15787
+ out2.push("");
15788
+ for (const line of wrap(problem.statement, width)) out2.push(line);
15789
+ out2.push("");
15733
15790
  const facts = [];
15734
15791
  const m = problem.mapping ?? null;
15735
15792
  if (m) {
15736
15793
  const story = problemStory(m, { proofs: problem.proofs, kinds: problem.proof_kinds ?? [], provenance: problem.provenance ?? "inferred", authorKind: problem.author_kind ?? "nanopm", createdAt: problem.created_at ?? "" });
15737
- out.push(...wrap(story.story, width));
15738
- out.push("");
15739
- out.push(...wrap(story.doubt, width).map((l) => paint(ANSI.dim, l)));
15740
- out.push(...wrap(story.assumes ? `${story.test} ${story.assumes}` : story.test, width).map((l) => paint(ANSI.dim, l)));
15741
- out.push("");
15742
- out.push(paint(ANSI.dim, "how I got here"));
15794
+ out2.push(...wrap(story.story, width));
15795
+ out2.push("");
15796
+ out2.push(...wrap(story.doubt, width).map((l) => paint(ANSI.dim, l)));
15797
+ out2.push(...wrap(story.assumes ? `${story.test} ${story.assumes}` : story.test, width).map((l) => paint(ANSI.dim, l)));
15798
+ out2.push("");
15799
+ out2.push(paint(ANSI.dim, "how I got here"));
15743
15800
  facts.push(["kind", `${m.level === 1 ? "a need" : "a difficulty"} \xB7 ${m.knowledge} \xB7 ${m.origin === "expert_inference" ? "expert inference" : "source analysis"}`]);
15744
15801
  if (m.parent_reason) facts.push(["grouping", m.parent_reason]);
15745
15802
  }
@@ -15750,31 +15807,31 @@ function renderProblem(problem, evidence2, addressedBy, opts) {
15750
15807
  ]);
15751
15808
  if (problem.parent_ref) facts.push(["part of", problem.parent_ref]);
15752
15809
  for (const [label, value] of facts) {
15753
- out.push(`${paint(ANSI.dim, label.padEnd(9))}${clip(value, width - 9)}`);
15810
+ out2.push(`${paint(ANSI.dim, label.padEnd(9))}${clip(value, width - 9)}`);
15754
15811
  }
15755
15812
  if (evidence2.length) {
15756
- out.push("");
15757
- out.push(paint(ANSI.dim, "sources"));
15813
+ out2.push("");
15814
+ out2.push(paint(ANSI.dim, "sources"));
15758
15815
  for (const item of evidence2) {
15759
15816
  const when = (item.occurred_at ?? item.created_at).slice(0, 10);
15760
15817
  const where = item.metric_key ?? item.source_ref ?? item.kind;
15761
- for (const line of wrap(item.body, width - 2)) out.push(` ${line}`);
15762
- out.push(paint(ANSI.dim, ` \u2014 ${item.relation ?? "supports"} \xB7 ${item.kind} \xB7 ${where} \xB7 ${when}`));
15818
+ for (const line of wrap(item.body, width - 2)) out2.push(` ${line}`);
15819
+ out2.push(paint(ANSI.dim, ` \u2014 ${item.relation ?? "supports"} \xB7 ${item.kind} \xB7 ${where} \xB7 ${when}`));
15763
15820
  }
15764
15821
  } else {
15765
- out.push("");
15766
- out.push(paint(ANSI.dim, "No proof yet. This ranking is a judgement, and the next review may settle it."));
15822
+ out2.push("");
15823
+ out2.push(paint(ANSI.dim, "No proof yet. This ranking is a judgement, and the next review may settle it."));
15767
15824
  }
15768
15825
  if (addressedBy.length) {
15769
- out.push("");
15770
- out.push(paint(ANSI.dim, "addressed by"));
15826
+ out2.push("");
15827
+ out2.push(paint(ANSI.dim, "addressed by"));
15771
15828
  for (const item of addressedBy) {
15772
- out.push(` ${paint(ANSI.dim, item.ref)} ${clip(item.title, width - 12)} ${paint(ANSI.dim, `(${item.status})`)}`);
15829
+ out2.push(` ${paint(ANSI.dim, item.ref)} ${clip(item.title, width - 12)} ${paint(ANSI.dim, `(${item.status})`)}`);
15773
15830
  }
15774
15831
  }
15775
- out.push("");
15776
- out.push(paint(ANSI.dim, " nanopm problems \xB7 nanopm roadmap"));
15777
- return out.join("\n");
15832
+ out2.push("");
15833
+ out2.push(paint(ANSI.dim, " nanopm problems \xB7 nanopm roadmap"));
15834
+ return out2.join("\n");
15778
15835
  }
15779
15836
 
15780
15837
  // src/format/tree.ts
@@ -15796,49 +15853,49 @@ var NO_AREA = "no area yet";
15796
15853
  var SHOWN = /* @__PURE__ */ new Set(["proposed", "accepted", "building", "done"]);
15797
15854
  function renderTree(problems2, solutions, strategy, opts) {
15798
15855
  const paint = painter(opts.color);
15799
- const out = [];
15856
+ const out2 = [];
15800
15857
  const width = Math.max(40, columns());
15801
15858
  if (strategy) {
15802
- out.push(paint(ANSI.bold, "Strategy"));
15803
- for (const line of wrap(strategy, width - 4)) out.push(` ${line}`);
15804
- out.push("");
15859
+ out2.push(paint(ANSI.bold, "Strategy"));
15860
+ for (const line of wrap(strategy, width - 4)) out2.push(` ${line}`);
15861
+ out2.push("");
15805
15862
  }
15806
15863
  const shown = solutions.filter((s) => SHOWN.has(s.status));
15807
15864
  const open = problems2.filter((p) => p.status !== "dropped" && (!opts.alongside || shown.some((s) => s.problem_id === p.id)));
15808
15865
  if (opts.alongside && !open.length) return "";
15809
15866
  if (!open.length && !shown.length) {
15810
- out.push("No problems yet. `nanopm opportunities` shows what to work on.");
15811
- return out.join("\n");
15867
+ out2.push("No problems yet. `nanopm opportunities` shows what to work on.");
15868
+ return out2.join("\n");
15812
15869
  }
15813
15870
  const byProblem = /* @__PURE__ */ new Map();
15814
15871
  for (const s of shown) byProblem.set(s.problem_id, [...byProblem.get(s.problem_id) ?? [], s]);
15815
15872
  const refWidth = Math.max(...[...open.map((p) => p.ref), ...shown.map((s) => s.ref)].map((r) => r.length), 5);
15816
15873
  for (const [area, rows2] of groupByArea(open)) {
15817
- out.push(paint(ANSI.bold, area));
15874
+ out2.push(paint(ANSI.bold, area));
15818
15875
  for (const p of rows2) {
15819
15876
  const shape2 = PROBLEM_SHAPE[p.status] ?? { mark: "\xB7", colour: "" };
15820
15877
  const kind = p.kind === "founder" ? paint(ANSI.dim, " (founder)") : "";
15821
15878
  const proof = p.proofs ? `${p.proofs} proof${p.proofs === 1 ? "" : "s"}` : "no proof";
15822
15879
  const tail = `${renderImportance(p, opts)} ${paint(ANSI.dim, proof)}`;
15823
15880
  const head = ` ${paint(shape2.colour, shape2.mark)} ${paint(ANSI.dim, p.ref.padEnd(refWidth))} `;
15824
- out.push(head + clip(p.statement, Math.max(30, width - visibleLength(head) - visibleLength(tail) - 2)) + kind + " " + tail);
15881
+ out2.push(head + clip(p.statement, Math.max(30, width - visibleLength(head) - visibleLength(tail) - 2)) + kind + " " + tail);
15825
15882
  const under = byProblem.get(p.id) ?? [];
15826
15883
  if (!under.length) {
15827
- out.push(paint(ANSI.dim, ` ${" ".repeat(refWidth + 2)} no solution yet`));
15884
+ out2.push(paint(ANSI.dim, ` ${" ".repeat(refWidth + 2)} no solution yet`));
15828
15885
  continue;
15829
15886
  }
15830
- for (const s of under) out.push(...solutionLines(s, refWidth, width, paint));
15887
+ for (const s of under) out2.push(...solutionLines(s, refWidth, width, paint));
15831
15888
  }
15832
- out.push("");
15889
+ out2.push("");
15833
15890
  }
15834
15891
  const waiting = shown.filter((s) => s.status === "proposed").length;
15835
15892
  if (waiting) {
15836
- out.push(`${waiting} waiting on you.`);
15837
- out.push(paint(ANSI.dim, ' nanopm accept <ref> \xB7 nanopm reject <ref> "why" \xB7 nanopm roadmap <ref> \xB7 nanopm roadmap --board'));
15893
+ out2.push(`${waiting} waiting on you.`);
15894
+ out2.push(paint(ANSI.dim, ' nanopm accept <ref> \xB7 nanopm reject <ref> "why" \xB7 nanopm roadmap <ref> \xB7 nanopm roadmap --board'));
15838
15895
  } else if (!opts.alongside) {
15839
- out.push(paint(ANSI.dim, "Nothing waiting on you."));
15896
+ out2.push(paint(ANSI.dim, "Nothing waiting on you."));
15840
15897
  }
15841
- return out.join("\n").trimEnd();
15898
+ return out2.join("\n").trimEnd();
15842
15899
  }
15843
15900
  function solutionLines(s, refWidth, width, paint, indent = 6) {
15844
15901
  const shape2 = SOLUTION_SHAPE[s.status] ?? { mark: "\xB7", colour: "", label: s.status };
@@ -15870,25 +15927,25 @@ var BOARD_COLUMNS = [
15870
15927
  ];
15871
15928
  function renderBoard(problems2, solutions, opts) {
15872
15929
  const paint = painter(opts.color);
15873
- const out = [];
15930
+ const out2 = [];
15874
15931
  const byId = new Map(problems2.map((p) => [p.id, p]));
15875
15932
  const width = Math.max(40, columns());
15876
15933
  const refWidth = Math.max(...solutions.map((s) => s.ref.length), 5);
15877
15934
  for (const col of BOARD_COLUMNS) {
15878
15935
  const rows2 = solutions.filter((s) => s.status === col.status);
15879
- out.push(`${paint(ANSI.bold, col.label)} ${paint(ANSI.dim, String(rows2.length))}`);
15880
- if (!rows2.length) out.push(paint(ANSI.dim, " \u2014"));
15936
+ out2.push(`${paint(ANSI.bold, col.label)} ${paint(ANSI.dim, String(rows2.length))}`);
15937
+ if (!rows2.length) out2.push(paint(ANSI.dim, " \u2014"));
15881
15938
  for (const s of rows2) {
15882
15939
  const shape2 = SOLUTION_SHAPE[s.status];
15883
15940
  const problem = byId.get(s.problem_id);
15884
15941
  const head = ` ${paint(shape2.colour, shape2.mark)} ${paint(ANSI.dim, s.ref.padEnd(refWidth))} `;
15885
- out.push(head + clip(s.title, Math.max(30, width - visibleLength(head))));
15942
+ out2.push(head + clip(s.title, Math.max(30, width - visibleLength(head))));
15886
15943
  const under = [problem ? `${problem.ref} ${clip(problem.statement, 50)}` : "its problem was dropped", s.assumption ? clip(s.assumption, 40) : null].filter(Boolean).join(" \xB7 ");
15887
- out.push(paint(ANSI.dim, ` ${" ".repeat(refWidth + 2)} ${under}`));
15944
+ out2.push(paint(ANSI.dim, ` ${" ".repeat(refWidth + 2)} ${under}`));
15888
15945
  }
15889
- out.push("");
15946
+ out2.push("");
15890
15947
  }
15891
- return out.join("\n").trimEnd();
15948
+ return out2.join("\n").trimEnd();
15892
15949
  }
15893
15950
 
15894
15951
  // src/commands/roadmap.ts
@@ -16067,9 +16124,9 @@ function normalizeRef(input, items) {
16067
16124
  }
16068
16125
 
16069
16126
  // src/commands/build.ts
16070
- var execFileAsync9 = promisify9(execFile10);
16127
+ var execFileAsync9 = promisify10(execFile11);
16071
16128
  async function build(ref, opts) {
16072
- const { renderer, out } = rendererFor(opts.out);
16129
+ const { renderer, out: out2 } = rendererFor(opts.out);
16073
16130
  const auth = await authenticate();
16074
16131
  const project = await resolveProject(auth.client, { projectId: opts.projectId, cwd: opts.cwd });
16075
16132
  const repo = await detectRepo(opts.cwd);
@@ -16079,9 +16136,9 @@ async function build(ref, opts) {
16079
16136
  const solution = (solutions ?? []).find((s) => s.ref === wanted);
16080
16137
  if (!solution) throw new Error(`No solution ${wanted} on the Roadmap.`);
16081
16138
  const stream = async (jobId2) => {
16082
- out(`${solution.ref} ${solution.title}`);
16083
- const answerer = opts.answersFile ? await answersFromFile(opts.answersFile) : answersFromTerminal(out);
16084
- return streamJob(auth.client, jobId2, { pollMs: 700, out, renderer, cancelSignal: opts.cancelSignal, answerer, verbose: verboseWanted() });
16139
+ out2(`${solution.ref} ${solution.title}`);
16140
+ const answerer = opts.answersFile ? await answersFromFile(opts.answersFile) : answersFromTerminal(out2);
16141
+ return streamJob(auth.client, jobId2, { pollMs: 700, out: out2, renderer, cancelSignal: opts.cancelSignal, answerer, verbose: verboseWanted() });
16085
16142
  };
16086
16143
  const live = await buildOf(auth.client, project.id, [...ACTIVE_STATUSES]);
16087
16144
  if (live) {
@@ -16091,7 +16148,7 @@ async function build(ref, opts) {
16091
16148
  }
16092
16149
  const stopped = opts.continue || opts.throwAway ? await buildOf(auth.client, project.id, ["failed", "cancelled"], solution.id) : null;
16093
16150
  if ((opts.continue || opts.throwAway) && !stopped) throw new Error(`${solution.ref} has no stopped build to ${opts.continue ? "continue" : "throw away"}.`);
16094
- if (opts.throwAway) return throwAway(auth.client, stopped, repo.root, out);
16151
+ if (opts.throwAway) return throwAway(auth.client, stopped, repo.root, out2);
16095
16152
  const [daemon, local] = await Promise.all([awakeDaemon(auth.client, auth.userId), localDaemonId()]);
16096
16153
  if (!local || daemon?.id !== local) throw new Error("This machine\u2019s daemon is asleep, so nothing was queued. `nanopm daemon install` starts it; then build again.");
16097
16154
  await rememberClone(auth.client, project.id, repo.root);
@@ -16113,13 +16170,13 @@ async function buildOf(client, projectId, statuses, solutionId) {
16113
16170
  const params = data?.params ?? {};
16114
16171
  return data ? { id: data.id, solutionId: params.solution_id ?? "", was: wasOf(data.params), origin: params.origin ?? data.id } : null;
16115
16172
  }
16116
- async function throwAway(client, build2, clone, out) {
16173
+ async function throwAway(client, build2, clone, out2) {
16117
16174
  const { place } = await loadBuildHistory(client, [build2.origin], { home: homedir6(), jobId: build2.origin });
16118
16175
  if (!place) throw new Error("that build left nothing on this machine.");
16119
16176
  if (existsSync7(place.dir)) await execFileAsync9("git", ["-C", clone, "worktree", "remove", "--force", place.dir]);
16120
16177
  await execFileAsync9("git", ["-C", clone, "branch", "-D", place.branch]).catch(() => void 0);
16121
16178
  await appendJobEvent(client, build2.id, "thrown", {}, "user");
16122
- out(`Thrown away: the copy of the code and the local branch ${place.branch}.`);
16179
+ out2(`Thrown away: the copy of the code and the local branch ${place.branch}.`);
16123
16180
  return 0;
16124
16181
  }
16125
16182
 
@@ -16138,7 +16195,7 @@ var POSTGRES_KEYS = [
16138
16195
  var POSTHOG_KEYS = ["POSTHOG_API_KEY", "POSTHOG_PERSONAL_API_KEY", "NEXT_PUBLIC_POSTHOG_KEY"];
16139
16196
  var AMPLITUDE_KEYS = ["AMPLITUDE_API_KEY", "AMPLITUDE_SECRET_KEY", "NEXT_PUBLIC_AMPLITUDE_KEY"];
16140
16197
  function parseEnv(text2) {
16141
- const out = {};
16198
+ const out2 = {};
16142
16199
  for (const raw of text2.split("\n")) {
16143
16200
  const line = raw.trim();
16144
16201
  if (!line || line.startsWith("#")) continue;
@@ -16148,9 +16205,9 @@ function parseEnv(text2) {
16148
16205
  if (value.startsWith('"') && value.endsWith('"') || value.startsWith("'") && value.endsWith("'")) {
16149
16206
  value = value.slice(1, -1);
16150
16207
  }
16151
- if (value) out[match[1]] = value;
16208
+ if (value) out2[match[1]] = value;
16152
16209
  }
16153
- return out;
16210
+ return out2;
16154
16211
  }
16155
16212
  var PLACEHOLDER = /<|your-|xxxx/i;
16156
16213
  function detectInEnv(env, file) {
@@ -16172,15 +16229,15 @@ function detectInEnv(env, file) {
16172
16229
  return found;
16173
16230
  }
16174
16231
  async function envFiles(repoRoot) {
16175
- const out = [];
16232
+ const out2 = [];
16176
16233
  for (const file of ENV_FILES) {
16177
16234
  try {
16178
- out.push({ file, env: parseEnv(await readFile15(join17(repoRoot, file), "utf8")) });
16235
+ out2.push({ file, env: parseEnv(await readFile15(join17(repoRoot, file), "utf8")) });
16179
16236
  } catch {
16180
16237
  continue;
16181
16238
  }
16182
16239
  }
16183
- return out;
16240
+ return out2;
16184
16241
  }
16185
16242
  async function detectSources(repoRoot) {
16186
16243
  const found = [];
@@ -16266,7 +16323,7 @@ async function start(sentence2, opts) {
16266
16323
  return startProject(text2, { ...opts, repo });
16267
16324
  }
16268
16325
  async function startProject(text2, opts) {
16269
- const { renderer, out } = rendererFor(opts.out);
16326
+ const { renderer, out: out2 } = rendererFor(opts.out);
16270
16327
  const auth = await authenticate();
16271
16328
  const daemonId = await localDaemonId();
16272
16329
  if (!daemonId) throw new Error("no daemon on this machine yet; `nanopm login` starts one (or `nanopm daemon install`)");
@@ -16282,24 +16339,24 @@ async function startProject(text2, opts) {
16282
16339
  const { data, error: error2 } = await auth.client.from("projects").insert({ name, slug: uniqueSlug(name, (mine ?? []).map((p) => p.slug)), repo_remote: opts.repo?.remote ?? null, created_by: auth.userId }).select("id, name, slug").single();
16283
16340
  if (error2 || !data) throw new Error(`could not create the project: ${error2?.message}`);
16284
16341
  project = data;
16285
- out(`created project ${project.name} (${project.id})${opts.repo ? "" : ", with no repository yet"}`);
16342
+ out2(`created project ${project.name} (${project.id})${opts.repo ? "" : ", with no repository yet"}`);
16286
16343
  }
16287
16344
  await auth.client.from("project_members").update({ daemon_id: daemonId }).eq("project_id", project.id).eq("user_id", auth.userId);
16288
16345
  if (opts.repo) await rememberClone(auth.client, project.id, opts.repo.root);
16289
16346
  const { data: job, error } = await auth.client.from("jobs").insert({ project_id: project.id, skill: "start", params: { sentence: text2 }, requested_by: auth.userId, target_daemon_id: daemonId }).select("id").single();
16290
16347
  if (error || !job) throw new Error(`could not queue the conversation: ${error?.message}`);
16291
- out(`queued start as job ${job.id}`);
16348
+ out2(`queued start as job ${job.id}`);
16292
16349
  const link = newProjectUrl(job.id);
16293
- out(`Follow along and answer at ${link}`);
16350
+ out2(`Follow along and answer at ${link}`);
16294
16351
  if (!opts.noBrowser) openBrowser(link);
16295
- const answerer = opts.answersFile ? await answersFromFile(opts.answersFile) : answersFromTerminal(out);
16296
- const exit = await streamJob(auth.client, job.id, { pollMs: 1e3, out, renderer, cancelSignal: opts.cancelSignal, answerer, link, verbose: verboseWanted() });
16297
- for (const line of closingLines(project, exit, process.env, opts.repo ? "onboard" : "start")) out(line);
16352
+ const answerer = opts.answersFile ? await answersFromFile(opts.answersFile) : answersFromTerminal(out2);
16353
+ const exit = await streamJob(auth.client, job.id, { pollMs: 1e3, out: out2, renderer, cancelSignal: opts.cancelSignal, answerer, link, verbose: verboseWanted() });
16354
+ for (const line of closingLines(project, exit, process.env, opts.repo ? "onboard" : "start")) out2(line);
16298
16355
  return exit;
16299
16356
  }
16300
- async function askSentence(out) {
16357
+ async function askSentence(out2) {
16301
16358
  if (!process.stdin.isTTY) return null;
16302
- out("Tell me about it in a sentence: what are you building, and for whom?");
16359
+ out2("Tell me about it in a sentence: what are you building, and for whom?");
16303
16360
  const rl = createInterface3({ input: process.stdin, output: process.stdout });
16304
16361
  try {
16305
16362
  return (await rl.question("> ")).trim() || null;
@@ -16310,17 +16367,17 @@ async function askSentence(out) {
16310
16367
 
16311
16368
  // src/commands/onboard.ts
16312
16369
  async function onboard(opts) {
16313
- const { renderer, out } = rendererFor(opts.out);
16370
+ const { renderer, out: out2 } = rendererFor(opts.out);
16314
16371
  const repo = await detectRepo(opts.cwd);
16315
16372
  if (!repo) throw new Error('run this inside a git repository, or nanopm start "<sentence>" for a project with no code yet');
16316
16373
  const auth = await authenticate();
16317
16374
  const daemonId = await localDaemonId();
16318
16375
  if (!daemonId) throw new Error("no daemon on this machine yet; `nanopm login` starts one (or `nanopm daemon install`)");
16319
- let project = opts.projectId ? await attach(auth.client, opts.projectId, repo, out) : repo.remote ? (await auth.client.from("projects").select("id, name, slug").eq("repo_remote", repo.remote).limit(1).maybeSingle()).data : null;
16376
+ let project = opts.projectId ? await attach(auth.client, opts.projectId, repo, out2) : repo.remote ? (await auth.client.from("projects").select("id, name, slug").eq("repo_remote", repo.remote).limit(1).maybeSingle()).data : null;
16320
16377
  if (repo.empty) {
16321
16378
  if (opts.scanOnly) throw new Error('nothing to read here yet; nanopm start "<sentence>" holds the conversation instead');
16322
- out("Nothing to read here yet.");
16323
- const sentence2 = opts.sentence ?? await askSentence(out);
16379
+ out2("Nothing to read here yet.");
16380
+ const sentence2 = opts.sentence ?? await askSentence(out2);
16324
16381
  if (!sentence2) throw new Error('nothing to read here yet: say what you are building with nanopm start "<sentence>", in this folder, so the repository is recorded');
16325
16382
  return startProject(startSentence(sentence2), { ...opts, repo, existing: project ?? void 0 });
16326
16383
  }
@@ -16329,9 +16386,9 @@ async function onboard(opts) {
16329
16386
  const { data, error: error2 } = await auth.client.from("projects").insert({ name, slug: slugify(name), repo_remote: repo.remote, created_by: auth.userId }).select("id, name, slug").single();
16330
16387
  if (error2 || !data) throw new Error(`could not create the project: ${error2?.message}`);
16331
16388
  project = data;
16332
- out(`created project ${project.name} (${project.id})`);
16389
+ out2(`created project ${project.name} (${project.id})`);
16333
16390
  } else {
16334
- out(`project ${project.name} (${project.id}) exists; refreshing its memory`);
16391
+ out2(`project ${project.name} (${project.id}) exists; refreshing its memory`);
16335
16392
  }
16336
16393
  await auth.client.from("project_members").update({ daemon_id: daemonId }).eq("project_id", project.id).eq("user_id", auth.userId);
16337
16394
  await rememberClone(auth.client, project.id, repo.root);
@@ -16355,18 +16412,18 @@ async function onboard(opts) {
16355
16412
  target_daemon_id: daemonId
16356
16413
  }).select("id").single();
16357
16414
  if (error || !job) throw new Error(`could not queue onboarding: ${error?.message}`);
16358
- out(`queued onboard as job ${job.id}`);
16415
+ out2(`queued onboard as job ${job.id}`);
16359
16416
  const link = newProjectUrl(job.id);
16360
16417
  if (!opts.scanOnly) {
16361
- out(`Follow along, then confirm what I read at ${link}`);
16418
+ out2(`Follow along, then confirm what I read at ${link}`);
16362
16419
  if (!opts.noBrowser) openBrowser(link);
16363
16420
  }
16364
- const answerer = opts.answersFile ? await answersFromFile(opts.answersFile) : answersFromTerminal(out);
16365
- const exit = await streamJob(auth.client, job.id, { pollMs: 1e3, out, renderer, cancelSignal: opts.cancelSignal, answerer, link, verbose: verboseWanted() });
16366
- if (!opts.scanOnly) for (const line of closingLines(project, exit)) out(line);
16421
+ const answerer = opts.answersFile ? await answersFromFile(opts.answersFile) : answersFromTerminal(out2);
16422
+ const exit = await streamJob(auth.client, job.id, { pollMs: 1e3, out: out2, renderer, cancelSignal: opts.cancelSignal, answerer, link, verbose: verboseWanted() });
16423
+ if (!opts.scanOnly) for (const line of closingLines(project, exit)) out2(line);
16367
16424
  return exit;
16368
16425
  }
16369
- async function attach(client, projectId, repo, out) {
16426
+ async function attach(client, projectId, repo, out2) {
16370
16427
  const { data: project } = await client.from("projects").select("id, name, slug, repo_remote").eq("id", projectId).maybeSingle();
16371
16428
  if (!project) throw new Error(`project ${projectId} not found (or you are not a member)`);
16372
16429
  if (repo.remote && project.repo_remote && project.repo_remote !== repo.remote) {
@@ -16377,7 +16434,7 @@ async function attach(client, projectId, repo, out) {
16377
16434
  if (taken) throw new Error(`${repo.remote} is already the repository of ${taken.name}`);
16378
16435
  const { data: updated } = await client.from("projects").update({ repo_remote: repo.remote }).eq("id", project.id).select("id").maybeSingle();
16379
16436
  if (!updated) throw new Error(`only the owner of ${project.name} can attach its repository`);
16380
- out(`${project.name} now lives at ${repo.remote}`);
16437
+ out2(`${project.name} now lives at ${repo.remote}`);
16381
16438
  }
16382
16439
  return { id: project.id, name: project.name, slug: project.slug };
16383
16440
  }
@@ -16417,7 +16474,7 @@ async function want(sentence2, opts) {
16417
16474
 
16418
16475
  // src/commands/resume.ts
16419
16476
  async function resume(jobId, opts) {
16420
- const { renderer, out } = rendererFor(opts.out);
16477
+ const { renderer, out: out2 } = rendererFor(opts.out);
16421
16478
  const auth = await authenticate();
16422
16479
  const { data: original } = await auth.client.from("jobs").select("id, project_id, skill, params, status, attempt, harness_session_id, error").eq("id", jobId).maybeSingle();
16423
16480
  if (!original) throw new Error(`job ${jobId} not found`);
@@ -16435,9 +16492,9 @@ async function resume(jobId, opts) {
16435
16492
  }).select("id").single();
16436
16493
  if (error || !job) throw new Error(`could not queue the resume: ${error?.message}`);
16437
16494
  const code = original.error?.code ?? "failed";
16438
- out(`resuming ${original.skill} (${code}) as job ${job.id}, attempt ${original.attempt + 1}`);
16439
- const answerer = opts.answersFile ? await answersFromFile(opts.answersFile) : answersFromTerminal(out);
16440
- return streamJob(auth.client, job.id, { pollMs: 1e3, out, renderer, cancelSignal: opts.cancelSignal, answerer, verbose: verboseWanted() });
16495
+ out2(`resuming ${original.skill} (${code}) as job ${job.id}, attempt ${original.attempt + 1}`);
16496
+ const answerer = opts.answersFile ? await answersFromFile(opts.answersFile) : answersFromTerminal(out2);
16497
+ return streamJob(auth.client, job.id, { pollMs: 1e3, out: out2, renderer, cancelSignal: opts.cancelSignal, answerer, verbose: verboseWanted() });
16441
16498
  }
16442
16499
 
16443
16500
  // src/commands/connect.ts
@@ -16488,7 +16545,7 @@ async function connect2(opts) {
16488
16545
  const paint = painter(color);
16489
16546
  const auth = await authenticate();
16490
16547
  const project = await resolveProject(auth.client, { projectId: opts.projectId, cwd: opts.cwd });
16491
- const out = [];
16548
+ const out2 = [];
16492
16549
  const index = { i: 0 };
16493
16550
  let url = opts.url;
16494
16551
  let label = opts.label;
@@ -16501,31 +16558,31 @@ async function connect2(opts) {
16501
16558
  if (!url) {
16502
16559
  const databases = found.filter((f) => f.kind === "postgres");
16503
16560
  const analytics = found.filter((f) => f.kind !== "postgres");
16504
- out.push(whereItGoes(color));
16561
+ out2.push(whereItGoes(color));
16505
16562
  if (databases.length) {
16506
16563
  const first = databases[0];
16507
- out.push(`Found ${paint(ANSI.bold, first.from)} in ${first.file}:`);
16508
- out.push(` ${mask(first.value)}`);
16509
- out.push("");
16510
- out.push(paint(ANSI.dim, "That is probably the owner role, which this command will refuse. Paste the"));
16511
- out.push(paint(ANSI.dim, "read-only one instead, or press enter to try this one and see."));
16512
- out.push("");
16513
- process.stdout.write(`${out.join("\n")}
16564
+ out2.push(`Found ${paint(ANSI.bold, first.from)} in ${first.file}:`);
16565
+ out2.push(` ${mask(first.value)}`);
16566
+ out2.push("");
16567
+ out2.push(paint(ANSI.dim, "That is probably the owner role, which this command will refuse. Paste the"));
16568
+ out2.push(paint(ANSI.dim, "read-only one instead, or press enter to try this one and see."));
16569
+ out2.push("");
16570
+ process.stdout.write(`${out2.join("\n")}
16514
16571
  `);
16515
- out.length = 0;
16572
+ out2.length = 0;
16516
16573
  const answer = await ask5("Connection string (enter to use the one above): ", opts.answers, index);
16517
16574
  url = answer || first.value;
16518
16575
  } else {
16519
- process.stdout.write(`${out.join("\n")}
16576
+ process.stdout.write(`${out2.join("\n")}
16520
16577
  `);
16521
- out.length = 0;
16578
+ out2.length = 0;
16522
16579
  url = await ask5("Read-only Postgres connection string: ", opts.answers, index);
16523
16580
  }
16524
16581
  if (analytics.length) {
16525
16582
  const kinds = new Set(analytics.map((a) => a.kind));
16526
- if (kinds.has("posthog")) out.push(paint(ANSI.dim, "Also saw posthog in your env files. `nanopm connect --kind posthog` reads it."));
16527
- if (kinds.has("amplitude")) out.push(paint(ANSI.dim, "Also saw amplitude in your env files. NanoPM cannot read that yet."));
16528
- out.push("");
16583
+ if (kinds.has("posthog")) out2.push(paint(ANSI.dim, "Also saw posthog in your env files. `nanopm connect --kind posthog` reads it."));
16584
+ if (kinds.has("amplitude")) out2.push(paint(ANSI.dim, "Also saw amplitude in your env files. NanoPM cannot read that yet."));
16585
+ out2.push("");
16529
16586
  }
16530
16587
  }
16531
16588
  if (!url) throw new Error("nothing to connect. Run it again with a read-only connection string.");
@@ -16551,18 +16608,18 @@ ${refusalForWritableRole(url, color)}`);
16551
16608
  });
16552
16609
  if (error) throw new Error(`could not store the connection: ${pgMessage(error)}`);
16553
16610
  await auth.client.rpc("set_connection_status", { p_connection_id: connectionId, p_status: "ok" });
16554
- out.push(paint(ANSI.green, `\u2713 connected ${label}`));
16555
- out.push(paint(ANSI.dim, ` ${test.serverVersion ?? "postgres"} \xB7 read-only \xB7 ${describeUrl(url)}`));
16556
- out.push("");
16611
+ out2.push(paint(ANSI.green, `\u2713 connected ${label}`));
16612
+ out2.push(paint(ANSI.dim, ` ${test.serverVersion ?? "postgres"} \xB7 read-only \xB7 ${describeUrl(url)}`));
16613
+ out2.push("");
16557
16614
  if (test.blindness) {
16558
16615
  const role7 = parseConnectionUrl(url)?.user.split(".")[0] ?? "the role";
16559
- out.push(paint(ANSI.amber, test.blindness.replace("${the role}", role7)));
16560
- out.push("");
16561
- out.push(paint(ANSI.dim, " nanopm review will run, and will have almost nothing to read."));
16562
- out.push("");
16616
+ out2.push(paint(ANSI.amber, test.blindness.replace("${the role}", role7)));
16617
+ out2.push("");
16618
+ out2.push(paint(ANSI.dim, " nanopm review will run, and will have almost nothing to read."));
16619
+ out2.push("");
16563
16620
  }
16564
- out.push(paint(ANSI.dim, " nanopm review \xB7 nanopm connect --list"));
16565
- return out.join("\n");
16621
+ out2.push(paint(ANSI.dim, " nanopm review \xB7 nanopm connect --list"));
16622
+ return out2.join("\n");
16566
16623
  }
16567
16624
  function soleKind(found) {
16568
16625
  const kinds = [...new Set(found.map((f) => f.kind))];
@@ -16694,18 +16751,18 @@ async function sources(opts) {
16694
16751
  ok: { sign: "\u25CF", colour: ANSI.green },
16695
16752
  failed: { sign: "\u2298", colour: ANSI.red }
16696
16753
  };
16697
- const out = [];
16754
+ const out2 = [];
16698
16755
  for (const s of rows2) {
16699
16756
  const m = mark2[s.status] ?? { sign: "\xB7", colour: ANSI.dim };
16700
16757
  const watches2 = watchesOf(s);
16701
- out.push(`${paint(m.colour, m.sign)} ${paint(ANSI.bold, s.label)} ${paint(ANSI.dim, `${s.kind.replace("_watch", "")} \xB7 ${watches2.length} watch${watches2.length === 1 ? "" : "es"}`)}`);
16702
- out.push(paint(ANSI.dim, ` ${describeCadence(s)} ${s.timezone} \xB7 next ${describeNextRun(s, /* @__PURE__ */ new Date(), { timezone: false })}`));
16703
- out.push(paint(ANSI.dim, ` last run ${ago(s.last_run_at)}${s.last_run_at ? ` \xB7 ${describeStats(s.last_run_stats)}` : ""}`));
16704
- if (s.status === "failed" && s.status_detail) out.push(paint(ANSI.red, ` ${s.status_detail}`));
16758
+ out2.push(`${paint(m.colour, m.sign)} ${paint(ANSI.bold, s.label)} ${paint(ANSI.dim, `${s.kind.replace("_watch", "")} \xB7 ${watches2.length} watch${watches2.length === 1 ? "" : "es"}`)}`);
16759
+ out2.push(paint(ANSI.dim, ` ${describeCadence(s)} ${s.timezone} \xB7 next ${describeNextRun(s, /* @__PURE__ */ new Date(), { timezone: false })}`));
16760
+ out2.push(paint(ANSI.dim, ` last run ${ago(s.last_run_at)}${s.last_run_at ? ` \xB7 ${describeStats(s.last_run_stats)}` : ""}`));
16761
+ if (s.status === "failed" && s.status_detail) out2.push(paint(ANSI.red, ` ${s.status_detail}`));
16705
16762
  }
16706
- out.push("");
16707
- out.push(paint(ANSI.dim, " nanopm watch \xB7 nanopm source run <label> \xB7 nanopm evidence"));
16708
- return out.join("\n");
16763
+ out2.push("");
16764
+ out2.push(paint(ANSI.dim, " nanopm watch \xB7 nanopm source run <label> \xB7 nanopm evidence"));
16765
+ return out2.join("\n");
16709
16766
  }
16710
16767
  function describeStats(stats) {
16711
16768
  const s = stats ?? {};
@@ -16740,7 +16797,7 @@ async function addSource(kindArg, opts) {
16740
16797
  const index = { i: 0 };
16741
16798
  const connection = await pickConnection(auth.client, project.id, spec2.connection, opts, index);
16742
16799
  const label = opts.label || `${spec2.label} watches`;
16743
- const out = [];
16800
+ const out2 = [];
16744
16801
  let watches2 = [];
16745
16802
  if (opts.fromReview) {
16746
16803
  const rows2 = await lastReviewMetrics(auth.client, project.id);
@@ -16757,15 +16814,15 @@ async function addSource(kindArg, opts) {
16757
16814
  const tried = await seedWatches(seeded, read6);
16758
16815
  watches2 = tried.kept;
16759
16816
  for (const skip of tried.skipped) {
16760
- out.push(paint(ANSI.amber, `skipped ${skip.watch.id}: ${skip.why}`));
16761
- out.push(paint(ANSI.dim, ` ${clipQuery(skip.watch.query)}`));
16817
+ out2.push(paint(ANSI.amber, `skipped ${skip.watch.id}: ${skip.why}`));
16818
+ out2.push(paint(ANSI.dim, ` ${clipQuery(skip.watch.query)}`));
16762
16819
  }
16763
16820
  if (tried.skipped.length) {
16764
- out.push(paint(ANSI.dim, " A watch reads one row with one number. Name it: `select count(*) \u2026 as value`."));
16765
- out.push("");
16821
+ out2.push(paint(ANSI.dim, " A watch reads one row with one number. Name it: `select count(*) \u2026 as value`."));
16822
+ out2.push("");
16766
16823
  }
16767
16824
  if (!watches2.length) {
16768
- throw new Error([...out, "None of those queries returns one number, so there is nothing to watch yet."].join("\n"));
16825
+ throw new Error([...out2, "None of those queries returns one number, so there is nothing to watch yet."].join("\n"));
16769
16826
  }
16770
16827
  }
16771
16828
  const { error } = await auth.client.rpc("put_source", {
@@ -16780,28 +16837,28 @@ async function addSource(kindArg, opts) {
16780
16837
  p_timezone: timezone
16781
16838
  });
16782
16839
  if (error) throw new Error(`could not store the source: ${pgMessage(error)}`);
16783
- out.push(paint(ANSI.green, `\u2713 watching ${label}`));
16784
- out.push(paint(ANSI.dim, ` ${connection.label} \xB7 ${watches2.length} watch${watches2.length === 1 ? "" : "es"} \xB7 ${describeCadence({ cadence, day_of_week: day2 ?? null, hour, timezone, next_due_at: (/* @__PURE__ */ new Date()).toISOString() })} ${timezone}`));
16785
- out.push("");
16786
- out.push("The first run writes down where every number stands and raises nothing. It takes a");
16787
- out.push("second reading for a rule to have something to compare against.");
16788
- out.push("");
16789
- out.push(paint(ANSI.dim, ` nanopm source run ${label} \xB7 nanopm watch ${label} \xB7 nanopm watch add ${label} <id> --query "\u2026"`));
16790
- return out.join("\n");
16840
+ out2.push(paint(ANSI.green, `\u2713 watching ${label}`));
16841
+ out2.push(paint(ANSI.dim, ` ${connection.label} \xB7 ${watches2.length} watch${watches2.length === 1 ? "" : "es"} \xB7 ${describeCadence({ cadence, day_of_week: day2 ?? null, hour, timezone, next_due_at: (/* @__PURE__ */ new Date()).toISOString() })} ${timezone}`));
16842
+ out2.push("");
16843
+ out2.push("The first run writes down where every number stands and raises nothing. It takes a");
16844
+ out2.push("second reading for a rule to have something to compare against.");
16845
+ out2.push("");
16846
+ out2.push(paint(ANSI.dim, ` nanopm source run ${label} \xB7 nanopm watch ${label} \xB7 nanopm watch add ${label} <id> --query "\u2026"`));
16847
+ return out2.join("\n");
16791
16848
  }
16792
16849
  async function runSource(labelOrId, opts) {
16793
- const { renderer, out } = rendererFor(opts.out);
16850
+ const { renderer, out: out2 } = rendererFor(opts.out);
16794
16851
  const auth = await authenticate();
16795
16852
  const project = await resolveProject(auth.client, { projectId: opts.projectId, cwd: opts.cwd });
16796
16853
  const source = findSource(await liveSources(auth.client, project.id), labelOrId);
16797
16854
  const { data: jobId, error } = await auth.client.rpc("enqueue_source_run", { p_source_id: source.id, p_trigger: "user" });
16798
16855
  if (error) throw new Error(`could not queue the run: ${pgMessage(error)}`);
16799
16856
  if (!jobId) {
16800
- out(`the map is being worked out, so ${source.label} was not read. It stays due; it runs when the map lands.`);
16857
+ out2(`the map is being worked out, so ${source.label} was not read. It stays due; it runs when the map lands.`);
16801
16858
  return 0;
16802
16859
  }
16803
- out(`reading ${source.label} as job ${jobId}`);
16804
- return streamJob(auth.client, jobId, { pollMs: 1e3, out, renderer, cancelSignal: opts.cancelSignal, verbose: verboseWanted() });
16860
+ out2(`reading ${source.label} as job ${jobId}`);
16861
+ return streamJob(auth.client, jobId, { pollMs: 1e3, out: out2, renderer, cancelSignal: opts.cancelSignal, verbose: verboseWanted() });
16805
16862
  }
16806
16863
  async function removeSource(labelOrId, opts) {
16807
16864
  const auth = await authenticate();
@@ -16963,13 +17020,13 @@ async function watches(labelOrId, opts) {
16963
17020
  paint(ANSI.dim, " nanopm source add postgres --from-review")
16964
17021
  ].join("\n");
16965
17022
  }
16966
- const out = [];
17023
+ const out2 = [];
16967
17024
  for (const source of rows2) {
16968
17025
  const list = watchesOf(source);
16969
- out.push(paint(ANSI.bold, source.label));
17026
+ out2.push(paint(ANSI.bold, source.label));
16970
17027
  if (!list.length) {
16971
- out.push(paint(ANSI.dim, ` nothing watched yet \xB7 nanopm watch add ${source.label} <id> --query "\u2026"`));
16972
- out.push("");
17028
+ out2.push(paint(ANSI.dim, ` nothing watched yet \xB7 nanopm watch add ${source.label} <id> --query "\u2026"`));
17029
+ out2.push("");
16973
17030
  continue;
16974
17031
  }
16975
17032
  const width = Math.max(...list.map((w) => w.id.length));
@@ -16978,13 +17035,13 @@ async function watches(labelOrId, opts) {
16978
17035
  const last = state.observations?.[state.observations.length - 1];
16979
17036
  const open = state.open?.since ? paint(ANSI.amber, ` open since ${ago(state.open.since)}`) : "";
16980
17037
  const reading = last?.value === void 0 ? paint(ANSI.dim, "not read yet") : `${last.value}${watch.unit ? ` ${watch.unit}` : ""}${last.at ? paint(ANSI.dim, ` (${ago(last.at)})`) : ""}`;
16981
- out.push(` ${paint(ANSI.dim, watch.id.padEnd(width))} ${watch.label}`);
16982
- out.push(` ${" ".repeat(width)} ${paint(ANSI.dim, describeRule(watch.rule))} \xB7 ${reading}${open}`);
17038
+ out2.push(` ${paint(ANSI.dim, watch.id.padEnd(width))} ${watch.label}`);
17039
+ out2.push(` ${" ".repeat(width)} ${paint(ANSI.dim, describeRule(watch.rule))} \xB7 ${reading}${open}`);
16983
17040
  }
16984
- out.push("");
17041
+ out2.push("");
16985
17042
  }
16986
- out.push(paint(ANSI.dim, ' nanopm watch add <source> <id> --query "\u2026" \xB7 nanopm watch remove <source> <id>'));
16987
- return out.join("\n");
17043
+ out2.push(paint(ANSI.dim, ' nanopm watch add <source> <id> --query "\u2026" \xB7 nanopm watch remove <source> <id>'));
17044
+ return out2.join("\n");
16988
17045
  }
16989
17046
  function watchInput(id, query, opts) {
16990
17047
  const text2 = (query ?? "").trim();
@@ -17109,16 +17166,16 @@ async function evidence(opts) {
17109
17166
  paint(ANSI.dim, sources2.length ? " nanopm source \xB7 nanopm watch" : " nanopm source add postgres --from-review")
17110
17167
  ].join("\n");
17111
17168
  }
17112
- const out = [];
17169
+ const out2 = [];
17113
17170
  for (const row of rows2) {
17114
17171
  const when = (row.occurred_at ?? row.created_at).slice(0, 10);
17115
17172
  const where = row.source_id ? labelById.get(row.source_id) ?? "a removed feed" : "";
17116
- out.push(`${paint(ANSI.dim, when)} ${bars(row.strength, color)} ${clip(row.body, 72)}`);
17117
- out.push(paint(ANSI.dim, ` ${row.kind}${where ? ` \xB7 ${where}` : ""}${row.source_ref ? ` \xB7 ${row.source_ref}` : ""}`));
17173
+ out2.push(`${paint(ANSI.dim, when)} ${bars(row.strength, color)} ${clip(row.body, 72)}`);
17174
+ out2.push(paint(ANSI.dim, ` ${row.kind}${where ? ` \xB7 ${where}` : ""}${row.source_ref ? ` \xB7 ${row.source_ref}` : ""}`));
17118
17175
  }
17119
- out.push("");
17120
- out.push(paint(ANSI.dim, ` ${rows2.length} of the newest first \xB7 nanopm evidence --unclaimed \xB7 nanopm problems`));
17121
- return out.join("\n");
17176
+ out2.push("");
17177
+ out2.push(paint(ANSI.dim, ` ${rows2.length} of the newest first \xB7 nanopm evidence --unclaimed \xB7 nanopm problems`));
17178
+ return out2.join("\n");
17122
17179
  }
17123
17180
  function bars(strength, color) {
17124
17181
  const paint = painter(color);
@@ -17158,25 +17215,25 @@ function renderReview(review2, metrics2, opts) {
17158
17215
  if (!review2) {
17159
17216
  return ["No review yet.", "", paint(ANSI.dim, " nanopm connect \xB7 nanopm review")].join("\n");
17160
17217
  }
17161
- const out = [];
17218
+ const out2 = [];
17162
17219
  const width = columns();
17163
- out.push(paint(ANSI.dim, `Reviewed ${review2.created_at.slice(0, 10)}`));
17164
- out.push("");
17165
- for (const line of wrap(review2.summary, width)) out.push(line);
17166
- out.push("");
17220
+ out2.push(paint(ANSI.dim, `Reviewed ${review2.created_at.slice(0, 10)}`));
17221
+ out2.push("");
17222
+ for (const line of wrap(review2.summary, width)) out2.push(line);
17223
+ out2.push("");
17167
17224
  if (metrics2.length) {
17168
- out.push(renderMetrics(metrics2, opts).split("\n\n")[0]);
17169
- out.push("");
17225
+ out2.push(renderMetrics(metrics2, opts).split("\n\n")[0]);
17226
+ out2.push("");
17170
17227
  }
17171
17228
  for (const item of review2.confirms) {
17172
- out.push(`${paint(ANSI.green, "\u2713")} ${wrap(item, width - 2).join("\n ")}`);
17229
+ out2.push(`${paint(ANSI.green, "\u2713")} ${wrap(item, width - 2).join("\n ")}`);
17173
17230
  }
17174
17231
  for (const item of review2.contradicts) {
17175
- out.push(`${paint(ANSI.amber, "\u2717")} ${wrap(item, width - 2).join("\n ")}`);
17232
+ out2.push(`${paint(ANSI.amber, "\u2717")} ${wrap(item, width - 2).join("\n ")}`);
17176
17233
  }
17177
- if (review2.confirms.length || review2.contradicts.length) out.push("");
17178
- out.push(paint(ANSI.dim, " nanopm roadmap \xB7 nanopm metrics \xB7 nanopm log"));
17179
- return out.join("\n");
17234
+ if (review2.confirms.length || review2.contradicts.length) out2.push("");
17235
+ out2.push(paint(ANSI.dim, " nanopm roadmap \xB7 nanopm metrics \xB7 nanopm log"));
17236
+ return out2.join("\n");
17180
17237
  }
17181
17238
 
17182
17239
  // src/commands/review.ts
@@ -17249,13 +17306,13 @@ async function problems(ref, opts) {
17249
17306
  const auth = await authenticate();
17250
17307
  const color = opts.color ?? useColor();
17251
17308
  if (opts.all) {
17252
- const out = [];
17309
+ const out2 = [];
17253
17310
  for (const project2 of await memberProjects(auth.client, auth.userId, opts.cwd)) {
17254
- out.push(painter(color)(ANSI.bold, project2.name));
17255
- out.push(await problemList(auth.client, project2.id, color));
17256
- out.push("");
17311
+ out2.push(painter(color)(ANSI.bold, project2.name));
17312
+ out2.push(await problemList(auth.client, project2.id, color));
17313
+ out2.push("");
17257
17314
  }
17258
- return out.join("\n").trimEnd();
17315
+ return out2.join("\n").trimEnd();
17259
17316
  }
17260
17317
  const project = await resolveProject(auth.client, { projectId: opts.projectId, cwd: opts.cwd });
17261
17318
  if (!ref) return problemList(auth.client, project.id, color);
@@ -17452,76 +17509,76 @@ function renderMarket(rows2, opts) {
17452
17509
  const rest = rows2.filter((r) => r.kind !== "competitor");
17453
17510
  const refWidth = Math.max(...rows2.map((r) => r.ref.length));
17454
17511
  const width = columns();
17455
- const out = [];
17512
+ const out2 = [];
17456
17513
  for (const c of competitors) {
17457
17514
  const mark2 = c.confirmed ? paint(ANSI.green, "\u25CF") : paint(ANSI.amber, "\u25CB");
17458
17515
  const name = c.competitor ?? c.domain ?? c.ref;
17459
- out.push(`${mark2} ${paint(ANSI.dim, c.ref.padEnd(refWidth))} ${paint(ANSI.bold, name)}${c.domain ? paint(ANSI.dim, ` ${c.domain}`) : ""}`);
17516
+ out2.push(`${mark2} ${paint(ANSI.dim, c.ref.padEnd(refWidth))} ${paint(ANSI.bold, name)}${c.domain ? paint(ANSI.dim, ` ${c.domain}`) : ""}`);
17460
17517
  const indent = ` ${" ".repeat(refWidth)} `;
17461
- out.push(`${indent}${clip(firstSentence(c.statement), Math.max(40, width - indent.length))}`);
17462
- out.push(
17518
+ out2.push(`${indent}${clip(firstSentence(c.statement), Math.max(40, width - indent.length))}`);
17519
+ out2.push(
17463
17520
  paint(
17464
17521
  ANSI.dim,
17465
17522
  indent + (c.confirmed ? `confirmed${c.provenance === "stated" ? " \xB7 you added them" : ""}` : `not confirmed \xB7 nanopm market confirm ${c.ref}`)
17466
17523
  )
17467
17524
  );
17468
17525
  for (const r of rest.filter((r2) => sameCompetitor(r2, c))) {
17469
- out.push(paint(ANSI.dim, `${indent}${r.ref.padEnd(refWidth)} ${KIND_LABEL[r.kind] ?? r.kind} \xB7 ${dateOf(r)}`));
17470
- out.push(`${indent}${" ".repeat(refWidth + 1)}${clip(firstSentence(r.statement), Math.max(40, width - indent.length - refWidth - 1))}`);
17526
+ out2.push(paint(ANSI.dim, `${indent}${r.ref.padEnd(refWidth)} ${KIND_LABEL[r.kind] ?? r.kind} \xB7 ${dateOf(r)}`));
17527
+ out2.push(`${indent}${" ".repeat(refWidth + 1)}${clip(firstSentence(r.statement), Math.max(40, width - indent.length - refWidth - 1))}`);
17471
17528
  }
17472
- out.push("");
17529
+ out2.push("");
17473
17530
  }
17474
17531
  const unmatched = rest.filter((r) => !competitors.some((c) => sameCompetitor(r, c)));
17475
17532
  if (unmatched.length) {
17476
17533
  for (const r of unmatched) {
17477
- out.push(`${paint(ANSI.dim, "\xB7")} ${paint(ANSI.dim, r.ref.padEnd(refWidth))} ${paint(ANSI.dim, `${KIND_LABEL[r.kind] ?? r.kind}${r.competitor ? ` \xB7 ${r.competitor}` : ""} \xB7 ${dateOf(r)}`)}`);
17478
- out.push(` ${" ".repeat(refWidth)} ${clip(firstSentence(r.statement), Math.max(40, width - refWidth - 4))}`);
17534
+ out2.push(`${paint(ANSI.dim, "\xB7")} ${paint(ANSI.dim, r.ref.padEnd(refWidth))} ${paint(ANSI.dim, `${KIND_LABEL[r.kind] ?? r.kind}${r.competitor ? ` \xB7 ${r.competitor}` : ""} \xB7 ${dateOf(r)}`)}`);
17535
+ out2.push(` ${" ".repeat(refWidth)} ${clip(firstSentence(r.statement), Math.max(40, width - refWidth - 4))}`);
17479
17536
  }
17480
- out.push("");
17537
+ out2.push("");
17481
17538
  }
17482
17539
  const unconfirmed = competitors.filter((c) => !c.confirmed).length;
17483
17540
  if (unconfirmed) {
17484
- out.push(
17541
+ out2.push(
17485
17542
  paint(
17486
17543
  ANSI.dim,
17487
17544
  `${unconfirmed} of ${competitors.length} competitor${competitors.length === 1 ? "" : "s"} not confirmed.`
17488
17545
  )
17489
17546
  );
17490
17547
  }
17491
- out.push("");
17492
- out.push(paint(ANSI.dim, " nanopm market <ref> \xB7 nanopm market confirm <ref> \xB7 nanopm market add <name> <domain> \xB7 nanopm market --refresh"));
17493
- return out.join("\n");
17548
+ out2.push("");
17549
+ out2.push(paint(ANSI.dim, " nanopm market <ref> \xB7 nanopm market confirm <ref> \xB7 nanopm market add <name> <domain> \xB7 nanopm market --refresh"));
17550
+ return out2.join("\n");
17494
17551
  }
17495
17552
  function renderMarketEntry(row, related, opts) {
17496
17553
  const paint = painter(opts.color);
17497
17554
  const width = columns();
17498
- const out = [];
17499
- out.push(`${paint(ANSI.dim, row.ref)} ${paint(ANSI.bold, KIND_LABEL[row.kind] ?? row.kind)}${row.competitor ? ` \xB7 ${row.competitor}` : ""}`);
17500
- out.push("");
17501
- for (const line of wrap(row.statement, width)) out.push(line);
17502
- out.push("");
17555
+ const out2 = [];
17556
+ out2.push(`${paint(ANSI.dim, row.ref)} ${paint(ANSI.bold, KIND_LABEL[row.kind] ?? row.kind)}${row.competitor ? ` \xB7 ${row.competitor}` : ""}`);
17557
+ out2.push("");
17558
+ for (const line of wrap(row.statement, width)) out2.push(line);
17559
+ out2.push("");
17503
17560
  const facts = [];
17504
17561
  if (row.domain) facts.push(["site", row.domain]);
17505
17562
  facts.push(["from", row.provenance === "stated" ? "you" : "the web"]);
17506
17563
  if (row.kind === "competitor") {
17507
17564
  facts.push(["confirmed", row.confirmed ? "yes" : `no \xB7 nanopm market confirm ${row.ref}`]);
17508
17565
  }
17509
- for (const [k, v] of facts) out.push(`${paint(ANSI.dim, k.padEnd(10))} ${v}`);
17566
+ for (const [k, v] of facts) out2.push(`${paint(ANSI.dim, k.padEnd(10))} ${v}`);
17510
17567
  const pages = row.sources.filter((s) => s.kind === "web");
17511
17568
  if (pages.length) {
17512
- out.push("");
17513
- out.push(paint(ANSI.dim, pages.length === 1 ? "Page" : "Pages"));
17514
- for (const s of pages) out.push(` ${s.ref}${s.note ? paint(ANSI.dim, ` ${s.note}`) : ""}`);
17569
+ out2.push("");
17570
+ out2.push(paint(ANSI.dim, pages.length === 1 ? "Page" : "Pages"));
17571
+ for (const s of pages) out2.push(` ${s.ref}${s.note ? paint(ANSI.dim, ` ${s.note}`) : ""}`);
17515
17572
  }
17516
17573
  if (related.length) {
17517
- out.push("");
17518
- out.push(paint(ANSI.dim, `Also about ${row.competitor}`));
17574
+ out2.push("");
17575
+ out2.push(paint(ANSI.dim, `Also about ${row.competitor}`));
17519
17576
  for (const r of related) {
17520
- out.push(` ${paint(ANSI.dim, r.ref)} ${paint(ANSI.dim, `${KIND_LABEL[r.kind] ?? r.kind} \xB7 ${dateOf(r)}`)}`);
17521
- for (const line of wrap(r.statement, width - 4)) out.push(` ${line}`);
17577
+ out2.push(` ${paint(ANSI.dim, r.ref)} ${paint(ANSI.dim, `${KIND_LABEL[r.kind] ?? r.kind} \xB7 ${dateOf(r)}`)}`);
17578
+ for (const line of wrap(r.statement, width - 4)) out2.push(` ${line}`);
17522
17579
  }
17523
17580
  }
17524
- return out.join("\n");
17581
+ return out2.join("\n");
17525
17582
  }
17526
17583
 
17527
17584
  // src/commands/search.ts
@@ -17546,27 +17603,27 @@ async function showSearch(opts) {
17546
17603
  const paint = painter(opts.color ?? useColor());
17547
17604
  const row = await searchConnection(auth.client, project.id);
17548
17605
  const local = await localWebSearch();
17549
- const out = [describeSearch(row, local), ""];
17606
+ const out2 = [describeSearch(row, local), ""];
17550
17607
  if (isClaudeMode(row)) {
17551
- out.push(paint(ANSI.dim, " Claude Code's own search and fetch, on your subscription. The allowlist and the hourly"));
17552
- out.push(paint(ANSI.dim, " cap do not apply; every search and fetch is journalled."));
17553
- out.push(paint(ANSI.dim, " nanopm search default go back to DuckDuckGo"));
17608
+ out2.push(paint(ANSI.dim, " Claude Code's own search and fetch, on your subscription. The allowlist and the hourly"));
17609
+ out2.push(paint(ANSI.dim, " cap do not apply; every search and fetch is journalled."));
17610
+ out2.push(paint(ANSI.dim, " nanopm search default go back to DuckDuckGo"));
17554
17611
  } else if (row) {
17555
- out.push(paint(ANSI.dim, " nanopm search default go back to DuckDuckGo; the key is deleted"));
17556
- out.push(paint(ANSI.dim, " nanopm search claude Claude Code's own search instead, no key"));
17612
+ out2.push(paint(ANSI.dim, " nanopm search default go back to DuckDuckGo; the key is deleted"));
17613
+ out2.push(paint(ANSI.dim, " nanopm search claude Claude Code's own search instead, no key"));
17557
17614
  } else if (local) {
17558
- out.push(paint(ANSI.dim, " No key, no account: Claude Code's own search, on the subscription you already have."));
17559
- out.push(paint(ANSI.dim, " The allowlist and the hourly cap do not apply; every search and fetch is journalled."));
17560
- out.push(paint(ANSI.dim, " A run claimed by a machine without it falls back to DuckDuckGo."));
17561
- out.push(paint(ANSI.dim, ` nanopm search ${SEARCH_PROVIDERS.join("|")} <key> bring your own key instead`));
17562
- out.push(paint(ANSI.dim, " nanopm search duckduckgo use DuckDuckGo instead"));
17615
+ out2.push(paint(ANSI.dim, " No key, no account: Claude Code's own search, on the subscription you already have."));
17616
+ out2.push(paint(ANSI.dim, " The allowlist and the hourly cap do not apply; every search and fetch is journalled."));
17617
+ out2.push(paint(ANSI.dim, " A run claimed by a machine without it falls back to DuckDuckGo."));
17618
+ out2.push(paint(ANSI.dim, ` nanopm search ${SEARCH_PROVIDERS.join("|")} <key> bring your own key instead`));
17619
+ out2.push(paint(ANSI.dim, " nanopm search duckduckgo use DuckDuckGo instead"));
17563
17620
  } else {
17564
- out.push(paint(ANSI.dim, " No key, no account. Queries leave this machine directly, capped and journalled."));
17565
- out.push(paint(ANSI.dim, " Claude's own search would be the default, but it did not answer on this machine."));
17566
- out.push(paint(ANSI.dim, ` nanopm search ${SEARCH_PROVIDERS.join("|")} <key> bring your own key for a better search`));
17567
- out.push(paint(ANSI.dim, " nanopm search claude Claude Code's own search and fetch, on your subscription"));
17621
+ out2.push(paint(ANSI.dim, " No key, no account. Queries leave this machine directly, capped and journalled."));
17622
+ out2.push(paint(ANSI.dim, " Claude's own search would be the default, but it did not answer on this machine."));
17623
+ out2.push(paint(ANSI.dim, ` nanopm search ${SEARCH_PROVIDERS.join("|")} <key> bring your own key for a better search`));
17624
+ out2.push(paint(ANSI.dim, " nanopm search claude Claude Code's own search and fetch, on your subscription"));
17568
17625
  }
17569
- return out.join("\n");
17626
+ return out2.join("\n");
17570
17627
  }
17571
17628
  async function setClaudeSearch(opts) {
17572
17629
  const auth = await authenticate();
@@ -17774,34 +17831,34 @@ function render(rows2) {
17774
17831
  if (people2.length === 0) return "Nobody yet. `nanopm personas --refresh` works out who this is for from your docs, your pricing and what the product says about itself.";
17775
17832
  const needs2 = rows2.filter((r) => r.topic === NEEDS_TOPIC);
17776
17833
  const taken = /* @__PURE__ */ new Set();
17777
- const out = [];
17834
+ const out2 = [];
17778
17835
  for (const row of people2) {
17779
17836
  const p = parsePersona(row.key, row.content);
17780
17837
  const mark2 = row.provenance === "stated" ? "" : ` \xB7 to review${row.confidence != null ? ` (${row.confidence}% sure)` : ""}`;
17781
- out.push(`${row.title ?? p.slug}${mark2}`);
17782
- out.push(` ${p.who}`);
17838
+ out2.push(`${row.title ?? p.slug}${mark2}`);
17839
+ out2.push(` ${p.who}`);
17783
17840
  for (const [label, value] of [
17784
17841
  ["comes for", p.comesFor],
17785
17842
  ["today instead", p.todayInstead],
17786
17843
  ["context", p.context],
17787
17844
  ["weight", p.weight]
17788
17845
  ]) {
17789
- if (value) out.push(` ${label}: ${value}`);
17846
+ if (value) out2.push(` ${label}: ${value}`);
17790
17847
  }
17791
17848
  const mine = needs2.filter((n) => needOwner(n) === p.slug);
17792
17849
  for (const n of mine) {
17793
17850
  taken.add(n.key);
17794
- out.push(` - ${n.title ? `${n.title}: ` : ""}${n.content}`);
17851
+ out2.push(` - ${n.title ? `${n.title}: ` : ""}${n.content}`);
17795
17852
  }
17796
- out.push("");
17853
+ out2.push("");
17797
17854
  }
17798
17855
  const loose = needs2.filter((n) => !taken.has(n.key));
17799
17856
  if (loose.length) {
17800
- out.push("Not attached to anyone yet");
17801
- for (const n of loose) out.push(` - ${n.title ? `${n.title}: ` : ""}${n.content}`);
17802
- out.push("");
17857
+ out2.push("Not attached to anyone yet");
17858
+ for (const n of loose) out2.push(` - ${n.title ? `${n.title}: ` : ""}${n.content}`);
17859
+ out2.push("");
17803
17860
  }
17804
- return out.join("\n").trimEnd();
17861
+ return out2.join("\n").trimEnd();
17805
17862
  }
17806
17863
 
17807
17864
  // src/commands/direction.ts
@@ -17826,21 +17883,21 @@ function renderDirection(rows2) {
17826
17883
  if (!rows2.some((r) => DIRECTION_ROWS.some((line) => r.key === directionKey(line))) && !objective) {
17827
17884
  return "No direction yet. `nanopm direction --refresh` has the crew write a mission and a vision from what the card holds; the objective is yours, and the onboarding asks for it.";
17828
17885
  }
17829
- const out = [];
17886
+ const out2 = [];
17830
17887
  for (const line of DIRECTION_ROWS) {
17831
17888
  const row = rows2.find((r) => r.key === directionKey(line));
17832
17889
  const next = rows2.find((r) => r.key === directionKey(line, true));
17833
- out.push(`${DIRECTION_ROW_TITLE[line]}${mark(row)}`);
17834
- out.push(` ${row?.content ?? "\u2014"}`);
17890
+ out2.push(`${DIRECTION_ROW_TITLE[line]}${mark(row)}`);
17891
+ out2.push(` ${row?.content ?? "\u2014"}`);
17835
17892
  if (next) {
17836
17893
  const because = reasonOf2(next.sources);
17837
- out.push(` proposed: ${next.content}${because ? ` \xB7 ${because}` : ""}`);
17894
+ out2.push(` proposed: ${next.content}${because ? ` \xB7 ${because}` : ""}`);
17838
17895
  }
17839
17896
  }
17840
- out.push("Objective");
17841
- out.push(` ${objective ? objective.content.replace(/^Lever: [a-z]+\.\s*/, "") : "\u2014"}`);
17842
- if (counts) out.push(` ${counts.content.replace(/^Lever: [a-z]+\.\s*/, "")}`);
17843
- return out.join("\n");
17897
+ out2.push("Objective");
17898
+ out2.push(` ${objective ? objective.content.replace(/^Lever: [a-z]+\.\s*/, "") : "\u2014"}`);
17899
+ if (counts) out2.push(` ${counts.content.replace(/^Lever: [a-z]+\.\s*/, "")}`);
17900
+ return out2.join("\n");
17844
17901
  }
17845
17902
  function mark(row) {
17846
17903
  return row && row.provenance !== "stated" ? ` \xB7 to review${row.confidence != null ? ` (${row.confidence}% sure)` : ""}` : "";
@@ -17871,19 +17928,19 @@ function render2(rows2) {
17871
17928
  if (!rows2.some((r) => PITCH_LINE_KEYS.some((l) => r.key === pitchKey(l)))) {
17872
17929
  return "No pitch yet. An onboarding writes the first one; `nanopm pitch --refresh` has the crew write one from what the card holds.";
17873
17930
  }
17874
- const out = [];
17931
+ const out2 = [];
17875
17932
  for (const line of PITCH_LINE_KEYS) {
17876
17933
  const row = rows2.find((r) => r.key === pitchKey(line));
17877
17934
  const next = rows2.find((r) => r.key === pitchKey(line, true));
17878
17935
  const mark2 = row && row.provenance !== "stated" ? ` \xB7 to review${row.confidence != null ? ` (${row.confidence}% sure)` : ""}` : "";
17879
- out.push(`${PITCH_LINE_TITLE[line]}${mark2}`);
17880
- out.push(` ${row?.content ?? "\u2014"}`);
17936
+ out2.push(`${PITCH_LINE_TITLE[line]}${mark2}`);
17937
+ out2.push(` ${row?.content ?? "\u2014"}`);
17881
17938
  if (next) {
17882
17939
  const because = reasonOf3(next.sources);
17883
- out.push(` proposed: ${next.content}${because ? ` \xB7 ${because}` : ""}`);
17940
+ out2.push(` proposed: ${next.content}${because ? ` \xB7 ${because}` : ""}`);
17884
17941
  }
17885
17942
  }
17886
- return out.join("\n");
17943
+ return out2.join("\n");
17887
17944
  }
17888
17945
  function reasonOf3(sources2) {
17889
17946
  if (!Array.isArray(sources2)) return null;
@@ -17955,16 +18012,16 @@ async function join18(code, opts) {
17955
18012
  const joined = row ? { id: row.joined_id, name: row.joined_name, repo_remote: row.joined_remote, role: row.joined_role } : void 0;
17956
18013
  if (!joined) throw new Error("nothing was joined");
17957
18014
  const paint = painter(opts.color ?? useColor());
17958
- const out = [paint(ANSI.green, `\u2713 you are on ${joined.name}, as ${joined.role}`)];
18015
+ const out2 = [paint(ANSI.green, `\u2713 you are on ${joined.name}, as ${joined.role}`)];
17959
18016
  if (joined.repo_remote && joined.repo_remote !== repo.remote) {
17960
- out.push(paint(ANSI.amber, ` This clone's remote is ${repo.remote}; the project's is ${joined.repo_remote}. A branch NanoPM makes here lands on yours.`));
18017
+ out2.push(paint(ANSI.amber, ` This clone's remote is ${repo.remote}; the project's is ${joined.repo_remote}. A branch NanoPM makes here lands on yours.`));
17961
18018
  }
17962
- out.push(paint(ANSI.dim, ` Clone: ${repo.root}${daemonId ? "" : " \xB7 no daemon on this machine yet: nanopm daemon install"}`));
18019
+ out2.push(paint(ANSI.dim, ` Clone: ${repo.root}${daemonId ? "" : " \xB7 no daemon on this machine yet: nanopm daemon install"}`));
17963
18020
  const { data: slugRow } = await auth.client.from("projects").select("slug").eq("id", joined.id).maybeSingle();
17964
- out.push("");
17965
- if (slugRow?.slug) out.push(` Your project: ${webUrl()}/p/${slugRow.slug}`);
17966
- out.push(paint(ANSI.dim, " nanopm roadmap \xB7 nanopm problems \xB7 nanopm status"));
17967
- return await ensureName(auth.client, auth.userId, opts, out.join("\n"));
18021
+ out2.push("");
18022
+ if (slugRow?.slug) out2.push(` Your project: ${webUrl()}/p/${slugRow.slug}`);
18023
+ out2.push(paint(ANSI.dim, " nanopm roadmap \xB7 nanopm problems \xB7 nanopm status"));
18024
+ return await ensureName(auth.client, auth.userId, opts, out2.join("\n"));
17968
18025
  }
17969
18026
  async function members(opts) {
17970
18027
  const auth = await authenticate();
@@ -17974,14 +18031,14 @@ async function members(opts) {
17974
18031
  if (error) throw new Error(pgMessage(error));
17975
18032
  const rows2 = data ?? [];
17976
18033
  const width = Math.max(...rows2.map((r) => r.name.length));
17977
- const out = rows2.map((r) => {
18034
+ const out2 = rows2.map((r) => {
17978
18035
  const you = r.user_id === auth.userId ? paint(ANSI.dim, " (you)") : "";
17979
18036
  const seen = r.daemon_last_seen_at ? Date.now() - new Date(r.daemon_last_seen_at).getTime() : Infinity;
17980
18037
  const daemon = !r.daemon_id ? paint(ANSI.dim, "no daemon") : seen < 12e4 ? paint(ANSI.green, "\u25CF daemon online") + (r.daemon_shared ? paint(ANSI.dim, " \xB7 shared") : "") : paint(ANSI.amber, `\u25CB daemon last seen ${Math.round(seen / 6e4)} min ago`) + (r.daemon_shared ? paint(ANSI.dim, " \xB7 shared") : "");
17981
18038
  return `${paint(ANSI.bold, r.name.padEnd(width))}${you} ${paint(ANSI.dim, r.role.padEnd(6))} ${daemon}`;
17982
18039
  });
17983
- out.push("", paint(ANSI.dim, " nanopm invite \xB7 nanopm name <what partners see>"));
17984
- return out.join("\n");
18040
+ out2.push("", paint(ANSI.dim, " nanopm invite \xB7 nanopm name <what partners see>"));
18041
+ return out2.join("\n");
17985
18042
  }
17986
18043
  async function setName(name) {
17987
18044
  const trimmed = name.trim();
@@ -18050,36 +18107,36 @@ var MEASURE_DEFINITIONS = [
18050
18107
  function renderStatus(projects, daemon, opts) {
18051
18108
  const paint = painter(opts.color);
18052
18109
  const now = opts.now ?? /* @__PURE__ */ new Date();
18053
- const out = [];
18110
+ const out2 = [];
18054
18111
  if (!daemon) {
18055
- out.push(paint(ANSI.amber, "\u25CB no daemon on this machine"));
18056
- out.push(paint(ANSI.dim, " nanopm daemon install"));
18112
+ out2.push(paint(ANSI.amber, "\u25CB no daemon on this machine"));
18113
+ out2.push(paint(ANSI.dim, " nanopm daemon install"));
18057
18114
  } else {
18058
18115
  const seenMs = daemon.lastSeenAt ? now.getTime() - new Date(daemon.lastSeenAt).getTime() : Infinity;
18059
18116
  const alive = daemon.status === "online" && seenMs < 5 * 6e4;
18060
18117
  const stale = seenMs > 24 * 36e5;
18061
18118
  const mark2 = alive ? paint(ANSI.green, "\u25CF") : stale ? paint(ANSI.red, "\u2298") : paint(ANSI.amber, "\u25CB");
18062
- out.push(`${mark2} daemon on ${daemon.hostname} \xB7 ${alive ? "online" : `last seen ${ago(daemon.lastSeenAt, now)}`} \xB7 nanopm ${daemon.version}`);
18119
+ out2.push(`${mark2} daemon on ${daemon.hostname} \xB7 ${alive ? "online" : `last seen ${ago(daemon.lastSeenAt, now)}`} \xB7 nanopm ${daemon.version}`);
18063
18120
  if (stale) {
18064
- out.push(paint(ANSI.dim, " It has not checked in for a day. Nothing scheduled will run until it does."));
18065
- out.push(paint(ANSI.dim, " nanopm daemon install"));
18121
+ out2.push(paint(ANSI.dim, " It has not checked in for a day. Nothing scheduled will run until it does."));
18122
+ out2.push(paint(ANSI.dim, " nanopm daemon install"));
18066
18123
  }
18067
18124
  }
18068
18125
  if (opts.activeProjects !== void 0 && projects.length > 1) {
18069
- out.push(paint(ANSI.dim, ` ${opts.activeProjects} of ${projects.length} projects active in the last 30 days`));
18126
+ out2.push(paint(ANSI.dim, ` ${opts.activeProjects} of ${projects.length} projects active in the last 30 days`));
18070
18127
  }
18071
- out.push("");
18128
+ out2.push("");
18072
18129
  for (const project of projects) {
18073
- out.push(paint(ANSI.bold, project.name) + (project.onThisMachine === false ? paint(ANSI.dim, " \xB7 clone not on this machine") : ""));
18130
+ out2.push(paint(ANSI.bold, project.name) + (project.onThisMachine === false ? paint(ANSI.dim, " \xB7 clone not on this machine") : ""));
18074
18131
  if (project.schedule) {
18075
- out.push(` ${paint(ANSI.dim, "reviews")} ${describeCadence(project.schedule)} \xB7 next ${describeNextRun(project.schedule, now)}`);
18132
+ out2.push(` ${paint(ANSI.dim, "reviews")} ${describeCadence(project.schedule)} \xB7 next ${describeNextRun(project.schedule, now)}`);
18076
18133
  } else {
18077
- out.push(` ${paint(ANSI.dim, "reviews")} not scheduled \xB7 ${paint(ANSI.dim, "nanopm schedule weekly monday")}`);
18134
+ out2.push(` ${paint(ANSI.dim, "reviews")} not scheduled \xB7 ${paint(ANSI.dim, "nanopm schedule weekly monday")}`);
18078
18135
  }
18079
- out.push(` ${paint(ANSI.dim, "last")} ${ago(project.lastReviewAt, now)}`);
18080
- out.push(` ${paint(ANSI.dim, "search")} ${project.search}`);
18136
+ out2.push(` ${paint(ANSI.dim, "last")} ${ago(project.lastReviewAt, now)}`);
18137
+ out2.push(` ${paint(ANSI.dim, "search")} ${project.search}`);
18081
18138
  if (project.stalledDays !== null) {
18082
- out.push(
18139
+ out2.push(
18083
18140
  paint(
18084
18141
  ANSI.amber,
18085
18142
  ` ? a review was queued ${project.stalledDays} day${project.stalledDays === 1 ? "" : "s"} ago and has not run \u2014 is the daemon awake?`
@@ -18089,7 +18146,7 @@ function renderStatus(projects, daemon, opts) {
18089
18146
  const parts = [];
18090
18147
  if (project.runningJobs) parts.push(`${project.runningJobs} running`);
18091
18148
  if (project.queuedJobs) parts.push(`${project.queuedJobs} queued`);
18092
- out.push(` ${paint(ANSI.dim, "jobs")} ${parts.join(", ")}`);
18149
+ out2.push(` ${paint(ANSI.dim, "jobs")} ${parts.join(", ")}`);
18093
18150
  }
18094
18151
  const waiting = [];
18095
18152
  if (project.waitingDecisions) {
@@ -18098,23 +18155,23 @@ function renderStatus(projects, daemon, opts) {
18098
18155
  if (project.unprovenProblems) {
18099
18156
  waiting.push(`${project.unprovenProblems} of ${project.openProblems} problems unproven`);
18100
18157
  }
18101
- out.push(
18158
+ out2.push(
18102
18159
  ` ${paint(ANSI.dim, "waiting")} ${waiting.length ? waiting.join(" \xB7 ") : paint(ANSI.dim, "nothing needs you")}`
18103
18160
  );
18104
18161
  if (project.measures) {
18105
18162
  const m = project.measures;
18106
- out.push(
18163
+ out2.push(
18107
18164
  ` ${paint(ANSI.dim, "measures")} autonomy ${ratio(m.autonomy.done, m.autonomy.accepted)} \xB7 decisions ${ratio(m.decisions.asWritten, m.decisions.decided)} \xB7 reviews ${m.reviews.weeks} of ${m.reviews.of} weeks`
18108
18165
  );
18109
18166
  }
18110
- out.push("");
18167
+ out2.push("");
18111
18168
  }
18112
18169
  if (projects.some((p) => p.measures)) {
18113
- for (const line of MEASURE_DEFINITIONS) out.push(paint(ANSI.dim, ` ${line}`));
18114
- out.push("");
18170
+ for (const line of MEASURE_DEFINITIONS) out2.push(paint(ANSI.dim, ` ${line}`));
18171
+ out2.push("");
18115
18172
  }
18116
- out.push(paint(ANSI.dim, " nanopm roadmap \xB7 nanopm problems \xB7 nanopm review --last"));
18117
- return out.join("\n");
18173
+ out2.push(paint(ANSI.dim, " nanopm roadmap \xB7 nanopm problems \xB7 nanopm review --last"));
18174
+ return out2.join("\n");
18118
18175
  }
18119
18176
 
18120
18177
  // src/commands/schedule.ts