@sonnechasser/ntrp 1.4.7 → 1.4.9

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
@@ -1637,6 +1637,7 @@ function buildSessionContextDoc(file, opts = {}) {
1637
1637
  lines.push(`- Exchanges: ${exchanges}`);
1638
1638
  if (file.summary) lines.push(`- Summary: ${file.summary}`);
1639
1639
  if (file.resumed_from) lines.push(`- Resumed from: ${file.resumed_from}`);
1640
+ lines.push("- Packaging: answer-first; recommendations filtered (silent idea-gate on deep/think/strategy)");
1640
1641
  lines.push("");
1641
1642
  lines.push("## Dataset");
1642
1643
  lines.push("");
@@ -8706,6 +8707,8 @@ KINDS:
8706
8707
  - "preference": how the user wants analysis done or framed.
8707
8708
  - "calibration": a judgment update \u2014 an adjustment to HOW this business should be analyzed. Write it as an applicable rule, e.g. "Enterprise freshness reds here trace to rep coverage gaps, not data hygiene \u2014 cut by owner first" or "This team treats 45 quiet days as normal cadence; don't flag staleness before that." Capture corrections the user made to your reasoning as calibrations, not preferences.
8708
8709
 
8710
+ AFTER-ACTION LENS (silent \u2014 never name AAR to anyone): prefer items that capture BELIEF that changed, EVIDENCE that forced the change, or the NEXT BET the team should re-check. Calibrations that supersede contradictions are the highest-value distillates.
8711
+
8709
8712
  SUPERSEDING (calibrations only): the current calibrations are provided with ids. If the session contradicts or refines one, return the replacement with "supersedes" set to the old id. Never emit a calibration that duplicates an existing one.
8710
8713
 
8711
8714
  EXCLUDE:
@@ -12327,7 +12330,7 @@ var init_guide_slides = __esm({
12327
12330
  tagline: "Start with the question you care about \u2014 we'll mirror it back before we compute.",
12328
12331
  visual: {
12329
12332
  kind: "funnel",
12330
- caption: "When a dim \u23CE hint shows, bare Enter submits it. No hint means Enter does nothing.",
12333
+ caption: "When a dim \u23CE hint shows, bare Enter submits it. Type b (or back) to step back one gate. No hint means Enter does nothing.",
12331
12334
  funnel: [
12332
12335
  { label: "Type a question", widthPct: 100 },
12333
12336
  { label: "\u23CE yes (scope)", widthPct: 78 },
@@ -12338,6 +12341,8 @@ var init_guide_slides = __esm({
12338
12341
  lines: [
12339
12342
  `We designed the conversation so you don't need a slash to start. Type the question you actually need \u2014 "is our retention real for the board?" or "pipeline health" \u2014 and NTRP restates it as a scope card so you can confirm we're answering the right thing.`,
12340
12343
  "",
12344
+ "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.",
12345
+ "",
12341
12346
  "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.",
12342
12347
  "",
12343
12348
  "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."
@@ -12346,7 +12351,7 @@ var init_guide_slides = __esm({
12346
12351
  "Power-user slash commands still work. They stay hidden from /help: /new, /diagnose, /metrics, /ingest, /session.",
12347
12352
  '"use demo data" loads a fitted sample book. Company profile is optional. In scripts, /demo --no-profile.',
12348
12353
  'After compute, the prompt returns to \u203A. Brief is the default depth; type "go deep" for the long read.',
12349
- "/home shows phase status. /help lists conversation shortcuts \u2014 not every command."
12354
+ "/home shows phase status. /help lists conversation shortcuts and the Keys legend (\u23CE, b/back, cancel)."
12350
12355
  ]
12351
12356
  };
12352
12357
  ASK = {
@@ -12366,7 +12371,7 @@ var init_guide_slides = __esm({
12366
12371
  "",
12367
12372
  '"what is our ARR?" or "why is this red?" need a loaded dataset. Narrative findings and /ask need a stored key \u2014 /connect pastes any provider key and we detect it.',
12368
12373
  "",
12369
- "Replay this tour with /deepdive, jump to one slide with /deepdive freshness (or nrr, arr, \u2026), or /deepdive guide for this how-to section. When the prompt shows a dim \u23CE hint (yes, use demo data, go ahead, /connect), bare Enter submits it."
12374
+ "Replay this tour with /deepdive, jump to one slide with /deepdive freshness (or nrr, arr, \u2026), or /deepdive guide for this how-to section. When the prompt shows a dim \u23CE hint (yes, use demo data, go ahead, /connect), bare Enter submits it \u2014 and b / back steps back one confirm gate (or the previous tour slide while you're in /deepdive)."
12370
12375
  ],
12371
12376
  deepdive: [
12372
12377
  "/connect ollama for a keyless local model. /connect --base-url <url> --id <name> for any OpenAI-compatible endpoint.",
@@ -12594,7 +12599,9 @@ function paintIntro(opts) {
12594
12599
  extraLines: [
12595
12600
  ...TOUR_INTRO_LINES,
12596
12601
  "",
12597
- chalk10.dim("No AI key needed for this tour. Enter advances; /deepdive (or d) opens more on any slide.")
12602
+ chalk10.dim(
12603
+ "No AI key needed for this tour. Enter advances; b goes to the previous slide; /deepdive (or d) opens more on any slide. After the tour, b / back steps back one confirm gate in the REPL."
12604
+ )
12598
12605
  ],
12599
12606
  footer: `${progressDots(opts.index, opts.total)} \u23CE next \xB7 q skip`
12600
12607
  });
@@ -12793,7 +12800,7 @@ async function offerFirstRunTour(ctx) {
12793
12800
  {
12794
12801
  value: "tour",
12795
12802
  label: "Take the tour",
12796
- description: "\u23CE through slides \u2014 /deepdive on any slide for more"
12803
+ description: "\u23CE next \xB7 b back on slides \u2014 /deepdive on any slide for more"
12797
12804
  },
12798
12805
  {
12799
12806
  value: "skip",
@@ -12859,7 +12866,7 @@ var init_metric_tour = __esm({
12859
12866
  "",
12860
12867
  "We're a stethoscope, not a hospital: we listen hard, translate scores into dollars, and point at plays \u2014 you decide the surgery."
12861
12868
  ];
12862
- TOUR_OFFER_BLURB = "How we think about SaaS numbers and five vitals \u2014 then how to talk to NTRP and ship work to your AI.";
12869
+ TOUR_OFFER_BLURB = "How we think about SaaS numbers and five vitals \u2014 then how to talk to NTRP and ship work to your AI. On slides, b goes back; after the tour, the same word steps back at confirm gates.";
12863
12870
  }
12864
12871
  });
12865
12872
 
@@ -15590,7 +15597,8 @@ function buildConversationPrompt(ctx) {
15590
15597
  const enterHint2 = action ? chalk11.dim(` \xB7 \u23CE ${action.hint}`) : "";
15591
15598
  return accentPromptHead(phase, sessionName) + chalk11.dim(stack) + enterHint2 + " ";
15592
15599
  }
15593
- const enterHint = action ? chalk11.dim(`\u23CE ${action.hint} `) : "";
15600
+ const modalBack = phase === "scope" || phase === "awaiting_data" || phase === "strategize" ? " \xB7 b back" : "";
15601
+ const enterHint = action ? chalk11.dim(`\u23CE ${action.hint}${modalBack} `) : "";
15594
15602
  return accentPromptHead(phase, sessionName) + enterHint;
15595
15603
  }
15596
15604
  var PROMPT_LABELS;
@@ -21425,6 +21433,21 @@ var init_tool_schemas = __esm({
21425
21433
  required: ["play_id"]
21426
21434
  }
21427
21435
  },
21436
+ {
21437
+ name: "get_framework_detail",
21438
+ description: "Read a consulting/operating framework pack by id: when to use, vital links, board vs operator cut, kill rules, and full body. The system prompt lists only the catalog \u2014 call this when you need depth before recommending. Never name brand labels (Heilmeier, Minto, TOC, MEDDPICC, \u2026) to the user.",
21439
+ parameters: {
21440
+ type: "object",
21441
+ properties: {
21442
+ framework_id: {
21443
+ type: "string",
21444
+ maxLength: 40,
21445
+ description: "Exact id from the framework catalog, e.g. 'bottleneck', 'packaging', 'aar', 'meddpicc'."
21446
+ }
21447
+ },
21448
+ required: ["framework_id"]
21449
+ }
21450
+ },
21428
21451
  {
21429
21452
  name: "get_session_brief",
21430
21453
  description: "Read the 1-page context brief of a PRIOR session by id or 4-char suffix: status, dataset, scope, computed scores with dollar values, headline metrics, deliverables, and conversation log. Use when the user references earlier work \u2014 'last week we found\u2026', 'compare with the previous analysis', 'what did session 9297 conclude?'. Read-only.",
@@ -21526,6 +21549,251 @@ var init_tool_arg_validate = __esm({
21526
21549
  }
21527
21550
  });
21528
21551
 
21552
+ // src/data/frameworks.ts
21553
+ var frameworks_exports = {};
21554
+ __export(frameworks_exports, {
21555
+ FRAMEWORK_PACKS: () => FRAMEWORK_PACKS,
21556
+ PLAY_FRAMEWORK_HOOKS: () => PLAY_FRAMEWORK_HOOKS,
21557
+ buildFrameworkCatalogBlock: () => buildFrameworkCatalogBlock,
21558
+ frameworkIdsForPlay: () => frameworkIdsForPlay,
21559
+ getFrameworkById: () => getFrameworkById,
21560
+ listFrameworkIds: () => listFrameworkIds
21561
+ });
21562
+ function listFrameworkIds() {
21563
+ return FRAMEWORK_PACKS.map((p) => p.id);
21564
+ }
21565
+ function getFrameworkById(id) {
21566
+ return FRAMEWORK_PACKS.find((p) => p.id === id);
21567
+ }
21568
+ function buildFrameworkCatalogBlock() {
21569
+ const lines = FRAMEWORK_PACKS.filter((p) => p.always_on_catalog).map((p) => p.catalog_line);
21570
+ return `FRAMEWORK TOOLKIT (silent brands \u2014 call get_framework_detail for full body; never name brands to the user):
21571
+ ${lines.join("\n")}
21572
+ Optional packs via get_framework_detail: meddpicc, challenger, jtbd, porter, mckinsey_7s, ooda.`;
21573
+ }
21574
+ function frameworkIdsForPlay(playId) {
21575
+ return PLAY_FRAMEWORK_HOOKS[playId] ?? [];
21576
+ }
21577
+ var PLAY_FRAMEWORK_HOOKS, SILENT, FRAMEWORK_PACKS;
21578
+ var init_frameworks = __esm({
21579
+ "src/data/frameworks.ts"() {
21580
+ "use strict";
21581
+ PLAY_FRAMEWORK_HOOKS = {
21582
+ "clean-dead-pipeline": ["bottleneck", "heilmeier", "aar"],
21583
+ "unstick-pipeline": ["bottleneck", "okr_measurability", "owner_shape"],
21584
+ "fix-handoff-gap": ["bottleneck", "owner_shape", "aar"],
21585
+ "retarget-effort": ["bottleneck", "gtm_engineering"],
21586
+ "multi-thread-deals": ["owner_shape", "heilmeier"]
21587
+ };
21588
+ SILENT = "Never name this framework (or DARPA, McKinsey, Goldratt, etc.) in customer-visible text. Apply the moves; keep the brand silent.";
21589
+ FRAMEWORK_PACKS = [
21590
+ {
21591
+ id: "bottleneck",
21592
+ catalog_line: "- bottleneck \u2014 name the constraint vital/stage before prescribing; do not fix non-constraints",
21593
+ internal_name: "Theory of Constraints / bottleneck",
21594
+ always_on_catalog: true,
21595
+ vital_links: ["freshness", "flow_rate", "drop_rate", "signal_to_noise", "thread_depth"],
21596
+ board_cut: "One constraint is gating the dollar outcome. Fixing anything else is theater.",
21597
+ operator_cut: "Locate the gating vital (layer order) or the stage where deals die; put the first play there.",
21598
+ when_to_use: "Before recommending a play or sequencing workstreams.",
21599
+ kill_rules: [
21600
+ "If the recommendation does not move the constraint, demote or drop it.",
21601
+ "Do not prescribe parallel fixes that starve the constraint of capacity."
21602
+ ],
21603
+ silent_brand_rule: SILENT,
21604
+ body: `BOTTLENECK (constraint first):
21605
+ - Respect layer gating: freshness trust \u2192 flow/drop movement \u2192 signal efficiency \u2192 thread resilience.
21606
+ - Name the single constraint vital or pipeline stage with its score and dollar label.
21607
+ - Prescribe against the constraint first. Non-constraint work is optional polish.
21608
+ - If two problems compete, pick the one that unblocks the other (dependency), not the louder scorecard row.`
21609
+ },
21610
+ {
21611
+ id: "packaging",
21612
+ catalog_line: "- packaging \u2014 answer first, then situation/complication that make it true, then so-what",
21613
+ internal_name: "Minto pyramid + SCQA sibling",
21614
+ always_on_catalog: true,
21615
+ vital_links: [],
21616
+ board_cut: "Open with the call and the dollar. Context is support, not the lead.",
21617
+ operator_cut: "Headline \u226420 words \u2192 2\u20133 non-overlapping drivers \u2192 owner-shaped so-what.",
21618
+ when_to_use: "Every executive-facing answer, finding, recap Story, summary_30k.",
21619
+ kill_rules: ["Never open with methodology or a full scorecard.", "Merge overlapping drivers."],
21620
+ silent_brand_rule: SILENT,
21621
+ body: `PACKAGING (answer-first):
21622
+ - Governing thought first (verdict + dollars).
21623
+ - Then the situation and complication that make the call true (support, not opener).
21624
+ - Then drivers (distinct, non-overlapping) and so-what / ask.
21625
+ - Brief: one headline + \u22642 drivers + one so-what.`
21626
+ },
21627
+ {
21628
+ id: "aar",
21629
+ catalog_line: "- after-action \u2014 what we believed, what changed, next bet",
21630
+ internal_name: "After Action Review",
21631
+ always_on_catalog: true,
21632
+ vital_links: [],
21633
+ board_cut: "What we thought would move, what the instruments showed, what we bet next.",
21634
+ operator_cut: "Belief \u2192 evidence from vitals/metrics \u2192 keep, kill, or re-sequence the play.",
21635
+ when_to_use: "Strategy review, session distill calibrations, win/miss logging.",
21636
+ kill_rules: ["Do not log vanity narrative without a measured instrument.", "Calibrations supersede; do not pile contradictions."],
21637
+ silent_brand_rule: SILENT,
21638
+ body: `AFTER-ACTION:
21639
+ - BELIEF: what we expected to move (metric + range + check date).
21640
+ - EVIDENCE: what the instruments actually showed (baseline \u2192 reading).
21641
+ - LEARNING: keep, kill, or re-sequence \u2014 write as a calibration when judgment changed.
21642
+ - NEXT BET: the single next exam, not a new multi-week program.`
21643
+ },
21644
+ {
21645
+ id: "owner_shape",
21646
+ catalog_line: "- owner-shape \u2014 function-shaped owner and decision rights; not named people",
21647
+ internal_name: "RACI-lite",
21648
+ always_on_catalog: true,
21649
+ vital_links: [],
21650
+ board_cut: "Every ask names who decides and who executes by function.",
21651
+ operator_cut: "RevOps / AE lead / CS lead / Marketing ops \u2014 R does, A decides; avoid named individuals in plans.",
21652
+ when_to_use: "Workstream titles, handoff plans, play prescriptions.",
21653
+ kill_rules: ["No orphan actions without an accountable function.", "Do not invent headcount or reorgs."],
21654
+ silent_brand_rule: SILENT,
21655
+ body: `OWNER-SHAPE:
21656
+ - Name the function (RevOps, sales manager, CS lead, marketing ops) \u2014 not a person's name from the CRM.
21657
+ - Accountable decides; Responsible executes. Consulted optional; Inform via existing channels.
21658
+ - First action must be startable by that function within 48 hours with no new tooling.`
21659
+ },
21660
+ {
21661
+ id: "okr_measurability",
21662
+ catalog_line: "- exams \u2014 mid-term and final checks as dated key results with instruments",
21663
+ internal_name: "OKR / measurability dialect",
21664
+ always_on_catalog: true,
21665
+ vital_links: ["freshness", "flow_rate", "drop_rate", "signal_to_noise", "thread_depth"],
21666
+ board_cut: "Objective + dated key results the board can re-read next quarter.",
21667
+ operator_cut: "Baseline, target range, check date, measured_by instrument \u2014 or demote to assumption.",
21668
+ when_to_use: "Strategist outcomes, Heilmeier exams, milestones.",
21669
+ kill_rules: ["No 'improve' without a number.", "No exam faster than sales-cycle physics."],
21670
+ silent_brand_rule: SILENT,
21671
+ body: `EXAMS (objective + key results):
21672
+ - Objective = precise end state in one line.
21673
+ - Key results = mid-term check + final exam: metric, baseline, target RANGE, check date, instrument.
21674
+ - If unmeasurable with current data, exclude from targets and record as an assumption.`
21675
+ },
21676
+ {
21677
+ id: "heilmeier",
21678
+ catalog_line: "- idea-gate \u2014 newness, stake, and exams must pass before a recommendation ships",
21679
+ internal_name: "Heilmeier catechism",
21680
+ always_on_catalog: true,
21681
+ vital_links: [],
21682
+ board_cut: "Only recommendations with stake and exams reach the room.",
21683
+ operator_cut: "Kill restatements; demote unmeasurable ideas.",
21684
+ when_to_use: "Deep recommend, think before draft_strategy, strategist Stage C.",
21685
+ kill_rules: ["Fail newness, stake, or exams \u2192 do not recommend."],
21686
+ silent_brand_rule: SILENT,
21687
+ body: `IDEA GATE: end state, status quo + limit, newness, stake + cost of inaction, risks/payoffs, effort, time physics, mid+final exams. Kill on newness/stake/exams failure.`
21688
+ },
21689
+ {
21690
+ id: "minto",
21691
+ catalog_line: "- pyramid \u2014 headline, drivers, so-what for recall",
21692
+ internal_name: "Minto Pyramid Principle",
21693
+ always_on_catalog: true,
21694
+ vital_links: [],
21695
+ board_cut: "Repeatable headline + one number an hour later.",
21696
+ operator_cut: "Same shape for findings and deep answers.",
21697
+ when_to_use: "All packaged analyst prose.",
21698
+ kill_rules: ["Methodology-first openers die."],
21699
+ silent_brand_rule: SILENT,
21700
+ body: `PYRAMID: HEADLINE FIRST \u2192 DRIVERS (non-overlapping) \u2192 SO-WHAT. Recall test. Altitude control.`
21701
+ },
21702
+ {
21703
+ id: "gtm_engineering",
21704
+ catalog_line: "- systems \u2014 data foundation \u2192 modeling \u2192 activation; mechanism per fix",
21705
+ internal_name: "GTM engineering",
21706
+ always_on_catalog: true,
21707
+ vital_links: ["signal_to_noise", "drop_rate", "freshness"],
21708
+ board_cut: "Durable systems, not heroics.",
21709
+ operator_cut: "Pair every cleanup with a mechanism and an instrument.",
21710
+ when_to_use: "When recommending action on tool-capable surfaces.",
21711
+ kill_rules: ["Skipping the rung below will not hold."],
21712
+ silent_brand_rule: SILENT,
21713
+ body: `GTM SYSTEMS: three rungs (foundation \u2192 modeling \u2192 activation); signals over lists; CRM is cheapest pipeline; every fix gets a mechanism; instrument what you change.`
21714
+ },
21715
+ {
21716
+ id: "meddpicc",
21717
+ catalog_line: "- deal-qualification pack (optional) \u2014 use when threading or late-stage risk needs a qualification lens",
21718
+ internal_name: "MEDDPICC",
21719
+ always_on_catalog: false,
21720
+ vital_links: ["thread_depth", "flow_rate"],
21721
+ board_cut: "Late-stage risk is usually missing economic buyer or decision process \u2014 not more activity.",
21722
+ operator_cut: "Map Metrics, Economic buyer, Decision criteria/process, Paper process, Identify pain, Champion, Competition \u2014 as observation, not CRM surgery.",
21723
+ when_to_use: "Thread-depth or stuck late-stage deals when the user asks how to qualify.",
21724
+ kill_rules: ["Do not rewrite the CRM; observe and recommend contacts/process gaps."],
21725
+ silent_brand_rule: SILENT,
21726
+ body: `DEAL QUALIFICATION LENS: use when single-threaded or stuck late-stage deals need a checklist. Prefer introducing a second contact and clarifying decision process over more outbound volume.`
21727
+ },
21728
+ {
21729
+ id: "challenger",
21730
+ catalog_line: "- teach-tailor-take pack (optional) \u2014 pressure-test weak narratives in think",
21731
+ internal_name: "Challenger Sale",
21732
+ always_on_catalog: false,
21733
+ vital_links: ["signal_to_noise"],
21734
+ board_cut: "Insight that reframes the problem beats more activity.",
21735
+ operator_cut: "In think: teach with evidence, tailor to this pipeline, take control of next step \u2014 still stethoscope.",
21736
+ when_to_use: "Think channel when the user is soft on a weak story.",
21737
+ kill_rules: ["Do not turn into a pitch script or CRM sequence builder."],
21738
+ silent_brand_rule: SILENT,
21739
+ body: `TEACH-TAILOR-TAKE: reframe with verified numbers; tailor to segment/motion; land one owner-shaped next step.`
21740
+ },
21741
+ {
21742
+ id: "jtbd",
21743
+ catalog_line: "- job-of-the-metric pack (optional) \u2014 what job this number does for this board",
21744
+ internal_name: "Jobs to be Done",
21745
+ always_on_catalog: false,
21746
+ vital_links: [],
21747
+ board_cut: "Each metric has a job in a decision \u2014 name the job or drop the chart.",
21748
+ operator_cut: "When explaining ARR/NRR/vitals: what decision does this number unlock?",
21749
+ when_to_use: "Metric definitions / board deck framing.",
21750
+ kill_rules: ["Ornamental metrics without a decision job die."],
21751
+ silent_brand_rule: SILENT,
21752
+ body: `JOB OF THE METRIC: for each number, name the decision it serves. If none, cut it from the board package.`
21753
+ },
21754
+ {
21755
+ id: "porter",
21756
+ catalog_line: "- industry-structure pack (optional, weak vital fit) \u2014 only if the user asks competitive structure",
21757
+ internal_name: "Porter Five Forces",
21758
+ always_on_catalog: false,
21759
+ vital_links: [],
21760
+ board_cut: "Industry structure is context, not a substitute for pipeline dollars.",
21761
+ operator_cut: "Do not let industry essays displace vital-sign evidence.",
21762
+ when_to_use: "Only when the user explicitly asks for competitive structure.",
21763
+ kill_rules: ["Never use as a substitute for gating vital diagnosis."],
21764
+ silent_brand_rule: SILENT,
21765
+ body: `INDUSTRY STRUCTURE: optional context. Prefer vital signs and dollars for recommendations.`
21766
+ },
21767
+ {
21768
+ id: "mckinsey_7s",
21769
+ catalog_line: "- org-alignment pack (optional, weak vital fit) \u2014 only if the user asks org design",
21770
+ internal_name: "McKinsey 7S",
21771
+ always_on_catalog: false,
21772
+ vital_links: [],
21773
+ board_cut: "Org redesign is out of stethoscope scope.",
21774
+ operator_cut: "Refuse surgery; observe handoff/ownership symptoms via vitals.",
21775
+ when_to_use: "Only if asked; redirect to owner-shape + bottleneck.",
21776
+ kill_rules: ["No reorg prescriptions."],
21777
+ silent_brand_rule: SILENT,
21778
+ body: `ORG ALIGNMENT: out of scope for CRM surgery. Point to function-shaped owners and the constraint vital instead.`
21779
+ },
21780
+ {
21781
+ id: "ooda",
21782
+ catalog_line: "- observe-orient loop pack (optional) \u2014 overlaps gating; use sparingly",
21783
+ internal_name: "OODA",
21784
+ always_on_catalog: false,
21785
+ vital_links: [],
21786
+ board_cut: "Prefer layer gating and hypothesis-first grounding.",
21787
+ operator_cut: "Already covered by Stage A + bottleneck \u2014 do not duplicate jargon.",
21788
+ when_to_use: "Rarely; prefer existing grounding.",
21789
+ kill_rules: ["Do not invent a parallel loop vocabulary for the user."],
21790
+ silent_brand_rule: SILENT,
21791
+ body: `OBSERVE-ORIENT: prefer NTRP layer gating and Stage A hypothesis-first over a separate combat loop.`
21792
+ }
21793
+ ];
21794
+ }
21795
+ });
21796
+
21529
21797
  // src/conversation/agent-context.ts
21530
21798
  var agent_context_exports = {};
21531
21799
  __export(agent_context_exports, {
@@ -21550,10 +21818,12 @@ var init_agent_context = __esm({
21550
21818
  var scope_exports = {};
21551
21819
  __export(scope_exports, {
21552
21820
  confirmScope: () => confirmScope,
21821
+ isBackInput: () => isBackInput,
21553
21822
  isScopeAdjustInput: () => isScopeAdjustInput,
21554
21823
  isScopeConfirmInput: () => isScopeConfirmInput,
21555
21824
  isSkipClarifier: () => isSkipClarifier,
21556
- proposeScopeFromIntent: () => proposeScopeFromIntent
21825
+ proposeScopeFromIntent: () => proposeScopeFromIntent,
21826
+ unconfirmScope: () => unconfirmScope
21557
21827
  });
21558
21828
  function proposeScopeFromIntent(input) {
21559
21829
  const text = input.trim();
@@ -21598,12 +21868,20 @@ function confirmScope(ctx) {
21598
21868
  if (ctx.scope.primary_lens === "revenue_metrics" || ctx.scope.primary_lens === "gtm_health") {
21599
21869
  }
21600
21870
  }
21871
+ function unconfirmScope(ctx) {
21872
+ if (!ctx.scope?.confirmed_at) return;
21873
+ const { confirmed_at: _dropped, ...rest } = ctx.scope;
21874
+ ctx.scope = rest;
21875
+ }
21601
21876
  function isScopeConfirmInput(input) {
21602
21877
  return /^(yes|y|yeah|yep|correct|looks good|that'?s right|confirm|ok|okay|go)$/i.test(input.trim());
21603
21878
  }
21604
21879
  function isScopeAdjustInput(input) {
21605
21880
  return /^(adjust|change|refine|edit|no|nope)$/i.test(input.trim());
21606
21881
  }
21882
+ function isBackInput(input) {
21883
+ return /^(b|back|prev)\s*[.!]?\s*$/i.test(input.trim());
21884
+ }
21607
21885
  function isSkipClarifier(input) {
21608
21886
  return /^skip$/i.test(input.trim());
21609
21887
  }
@@ -21939,10 +22217,10 @@ function wrapForTarget(target, analysisBlock, conversationBlock, openQuestions,
21939
22217
  const contextLabel = handoffInstructionPrefix(ctx.analysis.primary);
21940
22218
  const forWhom = audiencePhrase(ctx.scope?.audience);
21941
22219
  const instructions = {
21942
- deck: `produce an executive review deck outline for ${company}, framed for ${forWhom}. Pyramid structure: (1) HEADLINE \u2014 the governing number and verdict in one line; (2) DRIVERS \u2014 2\u20133 distinct, non-overlapping risks or causes with dollar impact (not a full scorecard); (3) SO-WHAT \u2014 recommended plays; (4) ASKS / decisions the room must make. Optionally one pipeline-health slide after the headline if it supports the call. Use the Metric definitions appendix so every slide can briefly remind the room what the number means for this audience.`,
21943
- asana: `produce an Asana project plan with sections and tasks tied to findings for ${forWhom}. Prioritize by dollar impact. Reference metric definitions when a task owner needs to know what "good" looks like.`,
22220
+ deck: `produce an executive review deck outline for ${company}, framed for ${forWhom}. Pyramid structure: (1) HEADLINE \u2014 the governing number and verdict in one line; (2) DRIVERS \u2014 2\u20133 distinct, non-overlapping risks or causes with dollar impact (not a full scorecard); (3) SO-WHAT \u2014 recommended plays with function-shaped owners (RevOps / sales manager / CS \u2014 not personal names); (4) ASKS / decisions the room must make. Optionally one pipeline-health slide after the headline if it supports the call. Use the Metric definitions appendix so every slide can briefly remind the room what the number means for this audience.`,
22221
+ asana: `produce an Asana project plan with sections and tasks tied to findings for ${forWhom}. Prioritize by dollar impact. Assign by function (not personal names). Reference metric definitions when a task owner needs to know what "good" looks like.`,
21944
22222
  clay: `produce a Clay table specification to operationalize the highest-impact finding for ${forWhom}.`,
21945
- plan: `produce a prioritized action plan for ${forWhom}, answer-first: open with the governing call (highest-dollar problem), then items each with problem, play, first 3 steps, owner, and leading indicator. Ground indicators in the Metric definitions appendix. End with the sharpest asks / decisions.`
22223
+ plan: `produce a prioritized action plan for ${forWhom}, answer-first: open with the governing call (highest-dollar problem / constraint), then items each with problem, play, first 3 steps, function-shaped owner, and leading indicator. Ground indicators in the Metric definitions appendix. End with the sharpest asks / decisions.`
21946
22224
  };
21947
22225
  const parts = [
21948
22226
  `# NTRP handoff \u2192 ${target}`,
@@ -22304,6 +22582,7 @@ async function handleGetPlayDetail(input) {
22304
22582
  }
22305
22583
  } catch {
22306
22584
  }
22585
+ const { frameworkIdsForPlay: frameworkIdsForPlay2 } = await Promise.resolve().then(() => (init_frameworks(), frameworks_exports));
22307
22586
  const detail = {
22308
22587
  play_id: play.id,
22309
22588
  play_name: play.name,
@@ -22315,7 +22594,8 @@ async function handleGetPlayDetail(input) {
22315
22594
  tools_that_help: play.tools_that_help,
22316
22595
  expected_outcome: play.expected_outcome,
22317
22596
  source: play.source ?? "seed",
22318
- local_track_record: trackRecord
22597
+ local_track_record: trackRecord,
22598
+ framework_ids: frameworkIdsForPlay2(play.id)
22319
22599
  };
22320
22600
  if (play.source === "learned") {
22321
22601
  detail.security_notice = UNTRUSTED_CONTENT_NOTICE;
@@ -22326,6 +22606,30 @@ async function handleGetPlayDetail(input) {
22326
22606
  }
22327
22607
  return detail;
22328
22608
  }
22609
+ async function handleGetFrameworkDetail(input) {
22610
+ const frameworkId = typeof input.framework_id === "string" ? input.framework_id.trim() : "";
22611
+ const { getFrameworkById: getFrameworkById2, listFrameworkIds: listFrameworkIds2 } = await Promise.resolve().then(() => (init_frameworks(), frameworks_exports));
22612
+ const pack = frameworkId ? getFrameworkById2(frameworkId) : void 0;
22613
+ if (!pack) {
22614
+ return {
22615
+ error: `Unknown framework id '${frameworkId}'.`,
22616
+ valid_framework_ids: listFrameworkIds2()
22617
+ };
22618
+ }
22619
+ return {
22620
+ framework_id: pack.id,
22621
+ catalog_line: pack.catalog_line,
22622
+ always_on_catalog: pack.always_on_catalog,
22623
+ vital_links: pack.vital_links,
22624
+ board_cut: pack.board_cut,
22625
+ operator_cut: pack.operator_cut,
22626
+ when_to_use: pack.when_to_use,
22627
+ kill_rules: pack.kill_rules,
22628
+ silent_brand_rule: pack.silent_brand_rule,
22629
+ body: pack.body,
22630
+ notice: "Apply the moves in body/board_cut/operator_cut. Never name internal brand labels to the user."
22631
+ };
22632
+ }
22329
22633
  async function handleGetRevenueMetrics(_input, ctx) {
22330
22634
  if (!ctx.metrics || ctx.metrics.length === 0) {
22331
22635
  const { computeFullMetrics: computeFullMetrics2 } = await Promise.resolve().then(() => (init_compute(), compute_exports));
@@ -22620,6 +22924,7 @@ var init_tool_handlers = __esm({
22620
22924
  query_segment_comparison: handleQuerySegmentComparison,
22621
22925
  query_entity_counts: (input, _) => handleQueryEntityCounts(input),
22622
22926
  get_play_detail: (input, _) => handleGetPlayDetail(input),
22927
+ get_framework_detail: (input, _) => handleGetFrameworkDetail(input),
22623
22928
  get_session_brief: (input, _) => handleGetSessionBrief(input),
22624
22929
  get_revenue_metrics: handleGetRevenueMetrics,
22625
22930
  get_revenue_metrics_timeseries: (input, _) => handleGetRevenueMetricsTimeseries(input),
@@ -22901,6 +23206,12 @@ ${GTM_ENGINEERING_BLOCK}
22901
23206
  IDEA GATE (when recommending a play or before draft_strategy \u2014 silent; probing/imagination turns skip this until you conclude):
22902
23207
  ${HEILMEIER_GATE}
22903
23208
 
23209
+ BOTTLENECK (when concluding or recommending \u2014 silent):
23210
+ ${BOTTLENECK_GATE}
23211
+
23212
+ OWNER SHAPE (when recommending \u2014 silent):
23213
+ ${OWNER_SHAPE_BLOCK}
23214
+
22904
23215
  OUTPUT DOCTRINE (how answers are structured for recall):
22905
23216
  ${PYRAMID_OUTPUT_BLOCK}
22906
23217
 
@@ -22915,6 +23226,8 @@ ${VITAL_SIGNS_BLOCK}
22915
23226
  PLAYBOOK \u2014 name a play when it helps the user act (not a full program):
22916
23227
  ${buildPlaybookBlock()}
22917
23228
 
23229
+ ${buildFrameworkCatalogBlock()}
23230
+
22918
23231
  ${commandSection}
22919
23232
  ${formattingSection}
22920
23233
  OUTPUT RULES:
@@ -22943,6 +23256,7 @@ var init_think_prompt = __esm({
22943
23256
  "src/ai/think-prompt.ts"() {
22944
23257
  "use strict";
22945
23258
  init_prompt_parts();
23259
+ init_frameworks();
22946
23260
  init_prompt();
22947
23261
  }
22948
23262
  });
@@ -22973,6 +23287,7 @@ var init_ghost_hints = __esm({
22973
23287
  ],
22974
23288
  explore: [
22975
23289
  'try "what is the most expensive problem?"',
23290
+ 'try "which constraint is gating the dollars?"',
22976
23291
  'try "how should we fix this?"',
22977
23292
  'try "which segment is weakest?"',
22978
23293
  'try "what is ARR?"',
@@ -23000,7 +23315,7 @@ function buildSituationalAwarenessBlock(ctx, opts = {}) {
23000
23315
  `Phase: ${phase} (${formatPhaseLabel(phase)}) \u2014 ${PHASE_MEANING[phase]}`
23001
23316
  ];
23002
23317
  if (channel === "think") {
23003
- lines.push("Channel: think \u2014 socratic partner; type done/cancel returns to \u203A");
23318
+ lines.push("Channel: think \u2014 socratic partner; type done/cancel/b back returns to \u203A");
23004
23319
  lines.push("Response mode: deep (tools on)");
23005
23320
  } else if (channel === "strategist") {
23006
23321
  lines.push("Channel: strategist \u2014 measurable plan engine owns sequencing");
@@ -23044,11 +23359,13 @@ function buildSituationalAwarenessBlock(ctx, opts = {}) {
23044
23359
  } else if (phase === "awaiting_data") {
23045
23360
  lines.push("Already done: scope confirmed; analysis not ready");
23046
23361
  lines.push(
23047
- 'Available now: tell the user to paste a CSV path, type "use demo data", or \u23CE go ahead when can_compute \u2014 you cannot open files'
23362
+ 'Available now: tell the user to paste a CSV path, type "use demo data", \u23CE go ahead when can_compute, or b/back to rewind the focus card \u2014 you cannot open files'
23048
23363
  );
23049
23364
  } else if (phase === "scope") {
23050
23365
  lines.push("Already done: intent proposed; waiting on confirm");
23051
- lines.push("Available now: wait for CLI confirm (\u23CE yes) \u2014 do not invent a different question");
23366
+ lines.push(
23367
+ "Available now: wait for CLI confirm (\u23CE yes) or b/back/adjust \u2014 do not invent a different question"
23368
+ );
23052
23369
  } else if (phase === "orient") {
23053
23370
  lines.push("Already done: nothing locked yet");
23054
23371
  lines.push("Available now: help the user name a focus; CLI will propose scope from their words");
@@ -23063,9 +23380,28 @@ function buildSituationalAwarenessBlock(ctx, opts = {}) {
23063
23380
  if (examples.length > 0) {
23064
23381
  lines.push(`User may try: ${examples.join(" \xB7 ")}`);
23065
23382
  }
23066
- lines.push(
23067
- "Your job in this harness: fill the judgment gap after formula compute \u2014 connect dollars, name the expensive problem, do not restate scorecards the user already saw"
23068
- );
23383
+ if (channel === "think") {
23384
+ lines.push(
23385
+ "Doctrine: probing leads with the sharpest question (idea-gate off until you conclude); concluding/recommend turns \u2014 silent idea-gate then bottleneck then pyramid packaging; never name frameworks to the user"
23386
+ );
23387
+ } else if (responseMode === "brief") {
23388
+ lines.push(
23389
+ "Doctrine: brief packaging \u2014 headline \u226420 words, \u22642 drivers, one so-what; idea-gate off; never name frameworks to the user; do not restate The Call/scorecard \u2014 advance"
23390
+ );
23391
+ } else {
23392
+ lines.push(
23393
+ "Doctrine: deep/recommend \u2014 silent idea-gate then bottleneck then owner-shape then pyramid packaging; call get_framework_detail for toolkit depth; never name frameworks to the user"
23394
+ );
23395
+ }
23396
+ if (analysisReady) {
23397
+ lines.push(
23398
+ "Your job in this harness: after formula compute, lead with the most expensive or actionable implication \u2014 connect dollars, name the constraint, do not restate scorecards or The Call the user already saw"
23399
+ );
23400
+ } else {
23401
+ lines.push(
23402
+ "Your job in this harness: help the user through the funnel; CLI owns pre-explore gates"
23403
+ );
23404
+ }
23069
23405
  lines.push(
23070
23406
  "Pre-explore funnel gates are handled by the CLI; you speak in explore/think/strategist unless tools explicitly allow a restart"
23071
23407
  );
@@ -23078,10 +23414,12 @@ function buildSituationalRuntimeFacts(ctx, opts = {}) {
23078
23414
  return {
23079
23415
  phase,
23080
23416
  mode: responseMode,
23417
+ channel,
23081
23418
  lens: ctx.scope?.primary_lens,
23082
23419
  stage: ctx.stage,
23083
23420
  can_compute: ctx.gapAudit?.can_compute === void 0 ? void 0 : String(ctx.gapAudit.can_compute),
23084
- engine: canUseReplAi(ctx) ? "connected" : "none"
23421
+ engine: canUseReplAi(ctx) ? "connected" : "none",
23422
+ idea_gate: channel === "think" || responseMode === "deep" ? "on" : "off"
23085
23423
  };
23086
23424
  }
23087
23425
  function getConversationPhaseBlock(ctx, opts = {}) {
@@ -23100,13 +23438,13 @@ var init_situation = __esm({
23100
23438
  FUNNEL_SPINE = "Funnel: orient \u2192 scope \u2192 data \u2192 compute \u2192 explore | side doors: think, strategy, ship";
23101
23439
  PHASE_MEANING = {
23102
23440
  orient: "setup \u2014 open chat; user has not locked a scope yet",
23103
- scope: "scope confirm \u2014 CLI owns yes/adjust; do not re-propose unless asked",
23104
- awaiting_data: "data gate \u2014 load CSV/demo or compute; CLI owns the gate",
23441
+ scope: "scope confirm \u2014 CLI owns yes / b back / adjust; do not re-propose unless asked",
23442
+ awaiting_data: "data gate \u2014 load CSV/demo or compute; b/back rewinds to scope confirm (does not unload data); CLI owns the gate",
23105
23443
  compute: "compute in progress \u2014 wait",
23106
23444
  explore: "analysis complete \u2014 free-form Q&A on this session's artifact",
23107
- think: "socratic think channel \u2014 return to \u203A via done/cancel",
23108
- strategize: "strategist flow \u2014 objective confirm or plan drafting",
23109
- deliver: "ship/handoff wizard \u2014 CLI owns write confirms"
23445
+ think: "socratic think channel \u2014 return to \u203A via done/cancel/b back",
23446
+ strategize: "strategist flow \u2014 objective confirm (yes / b back / adjust) or plan drafting",
23447
+ deliver: "ship/handoff wizard \u2014 CLI owns write confirms; b/back or cancel leaves the draft"
23110
23448
  };
23111
23449
  }
23112
23450
  });
@@ -23301,7 +23639,7 @@ ${commandStyleRule}`;
23301
23639
  - Respond in plain text (not JSON). You do NOT need to emit the findings schema \u2014 only emit structured findings if the user explicitly asks for them.
23302
23640
  - Include specific numbers from tool results, never guess.
23303
23641
  - When you have enough information, answer directly. Don't call tools you don't need.`;
23304
- const executionBiasSection = responseMode === "brief" ? `OUTPUT DOCTRINE (brief \u2014 one headline + \u22642 drivers + one so-what):
23642
+ const executionBiasSection = responseMode === "brief" ? `OUTPUT DOCTRINE (brief \u2014 one headline + \u22642 drivers + one so-what; situation/complication only as support after the headline):
23305
23643
  ${PYRAMID_OUTPUT_BLOCK}
23306
23644
 
23307
23645
  ` : `EXECUTION BIAS (how you work):
@@ -23313,6 +23651,12 @@ ${GTM_ENGINEERING_BLOCK}
23313
23651
  IDEA GATE (when recommending action \u2014 silent):
23314
23652
  ${HEILMEIER_GATE}
23315
23653
 
23654
+ BOTTLENECK (constraint first \u2014 silent):
23655
+ ${BOTTLENECK_GATE}
23656
+
23657
+ OWNER SHAPE (when recommending action \u2014 silent):
23658
+ ${OWNER_SHAPE_BLOCK}
23659
+
23316
23660
  OUTPUT DOCTRINE (how answers are structured for recall):
23317
23661
  ${PYRAMID_OUTPUT_BLOCK}
23318
23662
 
@@ -23328,6 +23672,8 @@ ${VITAL_SIGNS_BLOCK}
23328
23672
  PLAYBOOK \u2014 recommend these plays when appropriate:
23329
23673
  ${buildPlaybookBlock()}
23330
23674
 
23675
+ ${buildFrameworkCatalogBlock()}
23676
+
23331
23677
  ${commandSection}`;
23332
23678
  const stable = `You are a world-class GTM operating partner \u2014 the kind of analyst a CEO keeps on speed dial. You are exceptionally well-read, rigorous, and commercially sharp, and you have tools to query a local database of this company's CRM and pipeline data. The user is having an ongoing, free-form conversation with you about their go-to-market health and SaaS metrics.
23333
23679
 
@@ -23576,6 +23922,7 @@ var init_agentic_loop = __esm({
23576
23922
  init_thread();
23577
23923
  init_untrusted();
23578
23924
  init_prompt_parts();
23925
+ init_frameworks();
23579
23926
  init_think_prompt();
23580
23927
  init_prompt();
23581
23928
  init_context2();
@@ -28480,13 +28827,16 @@ ${companyContextSection4()}${operatorSection4()}HOW YOU THINK (the strategist me
28480
28827
  8. RESPECT CAPACITY. Total effort must fit the team that actually exists. A brilliant plan the team cannot staff is a bad plan.
28481
28828
 
28482
28829
  ALTITUDE CONTRACT (the plan must work at every altitude a client reads it at):
28483
- - 30,000 FT: summary_30k is answer-first \u2014 open with the governing thought and what leadership should expect by when, then the situation and complication that make it true. A board member reads only this; it must survive being forwarded unedited.
28484
- - 10,000 FT: each workstream's title + problem line is a delegation unit \u2014 a one-liner an owner could receive in Slack and know what they own, why it's theirs, and what number they move.
28830
+ - 30,000 FT: summary_30k is answer-first \u2014 open with the governing thought (the answer) and what leadership should expect by when, then the situation and complication that make it true as support. A board member reads only this; it must survive being forwarded unedited.
28831
+ - 10,000 FT: each workstream's title + problem line is a delegation unit \u2014 a one-liner an owner could receive in Slack and know what they own, why it's theirs, and what number they move. Use function-shaped owners (see OWNER SHAPE).
28485
28832
  - GROUND LEVEL: actions are Monday-morning prescriptive. You have built this before \u2014 read the linked play's full detail with get_play_detail and prescribe its known-good sequence adapted to THIS company's numbers and constraints. The first action of every workstream must be startable within 48 hours with no new tooling.
28486
28833
 
28487
28834
  OUTPUT DOCTRINE (narrative fields \u2014 summary_30k and workstream problem lines \u2014 follow pyramid recall):
28488
28835
  ${PYRAMID_OUTPUT_BLOCK}
28489
28836
 
28837
+ OWNER SHAPE (workstreams and actions \u2014 silent):
28838
+ ${OWNER_SHAPE_BLOCK}
28839
+
28490
28840
  ${buildUserVisibleProseBlock()}
28491
28841
 
28492
28842
  GTM ENGINEERING (plans install systems, not heroics):
@@ -28501,7 +28851,10 @@ ${METRICS_BLOCK}
28501
28851
  PLAYBOOK (every workstream must link to at least one play by exact id):
28502
28852
  ${buildPlaybookBlock()}
28503
28853
 
28854
+ ${buildFrameworkCatalogBlock()}
28855
+
28504
28856
  MEASURABILITY CONTRACT (non-negotiable \u2014 this is what separates you from a slide deck):
28857
+ - Treat the objective as the end state; treat expected outcomes and leading indicators as key results (mid-term and final exams).
28505
28858
  - Every expected outcome and leading indicator needs: a metric, the current baseline copied from your verified grounding work, a target RANGE, a check date, and the named instrument that will measure it.
28506
28859
  - Banned words for outcomes: "improve", "better", "significantly", "optimize", "increase" without a number. If you cannot quantify it, it is not an outcome \u2014 demote it to an assumption or replace it with its best measurable proxy.
28507
28860
  - Every workstream needs at least one dated milestone with a verification method that names a number and threshold.
@@ -28520,9 +28873,13 @@ function buildGroundingMessage(input) {
28520
28873
 
28521
28874
  Establish verified reality before any planning. Work hypothesis-first, like an engagement manager on day one: form your top candidate explanations for what stands between today and the objective, then use tools to confirm or kill each one \u2014 don't boil the ocean.
28522
28875
  1. Current state: which vital signs / metrics are worst, what are the exact scores and dollar values, which segments concentrate the problem?
28523
- 2. What is already in flight (active strategies below, if any), what has worked before (wins), and what the local play track record says.
28524
- 3. What are the binding constraints: data gaps that limit measurability, sales-cycle length that bounds verification speed, capacity signals?
28525
- Rank the problems you verify by dollars at stake \xD7 confidence in the read \xD7 speed to impact \u2014 that ranking becomes the spine of the plan.`);
28876
+ 2. CONSTRAINT: name the single gating vital (layer order) or stage where deals die \u2014 with score and dollar label. This is the bottleneck the plan must move first.
28877
+ 3. What is already in flight (active strategies below, if any), what has worked before (wins), and what the local play track record says.
28878
+ 4. What are the binding constraints: data gaps that limit measurability, sales-cycle length that bounds verification speed, capacity signals?
28879
+ Rank the problems you verify by dollars at stake \xD7 confidence in the read \xD7 speed to impact \u2014 that ranking becomes the spine of the plan. Non-constraint problems wait.
28880
+
28881
+ BOTTLENECK (silent):
28882
+ ${BOTTLENECK_GATE}`);
28526
28883
  sections.push(`CURRENT HEALTH SNAPSHOT (verified \u2014 you may cite these numbers as baselines):
28527
28884
  ${input.healthSnapshot}`);
28528
28885
  if (input.gapAuditBlock) {
@@ -28571,6 +28928,8 @@ function buildStressTestMessage() {
28571
28928
  8. ALTITUDE CHECK: does summary_30k survive being forwarded to a board member unedited? Is each workstream title + problem a self-contained delegation one-liner? Is every first action startable within 48 hours?
28572
28929
  9. HEILMEIER GATE (silent \u2014 never name this to the user). Run every workstream through this filter. Failures: cut the workstream, demote it to assumptions, or rewrite until it passes \u2014 then emit final JSON.
28573
28930
  ${HEILMEIER_GATE}
28931
+ 10. AFTER-ACTION PREP (silent). Ensure each workstream's exams can support a later belief\u2192evidence\u2192next-bet review. Fold gaps into assumptions or contingencies.
28932
+ ${AAR_BLOCK}
28574
28933
 
28575
28934
  Then respond with the FINAL revised plan as strict JSON in the same schema \u2014 no markdown fences, no prose. Fold what you learned into constraints, assumptions, risks, and confidence. This version is the one that gets saved and reviewed against, so make every number one you are willing to be checked on.`;
28576
28935
  }
@@ -28579,6 +28938,7 @@ var init_strategist_prompt = __esm({
28579
28938
  "src/ai/strategist-prompt.ts"() {
28580
28939
  "use strict";
28581
28940
  init_prompt_parts();
28941
+ init_frameworks();
28582
28942
  init_prompt();
28583
28943
  STRATEGIST_PLAN_SCHEMA_BLOCK = `{
28584
28944
  "title": "Short plan name, e.g. 'Q4 Pipeline Recovery'",
@@ -29614,7 +29974,7 @@ async function handleStrategizeFlow(input, ctx) {
29614
29974
  " " + chalk37.dim("That looks like a question. A strategy objective is waiting.")
29615
29975
  );
29616
29976
  console.log(
29617
- " " + chalk37.dim("Type ") + chalk37.cyan("yes") + chalk37.dim(" to make the plan. Type ") + chalk37.cyan("adjust") + chalk37.dim(" to change the objective. Type ") + chalk37.cyan("cancel") + chalk37.dim(" to answer questions first.")
29977
+ " " + chalk37.dim("Type ") + chalk37.cyan("yes") + chalk37.dim(" to make the plan. Type ") + chalk37.cyan("b") + chalk37.dim(" / ") + chalk37.cyan("adjust") + chalk37.dim(" to change the objective. Type ") + chalk37.cyan("cancel") + chalk37.dim(" to answer questions first.")
29618
29978
  );
29619
29979
  console.log();
29620
29980
  return "Awaiting confirm";
@@ -29627,7 +29987,7 @@ async function handleStrategizeFlow(input, ctx) {
29627
29987
  }
29628
29988
  console.log();
29629
29989
  console.log(
29630
- " " + chalk37.dim("Type ") + chalk37.cyan("yes") + chalk37.dim(" to make the plan. Type ") + chalk37.cyan("adjust") + chalk37.dim(" to change the objective. Type ") + chalk37.cyan("cancel") + chalk37.dim(" to stop.")
29990
+ " " + chalk37.dim("Type ") + chalk37.cyan("yes") + chalk37.dim(" to make the plan. Type ") + chalk37.cyan("b") + chalk37.dim(" / ") + chalk37.cyan("adjust") + chalk37.dim(" to change the objective. Type ") + chalk37.cyan("cancel") + chalk37.dim(" to stop.")
29631
29991
  );
29632
29992
  console.log();
29633
29993
  return "Awaiting confirm";
@@ -29804,7 +30164,7 @@ function printObjectiveCard(ctx, objective, proposed) {
29804
30164
  );
29805
30165
  console.log();
29806
30166
  console.log(
29807
- " " + chalk37.dim("Confirm? ") + chalk37.cyan("\u23CE yes") + chalk37.dim(" \xB7 ") + chalk37.cyan("adjust") + chalk37.dim(" \xB7 ") + chalk37.cyan("cancel")
30167
+ " " + chalk37.dim("Confirm? ") + chalk37.cyan("\u23CE yes") + chalk37.dim(" \xB7 ") + chalk37.cyan("b back") + chalk37.dim(" \xB7 ") + chalk37.cyan("adjust") + chalk37.dim(" \xB7 ") + chalk37.cyan("cancel")
29808
30168
  );
29809
30169
  console.log();
29810
30170
  }
@@ -30286,6 +30646,11 @@ function renderReport(report) {
30286
30646
  `${strategy.slug} \xB7 baseline batch ${shortBatch(report.baseline_batch_id)} \u2192 latest ${shortBatch(report.latest_batch_id)}`
30287
30647
  )
30288
30648
  );
30649
+ console.log(
30650
+ " " + chalk38.dim(
30651
+ "Read as belief \u2192 evidence \u2192 next bet: what we expected, what instruments showed, what to keep/kill/re-sequence."
30652
+ )
30653
+ );
30289
30654
  if (report.single_batch) {
30290
30655
  console.log(
30291
30656
  " " + chalk38.yellow("Only one compute batch exists \u2014 ingest fresh data and re-run for a real before/after.")
@@ -32167,7 +32532,7 @@ function printScopeProposal(ctx) {
32167
32532
  }
32168
32533
  console.log();
32169
32534
  console.log(
32170
- " " + chalk52.dim("Confirm? ") + chalk52.cyan("\u23CE yes") + chalk52.dim(" \xB7 ") + chalk52.cyan("adjust")
32535
+ " " + chalk52.dim("Confirm? ") + chalk52.cyan("\u23CE yes") + chalk52.dim(" \xB7 ") + chalk52.cyan("b back") + chalk52.dim(" \xB7 ") + chalk52.cyan("adjust")
32171
32536
  );
32172
32537
  console.log();
32173
32538
  }
@@ -36564,7 +36929,8 @@ handler: ../commands/knowledge.ts
36564
36929
 
36565
36930
  Teach the analyst from work done outside the platform.
36566
36931
  Type \`/knowledge add <file>\` to ingest a markdown, text, or PDF case study, framework, or benchmark report.
36567
- NTRP indexes it for retrieval during analysis. Type \`/knowledge list\` to show what is indexed.
36932
+ NTRP indexes it for retrieval during analysis (your own consulting packs welcome \u2014 built-in toolkit is silent and lives behind get_framework_detail).
36933
+ Type \`/knowledge list\` to show what is indexed.
36568
36934
  Drop files into ~/.ntrp/knowledge to stage them.`
36569
36935
  },
36570
36936
  {
@@ -36683,6 +37049,7 @@ CLI slide deck: SaaS refresher, five vital signs (definition, formula, visual, d
36683
37049
  Bare \`/deepdive\` runs the full onboarding tour. Type \`/deepdive guide\` to jump to how to use NTRP (includes teaching the inbox once).
36684
37050
  Type \`/deepdive handoff\` for the ship-to-AI slide. Type \`/deepdive <metric>\` to jump to one metric card.
36685
37051
  Type \`/deepdive list\` to print the catalog. Works without an AI key. Re-run anytime from the homescreen.
37052
+ During the tour: Enter advances, \`b\` / \`back\` goes to the previous slide, \`q\` quits. After the tour, the same \`b\` / \`back\` word steps back one confirm gate in the REPL (scope, data, strategy) \u2014 it does not unload data or undo compute.
36686
37053
  Live values overlay when an analysis exists.`
36687
37054
  },
36688
37055
  {
@@ -36697,7 +37064,7 @@ handler: ../commands/thinkwithme.ts
36697
37064
 
36698
37065
  Open a dedicated \`think \u203A\` channel to explore ideas, challenge assumptions, and pull evidence from your data.
36699
37066
  Bare \`/thinkwithme\` enters the channel. Type \`/thinkwithme <topic>\` to seed the first turn.
36700
- Requires an analysis and an AI key (\`/connect\`). Type \`done\` or \`cancel\` to return to \u203A.
37067
+ Requires an analysis and an AI key (\`/connect\`). Type \`done\`, \`cancel\`, or \`b\` / \`back\` to return to \u203A.
36701
37068
  When you are ready to commit to a plan, say so or the partner can hand off to \`/strategy\`.`
36702
37069
  },
36703
37070
  {
@@ -37100,7 +37467,7 @@ function formatCatalogLine(meta) {
37100
37467
  const note = DESTRUCTIVE_COMMAND_NOTES[meta.name] ? ` (${DESTRUCTIVE_COMMAND_NOTES[meta.name]})` : "";
37101
37468
  return `- /${meta.name}${args} \u2014 ${meta.description}${note}`;
37102
37469
  }
37103
- var ANALYST_FILE_NAME, ANALYST_FILE_MAX_CHARS, EXECUTION_BIAS_BLOCK, ANALYST_INSTINCT_BLOCK, SAFETY_BLOCK, VITAL_SIGNS_BLOCK, PLAYBOOK_BLOCK, DESTRUCTIVE_COMMAND_NOTES, METRICS_BLOCK, GTM_ENGINEERING_BLOCK, HEILMEIER_GATE, PYRAMID_OUTPUT_BLOCK;
37470
+ var ANALYST_FILE_NAME, ANALYST_FILE_MAX_CHARS, EXECUTION_BIAS_BLOCK, ANALYST_INSTINCT_BLOCK, SAFETY_BLOCK, VITAL_SIGNS_BLOCK, PLAYBOOK_BLOCK, DESTRUCTIVE_COMMAND_NOTES, METRICS_BLOCK, GTM_ENGINEERING_BLOCK, HEILMEIER_GATE, BOTTLENECK_GATE, OWNER_SHAPE_BLOCK, AAR_BLOCK, PYRAMID_OUTPUT_BLOCK;
37104
37471
  var init_prompt_parts = __esm({
37105
37472
  "src/ai/prompt-parts.ts"() {
37106
37473
  "use strict";
@@ -37180,17 +37547,33 @@ Instrument trust: every metric here carries confidence and reliability_gate fiel
37180
37547
  - INSTRUMENT WHAT YOU CHANGE. A system you can't measure is a system you can't defend at the next QBR. Name the metric each mechanism should move and where it will be read.
37181
37548
  Restraint: you diagnose and prescribe the system; you do not build it here. Name the mechanism class, not a vendor shopping list.`;
37182
37549
  HEILMEIER_GATE = `Before you recommend an action, play, or workstream \u2014 run this gate. Failures are silent: kill, demote to an assumption, or ask one sharper question. Never say Heilmeier, DARPA, or catechism to the user.
37183
- - END STATE. Name the precise outcome in one line. Vague goals die here.
37550
+ - END STATE. Name the precise outcome in one line (the objective). Vague goals die here.
37184
37551
  - STATUS QUO. How is it done today, and what is the hard limit of that approach?
37185
37552
  - NEWNESS. What is new in this approach, and why might it work when the status quo fails? If nothing is new, kill it as a restatement.
37186
37553
  - STAKE. Who cares? If it works, what difference does it make \u2014 dollars recovered or a decision unlocked? Name the cost of doing nothing. No stake \u2192 do not recommend.
37187
37554
  - RISKS AND PAYOFFS. State the main downside and the upside in the same breath.
37188
37555
  - EFFORT. Rough cost in team hours or capacity. If it cannot fit the team that exists, cut or re-sequence \u2014 do not soft-pedal.
37189
37556
  - TIME. Bound by sales-cycle and metric-update physics. Dates faster than evidence can appear are invalid.
37190
- - EXAMS. Name a mid-term check and a final exam: dated, measurable, with an instrument that exists. No exams \u2192 demote to assumption or drop.
37557
+ - EXAMS (key results). Name a mid-term check and a final exam: dated, measurable, with baseline + target RANGE + an instrument that exists. These are the objective's key results. No exams \u2192 demote to assumption or drop.
37191
37558
  - KILL RULE. If newness, stake, or exams fail: do not recommend. Demote, drop, or ask one sharper question. Surviving ideas then get pyramid packaging.`;
37559
+ BOTTLENECK_GATE = `Before you prescribe a fix \u2014 name the constraint. Never say Theory of Constraints, TOC, or Goldratt to the user.
37560
+ - CONSTRAINT FIRST. Identify the single gating vital (layer order: freshness \u2192 flow_rate/drop_rate \u2192 signal_to_noise \u2192 thread_depth) or the pipeline stage where deals die \u2014 with score and dollar label.
37561
+ - PRESCRIBE THE CONSTRAINT. The first play or workstream must move that constraint. Non-constraint work is optional polish.
37562
+ - DEPENDENCY OVER NOISE. If two problems compete, pick the one that unblocks the other.
37563
+ - KILL RULE. If the recommendation does not move the named constraint, demote or drop it.`;
37564
+ OWNER_SHAPE_BLOCK = `When you recommend action \u2014 shape the owner. Never say RACI to the user.
37565
+ - FUNCTION NOT PERSON. Name RevOps, sales manager, CS lead, marketing ops, or AE lead \u2014 not a CRM contact's personal name.
37566
+ - ACCOUNTABLE DECIDES; RESPONSIBLE EXECUTES. One accountable function per ask.
37567
+ - 48-HOUR START. The first action must be startable by that function within 48 hours with no new tooling.
37568
+ - NO REORG. Do not invent headcount or redesign the company.`;
37569
+ AAR_BLOCK = `When reviewing outcomes or distilling learning \u2014 run after-action. Never say AAR or after-action review to the user.
37570
+ - BELIEF. What we expected to move (metric + range + check date).
37571
+ - EVIDENCE. What the instruments showed (baseline \u2192 reading).
37572
+ - LEARNING. Keep, kill, or re-sequence. Write judgment changes as calibrations that supersede contradictions.
37573
+ - NEXT BET. One next exam \u2014 not a new multi-week program.`;
37192
37574
  PYRAMID_OUTPUT_BLOCK = `Structure everything the way a client remembers it \u2014 pyramid, answer first:
37193
37575
  - HEADLINE FIRST. Open with the verdict and the number in one sentence (\u226420 words): what is true and what it costs. Never open with methodology or context.
37576
+ - SITUATION AS SUPPORT. After the headline, the situation and complication that make the call true may follow as support \u2014 never as the opener. Do not name SCQA to the user.
37194
37577
  - THEN THE DRIVERS. Support the headline with 2-3 drivers that are distinct, non-overlapping, and together explain the headline \u2014 each with its own number. If two points share a cause, merge them.
37195
37578
  - THEN THE SO-WHAT. Close with what it means for the decision at hand: the action, the owner-shaped next step, or the sharpest next cut.
37196
37579
  - THE RECALL TEST. A busy executive should be able to repeat your headline and one number to their CEO an hour later. If they couldn't, tighten it.
@@ -38427,6 +38810,83 @@ function popModalState(ctx) {
38427
38810
  }
38428
38811
  return popped;
38429
38812
  }
38813
+ function handleBackNavigation(ctx, line) {
38814
+ if (!isBackInput(line)) return null;
38815
+ const phase = resolveConversationPhase(ctx);
38816
+ if (phase === "orient" || phase === "explore" || phase === "compute") {
38817
+ return null;
38818
+ }
38819
+ if (phase === "scope") {
38820
+ console.log();
38821
+ console.log(" " + chalk84.dim("What should we focus on instead?"));
38822
+ ctx.scope = void 0;
38823
+ clearPendingAsk(ctx);
38824
+ saveSessionState(ctx);
38825
+ recordMessage(ctx, "user", line);
38826
+ recordMessage(ctx, "agent", "Scope cleared (back)");
38827
+ console.log();
38828
+ return { handled: true, summary: "Scope cleared" };
38829
+ }
38830
+ if (phase === "awaiting_data") {
38831
+ if (!ctx.scope?.confirmed_at) return null;
38832
+ const hadData = sessionHasData(ctx);
38833
+ const hadAnalysis = ctx.stage === "analyzed" || isAnalysisReady(ctx);
38834
+ unconfirmScope(ctx);
38835
+ saveSessionState(ctx);
38836
+ recordMessage(ctx, "user", line);
38837
+ recordMessage(ctx, "agent", "Scope unconfirmed (back)");
38838
+ printScopeProposal(ctx);
38839
+ if (hadData || hadAnalysis) {
38840
+ console.log(
38841
+ " " + chalk84.dim(
38842
+ "Focus gate rewound \u2014 loaded data and any analysis stay. `b` does not unload demo data or undo compute."
38843
+ )
38844
+ );
38845
+ console.log();
38846
+ }
38847
+ return { handled: true, summary: "Scope unconfirmed" };
38848
+ }
38849
+ if (phase === "strategize") {
38850
+ const state2 = ctx.strategistState;
38851
+ if (state2?.step === "objective_confirm") {
38852
+ state2.step = "objective_input";
38853
+ saveSessionState(ctx);
38854
+ recordMessage(ctx, "user", line);
38855
+ recordMessage(ctx, "agent", "Strategy objective adjust (back)");
38856
+ console.log();
38857
+ console.log(" " + chalk84.dim("What is the objective? State a finish line."));
38858
+ console.log();
38859
+ return { handled: true, summary: "Awaiting objective" };
38860
+ }
38861
+ const popped = popModalState(ctx);
38862
+ if (popped.length === 0) return null;
38863
+ saveSessionState(ctx);
38864
+ recordMessage(ctx, "user", line);
38865
+ recordMessage(ctx, "agent", `Back \u2014 dropped ${popped.join(", ")}`);
38866
+ const backTo = formatPhaseLabel(resolveConversationPhase(ctx));
38867
+ console.log();
38868
+ console.log(
38869
+ " " + chalk84.dim(`Back \u2014 dropped ${popped.join(" and ")}. At `) + chalk84.cyan(backTo) + chalk84.dim(".")
38870
+ );
38871
+ console.log();
38872
+ return { handled: true, summary: "Back" };
38873
+ }
38874
+ if (phase === "think" || phase === "deliver") {
38875
+ const popped = popModalState(ctx);
38876
+ if (popped.length === 0) return null;
38877
+ saveSessionState(ctx);
38878
+ recordMessage(ctx, "user", line);
38879
+ recordMessage(ctx, "agent", `Back \u2014 dropped ${popped.join(", ")}`);
38880
+ const backTo = formatPhaseLabel(resolveConversationPhase(ctx));
38881
+ console.log();
38882
+ console.log(
38883
+ " " + chalk84.dim(`Back \u2014 dropped ${popped.join(" and ")}. At `) + chalk84.cyan(backTo) + chalk84.dim(".")
38884
+ );
38885
+ console.log();
38886
+ return { handled: true, summary: "Back" };
38887
+ }
38888
+ return null;
38889
+ }
38430
38890
  async function conversationRouter(input, ctx) {
38431
38891
  if (ctx.oneShot || (ctx.wizardDepth ?? 0) > 0) {
38432
38892
  return { handled: false };
@@ -38456,6 +38916,8 @@ async function conversationRouter(input, ctx) {
38456
38916
  return { handled: true, summary: "Cancelled" };
38457
38917
  }
38458
38918
  }
38919
+ const backResult = handleBackNavigation(ctx, line);
38920
+ if (backResult) return backResult;
38459
38921
  if (/^\S+$/.test(line) && !/^\d$/.test(line)) {
38460
38922
  const { resolveSessionByToken: resolveSessionByToken2 } = await Promise.resolve().then(() => (init_context2(), context_exports));
38461
38923
  if (resolveSessionByToken2(line, { printErrors: false }) !== void 0) {
@@ -38560,6 +39022,8 @@ var init_router = __esm({
38560
39022
  init_think_flow();
38561
39023
  init_context2();
38562
39024
  init_demo();
39025
+ init_scope();
39026
+ init_pending_ask();
38563
39027
  FRESH_START_RE = /\b(start (over|fresh)|new analysis|start again|reset session)\b/i;
38564
39028
  CANCEL_ESCAPE_RE = /^(cancel|stop|abort|quit|never\s?mind|nevermind|forget it)\s*[.!]?\s*$/i;
38565
39029
  }
@@ -38783,17 +39247,17 @@ var init_loop_guard2 = __esm({
38783
39247
  PHASE_GUIDES = {
38784
39248
  scope: {
38785
39249
  mode: "scope confirmation",
38786
- accepts: "\u23CE or yes (run with this focus) \xB7 adjust (restate it) \xB7 a new focus statement",
38787
- leave: "cancel drops the proposal \xB7 /home shows the dashboard"
39250
+ accepts: "\u23CE or yes (run with this focus) \xB7 b / back (clear it) \xB7 adjust (restate it) \xB7 a new focus statement",
39251
+ leave: "b / back or cancel drops the proposal \xB7 /home shows the dashboard"
38788
39252
  },
38789
39253
  awaiting_data: {
38790
39254
  mode: "data loading",
38791
39255
  accepts: 'a CSV path \xB7 "use demo data" \xB7 "go ahead" (compute once data is loaded) \xB7 \u23CE runs the action shown at the prompt',
38792
- leave: "cancel drops the scope \xB7 /home shows the dashboard"
39256
+ leave: "b / back rewinds to the focus confirm card (data stays) \xB7 cancel \xB7 /home shows the dashboard"
38793
39257
  },
38794
39258
  strategize: {
38795
39259
  mode: "strategy objective confirm",
38796
- accepts: "\u23CE or yes (build the plan) \xB7 adjust (restate the objective) \xB7 cancel",
39260
+ accepts: "\u23CE or yes (build the plan) \xB7 b / back or adjust (restate the objective) \xB7 cancel",
38797
39261
  leave: "cancel drops the strategy session and returns to Q&A"
38798
39262
  }
38799
39263
  };
@@ -39721,6 +40185,20 @@ function printHelp() {
39721
40185
  console.log(" " + chalk88.dim("Type ") + paint("accent", '"ship a board deck"') + chalk88.dim(" to write a handoff."));
39722
40186
  console.log(" " + chalk88.dim("The ") + paint("accent", "\u203A") + chalk88.dim(" prompt shows brief or deep after analysis. Brief is the default."));
39723
40187
  console.log();
40188
+ console.log(" " + sectionHeading("Keys"));
40189
+ console.log(
40190
+ " " + paint("accent", "\u23CE") + chalk88.dim(" Runs the armed action shown at the prompt (yes, use demo data, go ahead, /connect).")
40191
+ );
40192
+ console.log(
40193
+ " " + paint("accent", "b") + chalk88.dim(" / ") + paint("accent", "back") + chalk88.dim(" Steps back one confirm gate, or leaves a modal. Does not unload data or undo compute.")
40194
+ );
40195
+ console.log(
40196
+ " " + paint("accent", "cancel") + chalk88.dim(" Drops the current modal overlay (strategy, think, ship, unconfirmed scope).")
40197
+ );
40198
+ console.log(
40199
+ " " + chalk88.dim("On ") + paint("accent", "/deepdive") + chalk88.dim(" slides, ") + paint("accent", "b") + chalk88.dim(" moves to the previous slide.")
40200
+ );
40201
+ console.log();
39724
40202
  console.log(" " + sectionHeading("Shortcuts"));
39725
40203
  const shortcuts = [
39726
40204
  ["/home", "Show status"],