@sonnechasser/ntrp 2.1.10 → 2.1.12

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.
Files changed (3) hide show
  1. package/dist/index.js +338 -174
  2. package/dist/mcp/server.js +251 -139
  3. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -1719,8 +1719,10 @@ var init_prefs = __esm({
1719
1719
  // src/voice/copy.ts
1720
1720
  var copy_exports = {};
1721
1721
  __export(copy_exports, {
1722
+ EXPORT_PICKUP_PHRASE: () => EXPORT_PICKUP_PHRASE,
1722
1723
  voiceAuthFailureAlert: () => voiceAuthFailureAlert,
1723
1724
  voiceAuthFailureHint: () => voiceAuthFailureHint,
1725
+ voiceExportPickupNudge: () => voiceExportPickupNudge,
1724
1726
  voiceGapLoadCta: () => voiceGapLoadCta,
1725
1727
  voiceGapReadyCta: () => voiceGapReadyCta,
1726
1728
  voiceHandoffAddendum: () => voiceHandoffAddendum,
@@ -1873,6 +1875,34 @@ function voiceHealthGloss(status, prefs) {
1873
1875
  }
1874
1876
  });
1875
1877
  }
1878
+ function voiceExportPickupNudge(input, prefs) {
1879
+ const v = voice(prefs);
1880
+ const phrase = EXPORT_PICKUP_PHRASE;
1881
+ if (input.inboxReady) {
1882
+ return pick(v, {
1883
+ robotic: `Open your desktop AI. Tell it to ${phrase}.`,
1884
+ composed: `Open your desktop AI and tell it to ${phrase}.`,
1885
+ casual: `This terminal is not your AI. Open it and tell it to ${phrase}.`,
1886
+ loose: {
1887
+ light: `Written. Open your desktop AI and tell it to ${phrase}.`,
1888
+ medium: `This CLI cannot see your AI. Open it. Tell it to ${phrase}.`,
1889
+ dark: `The file is on disk. Your AI is not in this window. Open it and ${phrase}.`,
1890
+ heavy: `NTRP wrote it. This window is not your AI. Open the desktop app and ${phrase}.`
1891
+ }
1892
+ });
1893
+ }
1894
+ return pick(v, {
1895
+ robotic: `Your desktop AI cannot see this folder. Type /inbox set, then tell that AI to ${phrase}.`,
1896
+ composed: `These files are on disk. Set a pickup folder (/inbox set), then tell your desktop AI to ${phrase}.`,
1897
+ casual: `Your AI cannot see this folder. Type /inbox set, then tell it to ${phrase}.`,
1898
+ loose: {
1899
+ light: `Your AI cannot see this folder yet. Type /inbox set, then tell it to ${phrase}.`,
1900
+ medium: `This folder is invisible to your AI. Type /inbox set, then tell it to ${phrase}.`,
1901
+ dark: `Your AI cannot see this terminal. Type /inbox set, then ${phrase}.`,
1902
+ heavy: `This window is not your AI. Type /inbox set, then ${phrase}.`
1903
+ }
1904
+ });
1905
+ }
1876
1906
  function voiceHandoffAddendum(prefs) {
1877
1907
  const v = voice(prefs);
1878
1908
  return [
@@ -1887,11 +1917,13 @@ function pick(prefs, map) {
1887
1917
  if (typeof entry === "string") return entry;
1888
1918
  return entry[prefs.roast];
1889
1919
  }
1920
+ var EXPORT_PICKUP_PHRASE;
1890
1921
  var init_copy = __esm({
1891
1922
  "src/voice/copy.ts"() {
1892
1923
  "use strict";
1893
1924
  init_catalog();
1894
1925
  init_prefs();
1926
+ EXPORT_PICKUP_PHRASE = "pick up the latest NTRP handoff";
1895
1927
  }
1896
1928
  });
1897
1929
 
@@ -2244,8 +2276,10 @@ var init_verbosity = __esm({
2244
2276
  // src/trace/result-block.ts
2245
2277
  var result_block_exports = {};
2246
2278
  __export(result_block_exports, {
2279
+ VITAL_PROBLEM_READ: () => VITAL_PROBLEM_READ,
2247
2280
  buildVitalHeadlines: () => buildVitalHeadlines,
2248
2281
  formatDuration: () => formatDuration,
2282
+ formatVitalCall: () => formatVitalCall,
2249
2283
  headlinesFromFindingCards: () => headlinesFromFindingCards,
2250
2284
  printArtifactLine: () => printArtifactLine,
2251
2285
  printMetricsResultBlock: () => printMetricsResultBlock,
@@ -2269,12 +2303,32 @@ function flagLabel(vitals, status) {
2269
2303
  function deltaCell(delta) {
2270
2304
  if (!delta) return " ";
2271
2305
  const rounded = Math.round(delta.delta);
2272
- if (rounded === 0) return chalk5.dim(" 0 ");
2306
+ if (rounded === 0) return " ";
2273
2307
  if (rounded < 0) {
2274
2308
  return paint("error", `\u25BC ${String(Math.abs(rounded)).padStart(2)} `);
2275
2309
  }
2276
2310
  return paint("success", `\u25B2 ${String(rounded).padStart(2)} `);
2277
2311
  }
2312
+ function otherRedLabels(health) {
2313
+ return health.vital_signs.filter((v) => v.status === "red" && v.vital_sign !== health.gating_vital_sign).map((v) => VITAL_SIGN_LABELS[v.vital_sign]);
2314
+ }
2315
+ function formatVitalCall(health) {
2316
+ const gateName = VITAL_SIGN_LABELS[health.gating_vital_sign];
2317
+ const gating = health.vital_signs.find((v) => v.vital_sign === health.gating_vital_sign);
2318
+ const read = VITAL_PROBLEM_READ[health.gating_vital_sign];
2319
+ const alsoRed = otherRedLabels(health);
2320
+ const dollars = health.total_value_at_risk;
2321
+ const head = dollars != null && dollars > 0 ? `${formatCurrency(dollars)} at risk.` : "No dollar-weighted risk.";
2322
+ let start = `Start with ${gateName}`;
2323
+ if (gating && gating.dollar_value != null && gating.dollar_value > 0) {
2324
+ start += `: ${formatCurrency(gating.dollar_value)} ${read}`;
2325
+ }
2326
+ start += ".";
2327
+ if (alsoRed.length > 0) {
2328
+ start += ` Also red: ${alsoRed.join(", ")}.`;
2329
+ }
2330
+ return `${head} ${start}`;
2331
+ }
2278
2332
  function printVitalResultBlock(health, opts = {}) {
2279
2333
  const vitals = health.vital_signs;
2280
2334
  const deltaBySign = new Map((opts.deltas ?? []).map((d) => [d.vital_sign, d]));
@@ -2291,18 +2345,25 @@ function printVitalResultBlock(health, opts = {}) {
2291
2345
  console.log(` ${dot} ${label} ${score} ${delta} ${flag}`.trimEnd());
2292
2346
  }
2293
2347
  console.log();
2348
+ printVitalCall(health);
2349
+ printFindingsList(opts.findings ?? []);
2350
+ }
2351
+ function printVitalCall(health) {
2352
+ const gateName = VITAL_SIGN_LABELS[health.gating_vital_sign];
2353
+ const gating = health.vital_signs.find((v) => v.vital_sign === health.gating_vital_sign);
2354
+ const read = VITAL_PROBLEM_READ[health.gating_vital_sign];
2355
+ const alsoRed = otherRedLabels(health);
2294
2356
  const dollars = health.total_value_at_risk;
2295
- const gating = VITAL_SIGN_LABELS[health.gating_vital_sign];
2296
- const gatingVital = vitals.find((v) => v.vital_sign === health.gating_vital_sign);
2297
- if (dollars != null && dollars > 0) {
2298
- const why = gatingVital?.dollar_label ? ` (${gatingVital.dollar_label})` : "";
2299
- console.log(
2300
- ` ${paint("success", formatCurrency(dollars))} ${chalk5.dim("at risk")} ${chalk5.dim("\u2014")} ${chalk5.dim("held back by")} ${paint("accent", gating)}${chalk5.dim(why)}`
2301
- );
2302
- } else {
2303
- console.log(` ${chalk5.dim("No dollar-weighted risk detected")} ${chalk5.dim("\u2014")} ${chalk5.dim("held back by")} ${paint("accent", gating)}`);
2357
+ const head = dollars != null && dollars > 0 ? `${paint("success", formatCurrency(dollars))} at risk.` : "No dollar-weighted risk.";
2358
+ let start = `Start with ${paint("accent", gateName)}`;
2359
+ if (gating && gating.dollar_value != null && gating.dollar_value > 0) {
2360
+ start += `: ${formatCurrency(gating.dollar_value)} ${read}`;
2304
2361
  }
2305
- printFindingsList(opts.findings ?? []);
2362
+ start += ".";
2363
+ if (alsoRed.length > 0) {
2364
+ start += ` Also red: ${alsoRed.join(", ")}.`;
2365
+ }
2366
+ console.log(` ${head} ${start}`);
2306
2367
  }
2307
2368
  function printMetricsResultBlock(metrics, opts = {}) {
2308
2369
  const flagged = metrics.filter((m) => !m.unavailable_reason && (m.status === "red" || m.status === "yellow"));
@@ -2327,53 +2388,54 @@ function printMetricsResultBlock(metrics, opts = {}) {
2327
2388
  function printFindingsList(findings) {
2328
2389
  if (findings.length === 0) return;
2329
2390
  console.log();
2330
- console.log(` ${chalk5.bold("Findings")}`);
2331
2391
  findings.slice(0, 3).forEach((f, i) => {
2332
2392
  console.log(` ${chalk5.dim(`${i + 1}.`)} ${f.text}`);
2333
2393
  });
2334
2394
  }
2335
2395
  function printArtifactLine(path, description) {
2336
2396
  console.log();
2337
- const label = description?.trim() || "Full report";
2338
- console.log(` ${chalk5.dim(label)} ${chalk5.dim("\u2192")} ${path}`);
2339
- console.log(` ${chalk5.dim("Run with --verbose for per-vital detail.")}`);
2397
+ const label = description?.trim() || "Saved report";
2398
+ console.log(` ${chalk5.dim(label)} ${chalk5.dim("\u2192")} ${chalk5.dim(path)}`);
2340
2399
  console.log();
2341
2400
  }
2401
+ function problemPhrase(vs, who) {
2402
+ const read = VITAL_PROBLEM_READ[vs.vital_sign];
2403
+ const dollars = vs.dollar_value != null && vs.dollar_value > 0 ? formatCurrency(vs.dollar_value) : null;
2404
+ const prefix = who || VITAL_SIGN_LABELS[vs.vital_sign];
2405
+ if (dollars) return `${prefix}: ${dollars} ${read}`;
2406
+ return `${prefix}: ${VITAL_SIGN_LABELS[vs.vital_sign]} ${Math.round(vs.score)}`;
2407
+ }
2342
2408
  function buildVitalHeadlines(health, segments = []) {
2343
- const findings = [];
2344
- const gating = health.vital_signs.find((v) => v.vital_sign === health.gating_vital_sign);
2345
- if (gating) {
2346
- const label = VITAL_SIGN_LABELS[gating.vital_sign];
2347
- const dollars = gating.dollar_value != null && gating.dollar_value > 0 ? ` \u2014 ${formatCurrency(gating.dollar_value)} ${gating.dollar_label ?? ""}`.trimEnd() : "";
2348
- const severity = gating.status === "red" ? "critical" : gating.status === "yellow" ? "warn" : "info";
2349
- findings.push({
2350
- severity,
2351
- text: `${label} ${Math.round(gating.score)}${dollars}`
2352
- });
2353
- }
2354
2409
  const extras = [];
2355
- const sourceVitals = segments.length > 0 ? segments.flatMap(
2356
- (s) => s.result.vital_signs.map((vs) => ({ vs, segment: s.segment.name }))
2357
- ) : health.vital_signs.map((vs) => ({ vs, segment: "" }));
2358
- for (const { vs, segment } of sourceVitals) {
2359
- if (vs.status === "green") continue;
2360
- if (vs.vital_sign === health.gating_vital_sign && !segment) continue;
2361
- if (vs.dollar_value == null || vs.dollar_value <= 0) continue;
2362
- const label = VITAL_SIGN_LABELS[vs.vital_sign];
2363
- const who = segment ? `${segment}: ` : "";
2410
+ const push = (vs, who) => {
2411
+ if (vs.status === "green") return;
2412
+ if (vs.dollar_value == null || vs.dollar_value <= 0) return;
2364
2413
  extras.push({
2365
2414
  severity: vs.status === "red" ? "critical" : "warn",
2366
- text: `${who}${label} ${Math.round(vs.score)} \u2014 ${formatCurrency(vs.dollar_value)} ${vs.dollar_label ?? ""}`.trim(),
2415
+ text: problemPhrase(vs, who),
2367
2416
  dollars: vs.dollar_value
2368
2417
  });
2418
+ };
2419
+ if (segments.length > 0) {
2420
+ for (const s of segments) {
2421
+ for (const vs of s.result.vital_signs) {
2422
+ push(vs, s.segment.name);
2423
+ }
2424
+ }
2425
+ } else {
2426
+ for (const vs of health.vital_signs) {
2427
+ if (vs.vital_sign === health.gating_vital_sign) continue;
2428
+ push(vs);
2429
+ }
2369
2430
  }
2370
2431
  extras.sort((a, b) => b.dollars - a.dollars);
2432
+ const findings = [];
2371
2433
  for (const extra of extras) {
2372
2434
  if (findings.length >= 3) break;
2373
2435
  if (findings.some((f) => f.text === extra.text)) continue;
2374
2436
  findings.push({ severity: extra.severity, text: extra.text });
2375
2437
  }
2376
- return findings.slice(0, 3);
2438
+ return findings;
2377
2439
  }
2378
2440
  function headlinesFromFindingCards(cards) {
2379
2441
  return cards.slice(0, 3).map((card) => ({
@@ -2381,11 +2443,19 @@ function headlinesFromFindingCards(cards) {
2381
2443
  text: stripFindingMarkdown(card.finding)
2382
2444
  }));
2383
2445
  }
2446
+ var VITAL_PROBLEM_READ;
2384
2447
  var init_result_block = __esm({
2385
2448
  "src/trace/result-block.ts"() {
2386
2449
  "use strict";
2387
2450
  init_formatters();
2388
2451
  init_theme();
2452
+ VITAL_PROBLEM_READ = {
2453
+ freshness: "stale",
2454
+ flow_rate: "stuck",
2455
+ drop_rate: "lost at handoff",
2456
+ signal_to_noise: "misdirected effort",
2457
+ thread_depth: "single-threaded"
2458
+ };
2389
2459
  }
2390
2460
  });
2391
2461
 
@@ -2480,11 +2550,10 @@ var init_renderer = __esm({
2480
2550
  }
2481
2551
  const line = `${summary} \xB7 ${formatDuration(durationMs)}`;
2482
2552
  if (this.useSpinner() && this.spinner) {
2483
- this.spinner.succeed(line);
2553
+ this.spinner.stop();
2484
2554
  this.spinner = null;
2485
- return;
2486
2555
  }
2487
- console.log(` ${chalk6.green("\u2713")} ${line}`);
2556
+ console.log(` ${chalk6.green("\u2713")} ${chalk6.dim(line)}`);
2488
2557
  }
2489
2558
  persistDim(message) {
2490
2559
  const spinning = this.spinner?.isSpinning;
@@ -2888,6 +2957,7 @@ __export(handoff_skill_exports, {
2888
2957
  persistHandoffSkillFiles: () => persistHandoffSkillFiles,
2889
2958
  persistStandingSkill: () => persistStandingSkill,
2890
2959
  pickupContextFromEvent: () => pickupContextFromEvent,
2960
+ printExportPickupNudge: () => printExportPickupNudge,
2891
2961
  printHandoffDelivered: () => printHandoffDelivered,
2892
2962
  printStandingSkill: () => printStandingSkill
2893
2963
  });
@@ -3126,11 +3196,14 @@ function maybePrintAiInboxNudge() {
3126
3196
  )
3127
3197
  );
3128
3198
  }
3199
+ function printExportPickupNudge(opts) {
3200
+ console.log(" " + chalk7.dim(voiceExportPickupNudge(opts)));
3201
+ }
3129
3202
  function printHandoffDelivered(event, opts = {}) {
3130
3203
  persistHandoffSkillFiles(event);
3131
3204
  console.log();
3132
3205
  console.log(" " + paint("accent", `Handoff ready (${kindLabel(event.kind)})`));
3133
- maybePrintAiInboxNudge();
3206
+ printExportPickupNudge({ inboxReady: Boolean(event.inbox_path) });
3134
3207
  if (opts.body) {
3135
3208
  console.log(" " + chalk7.dim("File body (--print)"));
3136
3209
  printPlainBlock(opts.body);
@@ -3162,6 +3235,7 @@ var init_handoff_skill = __esm({
3162
3235
  init_store();
3163
3236
  init_theme();
3164
3237
  init_export_kinds();
3238
+ init_copy();
3165
3239
  STANDING_SKILL_NAME = "SKILL.md";
3166
3240
  ARCHIVE_PICKUP_NAME = "pickup.md";
3167
3241
  INBOX_PICKUP_NAME = "latest-pickup.md";
@@ -10686,7 +10760,7 @@ ${strategy.objective}
10686
10760
  ` : "";
10687
10761
  const workstreamSection = strategy.workstreams.length > 0 ? `
10688
10762
  ## Workstreams
10689
- ${strategy.workstreams.map(formatWorkstream).join("\n")}
10763
+ ${strategy.workstreams.map(formatWorkstreamMarkdown).join("\n")}
10690
10764
  ` : "";
10691
10765
  const constraintsSection = filterOperatorLines(strategy.constraints).length > 0 ? `
10692
10766
  ## Constraints
@@ -10742,7 +10816,7 @@ ${strategy.objective}
10742
10816
  ` : "";
10743
10817
  const workstreamSection = strategy.workstreams.length > 0 ? `
10744
10818
  ## Workstreams
10745
- ${strategy.workstreams.map(formatWorkstream).join("\n")}
10819
+ ${strategy.workstreams.map(formatWorkstreamMarkdown).join("\n")}
10746
10820
  ` : "";
10747
10821
  const assumptions = filterOperatorLines(strategy.assumptions);
10748
10822
  const assumptionsSection = assumptions.length > 0 ? `
@@ -10789,7 +10863,7 @@ ${frontmatter}
10789
10863
  ].filter((p) => p != null && String(p).length > 0);
10790
10864
  return parts.join("\n\n") + "\n";
10791
10865
  }
10792
- function formatWorkstream(ws) {
10866
+ function formatWorkstreamMarkdown(ws) {
10793
10867
  const lines = [];
10794
10868
  lines.push(`### ${ws.order}. ${ws.title}`);
10795
10869
  lines.push(`- Problem: ${ws.problem}`);
@@ -12152,6 +12226,7 @@ function resetContextForSwitch(ctx, opts) {
12152
12226
  ctx.thinkState = opts.thinkState;
12153
12227
  ctx.pendingAsk = opts.pendingAsk;
12154
12228
  ctx.lastCraftJobId = opts.lastCraftJobId;
12229
+ ctx.lastStrategyBrief = void 0;
12155
12230
  ctx.gapAudit = void 0;
12156
12231
  ctx.deliverIntent = false;
12157
12232
  ctx.computeInProgress = false;
@@ -14827,6 +14902,8 @@ var init_guide_slides = __esm({
14827
14902
  "",
14828
14903
  "Hit Enter by accident on \u23CE yes? Type b or back \u2014 that rewinds to the focus card. The same word leaves a strategy or think overlay. It does not unload demo data or undo compute; /scratch or a new session is the blunt reset.",
14829
14904
  "",
14905
+ "After a strategy brief, the terminal shows the call, the exams, the next date, and what ships. Type more or expand for the full plan. The library and handoff files already hold every step.",
14906
+ "",
14830
14907
  "Empty dataset? Load a sample with \u23CE use demo data, paste a CSV path, or /ingest. When the gap card says the formulas can compute, \u23CE go ahead runs the local math.",
14831
14908
  "",
14832
14909
  "Vital signs and SaaS metrics compute without an AI key. Every score that has a dollar translation shows it \u2014 that's the point of the stethoscope."
@@ -14835,7 +14912,7 @@ var init_guide_slides = __esm({
14835
14912
  "Power-user slash commands still work. They stay hidden from /help: /new, /diagnose, /metrics, /ingest, /session.",
14836
14913
  '"use demo data" loads a fitted sample book. Company profile is optional. In scripts, /demo --no-profile.',
14837
14914
  'After compute, the prompt returns to \u203A. Brief is the default depth; type "go deep" for the long read.',
14838
- "/home shows phase status. /help lists conversation shortcuts and the Keys legend (\u23CE, b/back, cancel)."
14915
+ "/home shows phase status. /help lists conversation shortcuts and the Keys legend (\u23CE, b/back, more/expand, cancel)."
14839
14916
  ]
14840
14917
  };
14841
14918
  ASK = {
@@ -14881,7 +14958,7 @@ var init_guide_slides = __esm({
14881
14958
  lines: [
14882
14959
  'We think of handoff as a quiet bridge: NTRP writes a board deck or brief into a folder you choose; your desktop AI (Claude, ChatGPT, Cursor, \u2026) opens it. You teach that AI a standing finder once \u2014 then later you just say "pick up the latest NTRP handoff."',
14883
14960
  "",
14884
- '"ship a board deck" or /handoff lands files in ~/Documents/ntrp-inbox by default (/inbox set to change). During demo or company setup \u2014 or anytime \u2014 /inbox skill prints the finder to paste once. After that, each /handoff prints "Handoff ready" without a paste block every write.',
14961
+ '"ship a board deck" or /handoff lands files in ~/Documents/ntrp-inbox by default (/inbox set to change). During demo or company setup \u2014 or anytime \u2014 /inbox skill prints the finder to paste once. After that, each write prints "Handoff ready" without a paste block, then tells you to leave this terminal and pick up the latest NTRP handoff in your desktop AI.',
14885
14962
  "",
14886
14963
  "Skipped earlier? We ask once when you load your own data. Or /inbox set ~/Documents/ntrp-inbox, then /inbox skill. /handoff skill reprints the same finder."
14887
14964
  ],
@@ -14911,6 +14988,8 @@ var init_guide_slides = __esm({
14911
14988
  "",
14912
14989
  "At the confirm card, \u23CE yes builds that plan once. Type keep going to keep working until the plan is ready \u2014 the same armed-Enter pattern as use demo data and go ahead. If you see Best so far, the prompt returns to \u203A and \u23CE keep going continues. Plan ready? Type /strategy review later to check milestones against new vitals.",
14913
14990
  "",
14991
+ "The printed brief is short on purpose: the call, the exams, the next date, and what ships. Type more (or expand) for the full plan. The library and handoff files already hold every step.",
14992
+ "",
14914
14993
  "When a vital is red, /playbook names a matching play \u2014 and outcomes from reviews annotate the catalog with what actually hit here. /remember stores a durable fact; /rate bad <reason> writes a calibration. Optional ~/.ntrp/ANALYST.md is standing voice and priorities (it never overrides safety rules).",
14915
14994
  "",
14916
14995
  "Handy later: /sessions show, /session <id> pickup, /end (transcript + brief), /home, /progress, /help."
@@ -30500,20 +30579,7 @@ function renderCraftHandoffMarkdown(opts) {
30500
30579
  lines.push("## Workstreams");
30501
30580
  lines.push("");
30502
30581
  for (const ws of plan.workstreams) {
30503
- lines.push(`### ${ws.order}. ${ws.title}`);
30504
- lines.push("");
30505
- lines.push(`- Problem: ${ws.problem}`);
30506
- if (ws.rationale && !isGenericWorkstreamRationale(ws.rationale)) {
30507
- lines.push(`- Why this order: ${ws.rationale}`);
30508
- }
30509
- if (ws.actions[0]) lines.push(`- First action (48h): ${ws.actions[0]}`);
30510
- lines.push(`- Effort: ~${Math.round(ws.effort_hours)} team-hours`);
30511
- lines.push(
30512
- `- Exam: ${ws.expected_outcome.metric} ${ws.expected_outcome.baseline} \u2192 ${ws.expected_outcome.target_range} by ${ws.expected_outcome.check_date} (${ws.expected_outcome.measured_by})`
30513
- );
30514
- lines.push(
30515
- `- Contingency: if ${ws.contingency.trigger} (check ${ws.contingency.trigger_check_date}) \u2192 ${ws.contingency.fallback}`
30516
- );
30582
+ lines.push(formatWorkstreamMarkdown(ws).trimEnd());
30517
30583
  lines.push("");
30518
30584
  }
30519
30585
  if (opts.roundtableDigest) {
@@ -30793,93 +30859,183 @@ var init_llm_attribution = __esm({
30793
30859
 
30794
30860
  // src/output/strategy-brief.ts
30795
30861
  import chalk24 from "chalk";
30796
- function printWrapped(text, width, prefix = INDENT, style) {
30862
+ function collectWrapped(lines, text, width, prefix = INDENT, style) {
30797
30863
  for (const line of wrapWords(text, width)) {
30798
- console.log(prefix + (style ? style(line) : line));
30864
+ lines.push(prefix + (style ? style(line) : line));
30799
30865
  }
30800
30866
  }
30801
- function outcomeLine(outcome) {
30802
- return `${chalk24.bold(outcome.metric)}: ${outcome.baseline} ${chalk24.dim("\u2192")} ${chalk24.bold(outcome.target_range)} ${chalk24.dim(`by ${outcome.check_date}`)}`;
30867
+ function outcomeLine(outcome, dateStyle) {
30868
+ const by = dateStyle === "accent" ? paint("accent", `by ${outcome.check_date}`) : chalk24.dim(`by ${outcome.check_date}`);
30869
+ return `${chalk24.bold(outcome.metric)}: ${outcome.baseline} ${chalk24.dim("\u2192")} ${chalk24.bold(outcome.target_range)} ${by}`;
30870
+ }
30871
+ function soonestByDue(items) {
30872
+ if (items.length === 0) return void 0;
30873
+ return [...items].sort((a, b) => a.due.localeCompare(b.due) || 0)[0];
30803
30874
  }
30804
- function printWorkstream(ws, width) {
30805
- console.log(`${INDENT}${paint("accent", `${ws.order}.`)} ${chalk24.bold(ws.title)}`);
30806
- printWrapped(ws.problem, width - 5, INDENT + " ", (s) => chalk24.dim(s));
30807
- console.log(`${INDENT} ${outcomeLine(ws.expected_outcome)}`);
30875
+ function altitudeLine(text) {
30876
+ const sentence = findingHeadline(text) || text.trim();
30877
+ return truncateVisible(sentence, FOH_PROSE_MAX);
30878
+ }
30879
+ function coverageLine(plan, stats) {
30880
+ const totalHours = plan.workstreams.reduce((sum, ws) => sum + ws.effort_hours, 0);
30881
+ const hours = `~${Math.round(totalHours)} total team hours across ${plan.workstreams.length} workstream${plan.workstreams.length === 1 ? "" : "s"}`;
30882
+ if (stats.total_targets <= 0) return chalk24.dim(hours);
30883
+ const coverage = `${stats.measurable_targets} of ${stats.total_targets} targets can be measured with current data`;
30884
+ const coverageStyled = stats.measurable_targets === stats.total_targets ? paint("success", coverage) : chalk24.hex("#eab308")(coverage);
30885
+ return `${coverageStyled}${chalk24.dim(` \xB7 ${hours}`)}`;
30886
+ }
30887
+ function renderFohWorkstream(ws, width) {
30888
+ const lines = [];
30889
+ lines.push(`${INDENT}${paint("accent", `${ws.order}.`)} ${chalk24.bold(ws.title)}`);
30890
+ lines.push(`${INDENT} ${outcomeLine(ws.expected_outcome, "accent")}`);
30891
+ const next = soonestByDue(ws.milestones);
30892
+ if (next) {
30893
+ lines.push(`${INDENT} Next ${paint("accent", next.due)} ${next.label}`);
30894
+ }
30895
+ const ship = soonestByDue(ws.deliverables);
30896
+ if (ship) {
30897
+ lines.push(`${INDENT} Ship ${paint("accent", ship.due)} ${ship.label}`);
30898
+ }
30899
+ lines.push("");
30900
+ return lines;
30901
+ }
30902
+ function renderBohWorkstream(ws, width) {
30903
+ const lines = [];
30904
+ lines.push(`${INDENT}${paint("accent", `${ws.order}.`)} ${chalk24.bold(ws.title)}`);
30905
+ collectWrapped(lines, ws.problem, width - 5, INDENT + " ", (s) => chalk24.dim(s));
30906
+ lines.push(`${INDENT} ${outcomeLine(ws.expected_outcome, "dim")}`);
30808
30907
  for (const li of ws.leading_indicators) {
30809
- console.log(`${INDENT} ${chalk24.dim("Lead:")} ${outcomeLine(li)}`);
30908
+ lines.push(`${INDENT} ${chalk24.dim("Lead:")} ${outcomeLine(li, "dim")}`);
30810
30909
  }
30811
30910
  if (ws.actions.length > 0) {
30812
- console.log(`${INDENT} ${chalk24.dim("First steps")}`);
30911
+ lines.push(`${INDENT} ${chalk24.dim("First steps")}`);
30813
30912
  for (const action of ws.actions.slice(0, 3)) {
30814
- printWrapped(`- ${action}`, width - 7, INDENT + " ", (s) => chalk24.dim(s));
30913
+ collectWrapped(lines, `- ${action}`, width - 7, INDENT + " ", (s) => chalk24.dim(s));
30815
30914
  }
30816
30915
  }
30817
30916
  if (ws.milestones.length > 0) {
30818
- console.log(`${INDENT} ${chalk24.dim("Milestones")}`);
30917
+ lines.push(`${INDENT} ${chalk24.dim("Milestones")}`);
30819
30918
  for (const m of ws.milestones) {
30820
- console.log(`${INDENT} ${paint("accent", m.due)} ${m.label}`);
30919
+ lines.push(`${INDENT} ${paint("accent", m.due)} ${m.label}`);
30821
30920
  }
30822
30921
  }
30823
30922
  if (ws.deliverables.length > 0) {
30824
- console.log(`${INDENT} ${chalk24.dim("Deliverables")}`);
30923
+ lines.push(`${INDENT} ${chalk24.dim("Deliverables")}`);
30825
30924
  for (const d of ws.deliverables) {
30826
- console.log(`${INDENT} ${chalk24.dim("[ ]")} ${d.label} ${chalk24.dim(`due ${d.due}`)}`);
30925
+ lines.push(`${INDENT} ${chalk24.dim("[ ]")} ${d.label} ${chalk24.dim(`due ${d.due}`)}`);
30827
30926
  }
30828
30927
  }
30829
- printWrapped(
30928
+ collectWrapped(
30929
+ lines,
30830
30930
  `If ${ws.contingency.trigger} (check ${ws.contingency.trigger_check_date}), then ${ws.contingency.fallback}`,
30831
30931
  width - 5,
30832
30932
  INDENT + " ",
30833
30933
  (s) => chalk24.dim(s)
30834
30934
  );
30835
- console.log(`${INDENT} ${chalk24.dim(`~${Math.round(ws.effort_hours)} team hours`)}`);
30836
- console.log();
30935
+ lines.push(`${INDENT} ${chalk24.dim(`~${Math.round(ws.effort_hours)} team hours`)}`);
30936
+ lines.push("");
30937
+ return lines;
30837
30938
  }
30838
- function printStrategyBrief(plan, stats) {
30939
+ function renderOperatorList(title, items, width) {
30940
+ const lines = [];
30941
+ if (items.length === 0) return lines;
30942
+ lines.push(`${INDENT}${chalk24.dim(title)}`);
30943
+ for (const item of items) {
30944
+ collectWrapped(lines, `- ${item}`, width - 2, INDENT, (s) => chalk24.dim(s));
30945
+ }
30946
+ lines.push("");
30947
+ return lines;
30948
+ }
30949
+ function renderStrategyBrief(plan, stats, opts = {}) {
30950
+ const mode = opts.mode ?? "foh";
30839
30951
  const width = Math.min(termWidth() - 4, 92);
30840
- console.log();
30841
- console.log(
30952
+ const lines = [""];
30953
+ lines.push(
30842
30954
  `${INDENT}${chalk24.bold(`Strategy brief \u2014 ${plan.title}`)} ${chalk24.dim(`confidence ${plan.confidence.toFixed(2)} \xB7 ${plan.priority} priority \xB7 review ${plan.review_cadence.toLowerCase()}`)}`
30843
30955
  );
30844
- console.log(INDENT + chalk24.dim(hr(width)));
30845
- console.log(`${INDENT}${chalk24.dim("The Call")}`);
30846
- printWrapped(plan.summary_30k, width);
30847
- console.log();
30848
- printWrapped(`Objective: ${plan.objective}`, width, INDENT, (s) => paint("accent", s));
30849
- console.log();
30850
- for (const ws of plan.workstreams) {
30851
- printWorkstream(ws, width);
30852
- }
30853
- const constraints = filterOperatorLines(plan.constraints);
30854
- if (constraints.length > 0) {
30855
- console.log(`${INDENT}${chalk24.dim("Constraints")}`);
30856
- for (const c of constraints) {
30857
- printWrapped(`- ${c}`, width - 2, INDENT, (s) => chalk24.dim(s));
30956
+ if (mode === "foh") {
30957
+ collectWrapped(lines, altitudeLine(plan.summary_30k), width, INDENT);
30958
+ lines.push("");
30959
+ collectWrapped(lines, altitudeLine(plan.objective), width, INDENT, (s) => paint("accent", s));
30960
+ lines.push("");
30961
+ for (const ws of plan.workstreams) {
30962
+ lines.push(...renderFohWorkstream(ws, width));
30858
30963
  }
30859
- console.log();
30860
- }
30861
- const assumptions = filterOperatorLines(plan.assumptions);
30862
- if (assumptions.length > 0) {
30863
- console.log(`${INDENT}${chalk24.dim("Assumptions (not verified, not targets)")}`);
30864
- for (const a of assumptions) {
30865
- printWrapped(`- ${a}`, width - 2, INDENT, (s) => chalk24.dim(s));
30964
+ } else {
30965
+ lines.push(INDENT + chalk24.dim(hr(width)));
30966
+ lines.push(`${INDENT}${chalk24.dim("The Call")}`);
30967
+ collectWrapped(lines, plan.summary_30k, width);
30968
+ lines.push("");
30969
+ collectWrapped(lines, `Objective: ${plan.objective}`, width, INDENT, (s) => paint("accent", s));
30970
+ lines.push("");
30971
+ for (const ws of plan.workstreams) {
30972
+ lines.push(...renderBohWorkstream(ws, width));
30866
30973
  }
30867
- console.log();
30974
+ lines.push(
30975
+ ...renderOperatorList("Constraints", filterOperatorLines(plan.constraints), width)
30976
+ );
30977
+ lines.push(
30978
+ ...renderOperatorList(
30979
+ "Assumptions (not verified, not targets)",
30980
+ filterOperatorLines(plan.assumptions),
30981
+ width
30982
+ )
30983
+ );
30984
+ lines.push(...renderOperatorList("Risks", filterOperatorLines(plan.risks), width));
30868
30985
  }
30869
- const risks = filterOperatorLines(plan.risks);
30870
- if (risks.length > 0) {
30871
- console.log(`${INDENT}${chalk24.dim("Risks")}`);
30872
- for (const r of risks) {
30873
- printWrapped(`- ${r}`, width - 2, INDENT, (s) => chalk24.dim(s));
30874
- }
30875
- console.log();
30986
+ lines.push(INDENT + chalk24.dim(hr(width)));
30987
+ lines.push(`${INDENT}${coverageLine(plan, stats)}`);
30988
+ if (opts.hint) {
30989
+ lines.push(`${INDENT}${chalk24.dim(STRATEGY_BRIEF_EXPAND_HINT)}`);
30876
30990
  }
30877
- const totalHours = plan.workstreams.reduce((sum, ws) => sum + ws.effort_hours, 0);
30878
- console.log(INDENT + chalk24.dim(hr(width)));
30879
- const coverage = stats.total_targets > 0 ? `${stats.measurable_targets} of ${stats.total_targets} targets can be measured with current data` : "No measured targets";
30880
- const coverageStyled = stats.total_targets > 0 && stats.measurable_targets === stats.total_targets ? paint("success", coverage) : chalk24.hex("#eab308")(coverage);
30881
- console.log(`${INDENT}${coverageStyled}${chalk24.dim(` \xB7 ~${Math.round(totalHours)} total team hours across ${plan.workstreams.length} workstream${plan.workstreams.length === 1 ? "" : "s"}`)}`);
30882
- console.log();
30991
+ lines.push("");
30992
+ return lines.join("\n");
30993
+ }
30994
+ function printStrategyBrief(plan, stats, opts = {}) {
30995
+ console.log(renderStrategyBrief(plan, stats, opts));
30996
+ }
30997
+ function resolveStrategyBriefMode(host) {
30998
+ return isVerbose(host.execution) ? "boh" : "foh";
30999
+ }
31000
+ function presentStrategyBrief(host, plan, stats, notices = [], meta = {}) {
31001
+ const mode = resolveStrategyBriefMode(host);
31002
+ printStrategyBrief(plan, stats, { mode, hint: mode === "foh" && !host.oneShot });
31003
+ printStrategyBriefFooter(notices, meta);
31004
+ host.lastStrategyBrief = { plan, stats, notices, meta };
31005
+ }
31006
+ function expandLastStrategyBrief(host) {
31007
+ const last = host.lastStrategyBrief;
31008
+ if (!last) return false;
31009
+ printStrategyBrief(last.plan, last.stats, { mode: "boh", hint: false });
31010
+ printStrategyBriefFooter(last.notices, last.meta);
31011
+ return true;
31012
+ }
31013
+ function isBriefExpandInput(input) {
31014
+ return /^(more|expand)\s*[.!]?\s*$/i.test(input.trim());
31015
+ }
31016
+ function planFromStrategy(strategy) {
31017
+ return {
31018
+ title: strategy.title,
31019
+ objective: strategy.objective || strategy.goal,
31020
+ summary_30k: strategy.raw_excerpt || strategy.goal,
31021
+ hypothesis: strategy.hypothesis,
31022
+ target_segment: strategy.target_segment,
31023
+ priority: strategy.priority,
31024
+ review_cadence: strategy.review_cadence,
31025
+ confidence: strategy.confidence,
31026
+ constraints: strategy.constraints,
31027
+ assumptions: strategy.assumptions,
31028
+ risks: strategy.risks,
31029
+ workstreams: strategy.workstreams
31030
+ };
31031
+ }
31032
+ function statsFromPlan(plan) {
31033
+ let total = 0;
31034
+ for (const ws of plan.workstreams) {
31035
+ total += 1;
31036
+ total += ws.leading_indicators.length;
31037
+ }
31038
+ return { measurable_targets: total, total_targets: total };
30883
31039
  }
30884
31040
  function printStrategyBriefFooter(notices, meta) {
30885
31041
  if (usedGroundedFallback(notices)) {
@@ -30898,7 +31054,10 @@ function printCraftWrapUp(opts) {
30898
31054
  );
30899
31055
  }
30900
31056
  const handoff = opts.inbox_path ?? opts.handoff_path;
30901
- if (handoff) console.log(INDENT + chalk24.dim(handoff));
31057
+ if (handoff) {
31058
+ console.log(INDENT + chalk24.dim(handoff));
31059
+ printExportPickupNudge({ inboxReady: Boolean(opts.inbox_path) });
31060
+ }
30902
31061
  } else {
30903
31062
  console.log(INDENT + paint("accent", "Best so far."));
30904
31063
  const path = opts.library_path ?? opts.handoff_path ?? opts.inbox_path;
@@ -30918,15 +31077,20 @@ function printCraftWrapUp(opts) {
30918
31077
  }
30919
31078
  console.log();
30920
31079
  }
30921
- var INDENT;
31080
+ var STRATEGY_BRIEF_EXPAND_HINT, INDENT, FOH_PROSE_MAX;
30922
31081
  var init_strategy_brief = __esm({
30923
31082
  "src/output/strategy-brief.ts"() {
30924
31083
  "use strict";
30925
31084
  init_theme();
30926
31085
  init_layout();
31086
+ init_formatters();
30927
31087
  init_strategy_signal();
30928
31088
  init_llm_attribution();
31089
+ init_handoff_skill();
31090
+ init_verbosity();
31091
+ STRATEGY_BRIEF_EXPAND_HINT = "Type more to show the full brief.";
30929
31092
  INDENT = " ";
31093
+ FOH_PROSE_MAX = 220;
30930
31094
  }
30931
31095
  });
30932
31096
 
@@ -31287,8 +31451,7 @@ async function runStrategistSession(ctx) {
31287
31451
  console.log();
31288
31452
  return;
31289
31453
  }
31290
- printStrategyBrief(plan, stats);
31291
- printStrategyBriefFooter(notices, meta);
31454
+ presentStrategyBrief(ctx, plan, stats, notices, meta);
31292
31455
  console.log();
31293
31456
  let saved = false;
31294
31457
  if (ctx.rl) {
@@ -31347,14 +31510,14 @@ async function ensureSnapshot(ctx) {
31347
31510
  }
31348
31511
  function printCraftReplResult(ctx, result, objective) {
31349
31512
  if (result.plan) {
31350
- printStrategyBrief(result.plan, {
31513
+ presentStrategyBrief(ctx, result.plan, {
31351
31514
  measurable_targets: result.measurable_targets,
31352
31515
  total_targets: result.total_targets
31353
- });
31516
+ }, result.notices, result.usage);
31354
31517
  } else {
31355
31518
  console.log(" " + chalk25.dim("(No plan produced)"));
31519
+ printStrategyBriefFooter(result.notices, result.usage);
31356
31520
  }
31357
- printStrategyBriefFooter(result.notices, result.usage);
31358
31521
  if (result.library_path) creditStrategySession(ctx);
31359
31522
  printCraftWrapUp({
31360
31523
  status: result.status,
@@ -31964,7 +32127,7 @@ function matchDefinitionExplainer(input) {
31964
32127
  }
31965
32128
  return getMetricExplainer(id);
31966
32129
  }
31967
- function printWrapped2(text, indent = " ") {
32130
+ function printWrapped(text, indent = " ") {
31968
32131
  for (const line of wrapWords(text, 78)) {
31969
32132
  console.log(indent + line);
31970
32133
  }
@@ -31981,10 +32144,10 @@ function tryKeylessDefinitionAnswer(ctx, input) {
31981
32144
  console.log(" " + chalk29.dim(explainer.tagline));
31982
32145
  console.log();
31983
32146
  console.log(" " + bold("Meaning"));
31984
- printWrapped2(explainer.meaning, " ");
32147
+ printWrapped(explainer.meaning, " ");
31985
32148
  console.log();
31986
32149
  console.log(" " + bold("How NTRP calculates it"));
31987
- printWrapped2(explainer.how_computed, " ");
32150
+ printWrapped(explainer.how_computed, " ");
31988
32151
  for (const f of explainer.formula_lines) {
31989
32152
  console.log(" " + paint("accent", f));
31990
32153
  }
@@ -32055,8 +32218,6 @@ async function advanceAfterScopeConfirm(ctx) {
32055
32218
  const offered = await offerDemoToAnswer(ctx);
32056
32219
  if (offered) return "Demo loaded and analysis started";
32057
32220
  if (audit.can_compute) {
32058
- console.log();
32059
- console.log(" " + chalk30.dim("Computing so I can answer\u2026"));
32060
32221
  await runConversationCompute(ctx);
32061
32222
  recordMessage(ctx, "agent", "Scope confirmed. Analysis started.");
32062
32223
  return "Analysis started";
@@ -33346,12 +33507,6 @@ async function resumePendingAsk(ctx) {
33346
33507
  if (!pending?.text) return false;
33347
33508
  ctx.computeInProgress = false;
33348
33509
  if (canUseReplAi(ctx)) {
33349
- console.log();
33350
- console.log(
33351
- " " + chalk32.dim(
33352
- pending.keylessAnswered ? "Picking up your question with the connected engine\u2026" : "Picking up your question\u2026"
33353
- )
33354
- );
33355
33510
  console.log();
33356
33511
  const { runNaturalLanguage: runNaturalLanguage3 } = await Promise.resolve().then(() => (init_nl(), nl_exports));
33357
33512
  await runNaturalLanguage3(pending.text, ctx);
@@ -33633,7 +33788,7 @@ async function runDiagnoseTrace(options) {
33633
33788
  kind: "phase_end",
33634
33789
  phase: "compute",
33635
33790
  durationMs: Date.now() - computeStarted,
33636
- summary: "Vital signs computed"
33791
+ summary: "Pipeline scored"
33637
33792
  });
33638
33793
  computeEnded = true;
33639
33794
  }
@@ -33663,7 +33818,7 @@ async function runDiagnoseTrace(options) {
33663
33818
  kind: "phase_end",
33664
33819
  phase: "compute",
33665
33820
  durationMs: Date.now() - computeStarted,
33666
- summary: "Vital signs computed"
33821
+ summary: "Pipeline scored"
33667
33822
  });
33668
33823
  }
33669
33824
  if (fullResult && fullResult.segments.length > 0) {
@@ -33671,7 +33826,7 @@ async function runDiagnoseTrace(options) {
33671
33826
  kind: "phase_end",
33672
33827
  phase: "segments",
33673
33828
  durationMs: Date.now() - (segmentsStarted ?? Date.now()),
33674
- summary: `Segments computed \xB7 ${fullResult.segments.length}`
33829
+ summary: `${fullResult.segments.length} segment${fullResult.segments.length === 1 ? "" : "s"}`
33675
33830
  });
33676
33831
  }
33677
33832
  if (!fullResult) {
@@ -33777,7 +33932,7 @@ async function runDiagnoseTrace(options) {
33777
33932
  });
33778
33933
  const reportPath = ctx ? await writeSilentMarkdownReport(ctx, fullResult, collectedFindings) : null;
33779
33934
  if (reportPath) {
33780
- bus.emit({ kind: "artifact", path: reportPath, description: "Full report" });
33935
+ bus.emit({ kind: "artifact", path: reportPath, description: "Saved report" });
33781
33936
  } else if (ctx?.events instanceof EventBus) {
33782
33937
  bus.emit({ kind: "artifact", path: logPathForRun(ctx.events.runId), description: "Run log" });
33783
33938
  }
@@ -33998,7 +34153,7 @@ async function runSegmentDiagnose(options, ctx) {
33998
34153
  kind: "phase_end",
33999
34154
  phase: "compute",
34000
34155
  durationMs: Date.now() - t0,
34001
- summary: "Vital signs computed"
34156
+ summary: "Pipeline scored"
34002
34157
  });
34003
34158
  } catch (err) {
34004
34159
  bus.emit({ kind: "debug", message: String(err) });
@@ -34048,7 +34203,7 @@ async function runSegmentDiagnose(options, ctx) {
34048
34203
  }
34049
34204
  const reportPath = await writeSilentMarkdownReport2(ctx, result, []);
34050
34205
  if (reportPath) {
34051
- bus.emit({ kind: "artifact", path: reportPath, description: "Full report" });
34206
+ bus.emit({ kind: "artifact", path: reportPath, description: "Saved report" });
34052
34207
  }
34053
34208
  return buildDiagnoseSummary(match.result, []);
34054
34209
  }
@@ -36679,6 +36834,7 @@ async function handler10(args, _ctx) {
36679
36834
  if (event.inbox_path) {
36680
36835
  console.log(chalk43.dim(` Inbox: ${event.inbox_path}`));
36681
36836
  }
36837
+ printExportPickupNudge({ inboxReady: Boolean(event.inbox_path) });
36682
36838
  console.log();
36683
36839
  return filepath;
36684
36840
  } catch (err) {
@@ -36695,6 +36851,7 @@ var init_export = __esm({
36695
36851
  init_path_safety();
36696
36852
  init_argparse();
36697
36853
  init_exports_registry();
36854
+ init_handoff_skill();
36698
36855
  init_segments();
36699
36856
  }
36700
36857
  });
@@ -36793,6 +36950,7 @@ async function handler11(args, _ctx) {
36793
36950
  if (event.inbox_path) {
36794
36951
  console.log(chalk44.dim(` Inbox: ${event.inbox_path}`));
36795
36952
  }
36953
+ printExportPickupNudge({ inboxReady: Boolean(event.inbox_path) });
36796
36954
  console.log();
36797
36955
  return folder;
36798
36956
  } catch (err) {
@@ -36810,6 +36968,7 @@ var init_backmeup = __esm({
36810
36968
  init_formatters();
36811
36969
  init_path_safety();
36812
36970
  init_exports_registry();
36971
+ init_handoff_skill();
36813
36972
  EVIDENCE_FILENAMES = {
36814
36973
  freshness: "freshness.csv",
36815
36974
  flow_rate: "flow-rate.csv",
@@ -38394,7 +38553,7 @@ async function handler16(args, ctx) {
38394
38553
  emitResult("strategy", result);
38395
38554
  return;
38396
38555
  }
38397
- renderStrategyResult(result);
38556
+ renderStrategyResult(result, ctx);
38398
38557
  } catch (err) {
38399
38558
  if (isStructuredOutput(ctx.execution)) emitError(first === "craft" ? "strategy.craft" : "strategy", err);
38400
38559
  console.error(chalk50.red(` ${err instanceof Error ? err.message : String(err)}`));
@@ -38550,10 +38709,10 @@ function craftJsonData(result) {
38550
38709
  stop_reason: result.stop_reason ?? null
38551
38710
  };
38552
38711
  }
38553
- function renderStrategyResult(result) {
38712
+ function renderStrategyResult(result, ctx) {
38554
38713
  switch (result.action) {
38555
38714
  case "craft":
38556
- renderCraftResult(result);
38715
+ renderCraftResult(result, ctx);
38557
38716
  return;
38558
38717
  case "review":
38559
38718
  return;
@@ -38569,8 +38728,7 @@ function renderStrategyResult(result) {
38569
38728
  printStrategyList(result.strategies);
38570
38729
  return;
38571
38730
  case "show":
38572
- console.log(chalk50.bold("\n Strategy\n"));
38573
- printStrategyDetail(result.strategy);
38731
+ printStrategyShow(result.strategy, ctx);
38574
38732
  if (result.sources.length > 0) {
38575
38733
  console.log(chalk50.hex("#14b8a6")(" Sources"));
38576
38734
  for (const source of result.sources) {
@@ -38587,17 +38745,17 @@ function renderStrategyResult(result) {
38587
38745
  return;
38588
38746
  }
38589
38747
  }
38590
- function renderCraftResult(result) {
38748
+ function renderCraftResult(result, ctx) {
38591
38749
  if (result.plan) {
38592
- printStrategyBrief(result.plan, {
38750
+ presentStrategyBrief(ctx, result.plan, {
38593
38751
  measurable_targets: result.measurable_targets,
38594
38752
  total_targets: result.total_targets
38595
- });
38753
+ }, result.notices, result.usage);
38596
38754
  } else {
38597
38755
  console.log();
38598
38756
  console.log(" " + chalk50.dim("(No plan produced)"));
38757
+ printStrategyBriefFooter(result.notices, result.usage);
38599
38758
  }
38600
- printStrategyBriefFooter(result.notices, result.usage);
38601
38759
  printCraftWrapUp({
38602
38760
  status: result.status,
38603
38761
  library_path: result.library_path,
@@ -38628,29 +38786,21 @@ function printStrategySummary(strategy) {
38628
38786
  console.log(` ${strategy.goal}`);
38629
38787
  console.log();
38630
38788
  }
38631
- function printStrategyDetail(strategy) {
38632
- printStrategySummary(strategy);
38633
- console.log(chalk50.hex("#14b8a6")(" Hypothesis"));
38634
- console.log(` ${strategy.hypothesis}`);
38635
- console.log();
38636
- console.log(chalk50.hex("#14b8a6")(" Target Segment"));
38637
- console.log(` ${strategy.target_segment}`);
38638
- console.log();
38639
- if (strategy.linked_play_ids.length > 0) {
38640
- console.log(chalk50.hex("#14b8a6")(" Linked Plays"));
38641
- console.log(` ${strategy.linked_play_ids.join(", ")}`);
38642
- console.log();
38643
- }
38644
- if (strategy.success_metrics.length > 0) {
38645
- console.log(chalk50.hex("#14b8a6")(" Success Metrics"));
38646
- for (const metric of strategy.success_metrics) {
38647
- console.log(` - ${metric.name}${metric.target ? `: ${metric.target}` : ""}`);
38789
+ function printStrategyShow(strategy, ctx) {
38790
+ if (strategy.workstreams.length > 0) {
38791
+ const plan = planFromStrategy(strategy);
38792
+ presentStrategyBrief(ctx, plan, statsFromPlan(plan));
38793
+ if (strategy.library_path) {
38794
+ console.log(" " + chalk50.dim(strategy.library_path));
38795
+ console.log();
38648
38796
  }
38797
+ return;
38798
+ }
38799
+ printStrategySummary(strategy);
38800
+ if (strategy.library_path) {
38801
+ console.log(" " + chalk50.dim(strategy.library_path));
38649
38802
  console.log();
38650
38803
  }
38651
- console.log(chalk50.hex("#14b8a6")(" Experiment Design"));
38652
- console.log(` ${strategy.experiment_design}`);
38653
- console.log();
38654
38804
  }
38655
38805
  function statusBadge3(status) {
38656
38806
  switch (status) {
@@ -40129,7 +40279,7 @@ async function handler28(args, ctx) {
40129
40279
  bus.emit({
40130
40280
  kind: "artifact",
40131
40281
  path: logPathForRun(bus.runId),
40132
- description: "Run log"
40282
+ description: "Saved log"
40133
40283
  });
40134
40284
  } catch (err) {
40135
40285
  bus.emit({ kind: "debug", message: String(err) });
@@ -45071,8 +45221,6 @@ async function ingestFromChat(ctx, filePath) {
45071
45221
  printGapCard(audit);
45072
45222
  if (audit.can_compute && ctx.scope?.confirmed_at) {
45073
45223
  if (ctx.pendingAsk) {
45074
- console.log();
45075
- console.log(" " + chalk88.dim("Computing so I can answer\u2026"));
45076
45224
  await runConversationCompute(ctx);
45077
45225
  return true;
45078
45226
  }
@@ -45156,8 +45304,6 @@ async function loadDemoFromChat(ctx, scenario, opts = {}) {
45156
45304
  const audit = await refreshGapAudit(ctx);
45157
45305
  const shouldAuto = opts.autoCompute || Boolean(ctx.pendingAsk && audit.can_compute && ctx.scope?.confirmed_at);
45158
45306
  if (shouldAuto && audit.can_compute) {
45159
- console.log();
45160
- console.log(" " + chalk88.dim("Computing so I can answer\u2026"));
45161
45307
  await runConversationCompute(ctx);
45162
45308
  return true;
45163
45309
  }
@@ -45936,6 +46082,18 @@ function handleBackNavigation(ctx, line) {
45936
46082
  }
45937
46083
  return null;
45938
46084
  }
46085
+ function handleBriefExpand(ctx, line) {
46086
+ if (!isBriefExpandInput(line)) return null;
46087
+ if (!ctx.lastStrategyBrief) return null;
46088
+ const phase = resolveConversationPhase(ctx);
46089
+ if (phase === "scope" || phase === "awaiting_data" || phase === "strategize" || phase === "think" || phase === "deliver") {
46090
+ return null;
46091
+ }
46092
+ recordMessage(ctx, "user", line);
46093
+ expandLastStrategyBrief(ctx);
46094
+ recordMessage(ctx, "agent", "Full strategy brief");
46095
+ return { handled: true, summary: "Full strategy brief" };
46096
+ }
45939
46097
  async function conversationRouter(input, ctx) {
45940
46098
  if (ctx.oneShot || (ctx.wizardDepth ?? 0) > 0) {
45941
46099
  return { handled: false };
@@ -45995,6 +46153,8 @@ async function conversationRouter(input, ctx) {
45995
46153
  }
45996
46154
  const backResult = handleBackNavigation(ctx, line);
45997
46155
  if (backResult) return backResult;
46156
+ const expandResult = handleBriefExpand(ctx, line);
46157
+ if (expandResult) return expandResult;
45998
46158
  if (/^\S+$/.test(line) && !/^\d$/.test(line)) {
45999
46159
  const { resolveSessionByToken: resolveSessionByToken2 } = await Promise.resolve().then(() => (init_context2(), context_exports));
46000
46160
  if (resolveSessionByToken2(line, { printErrors: false }) !== void 0) {
@@ -46106,6 +46266,7 @@ var init_router = __esm({
46106
46266
  init_demo();
46107
46267
  init_scope();
46108
46268
  init_pending_ask();
46269
+ init_strategy_brief();
46109
46270
  FRESH_START_RE = /\b(start (over|fresh)|new analysis|start again|reset session)\b/i;
46110
46271
  CANCEL_ESCAPE_RE = /^(cancel|stop|abort|quit|never\s?mind|nevermind|forget it)\s*[.!]?\s*$/i;
46111
46272
  }
@@ -47210,6 +47371,9 @@ function printHelp() {
47210
47371
  console.log(
47211
47372
  " " + chalk96.dim("On ") + paint("accent", "/settings") + chalk96.dim(" menus, ") + paint("accent", "b") + chalk96.dim(" leaves the hub (same as Done).")
47212
47373
  );
47374
+ console.log(
47375
+ " " + paint("accent", "more") + chalk96.dim(" / ") + paint("accent", "expand") + chalk96.dim(" Shows the full strategy brief after the short summary.")
47376
+ );
47213
47377
  console.log(
47214
47378
  " " + paint("accent", "cancel") + chalk96.dim(" Drops the current modal overlay (strategy, think, ship, unconfirmed scope).")
47215
47379
  );