@sonnechasser/ntrp 2.1.11 → 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.
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
 
@@ -2925,6 +2957,7 @@ __export(handoff_skill_exports, {
2925
2957
  persistHandoffSkillFiles: () => persistHandoffSkillFiles,
2926
2958
  persistStandingSkill: () => persistStandingSkill,
2927
2959
  pickupContextFromEvent: () => pickupContextFromEvent,
2960
+ printExportPickupNudge: () => printExportPickupNudge,
2928
2961
  printHandoffDelivered: () => printHandoffDelivered,
2929
2962
  printStandingSkill: () => printStandingSkill
2930
2963
  });
@@ -3163,11 +3196,14 @@ function maybePrintAiInboxNudge() {
3163
3196
  )
3164
3197
  );
3165
3198
  }
3199
+ function printExportPickupNudge(opts) {
3200
+ console.log(" " + chalk7.dim(voiceExportPickupNudge(opts)));
3201
+ }
3166
3202
  function printHandoffDelivered(event, opts = {}) {
3167
3203
  persistHandoffSkillFiles(event);
3168
3204
  console.log();
3169
3205
  console.log(" " + paint("accent", `Handoff ready (${kindLabel(event.kind)})`));
3170
- maybePrintAiInboxNudge();
3206
+ printExportPickupNudge({ inboxReady: Boolean(event.inbox_path) });
3171
3207
  if (opts.body) {
3172
3208
  console.log(" " + chalk7.dim("File body (--print)"));
3173
3209
  printPlainBlock(opts.body);
@@ -3199,6 +3235,7 @@ var init_handoff_skill = __esm({
3199
3235
  init_store();
3200
3236
  init_theme();
3201
3237
  init_export_kinds();
3238
+ init_copy();
3202
3239
  STANDING_SKILL_NAME = "SKILL.md";
3203
3240
  ARCHIVE_PICKUP_NAME = "pickup.md";
3204
3241
  INBOX_PICKUP_NAME = "latest-pickup.md";
@@ -10723,7 +10760,7 @@ ${strategy.objective}
10723
10760
  ` : "";
10724
10761
  const workstreamSection = strategy.workstreams.length > 0 ? `
10725
10762
  ## Workstreams
10726
- ${strategy.workstreams.map(formatWorkstream).join("\n")}
10763
+ ${strategy.workstreams.map(formatWorkstreamMarkdown).join("\n")}
10727
10764
  ` : "";
10728
10765
  const constraintsSection = filterOperatorLines(strategy.constraints).length > 0 ? `
10729
10766
  ## Constraints
@@ -10779,7 +10816,7 @@ ${strategy.objective}
10779
10816
  ` : "";
10780
10817
  const workstreamSection = strategy.workstreams.length > 0 ? `
10781
10818
  ## Workstreams
10782
- ${strategy.workstreams.map(formatWorkstream).join("\n")}
10819
+ ${strategy.workstreams.map(formatWorkstreamMarkdown).join("\n")}
10783
10820
  ` : "";
10784
10821
  const assumptions = filterOperatorLines(strategy.assumptions);
10785
10822
  const assumptionsSection = assumptions.length > 0 ? `
@@ -10826,7 +10863,7 @@ ${frontmatter}
10826
10863
  ].filter((p) => p != null && String(p).length > 0);
10827
10864
  return parts.join("\n\n") + "\n";
10828
10865
  }
10829
- function formatWorkstream(ws) {
10866
+ function formatWorkstreamMarkdown(ws) {
10830
10867
  const lines = [];
10831
10868
  lines.push(`### ${ws.order}. ${ws.title}`);
10832
10869
  lines.push(`- Problem: ${ws.problem}`);
@@ -12189,6 +12226,7 @@ function resetContextForSwitch(ctx, opts) {
12189
12226
  ctx.thinkState = opts.thinkState;
12190
12227
  ctx.pendingAsk = opts.pendingAsk;
12191
12228
  ctx.lastCraftJobId = opts.lastCraftJobId;
12229
+ ctx.lastStrategyBrief = void 0;
12192
12230
  ctx.gapAudit = void 0;
12193
12231
  ctx.deliverIntent = false;
12194
12232
  ctx.computeInProgress = false;
@@ -14864,6 +14902,8 @@ var init_guide_slides = __esm({
14864
14902
  "",
14865
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.",
14866
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
+ "",
14867
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.",
14868
14908
  "",
14869
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."
@@ -14872,7 +14912,7 @@ var init_guide_slides = __esm({
14872
14912
  "Power-user slash commands still work. They stay hidden from /help: /new, /diagnose, /metrics, /ingest, /session.",
14873
14913
  '"use demo data" loads a fitted sample book. Company profile is optional. In scripts, /demo --no-profile.',
14874
14914
  'After compute, the prompt returns to \u203A. Brief is the default depth; type "go deep" for the long read.',
14875
- "/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)."
14876
14916
  ]
14877
14917
  };
14878
14918
  ASK = {
@@ -14918,7 +14958,7 @@ var init_guide_slides = __esm({
14918
14958
  lines: [
14919
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."',
14920
14960
  "",
14921
- '"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.',
14922
14962
  "",
14923
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."
14924
14964
  ],
@@ -14948,6 +14988,8 @@ var init_guide_slides = __esm({
14948
14988
  "",
14949
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.",
14950
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
+ "",
14951
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).",
14952
14994
  "",
14953
14995
  "Handy later: /sessions show, /session <id> pickup, /end (transcript + brief), /home, /progress, /help."
@@ -30537,20 +30579,7 @@ function renderCraftHandoffMarkdown(opts) {
30537
30579
  lines.push("## Workstreams");
30538
30580
  lines.push("");
30539
30581
  for (const ws of plan.workstreams) {
30540
- lines.push(`### ${ws.order}. ${ws.title}`);
30541
- lines.push("");
30542
- lines.push(`- Problem: ${ws.problem}`);
30543
- if (ws.rationale && !isGenericWorkstreamRationale(ws.rationale)) {
30544
- lines.push(`- Why this order: ${ws.rationale}`);
30545
- }
30546
- if (ws.actions[0]) lines.push(`- First action (48h): ${ws.actions[0]}`);
30547
- lines.push(`- Effort: ~${Math.round(ws.effort_hours)} team-hours`);
30548
- lines.push(
30549
- `- 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})`
30550
- );
30551
- lines.push(
30552
- `- Contingency: if ${ws.contingency.trigger} (check ${ws.contingency.trigger_check_date}) \u2192 ${ws.contingency.fallback}`
30553
- );
30582
+ lines.push(formatWorkstreamMarkdown(ws).trimEnd());
30554
30583
  lines.push("");
30555
30584
  }
30556
30585
  if (opts.roundtableDigest) {
@@ -30830,93 +30859,183 @@ var init_llm_attribution = __esm({
30830
30859
 
30831
30860
  // src/output/strategy-brief.ts
30832
30861
  import chalk24 from "chalk";
30833
- function printWrapped(text, width, prefix = INDENT, style) {
30862
+ function collectWrapped(lines, text, width, prefix = INDENT, style) {
30834
30863
  for (const line of wrapWords(text, width)) {
30835
- console.log(prefix + (style ? style(line) : line));
30864
+ lines.push(prefix + (style ? style(line) : line));
30836
30865
  }
30837
30866
  }
30838
- function outcomeLine(outcome) {
30839
- 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];
30874
+ }
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;
30840
30901
  }
30841
- function printWorkstream(ws, width) {
30842
- console.log(`${INDENT}${paint("accent", `${ws.order}.`)} ${chalk24.bold(ws.title)}`);
30843
- printWrapped(ws.problem, width - 5, INDENT + " ", (s) => chalk24.dim(s));
30844
- console.log(`${INDENT} ${outcomeLine(ws.expected_outcome)}`);
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")}`);
30845
30907
  for (const li of ws.leading_indicators) {
30846
- console.log(`${INDENT} ${chalk24.dim("Lead:")} ${outcomeLine(li)}`);
30908
+ lines.push(`${INDENT} ${chalk24.dim("Lead:")} ${outcomeLine(li, "dim")}`);
30847
30909
  }
30848
30910
  if (ws.actions.length > 0) {
30849
- console.log(`${INDENT} ${chalk24.dim("First steps")}`);
30911
+ lines.push(`${INDENT} ${chalk24.dim("First steps")}`);
30850
30912
  for (const action of ws.actions.slice(0, 3)) {
30851
- printWrapped(`- ${action}`, width - 7, INDENT + " ", (s) => chalk24.dim(s));
30913
+ collectWrapped(lines, `- ${action}`, width - 7, INDENT + " ", (s) => chalk24.dim(s));
30852
30914
  }
30853
30915
  }
30854
30916
  if (ws.milestones.length > 0) {
30855
- console.log(`${INDENT} ${chalk24.dim("Milestones")}`);
30917
+ lines.push(`${INDENT} ${chalk24.dim("Milestones")}`);
30856
30918
  for (const m of ws.milestones) {
30857
- console.log(`${INDENT} ${paint("accent", m.due)} ${m.label}`);
30919
+ lines.push(`${INDENT} ${paint("accent", m.due)} ${m.label}`);
30858
30920
  }
30859
30921
  }
30860
30922
  if (ws.deliverables.length > 0) {
30861
- console.log(`${INDENT} ${chalk24.dim("Deliverables")}`);
30923
+ lines.push(`${INDENT} ${chalk24.dim("Deliverables")}`);
30862
30924
  for (const d of ws.deliverables) {
30863
- 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}`)}`);
30864
30926
  }
30865
30927
  }
30866
- printWrapped(
30928
+ collectWrapped(
30929
+ lines,
30867
30930
  `If ${ws.contingency.trigger} (check ${ws.contingency.trigger_check_date}), then ${ws.contingency.fallback}`,
30868
30931
  width - 5,
30869
30932
  INDENT + " ",
30870
30933
  (s) => chalk24.dim(s)
30871
30934
  );
30872
- console.log(`${INDENT} ${chalk24.dim(`~${Math.round(ws.effort_hours)} team hours`)}`);
30873
- console.log();
30935
+ lines.push(`${INDENT} ${chalk24.dim(`~${Math.round(ws.effort_hours)} team hours`)}`);
30936
+ lines.push("");
30937
+ return lines;
30874
30938
  }
30875
- 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";
30876
30951
  const width = Math.min(termWidth() - 4, 92);
30877
- console.log();
30878
- console.log(
30952
+ const lines = [""];
30953
+ lines.push(
30879
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()}`)}`
30880
30955
  );
30881
- console.log(INDENT + chalk24.dim(hr(width)));
30882
- console.log(`${INDENT}${chalk24.dim("The Call")}`);
30883
- printWrapped(plan.summary_30k, width);
30884
- console.log();
30885
- printWrapped(`Objective: ${plan.objective}`, width, INDENT, (s) => paint("accent", s));
30886
- console.log();
30887
- for (const ws of plan.workstreams) {
30888
- printWorkstream(ws, width);
30889
- }
30890
- const constraints = filterOperatorLines(plan.constraints);
30891
- if (constraints.length > 0) {
30892
- console.log(`${INDENT}${chalk24.dim("Constraints")}`);
30893
- for (const c of constraints) {
30894
- 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));
30895
30963
  }
30896
- console.log();
30897
- }
30898
- const assumptions = filterOperatorLines(plan.assumptions);
30899
- if (assumptions.length > 0) {
30900
- console.log(`${INDENT}${chalk24.dim("Assumptions (not verified, not targets)")}`);
30901
- for (const a of assumptions) {
30902
- 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));
30903
30973
  }
30904
- 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));
30905
30985
  }
30906
- const risks = filterOperatorLines(plan.risks);
30907
- if (risks.length > 0) {
30908
- console.log(`${INDENT}${chalk24.dim("Risks")}`);
30909
- for (const r of risks) {
30910
- printWrapped(`- ${r}`, width - 2, INDENT, (s) => chalk24.dim(s));
30911
- }
30912
- 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)}`);
30913
30990
  }
30914
- const totalHours = plan.workstreams.reduce((sum, ws) => sum + ws.effort_hours, 0);
30915
- console.log(INDENT + chalk24.dim(hr(width)));
30916
- const coverage = stats.total_targets > 0 ? `${stats.measurable_targets} of ${stats.total_targets} targets can be measured with current data` : "No measured targets";
30917
- const coverageStyled = stats.total_targets > 0 && stats.measurable_targets === stats.total_targets ? paint("success", coverage) : chalk24.hex("#eab308")(coverage);
30918
- console.log(`${INDENT}${coverageStyled}${chalk24.dim(` \xB7 ~${Math.round(totalHours)} total team hours across ${plan.workstreams.length} workstream${plan.workstreams.length === 1 ? "" : "s"}`)}`);
30919
- 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 };
30920
31039
  }
30921
31040
  function printStrategyBriefFooter(notices, meta) {
30922
31041
  if (usedGroundedFallback(notices)) {
@@ -30935,7 +31054,10 @@ function printCraftWrapUp(opts) {
30935
31054
  );
30936
31055
  }
30937
31056
  const handoff = opts.inbox_path ?? opts.handoff_path;
30938
- 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
+ }
30939
31061
  } else {
30940
31062
  console.log(INDENT + paint("accent", "Best so far."));
30941
31063
  const path = opts.library_path ?? opts.handoff_path ?? opts.inbox_path;
@@ -30955,15 +31077,20 @@ function printCraftWrapUp(opts) {
30955
31077
  }
30956
31078
  console.log();
30957
31079
  }
30958
- var INDENT;
31080
+ var STRATEGY_BRIEF_EXPAND_HINT, INDENT, FOH_PROSE_MAX;
30959
31081
  var init_strategy_brief = __esm({
30960
31082
  "src/output/strategy-brief.ts"() {
30961
31083
  "use strict";
30962
31084
  init_theme();
30963
31085
  init_layout();
31086
+ init_formatters();
30964
31087
  init_strategy_signal();
30965
31088
  init_llm_attribution();
31089
+ init_handoff_skill();
31090
+ init_verbosity();
31091
+ STRATEGY_BRIEF_EXPAND_HINT = "Type more to show the full brief.";
30966
31092
  INDENT = " ";
31093
+ FOH_PROSE_MAX = 220;
30967
31094
  }
30968
31095
  });
30969
31096
 
@@ -31324,8 +31451,7 @@ async function runStrategistSession(ctx) {
31324
31451
  console.log();
31325
31452
  return;
31326
31453
  }
31327
- printStrategyBrief(plan, stats);
31328
- printStrategyBriefFooter(notices, meta);
31454
+ presentStrategyBrief(ctx, plan, stats, notices, meta);
31329
31455
  console.log();
31330
31456
  let saved = false;
31331
31457
  if (ctx.rl) {
@@ -31384,14 +31510,14 @@ async function ensureSnapshot(ctx) {
31384
31510
  }
31385
31511
  function printCraftReplResult(ctx, result, objective) {
31386
31512
  if (result.plan) {
31387
- printStrategyBrief(result.plan, {
31513
+ presentStrategyBrief(ctx, result.plan, {
31388
31514
  measurable_targets: result.measurable_targets,
31389
31515
  total_targets: result.total_targets
31390
- });
31516
+ }, result.notices, result.usage);
31391
31517
  } else {
31392
31518
  console.log(" " + chalk25.dim("(No plan produced)"));
31519
+ printStrategyBriefFooter(result.notices, result.usage);
31393
31520
  }
31394
- printStrategyBriefFooter(result.notices, result.usage);
31395
31521
  if (result.library_path) creditStrategySession(ctx);
31396
31522
  printCraftWrapUp({
31397
31523
  status: result.status,
@@ -32001,7 +32127,7 @@ function matchDefinitionExplainer(input) {
32001
32127
  }
32002
32128
  return getMetricExplainer(id);
32003
32129
  }
32004
- function printWrapped2(text, indent = " ") {
32130
+ function printWrapped(text, indent = " ") {
32005
32131
  for (const line of wrapWords(text, 78)) {
32006
32132
  console.log(indent + line);
32007
32133
  }
@@ -32018,10 +32144,10 @@ function tryKeylessDefinitionAnswer(ctx, input) {
32018
32144
  console.log(" " + chalk29.dim(explainer.tagline));
32019
32145
  console.log();
32020
32146
  console.log(" " + bold("Meaning"));
32021
- printWrapped2(explainer.meaning, " ");
32147
+ printWrapped(explainer.meaning, " ");
32022
32148
  console.log();
32023
32149
  console.log(" " + bold("How NTRP calculates it"));
32024
- printWrapped2(explainer.how_computed, " ");
32150
+ printWrapped(explainer.how_computed, " ");
32025
32151
  for (const f of explainer.formula_lines) {
32026
32152
  console.log(" " + paint("accent", f));
32027
32153
  }
@@ -36708,6 +36834,7 @@ async function handler10(args, _ctx) {
36708
36834
  if (event.inbox_path) {
36709
36835
  console.log(chalk43.dim(` Inbox: ${event.inbox_path}`));
36710
36836
  }
36837
+ printExportPickupNudge({ inboxReady: Boolean(event.inbox_path) });
36711
36838
  console.log();
36712
36839
  return filepath;
36713
36840
  } catch (err) {
@@ -36724,6 +36851,7 @@ var init_export = __esm({
36724
36851
  init_path_safety();
36725
36852
  init_argparse();
36726
36853
  init_exports_registry();
36854
+ init_handoff_skill();
36727
36855
  init_segments();
36728
36856
  }
36729
36857
  });
@@ -36822,6 +36950,7 @@ async function handler11(args, _ctx) {
36822
36950
  if (event.inbox_path) {
36823
36951
  console.log(chalk44.dim(` Inbox: ${event.inbox_path}`));
36824
36952
  }
36953
+ printExportPickupNudge({ inboxReady: Boolean(event.inbox_path) });
36825
36954
  console.log();
36826
36955
  return folder;
36827
36956
  } catch (err) {
@@ -36839,6 +36968,7 @@ var init_backmeup = __esm({
36839
36968
  init_formatters();
36840
36969
  init_path_safety();
36841
36970
  init_exports_registry();
36971
+ init_handoff_skill();
36842
36972
  EVIDENCE_FILENAMES = {
36843
36973
  freshness: "freshness.csv",
36844
36974
  flow_rate: "flow-rate.csv",
@@ -38423,7 +38553,7 @@ async function handler16(args, ctx) {
38423
38553
  emitResult("strategy", result);
38424
38554
  return;
38425
38555
  }
38426
- renderStrategyResult(result);
38556
+ renderStrategyResult(result, ctx);
38427
38557
  } catch (err) {
38428
38558
  if (isStructuredOutput(ctx.execution)) emitError(first === "craft" ? "strategy.craft" : "strategy", err);
38429
38559
  console.error(chalk50.red(` ${err instanceof Error ? err.message : String(err)}`));
@@ -38579,10 +38709,10 @@ function craftJsonData(result) {
38579
38709
  stop_reason: result.stop_reason ?? null
38580
38710
  };
38581
38711
  }
38582
- function renderStrategyResult(result) {
38712
+ function renderStrategyResult(result, ctx) {
38583
38713
  switch (result.action) {
38584
38714
  case "craft":
38585
- renderCraftResult(result);
38715
+ renderCraftResult(result, ctx);
38586
38716
  return;
38587
38717
  case "review":
38588
38718
  return;
@@ -38598,8 +38728,7 @@ function renderStrategyResult(result) {
38598
38728
  printStrategyList(result.strategies);
38599
38729
  return;
38600
38730
  case "show":
38601
- console.log(chalk50.bold("\n Strategy\n"));
38602
- printStrategyDetail(result.strategy);
38731
+ printStrategyShow(result.strategy, ctx);
38603
38732
  if (result.sources.length > 0) {
38604
38733
  console.log(chalk50.hex("#14b8a6")(" Sources"));
38605
38734
  for (const source of result.sources) {
@@ -38616,17 +38745,17 @@ function renderStrategyResult(result) {
38616
38745
  return;
38617
38746
  }
38618
38747
  }
38619
- function renderCraftResult(result) {
38748
+ function renderCraftResult(result, ctx) {
38620
38749
  if (result.plan) {
38621
- printStrategyBrief(result.plan, {
38750
+ presentStrategyBrief(ctx, result.plan, {
38622
38751
  measurable_targets: result.measurable_targets,
38623
38752
  total_targets: result.total_targets
38624
- });
38753
+ }, result.notices, result.usage);
38625
38754
  } else {
38626
38755
  console.log();
38627
38756
  console.log(" " + chalk50.dim("(No plan produced)"));
38757
+ printStrategyBriefFooter(result.notices, result.usage);
38628
38758
  }
38629
- printStrategyBriefFooter(result.notices, result.usage);
38630
38759
  printCraftWrapUp({
38631
38760
  status: result.status,
38632
38761
  library_path: result.library_path,
@@ -38657,29 +38786,21 @@ function printStrategySummary(strategy) {
38657
38786
  console.log(` ${strategy.goal}`);
38658
38787
  console.log();
38659
38788
  }
38660
- function printStrategyDetail(strategy) {
38661
- printStrategySummary(strategy);
38662
- console.log(chalk50.hex("#14b8a6")(" Hypothesis"));
38663
- console.log(` ${strategy.hypothesis}`);
38664
- console.log();
38665
- console.log(chalk50.hex("#14b8a6")(" Target Segment"));
38666
- console.log(` ${strategy.target_segment}`);
38667
- console.log();
38668
- if (strategy.linked_play_ids.length > 0) {
38669
- console.log(chalk50.hex("#14b8a6")(" Linked Plays"));
38670
- console.log(` ${strategy.linked_play_ids.join(", ")}`);
38671
- console.log();
38672
- }
38673
- if (strategy.success_metrics.length > 0) {
38674
- console.log(chalk50.hex("#14b8a6")(" Success Metrics"));
38675
- for (const metric of strategy.success_metrics) {
38676
- 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();
38677
38796
  }
38797
+ return;
38798
+ }
38799
+ printStrategySummary(strategy);
38800
+ if (strategy.library_path) {
38801
+ console.log(" " + chalk50.dim(strategy.library_path));
38678
38802
  console.log();
38679
38803
  }
38680
- console.log(chalk50.hex("#14b8a6")(" Experiment Design"));
38681
- console.log(` ${strategy.experiment_design}`);
38682
- console.log();
38683
38804
  }
38684
38805
  function statusBadge3(status) {
38685
38806
  switch (status) {
@@ -45961,6 +46082,18 @@ function handleBackNavigation(ctx, line) {
45961
46082
  }
45962
46083
  return null;
45963
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
+ }
45964
46097
  async function conversationRouter(input, ctx) {
45965
46098
  if (ctx.oneShot || (ctx.wizardDepth ?? 0) > 0) {
45966
46099
  return { handled: false };
@@ -46020,6 +46153,8 @@ async function conversationRouter(input, ctx) {
46020
46153
  }
46021
46154
  const backResult = handleBackNavigation(ctx, line);
46022
46155
  if (backResult) return backResult;
46156
+ const expandResult = handleBriefExpand(ctx, line);
46157
+ if (expandResult) return expandResult;
46023
46158
  if (/^\S+$/.test(line) && !/^\d$/.test(line)) {
46024
46159
  const { resolveSessionByToken: resolveSessionByToken2 } = await Promise.resolve().then(() => (init_context2(), context_exports));
46025
46160
  if (resolveSessionByToken2(line, { printErrors: false }) !== void 0) {
@@ -46131,6 +46266,7 @@ var init_router = __esm({
46131
46266
  init_demo();
46132
46267
  init_scope();
46133
46268
  init_pending_ask();
46269
+ init_strategy_brief();
46134
46270
  FRESH_START_RE = /\b(start (over|fresh)|new analysis|start again|reset session)\b/i;
46135
46271
  CANCEL_ESCAPE_RE = /^(cancel|stop|abort|quit|never\s?mind|nevermind|forget it)\s*[.!]?\s*$/i;
46136
46272
  }
@@ -47235,6 +47371,9 @@ function printHelp() {
47235
47371
  console.log(
47236
47372
  " " + chalk96.dim("On ") + paint("accent", "/settings") + chalk96.dim(" menus, ") + paint("accent", "b") + chalk96.dim(" leaves the hub (same as Done).")
47237
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
+ );
47238
47377
  console.log(
47239
47378
  " " + paint("accent", "cancel") + chalk96.dim(" Drops the current modal overlay (strategy, think, ship, unconfirmed scope).")
47240
47379
  );
@@ -1308,6 +1308,34 @@ function voiceHealthGloss(status, prefs) {
1308
1308
  }
1309
1309
  });
1310
1310
  }
1311
+ function voiceExportPickupNudge(input, prefs) {
1312
+ const v = voice(prefs);
1313
+ const phrase = EXPORT_PICKUP_PHRASE;
1314
+ if (input.inboxReady) {
1315
+ return pick(v, {
1316
+ robotic: `Open your desktop AI. Tell it to ${phrase}.`,
1317
+ composed: `Open your desktop AI and tell it to ${phrase}.`,
1318
+ casual: `This terminal is not your AI. Open it and tell it to ${phrase}.`,
1319
+ loose: {
1320
+ light: `Written. Open your desktop AI and tell it to ${phrase}.`,
1321
+ medium: `This CLI cannot see your AI. Open it. Tell it to ${phrase}.`,
1322
+ dark: `The file is on disk. Your AI is not in this window. Open it and ${phrase}.`,
1323
+ heavy: `NTRP wrote it. This window is not your AI. Open the desktop app and ${phrase}.`
1324
+ }
1325
+ });
1326
+ }
1327
+ return pick(v, {
1328
+ robotic: `Your desktop AI cannot see this folder. Type /inbox set, then tell that AI to ${phrase}.`,
1329
+ composed: `These files are on disk. Set a pickup folder (/inbox set), then tell your desktop AI to ${phrase}.`,
1330
+ casual: `Your AI cannot see this folder. Type /inbox set, then tell it to ${phrase}.`,
1331
+ loose: {
1332
+ light: `Your AI cannot see this folder yet. Type /inbox set, then tell it to ${phrase}.`,
1333
+ medium: `This folder is invisible to your AI. Type /inbox set, then tell it to ${phrase}.`,
1334
+ dark: `Your AI cannot see this terminal. Type /inbox set, then ${phrase}.`,
1335
+ heavy: `This window is not your AI. Type /inbox set, then ${phrase}.`
1336
+ }
1337
+ });
1338
+ }
1311
1339
  function voiceHandoffAddendum(prefs) {
1312
1340
  const v = voice(prefs);
1313
1341
  return [
@@ -1322,11 +1350,13 @@ function pick(prefs, map) {
1322
1350
  if (typeof entry === "string") return entry;
1323
1351
  return entry[prefs.roast];
1324
1352
  }
1353
+ var EXPORT_PICKUP_PHRASE;
1325
1354
  var init_copy = __esm({
1326
1355
  "src/voice/copy.ts"() {
1327
1356
  "use strict";
1328
1357
  init_catalog();
1329
1358
  init_prefs();
1359
+ EXPORT_PICKUP_PHRASE = "pick up the latest NTRP handoff";
1330
1360
  }
1331
1361
  });
1332
1362
 
@@ -2533,6 +2563,9 @@ function maybePrintAiInboxNudge() {
2533
2563
  )
2534
2564
  );
2535
2565
  }
2566
+ function printExportPickupNudge(opts) {
2567
+ console.log(" " + chalk7.dim(voiceExportPickupNudge(opts)));
2568
+ }
2536
2569
  function printStandingSkill() {
2537
2570
  persistStandingSkill();
2538
2571
  const loc = handoffLocations();
@@ -2558,6 +2591,7 @@ var init_handoff_skill = __esm({
2558
2591
  init_store();
2559
2592
  init_theme();
2560
2593
  init_export_kinds();
2594
+ init_copy();
2561
2595
  STANDING_SKILL_NAME = "SKILL.md";
2562
2596
  ARCHIVE_PICKUP_NAME = "pickup.md";
2563
2597
  INBOX_PICKUP_NAME = "latest-pickup.md";
@@ -9680,7 +9714,7 @@ ${strategy.objective}
9680
9714
  ` : "";
9681
9715
  const workstreamSection = strategy.workstreams.length > 0 ? `
9682
9716
  ## Workstreams
9683
- ${strategy.workstreams.map(formatWorkstream).join("\n")}
9717
+ ${strategy.workstreams.map(formatWorkstreamMarkdown).join("\n")}
9684
9718
  ` : "";
9685
9719
  const constraintsSection = filterOperatorLines(strategy.constraints).length > 0 ? `
9686
9720
  ## Constraints
@@ -9736,7 +9770,7 @@ ${strategy.objective}
9736
9770
  ` : "";
9737
9771
  const workstreamSection = strategy.workstreams.length > 0 ? `
9738
9772
  ## Workstreams
9739
- ${strategy.workstreams.map(formatWorkstream).join("\n")}
9773
+ ${strategy.workstreams.map(formatWorkstreamMarkdown).join("\n")}
9740
9774
  ` : "";
9741
9775
  const assumptions = filterOperatorLines(strategy.assumptions);
9742
9776
  const assumptionsSection = assumptions.length > 0 ? `
@@ -9783,7 +9817,7 @@ ${frontmatter}
9783
9817
  ].filter((p) => p != null && String(p).length > 0);
9784
9818
  return parts.join("\n\n") + "\n";
9785
9819
  }
9786
- function formatWorkstream(ws) {
9820
+ function formatWorkstreamMarkdown(ws) {
9787
9821
  const lines = [];
9788
9822
  lines.push(`### ${ws.order}. ${ws.title}`);
9789
9823
  lines.push(`- Problem: ${ws.problem}`);
@@ -11146,6 +11180,7 @@ function resetContextForSwitch(ctx, opts) {
11146
11180
  ctx.thinkState = opts.thinkState;
11147
11181
  ctx.pendingAsk = opts.pendingAsk;
11148
11182
  ctx.lastCraftJobId = opts.lastCraftJobId;
11183
+ ctx.lastStrategyBrief = void 0;
11149
11184
  ctx.gapAudit = void 0;
11150
11185
  ctx.deliverIntent = false;
11151
11186
  ctx.computeInProgress = false;
@@ -23835,20 +23870,7 @@ function renderCraftHandoffMarkdown(opts) {
23835
23870
  lines.push("## Workstreams");
23836
23871
  lines.push("");
23837
23872
  for (const ws of plan.workstreams) {
23838
- lines.push(`### ${ws.order}. ${ws.title}`);
23839
- lines.push("");
23840
- lines.push(`- Problem: ${ws.problem}`);
23841
- if (ws.rationale && !isGenericWorkstreamRationale(ws.rationale)) {
23842
- lines.push(`- Why this order: ${ws.rationale}`);
23843
- }
23844
- if (ws.actions[0]) lines.push(`- First action (48h): ${ws.actions[0]}`);
23845
- lines.push(`- Effort: ~${Math.round(ws.effort_hours)} team-hours`);
23846
- lines.push(
23847
- `- 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})`
23848
- );
23849
- lines.push(
23850
- `- Contingency: if ${ws.contingency.trigger} (check ${ws.contingency.trigger_check_date}) \u2192 ${ws.contingency.fallback}`
23851
- );
23873
+ lines.push(formatWorkstreamMarkdown(ws).trimEnd());
23852
23874
  lines.push("");
23853
23875
  }
23854
23876
  if (opts.roundtableDigest) {
@@ -24128,93 +24150,149 @@ var init_llm_attribution = __esm({
24128
24150
 
24129
24151
  // src/output/strategy-brief.ts
24130
24152
  import chalk17 from "chalk";
24131
- function printWrapped(text, width, prefix = INDENT, style) {
24153
+ function collectWrapped(lines, text, width, prefix = INDENT, style) {
24132
24154
  for (const line of wrapWords(text, width)) {
24133
- console.log(prefix + (style ? style(line) : line));
24155
+ lines.push(prefix + (style ? style(line) : line));
24134
24156
  }
24135
24157
  }
24136
- function outcomeLine(outcome) {
24137
- return `${chalk17.bold(outcome.metric)}: ${outcome.baseline} ${chalk17.dim("\u2192")} ${chalk17.bold(outcome.target_range)} ${chalk17.dim(`by ${outcome.check_date}`)}`;
24158
+ function outcomeLine(outcome, dateStyle) {
24159
+ const by = dateStyle === "accent" ? paint("accent", `by ${outcome.check_date}`) : chalk17.dim(`by ${outcome.check_date}`);
24160
+ return `${chalk17.bold(outcome.metric)}: ${outcome.baseline} ${chalk17.dim("\u2192")} ${chalk17.bold(outcome.target_range)} ${by}`;
24161
+ }
24162
+ function soonestByDue(items) {
24163
+ if (items.length === 0) return void 0;
24164
+ return [...items].sort((a, b) => a.due.localeCompare(b.due) || 0)[0];
24138
24165
  }
24139
- function printWorkstream(ws, width) {
24140
- console.log(`${INDENT}${paint("accent", `${ws.order}.`)} ${chalk17.bold(ws.title)}`);
24141
- printWrapped(ws.problem, width - 5, INDENT + " ", (s) => chalk17.dim(s));
24142
- console.log(`${INDENT} ${outcomeLine(ws.expected_outcome)}`);
24166
+ function altitudeLine(text) {
24167
+ const sentence = findingHeadline(text) || text.trim();
24168
+ return truncateVisible(sentence, FOH_PROSE_MAX);
24169
+ }
24170
+ function coverageLine(plan, stats) {
24171
+ const totalHours = plan.workstreams.reduce((sum, ws) => sum + ws.effort_hours, 0);
24172
+ const hours = `~${Math.round(totalHours)} total team hours across ${plan.workstreams.length} workstream${plan.workstreams.length === 1 ? "" : "s"}`;
24173
+ if (stats.total_targets <= 0) return chalk17.dim(hours);
24174
+ const coverage = `${stats.measurable_targets} of ${stats.total_targets} targets can be measured with current data`;
24175
+ const coverageStyled = stats.measurable_targets === stats.total_targets ? paint("success", coverage) : chalk17.hex("#eab308")(coverage);
24176
+ return `${coverageStyled}${chalk17.dim(` \xB7 ${hours}`)}`;
24177
+ }
24178
+ function renderFohWorkstream(ws, width) {
24179
+ const lines = [];
24180
+ lines.push(`${INDENT}${paint("accent", `${ws.order}.`)} ${chalk17.bold(ws.title)}`);
24181
+ lines.push(`${INDENT} ${outcomeLine(ws.expected_outcome, "accent")}`);
24182
+ const next = soonestByDue(ws.milestones);
24183
+ if (next) {
24184
+ lines.push(`${INDENT} Next ${paint("accent", next.due)} ${next.label}`);
24185
+ }
24186
+ const ship = soonestByDue(ws.deliverables);
24187
+ if (ship) {
24188
+ lines.push(`${INDENT} Ship ${paint("accent", ship.due)} ${ship.label}`);
24189
+ }
24190
+ lines.push("");
24191
+ return lines;
24192
+ }
24193
+ function renderBohWorkstream(ws, width) {
24194
+ const lines = [];
24195
+ lines.push(`${INDENT}${paint("accent", `${ws.order}.`)} ${chalk17.bold(ws.title)}`);
24196
+ collectWrapped(lines, ws.problem, width - 5, INDENT + " ", (s) => chalk17.dim(s));
24197
+ lines.push(`${INDENT} ${outcomeLine(ws.expected_outcome, "dim")}`);
24143
24198
  for (const li of ws.leading_indicators) {
24144
- console.log(`${INDENT} ${chalk17.dim("Lead:")} ${outcomeLine(li)}`);
24199
+ lines.push(`${INDENT} ${chalk17.dim("Lead:")} ${outcomeLine(li, "dim")}`);
24145
24200
  }
24146
24201
  if (ws.actions.length > 0) {
24147
- console.log(`${INDENT} ${chalk17.dim("First steps")}`);
24202
+ lines.push(`${INDENT} ${chalk17.dim("First steps")}`);
24148
24203
  for (const action of ws.actions.slice(0, 3)) {
24149
- printWrapped(`- ${action}`, width - 7, INDENT + " ", (s) => chalk17.dim(s));
24204
+ collectWrapped(lines, `- ${action}`, width - 7, INDENT + " ", (s) => chalk17.dim(s));
24150
24205
  }
24151
24206
  }
24152
24207
  if (ws.milestones.length > 0) {
24153
- console.log(`${INDENT} ${chalk17.dim("Milestones")}`);
24208
+ lines.push(`${INDENT} ${chalk17.dim("Milestones")}`);
24154
24209
  for (const m of ws.milestones) {
24155
- console.log(`${INDENT} ${paint("accent", m.due)} ${m.label}`);
24210
+ lines.push(`${INDENT} ${paint("accent", m.due)} ${m.label}`);
24156
24211
  }
24157
24212
  }
24158
24213
  if (ws.deliverables.length > 0) {
24159
- console.log(`${INDENT} ${chalk17.dim("Deliverables")}`);
24214
+ lines.push(`${INDENT} ${chalk17.dim("Deliverables")}`);
24160
24215
  for (const d of ws.deliverables) {
24161
- console.log(`${INDENT} ${chalk17.dim("[ ]")} ${d.label} ${chalk17.dim(`due ${d.due}`)}`);
24216
+ lines.push(`${INDENT} ${chalk17.dim("[ ]")} ${d.label} ${chalk17.dim(`due ${d.due}`)}`);
24162
24217
  }
24163
24218
  }
24164
- printWrapped(
24219
+ collectWrapped(
24220
+ lines,
24165
24221
  `If ${ws.contingency.trigger} (check ${ws.contingency.trigger_check_date}), then ${ws.contingency.fallback}`,
24166
24222
  width - 5,
24167
24223
  INDENT + " ",
24168
24224
  (s) => chalk17.dim(s)
24169
24225
  );
24170
- console.log(`${INDENT} ${chalk17.dim(`~${Math.round(ws.effort_hours)} team hours`)}`);
24171
- console.log();
24226
+ lines.push(`${INDENT} ${chalk17.dim(`~${Math.round(ws.effort_hours)} team hours`)}`);
24227
+ lines.push("");
24228
+ return lines;
24229
+ }
24230
+ function renderOperatorList(title, items, width) {
24231
+ const lines = [];
24232
+ if (items.length === 0) return lines;
24233
+ lines.push(`${INDENT}${chalk17.dim(title)}`);
24234
+ for (const item of items) {
24235
+ collectWrapped(lines, `- ${item}`, width - 2, INDENT, (s) => chalk17.dim(s));
24236
+ }
24237
+ lines.push("");
24238
+ return lines;
24172
24239
  }
24173
- function printStrategyBrief(plan, stats) {
24240
+ function renderStrategyBrief(plan, stats, opts = {}) {
24241
+ const mode = opts.mode ?? "foh";
24174
24242
  const width = Math.min(termWidth() - 4, 92);
24175
- console.log();
24176
- console.log(
24243
+ const lines = [""];
24244
+ lines.push(
24177
24245
  `${INDENT}${chalk17.bold(`Strategy brief \u2014 ${plan.title}`)} ${chalk17.dim(`confidence ${plan.confidence.toFixed(2)} \xB7 ${plan.priority} priority \xB7 review ${plan.review_cadence.toLowerCase()}`)}`
24178
24246
  );
24179
- console.log(INDENT + chalk17.dim(hr(width)));
24180
- console.log(`${INDENT}${chalk17.dim("The Call")}`);
24181
- printWrapped(plan.summary_30k, width);
24182
- console.log();
24183
- printWrapped(`Objective: ${plan.objective}`, width, INDENT, (s) => paint("accent", s));
24184
- console.log();
24185
- for (const ws of plan.workstreams) {
24186
- printWorkstream(ws, width);
24187
- }
24188
- const constraints = filterOperatorLines(plan.constraints);
24189
- if (constraints.length > 0) {
24190
- console.log(`${INDENT}${chalk17.dim("Constraints")}`);
24191
- for (const c of constraints) {
24192
- printWrapped(`- ${c}`, width - 2, INDENT, (s) => chalk17.dim(s));
24247
+ if (mode === "foh") {
24248
+ collectWrapped(lines, altitudeLine(plan.summary_30k), width, INDENT);
24249
+ lines.push("");
24250
+ collectWrapped(lines, altitudeLine(plan.objective), width, INDENT, (s) => paint("accent", s));
24251
+ lines.push("");
24252
+ for (const ws of plan.workstreams) {
24253
+ lines.push(...renderFohWorkstream(ws, width));
24193
24254
  }
24194
- console.log();
24195
- }
24196
- const assumptions = filterOperatorLines(plan.assumptions);
24197
- if (assumptions.length > 0) {
24198
- console.log(`${INDENT}${chalk17.dim("Assumptions (not verified, not targets)")}`);
24199
- for (const a of assumptions) {
24200
- printWrapped(`- ${a}`, width - 2, INDENT, (s) => chalk17.dim(s));
24255
+ } else {
24256
+ lines.push(INDENT + chalk17.dim(hr(width)));
24257
+ lines.push(`${INDENT}${chalk17.dim("The Call")}`);
24258
+ collectWrapped(lines, plan.summary_30k, width);
24259
+ lines.push("");
24260
+ collectWrapped(lines, `Objective: ${plan.objective}`, width, INDENT, (s) => paint("accent", s));
24261
+ lines.push("");
24262
+ for (const ws of plan.workstreams) {
24263
+ lines.push(...renderBohWorkstream(ws, width));
24201
24264
  }
24202
- console.log();
24265
+ lines.push(
24266
+ ...renderOperatorList("Constraints", filterOperatorLines(plan.constraints), width)
24267
+ );
24268
+ lines.push(
24269
+ ...renderOperatorList(
24270
+ "Assumptions (not verified, not targets)",
24271
+ filterOperatorLines(plan.assumptions),
24272
+ width
24273
+ )
24274
+ );
24275
+ lines.push(...renderOperatorList("Risks", filterOperatorLines(plan.risks), width));
24203
24276
  }
24204
- const risks = filterOperatorLines(plan.risks);
24205
- if (risks.length > 0) {
24206
- console.log(`${INDENT}${chalk17.dim("Risks")}`);
24207
- for (const r of risks) {
24208
- printWrapped(`- ${r}`, width - 2, INDENT, (s) => chalk17.dim(s));
24209
- }
24210
- console.log();
24277
+ lines.push(INDENT + chalk17.dim(hr(width)));
24278
+ lines.push(`${INDENT}${coverageLine(plan, stats)}`);
24279
+ if (opts.hint) {
24280
+ lines.push(`${INDENT}${chalk17.dim(STRATEGY_BRIEF_EXPAND_HINT)}`);
24211
24281
  }
24212
- const totalHours = plan.workstreams.reduce((sum, ws) => sum + ws.effort_hours, 0);
24213
- console.log(INDENT + chalk17.dim(hr(width)));
24214
- const coverage = stats.total_targets > 0 ? `${stats.measurable_targets} of ${stats.total_targets} targets can be measured with current data` : "No measured targets";
24215
- const coverageStyled = stats.total_targets > 0 && stats.measurable_targets === stats.total_targets ? paint("success", coverage) : chalk17.hex("#eab308")(coverage);
24216
- console.log(`${INDENT}${coverageStyled}${chalk17.dim(` \xB7 ~${Math.round(totalHours)} total team hours across ${plan.workstreams.length} workstream${plan.workstreams.length === 1 ? "" : "s"}`)}`);
24217
- console.log();
24282
+ lines.push("");
24283
+ return lines.join("\n");
24284
+ }
24285
+ function printStrategyBrief(plan, stats, opts = {}) {
24286
+ console.log(renderStrategyBrief(plan, stats, opts));
24287
+ }
24288
+ function resolveStrategyBriefMode(host) {
24289
+ return isVerbose(host.execution) ? "boh" : "foh";
24290
+ }
24291
+ function presentStrategyBrief(host, plan, stats, notices = [], meta = {}) {
24292
+ const mode = resolveStrategyBriefMode(host);
24293
+ printStrategyBrief(plan, stats, { mode, hint: mode === "foh" && !host.oneShot });
24294
+ printStrategyBriefFooter(notices, meta);
24295
+ host.lastStrategyBrief = { plan, stats, notices, meta };
24218
24296
  }
24219
24297
  function printStrategyBriefFooter(notices, meta) {
24220
24298
  if (usedGroundedFallback(notices)) {
@@ -24233,7 +24311,10 @@ function printCraftWrapUp(opts) {
24233
24311
  );
24234
24312
  }
24235
24313
  const handoff = opts.inbox_path ?? opts.handoff_path;
24236
- if (handoff) console.log(INDENT + chalk17.dim(handoff));
24314
+ if (handoff) {
24315
+ console.log(INDENT + chalk17.dim(handoff));
24316
+ printExportPickupNudge({ inboxReady: Boolean(opts.inbox_path) });
24317
+ }
24237
24318
  } else {
24238
24319
  console.log(INDENT + paint("accent", "Best so far."));
24239
24320
  const path = opts.library_path ?? opts.handoff_path ?? opts.inbox_path;
@@ -24253,15 +24334,20 @@ function printCraftWrapUp(opts) {
24253
24334
  }
24254
24335
  console.log();
24255
24336
  }
24256
- var INDENT;
24337
+ var STRATEGY_BRIEF_EXPAND_HINT, INDENT, FOH_PROSE_MAX;
24257
24338
  var init_strategy_brief = __esm({
24258
24339
  "src/output/strategy-brief.ts"() {
24259
24340
  "use strict";
24260
24341
  init_theme();
24261
24342
  init_layout();
24343
+ init_formatters();
24262
24344
  init_strategy_signal();
24263
24345
  init_llm_attribution();
24346
+ init_handoff_skill();
24347
+ init_verbosity();
24348
+ STRATEGY_BRIEF_EXPAND_HINT = "Type more to show the full brief.";
24264
24349
  INDENT = " ";
24350
+ FOH_PROSE_MAX = 220;
24265
24351
  }
24266
24352
  });
24267
24353
 
@@ -24622,8 +24708,7 @@ async function runStrategistSession(ctx) {
24622
24708
  console.log();
24623
24709
  return;
24624
24710
  }
24625
- printStrategyBrief(plan, stats);
24626
- printStrategyBriefFooter(notices, meta);
24711
+ presentStrategyBrief(ctx, plan, stats, notices, meta);
24627
24712
  console.log();
24628
24713
  let saved = false;
24629
24714
  if (ctx.rl) {
@@ -24682,14 +24767,14 @@ async function ensureSnapshot(ctx) {
24682
24767
  }
24683
24768
  function printCraftReplResult(ctx, result, objective) {
24684
24769
  if (result.plan) {
24685
- printStrategyBrief(result.plan, {
24770
+ presentStrategyBrief(ctx, result.plan, {
24686
24771
  measurable_targets: result.measurable_targets,
24687
24772
  total_targets: result.total_targets
24688
- });
24773
+ }, result.notices, result.usage);
24689
24774
  } else {
24690
24775
  console.log(" " + chalk18.dim("(No plan produced)"));
24776
+ printStrategyBriefFooter(result.notices, result.usage);
24691
24777
  }
24692
- printStrategyBriefFooter(result.notices, result.usage);
24693
24778
  if (result.library_path) creditStrategySession(ctx);
24694
24779
  printCraftWrapUp({
24695
24780
  status: result.status,
@@ -25542,7 +25627,7 @@ function matchDefinitionExplainer(input) {
25542
25627
  }
25543
25628
  return getMetricExplainer(id);
25544
25629
  }
25545
- function printWrapped2(text, indent = " ") {
25630
+ function printWrapped(text, indent = " ") {
25546
25631
  for (const line of wrapWords(text, 78)) {
25547
25632
  console.log(indent + line);
25548
25633
  }
@@ -25559,10 +25644,10 @@ function tryKeylessDefinitionAnswer(ctx, input) {
25559
25644
  console.log(" " + chalk23.dim(explainer.tagline));
25560
25645
  console.log();
25561
25646
  console.log(" " + bold("Meaning"));
25562
- printWrapped2(explainer.meaning, " ");
25647
+ printWrapped(explainer.meaning, " ");
25563
25648
  console.log();
25564
25649
  console.log(" " + bold("How NTRP calculates it"));
25565
- printWrapped2(explainer.how_computed, " ");
25650
+ printWrapped(explainer.how_computed, " ");
25566
25651
  for (const f of explainer.formula_lines) {
25567
25652
  console.log(" " + paint("accent", f));
25568
25653
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sonnechasser/ntrp",
3
- "version": "2.1.11",
3
+ "version": "2.1.12",
4
4
  "description": "GTM Health Diagnostic CLI — local pipeline analysis tool",
5
5
  "homepage": "https://ntrp.sonnechasser.com",
6
6
  "repository": {