@sonnechasser/ntrp 2.2.1 → 2.3.2
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 +231 -142
- package/dist/mcp/server.js +58 -51
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -1584,7 +1584,7 @@ function printGuideCatalogLine(slide) {
|
|
|
1584
1584
|
function printDeepdiveHint(metricId, label) {
|
|
1585
1585
|
const name = label ?? metricId;
|
|
1586
1586
|
console.log(
|
|
1587
|
-
" " + chalk3.dim(
|
|
1587
|
+
" " + chalk3.dim(`Ask what ${name.toLowerCase()} means \u2014 or `) + paint("accent", `/tour ${metricId}`) + chalk3.dim(" for the card")
|
|
1588
1588
|
);
|
|
1589
1589
|
console.log();
|
|
1590
1590
|
}
|
|
@@ -1987,6 +1987,23 @@ function printHealthSummary(result, _pipelineMetrics) {
|
|
|
1987
1987
|
VITAL_SIGN_LABELS[result.gating_vital_sign]
|
|
1988
1988
|
);
|
|
1989
1989
|
}
|
|
1990
|
+
function printHealthLine(result) {
|
|
1991
|
+
const score = `${chalk4.bold(String(Math.round(result.overall_score)))}${chalk4.dim("/100")}`;
|
|
1992
|
+
const gloss = voiceHealthGloss(result.overall_status);
|
|
1993
|
+
const parts = [
|
|
1994
|
+
`${chalk4.dim("Health")} ${score} ${statusBadge(result.overall_status)}${gloss ? chalk4.dim(` ${gloss}`) : ""}`,
|
|
1995
|
+
`${chalk4.dim("held back by")} ${paint("accent", VITAL_SIGN_LABELS[result.gating_vital_sign])}`
|
|
1996
|
+
];
|
|
1997
|
+
if (result.total_value_at_risk != null && result.total_value_at_risk > 0) {
|
|
1998
|
+
parts.push(`${paint("success", formatCurrency(result.total_value_at_risk))} ${chalk4.dim("total at risk")}`);
|
|
1999
|
+
}
|
|
2000
|
+
console.log();
|
|
2001
|
+
console.log(" " + parts.join(chalk4.dim(" \xB7 ")));
|
|
2002
|
+
printDeepdiveHint(
|
|
2003
|
+
result.gating_vital_sign,
|
|
2004
|
+
VITAL_SIGN_LABELS[result.gating_vital_sign]
|
|
2005
|
+
);
|
|
2006
|
+
}
|
|
1990
2007
|
function printVitalSigns(vitals) {
|
|
1991
2008
|
console.log();
|
|
1992
2009
|
printHeading("Vital Signs");
|
|
@@ -16931,7 +16948,7 @@ function consumeOrientEmptyEnterCoach(ctx) {
|
|
|
16931
16948
|
if (!hasValidLicense()) {
|
|
16932
16949
|
return "Type /activate to paste a key. Type /checkout if you need to sign up.";
|
|
16933
16950
|
}
|
|
16934
|
-
return "Type a question, type use demo data
|
|
16951
|
+
return "Type a question, or type use demo data. Enter alone does not start a step here.";
|
|
16935
16952
|
}
|
|
16936
16953
|
function formatPhaseLabel(phase) {
|
|
16937
16954
|
switch (phase) {
|
|
@@ -17555,7 +17572,10 @@ function buildExploreTeachingGhosts(ctx, staticExplore) {
|
|
|
17555
17572
|
if (cached2 && cached2.length > 0) {
|
|
17556
17573
|
const ghosts = asksToGhostHints(cached2);
|
|
17557
17574
|
const gating = ctx.snapshot.computeResult?.aggregate.gating_vital_sign;
|
|
17558
|
-
if (gating)
|
|
17575
|
+
if (gating) {
|
|
17576
|
+
const label = (VITAL_SIGN_LABELS[gating] ?? gating).toLowerCase();
|
|
17577
|
+
ghosts.push(`try "why is ${label} red?"`);
|
|
17578
|
+
}
|
|
17559
17579
|
ghosts.push('try "how should we fix this?"');
|
|
17560
17580
|
return dedupeGhosts(ghosts);
|
|
17561
17581
|
}
|
|
@@ -17566,7 +17586,11 @@ function buildExploreTeachingGhosts(ctx, staticExplore) {
|
|
|
17566
17586
|
});
|
|
17567
17587
|
return dedupeGhosts([
|
|
17568
17588
|
...asksToGhostHints(asks),
|
|
17569
|
-
|
|
17589
|
+
(() => {
|
|
17590
|
+
const g = health.gating_vital_sign;
|
|
17591
|
+
const label = (VITAL_SIGN_LABELS[g] ?? g).toLowerCase();
|
|
17592
|
+
return `try "why is ${label} red?"`;
|
|
17593
|
+
})(),
|
|
17570
17594
|
'try "how should we fix this?"'
|
|
17571
17595
|
]);
|
|
17572
17596
|
}
|
|
@@ -18345,21 +18369,21 @@ var init_metric_definitions = __esm({
|
|
|
18345
18369
|
kind: "vital",
|
|
18346
18370
|
label: "Freshness",
|
|
18347
18371
|
group: "Vital Signs",
|
|
18348
|
-
tagline: "Is the CRM
|
|
18349
|
-
how_computed: "
|
|
18372
|
+
tagline: "Is the CRM talking about live deals \u2014 or a museum of old ones?",
|
|
18373
|
+
how_computed: "We look at how recently people, companies, and deals got a touch. Deals need a fresher pulse than people or companies, and past-due deals count as stale. The formula lines on this card show the exact mix and windows.",
|
|
18350
18374
|
formula_lines: [
|
|
18351
18375
|
"freshness = people%\xD70.35 + orgs%\xD70.30 + opps%\xD70.35",
|
|
18352
18376
|
"people/orgs fresh if activity within 90d",
|
|
18353
18377
|
"opps fresh if activity within 30d AND not past-due"
|
|
18354
18378
|
],
|
|
18355
|
-
meaning:
|
|
18356
|
-
expert_read:
|
|
18379
|
+
meaning: `Freshness asks whether the pipeline number is still true. Quiet deals pad the forecast. Like milk dates on a shelf \u2014 past the date, don't trust the carton. Stale open dollars show as "pipeline at risk."`,
|
|
18380
|
+
expert_read: "Cut by owner and stage first \u2014 reds usually clump, they don't sprinkle. A sudden cliff is often a broken sync or a departed rep, not slow decay. New bulk imports can look stale when they're just new.",
|
|
18357
18381
|
deepdive: [
|
|
18358
|
-
"
|
|
18359
|
-
'
|
|
18360
|
-
"
|
|
18361
|
-
"
|
|
18362
|
-
"
|
|
18382
|
+
"Green 80+, yellow 60+, red below 60. Motion presets can change the windows.",
|
|
18383
|
+
'Dollars: sum of amount on stale open deals \u2192 "pipeline at risk".',
|
|
18384
|
+
"Read this first. If Freshness is red, later vitals are harder to trust.",
|
|
18385
|
+
"Weak score \u2192 play Clean Dead Pipeline.",
|
|
18386
|
+
"Useful moves: stale-deal alert, weekly hygiene scrub, enrichment on quiet records."
|
|
18363
18387
|
],
|
|
18364
18388
|
visual: {
|
|
18365
18389
|
kind: "bars",
|
|
@@ -18383,21 +18407,21 @@ var init_metric_definitions = __esm({
|
|
|
18383
18407
|
kind: "vital",
|
|
18384
18408
|
label: "Flow Rate",
|
|
18385
18409
|
group: "Vital Signs",
|
|
18386
|
-
tagline: "Where
|
|
18387
|
-
how_computed: "
|
|
18410
|
+
tagline: "Where do deals stop moving \u2014 and what's piled up behind that stage?",
|
|
18411
|
+
how_computed: "We start from how old open deals are versus a max window, then shave points for the share that look stuck. A deal is stuck when it goes quiet too long or its close date is already past. The formula lines show the base, the penalty, and the stuck rules.",
|
|
18388
18412
|
formula_lines: [
|
|
18389
18413
|
"base = 100 \xD7 (1 \u2212 avgOpenAge / max_days)",
|
|
18390
18414
|
"score = base \u2212 stuckSharePenalty (\u226420)",
|
|
18391
18415
|
"stuck = no update > stuck_days OR past-due close"
|
|
18392
18416
|
],
|
|
18393
|
-
meaning: '
|
|
18394
|
-
expert_read: "
|
|
18417
|
+
meaning: 'Flow Rate asks whether deals are still advancing or sitting still. Stuck deals pad next quarter on paper. Like traffic behind a closed lane \u2014 the jam is one stage, not the whole highway. Dollars on stuck deals show as "stuck in pipeline."',
|
|
18418
|
+
expert_read: "Find the stage where advancement collapses first \u2014 it's usually one stage, not everywhere. Compare stuck age to this company's own median cycle rather than a generic norm. Stuck deals plus past-due closes are often a forecast-credibility problem before they're a revenue miss.",
|
|
18395
18419
|
deepdive: [
|
|
18396
|
-
"Status uses average open age. Green
|
|
18397
|
-
'
|
|
18398
|
-
"
|
|
18399
|
-
"
|
|
18400
|
-
"
|
|
18420
|
+
"Status uses average open age. Green \u226445 days, yellow \u226490, else red. Defaults: max_days 120, stuck_days 60.",
|
|
18421
|
+
'Dollars: sum of amount on stuck deals \u2192 "stuck in pipeline".',
|
|
18422
|
+
"Read with Drop Rate \u2014 stuck deals and handoff leaks often show up together.",
|
|
18423
|
+
"Weak score \u2192 play Unstick the Pipeline.",
|
|
18424
|
+
"Useful moves: stage-age report, past-due close cleanup, progression plans on stuck deals, forecast hygiene on optimistic close dates."
|
|
18401
18425
|
],
|
|
18402
18426
|
visual: {
|
|
18403
18427
|
kind: "funnel",
|
|
@@ -18423,21 +18447,21 @@ var init_metric_definitions = __esm({
|
|
|
18423
18447
|
kind: "vital",
|
|
18424
18448
|
label: "Drop Rate",
|
|
18425
18449
|
group: "Vital Signs",
|
|
18426
|
-
tagline: "Where does paid demand fall between systems before
|
|
18427
|
-
how_computed: "
|
|
18450
|
+
tagline: "Where does paid demand fall between systems before a rep works it?",
|
|
18451
|
+
how_computed: "We blend two checks: do marketing people also show up in sales CRM, and are open deals still being worked (not abandoned). The formula lines show the weights and the 30-day abandon window.",
|
|
18428
18452
|
formula_lines: [
|
|
18429
18453
|
"score = crossSystemRetention\xD70.6 + oppRetention\xD70.4",
|
|
18430
18454
|
"cross-system = marketing people also in sales CRM",
|
|
18431
18455
|
"abandoned = open opps with no activity in 30d"
|
|
18432
18456
|
],
|
|
18433
|
-
meaning: '
|
|
18434
|
-
expert_read:
|
|
18457
|
+
meaning: 'Drop Rate prices the handoff leak \u2014 money you already spent on leads that never reach a working rep. Like buying groceries and leaving half the bags in the parking lot. The dollar line is "est. lost at handoff."',
|
|
18458
|
+
expert_read: "A useful first cut is by lead source \u2014 the leak usually concentrates in one or two, not everywhere. Routing rules, unassigned territories, dead queues, or a sync gap between marketing and CRM show up more often than lazy reps. The cheapest pipeline you can buy is often the leads you already paid for.",
|
|
18435
18459
|
deepdive: [
|
|
18436
|
-
"
|
|
18437
|
-
'
|
|
18438
|
-
"
|
|
18439
|
-
"
|
|
18440
|
-
"
|
|
18460
|
+
"Green 80+, yellow 60+, red below 60.",
|
|
18461
|
+
'Dollars: dropped \xD7 conversion \xD7 avg deal (fallback: drop% \xD7 open pipeline) \u2192 "est. lost at handoff".',
|
|
18462
|
+
"Read with Flow Rate \u2014 handoff leaks and stuck deals often show up together.",
|
|
18463
|
+
"Weak score \u2192 play Fix the Handoff Gap.",
|
|
18464
|
+
"Useful moves: source-level handoff audit, routing and sync repair, time-to-first-touch SLA, weekly marketing-only-leads report."
|
|
18441
18465
|
],
|
|
18442
18466
|
visual: {
|
|
18443
18467
|
kind: "funnel",
|
|
@@ -18462,21 +18486,21 @@ var init_metric_definitions = __esm({
|
|
|
18462
18486
|
kind: "vital",
|
|
18463
18487
|
label: "Signal:Noise",
|
|
18464
18488
|
group: "Vital Signs",
|
|
18465
|
-
tagline: "How much
|
|
18466
|
-
how_computed: "
|
|
18489
|
+
tagline: "How much effort still aims at accounts that can close \u2014 versus dead ends?",
|
|
18490
|
+
how_computed: "We look at the last 90 days of activity and ask what share is signal \u2014 linked to an open deal, a pipeline person, or a pipeline company. The formula lines show the ratio and what counts as signal.",
|
|
18467
18491
|
formula_lines: [
|
|
18468
18492
|
"score = (signalCount / activityCount) \xD7 100",
|
|
18469
18493
|
"signal = linked to open opp / pipeline person / pipeline org",
|
|
18470
18494
|
"lookback = trailing 90 days"
|
|
18471
18495
|
],
|
|
18472
|
-
meaning:
|
|
18496
|
+
meaning: 'Signal:Noise asks whether activity still points at deals that can close. Noise hours are capacity spent on dead ends. Like a radio stuck between stations \u2014 lots of static, little signal. Those hours show as "misdirected effort."',
|
|
18473
18497
|
expert_read: "A useful cut is by rep and by account status. Persistent noise often means reps keep working dead accounts they already know \u2014 stale targeting or coverage models more than a coaching gap. Also check whether activity is logged against closed or unlinked records; that can be a hygiene artifact pretending to be effort.",
|
|
18474
18498
|
deepdive: [
|
|
18475
|
-
"
|
|
18476
|
-
|
|
18477
|
-
"
|
|
18478
|
-
"
|
|
18479
|
-
"
|
|
18499
|
+
"Green 65+, yellow 40+, red below 40.",
|
|
18500
|
+
'Dollar defaults: 0.25 hours/activity \xD7 $75/hr (config: hours_per_activity, rep_hourly_cost) \u2192 "misdirected effort".',
|
|
18501
|
+
"Read Freshness, Flow Rate, and Drop Rate before you trust this score.",
|
|
18502
|
+
"Weak score \u2192 play Retarget Misdirected Effort.",
|
|
18503
|
+
"Useful moves: refresh account lists, signal-based targeting, stop logging against closed or unlinked records, coverage-model redesign."
|
|
18480
18504
|
],
|
|
18481
18505
|
visual: {
|
|
18482
18506
|
kind: "split",
|
|
@@ -18499,21 +18523,21 @@ var init_metric_definitions = __esm({
|
|
|
18499
18523
|
kind: "vital",
|
|
18500
18524
|
label: "Thread Depth",
|
|
18501
18525
|
group: "Vital Signs",
|
|
18502
|
-
tagline: "How fragile is
|
|
18503
|
-
how_computed: "
|
|
18526
|
+
tagline: "How fragile is a deal if one champion changes jobs?",
|
|
18527
|
+
how_computed: "We ask what share of open deals have at least two people active in the last 90 days. Contacts on the deal and activity at the same company both count. The formula lines show the threshold.",
|
|
18504
18528
|
formula_lines: [
|
|
18505
18529
|
"score = % open deals with \u22652 active people (90d)",
|
|
18506
18530
|
"people counted via opp contacts + same-org activity",
|
|
18507
18531
|
"threshold configurable (default 2)"
|
|
18508
18532
|
],
|
|
18509
|
-
meaning:
|
|
18510
|
-
expert_read: "Weight by deal size when you triage
|
|
18533
|
+
meaning: 'Thread Depth asks how many people keep each deal warm. One contact is a single point of failure \u2014 if they leave, the deal often dies with them. Like a bridge with one cable. Dollars on thin deals show as "single-threaded."',
|
|
18534
|
+
expert_read: "Weight by deal size when you triage \u2014 one thin mega-deal can outweigh ten small ones. Late-cycle single-threading tends to matter more than early. In enterprise motions, thin threads often lead to slipped quarters: the champion leaves and nobody else is warm enough to carry the deal.",
|
|
18511
18535
|
deepdive: [
|
|
18512
|
-
"
|
|
18513
|
-
'
|
|
18514
|
-
"
|
|
18515
|
-
"
|
|
18516
|
-
"
|
|
18536
|
+
"Green 65+, yellow 40+, red below 40.",
|
|
18537
|
+
'Dollars: sum of amount on single-threaded deals \u2192 "single-threaded".',
|
|
18538
|
+
"Read this last, after Freshness, Flow Rate, Drop Rate, and Signal:Noise.",
|
|
18539
|
+
"Weak score \u2192 play Multi-Thread Your Deals.",
|
|
18540
|
+
"Useful moves: buying-committee map, warm internal referral first, CRM contact roles, mid-stage single-thread alerts, champion job-change signals."
|
|
18517
18541
|
],
|
|
18518
18542
|
visual: {
|
|
18519
18543
|
kind: "bars",
|
|
@@ -25903,8 +25927,8 @@ var init_ghost_hints = __esm({
|
|
|
25903
25927
|
PHASE_GHOST_HINTS = {
|
|
25904
25928
|
orient: [
|
|
25905
25929
|
'try "pipeline health"',
|
|
25906
|
-
|
|
25907
|
-
|
|
25930
|
+
'try "how healthy is the pipeline?"',
|
|
25931
|
+
'try "what should I do next?"',
|
|
25908
25932
|
'try "is our retention real for the board?"',
|
|
25909
25933
|
'try "what is the most expensive problem to solve?"',
|
|
25910
25934
|
'try "board deck on Q3"'
|
|
@@ -25921,7 +25945,7 @@ var init_ghost_hints = __esm({
|
|
|
25921
25945
|
'try "how should we fix this?"',
|
|
25922
25946
|
'try "which segment is weakest?"',
|
|
25923
25947
|
'try "what is ARR?"',
|
|
25924
|
-
|
|
25948
|
+
'try "why is freshness red?"',
|
|
25925
25949
|
'try "ship a board deck"'
|
|
25926
25950
|
]
|
|
25927
25951
|
};
|
|
@@ -36716,7 +36740,7 @@ async function handler19(args, ctx) {
|
|
|
36716
36740
|
console.log(chalk51.dim(` Trigger: ${triggerLabel} \u2014 ${play.trigger_condition}`));
|
|
36717
36741
|
if (play.trigger_vital_sign) {
|
|
36718
36742
|
console.log(
|
|
36719
|
-
chalk51.dim("
|
|
36743
|
+
chalk51.dim(" Ask what ") + chalk51.dim(play.trigger_vital_sign.replace(/_/g, " ")) + chalk51.dim(" means \u2014 or ") + paint("accent", `/tour ${play.trigger_vital_sign}`) + chalk51.dim(" for the card")
|
|
36720
36744
|
);
|
|
36721
36745
|
}
|
|
36722
36746
|
console.log();
|
|
@@ -40446,7 +40470,8 @@ function getDeepdiveNudge(ctx) {
|
|
|
40446
40470
|
if (hasCompletedMetricsTour() || hasSeenDeepdiveHomeNudge()) return null;
|
|
40447
40471
|
if (!hasAnalysisForDeepdiveNudge(ctx)) return null;
|
|
40448
40472
|
return {
|
|
40449
|
-
text: "
|
|
40473
|
+
text: "Ask how healthy the pipeline looks",
|
|
40474
|
+
tip: "Or /tour for the numbers deck",
|
|
40450
40475
|
command: "/tour"
|
|
40451
40476
|
};
|
|
40452
40477
|
}
|
|
@@ -45677,12 +45702,69 @@ var init_explore_expand = __esm({
|
|
|
45677
45702
|
}
|
|
45678
45703
|
});
|
|
45679
45704
|
|
|
45705
|
+
// src/conversation/health-chat.ts
|
|
45706
|
+
var health_chat_exports = {};
|
|
45707
|
+
__export(health_chat_exports, {
|
|
45708
|
+
isHealthChatPhase: () => isHealthChatPhase,
|
|
45709
|
+
isHealthIntent: () => isHealthIntent,
|
|
45710
|
+
tryHealthChatReadout: () => tryHealthChatReadout
|
|
45711
|
+
});
|
|
45712
|
+
import chalk94 from "chalk";
|
|
45713
|
+
function isHealthIntent(line) {
|
|
45714
|
+
const text = line.trim();
|
|
45715
|
+
if (!text) return false;
|
|
45716
|
+
if (/^(tour|\/tour|deepdive|\/deepdive)\b/i.test(text)) return false;
|
|
45717
|
+
return HEALTH_INTENT_RE.test(text);
|
|
45718
|
+
}
|
|
45719
|
+
function isHealthChatPhase(ctx) {
|
|
45720
|
+
const phase = resolveConversationPhase(ctx);
|
|
45721
|
+
return phase === "explore" || phase === "orient" && isAnalysisReady(ctx);
|
|
45722
|
+
}
|
|
45723
|
+
function tryHealthChatReadout(ctx, line) {
|
|
45724
|
+
if (!isHealthIntent(line)) return false;
|
|
45725
|
+
if (!isHealthChatPhase(ctx)) return false;
|
|
45726
|
+
const aggregate = ctx.snapshot.computeResult?.aggregate;
|
|
45727
|
+
if (!aggregate) {
|
|
45728
|
+
console.log();
|
|
45729
|
+
console.log(
|
|
45730
|
+
" " + chalk94.dim(
|
|
45731
|
+
"No health reading in this session yet. Run analysis first, then ask again."
|
|
45732
|
+
)
|
|
45733
|
+
);
|
|
45734
|
+
console.log();
|
|
45735
|
+
recordMessage(ctx, "user", line);
|
|
45736
|
+
recordMessage(ctx, "agent", "No health reading yet");
|
|
45737
|
+
saveSessionState(ctx);
|
|
45738
|
+
return true;
|
|
45739
|
+
}
|
|
45740
|
+
printHealthLine(aggregate);
|
|
45741
|
+
printVitalSigns(aggregate.vital_signs);
|
|
45742
|
+
recordMessage(ctx, "user", line);
|
|
45743
|
+
recordMessage(
|
|
45744
|
+
ctx,
|
|
45745
|
+
"agent",
|
|
45746
|
+
`Health ${Math.round(aggregate.overall_score)}/100 \xB7 gating ${aggregate.gating_vital_sign}`
|
|
45747
|
+
);
|
|
45748
|
+
saveSessionState(ctx);
|
|
45749
|
+
return true;
|
|
45750
|
+
}
|
|
45751
|
+
var HEALTH_INTENT_RE;
|
|
45752
|
+
var init_health_chat = __esm({
|
|
45753
|
+
"src/conversation/health-chat.ts"() {
|
|
45754
|
+
"use strict";
|
|
45755
|
+
init_context2();
|
|
45756
|
+
init_terminal();
|
|
45757
|
+
init_phase();
|
|
45758
|
+
HEALTH_INTENT_RE = /\b((how|how'?s|how is)\s+(healthy|the\s+pipeline|our\s+pipeline|pipeline\s+health)|pipeline\s+health|show\s+vitals|vital\s+signs?|where\s+are\s+we\s+red|where'?s\s+the\s+red|health\s+check|how\s+healthy)\b/i;
|
|
45759
|
+
}
|
|
45760
|
+
});
|
|
45761
|
+
|
|
45680
45762
|
// src/conversation/router.ts
|
|
45681
45763
|
var router_exports = {};
|
|
45682
45764
|
__export(router_exports, {
|
|
45683
45765
|
conversationRouter: () => conversationRouter
|
|
45684
45766
|
});
|
|
45685
|
-
import
|
|
45767
|
+
import chalk95 from "chalk";
|
|
45686
45768
|
function popModalState(ctx) {
|
|
45687
45769
|
const popped = [];
|
|
45688
45770
|
if (ctx.strategistState) {
|
|
@@ -45715,7 +45797,7 @@ function handleBackNavigation(ctx, line) {
|
|
|
45715
45797
|
}
|
|
45716
45798
|
if (phase === "scope") {
|
|
45717
45799
|
console.log();
|
|
45718
|
-
console.log(" " +
|
|
45800
|
+
console.log(" " + chalk95.dim("What should we focus on instead?"));
|
|
45719
45801
|
ctx.scope = void 0;
|
|
45720
45802
|
clearPendingAsk(ctx);
|
|
45721
45803
|
saveSessionState(ctx);
|
|
@@ -45735,7 +45817,7 @@ function handleBackNavigation(ctx, line) {
|
|
|
45735
45817
|
printScopeProposal(ctx);
|
|
45736
45818
|
if (hadData || hadAnalysis) {
|
|
45737
45819
|
console.log(
|
|
45738
|
-
" " +
|
|
45820
|
+
" " + chalk95.dim(
|
|
45739
45821
|
"Focus gate rewound \u2014 loaded data and any analysis stay. `b` does not unload demo data or undo compute."
|
|
45740
45822
|
)
|
|
45741
45823
|
);
|
|
@@ -45751,7 +45833,7 @@ function handleBackNavigation(ctx, line) {
|
|
|
45751
45833
|
recordMessage(ctx, "user", line);
|
|
45752
45834
|
recordMessage(ctx, "agent", "Strategy objective adjust (back)");
|
|
45753
45835
|
console.log();
|
|
45754
|
-
console.log(" " +
|
|
45836
|
+
console.log(" " + chalk95.dim("What is the objective? State a finish line."));
|
|
45755
45837
|
console.log();
|
|
45756
45838
|
return { handled: true, summary: "Awaiting objective" };
|
|
45757
45839
|
}
|
|
@@ -45763,7 +45845,7 @@ function handleBackNavigation(ctx, line) {
|
|
|
45763
45845
|
const backTo = formatPhaseLabel(resolveConversationPhase(ctx));
|
|
45764
45846
|
console.log();
|
|
45765
45847
|
console.log(
|
|
45766
|
-
" " +
|
|
45848
|
+
" " + chalk95.dim(`Back \u2014 dropped ${popped.join(" and ")}. At `) + chalk95.cyan(backTo) + chalk95.dim(".")
|
|
45767
45849
|
);
|
|
45768
45850
|
console.log();
|
|
45769
45851
|
return { handled: true, summary: "Back" };
|
|
@@ -45777,7 +45859,7 @@ function handleBackNavigation(ctx, line) {
|
|
|
45777
45859
|
const backTo = formatPhaseLabel(resolveConversationPhase(ctx));
|
|
45778
45860
|
console.log();
|
|
45779
45861
|
console.log(
|
|
45780
|
-
" " +
|
|
45862
|
+
" " + chalk95.dim(`Back \u2014 dropped ${popped.join(" and ")}. At `) + chalk95.cyan(backTo) + chalk95.dim(".")
|
|
45781
45863
|
);
|
|
45782
45864
|
console.log();
|
|
45783
45865
|
return { handled: true, summary: "Back" };
|
|
@@ -45821,7 +45903,7 @@ async function conversationRouter(input, ctx) {
|
|
|
45821
45903
|
if (!ok) {
|
|
45822
45904
|
console.log();
|
|
45823
45905
|
console.log(
|
|
45824
|
-
" " +
|
|
45906
|
+
" " + chalk95.dim("Staying on this session. Type ") + chalk95.cyan("/home") + chalk95.dim(" for the dashboard.")
|
|
45825
45907
|
);
|
|
45826
45908
|
console.log();
|
|
45827
45909
|
return { handled: true };
|
|
@@ -45836,7 +45918,7 @@ async function conversationRouter(input, ctx) {
|
|
|
45836
45918
|
}
|
|
45837
45919
|
console.log();
|
|
45838
45920
|
console.log(
|
|
45839
|
-
" " +
|
|
45921
|
+
" " + chalk95.dim("Start a fresh analysis with ") + chalk95.cyan("/new") + chalk95.dim(" \u2014 or ") + chalk95.cyan("/home") + chalk95.dim(" for the dashboard.")
|
|
45840
45922
|
);
|
|
45841
45923
|
console.log();
|
|
45842
45924
|
return { handled: true };
|
|
@@ -45850,7 +45932,7 @@ async function conversationRouter(input, ctx) {
|
|
|
45850
45932
|
const backTo = formatPhaseLabel(resolveConversationPhase(ctx));
|
|
45851
45933
|
console.log();
|
|
45852
45934
|
console.log(
|
|
45853
|
-
" " +
|
|
45935
|
+
" " + chalk95.dim(`Cancelled \u2014 dropped ${popped.join(" and ")}. Back to `) + chalk95.cyan(backTo) + chalk95.dim(".")
|
|
45854
45936
|
);
|
|
45855
45937
|
console.log();
|
|
45856
45938
|
return { handled: true, summary: "Cancelled" };
|
|
@@ -45894,6 +45976,12 @@ async function conversationRouter(input, ctx) {
|
|
|
45894
45976
|
const summary = await handleKeepGoingLine(ctx, line) ?? void 0;
|
|
45895
45977
|
return { handled: true, summary };
|
|
45896
45978
|
}
|
|
45979
|
+
if (phase === "explore" || phase === "orient" && isAnalysisReady(ctx)) {
|
|
45980
|
+
const { tryHealthChatReadout: tryHealthChatReadout2 } = await Promise.resolve().then(() => (init_health_chat(), health_chat_exports));
|
|
45981
|
+
if (tryHealthChatReadout2(ctx, line)) {
|
|
45982
|
+
return { handled: true, summary: "Pipeline health" };
|
|
45983
|
+
}
|
|
45984
|
+
}
|
|
45897
45985
|
if (phase === "think") {
|
|
45898
45986
|
const summary = await handleThinkFlow(line, ctx) ?? void 0;
|
|
45899
45987
|
return { handled: true, summary };
|
|
@@ -45932,7 +46020,7 @@ async function conversationRouter(input, ctx) {
|
|
|
45932
46020
|
}
|
|
45933
46021
|
if (phase === "compute") {
|
|
45934
46022
|
console.log();
|
|
45935
|
-
console.log(" " +
|
|
46023
|
+
console.log(" " + chalk95.dim("Analysis running \u2014 wait for it to finish before typing another question."));
|
|
45936
46024
|
console.log();
|
|
45937
46025
|
return { handled: true };
|
|
45938
46026
|
}
|
|
@@ -45988,7 +46076,7 @@ __export(dispatch_exports, {
|
|
|
45988
46076
|
dispatch: () => dispatch,
|
|
45989
46077
|
replayPendingBlockedLine: () => replayPendingBlockedLine
|
|
45990
46078
|
});
|
|
45991
|
-
import
|
|
46079
|
+
import chalk96 from "chalk";
|
|
45992
46080
|
function printLicenseRequired(command) {
|
|
45993
46081
|
printLicenseBlocked(command);
|
|
45994
46082
|
}
|
|
@@ -46006,7 +46094,7 @@ async function replayPendingBlockedLine(ctx) {
|
|
|
46006
46094
|
if (!hasValidLicense()) return false;
|
|
46007
46095
|
ctx.pendingBlockedLine = void 0;
|
|
46008
46096
|
console.log();
|
|
46009
|
-
console.log(" " +
|
|
46097
|
+
console.log(" " + chalk96.dim("Picking up where you left off\u2026"));
|
|
46010
46098
|
console.log();
|
|
46011
46099
|
await dispatch(line, ctx);
|
|
46012
46100
|
return true;
|
|
@@ -46076,7 +46164,7 @@ async function dispatch(input, ctx) {
|
|
|
46076
46164
|
if (tokens.length === 1) {
|
|
46077
46165
|
if (/^\d$/.test(first)) {
|
|
46078
46166
|
console.log(
|
|
46079
|
-
" " +
|
|
46167
|
+
" " + chalk96.dim("Looks like a menu pick \u2014 run ") + paint("accent", "/new") + chalk96.dim(" to start (pick Demo, then choose your analysis type).")
|
|
46080
46168
|
);
|
|
46081
46169
|
return { kind: "handled" };
|
|
46082
46170
|
}
|
|
@@ -46099,19 +46187,19 @@ async function dispatch(input, ctx) {
|
|
|
46099
46187
|
return { kind: "handled", summary };
|
|
46100
46188
|
}
|
|
46101
46189
|
console.log(
|
|
46102
|
-
" " +
|
|
46190
|
+
" " + chalk96.dim("Not in Q&A yet. Confirm the scope, load data, and run analysis first. Type ") + paint("accent", "/home") + chalk96.dim(" for status.")
|
|
46103
46191
|
);
|
|
46104
46192
|
return { kind: "handled" };
|
|
46105
46193
|
}
|
|
46106
46194
|
console.log(
|
|
46107
|
-
" " +
|
|
46195
|
+
" " + chalk96.dim("Natural-language questions run inside ntrp. Start with ") + paint("accent", "ntrp") + chalk96.dim(" and type a question after analysis.")
|
|
46108
46196
|
);
|
|
46109
46197
|
return { kind: "handled" };
|
|
46110
46198
|
}
|
|
46111
46199
|
async function runSlashCommand(name, args, ctx) {
|
|
46112
46200
|
const handler54 = await resolveHandler(name);
|
|
46113
46201
|
if (!handler54) {
|
|
46114
|
-
console.error(
|
|
46202
|
+
console.error(chalk96.red(` Unknown command: /${name}`));
|
|
46115
46203
|
return void 0;
|
|
46116
46204
|
}
|
|
46117
46205
|
const result = await handler54(args, ctx);
|
|
@@ -46148,7 +46236,7 @@ var init_inline_suggestion = __esm({
|
|
|
46148
46236
|
});
|
|
46149
46237
|
|
|
46150
46238
|
// src/conversation/loop-guard.ts
|
|
46151
|
-
import
|
|
46239
|
+
import chalk97 from "chalk";
|
|
46152
46240
|
function createLoopGuardState() {
|
|
46153
46241
|
return { phase: null, stuckTurns: 0 };
|
|
46154
46242
|
}
|
|
@@ -46180,10 +46268,10 @@ function printLoopEscalation(phase) {
|
|
|
46180
46268
|
if (!guide) return;
|
|
46181
46269
|
console.log();
|
|
46182
46270
|
console.log(
|
|
46183
|
-
" " +
|
|
46271
|
+
" " + chalk97.yellow("We seem to be going in circles \u2014 you're in ") + paint("accent", guide.mode) + chalk97.yellow(" mode.")
|
|
46184
46272
|
);
|
|
46185
|
-
console.log(" " +
|
|
46186
|
-
console.log(" " +
|
|
46273
|
+
console.log(" " + chalk97.dim("Right now I can only accept: ") + guide.accepts);
|
|
46274
|
+
console.log(" " + chalk97.dim("To leave: ") + guide.leave);
|
|
46187
46275
|
console.log();
|
|
46188
46276
|
}
|
|
46189
46277
|
var LOOP_GUARD_THRESHOLD, MODAL_PHASES, PHASE_GUIDES;
|
|
@@ -46227,7 +46315,7 @@ __export(welcome_exports, {
|
|
|
46227
46315
|
resolveWelcomeNextAction: () => resolveWelcomeNextAction,
|
|
46228
46316
|
staleProcessHomeNotice: () => staleProcessHomeNotice
|
|
46229
46317
|
});
|
|
46230
|
-
import
|
|
46318
|
+
import chalk98 from "chalk";
|
|
46231
46319
|
function formatHomeEntityCounts(counts) {
|
|
46232
46320
|
const parts = [];
|
|
46233
46321
|
const people = counts.people ?? 0;
|
|
@@ -46241,11 +46329,11 @@ function formatHomeEntityCounts(counts) {
|
|
|
46241
46329
|
return parts.join(" \xB7 ");
|
|
46242
46330
|
}
|
|
46243
46331
|
function formatEmptyDataHomeHint(savedSessionCount, opts = {}) {
|
|
46244
|
-
const onboard = opts.includeOnboard === true ?
|
|
46332
|
+
const onboard = opts.includeOnboard === true ? chalk98.dim(", or ") + paint("accent", "/onboard") + chalk98.dim(" to calibrate") : "";
|
|
46245
46333
|
if (savedSessionCount > 0) {
|
|
46246
|
-
return
|
|
46334
|
+
return chalk98.dim("Drop a CSV or type ") + paint("accent", "use demo data") + chalk98.dim(" to load a sample") + onboard + chalk98.dim(". Type ") + paint("accent", "/session") + chalk98.dim(" to open a saved session");
|
|
46247
46335
|
}
|
|
46248
|
-
return
|
|
46336
|
+
return chalk98.dim("Drop a CSV or type ") + paint("accent", "use demo data") + chalk98.dim(" to load a sample pipeline") + onboard;
|
|
46249
46337
|
}
|
|
46250
46338
|
function staleProcessHomeNotice() {
|
|
46251
46339
|
const running = getInstalledVersion();
|
|
@@ -46287,19 +46375,19 @@ function sessionSummaryText(s) {
|
|
|
46287
46375
|
summary: s.summary,
|
|
46288
46376
|
dataset: s.dataset
|
|
46289
46377
|
});
|
|
46290
|
-
return summary === NO_SUMMARY ?
|
|
46378
|
+
return summary === NO_SUMMARY ? chalk98.dim(summary) : summary;
|
|
46291
46379
|
}
|
|
46292
46380
|
function formatLastSessionLine(s, colW, ctx, opts) {
|
|
46293
46381
|
const phase = sessionPhaseLabel(s, ctx);
|
|
46294
|
-
const current = opts?.markCurrent && s.id === ctx?.sessionId ?
|
|
46295
|
-
const meta = `${formatSessionId(s.id, s.name)} ${
|
|
46382
|
+
const current = opts?.markCurrent && s.id === ctx?.sessionId ? chalk98.dim(" \xB7 current") : "";
|
|
46383
|
+
const meta = `${formatSessionId(s.id, s.name)} ${chalk98.dim("\xB7")} ${chalk98.dim(lensBadgeLabel(s.analysis))} ${chalk98.dim("\xB7")} ${paint("accent", phase)} ${chalk98.dim("\xB7")} ${sessionSummaryText(s)}${current}`;
|
|
46296
46384
|
return truncateVisible(` ${meta}`, colW);
|
|
46297
46385
|
}
|
|
46298
46386
|
function formatActiveSessionLine(s, colW, ctx, opts) {
|
|
46299
46387
|
const indent = " ";
|
|
46300
46388
|
const idPart = formatSessionId(s.id, s.name);
|
|
46301
|
-
const status =
|
|
46302
|
-
const current = opts?.markCurrent && s.id === ctx?.sessionId ?
|
|
46389
|
+
const status = chalk98.dim(` \xB7 ${sessionStatusSuffix(s)}`);
|
|
46390
|
+
const current = opts?.markCurrent && s.id === ctx?.sessionId ? chalk98.dim(" \xB7 current") : "";
|
|
46303
46391
|
const suffix = `${status}${current}`;
|
|
46304
46392
|
const summaryBudget = Math.max(8, colW - visibleWidth(indent) - visibleWidth(idPart) - visibleWidth(suffix) - 2);
|
|
46305
46393
|
const summaryPart = truncateVisible(sessionSummaryText(s), summaryBudget);
|
|
@@ -46340,13 +46428,13 @@ function buildSystemLines(colW, statusRows, recent) {
|
|
|
46340
46428
|
lines.push(sectionHeading("System"));
|
|
46341
46429
|
const labelW = Math.max(...statusRows.map((r) => r.label.length), "last used".length);
|
|
46342
46430
|
for (const item of statusRows) {
|
|
46343
|
-
const label =
|
|
46431
|
+
const label = chalk98.dim(padRight(item.label, labelW));
|
|
46344
46432
|
const state2 = padRight(item.state, 10);
|
|
46345
46433
|
const detailW = Math.max(1, colW - labelW - 13);
|
|
46346
|
-
lines.push(`${label} ${state2} ${
|
|
46434
|
+
lines.push(`${label} ${state2} ${chalk98.dim(truncateVisible(item.detail, detailW))}`);
|
|
46347
46435
|
}
|
|
46348
46436
|
if (recent) {
|
|
46349
|
-
lines.push(`${
|
|
46437
|
+
lines.push(`${chalk98.dim(padRight("last used", labelW))} ${chalk98.dim(recent)}`);
|
|
46350
46438
|
}
|
|
46351
46439
|
return lines;
|
|
46352
46440
|
}
|
|
@@ -46354,10 +46442,10 @@ function buildLastSessionLines(colW, ctx, lastSession, nextAction, emptyDataHint
|
|
|
46354
46442
|
const lines = [""];
|
|
46355
46443
|
lines.push(sectionHeading("Last Session"));
|
|
46356
46444
|
if (!lastSession) {
|
|
46357
|
-
lines.push(` ${
|
|
46445
|
+
lines.push(` ${chalk98.dim("(none)")}`);
|
|
46358
46446
|
lines.push(
|
|
46359
46447
|
truncateVisible(
|
|
46360
|
-
` ${nextAction.command ? actionHint(nextAction.label, nextAction.command, nextAction.detail) : `${
|
|
46448
|
+
` ${nextAction.command ? actionHint(nextAction.label, nextAction.command, nextAction.detail) : `${chalk98.dim(nextAction.label)} ${chalk98.dim(nextAction.detail)}`}`,
|
|
46361
46449
|
colW
|
|
46362
46450
|
)
|
|
46363
46451
|
);
|
|
@@ -46371,7 +46459,7 @@ function buildLastSessionLines(colW, ctx, lastSession, nextAction, emptyDataHint
|
|
|
46371
46459
|
if (!isCurrent) {
|
|
46372
46460
|
lines.push(
|
|
46373
46461
|
truncateVisible(
|
|
46374
|
-
` ${
|
|
46462
|
+
` ${chalk98.dim("Resume:")} ${paint("accent", `/session ${lastSession.id.slice(-4)}`)}`,
|
|
46375
46463
|
colW
|
|
46376
46464
|
)
|
|
46377
46465
|
);
|
|
@@ -46380,7 +46468,7 @@ function buildLastSessionLines(colW, ctx, lastSession, nextAction, emptyDataHint
|
|
|
46380
46468
|
truncateVisible(` ${actionHint(nextAction.label, nextAction.command, nextAction.detail)}`, colW)
|
|
46381
46469
|
);
|
|
46382
46470
|
} else {
|
|
46383
|
-
lines.push(truncateVisible(` ${
|
|
46471
|
+
lines.push(truncateVisible(` ${chalk98.dim(nextAction.label)} ${chalk98.dim(nextAction.detail)}`, colW));
|
|
46384
46472
|
}
|
|
46385
46473
|
if (isCurrent && emptyDataHint) {
|
|
46386
46474
|
lines.push(truncateVisible(` ${emptyDataHint}`, colW));
|
|
@@ -46391,14 +46479,14 @@ function buildActiveSessionsLines(colW, ctx, activeSessions) {
|
|
|
46391
46479
|
const lines = [""];
|
|
46392
46480
|
lines.push(sectionHeading("Active Sessions"));
|
|
46393
46481
|
if (activeSessions.length === 0) {
|
|
46394
|
-
lines.push(` ${
|
|
46482
|
+
lines.push(` ${chalk98.dim("(none in progress)")}`);
|
|
46395
46483
|
return lines;
|
|
46396
46484
|
}
|
|
46397
46485
|
for (const s of activeSessions.slice(0, 5)) {
|
|
46398
46486
|
lines.push(formatActiveSessionLine(s, colW, ctx, { markCurrent: true }));
|
|
46399
46487
|
}
|
|
46400
46488
|
if (activeSessions.length > 5) {
|
|
46401
|
-
lines.push(` ${
|
|
46489
|
+
lines.push(` ${chalk98.dim(`+${activeSessions.length - 5} more \xB7 `)}${paint("accent", "/session")}`);
|
|
46402
46490
|
}
|
|
46403
46491
|
return lines;
|
|
46404
46492
|
}
|
|
@@ -46486,7 +46574,7 @@ async function printWelcome(ctx, version) {
|
|
|
46486
46574
|
const gap = Math.max(0, innerW - versionTag.length);
|
|
46487
46575
|
const gapL = Math.floor(gap / 2);
|
|
46488
46576
|
push(
|
|
46489
|
-
border(`\u256D${"\u2500".repeat(gapL)}`) +
|
|
46577
|
+
border(`\u256D${"\u2500".repeat(gapL)}`) + chalk98.dim(versionTag) + border(`${"\u2500".repeat(gap - gapL)}\u256E`)
|
|
46490
46578
|
);
|
|
46491
46579
|
if (useWideLayout) {
|
|
46492
46580
|
const leftLines = systemLines;
|
|
@@ -46513,7 +46601,7 @@ async function printWelcome(ctx, version) {
|
|
|
46513
46601
|
push(border(`\u2570${"\u2500".repeat(innerW)}\u256F`));
|
|
46514
46602
|
push(
|
|
46515
46603
|
truncateVisible(
|
|
46516
|
-
license.valid ? ` ${paint("accent", "/help")}${
|
|
46604
|
+
license.valid ? ` ${paint("accent", "/help")}${chalk98.dim(" commands \xB7 ")}${paint("accent", "/tour")}${chalk98.dim(" tour \xB7 ")}${paint("accent", "/progress")}${chalk98.dim(" hours \xB7 ")}${paint("accent", "/session")}${chalk98.dim(" resume")}` : ` ${paint("accent", "/help")}${chalk98.dim(" commands \xB7 ")}${paint("accent", "/activate")}${chalk98.dim(" paste a key \xB7 ")}${paint("accent", "/checkout")}${chalk98.dim(" signup \xB7 ")}${paint("accent", "/progress")}${chalk98.dim(" hours")}`,
|
|
46517
46605
|
cardW
|
|
46518
46606
|
)
|
|
46519
46607
|
);
|
|
@@ -46524,14 +46612,15 @@ async function printWelcome(ctx, version) {
|
|
|
46524
46612
|
if (strategyNudge) {
|
|
46525
46613
|
push(
|
|
46526
46614
|
truncateVisible(
|
|
46527
|
-
` ${paint("warning", "\u2691")} ${
|
|
46615
|
+
` ${paint("warning", "\u2691")} ${chalk98.dim(strategyNudge.text)} ${chalk98.dim("\xB7")} ${paint("accent", strategyNudge.command)}`,
|
|
46528
46616
|
cardW
|
|
46529
46617
|
)
|
|
46530
46618
|
);
|
|
46531
46619
|
} else if (deepdiveNudge) {
|
|
46620
|
+
const tip = deepdiveNudge.tip ?? deepdiveNudge.command;
|
|
46532
46621
|
push(
|
|
46533
46622
|
truncateVisible(
|
|
46534
|
-
` ${paint("warning", "\u2691")} ${
|
|
46623
|
+
tip ? ` ${paint("warning", "\u2691")} ${chalk98.dim(deepdiveNudge.text)} ${chalk98.dim("\xB7")} ${chalk98.dim(tip)}` : ` ${paint("warning", "\u2691")} ${chalk98.dim(deepdiveNudge.text)}`,
|
|
46535
46624
|
cardW
|
|
46536
46625
|
)
|
|
46537
46626
|
);
|
|
@@ -46539,7 +46628,7 @@ async function printWelcome(ctx, version) {
|
|
|
46539
46628
|
} else if (voiceNudge) {
|
|
46540
46629
|
push(
|
|
46541
46630
|
truncateVisible(
|
|
46542
|
-
` ${paint("warning", "\u2691")} ${
|
|
46631
|
+
` ${paint("warning", "\u2691")} ${chalk98.dim(voiceNudge.text)} ${chalk98.dim("\xB7")} ${paint("accent", voiceNudge.command)}`,
|
|
46543
46632
|
cardW
|
|
46544
46633
|
)
|
|
46545
46634
|
);
|
|
@@ -46727,7 +46816,7 @@ __export(repl_exports, {
|
|
|
46727
46816
|
});
|
|
46728
46817
|
import { createInterface as createInterface2 } from "readline/promises";
|
|
46729
46818
|
import { clearLine as clearLine2, cursorTo as cursorTo2 } from "readline";
|
|
46730
|
-
import
|
|
46819
|
+
import chalk99 from "chalk";
|
|
46731
46820
|
import { join as join42 } from "path";
|
|
46732
46821
|
function buildPrompt(ctx) {
|
|
46733
46822
|
return buildConversationPrompt(ctx);
|
|
@@ -46819,12 +46908,12 @@ function renderInlineSuggestion(rl, prompt, ctx) {
|
|
|
46819
46908
|
suggestionPainted = suffix !== null;
|
|
46820
46909
|
clearLine2(process.stdout, 0);
|
|
46821
46910
|
cursorTo2(process.stdout, 0);
|
|
46822
|
-
process.stdout.write(prompt + line + (suffix ?
|
|
46911
|
+
process.stdout.write(prompt + line + (suffix ? chalk99.dim(suffix) : ""));
|
|
46823
46912
|
cursorTo2(process.stdout, promptWidth + cursor);
|
|
46824
46913
|
}
|
|
46825
46914
|
function appendTurnLine(current, promptLabel, currentSummary) {
|
|
46826
|
-
const currentLine = currentSummary ? `${promptLabel} ${current} ${
|
|
46827
|
-
console.log(" " +
|
|
46915
|
+
const currentLine = currentSummary ? `${promptLabel} ${current} ${chalk99.white("\u2192")} ${currentSummary}` : `${promptLabel} ${current}`;
|
|
46916
|
+
console.log(" " + chalk99.dim(currentLine));
|
|
46828
46917
|
console.log();
|
|
46829
46918
|
}
|
|
46830
46919
|
async function goHome(ctx, version, history, opts) {
|
|
@@ -46834,7 +46923,7 @@ async function goHome(ctx, version, history, opts) {
|
|
|
46834
46923
|
clearTerminalHome();
|
|
46835
46924
|
if (opts?.banner) {
|
|
46836
46925
|
console.log();
|
|
46837
|
-
console.log(" " + paint("accent", "\u2713") + " " +
|
|
46926
|
+
console.log(" " + paint("accent", "\u2713") + " " + chalk99.dim(opts.banner));
|
|
46838
46927
|
}
|
|
46839
46928
|
await printWelcome(ctx, version);
|
|
46840
46929
|
}
|
|
@@ -46857,11 +46946,11 @@ async function handleDispatchResult(result, ctx, version, history) {
|
|
|
46857
46946
|
case "unknown":
|
|
46858
46947
|
if (result.suggestion) {
|
|
46859
46948
|
console.log(
|
|
46860
|
-
" " +
|
|
46949
|
+
" " + chalk99.red(`Unknown command: ${result.token}.`) + chalk99.dim(" Did you mean ") + paint("accent", result.suggestion) + chalk99.dim("?")
|
|
46861
46950
|
);
|
|
46862
46951
|
} else {
|
|
46863
46952
|
console.log(
|
|
46864
|
-
" " +
|
|
46953
|
+
" " + chalk99.red(`Unknown command: ${result.token}`) + chalk99.dim(" Type ") + paint("accent", "/help") + chalk99.dim(" to see available commands.")
|
|
46865
46954
|
);
|
|
46866
46955
|
}
|
|
46867
46956
|
break;
|
|
@@ -46885,7 +46974,7 @@ async function runRepl(ctx, version) {
|
|
|
46885
46974
|
});
|
|
46886
46975
|
ctx.rl = rl;
|
|
46887
46976
|
console.log();
|
|
46888
|
-
console.log(" " +
|
|
46977
|
+
console.log(" " + chalk99.dim("What do you want to look at?"));
|
|
46889
46978
|
console.log();
|
|
46890
46979
|
if (ctx.pendingUpdateCheck) {
|
|
46891
46980
|
void ctx.pendingUpdateCheck.then((result) => {
|
|
@@ -46927,7 +47016,7 @@ async function runRepl(ctx, version) {
|
|
|
46927
47016
|
return;
|
|
46928
47017
|
}
|
|
46929
47018
|
sigintPrimed = true;
|
|
46930
|
-
console.log("\n " +
|
|
47019
|
+
console.log("\n " + chalk99.dim("Type /exit to quit, or press Ctrl+C again."));
|
|
46931
47020
|
};
|
|
46932
47021
|
rl.on("SIGINT", sigintHandler);
|
|
46933
47022
|
function shutdownRepl() {
|
|
@@ -46964,7 +47053,7 @@ async function runRepl(ctx, version) {
|
|
|
46964
47053
|
if (!typed && !enterAction) {
|
|
46965
47054
|
clearGhostRowAfterSubmit();
|
|
46966
47055
|
const coach = consumeOrientEmptyEnterCoach(ctx);
|
|
46967
|
-
if (coach) console.log(" " +
|
|
47056
|
+
if (coach) console.log(" " + chalk99.dim(coach));
|
|
46968
47057
|
continue;
|
|
46969
47058
|
}
|
|
46970
47059
|
const line = typed || enterAction.submit;
|
|
@@ -47015,11 +47104,11 @@ async function runRepl(ctx, version) {
|
|
|
47015
47104
|
ctx.wizardDepth = 0;
|
|
47016
47105
|
ctx.secretInputActive = false;
|
|
47017
47106
|
if (err instanceof Error && err.message === "Cancelled") {
|
|
47018
|
-
console.log(" " +
|
|
47107
|
+
console.log(" " + chalk99.dim("Cancelled."));
|
|
47019
47108
|
} else {
|
|
47020
47109
|
const { tryRecoverLlmAuthFailure: tryRecoverLlmAuthFailure2 } = await Promise.resolve().then(() => (init_auth_recovery(), auth_recovery_exports));
|
|
47021
47110
|
if (!await tryRecoverLlmAuthFailure2(ctx, err)) {
|
|
47022
|
-
console.error(" " +
|
|
47111
|
+
console.error(" " + chalk99.red("Error: " + String(err.message ?? err)));
|
|
47023
47112
|
}
|
|
47024
47113
|
}
|
|
47025
47114
|
}
|
|
@@ -47046,10 +47135,10 @@ async function runRepl(ctx, version) {
|
|
|
47046
47135
|
await closeSession(ctx);
|
|
47047
47136
|
}
|
|
47048
47137
|
if (isTranscriptActive(ctx.sessionId)) {
|
|
47049
|
-
console.log(" " +
|
|
47050
|
-
console.log(" " +
|
|
47138
|
+
console.log(" " + chalk99.dim("Transcript: ") + chalk99.dim(transcriptPathForSession(ctx.sessionId)));
|
|
47139
|
+
console.log(" " + chalk99.dim("Context brief: ") + chalk99.dim(contextDocPathForSession(ctx.sessionId)));
|
|
47051
47140
|
}
|
|
47052
|
-
console.log(" " +
|
|
47141
|
+
console.log(" " + chalk99.dim(randomGoodbye()));
|
|
47053
47142
|
}
|
|
47054
47143
|
function printHelpOneShot() {
|
|
47055
47144
|
printHelp();
|
|
@@ -47057,43 +47146,43 @@ function printHelpOneShot() {
|
|
|
47057
47146
|
function printHelp() {
|
|
47058
47147
|
console.log();
|
|
47059
47148
|
console.log(" " + sectionHeading("Conversation"));
|
|
47060
|
-
console.log(" " +
|
|
47061
|
-
console.log(" " +
|
|
47062
|
-
console.log(" " +
|
|
47063
|
-
console.log(" " +
|
|
47064
|
-
console.log(" " +
|
|
47065
|
-
console.log(" " +
|
|
47149
|
+
console.log(" " + chalk99.dim("Type the question. You do not need a slash command."));
|
|
47150
|
+
console.log(" " + chalk99.dim("Paste a CSV path or type ") + paint("accent", '"use demo data"') + chalk99.dim(" to load data."));
|
|
47151
|
+
console.log(" " + chalk99.dim("After analysis, type questions in English."));
|
|
47152
|
+
console.log(" " + chalk99.dim("Type ") + paint("accent", '"how should we fix this?"') + chalk99.dim(" to make a strategy. Type ") + paint("accent", "keep going") + chalk99.dim(" at confirm to keep working until the plan is ready."));
|
|
47153
|
+
console.log(" " + chalk99.dim("Type ") + paint("accent", '"ship a board deck"') + chalk99.dim(" to write a handoff."));
|
|
47154
|
+
console.log(" " + chalk99.dim("The ") + paint("accent", "\u203A") + chalk99.dim(" prompt shows brief or deep after analysis. Brief is the default."));
|
|
47066
47155
|
console.log();
|
|
47067
47156
|
console.log(" " + sectionHeading("Keys"));
|
|
47068
47157
|
console.log(
|
|
47069
|
-
" " + paint("accent", "\u23CE") +
|
|
47158
|
+
" " + paint("accent", "\u23CE") + chalk99.dim(
|
|
47070
47159
|
" Accepts the default. At the main prompt it runs the armed action (yes, use demo data, go ahead, /connect, /update, keep going)."
|
|
47071
47160
|
)
|
|
47072
47161
|
);
|
|
47073
47162
|
console.log(
|
|
47074
|
-
" " +
|
|
47163
|
+
" " + chalk99.dim(
|
|
47075
47164
|
" On yes/no questions and menus it accepts yes (or the recommended option). Type something else to decline or pick another path."
|
|
47076
47165
|
)
|
|
47077
47166
|
);
|
|
47078
47167
|
console.log(
|
|
47079
|
-
" " + paint("accent", "b") +
|
|
47168
|
+
" " + paint("accent", "b") + chalk99.dim(" / ") + paint("accent", "back") + chalk99.dim(" Steps back one confirm gate, or leaves a modal. Does not unload data or undo compute.")
|
|
47080
47169
|
);
|
|
47081
47170
|
console.log(
|
|
47082
|
-
" " +
|
|
47171
|
+
" " + chalk99.dim("On ") + paint("accent", "/settings") + chalk99.dim(" menus, ") + paint("accent", "b") + chalk99.dim(" leaves the hub (same as Done).")
|
|
47083
47172
|
);
|
|
47084
47173
|
console.log(
|
|
47085
|
-
" " + paint("accent", "more") +
|
|
47174
|
+
" " + paint("accent", "more") + chalk99.dim(" Shows the full strategy brief after the short summary.")
|
|
47086
47175
|
);
|
|
47087
47176
|
console.log(
|
|
47088
|
-
" " + paint("accent", "expand") +
|
|
47177
|
+
" " + paint("accent", "expand") + chalk99.dim(
|
|
47089
47178
|
" After a short Q&A answer, shows more reasoning for that same answer. With no last answer, expands the strategy brief when one is on screen."
|
|
47090
47179
|
)
|
|
47091
47180
|
);
|
|
47092
47181
|
console.log(
|
|
47093
|
-
" " + paint("accent", "cancel") +
|
|
47182
|
+
" " + paint("accent", "cancel") + chalk99.dim(" Drops the current modal overlay (strategy, think, ship, unconfirmed scope).")
|
|
47094
47183
|
);
|
|
47095
47184
|
console.log(
|
|
47096
|
-
" " +
|
|
47185
|
+
" " + chalk99.dim("On ") + paint("accent", "/tour") + chalk99.dim(" slides, ") + paint("accent", "b") + chalk99.dim(" moves to the previous slide.")
|
|
47097
47186
|
);
|
|
47098
47187
|
console.log();
|
|
47099
47188
|
console.log(" " + sectionHeading("Shortcuts"));
|
|
@@ -47121,11 +47210,11 @@ function printHelp() {
|
|
|
47121
47210
|
];
|
|
47122
47211
|
const maxW = Math.max(...shortcuts.map(([c]) => c.length)) + 2;
|
|
47123
47212
|
for (const [cmd, desc] of shortcuts) {
|
|
47124
|
-
console.log(` ${paint("accent", padRight(cmd, maxW))} ${
|
|
47213
|
+
console.log(` ${paint("accent", padRight(cmd, maxW))} ${chalk99.dim(desc)}`);
|
|
47125
47214
|
}
|
|
47126
47215
|
console.log();
|
|
47127
47216
|
console.log(" " + sectionHeading("Teach NTRP"));
|
|
47128
|
-
console.log(" " +
|
|
47217
|
+
console.log(" " + chalk99.dim("NTRP learns your business over time. There are three ways to teach it:"));
|
|
47129
47218
|
const teach = [
|
|
47130
47219
|
["/remember <fact>", "Store a fact, a decision, or a preference"],
|
|
47131
47220
|
["/recall [topic]", "Show what NTRP stores about your business"],
|
|
@@ -47134,13 +47223,13 @@ function printHelp() {
|
|
|
47134
47223
|
];
|
|
47135
47224
|
const teachMaxW = Math.max(...teach.map(([c]) => c.length)) + 2;
|
|
47136
47225
|
for (const [cmd, desc] of teach) {
|
|
47137
|
-
console.log(` ${paint("accent", padRight(cmd, teachMaxW))} ${
|
|
47226
|
+
console.log(` ${paint("accent", padRight(cmd, teachMaxW))} ${chalk99.dim(desc)}`);
|
|
47138
47227
|
}
|
|
47139
|
-
console.log(" " +
|
|
47228
|
+
console.log(" " + chalk99.dim("When a key is connected, NTRP stores a small number of facts when you close a session."));
|
|
47140
47229
|
console.log();
|
|
47141
|
-
console.log(" " +
|
|
47230
|
+
console.log(" " + chalk99.dim("Factory reset: type ") + paint("accent", "/scratch") + chalk99.dim("."));
|
|
47142
47231
|
console.log();
|
|
47143
|
-
console.log(" " +
|
|
47232
|
+
console.log(" " + chalk99.dim("These commands also work: ") + paint("accent", "/new") + chalk99.dim(", ") + paint("accent", "/diagnose") + chalk99.dim(", ") + paint("accent", "/metrics") + chalk99.dim(", ") + paint("accent", "/session") + chalk99.dim("."));
|
|
47144
47233
|
console.log();
|
|
47145
47234
|
}
|
|
47146
47235
|
var REPL_BUILTINS, ANSI_PATTERN, GOODBYES, GHOST_HINTS, ghostHintTurn, activeGhostHint, suggestionPainted;
|
|
@@ -47240,7 +47329,7 @@ init_errors();
|
|
|
47240
47329
|
init_diagnostics();
|
|
47241
47330
|
init_types();
|
|
47242
47331
|
init_version();
|
|
47243
|
-
import
|
|
47332
|
+
import chalk100 from "chalk";
|
|
47244
47333
|
var VERSION = getInstalledVersion();
|
|
47245
47334
|
function exitIfVersionFlag(argv) {
|
|
47246
47335
|
const rest = argv.slice(2).filter((a) => a === "--version" || a === "-v");
|
|
@@ -47278,7 +47367,7 @@ async function main() {
|
|
|
47278
47367
|
debug: args.globals.debug
|
|
47279
47368
|
});
|
|
47280
47369
|
if (!ctx.execution.color) {
|
|
47281
|
-
|
|
47370
|
+
chalk100.level = 0;
|
|
47282
47371
|
}
|
|
47283
47372
|
fatalReporting = {
|
|
47284
47373
|
command: firstToken(args.input) || "ntrp",
|
|
@@ -47295,16 +47384,16 @@ async function main() {
|
|
|
47295
47384
|
if (isStructuredOutput(ctx.execution)) {
|
|
47296
47385
|
emitError(cmd || "ntrp", new NtrpError("license_invalid", lic.message, 3 /* Auth */));
|
|
47297
47386
|
}
|
|
47298
|
-
console.error(
|
|
47387
|
+
console.error(chalk100.red(`
|
|
47299
47388
|
${lic.message}`));
|
|
47300
|
-
console.error(
|
|
47389
|
+
console.error(chalk100.dim(" Trial's over \u2014 /upgrade and paste your key.\n"));
|
|
47301
47390
|
process.exit(1);
|
|
47302
47391
|
}
|
|
47303
47392
|
}
|
|
47304
47393
|
const PROFILE_HINT_SKIP = /* @__PURE__ */ new Set(["onboard", "setup", "config", "connect", "activate", "help", "home", "exit", "quit", "clear", "profile", "progress", "deepdive", "tour", "settings", "voice", "logs"]);
|
|
47305
47394
|
if (!isProfileConfigured() && !PROFILE_HINT_SKIP.has(cmd) && !ctx.execution.quiet) {
|
|
47306
47395
|
console.error(
|
|
47307
|
-
" " +
|
|
47396
|
+
" " + chalk100.dim("Tip: run ") + paint("accent", "ntrp") + chalk100.dim(" interactively to set up your company profile for richer answers.")
|
|
47308
47397
|
);
|
|
47309
47398
|
}
|
|
47310
47399
|
const result = await dispatch(args.input, ctx);
|
|
@@ -47316,12 +47405,12 @@ async function main() {
|
|
|
47316
47405
|
}
|
|
47317
47406
|
if (result.suggestion) {
|
|
47318
47407
|
console.error(
|
|
47319
|
-
|
|
47408
|
+
chalk100.red(` Unknown command: ${result.token}.`) + chalk100.dim(" Did you mean ") + paint("accent", result.suggestion) + chalk100.dim("?")
|
|
47320
47409
|
);
|
|
47321
47410
|
} else {
|
|
47322
|
-
console.error(
|
|
47411
|
+
console.error(chalk100.red(` Unknown command: ${result.token}`));
|
|
47323
47412
|
}
|
|
47324
|
-
console.error(
|
|
47413
|
+
console.error(chalk100.dim(" Run 'ntrp' for the interactive prompt."));
|
|
47325
47414
|
process.exit(1);
|
|
47326
47415
|
break;
|
|
47327
47416
|
case "help":
|
|
@@ -47374,7 +47463,7 @@ async function main() {
|
|
|
47374
47463
|
if (justUpdated) {
|
|
47375
47464
|
clearTerminalHome();
|
|
47376
47465
|
console.log();
|
|
47377
|
-
console.log(" " + paint("accent", "\u2713") + " " +
|
|
47466
|
+
console.log(" " + paint("accent", "\u2713") + " " + chalk100.dim(`Now running v${justUpdated.to}`));
|
|
47378
47467
|
}
|
|
47379
47468
|
await printWelcome2(ctx, VERSION);
|
|
47380
47469
|
await runRepl(ctx, VERSION);
|
|
@@ -47388,9 +47477,9 @@ async function die(err, scope) {
|
|
|
47388
47477
|
if (fatalReporting.structured) {
|
|
47389
47478
|
emitError(fatalReporting.command, err);
|
|
47390
47479
|
}
|
|
47391
|
-
console.error(
|
|
47480
|
+
console.error(chalk100.red(` ${describeError(err)}`));
|
|
47392
47481
|
if (!(err instanceof NtrpError) && err instanceof Error && err.stack) {
|
|
47393
|
-
console.error(
|
|
47482
|
+
console.error(chalk100.dim(err.stack.split("\n").slice(1).join("\n")));
|
|
47394
47483
|
}
|
|
47395
47484
|
process.exit(err instanceof NtrpError ? err.exitCode : 1 /* RuntimeError */);
|
|
47396
47485
|
}
|
package/dist/mcp/server.js
CHANGED
|
@@ -1088,7 +1088,7 @@ import chalk3 from "chalk";
|
|
|
1088
1088
|
function printDeepdiveHint(metricId, label) {
|
|
1089
1089
|
const name = label ?? metricId;
|
|
1090
1090
|
console.log(
|
|
1091
|
-
" " + chalk3.dim(
|
|
1091
|
+
" " + chalk3.dim(`Ask what ${name.toLowerCase()} means \u2014 or `) + paint("accent", `/tour ${metricId}`) + chalk3.dim(" for the card")
|
|
1092
1092
|
);
|
|
1093
1093
|
console.log();
|
|
1094
1094
|
}
|
|
@@ -12989,21 +12989,21 @@ var init_metric_definitions = __esm({
|
|
|
12989
12989
|
kind: "vital",
|
|
12990
12990
|
label: "Freshness",
|
|
12991
12991
|
group: "Vital Signs",
|
|
12992
|
-
tagline: "Is the CRM
|
|
12993
|
-
how_computed: "
|
|
12992
|
+
tagline: "Is the CRM talking about live deals \u2014 or a museum of old ones?",
|
|
12993
|
+
how_computed: "We look at how recently people, companies, and deals got a touch. Deals need a fresher pulse than people or companies, and past-due deals count as stale. The formula lines on this card show the exact mix and windows.",
|
|
12994
12994
|
formula_lines: [
|
|
12995
12995
|
"freshness = people%\xD70.35 + orgs%\xD70.30 + opps%\xD70.35",
|
|
12996
12996
|
"people/orgs fresh if activity within 90d",
|
|
12997
12997
|
"opps fresh if activity within 30d AND not past-due"
|
|
12998
12998
|
],
|
|
12999
|
-
meaning:
|
|
13000
|
-
expert_read:
|
|
12999
|
+
meaning: `Freshness asks whether the pipeline number is still true. Quiet deals pad the forecast. Like milk dates on a shelf \u2014 past the date, don't trust the carton. Stale open dollars show as "pipeline at risk."`,
|
|
13000
|
+
expert_read: "Cut by owner and stage first \u2014 reds usually clump, they don't sprinkle. A sudden cliff is often a broken sync or a departed rep, not slow decay. New bulk imports can look stale when they're just new.",
|
|
13001
13001
|
deepdive: [
|
|
13002
|
-
"
|
|
13003
|
-
'
|
|
13004
|
-
"
|
|
13005
|
-
"
|
|
13006
|
-
"
|
|
13002
|
+
"Green 80+, yellow 60+, red below 60. Motion presets can change the windows.",
|
|
13003
|
+
'Dollars: sum of amount on stale open deals \u2192 "pipeline at risk".',
|
|
13004
|
+
"Read this first. If Freshness is red, later vitals are harder to trust.",
|
|
13005
|
+
"Weak score \u2192 play Clean Dead Pipeline.",
|
|
13006
|
+
"Useful moves: stale-deal alert, weekly hygiene scrub, enrichment on quiet records."
|
|
13007
13007
|
],
|
|
13008
13008
|
visual: {
|
|
13009
13009
|
kind: "bars",
|
|
@@ -13027,21 +13027,21 @@ var init_metric_definitions = __esm({
|
|
|
13027
13027
|
kind: "vital",
|
|
13028
13028
|
label: "Flow Rate",
|
|
13029
13029
|
group: "Vital Signs",
|
|
13030
|
-
tagline: "Where
|
|
13031
|
-
how_computed: "
|
|
13030
|
+
tagline: "Where do deals stop moving \u2014 and what's piled up behind that stage?",
|
|
13031
|
+
how_computed: "We start from how old open deals are versus a max window, then shave points for the share that look stuck. A deal is stuck when it goes quiet too long or its close date is already past. The formula lines show the base, the penalty, and the stuck rules.",
|
|
13032
13032
|
formula_lines: [
|
|
13033
13033
|
"base = 100 \xD7 (1 \u2212 avgOpenAge / max_days)",
|
|
13034
13034
|
"score = base \u2212 stuckSharePenalty (\u226420)",
|
|
13035
13035
|
"stuck = no update > stuck_days OR past-due close"
|
|
13036
13036
|
],
|
|
13037
|
-
meaning: '
|
|
13038
|
-
expert_read: "
|
|
13037
|
+
meaning: 'Flow Rate asks whether deals are still advancing or sitting still. Stuck deals pad next quarter on paper. Like traffic behind a closed lane \u2014 the jam is one stage, not the whole highway. Dollars on stuck deals show as "stuck in pipeline."',
|
|
13038
|
+
expert_read: "Find the stage where advancement collapses first \u2014 it's usually one stage, not everywhere. Compare stuck age to this company's own median cycle rather than a generic norm. Stuck deals plus past-due closes are often a forecast-credibility problem before they're a revenue miss.",
|
|
13039
13039
|
deepdive: [
|
|
13040
|
-
"Status uses average open age. Green
|
|
13041
|
-
'
|
|
13042
|
-
"
|
|
13043
|
-
"
|
|
13044
|
-
"
|
|
13040
|
+
"Status uses average open age. Green \u226445 days, yellow \u226490, else red. Defaults: max_days 120, stuck_days 60.",
|
|
13041
|
+
'Dollars: sum of amount on stuck deals \u2192 "stuck in pipeline".',
|
|
13042
|
+
"Read with Drop Rate \u2014 stuck deals and handoff leaks often show up together.",
|
|
13043
|
+
"Weak score \u2192 play Unstick the Pipeline.",
|
|
13044
|
+
"Useful moves: stage-age report, past-due close cleanup, progression plans on stuck deals, forecast hygiene on optimistic close dates."
|
|
13045
13045
|
],
|
|
13046
13046
|
visual: {
|
|
13047
13047
|
kind: "funnel",
|
|
@@ -13067,21 +13067,21 @@ var init_metric_definitions = __esm({
|
|
|
13067
13067
|
kind: "vital",
|
|
13068
13068
|
label: "Drop Rate",
|
|
13069
13069
|
group: "Vital Signs",
|
|
13070
|
-
tagline: "Where does paid demand fall between systems before
|
|
13071
|
-
how_computed: "
|
|
13070
|
+
tagline: "Where does paid demand fall between systems before a rep works it?",
|
|
13071
|
+
how_computed: "We blend two checks: do marketing people also show up in sales CRM, and are open deals still being worked (not abandoned). The formula lines show the weights and the 30-day abandon window.",
|
|
13072
13072
|
formula_lines: [
|
|
13073
13073
|
"score = crossSystemRetention\xD70.6 + oppRetention\xD70.4",
|
|
13074
13074
|
"cross-system = marketing people also in sales CRM",
|
|
13075
13075
|
"abandoned = open opps with no activity in 30d"
|
|
13076
13076
|
],
|
|
13077
|
-
meaning: '
|
|
13078
|
-
expert_read:
|
|
13077
|
+
meaning: 'Drop Rate prices the handoff leak \u2014 money you already spent on leads that never reach a working rep. Like buying groceries and leaving half the bags in the parking lot. The dollar line is "est. lost at handoff."',
|
|
13078
|
+
expert_read: "A useful first cut is by lead source \u2014 the leak usually concentrates in one or two, not everywhere. Routing rules, unassigned territories, dead queues, or a sync gap between marketing and CRM show up more often than lazy reps. The cheapest pipeline you can buy is often the leads you already paid for.",
|
|
13079
13079
|
deepdive: [
|
|
13080
|
-
"
|
|
13081
|
-
'
|
|
13082
|
-
"
|
|
13083
|
-
"
|
|
13084
|
-
"
|
|
13080
|
+
"Green 80+, yellow 60+, red below 60.",
|
|
13081
|
+
'Dollars: dropped \xD7 conversion \xD7 avg deal (fallback: drop% \xD7 open pipeline) \u2192 "est. lost at handoff".',
|
|
13082
|
+
"Read with Flow Rate \u2014 handoff leaks and stuck deals often show up together.",
|
|
13083
|
+
"Weak score \u2192 play Fix the Handoff Gap.",
|
|
13084
|
+
"Useful moves: source-level handoff audit, routing and sync repair, time-to-first-touch SLA, weekly marketing-only-leads report."
|
|
13085
13085
|
],
|
|
13086
13086
|
visual: {
|
|
13087
13087
|
kind: "funnel",
|
|
@@ -13106,21 +13106,21 @@ var init_metric_definitions = __esm({
|
|
|
13106
13106
|
kind: "vital",
|
|
13107
13107
|
label: "Signal:Noise",
|
|
13108
13108
|
group: "Vital Signs",
|
|
13109
|
-
tagline: "How much
|
|
13110
|
-
how_computed: "
|
|
13109
|
+
tagline: "How much effort still aims at accounts that can close \u2014 versus dead ends?",
|
|
13110
|
+
how_computed: "We look at the last 90 days of activity and ask what share is signal \u2014 linked to an open deal, a pipeline person, or a pipeline company. The formula lines show the ratio and what counts as signal.",
|
|
13111
13111
|
formula_lines: [
|
|
13112
13112
|
"score = (signalCount / activityCount) \xD7 100",
|
|
13113
13113
|
"signal = linked to open opp / pipeline person / pipeline org",
|
|
13114
13114
|
"lookback = trailing 90 days"
|
|
13115
13115
|
],
|
|
13116
|
-
meaning:
|
|
13116
|
+
meaning: 'Signal:Noise asks whether activity still points at deals that can close. Noise hours are capacity spent on dead ends. Like a radio stuck between stations \u2014 lots of static, little signal. Those hours show as "misdirected effort."',
|
|
13117
13117
|
expert_read: "A useful cut is by rep and by account status. Persistent noise often means reps keep working dead accounts they already know \u2014 stale targeting or coverage models more than a coaching gap. Also check whether activity is logged against closed or unlinked records; that can be a hygiene artifact pretending to be effort.",
|
|
13118
13118
|
deepdive: [
|
|
13119
|
-
"
|
|
13120
|
-
|
|
13121
|
-
"
|
|
13122
|
-
"
|
|
13123
|
-
"
|
|
13119
|
+
"Green 65+, yellow 40+, red below 40.",
|
|
13120
|
+
'Dollar defaults: 0.25 hours/activity \xD7 $75/hr (config: hours_per_activity, rep_hourly_cost) \u2192 "misdirected effort".',
|
|
13121
|
+
"Read Freshness, Flow Rate, and Drop Rate before you trust this score.",
|
|
13122
|
+
"Weak score \u2192 play Retarget Misdirected Effort.",
|
|
13123
|
+
"Useful moves: refresh account lists, signal-based targeting, stop logging against closed or unlinked records, coverage-model redesign."
|
|
13124
13124
|
],
|
|
13125
13125
|
visual: {
|
|
13126
13126
|
kind: "split",
|
|
@@ -13143,21 +13143,21 @@ var init_metric_definitions = __esm({
|
|
|
13143
13143
|
kind: "vital",
|
|
13144
13144
|
label: "Thread Depth",
|
|
13145
13145
|
group: "Vital Signs",
|
|
13146
|
-
tagline: "How fragile is
|
|
13147
|
-
how_computed: "
|
|
13146
|
+
tagline: "How fragile is a deal if one champion changes jobs?",
|
|
13147
|
+
how_computed: "We ask what share of open deals have at least two people active in the last 90 days. Contacts on the deal and activity at the same company both count. The formula lines show the threshold.",
|
|
13148
13148
|
formula_lines: [
|
|
13149
13149
|
"score = % open deals with \u22652 active people (90d)",
|
|
13150
13150
|
"people counted via opp contacts + same-org activity",
|
|
13151
13151
|
"threshold configurable (default 2)"
|
|
13152
13152
|
],
|
|
13153
|
-
meaning:
|
|
13154
|
-
expert_read: "Weight by deal size when you triage
|
|
13153
|
+
meaning: 'Thread Depth asks how many people keep each deal warm. One contact is a single point of failure \u2014 if they leave, the deal often dies with them. Like a bridge with one cable. Dollars on thin deals show as "single-threaded."',
|
|
13154
|
+
expert_read: "Weight by deal size when you triage \u2014 one thin mega-deal can outweigh ten small ones. Late-cycle single-threading tends to matter more than early. In enterprise motions, thin threads often lead to slipped quarters: the champion leaves and nobody else is warm enough to carry the deal.",
|
|
13155
13155
|
deepdive: [
|
|
13156
|
-
"
|
|
13157
|
-
'
|
|
13158
|
-
"
|
|
13159
|
-
"
|
|
13160
|
-
"
|
|
13156
|
+
"Green 65+, yellow 40+, red below 40.",
|
|
13157
|
+
'Dollars: sum of amount on single-threaded deals \u2192 "single-threaded".',
|
|
13158
|
+
"Read this last, after Freshness, Flow Rate, Drop Rate, and Signal:Noise.",
|
|
13159
|
+
"Weak score \u2192 play Multi-Thread Your Deals.",
|
|
13160
|
+
"Useful moves: buying-committee map, warm internal referral first, CRM contact roles, mid-stage single-thread alerts, champion job-change signals."
|
|
13161
13161
|
],
|
|
13162
13162
|
visual: {
|
|
13163
13163
|
kind: "bars",
|
|
@@ -18778,7 +18778,7 @@ function consumeOrientEmptyEnterCoach(ctx) {
|
|
|
18778
18778
|
if (!hasValidLicense()) {
|
|
18779
18779
|
return "Type /activate to paste a key. Type /checkout if you need to sign up.";
|
|
18780
18780
|
}
|
|
18781
|
-
return "Type a question, type use demo data
|
|
18781
|
+
return "Type a question, or type use demo data. Enter alone does not start a step here.";
|
|
18782
18782
|
}
|
|
18783
18783
|
function formatPhaseLabel(phase) {
|
|
18784
18784
|
switch (phase) {
|
|
@@ -19698,7 +19698,10 @@ function buildExploreTeachingGhosts(ctx, staticExplore) {
|
|
|
19698
19698
|
if (cached2 && cached2.length > 0) {
|
|
19699
19699
|
const ghosts = asksToGhostHints(cached2);
|
|
19700
19700
|
const gating = ctx.snapshot.computeResult?.aggregate.gating_vital_sign;
|
|
19701
|
-
if (gating)
|
|
19701
|
+
if (gating) {
|
|
19702
|
+
const label = (VITAL_SIGN_LABELS[gating] ?? gating).toLowerCase();
|
|
19703
|
+
ghosts.push(`try "why is ${label} red?"`);
|
|
19704
|
+
}
|
|
19702
19705
|
ghosts.push('try "how should we fix this?"');
|
|
19703
19706
|
return dedupeGhosts(ghosts);
|
|
19704
19707
|
}
|
|
@@ -19709,7 +19712,11 @@ function buildExploreTeachingGhosts(ctx, staticExplore) {
|
|
|
19709
19712
|
});
|
|
19710
19713
|
return dedupeGhosts([
|
|
19711
19714
|
...asksToGhostHints(asks),
|
|
19712
|
-
|
|
19715
|
+
(() => {
|
|
19716
|
+
const g = health.gating_vital_sign;
|
|
19717
|
+
const label = (VITAL_SIGN_LABELS[g] ?? g).toLowerCase();
|
|
19718
|
+
return `try "why is ${label} red?"`;
|
|
19719
|
+
})(),
|
|
19713
19720
|
'try "how should we fix this?"'
|
|
19714
19721
|
]);
|
|
19715
19722
|
}
|
|
@@ -24648,8 +24655,8 @@ var init_ghost_hints = __esm({
|
|
|
24648
24655
|
PHASE_GHOST_HINTS = {
|
|
24649
24656
|
orient: [
|
|
24650
24657
|
'try "pipeline health"',
|
|
24651
|
-
|
|
24652
|
-
|
|
24658
|
+
'try "how healthy is the pipeline?"',
|
|
24659
|
+
'try "what should I do next?"',
|
|
24653
24660
|
'try "is our retention real for the board?"',
|
|
24654
24661
|
'try "what is the most expensive problem to solve?"',
|
|
24655
24662
|
'try "board deck on Q3"'
|
|
@@ -24666,7 +24673,7 @@ var init_ghost_hints = __esm({
|
|
|
24666
24673
|
'try "how should we fix this?"',
|
|
24667
24674
|
'try "which segment is weakest?"',
|
|
24668
24675
|
'try "what is ARR?"',
|
|
24669
|
-
|
|
24676
|
+
'try "why is freshness red?"',
|
|
24670
24677
|
'try "ship a board deck"'
|
|
24671
24678
|
]
|
|
24672
24679
|
};
|