@sonnechasser/ntrp 0.2.2 → 0.2.3

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.
@@ -0,0 +1,1775 @@
1
+ #!/usr/bin/env node
2
+ var __getOwnPropNames = Object.getOwnPropertyNames;
3
+ var __esm = (fn, res) => function __init() {
4
+ return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
5
+ };
6
+
7
+ // src/ai/llm/thread-compat.ts
8
+ var init_thread_compat = __esm({
9
+ "src/ai/llm/thread-compat.ts"() {
10
+ "use strict";
11
+ }
12
+ });
13
+
14
+ // src/io/context.ts
15
+ var init_context = __esm({
16
+ "src/io/context.ts"() {
17
+ "use strict";
18
+ }
19
+ });
20
+
21
+ // src/cli/context.ts
22
+ import { basename, join, resolve, sep } from "path";
23
+ import { existsSync, mkdirSync, writeFileSync, readFileSync, readdirSync, statSync, rmSync } from "fs";
24
+ import { homedir } from "os";
25
+ import { randomUUID } from "crypto";
26
+ function isAnalysisReady(ctx) {
27
+ if (ctx.stage !== "analyzed" || ctx.analysis.completed.length === 0) return false;
28
+ if (!ctx.dataset) return false;
29
+ const counts = ctx.dataset.counts ?? {};
30
+ return Object.values(counts).some((n) => n > 0);
31
+ }
32
+ function defaultSessionAnalysis(primary = "gtm_health") {
33
+ return { primary, completed: [] };
34
+ }
35
+ var init_context2 = __esm({
36
+ "src/cli/context.ts"() {
37
+ "use strict";
38
+ init_thread_compat();
39
+ init_context();
40
+ }
41
+ });
42
+
43
+ // src/config/store.ts
44
+ import { readFileSync as readFileSync2, writeFileSync as writeFileSync2, existsSync as existsSync2, mkdirSync as mkdirSync2 } from "fs";
45
+ import { homedir as homedir2 } from "os";
46
+ import { join as join2, resolve as resolve2 } from "path";
47
+ function ntrpHome() {
48
+ return NTRP_DIR;
49
+ }
50
+ function getMemoryDir() {
51
+ const dir = join2(NTRP_DIR, "memory");
52
+ if (!existsSync2(dir)) {
53
+ mkdirSync2(dir, { recursive: true });
54
+ }
55
+ return dir;
56
+ }
57
+ var NTRP_DIR, CONFIG_PATH;
58
+ var init_store = __esm({
59
+ "src/config/store.ts"() {
60
+ "use strict";
61
+ NTRP_DIR = process.env.NTRP_HOME ? resolve2(process.env.NTRP_HOME) : join2(homedir2(), ".ntrp");
62
+ CONFIG_PATH = join2(NTRP_DIR, "config.json");
63
+ }
64
+ });
65
+
66
+ // src/config/profile.ts
67
+ import { readFileSync as readFileSync3, writeFileSync as writeFileSync3, existsSync as existsSync3, mkdirSync as mkdirSync3 } from "fs";
68
+ import { join as join3 } from "path";
69
+ var NTRP_DIR2, PROFILE_PATH;
70
+ var init_profile = __esm({
71
+ "src/config/profile.ts"() {
72
+ "use strict";
73
+ init_store();
74
+ NTRP_DIR2 = ntrpHome();
75
+ PROFILE_PATH = join3(NTRP_DIR2, "profile.json");
76
+ }
77
+ });
78
+
79
+ // src/db/connection.ts
80
+ import { dirname, join as join4, resolve as resolve3 } from "path";
81
+ var NTRP_DIR3, DEFAULT_DB_PATH, DB_PATH_PINNED;
82
+ var init_connection = __esm({
83
+ "src/db/connection.ts"() {
84
+ "use strict";
85
+ NTRP_DIR3 = process.env.NTRP_HOME ? resolve3(process.env.NTRP_HOME) : join4(process.env.HOME ?? "", ".ntrp");
86
+ DEFAULT_DB_PATH = process.env.NTRP_DB_PATH ? resolve3(process.env.NTRP_DB_PATH) : join4(NTRP_DIR3, "ntrp.duckdb");
87
+ DB_PATH_PINNED = !!process.env.NTRP_DB_PATH;
88
+ }
89
+ });
90
+
91
+ // src/output/formatters.ts
92
+ var init_formatters = __esm({
93
+ "src/output/formatters.ts"() {
94
+ "use strict";
95
+ }
96
+ });
97
+
98
+ // src/db/queries.ts
99
+ var init_queries = __esm({
100
+ "src/db/queries.ts"() {
101
+ "use strict";
102
+ init_connection();
103
+ init_formatters();
104
+ init_connection();
105
+ }
106
+ });
107
+
108
+ // src/ui/theme.ts
109
+ import chalk from "chalk";
110
+ var init_theme = __esm({
111
+ "src/ui/theme.ts"() {
112
+ "use strict";
113
+ init_formatters();
114
+ }
115
+ });
116
+
117
+ // src/services/session-analysis.ts
118
+ var init_session_analysis = __esm({
119
+ "src/services/session-analysis.ts"() {
120
+ "use strict";
121
+ init_queries();
122
+ init_profile();
123
+ init_formatters();
124
+ init_theme();
125
+ }
126
+ });
127
+
128
+ // src/conversation/handoff-draft.ts
129
+ function isShipIntent(input) {
130
+ const line = input.trim();
131
+ if (/\?\s*$/.test(line) || QUESTION_LEAD_RE.test(line)) return false;
132
+ return SHIP_INTENT_RE.test(line);
133
+ }
134
+ var QUESTION_LEAD_RE, SHIP_INTENT_RE;
135
+ var init_handoff_draft = __esm({
136
+ "src/conversation/handoff-draft.ts"() {
137
+ "use strict";
138
+ init_profile();
139
+ init_session_analysis();
140
+ QUESTION_LEAD_RE = /^\s*(what|why|how|when|where|who|which|is|are|was|were|do|does|did|explain|tell me|help me understand)\b/i;
141
+ SHIP_INTENT_RE = /\b(ship|export|deliver|write[- ]?up|board memo|action plan|turn (this|it|that) into|(draft|create|make|build|prepare|generate|send)\s+(me\s+)?(a\s+|the\s+)?hand[- ]?off|hand[- ]?off\s+(prompt|doc|document|plan))\b/i;
142
+ }
143
+ });
144
+
145
+ // src/ai/llm/providers.ts
146
+ var init_providers = __esm({
147
+ "src/ai/llm/providers.ts"() {
148
+ "use strict";
149
+ init_store();
150
+ }
151
+ });
152
+
153
+ // src/config/llm-config.ts
154
+ var init_llm_config = __esm({
155
+ "src/config/llm-config.ts"() {
156
+ "use strict";
157
+ init_providers();
158
+ init_store();
159
+ }
160
+ });
161
+
162
+ // src/ai/llm/gate.ts
163
+ var init_gate = __esm({
164
+ "src/ai/llm/gate.ts"() {
165
+ "use strict";
166
+ init_llm_config();
167
+ }
168
+ });
169
+
170
+ // src/ai/repl-api.ts
171
+ var init_repl_api = __esm({
172
+ "src/ai/repl-api.ts"() {
173
+ "use strict";
174
+ init_gate();
175
+ }
176
+ });
177
+
178
+ // src/cli/repl-globals.ts
179
+ var init_repl_globals = __esm({
180
+ "src/cli/repl-globals.ts"() {
181
+ "use strict";
182
+ }
183
+ });
184
+
185
+ // src/cli/prompts.ts
186
+ import { createInterface } from "readline/promises";
187
+ import { clearLine, cursorTo } from "readline";
188
+ import chalk2 from "chalk";
189
+ var init_prompts = __esm({
190
+ "src/cli/prompts.ts"() {
191
+ "use strict";
192
+ init_repl_globals();
193
+ init_theme();
194
+ }
195
+ });
196
+
197
+ // src/pipeline/segments.ts
198
+ var init_segments = __esm({
199
+ "src/pipeline/segments.ts"() {
200
+ "use strict";
201
+ init_connection();
202
+ init_queries();
203
+ }
204
+ });
205
+
206
+ // src/baselines/defaults.ts
207
+ var init_defaults = __esm({
208
+ "src/baselines/defaults.ts"() {
209
+ "use strict";
210
+ }
211
+ });
212
+
213
+ // src/baselines/profile-presets.ts
214
+ var init_profile_presets = __esm({
215
+ "src/baselines/profile-presets.ts"() {
216
+ "use strict";
217
+ }
218
+ });
219
+
220
+ // src/baselines/resolve.ts
221
+ var init_resolve = __esm({
222
+ "src/baselines/resolve.ts"() {
223
+ "use strict";
224
+ init_defaults();
225
+ init_profile_presets();
226
+ }
227
+ });
228
+
229
+ // src/vitals/shared.ts
230
+ var init_shared = __esm({
231
+ "src/vitals/shared.ts"() {
232
+ "use strict";
233
+ }
234
+ });
235
+
236
+ // src/vitals/freshness.ts
237
+ var init_freshness = __esm({
238
+ "src/vitals/freshness.ts"() {
239
+ "use strict";
240
+ init_defaults();
241
+ init_shared();
242
+ }
243
+ });
244
+
245
+ // src/vitals/flow-rate.ts
246
+ var init_flow_rate = __esm({
247
+ "src/vitals/flow-rate.ts"() {
248
+ "use strict";
249
+ init_defaults();
250
+ init_shared();
251
+ }
252
+ });
253
+
254
+ // src/vitals/drop-rate.ts
255
+ var init_drop_rate = __esm({
256
+ "src/vitals/drop-rate.ts"() {
257
+ "use strict";
258
+ init_defaults();
259
+ init_shared();
260
+ }
261
+ });
262
+
263
+ // src/vitals/signal-to-noise.ts
264
+ var init_signal_to_noise = __esm({
265
+ "src/vitals/signal-to-noise.ts"() {
266
+ "use strict";
267
+ init_defaults();
268
+ init_store();
269
+ init_shared();
270
+ }
271
+ });
272
+
273
+ // src/vitals/thread-depth.ts
274
+ var init_thread_depth = __esm({
275
+ "src/vitals/thread-depth.ts"() {
276
+ "use strict";
277
+ init_defaults();
278
+ init_shared();
279
+ }
280
+ });
281
+
282
+ // src/vitals/health-score.ts
283
+ var LAYERS;
284
+ var init_health_score = __esm({
285
+ "src/vitals/health-score.ts"() {
286
+ "use strict";
287
+ init_segments();
288
+ init_resolve();
289
+ init_freshness();
290
+ init_flow_rate();
291
+ init_drop_rate();
292
+ init_signal_to_noise();
293
+ init_thread_depth();
294
+ init_connection();
295
+ init_queries();
296
+ LAYERS = [
297
+ { layer: 1, signs: ["freshness"] },
298
+ { layer: 2, signs: ["flow_rate", "drop_rate"] },
299
+ { layer: 3, signs: ["signal_to_noise"] },
300
+ { layer: 4, signs: ["thread_depth"] }
301
+ ];
302
+ }
303
+ });
304
+
305
+ // src/pipeline/divergence.ts
306
+ var init_divergence = __esm({
307
+ "src/pipeline/divergence.ts"() {
308
+ "use strict";
309
+ }
310
+ });
311
+
312
+ // src/db/schema.ts
313
+ var init_schema = __esm({
314
+ "src/db/schema.ts"() {
315
+ "use strict";
316
+ init_connection();
317
+ }
318
+ });
319
+
320
+ // src/baselines/metrics-benchmarks.ts
321
+ var init_metrics_benchmarks = __esm({
322
+ "src/baselines/metrics-benchmarks.ts"() {
323
+ "use strict";
324
+ }
325
+ });
326
+
327
+ // src/metrics/classify-source.ts
328
+ var init_classify_source = __esm({
329
+ "src/metrics/classify-source.ts"() {
330
+ "use strict";
331
+ }
332
+ });
333
+
334
+ // src/metrics/helpers.ts
335
+ var init_helpers = __esm({
336
+ "src/metrics/helpers.ts"() {
337
+ "use strict";
338
+ init_metrics_benchmarks();
339
+ }
340
+ });
341
+
342
+ // src/metrics/coverage.ts
343
+ var init_coverage = __esm({
344
+ "src/metrics/coverage.ts"() {
345
+ "use strict";
346
+ init_helpers();
347
+ init_classify_source();
348
+ }
349
+ });
350
+
351
+ // src/metrics/context.ts
352
+ var init_context3 = __esm({
353
+ "src/metrics/context.ts"() {
354
+ "use strict";
355
+ init_metrics_benchmarks();
356
+ init_profile();
357
+ init_health_score();
358
+ init_queries();
359
+ init_classify_source();
360
+ init_coverage();
361
+ }
362
+ });
363
+
364
+ // src/metrics/revenue.ts
365
+ var init_revenue = __esm({
366
+ "src/metrics/revenue.ts"() {
367
+ "use strict";
368
+ init_helpers();
369
+ init_formatters();
370
+ }
371
+ });
372
+
373
+ // src/metrics/retention.ts
374
+ var init_retention = __esm({
375
+ "src/metrics/retention.ts"() {
376
+ "use strict";
377
+ init_revenue();
378
+ init_helpers();
379
+ init_metrics_benchmarks();
380
+ }
381
+ });
382
+
383
+ // src/metrics/pipeline.ts
384
+ var init_pipeline = __esm({
385
+ "src/metrics/pipeline.ts"() {
386
+ "use strict";
387
+ init_helpers();
388
+ init_formatters();
389
+ init_metrics_benchmarks();
390
+ }
391
+ });
392
+
393
+ // src/metrics/sales-efficiency.ts
394
+ var init_sales_efficiency = __esm({
395
+ "src/metrics/sales-efficiency.ts"() {
396
+ "use strict";
397
+ init_helpers();
398
+ init_formatters();
399
+ init_metrics_benchmarks();
400
+ }
401
+ });
402
+
403
+ // src/metrics/unit-economics.ts
404
+ var init_unit_economics = __esm({
405
+ "src/metrics/unit-economics.ts"() {
406
+ "use strict";
407
+ init_formatters();
408
+ }
409
+ });
410
+
411
+ // src/metrics/confidence.ts
412
+ var init_confidence = __esm({
413
+ "src/metrics/confidence.ts"() {
414
+ "use strict";
415
+ }
416
+ });
417
+
418
+ // src/metrics/periods.ts
419
+ var init_periods = __esm({
420
+ "src/metrics/periods.ts"() {
421
+ "use strict";
422
+ init_helpers();
423
+ }
424
+ });
425
+
426
+ // src/metrics/compute.ts
427
+ var init_compute = __esm({
428
+ "src/metrics/compute.ts"() {
429
+ "use strict";
430
+ init_segments();
431
+ init_queries();
432
+ init_context3();
433
+ init_revenue();
434
+ init_retention();
435
+ init_pipeline();
436
+ init_sales_efficiency();
437
+ init_unit_economics();
438
+ init_confidence();
439
+ init_periods();
440
+ init_context3();
441
+ }
442
+ });
443
+
444
+ // src/metrics/insights.ts
445
+ var init_insights = __esm({
446
+ "src/metrics/insights.ts"() {
447
+ "use strict";
448
+ init_coverage();
449
+ }
450
+ });
451
+
452
+ // src/ai/llm/models-cache.ts
453
+ var CACHE_TTL_MS;
454
+ var init_models_cache = __esm({
455
+ "src/ai/llm/models-cache.ts"() {
456
+ "use strict";
457
+ init_store();
458
+ CACHE_TTL_MS = 24 * 60 * 60 * 1e3;
459
+ }
460
+ });
461
+
462
+ // src/ai/llm/catalog.ts
463
+ var ENTRIES, byId;
464
+ var init_catalog = __esm({
465
+ "src/ai/llm/catalog.ts"() {
466
+ "use strict";
467
+ init_models_cache();
468
+ ENTRIES = [
469
+ {
470
+ id: "claude-opus-4-6",
471
+ provider: "anthropic",
472
+ tier: "high",
473
+ status: "active",
474
+ successor_id: null,
475
+ supports_tools: true,
476
+ max_context_tokens: 2e5,
477
+ display_name: "Claude Opus 4.6",
478
+ relative_cost: 3
479
+ },
480
+ {
481
+ id: "claude-sonnet-4-5-20250929",
482
+ provider: "anthropic",
483
+ tier: "medium",
484
+ status: "active",
485
+ successor_id: null,
486
+ supports_tools: true,
487
+ max_context_tokens: 2e5,
488
+ display_name: "Claude Sonnet 4.5",
489
+ relative_cost: 2
490
+ },
491
+ {
492
+ id: "claude-haiku-4-5-20251001",
493
+ provider: "anthropic",
494
+ tier: "low",
495
+ status: "active",
496
+ successor_id: null,
497
+ supports_tools: true,
498
+ max_context_tokens: 2e5,
499
+ display_name: "Claude Haiku 4.5",
500
+ relative_cost: 1
501
+ },
502
+ {
503
+ id: "gpt-4.1",
504
+ provider: "openai",
505
+ tier: "high",
506
+ status: "active",
507
+ successor_id: null,
508
+ supports_tools: true,
509
+ max_context_tokens: 1047576,
510
+ display_name: "GPT-4.1",
511
+ relative_cost: 3
512
+ },
513
+ {
514
+ id: "gpt-4.1-mini",
515
+ provider: "openai",
516
+ tier: "medium",
517
+ status: "active",
518
+ successor_id: null,
519
+ supports_tools: true,
520
+ max_context_tokens: 1047576,
521
+ display_name: "GPT-4.1 Mini",
522
+ relative_cost: 2
523
+ },
524
+ {
525
+ id: "gpt-4.1-nano",
526
+ provider: "openai",
527
+ tier: "low",
528
+ status: "active",
529
+ successor_id: null,
530
+ supports_tools: true,
531
+ max_context_tokens: 1047576,
532
+ display_name: "GPT-4.1 Nano",
533
+ relative_cost: 1
534
+ }
535
+ ];
536
+ byId = new Map(ENTRIES.map((e) => [e.id, e]));
537
+ }
538
+ });
539
+
540
+ // src/ai/llm/surfaces.ts
541
+ var init_surfaces = __esm({
542
+ "src/ai/llm/surfaces.ts"() {
543
+ "use strict";
544
+ }
545
+ });
546
+
547
+ // src/ai/llm/session-state.ts
548
+ var init_session_state = __esm({
549
+ "src/ai/llm/session-state.ts"() {
550
+ "use strict";
551
+ init_llm_config();
552
+ init_catalog();
553
+ init_surfaces();
554
+ }
555
+ });
556
+
557
+ // src/conversation/phase.ts
558
+ import chalk3 from "chalk";
559
+ function sessionHasData(ctx) {
560
+ const counts = ctx.dataset?.counts ?? {};
561
+ return Object.values(counts).some((n) => (n ?? 0) > 0);
562
+ }
563
+ function resolveConversationPhase(ctx) {
564
+ if (ctx.deliverIntent) return "deliver";
565
+ if (ctx.computeInProgress) return "compute";
566
+ if (ctx.strategistState && ctx.strategistState.step !== "awaiting_analysis") {
567
+ return "strategize";
568
+ }
569
+ if (isAnalysisReady(ctx)) return "explore";
570
+ const scope = ctx.scope;
571
+ if (scope?.confirmed_at) {
572
+ if (!sessionHasData(ctx)) return "awaiting_data";
573
+ if (ctx.stage !== "analyzed") return "awaiting_data";
574
+ }
575
+ if (scope?.intent_summary && !scope.confirmed_at) return "scope";
576
+ return "orient";
577
+ }
578
+ var init_phase = __esm({
579
+ "src/conversation/phase.ts"() {
580
+ "use strict";
581
+ init_context2();
582
+ init_session_state();
583
+ init_theme();
584
+ }
585
+ });
586
+
587
+ // src/conversation/gap-audit.ts
588
+ var init_gap_audit = __esm({
589
+ "src/conversation/gap-audit.ts"() {
590
+ "use strict";
591
+ init_connection();
592
+ init_queries();
593
+ init_profile();
594
+ init_compute();
595
+ init_insights();
596
+ init_phase();
597
+ }
598
+ });
599
+
600
+ // src/strategies/library.ts
601
+ import { writeFileSync as writeFileSync4 } from "fs";
602
+ import { join as join5 } from "path";
603
+ import { stringify as stringifyYaml } from "yaml";
604
+ var init_library = __esm({
605
+ "src/strategies/library.ts"() {
606
+ "use strict";
607
+ init_store();
608
+ }
609
+ });
610
+
611
+ // src/io/types.ts
612
+ var init_types = __esm({
613
+ "src/io/types.ts"() {
614
+ "use strict";
615
+ }
616
+ });
617
+
618
+ // src/io/errors.ts
619
+ var init_errors = __esm({
620
+ "src/io/errors.ts"() {
621
+ "use strict";
622
+ init_types();
623
+ }
624
+ });
625
+
626
+ // src/services/strategist.ts
627
+ import { createHash } from "crypto";
628
+ var init_strategist = __esm({
629
+ "src/services/strategist.ts"() {
630
+ "use strict";
631
+ init_schema();
632
+ init_queries();
633
+ init_health_score();
634
+ init_divergence();
635
+ init_gap_audit();
636
+ init_library();
637
+ init_errors();
638
+ init_types();
639
+ init_formatters();
640
+ init_formatters();
641
+ }
642
+ });
643
+
644
+ // src/config/install.ts
645
+ import { randomUUID as randomUUID2 } from "crypto";
646
+ import { existsSync as existsSync4, mkdirSync as mkdirSync4, readFileSync as readFileSync4, unlinkSync, writeFileSync as writeFileSync5 } from "fs";
647
+ import { join as join6 } from "path";
648
+ var init_install = __esm({
649
+ "src/config/install.ts"() {
650
+ "use strict";
651
+ init_store();
652
+ }
653
+ });
654
+
655
+ // src/config/progress-migrate.ts
656
+ import { existsSync as existsSync5, readFileSync as readFileSync5, renameSync, writeFileSync as writeFileSync6 } from "fs";
657
+ import { join as join7 } from "path";
658
+ var init_progress_migrate = __esm({
659
+ "src/config/progress-migrate.ts"() {
660
+ "use strict";
661
+ init_store();
662
+ }
663
+ });
664
+
665
+ // src/whimsy/usage-backfill.ts
666
+ var init_usage_backfill = __esm({
667
+ "src/whimsy/usage-backfill.ts"() {
668
+ "use strict";
669
+ }
670
+ });
671
+
672
+ // src/config/progress.ts
673
+ import { existsSync as existsSync6, mkdirSync as mkdirSync5, readFileSync as readFileSync6, unlinkSync as unlinkSync2, writeFileSync as writeFileSync7 } from "fs";
674
+ import { join as join8 } from "path";
675
+ var init_progress = __esm({
676
+ "src/config/progress.ts"() {
677
+ "use strict";
678
+ init_install();
679
+ init_progress_migrate();
680
+ init_store();
681
+ init_usage_backfill();
682
+ }
683
+ });
684
+
685
+ // src/whimsy/usage-stats.ts
686
+ var init_usage_stats = __esm({
687
+ "src/whimsy/usage-stats.ts"() {
688
+ "use strict";
689
+ init_progress();
690
+ }
691
+ });
692
+
693
+ // src/ai/llm/types.ts
694
+ var init_types2 = __esm({
695
+ "src/ai/llm/types.ts"() {
696
+ "use strict";
697
+ }
698
+ });
699
+
700
+ // src/ai/llm/errors.ts
701
+ var init_errors2 = __esm({
702
+ "src/ai/llm/errors.ts"() {
703
+ "use strict";
704
+ init_types2();
705
+ }
706
+ });
707
+
708
+ // src/ai/llm/adapters/anthropic.ts
709
+ import Anthropic from "@anthropic-ai/sdk";
710
+ var init_anthropic = __esm({
711
+ "src/ai/llm/adapters/anthropic.ts"() {
712
+ "use strict";
713
+ init_errors2();
714
+ }
715
+ });
716
+
717
+ // src/ai/llm/adapters/openai-compat.ts
718
+ import OpenAI from "openai";
719
+ var init_openai_compat = __esm({
720
+ "src/ai/llm/adapters/openai-compat.ts"() {
721
+ "use strict";
722
+ init_errors2();
723
+ }
724
+ });
725
+
726
+ // src/ai/llm/http.ts
727
+ var init_http = __esm({
728
+ "src/ai/llm/http.ts"() {
729
+ "use strict";
730
+ }
731
+ });
732
+
733
+ // src/ai/llm/ranking.ts
734
+ var init_ranking = __esm({
735
+ "src/ai/llm/ranking.ts"() {
736
+ "use strict";
737
+ }
738
+ });
739
+
740
+ // src/ai/llm/discovery.ts
741
+ var init_discovery = __esm({
742
+ "src/ai/llm/discovery.ts"() {
743
+ "use strict";
744
+ init_llm_config();
745
+ init_http();
746
+ init_models_cache();
747
+ init_providers();
748
+ init_ranking();
749
+ }
750
+ });
751
+
752
+ // src/ai/llm/heal.ts
753
+ var init_heal = __esm({
754
+ "src/ai/llm/heal.ts"() {
755
+ "use strict";
756
+ init_store();
757
+ init_discovery();
758
+ init_models_cache();
759
+ }
760
+ });
761
+
762
+ // src/ai/llm/resolver.ts
763
+ var init_resolver = __esm({
764
+ "src/ai/llm/resolver.ts"() {
765
+ "use strict";
766
+ init_llm_config();
767
+ init_catalog();
768
+ init_session_state();
769
+ }
770
+ });
771
+
772
+ // src/ai/llm/failover.ts
773
+ var init_failover = __esm({
774
+ "src/ai/llm/failover.ts"() {
775
+ "use strict";
776
+ init_usage_stats();
777
+ init_anthropic();
778
+ init_openai_compat();
779
+ init_catalog();
780
+ init_discovery();
781
+ init_errors2();
782
+ init_heal();
783
+ init_models_cache();
784
+ init_providers();
785
+ init_types2();
786
+ init_resolver();
787
+ }
788
+ });
789
+
790
+ // src/ai/web-search.ts
791
+ var init_web_search = __esm({
792
+ "src/ai/web-search.ts"() {
793
+ "use strict";
794
+ init_store();
795
+ }
796
+ });
797
+
798
+ // src/ai/tool-schemas.ts
799
+ var init_tool_schemas = __esm({
800
+ "src/ai/tool-schemas.ts"() {
801
+ "use strict";
802
+ init_web_search();
803
+ }
804
+ });
805
+
806
+ // src/ai/privacy.ts
807
+ import { homedir as homedir3 } from "os";
808
+ import { join as join9 } from "path";
809
+ var AUDIT_DIR;
810
+ var init_privacy = __esm({
811
+ "src/ai/privacy.ts"() {
812
+ "use strict";
813
+ AUDIT_DIR = join9(homedir3(), ".ntrp", "audit");
814
+ }
815
+ });
816
+
817
+ // src/ai/tool-handlers.ts
818
+ var init_tool_handlers = __esm({
819
+ "src/ai/tool-handlers.ts"() {
820
+ "use strict";
821
+ init_connection();
822
+ init_privacy();
823
+ init_web_search();
824
+ }
825
+ });
826
+
827
+ // src/data/playbook.ts
828
+ import { existsSync as existsSync7, readFileSync as readFileSync7, appendFileSync } from "fs";
829
+ import { join as join10 } from "path";
830
+ function playsPath() {
831
+ return join10(getMemoryDir(), PLAYS_FILE);
832
+ }
833
+ function getCustomPlays() {
834
+ const path = playsPath();
835
+ if (!existsSync7(path)) return [];
836
+ const out = [];
837
+ for (const line of readFileSync7(path, "utf-8").split("\n")) {
838
+ const trimmed = line.trim();
839
+ if (!trimmed) continue;
840
+ try {
841
+ const play = JSON.parse(trimmed);
842
+ out.push({ ...play, source: "learned" });
843
+ } catch {
844
+ }
845
+ }
846
+ return out;
847
+ }
848
+ function getAllPlays() {
849
+ return [...PLAYBOOK, ...getCustomPlays()];
850
+ }
851
+ function getPlaybook() {
852
+ return getAllPlays();
853
+ }
854
+ function matchTriggeredPlays(vitals2, layers) {
855
+ const bySign = new Map(vitals2.map((v) => [v.vital_sign, v]));
856
+ const out = [];
857
+ for (const layer of layers) {
858
+ for (const sign of layer.signs) {
859
+ const vital = bySign.get(sign);
860
+ if (!vital) continue;
861
+ const fires = vital.status === "red" || vital.score < VITAL_TRIGGER_THRESHOLDS[sign];
862
+ if (!fires) continue;
863
+ for (const play of getAllPlays()) {
864
+ if (play.trigger_vital_sign === sign) {
865
+ out.push({ play, vital, layer: layer.layer });
866
+ }
867
+ }
868
+ }
869
+ }
870
+ return out;
871
+ }
872
+ var PLAYBOOK, PLAYS_FILE, VITAL_TRIGGER_THRESHOLDS;
873
+ var init_playbook = __esm({
874
+ "src/data/playbook.ts"() {
875
+ "use strict";
876
+ init_store();
877
+ PLAYBOOK = [
878
+ {
879
+ id: "multi-thread-deals",
880
+ name: "Multi-Thread Your Deals",
881
+ trigger_vital_sign: "thread_depth",
882
+ trigger_condition: "thread_depth score < 2 (most deals have \u22641 active contact)",
883
+ why: "Single-threaded deals die when your one contact goes dark, changes roles, or loses budget authority. Every deal needs at least 2 active contacts to survive.",
884
+ steps: [
885
+ "Pull the list of single-threaded deals from entity_details",
886
+ "For each deal, identify the existing contact and their role",
887
+ "Research the account org chart for a second entry point (champion, economic buyer, or technical evaluator)",
888
+ "Send a warm intro or multi-thread via your existing contact",
889
+ "Log the new contact as an activity linked to the opportunity"
890
+ ],
891
+ tools_that_help: ["LinkedIn Sales Navigator", "Org chart tools", "CRM contact roles"],
892
+ expected_outcome: "Thread depth score rises above threshold; single-threaded deal count drops by 50%+ within 2 weeks"
893
+ },
894
+ {
895
+ id: "clean-dead-pipeline",
896
+ name: "Clean Dead Pipeline",
897
+ trigger_vital_sign: "freshness",
898
+ trigger_condition: "freshness score < 60",
899
+ why: "Stale accounts and zombie deals inflate your pipeline number but deliver zero revenue. They waste forecasting accuracy and hide the real health of your business.",
900
+ steps: [
901
+ "Export the stale opportunities list from entity_details",
902
+ "For each stale deal: contact the prospect within 48 hours or move to Closed Lost",
903
+ "For stale contacts: send a re-engagement sequence or archive",
904
+ "For stale organizations: verify they still match your ICP; archive if not",
905
+ "Set a recurring pipeline hygiene review (weekly 15-min scrub)"
906
+ ],
907
+ tools_that_help: ["CRM bulk update", "Email sequences", "Pipeline review meeting template"],
908
+ expected_outcome: "Freshness score jumps 20+ points; pipeline accuracy improves; forecast confidence increases"
909
+ },
910
+ {
911
+ id: "fix-handoff-gap",
912
+ name: "Fix the Handoff Gap",
913
+ trigger_vital_sign: "drop_rate",
914
+ trigger_condition: "drop_rate score indicates >30% of marketing leads not reaching sales",
915
+ why: "Every lead that marketing generates but sales never sees is wasted budget and lost revenue. The marketing\u2192sales handoff is the #1 leak in most GTM motions.",
916
+ steps: [
917
+ "Audit the marketing\u2192sales handoff: which leads exist only in marketing systems?",
918
+ "Check lead routing rules \u2014 are MQLs being assigned to active reps?",
919
+ "Verify SLA: how quickly are handed-off leads being touched by sales?",
920
+ "Fix routing gaps: unassigned territories, inactive rep queues, missing automation",
921
+ "Set up a weekly handoff report showing marketing-only leads"
922
+ ],
923
+ tools_that_help: ["Lead routing audit", "SLA dashboard", "Marketing\u2192Sales sync tool"],
924
+ expected_outcome: "Drop rate improves 15+ points; marketing-only lead count drops by 60%+"
925
+ },
926
+ {
927
+ id: "retarget-effort",
928
+ name: "Retarget Misdirected Effort",
929
+ trigger_vital_sign: "signal_to_noise",
930
+ trigger_condition: "signal_to_noise score < 50% (majority of activities not linked to pipeline)",
931
+ why: "When reps spend more than half their time on activities unconnected to open pipeline, they're burning hours that could be closing deals. Every noisy activity is time stolen from revenue.",
932
+ steps: [
933
+ "Review the noisy activities from entity_details \u2014 what types dominate?",
934
+ "Identify root cause: are reps prospecting dead accounts? Logging admin tasks? Working closed deals?",
935
+ "Create a 'focus list' of pipeline-linked accounts for each rep",
936
+ "Set activity targets: 80% of weekly activities should touch open pipeline",
937
+ "Remove or automate low-value activities that generate noise"
938
+ ],
939
+ tools_that_help: ["Activity reports by rep", "Focus account lists", "CRM activity rules"],
940
+ expected_outcome: "Signal-to-noise ratio improves to 70%+; rep productivity increases measurably"
941
+ },
942
+ {
943
+ id: "unstick-pipeline",
944
+ name: "Unstick the Pipeline",
945
+ trigger_vital_sign: "flow_rate",
946
+ trigger_condition: "flow_rate score < 50 (high average deal age or many stuck deals)",
947
+ why: "Stuck deals block revenue and demoralize reps. A deal that hasn't moved in 14+ days is either dead or needs intervention \u2014 either way, inaction is the worst choice.",
948
+ steps: [
949
+ "Pull stuck deals sorted by amount (highest value first)",
950
+ "For each stuck deal: identify the blocker (no next step? waiting on prospect? internal approval?)",
951
+ "Create a specific next action with a deadline for each stuck deal",
952
+ "Escalate deals stuck >30 days to manager review",
953
+ "Consider past-due deals: update close dates or move to Closed Lost"
954
+ ],
955
+ tools_that_help: ["Deal inspection reports", "Next-step templates", "Manager escalation workflow"],
956
+ expected_outcome: "Flow rate score improves 15+ points; stuck deal count drops by 40%+ within 2 weeks"
957
+ },
958
+ {
959
+ id: "reduce-logo-churn",
960
+ name: "Reduce Logo Churn",
961
+ trigger_metric: "grr",
962
+ trigger_lens: "revenue_metrics",
963
+ trigger_condition: "GRR below motion benchmark or churned ARR elevated",
964
+ why: "Revenue leaking from existing customers is the most expensive problem \u2014 you already paid to acquire them.",
965
+ steps: [
966
+ "Identify churned and at-risk accounts from retention metrics",
967
+ "Segment churn by deal size, tenure, and product usage patterns",
968
+ "Launch save plays for accounts showing contraction signals",
969
+ "Audit renewal process: timing, stakeholders, and success criteria",
970
+ "Implement early-warning triggers 90 days before renewal"
971
+ ],
972
+ tools_that_help: ["CS platform", "Renewal calendar", "NPS/CSAT surveys"],
973
+ expected_outcome: "GRR improves toward motion benchmark within 2 quarters"
974
+ },
975
+ {
976
+ id: "accelerate-expansion",
977
+ name: "Accelerate Expansion",
978
+ trigger_metric: "nrr",
979
+ trigger_lens: "revenue_metrics",
980
+ trigger_condition: "NRR below 100% with low expansion ARR",
981
+ why: "Growing from installed base is cheaper than new logo acquisition \u2014 low expansion means untapped wallet share.",
982
+ steps: [
983
+ "List accounts with single-product adoption and upsell potential",
984
+ "Map expansion triggers (seat growth, new use cases, tier upgrades)",
985
+ "Assign expansion targets to CS and AE teams by account tier",
986
+ "Create packaged upsell offers with clear ROI narratives",
987
+ "Track expansion pipeline separately from new business"
988
+ ],
989
+ tools_that_help: ["Account plans", "Usage analytics", "Expansion playbooks"],
990
+ expected_outcome: "Expansion ARR grows 20%+ quarter over quarter"
991
+ },
992
+ {
993
+ id: "fix-renewal-process",
994
+ name: "Fix the Renewal Process",
995
+ trigger_metric: "contraction_arr",
996
+ trigger_lens: "revenue_metrics",
997
+ trigger_condition: "Contraction ARR > 0",
998
+ why: "Downgrades are usually a process failure \u2014 late engagement, wrong stakeholders, or missing value proof.",
999
+ steps: [
1000
+ "Pull all contraction events and categorize root cause",
1001
+ "Standardize renewal timeline: 120/90/60/30-day checkpoints",
1002
+ "Ensure economic buyer is engaged before renewal date",
1003
+ "Build ROI recap deck template for every renewal",
1004
+ "Escalate contractions >20% to leadership review"
1005
+ ],
1006
+ tools_that_help: ["Renewal workflow", "QBR templates", "Value realization reports"],
1007
+ expected_outcome: "Contraction ARR drops 50%+ within 2 quarters"
1008
+ },
1009
+ {
1010
+ id: "rebalance-pipeline-mix",
1011
+ name: "Rebalance Pipeline Mix",
1012
+ trigger_metric: "pipeline_coverage",
1013
+ trigger_lens: "revenue_metrics",
1014
+ trigger_condition: "Pipeline coverage red while win rate is healthy",
1015
+ why: "Strong win rate with weak coverage means qualification works but top-of-funnel is starving the machine.",
1016
+ steps: [
1017
+ "Compare pipeline created vs closed-won by source and segment",
1018
+ "Identify segments with coverage below benchmark",
1019
+ "Shift marketing and SDR effort toward under-covered segments",
1020
+ "Set weekly pipeline-created targets by rep",
1021
+ "Review discounting and stage inflation masking thin pipeline"
1022
+ ],
1023
+ tools_that_help: ["Pipeline analytics", "Marketing attribution", "Capacity planning"],
1024
+ expected_outcome: "Pipeline coverage reaches motion benchmark within 90 days"
1025
+ },
1026
+ {
1027
+ id: "compress-sales-cycle",
1028
+ name: "Compress the Sales Cycle",
1029
+ trigger_metric: "avg_sales_cycle",
1030
+ trigger_lens: "revenue_metrics",
1031
+ trigger_condition: "Avg sales cycle exceeds profile sales_cycle_days by 50%+",
1032
+ why: "Deals aging past your motion's norm tie up capacity and push revenue into future quarters.",
1033
+ steps: [
1034
+ "Analyze cycle time by stage \u2014 find where deals stall longest",
1035
+ "Implement stage-exit criteria with required next steps",
1036
+ "Introduce mutual action plans for deals past midpoint",
1037
+ "Escalate deals exceeding 2x median cycle to manager review",
1038
+ "Remove low-probability aged deals to free rep capacity"
1039
+ ],
1040
+ tools_that_help: ["Stage duration reports", "MAP templates", "Deal coaching"],
1041
+ expected_outcome: "Median cycle time drops 20%+ within one quarter"
1042
+ },
1043
+ {
1044
+ id: "improve-magic-number",
1045
+ name: "Improve Magic Number",
1046
+ trigger_metric: "magic_number",
1047
+ trigger_lens: "revenue_metrics",
1048
+ trigger_condition: "Magic number below motion benchmark (when spend data available)",
1049
+ why: "Low S&M efficiency means you're buying growth too expensively \u2014 burn rate outpaces sustainable unit economics.",
1050
+ steps: [
1051
+ "Calculate magic number by channel and segment",
1052
+ "Cut spend on channels with magic number below 0.5",
1053
+ "Double down on highest-efficiency acquisition motions",
1054
+ "Align CAC targets to motion-specific payback thresholds",
1055
+ "Review rep ramp time and quota attainment curves"
1056
+ ],
1057
+ tools_that_help: ["Finance model", "Channel ROI dashboard", "CAC by source"],
1058
+ expected_outcome: "Magic number improves toward benchmark within 2 quarters"
1059
+ }
1060
+ ];
1061
+ PLAYS_FILE = "plays.jsonl";
1062
+ VITAL_TRIGGER_THRESHOLDS = {
1063
+ freshness: 60,
1064
+ flow_rate: 50,
1065
+ drop_rate: 70,
1066
+ signal_to_noise: 50,
1067
+ thread_depth: 60
1068
+ };
1069
+ }
1070
+ });
1071
+
1072
+ // src/workflows/registry.ts
1073
+ var init_registry = __esm({
1074
+ "src/workflows/registry.ts"() {
1075
+ "use strict";
1076
+ }
1077
+ });
1078
+
1079
+ // src/ai/prompt-parts.ts
1080
+ var init_prompt_parts = __esm({
1081
+ "src/ai/prompt-parts.ts"() {
1082
+ "use strict";
1083
+ init_profile();
1084
+ init_playbook();
1085
+ init_registry();
1086
+ }
1087
+ });
1088
+
1089
+ // src/ai/strategist-prompt.ts
1090
+ var init_strategist_prompt = __esm({
1091
+ "src/ai/strategist-prompt.ts"() {
1092
+ "use strict";
1093
+ init_prompt_parts();
1094
+ }
1095
+ });
1096
+
1097
+ // src/ai/json-response.ts
1098
+ var init_json_response = __esm({
1099
+ "src/ai/json-response.ts"() {
1100
+ "use strict";
1101
+ }
1102
+ });
1103
+
1104
+ // src/ai/strategist-validate.ts
1105
+ function extractNumbers(text) {
1106
+ const out = [];
1107
+ for (const match of text.matchAll(NUMBER_RE)) {
1108
+ const base = Number(match[1].replace(/,/g, ""));
1109
+ if (!Number.isFinite(base)) continue;
1110
+ const suffix = match[2]?.toLowerCase();
1111
+ out.push(suffix ? base * (SUFFIX_MULTIPLIER[suffix] ?? 1) : base);
1112
+ }
1113
+ return out;
1114
+ }
1115
+ function numbersMatch(a, b) {
1116
+ if (a === b) return true;
1117
+ if (a === 0 || b === 0) return Math.abs(a - b) < 0.5;
1118
+ return Math.abs(a - b) / Math.max(Math.abs(a), Math.abs(b)) <= 0.02;
1119
+ }
1120
+ function baselineAppearsInEvidence(value, evidenceNumbers) {
1121
+ const valueNumbers = extractNumbers(value);
1122
+ if (valueNumbers.length === 0) return false;
1123
+ return valueNumbers.some((v) => evidenceNumbers.some((e) => numbersMatch(v, e)));
1124
+ }
1125
+ function isKnownInstrument(measuredBy) {
1126
+ const lower = measuredBy.toLowerCase();
1127
+ return INSTRUMENT_TOKENS.some((token) => lower.includes(token));
1128
+ }
1129
+ function parseIsoDate(value) {
1130
+ if (typeof value !== "string") return null;
1131
+ const match = value.trim().match(ISO_DATE_RE);
1132
+ if (!match) return null;
1133
+ const date = /* @__PURE__ */ new Date(`${match[1]}-${match[2]}-${match[3]}T00:00:00Z`);
1134
+ return Number.isNaN(date.getTime()) ? null : date;
1135
+ }
1136
+ function toIso(date) {
1137
+ return date.toISOString().slice(0, 10);
1138
+ }
1139
+ function addDays(date, days) {
1140
+ const out = new Date(date);
1141
+ out.setUTCDate(out.getUTCDate() + days);
1142
+ return out;
1143
+ }
1144
+ function normalizeDate(value, today2, fallbackDays) {
1145
+ const parsed = parseIsoDate(value);
1146
+ if (parsed && parsed.getTime() >= today2.getTime()) {
1147
+ return { iso: toIso(parsed), repaired: false };
1148
+ }
1149
+ return { iso: toIso(addDays(today2, fallbackDays)), repaired: true };
1150
+ }
1151
+ function str(value) {
1152
+ return typeof value === "string" ? value.trim() : "";
1153
+ }
1154
+ function strArray(value) {
1155
+ if (!Array.isArray(value)) return [];
1156
+ return value.map(str).filter(Boolean);
1157
+ }
1158
+ function num(value, fallback) {
1159
+ const n = typeof value === "number" ? value : Number(value);
1160
+ return Number.isFinite(n) ? n : fallback;
1161
+ }
1162
+ function enumValue(value, allowed, fallback) {
1163
+ return typeof value === "string" && allowed.includes(value) ? value : fallback;
1164
+ }
1165
+ function hasNumber(text) {
1166
+ return extractNumbers(text).length > 0;
1167
+ }
1168
+ function validateOutcome(raw, label, evidenceNumbers, today2, fallbackDays) {
1169
+ if (!raw || typeof raw !== "object") {
1170
+ return { outcome: null, measurable: false, notes: [`${label}: missing \u2014 excluded from targets`] };
1171
+ }
1172
+ const record = raw;
1173
+ const metric = str(record.metric);
1174
+ const baseline = str(record.baseline);
1175
+ const targetRange = str(record.target_range);
1176
+ const measuredBy = str(record.measured_by);
1177
+ if (!metric) {
1178
+ return { outcome: null, measurable: false, notes: [`${label}: no metric named \u2014 excluded from targets`] };
1179
+ }
1180
+ const notes = [];
1181
+ let measurable = true;
1182
+ if (!hasNumber(baseline)) {
1183
+ measurable = false;
1184
+ notes.push(`${label} "${metric}": baseline is not a number \u2014 demoted to assumption`);
1185
+ } else if (!baselineAppearsInEvidence(baseline, evidenceNumbers)) {
1186
+ measurable = false;
1187
+ notes.push(`${label} "${metric}": baseline ${baseline} not found in grounded evidence \u2014 demoted to assumption`);
1188
+ }
1189
+ if (!hasNumber(targetRange)) {
1190
+ measurable = false;
1191
+ notes.push(`${label} "${metric}": target range has no number \u2014 demoted to assumption`);
1192
+ }
1193
+ if (!measuredBy || !isKnownInstrument(measuredBy)) {
1194
+ measurable = false;
1195
+ notes.push(`${label} "${metric}": measured_by "${measuredBy || "(empty)"}" is not a known instrument \u2014 demoted to assumption`);
1196
+ }
1197
+ const checkDate = normalizeDate(record.check_date, today2, fallbackDays);
1198
+ if (checkDate.repaired) {
1199
+ notes.push(`${label} "${metric}": check date repaired to ${checkDate.iso}`);
1200
+ }
1201
+ return {
1202
+ outcome: {
1203
+ metric,
1204
+ baseline: baseline || "unknown",
1205
+ target_range: targetRange || "unquantified",
1206
+ check_date: checkDate.iso,
1207
+ measured_by: measuredBy || "unspecified"
1208
+ },
1209
+ measurable,
1210
+ notes
1211
+ };
1212
+ }
1213
+ function validateMilestones(raw, today2, issues, workstreamTitle) {
1214
+ const out = [];
1215
+ if (Array.isArray(raw)) {
1216
+ for (const item of raw) {
1217
+ if (!item || typeof item !== "object") continue;
1218
+ const record = item;
1219
+ const label = str(record.label);
1220
+ const verification = str(record.verification);
1221
+ if (!label) continue;
1222
+ const due = normalizeDate(record.due, today2, 14);
1223
+ if (due.repaired) {
1224
+ issues.push(`Milestone "${label}" (${workstreamTitle}): due date repaired to ${due.iso}`);
1225
+ }
1226
+ if (!verification) {
1227
+ issues.push(`Milestone "${label}" (${workstreamTitle}): no verification method \u2014 flagged`);
1228
+ }
1229
+ out.push({ label, due: due.iso, verification: verification || "Verification method not specified \u2014 define before activating" });
1230
+ }
1231
+ }
1232
+ return out;
1233
+ }
1234
+ function validateDeliverables(raw, today2) {
1235
+ const out = [];
1236
+ if (Array.isArray(raw)) {
1237
+ for (const item of raw) {
1238
+ if (!item || typeof item !== "object") continue;
1239
+ const record = item;
1240
+ const label = str(record.label);
1241
+ if (!label) continue;
1242
+ out.push({
1243
+ label,
1244
+ kind: enumValue(record.kind, ["artifact", "process_change", "decision"], "artifact"),
1245
+ due: normalizeDate(record.due, today2, 21).iso
1246
+ });
1247
+ }
1248
+ }
1249
+ return out;
1250
+ }
1251
+ function validateContingency(raw, today2) {
1252
+ if (!raw || typeof raw !== "object") return null;
1253
+ const record = raw;
1254
+ const trigger = str(record.trigger);
1255
+ const fallback = str(record.fallback);
1256
+ if (!trigger || !fallback) return null;
1257
+ return {
1258
+ trigger,
1259
+ trigger_check_date: normalizeDate(record.trigger_check_date, today2, 21).iso,
1260
+ fallback
1261
+ };
1262
+ }
1263
+ function validPlayIds(value) {
1264
+ const allowed = new Set(getPlaybook().map((play) => play.id));
1265
+ return strArray(value).filter((id) => allowed.has(id));
1266
+ }
1267
+ function validateStrategistPlan(raw, opts) {
1268
+ const today2 = parseIsoDate(opts.todayIso) ?? /* @__PURE__ */ new Date();
1269
+ const evidenceNumbers = extractNumbers(opts.evidenceText);
1270
+ const issues = [];
1271
+ const demotedAssumptions = [];
1272
+ let measurableTargets = 0;
1273
+ let totalTargets = 0;
1274
+ const rawWorkstreams = Array.isArray(raw.workstreams) ? raw.workstreams : [];
1275
+ const workstreams = [];
1276
+ for (const [index, item] of rawWorkstreams.entries()) {
1277
+ if (!item || typeof item !== "object") continue;
1278
+ const record = item;
1279
+ const title = str(record.title) || `Workstream ${index + 1}`;
1280
+ const outcomeCheck = validateOutcome(record.expected_outcome, "Expected outcome", evidenceNumbers, today2, 30);
1281
+ totalTargets += outcomeCheck.outcome ? 1 : 0;
1282
+ if (outcomeCheck.measurable) measurableTargets++;
1283
+ for (const note of outcomeCheck.notes) issues.push(`${title}: ${note}`);
1284
+ if (outcomeCheck.outcome && !outcomeCheck.measurable) {
1285
+ demotedAssumptions.push(
1286
+ `Unverified expectation (${title}): ${outcomeCheck.outcome.metric} ${outcomeCheck.outcome.baseline} -> ${outcomeCheck.outcome.target_range} \u2014 quantify against grounded data before treating as a target`
1287
+ );
1288
+ }
1289
+ const leadingIndicators = [];
1290
+ if (Array.isArray(record.leading_indicators)) {
1291
+ for (const li of record.leading_indicators) {
1292
+ const check = validateOutcome(li, "Leading indicator", evidenceNumbers, today2, 14);
1293
+ if (!check.outcome) continue;
1294
+ totalTargets++;
1295
+ if (check.measurable) {
1296
+ measurableTargets++;
1297
+ leadingIndicators.push(check.outcome);
1298
+ } else {
1299
+ for (const note of check.notes) issues.push(`${title}: ${note}`);
1300
+ demotedAssumptions.push(
1301
+ `Unverified indicator (${title}): ${check.outcome.metric} \u2014 not measurable as stated`
1302
+ );
1303
+ }
1304
+ }
1305
+ }
1306
+ let milestones = validateMilestones(record.milestones, today2, issues, title);
1307
+ if (milestones.length === 0 && outcomeCheck.outcome) {
1308
+ milestones = [{
1309
+ label: `Outcome check: ${outcomeCheck.outcome.metric}`,
1310
+ due: outcomeCheck.outcome.check_date,
1311
+ verification: `${outcomeCheck.outcome.measured_by} shows ${outcomeCheck.outcome.target_range}`
1312
+ }];
1313
+ issues.push(`${title}: no dated milestones \u2014 derived one from the expected outcome`);
1314
+ }
1315
+ if (!outcomeCheck.outcome && milestones.length === 0) {
1316
+ issues.push(`${title}: no measurable outcome and no milestones \u2014 workstream demoted to assumption`);
1317
+ demotedAssumptions.push(`Dropped workstream "${title}" \u2014 had no measurable outcome or dated milestone`);
1318
+ continue;
1319
+ }
1320
+ const contingency = validateContingency(record.contingency, today2);
1321
+ if (!contingency) {
1322
+ issues.push(`${title}: contingency missing or incomplete \u2014 flagged for definition at first review`);
1323
+ }
1324
+ workstreams.push({
1325
+ order: num(record.order, workstreams.length + 1),
1326
+ title,
1327
+ problem: str(record.problem) || "Problem statement not captured",
1328
+ rationale: str(record.rationale) || "Sequence rationale not captured",
1329
+ play_ids: validPlayIds(record.play_ids),
1330
+ actions: strArray(record.actions),
1331
+ effort_hours: Math.max(0, num(record.effort_hours, 8)),
1332
+ milestones,
1333
+ deliverables: validateDeliverables(record.deliverables, today2),
1334
+ expected_outcome: outcomeCheck.outcome ?? {
1335
+ metric: "unspecified",
1336
+ baseline: "unknown",
1337
+ target_range: "unquantified",
1338
+ check_date: toIso(addDays(today2, 30)),
1339
+ measured_by: "unspecified"
1340
+ },
1341
+ leading_indicators: leadingIndicators,
1342
+ contingency: contingency ?? {
1343
+ trigger: "Define trigger at first review",
1344
+ trigger_check_date: toIso(addDays(today2, 21)),
1345
+ fallback: "Define fallback at first review"
1346
+ }
1347
+ });
1348
+ }
1349
+ if (workstreams.length === 0) return null;
1350
+ workstreams.sort((a, b) => a.order - b.order);
1351
+ workstreams.forEach((ws, i) => {
1352
+ ws.order = i + 1;
1353
+ });
1354
+ const capped = workstreams.slice(0, 5);
1355
+ if (workstreams.length > 5) {
1356
+ issues.push(`Plan proposed ${workstreams.length} workstreams \u2014 capped to 5 (focus beats coverage)`);
1357
+ }
1358
+ const rawConfidence = Math.min(1, Math.max(0, num(raw.confidence, 0.6)));
1359
+ const confidence = Math.max(0.2, rawConfidence - demotedAssumptions.length * 0.05);
1360
+ const plan = {
1361
+ title: str(raw.title) || "Untitled Strategy",
1362
+ objective: str(raw.objective) || "Objective not restated",
1363
+ summary_30k: str(raw.summary_30k) || str(raw.hypothesis) || "Executive summary not captured",
1364
+ hypothesis: str(raw.hypothesis) || "If the workstreams execute in order, the objective metrics should move within their target ranges.",
1365
+ target_segment: str(raw.target_segment) || "Whole pipeline",
1366
+ priority: enumValue(raw.priority, ["low", "medium", "high"], "medium"),
1367
+ review_cadence: str(raw.review_cadence) || "Weekly",
1368
+ confidence,
1369
+ constraints: strArray(raw.constraints),
1370
+ assumptions: [...strArray(raw.assumptions), ...demotedAssumptions],
1371
+ risks: strArray(raw.risks),
1372
+ workstreams: capped
1373
+ };
1374
+ return { plan, issues, measurableTargets, totalTargets };
1375
+ }
1376
+ var NUMBER_RE, SUFFIX_MULTIPLIER, INSTRUMENT_TOKENS, ISO_DATE_RE;
1377
+ var init_strategist_validate = __esm({
1378
+ "src/ai/strategist-validate.ts"() {
1379
+ "use strict";
1380
+ init_playbook();
1381
+ init_json_response();
1382
+ NUMBER_RE = /\$?\s*(\d[\d,]*\.?\d*)\s*(m|k|b|million|thousand|billion)?\b/gi;
1383
+ SUFFIX_MULTIPLIER = {
1384
+ k: 1e3,
1385
+ thousand: 1e3,
1386
+ m: 1e6,
1387
+ million: 1e6,
1388
+ b: 1e9,
1389
+ billion: 1e9
1390
+ };
1391
+ INSTRUMENT_TOKENS = [
1392
+ // vital signs + components
1393
+ "freshness",
1394
+ "flow_rate",
1395
+ "flow rate",
1396
+ "drop_rate",
1397
+ "drop rate",
1398
+ "signal_to_noise",
1399
+ "signal-to-noise",
1400
+ "signal to noise",
1401
+ "thread_depth",
1402
+ "thread depth",
1403
+ "health score",
1404
+ "vital",
1405
+ "gating",
1406
+ "score",
1407
+ // SaaS metrics lane
1408
+ "arr",
1409
+ "nrr",
1410
+ "grr",
1411
+ "net revenue retention",
1412
+ "gross revenue retention",
1413
+ "win rate",
1414
+ "pipeline coverage",
1415
+ "weighted pipeline",
1416
+ "velocity",
1417
+ "deal size",
1418
+ "sales cycle",
1419
+ "conversion",
1420
+ "ltv",
1421
+ "cac",
1422
+ "payback",
1423
+ "magic number",
1424
+ "churn",
1425
+ "expansion",
1426
+ "retention",
1427
+ // countable pipeline entities + states
1428
+ "count",
1429
+ "stale",
1430
+ "stuck",
1431
+ "single-threaded",
1432
+ "single threaded",
1433
+ "zombie",
1434
+ "dollar",
1435
+ "pipeline at risk",
1436
+ "opportunit",
1437
+ "deal",
1438
+ "activity",
1439
+ "activities",
1440
+ "contact",
1441
+ "lead",
1442
+ "account",
1443
+ "segment",
1444
+ "handoff",
1445
+ "past-due",
1446
+ "past due",
1447
+ // the review instrument itself
1448
+ "strategy review",
1449
+ "metric_readings",
1450
+ "vital_sign_readings",
1451
+ "entity count"
1452
+ ];
1453
+ ISO_DATE_RE = /^(\d{4})-(\d{2})-(\d{2})/;
1454
+ }
1455
+ });
1456
+
1457
+ // src/ai/strategist.ts
1458
+ var init_strategist2 = __esm({
1459
+ "src/ai/strategist.ts"() {
1460
+ "use strict";
1461
+ init_failover();
1462
+ init_surfaces();
1463
+ init_llm_config();
1464
+ init_repl_api();
1465
+ init_tool_schemas();
1466
+ init_web_search();
1467
+ init_tool_handlers();
1468
+ init_strategist_prompt();
1469
+ init_strategist_validate();
1470
+ }
1471
+ });
1472
+
1473
+ // src/ui/layout.ts
1474
+ var init_layout = __esm({
1475
+ "src/ui/layout.ts"() {
1476
+ "use strict";
1477
+ }
1478
+ });
1479
+
1480
+ // src/output/strategy-brief.ts
1481
+ import chalk4 from "chalk";
1482
+ var init_strategy_brief = __esm({
1483
+ "src/output/strategy-brief.ts"() {
1484
+ "use strict";
1485
+ init_theme();
1486
+ init_layout();
1487
+ }
1488
+ });
1489
+
1490
+ // src/output/llm-attribution.ts
1491
+ import chalk5 from "chalk";
1492
+ var init_llm_attribution = __esm({
1493
+ "src/output/llm-attribution.ts"() {
1494
+ "use strict";
1495
+ init_catalog();
1496
+ }
1497
+ });
1498
+
1499
+ // src/whimsy/time-milestones.ts
1500
+ var init_time_milestones = __esm({
1501
+ "src/whimsy/time-milestones.ts"() {
1502
+ "use strict";
1503
+ }
1504
+ });
1505
+
1506
+ // src/whimsy/time-perspectives.ts
1507
+ var TIME_PERSPECTIVES;
1508
+ var init_time_perspectives = __esm({
1509
+ "src/whimsy/time-perspectives.ts"() {
1510
+ "use strict";
1511
+ TIME_PERSPECTIVES = [
1512
+ { id: "dsotm", category: "music", reference_hours: 0.74, label: "Dark Side of the Moon", template: "\u2248 {ratio}\xD7 through {label}", min_ratio: 1, max_ratio: 200 },
1513
+ { id: "rush_2112", category: "music", reference_hours: 0.33, label: "2112", template: "\u2248 {ratio}\xD7 through {label}", min_ratio: 2, max_ratio: 200 },
1514
+ { id: "bohemian_rhapsody", category: "music", reference_hours: 0.1, label: "Bohemian Rhapsody", template: "\u2248 {ratio}\xD7 through {label}", min_ratio: 5, max_ratio: 500 },
1515
+ { id: "stairway", category: "music", reference_hours: 0.13, label: "Stairway to Heaven", template: "\u2248 {ratio}\xD7 through {label}", min_ratio: 5, max_ratio: 400 },
1516
+ { id: "podcast_binge", category: "music", reference_hours: 0.75, label: "hour-long podcast episodes", template: "\u2248 {ratio}\xD7 {label}", min_ratio: 2, max_ratio: 300 },
1517
+ { id: "abbey_road", category: "music", reference_hours: 0.8, label: "Abbey Road", template: "\u2248 {ratio}\xD7 through {label}", min_ratio: 1, max_ratio: 200 },
1518
+ { id: "iron_maiden_set", category: "music", reference_hours: 2, label: "an Iron Maiden marathon set", template: "\u2248 {ratio}\xD7 {label}", min_ratio: 0.5, max_ratio: 50 },
1519
+ { id: "festival_set", category: "music", reference_hours: 1.5, label: "main-stage festival sets", template: "\u2248 {ratio}\xD7 {label}", min_ratio: 1, max_ratio: 100 },
1520
+ { id: "jazz_club", category: "music", reference_hours: 3, label: "late-night jazz sets", template: "\u2248 {ratio}\xD7 {label}", min_ratio: 0.5, max_ratio: 80 },
1521
+ { id: "ring_cycle", category: "music", reference_hours: 15, label: "Wagner's Ring Cycle", template: "\u2248 {pct}% of {label}", min_ratio: 0.1, max_ratio: 2 },
1522
+ { id: "shrek", category: "film", reference_hours: 1.5, label: "Shrek (the first one)", template: "\u2248 {ratio}\xD7 watching {label}", min_ratio: 1, max_ratio: 150 },
1523
+ { id: "blockbuster", category: "film", reference_hours: 2.1, label: "average blockbusters", template: "\u2248 {ratio}\xD7 {label}", min_ratio: 1, max_ratio: 100 },
1524
+ { id: "dune_two", category: "film", reference_hours: 2.75, label: "Dune: Part Two", template: "\u2248 {ratio}\xD7 in theater for {label}", min_ratio: 1, max_ratio: 100 },
1525
+ { id: "scorsese", category: "film", reference_hours: 3.5, label: "Goodfellas", template: "\u2248 {ratio}\xD7 {label}", min_ratio: 1, max_ratio: 80 },
1526
+ { id: "godfather", category: "film", reference_hours: 6.5, label: "the Godfather saga", template: "\u2248 {ratio}\xD7 {label}", min_ratio: 0.5, max_ratio: 20 },
1527
+ { id: "lotr_extended", category: "film", reference_hours: 11.4, label: "the LOTR extended trilogy", template: "Longer than all of {label}", min_ratio: 1, max_ratio: 50 },
1528
+ { id: "cooking_brisket", category: "film", reference_hours: 12, label: "low-and-slow brisket cooks", template: "\u2248 {ratio}\xD7 {label}", min_ratio: 0.3, max_ratio: 30 },
1529
+ { id: "the_office", category: "film", reference_hours: 68, label: "The Office (full series)", template: "\u2248 {ratio}\xD7 bingeing {label}", min_ratio: 5, max_ratio: 200 },
1530
+ { id: "marvel_marathon", category: "film", reference_hours: 50, label: "an MCU Phase One marathon", template: "\u2248 {ratio}\xD7 {label}", min_ratio: 0.3, max_ratio: 30 },
1531
+ { id: "around_world", category: "film", reference_hours: 1920, label: "Around the World in 80 Days (fictionally)", template: "\u2248 {pct}% of {label}", min_ratio: 0.3, max_ratio: 1 },
1532
+ { id: "soccer_match", category: "sports", reference_hours: 1.75, label: "Premier League matches", template: "\u2248 {ratio}\xD7 {label}", min_ratio: 1, max_ratio: 150 },
1533
+ { id: "marathon", category: "sports", reference_hours: 2, label: "marathons at world-record pace", template: "\u2248 {ratio}\xD7 {label}", min_ratio: 1, max_ratio: 80 },
1534
+ { id: "baseball_game", category: "sports", reference_hours: 3, label: "nine-inning baseball games", template: "\u2248 {ratio}\xD7 {label}", min_ratio: 0.5, max_ratio: 80 },
1535
+ { id: "superbowl", category: "sports", reference_hours: 3.5, label: "Super Bowls", template: "\u2248 {ratio}\xD7 {label}", min_ratio: 0.5, max_ratio: 80 },
1536
+ { id: "nfl_game", category: "sports", reference_hours: 3.25, label: "NFL games (with commercials)", template: "\u2248 {ratio}\xD7 {label}", min_ratio: 0.5, max_ratio: 50 },
1537
+ { id: "wimbledon", category: "sports", reference_hours: 5, label: "Wimbledon finals", template: "\u2248 {ratio}\xD7 {label}", min_ratio: 0.3, max_ratio: 30 },
1538
+ { id: "tour_stage", category: "sports", reference_hours: 4.5, label: "Tour de France stages", template: "\u2248 {ratio}\xD7 {label}", min_ratio: 0.5, max_ratio: 60 },
1539
+ { id: "olympics", category: "sports", reference_hours: 250, label: "Summer Olympics broadcast hours", template: "\u2248 {ratio}\xD7 {label}", min_ratio: 2, max_ratio: 10 },
1540
+ { id: "moon_light", category: "cosmos", reference_hours: 1.3 / 3600, label: "a beam of light Earth \u2192 Moon", template: "\u2248 {ratio}\xD7 {label}", min_ratio: 1e3, max_ratio: 1e6 },
1541
+ { id: "iss_orbit", category: "cosmos", reference_hours: 1.5, label: "ISS orbits", template: "\u2248 {ratio}\xD7 {label}", min_ratio: 2, max_ratio: 200 },
1542
+ { id: "light_sun", category: "cosmos", reference_hours: 8.3, label: "solar light crossing to Earth", template: "\u2248 {ratio}\xD7 {label}", min_ratio: 0.5, max_ratio: 200 },
1543
+ { id: "sleep_cycle", category: "cosmos", reference_hours: 8, label: "full nights of sleep", template: "\u2248 {ratio}\xD7 {label}", min_ratio: 0.5, max_ratio: 150 },
1544
+ { id: "red_eye", category: "cosmos", reference_hours: 5.5, label: "transcontinental red-eyes", template: "\u2248 {ratio}\xD7 {label}", min_ratio: 0.5, max_ratio: 100 },
1545
+ { id: "mayfly", category: "cosmos", reference_hours: 24, label: "a mayfly's entire adult life", template: "\u2248 {pct}% of {label}", min_ratio: 0.1, max_ratio: 2 },
1546
+ { id: "earth_rotation", category: "cosmos", reference_hours: 24, label: "Earth rotations", template: "\u2248 {ratio}\xD7 {label}", min_ratio: 0.1, max_ratio: 50 },
1547
+ { id: "jupiter_storm", category: "cosmos", reference_hours: 150, label: "Jupiter's Great Red Spot rotation", template: "\u2248 {ratio}\xD7 {label}", min_ratio: 0.3, max_ratio: 15 },
1548
+ { id: "lunar_month", category: "cosmos", reference_hours: 708, label: "a lunar cycle", template: "\u2248 {pct}% of {label}", min_ratio: 0.05, max_ratio: 2 },
1549
+ { id: "mars_transit", category: "cosmos", reference_hours: 5110, label: "a one-way Mars transit (optimistic)", template: "\u2248 {pct}% of {label}", min_ratio: 1e-3, max_ratio: 5 },
1550
+ { id: "calendar_year", category: "cosmos", reference_hours: 8760, label: "all the hours in a calendar year", template: "\u2248 {pct}% of {label}", min_ratio: 0.05, max_ratio: 0.2 },
1551
+ { id: "standup", category: "gtm", reference_hours: 0.25, label: "daily standups", template: "\u2248 {ratio}\xD7 skipped {label}", min_ratio: 4, max_ratio: 500 },
1552
+ { id: "quick_sync", category: "gtm", reference_hours: 0.5, label: "avoided 'quick syncs'", template: "\u2248 {ratio}\xD7 {label}", min_ratio: 2, max_ratio: 200 },
1553
+ { id: "pipeline_review", category: "gtm", reference_hours: 1, label: "weekly pipeline reviews", template: "\u2248 {ratio}\xD7 {label}", min_ratio: 1, max_ratio: 200 },
1554
+ { id: "forecast_call", category: "gtm", reference_hours: 1.5, label: "forecast calls", template: "\u2248 {ratio}\xD7 {label}", min_ratio: 1, max_ratio: 150 },
1555
+ { id: "pivot_spiral", category: "gtm", reference_hours: 2, label: "spreadsheet pivot-table spirals", template: "\u2248 {ratio}\xD7 {label}", min_ratio: 1, max_ratio: 100 },
1556
+ { id: "win_loss", category: "gtm", reference_hours: 4, label: "win/loss interview blocks", template: "\u2248 {ratio}\xD7 {label}", min_ratio: 0.5, max_ratio: 50 },
1557
+ { id: "crm_cleanup", category: "gtm", reference_hours: 6, label: "CRM hygiene sprints", template: "\u2248 {ratio}\xD7 {label}", min_ratio: 0.5, max_ratio: 40 },
1558
+ { id: "qbr_prep", category: "gtm", reference_hours: 8, label: "QBR prep blocks", template: "\u2248 {ratio}\xD7 {label}", min_ratio: 0.3, max_ratio: 20 },
1559
+ { id: "board_deck", category: "gtm", reference_hours: 12, label: "board deck builds", template: "\u2248 {ratio}\xD7 {label}", min_ratio: 0.3, max_ratio: 30 },
1560
+ { id: "semester", category: "gtm", reference_hours: 400, label: "a college semester of analyst coverage", template: "\u2248 {ratio}\xD7 {label}", min_ratio: 1, max_ratio: 5 },
1561
+ { id: "business_year", category: "gtm", reference_hours: 2e3, label: "a full-time analyst year", template: "\u2248 {pct}% of {label}", min_ratio: 0.2, max_ratio: 1 }
1562
+ ];
1563
+ }
1564
+ });
1565
+
1566
+ // src/whimsy/time-bank-whimsy.ts
1567
+ var init_time_bank_whimsy = __esm({
1568
+ "src/whimsy/time-bank-whimsy.ts"() {
1569
+ "use strict";
1570
+ }
1571
+ });
1572
+
1573
+ // src/whimsy/perspective-rotation.ts
1574
+ var PERSPECTIVE_ROTATE_CALENDAR_MS;
1575
+ var init_perspective_rotation = __esm({
1576
+ "src/whimsy/perspective-rotation.ts"() {
1577
+ "use strict";
1578
+ PERSPECTIVE_ROTATE_CALENDAR_MS = 7 * 24 * 60 * 60 * 1e3;
1579
+ }
1580
+ });
1581
+
1582
+ // src/whimsy/time-bank.ts
1583
+ import chalk6 from "chalk";
1584
+ var init_time_bank = __esm({
1585
+ "src/whimsy/time-bank.ts"() {
1586
+ "use strict";
1587
+ init_progress();
1588
+ init_theme();
1589
+ init_time_milestones();
1590
+ init_time_perspectives();
1591
+ init_time_bank_whimsy();
1592
+ init_perspective_rotation();
1593
+ init_usage_stats();
1594
+ }
1595
+ });
1596
+
1597
+ // src/conversation/strategist-flow.ts
1598
+ import ora from "ora";
1599
+ import chalk7 from "chalk";
1600
+ function isStrategistIntent(input) {
1601
+ const line = input.trim();
1602
+ if (!line) return false;
1603
+ if (isShipIntent(line)) return false;
1604
+ return STRATEGIST_INTENT_RE.test(line);
1605
+ }
1606
+ function extractObjectiveSeed(input) {
1607
+ const cleaned = input.trim().replace(/^(hey|ok|okay|please|can you|could you|help me|let'?s|i want to|i'?d like to|i need to)\s+/i, "").replace(/^(build|draft|make|create|put together)\s+(me\s+)?(a\s+|the\s+)?(game\s?plan|battle\s?plan|strategy|plan|roadmap)\s*(to|for|around|on)?\s*/i, "").replace(/^strategize\s+(about|around|on|for)?\s*/i, "").trim();
1608
+ return cleaned.length >= 8 ? cleaned : input.trim();
1609
+ }
1610
+ var STRATEGIST_INTENT_RE;
1611
+ var init_strategist_flow = __esm({
1612
+ "src/conversation/strategist-flow.ts"() {
1613
+ "use strict";
1614
+ init_context2();
1615
+ init_handoff_draft();
1616
+ init_repl_api();
1617
+ init_theme();
1618
+ init_prompts();
1619
+ init_health_score();
1620
+ init_divergence();
1621
+ init_strategist();
1622
+ init_strategist2();
1623
+ init_strategy_brief();
1624
+ init_llm_attribution();
1625
+ init_time_bank();
1626
+ init_formatters();
1627
+ STRATEGIST_INTENT_RE = /\b(strateg(y|ize|ic)|game\s?plan|battle\s?plan|roadmap|(build|draft|make|create|put together)\s+(me\s+)?(a\s+|the\s+)?plan\b|plan\s+(to|for)\s+(fix|improv|reduc|recover|hit|reach|get|grow|turn)|how\s+(should|do|can)\s+we\s+(fix|approach|tackle|attack|prioritize|sequence|turn\s+(this|it)\s+around)|what\s+should\s+we\s+(do|fix|tackle|prioritize|focus\s+on)\s+(first|next)|what\s+order\s+should|where\s+(do|should)\s+we\s+start)\b/i;
1628
+ }
1629
+ });
1630
+
1631
+ // src/strategist/strategist-smoke.ts
1632
+ init_strategist_flow();
1633
+ init_phase();
1634
+ init_context2();
1635
+ init_strategist_validate();
1636
+ init_playbook();
1637
+ init_health_score();
1638
+ var failures = [];
1639
+ function assert(cond, msg) {
1640
+ if (!cond) failures.push(msg);
1641
+ }
1642
+ assert(isStrategistIntent("how should we fix drop rate before the board?"), "strategist intent: how should we fix");
1643
+ assert(isStrategistIntent("build me a game plan to recover pipeline"), "strategist intent: game plan");
1644
+ assert(isStrategistIntent("what should we prioritize first"), "strategist intent: prioritize");
1645
+ assert(!isStrategistIntent("ship a board deck"), "ship intent must not match strategist");
1646
+ assert(!isStrategistIntent("what is our NRR?"), "descriptive question must not match strategist");
1647
+ var seed = extractObjectiveSeed("help me build a plan to fix stale pipeline");
1648
+ assert(seed.includes("stale pipeline"), "objective seed strips lead-in verbs");
1649
+ function mockCtx(partial) {
1650
+ return {
1651
+ sessionId: "2026-07-03-test",
1652
+ sessionFile: "/tmp/test.json",
1653
+ oneShot: false,
1654
+ execution: { mode: "interactive", output: "terminal", color: true, progress: true, quiet: false },
1655
+ snapshot: { computeResult: null, divergences: [] },
1656
+ messages: [],
1657
+ conversation: [],
1658
+ stage: "analyzed",
1659
+ deliverables: [],
1660
+ analysis: defaultSessionAnalysis(),
1661
+ wizardDepth: 0,
1662
+ attachments: [],
1663
+ deliverIntent: false,
1664
+ computeInProgress: false,
1665
+ dataset: { counts: { opportunities: 10 } },
1666
+ ...partial
1667
+ };
1668
+ }
1669
+ assert(
1670
+ resolveConversationPhase(mockCtx({ strategistState: { step: "objective_confirm", objective: "fix freshness" } })) === "strategize",
1671
+ "objective_confirm \u2192 strategize phase"
1672
+ );
1673
+ assert(
1674
+ resolveConversationPhase(
1675
+ mockCtx({
1676
+ strategistState: { step: "awaiting_analysis", objective: "fix pipeline" },
1677
+ stage: "new",
1678
+ scope: { intent_summary: "health", primary_lens: "gtm_health", confirmed_at: (/* @__PURE__ */ new Date()).toISOString() }
1679
+ })
1680
+ ) === "awaiting_data",
1681
+ "awaiting_analysis rides normal funnel (not strategize)"
1682
+ );
1683
+ var evidence = JSON.stringify({
1684
+ vital_signs: { freshness: { score: 29, dollar_value: 31e5 } }
1685
+ });
1686
+ var today = "2026-07-03";
1687
+ var validPlan = {
1688
+ title: "Q4 Pipeline Recovery",
1689
+ objective: "Cut stale pipeline before board",
1690
+ summary_30k: "Freshness gates everything.",
1691
+ hypothesis: "Cleaning stale deals unlocks flow fixes.",
1692
+ target_segment: "Enterprise pipeline",
1693
+ priority: "high",
1694
+ review_cadence: "Weekly",
1695
+ confidence: 0.7,
1696
+ constraints: ["6 rep-hours/week"],
1697
+ assumptions: [],
1698
+ risks: ["Rep capacity"],
1699
+ workstreams: [
1700
+ {
1701
+ order: 1,
1702
+ title: "Clean dead pipeline",
1703
+ problem: "Stale deals block trust in the pipeline number",
1704
+ rationale: "Freshness is the gating vital sign",
1705
+ play_ids: ["clean-dead-pipeline"],
1706
+ actions: ["Pull stale list", "Re-engage or close"],
1707
+ effort_hours: 12,
1708
+ milestones: [{ label: "Stale list triaged", due: "2026-07-17", verification: "stale count < 40" }],
1709
+ deliverables: [{ label: "Re-engagement sequence", kind: "artifact", due: "2026-07-24" }],
1710
+ expected_outcome: {
1711
+ metric: "freshness score",
1712
+ baseline: "29",
1713
+ target_range: "45\u201355",
1714
+ check_date: "2026-08-01",
1715
+ measured_by: "freshness vital sign score"
1716
+ },
1717
+ leading_indicators: [
1718
+ {
1719
+ metric: "stale deal count",
1720
+ baseline: "120",
1721
+ target_range: "80\u201390",
1722
+ check_date: "2026-07-20",
1723
+ measured_by: "freshness entity_details stale count"
1724
+ }
1725
+ ],
1726
+ contingency: {
1727
+ trigger: "stale count flat by week 2",
1728
+ trigger_check_date: "2026-07-20",
1729
+ fallback: "Descope to top-2 segments only"
1730
+ }
1731
+ }
1732
+ ]
1733
+ };
1734
+ var validated = validateStrategistPlan(validPlan, { evidenceText: evidence + " score 29 dollar 3100000 stale 120", todayIso: today });
1735
+ assert(validated !== null, "valid plan parses");
1736
+ assert(validated.plan.workstreams.length === 1, "valid plan keeps workstream");
1737
+ assert(validated.measurableTargets >= 1, "valid plan has measurable targets");
1738
+ assert(isKnownInstrument("freshness vital sign score"), "freshness is known instrument");
1739
+ var vaguePlan = {
1740
+ ...validPlan,
1741
+ workstreams: [
1742
+ {
1743
+ ...validPlan.workstreams[0],
1744
+ expected_outcome: {
1745
+ metric: "team morale",
1746
+ baseline: "low",
1747
+ target_range: "much better",
1748
+ check_date: "2026-08-01",
1749
+ measured_by: "gut feel"
1750
+ }
1751
+ }
1752
+ ]
1753
+ };
1754
+ var demoted = validateStrategistPlan(vaguePlan, { evidenceText: evidence, todayIso: today });
1755
+ assert(demoted !== null, "vague plan still returns structure");
1756
+ assert(demoted.plan.assumptions.length > 0, "vague outcome demoted to assumptions");
1757
+ var vitals = ["freshness", "flow_rate", "thread_depth"].map((sign) => ({
1758
+ vital_sign: sign,
1759
+ score: sign === "freshness" ? 29 : sign === "flow_rate" ? 40 : 70,
1760
+ status: sign === "freshness" ? "red" : "yellow",
1761
+ dollar_value: sign === "freshness" ? 31e5 : null,
1762
+ dollar_label: sign === "freshness" ? "pipeline at risk" : null
1763
+ }));
1764
+ var triggered = matchTriggeredPlays(vitals, LAYERS);
1765
+ assert(triggered.length >= 1, "keyless skeleton triggers at least one play");
1766
+ assert(triggered[0].layer === 1, "first triggered play respects LAYERS order (freshness first)");
1767
+ var nums = extractNumbers("$3.1M stale pipeline, 120 deals");
1768
+ assert(nums.some((n) => n >= 3e6), "extractNumbers parses $3.1M");
1769
+ if (failures.length > 0) {
1770
+ console.error("FAIL strategist-smoke:");
1771
+ for (const f of failures) console.error(" -", f);
1772
+ process.exit(1);
1773
+ }
1774
+ console.log("PASS strategist-smoke");
1775
+ //# sourceMappingURL=strategist-smoke.js.map