@sonnechasser/ntrp 0.2.0 → 0.2.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 +77 -11
- package/dist/index.js.map +1 -1
- package/dist/investigation/verbosity-cli.js +1007 -198
- package/dist/investigation/verbosity-cli.js.map +1 -1
- package/dist/mcp/server.js +1992 -1133
- package/dist/mcp/server.js.map +1 -1
- package/dist/whimsy/time-bank-smoke.js +255 -199
- package/dist/whimsy/time-bank-smoke.js.map +1 -1
- package/package.json +1 -1
|
@@ -8279,6 +8279,736 @@ var init_playbook = __esm({
|
|
|
8279
8279
|
}
|
|
8280
8280
|
});
|
|
8281
8281
|
|
|
8282
|
+
// src/workflows/registry.ts
|
|
8283
|
+
function parseFrontmatter(raw) {
|
|
8284
|
+
if (!raw.startsWith("---")) return { meta: {}, body: raw };
|
|
8285
|
+
const end = raw.indexOf("\n---", 3);
|
|
8286
|
+
if (end === -1) return { meta: {}, body: raw };
|
|
8287
|
+
const fm = raw.slice(3, end).trim();
|
|
8288
|
+
const body = raw.slice(end + 4).replace(/^\r?\n/, "");
|
|
8289
|
+
const meta = {};
|
|
8290
|
+
for (const line of fm.split("\n")) {
|
|
8291
|
+
const match = line.match(/^(\w+):\s*(.*)$/);
|
|
8292
|
+
if (!match) continue;
|
|
8293
|
+
let value = match[2].trim();
|
|
8294
|
+
if (value.startsWith('"') && value.endsWith('"') || value.startsWith("'") && value.endsWith("'")) {
|
|
8295
|
+
value = value.slice(1, -1);
|
|
8296
|
+
}
|
|
8297
|
+
meta[match[1]] = value;
|
|
8298
|
+
}
|
|
8299
|
+
return { meta, body: body.trim() };
|
|
8300
|
+
}
|
|
8301
|
+
function loadRegistry() {
|
|
8302
|
+
if (registry) return registry;
|
|
8303
|
+
registry = /* @__PURE__ */ new Map();
|
|
8304
|
+
for (const entry of EMBEDDED_WORKFLOWS) {
|
|
8305
|
+
const { meta: fm, body } = parseFrontmatter(entry.raw);
|
|
8306
|
+
const meta = {
|
|
8307
|
+
name: fm.name ?? entry.name,
|
|
8308
|
+
description: fm.description ?? "",
|
|
8309
|
+
section: fm.section ?? "Other",
|
|
8310
|
+
args: fm.args,
|
|
8311
|
+
handler: fm.handler ?? "",
|
|
8312
|
+
body,
|
|
8313
|
+
hidden: fm.hidden === "true"
|
|
8314
|
+
};
|
|
8315
|
+
registry.set(meta.name, { meta, handler: null });
|
|
8316
|
+
}
|
|
8317
|
+
return registry;
|
|
8318
|
+
}
|
|
8319
|
+
function listWorkflows(includeHidden = false) {
|
|
8320
|
+
const all2 = Array.from(loadRegistry().values()).map((e) => e.meta);
|
|
8321
|
+
return includeHidden ? all2 : all2.filter((m) => !m.hidden);
|
|
8322
|
+
}
|
|
8323
|
+
var registry, EMBEDDED_WORKFLOWS;
|
|
8324
|
+
var init_registry = __esm({
|
|
8325
|
+
"src/workflows/registry.ts"() {
|
|
8326
|
+
"use strict";
|
|
8327
|
+
registry = null;
|
|
8328
|
+
EMBEDDED_WORKFLOWS = [
|
|
8329
|
+
{
|
|
8330
|
+
name: "new",
|
|
8331
|
+
raw: `---
|
|
8332
|
+
name: new
|
|
8333
|
+
description: Start a new analysis \u2014 one menu picks data + first report
|
|
8334
|
+
section: Hidden
|
|
8335
|
+
hidden: true
|
|
8336
|
+
args: [<file.csv>] | --demo [--scenario <name>] | --empty [--lens health|metrics]
|
|
8337
|
+
handler: ../commands/new.ts
|
|
8338
|
+
---
|
|
8339
|
+
|
|
8340
|
+
Start a fresh point-in-time analysis. Interactive mode uses **one menu**: demo \u2192
|
|
8341
|
+
health, demo \u2192 metrics, your CSV, or empty. Loads data and runs the first report
|
|
8342
|
+
(formulas only \u2014 no AI unless you add \`--findings\` later). Demo metrics works
|
|
8343
|
+
without \`/onboard\`; your own CSV needs a profile for metrics calibration.
|
|
8344
|
+
After the report, **ask questions in plain English** \u2014 no slash needed.`
|
|
8345
|
+
},
|
|
8346
|
+
{
|
|
8347
|
+
name: "end",
|
|
8348
|
+
raw: `---
|
|
8349
|
+
name: end
|
|
8350
|
+
description: Close the current analysis without a handoff
|
|
8351
|
+
section: Start
|
|
8352
|
+
args:
|
|
8353
|
+
handler: ../commands/end.ts
|
|
8354
|
+
---
|
|
8355
|
+
|
|
8356
|
+
Mark the current session as finished even when you didn't produce a report or
|
|
8357
|
+
other output. It drops off the "in progress" list, saves your transcript and
|
|
8358
|
+
dataset anchor for later, and rotates you to a fresh empty session. Use
|
|
8359
|
+
\`/handoff\` instead when you want to ship something.`
|
|
8360
|
+
},
|
|
8361
|
+
{
|
|
8362
|
+
name: "session",
|
|
8363
|
+
raw: `---
|
|
8364
|
+
name: session
|
|
8365
|
+
description: Pick up or browse your analyses
|
|
8366
|
+
section: Hidden
|
|
8367
|
+
hidden: true
|
|
8368
|
+
args: [<id|name>] | new
|
|
8369
|
+
handler: ../commands/session.ts
|
|
8370
|
+
---
|
|
8371
|
+
|
|
8372
|
+
Move between your point-in-time analyses. With no arguments, lists your
|
|
8373
|
+
sessions with unfinished work (reached insight, never delivered) surfaced
|
|
8374
|
+
first. Pass a session id (or type the 4-char suffix after listing) to pick it
|
|
8375
|
+
back up \u2014 this rebinds its dataset and conversation so you continue exactly
|
|
8376
|
+
where you left off. \`new\` starts a fresh analysis.`
|
|
8377
|
+
},
|
|
8378
|
+
{
|
|
8379
|
+
name: "handoff",
|
|
8380
|
+
raw: `---
|
|
8381
|
+
name: handoff
|
|
8382
|
+
description: Turn the analysis into an output
|
|
8383
|
+
section: Start
|
|
8384
|
+
args: [report|notes|csv|publish|prompt] [deck|asana|clay|plan]
|
|
8385
|
+
handler: ../commands/handoff.ts
|
|
8386
|
+
---
|
|
8387
|
+
|
|
8388
|
+
Close the loop to action. Produce a markdown report, a notes export, CSV
|
|
8389
|
+
receipts, or a repository package \u2014 or generate a ready-to-paste prompt for
|
|
8390
|
+
another agent to build a review deck, an Asana project, a Clay table, or an
|
|
8391
|
+
action plan from this diagnosis. Producing an output marks the session
|
|
8392
|
+
delivered so it stops showing up as unfinished work.`
|
|
8393
|
+
},
|
|
8394
|
+
{
|
|
8395
|
+
name: "onboard",
|
|
8396
|
+
raw: `---
|
|
8397
|
+
name: onboard
|
|
8398
|
+
description: Set up your company profile
|
|
8399
|
+
section: Settings
|
|
8400
|
+
handler: ../commands/onboard.ts
|
|
8401
|
+
---
|
|
8402
|
+
|
|
8403
|
+
Run the first-run wizard to build a rich company profile. Configures one or
|
|
8404
|
+
two LLM engines (Anthropic and/or OpenAI), then asks
|
|
8405
|
+
a few seed questions and uses AI to draft industry, ICP, deal size, and stack
|
|
8406
|
+
guesses. Profile is stored at \`~/.ntrp/profile.json\` and flows into every
|
|
8407
|
+
AI surface (findings, NL answers, demo generation).`
|
|
8408
|
+
},
|
|
8409
|
+
{
|
|
8410
|
+
name: "sessions",
|
|
8411
|
+
raw: `---
|
|
8412
|
+
name: sessions
|
|
8413
|
+
description: Browse past session history
|
|
8414
|
+
section: More
|
|
8415
|
+
args: [list|show <id>]
|
|
8416
|
+
handler: ../commands/sessions.ts
|
|
8417
|
+
hidden: true
|
|
8418
|
+
---
|
|
8419
|
+
|
|
8420
|
+
List and inspect past REPL sessions. Shows session dates, AI-generated
|
|
8421
|
+
summaries, and exchange counts. Use \`show <id>\` to view the full
|
|
8422
|
+
conversation from a specific session.`
|
|
8423
|
+
},
|
|
8424
|
+
{
|
|
8425
|
+
name: "setup",
|
|
8426
|
+
raw: `---
|
|
8427
|
+
name: setup
|
|
8428
|
+
description: Configure NTRP for headless and agent use
|
|
8429
|
+
section: Settings
|
|
8430
|
+
args: check | agent [--profile <file|->]
|
|
8431
|
+
handler: ../commands/setup.ts
|
|
8432
|
+
---
|
|
8433
|
+
|
|
8434
|
+
Validate local readiness or configure NTRP non-interactively for automation.
|
|
8435
|
+
\`setup check --json\` reports license, profile, API key, database, and writable
|
|
8436
|
+
directory state. \`setup agent\` accepts a profile JSON file or direct flags \u2014
|
|
8437
|
+
\`--llm-key <key>\` auto-detects the provider from any pasted key
|
|
8438
|
+
(\`--llm-provider <id>\` to force one).`
|
|
8439
|
+
},
|
|
8440
|
+
{
|
|
8441
|
+
name: "update",
|
|
8442
|
+
raw: `---
|
|
8443
|
+
name: update
|
|
8444
|
+
description: Update NTRP to the latest version
|
|
8445
|
+
section: Settings
|
|
8446
|
+
handler: ../commands/update.ts
|
|
8447
|
+
---
|
|
8448
|
+
|
|
8449
|
+
Update the globally installed NTRP package via npm.`
|
|
8450
|
+
},
|
|
8451
|
+
{
|
|
8452
|
+
name: "resume",
|
|
8453
|
+
raw: `---
|
|
8454
|
+
name: resume
|
|
8455
|
+
description: Continue a previous session
|
|
8456
|
+
section: More
|
|
8457
|
+
args: [id]
|
|
8458
|
+
handler: ../commands/resume.ts
|
|
8459
|
+
hidden: true
|
|
8460
|
+
---
|
|
8461
|
+
|
|
8462
|
+
Load a previous session's context so the AI can reference what was
|
|
8463
|
+
discussed before. Without an ID, resumes the most recent session.
|
|
8464
|
+
Use a full session ID or 4-char suffix.`
|
|
8465
|
+
},
|
|
8466
|
+
{
|
|
8467
|
+
name: "name",
|
|
8468
|
+
raw: `---
|
|
8469
|
+
name: name
|
|
8470
|
+
description: Tag this session with a label
|
|
8471
|
+
section: More
|
|
8472
|
+
args: [label]
|
|
8473
|
+
handler: ../commands/name.ts
|
|
8474
|
+
---
|
|
8475
|
+
|
|
8476
|
+
Give the current session a human-readable name so you can find it
|
|
8477
|
+
later. The name appears in the REPL prompt, session list, and
|
|
8478
|
+
welcome dashboard. Max 40 characters.`
|
|
8479
|
+
},
|
|
8480
|
+
{
|
|
8481
|
+
name: "switch",
|
|
8482
|
+
raw: `---
|
|
8483
|
+
name: switch
|
|
8484
|
+
description: Jump to a named session
|
|
8485
|
+
section: More
|
|
8486
|
+
args: [name]
|
|
8487
|
+
handler: ../commands/switch.ts
|
|
8488
|
+
hidden: true
|
|
8489
|
+
---
|
|
8490
|
+
|
|
8491
|
+
Save the current session and switch to a named one. If the name
|
|
8492
|
+
exists, loads its context and messages. If new, creates a fresh
|
|
8493
|
+
session with that name. Without arguments, lists all named sessions.`
|
|
8494
|
+
},
|
|
8495
|
+
{
|
|
8496
|
+
name: "actions",
|
|
8497
|
+
raw: `---
|
|
8498
|
+
name: actions
|
|
8499
|
+
description: Propose, approve, and execute actions
|
|
8500
|
+
section: More
|
|
8501
|
+
args: [list|test|show|approve|reject|execute|continue] [id]
|
|
8502
|
+
handler: ../commands/actions.ts
|
|
8503
|
+
---
|
|
8504
|
+
|
|
8505
|
+
Create and manage action proposals. \`/actions test\` creates a local manual
|
|
8506
|
+
dry-run proposal that exercises the approval and execution lifecycle without
|
|
8507
|
+
touching external tools. Execute-class actions require local approval before
|
|
8508
|
+
they can run. Use \`/actions continue\` to advance the newest pending or
|
|
8509
|
+
approved proposal without copying a handle during the active workflow.`
|
|
8510
|
+
},
|
|
8511
|
+
{
|
|
8512
|
+
name: "diagnose",
|
|
8513
|
+
raw: `---
|
|
8514
|
+
name: diagnose
|
|
8515
|
+
description: Compute vital signs and generate findings
|
|
8516
|
+
section: Hidden
|
|
8517
|
+
hidden: true
|
|
8518
|
+
args: [--deep] [--segment <name>]
|
|
8519
|
+
handler: ../commands/diagnose.ts
|
|
8520
|
+
---
|
|
8521
|
+
|
|
8522
|
+
Compute the 5 vital signs (freshness, flow rate, drop rate, signal-to-noise,
|
|
8523
|
+
thread depth) for either the full dataset or a segment. Companion to \`/metrics\`
|
|
8524
|
+
when your session primary is SaaS metrics. Use \`--deep\` to run the agentic
|
|
8525
|
+
investigation loop instead of the single-shot findings path.`
|
|
8526
|
+
},
|
|
8527
|
+
{
|
|
8528
|
+
name: "metrics",
|
|
8529
|
+
raw: `---
|
|
8530
|
+
name: metrics
|
|
8531
|
+
description: SaaS metrics \u2014 refresh or add the revenue view
|
|
8532
|
+
section: Hidden
|
|
8533
|
+
hidden: true
|
|
8534
|
+
args: [--findings] [--segment <name>]
|
|
8535
|
+
handler: ../commands/metrics.ts
|
|
8536
|
+
---
|
|
8537
|
+
|
|
8538
|
+
Compute SaaS revenue metrics from pipeline or revenue-ledger data: ARR, NRR/GRR,
|
|
8539
|
+
Win Rate, Pipeline Coverage, and more. Each metric includes a confidence score
|
|
8540
|
+
and reliability gate showing what data unlocks the next tier. Use \`--findings\`
|
|
8541
|
+
for AI analysis calibrated to your company profile. Revenue ledger CSV format:
|
|
8542
|
+
account, period, mrr, event_type.`
|
|
8543
|
+
},
|
|
8544
|
+
{
|
|
8545
|
+
name: "ask",
|
|
8546
|
+
raw: `---
|
|
8547
|
+
name: ask
|
|
8548
|
+
description: Chat with your pipeline data
|
|
8549
|
+
section: Hidden
|
|
8550
|
+
hidden: true
|
|
8551
|
+
args: <question>
|
|
8552
|
+
handler: ../commands/ask.ts
|
|
8553
|
+
---
|
|
8554
|
+
|
|
8555
|
+
Ask a plain-English question about your GTM health and SaaS metrics. Free-form
|
|
8556
|
+
text at the REPL prompt routes to the same agent. Respects your session primary
|
|
8557
|
+
lens; can cross-reference vital signs and revenue metrics via tools.`
|
|
8558
|
+
},
|
|
8559
|
+
{
|
|
8560
|
+
name: "recap",
|
|
8561
|
+
raw: `---
|
|
8562
|
+
name: recap
|
|
8563
|
+
description: Summarize the current session
|
|
8564
|
+
section: More
|
|
8565
|
+
handler: ../commands/recap.ts
|
|
8566
|
+
---
|
|
8567
|
+
|
|
8568
|
+
Summarize the current REPL session using AI. Reads all natural-language
|
|
8569
|
+
exchanges from the session and produces a structured overview: key findings,
|
|
8570
|
+
dollar impacts, and recommended next steps.`
|
|
8571
|
+
},
|
|
8572
|
+
{
|
|
8573
|
+
name: "remember",
|
|
8574
|
+
raw: `---
|
|
8575
|
+
name: remember
|
|
8576
|
+
description: Teach the analyst a durable fact
|
|
8577
|
+
section: More
|
|
8578
|
+
args: <fact> | decision: <text> | preference: <text>
|
|
8579
|
+
handler: ../commands/remember.ts
|
|
8580
|
+
---
|
|
8581
|
+
|
|
8582
|
+
Store a durable fact, decision, or preference about your business. Stored
|
|
8583
|
+
memory flows into every future analysis so the agent gets to know your
|
|
8584
|
+
business better over time \u2014 like a consultant building up a client file.`
|
|
8585
|
+
},
|
|
8586
|
+
{
|
|
8587
|
+
name: "recall",
|
|
8588
|
+
raw: `---
|
|
8589
|
+
name: recall
|
|
8590
|
+
description: See what the analyst remembers
|
|
8591
|
+
section: More
|
|
8592
|
+
args: [topic]
|
|
8593
|
+
handler: ../commands/recall.ts
|
|
8594
|
+
---
|
|
8595
|
+
|
|
8596
|
+
Jog the analyst's memory. With no arguments, lists the durable facts it knows
|
|
8597
|
+
and the analyses it has already run. Pass a topic to see what it remembers
|
|
8598
|
+
about that subject \u2014 pulled from facts, strategies, wins, and ingested
|
|
8599
|
+
knowledge.`
|
|
8600
|
+
},
|
|
8601
|
+
{
|
|
8602
|
+
name: "rate",
|
|
8603
|
+
raw: `---
|
|
8604
|
+
name: rate
|
|
8605
|
+
description: Give feedback on the last answer
|
|
8606
|
+
section: More
|
|
8607
|
+
args: good [note] | bad <note>
|
|
8608
|
+
handler: ../commands/rate.ts
|
|
8609
|
+
---
|
|
8610
|
+
|
|
8611
|
+
Tell the analyst how its last answer landed. \`/rate good\` reinforces the
|
|
8612
|
+
approach; \`/rate bad <what was off>\` records a correction. Feedback becomes a
|
|
8613
|
+
durable preference so the analyst gets better at working with you over time.`
|
|
8614
|
+
},
|
|
8615
|
+
{
|
|
8616
|
+
name: "knowledge",
|
|
8617
|
+
raw: `---
|
|
8618
|
+
name: knowledge
|
|
8619
|
+
description: Ingest external case studies & frameworks
|
|
8620
|
+
section: More
|
|
8621
|
+
args: [add <file> | list]
|
|
8622
|
+
handler: ../commands/knowledge.ts
|
|
8623
|
+
---
|
|
8624
|
+
|
|
8625
|
+
Teach the analyst from work done outside the platform. \`/knowledge add <file>\`
|
|
8626
|
+
ingests a markdown, text, or PDF case study, framework, or benchmark report and
|
|
8627
|
+
indexes it for retrieval during analysis. \`/knowledge list\` shows what's
|
|
8628
|
+
indexed. Drop files into ~/.ntrp/knowledge to stage them.`
|
|
8629
|
+
},
|
|
8630
|
+
{
|
|
8631
|
+
name: "ingest",
|
|
8632
|
+
raw: `---
|
|
8633
|
+
name: ingest
|
|
8634
|
+
description: Import CRM CSV exports (or --demo)
|
|
8635
|
+
section: Hidden
|
|
8636
|
+
hidden: true
|
|
8637
|
+
args: <file> | --demo [--scenario <name>]
|
|
8638
|
+
handler: ../commands/ingest.ts
|
|
8639
|
+
---
|
|
8640
|
+
|
|
8641
|
+
Import a CSV file from your CRM (Salesforce, HubSpot, Outreach). The command
|
|
8642
|
+
auto-detects the entity type based on column headers and runs identity
|
|
8643
|
+
resolution after import.
|
|
8644
|
+
|
|
8645
|
+
Pass \`--demo\` instead of a file to generate a synthetic dataset shaped by
|
|
8646
|
+
your company profile. Accepts \`--scenario <name>\` to pick a scenario (else
|
|
8647
|
+
random) and \`--regen-taxonomy\` to rebuild the profile-derived market
|
|
8648
|
+
taxonomy. Rep names draw from a curated music / sports / film roster for a
|
|
8649
|
+
little demo delight; pass \`--no-whimsy\` to use generic names instead.`
|
|
8650
|
+
},
|
|
8651
|
+
{
|
|
8652
|
+
name: "demo",
|
|
8653
|
+
raw: `---
|
|
8654
|
+
name: demo
|
|
8655
|
+
description: Generate demo scenario data
|
|
8656
|
+
section: Getting Started
|
|
8657
|
+
args: [--scenario <name>] [--regen-taxonomy] [--no-whimsy]
|
|
8658
|
+
handler: ../commands/generate.ts
|
|
8659
|
+
hidden: true
|
|
8660
|
+
---
|
|
8661
|
+
|
|
8662
|
+
Generate a complete dataset for one of 5 demo scenarios: hidden_crisis,
|
|
8663
|
+
leaky_bucket, stale_pipeline, lone_wolf, busy_bees. Without \`--scenario\`,
|
|
8664
|
+
picks one at random each run. Use \`--list-scenarios\` to see descriptions.
|
|
8665
|
+
Use \`--regen-taxonomy\` to force a fresh AI-built market taxonomy.
|
|
8666
|
+
By default, sales rep names are drawn from a curated music / sports / film
|
|
8667
|
+
roster; pass \`--no-whimsy\` for generic placeholder names.
|
|
8668
|
+
|
|
8669
|
+
This command is hidden \u2014 prefer \`/ingest --demo\` which delegates here.`
|
|
8670
|
+
},
|
|
8671
|
+
{
|
|
8672
|
+
name: "strategy",
|
|
8673
|
+
raw: `---
|
|
8674
|
+
name: strategy
|
|
8675
|
+
description: Ingest and manage GTM strategies
|
|
8676
|
+
section: More
|
|
8677
|
+
args: [list|ingest|add|show|sync|sources] [file|text|id|folder]
|
|
8678
|
+
handler: ../commands/strategy.ts
|
|
8679
|
+
---
|
|
8680
|
+
|
|
8681
|
+
Add user-authored or agent-authored GTM strategies to NTRP's strategy library.
|
|
8682
|
+
\`/strategy ingest <file>\` accepts markdown, YAML, PDF, or plain text files.
|
|
8683
|
+
\`/strategy ingest -\` reads strategy text from stdin, and \`/strategy add "..."\`
|
|
8684
|
+
creates a strategy from a short pasted description.
|
|
8685
|
+
\`/strategy sync --path <folder>\` scans an Obsidian-style local library and
|
|
8686
|
+
imports supported strategy documents. Use \`/strategy sources\` to see available
|
|
8687
|
+
library connector types.`
|
|
8688
|
+
},
|
|
8689
|
+
{
|
|
8690
|
+
name: "segment",
|
|
8691
|
+
raw: `---
|
|
8692
|
+
name: segment
|
|
8693
|
+
description: Browse and inspect segments
|
|
8694
|
+
section: More
|
|
8695
|
+
args: [list|show|compare|create|delete] [args]
|
|
8696
|
+
handler: ../commands/segment.ts
|
|
8697
|
+
---
|
|
8698
|
+
|
|
8699
|
+
Browse, inspect, and manage data segments. With no arguments, lists all
|
|
8700
|
+
segments sorted worst-first. Subcommands: \`show <name>\`, \`compare <a> <b>\`,
|
|
8701
|
+
\`create <name> --entity <type> --filter <expr>\`, \`delete <name>\`.`
|
|
8702
|
+
},
|
|
8703
|
+
{
|
|
8704
|
+
name: "report",
|
|
8705
|
+
raw: `---
|
|
8706
|
+
name: report
|
|
8707
|
+
description: Export latest diagnosis
|
|
8708
|
+
section: More
|
|
8709
|
+
args: [--format terminal|md|json] [--output <file>]
|
|
8710
|
+
handler: ../commands/report.ts
|
|
8711
|
+
---
|
|
8712
|
+
|
|
8713
|
+
Export the most recent diagnosis as terminal output, markdown, or JSON. Use
|
|
8714
|
+
\`--output <file>\` to write to disk instead of stdout.`
|
|
8715
|
+
},
|
|
8716
|
+
{
|
|
8717
|
+
name: "progress",
|
|
8718
|
+
raw: `---
|
|
8719
|
+
name: progress
|
|
8720
|
+
description: Usage stats and milestone ladder
|
|
8721
|
+
section: Navigation
|
|
8722
|
+
args: [reset] [--confirm]
|
|
8723
|
+
handler: ../commands/progress.ts
|
|
8724
|
+
---
|
|
8725
|
+
|
|
8726
|
+
Hours saved, weekly activity trend, session counts, AI token usage, and the
|
|
8727
|
+
full milestone ladder with progress bars. Use reset (type "reset" to confirm)
|
|
8728
|
+
to clear hours and milestones while keeping this install's identity.`
|
|
8729
|
+
},
|
|
8730
|
+
{
|
|
8731
|
+
name: "status",
|
|
8732
|
+
raw: `---
|
|
8733
|
+
name: status
|
|
8734
|
+
description: Show last diagnosis and entity counts
|
|
8735
|
+
section: More
|
|
8736
|
+
handler: ../commands/status.ts
|
|
8737
|
+
---
|
|
8738
|
+
|
|
8739
|
+
Show what data you currently have loaded and the result of your last
|
|
8740
|
+
diagnosis, if any.`
|
|
8741
|
+
},
|
|
8742
|
+
{
|
|
8743
|
+
name: "scratch",
|
|
8744
|
+
raw: `---
|
|
8745
|
+
name: scratch
|
|
8746
|
+
description: Wipe config, profile, and all datasets
|
|
8747
|
+
section: Admin
|
|
8748
|
+
args: [--confirm] [--include-progress]
|
|
8749
|
+
handler: ../commands/scratch.ts
|
|
8750
|
+
hidden: true
|
|
8751
|
+
---
|
|
8752
|
+
|
|
8753
|
+
Minimal factory reset: removes API key, config, company profile, all sessions,
|
|
8754
|
+
per-session datasets, and demo taxonomy cache. Preserves progress (hours saved)
|
|
8755
|
+
by default. Pass \`--include-progress\` to also wipe install identity and hours.
|
|
8756
|
+
Also preserves memory, strategies, wins, knowledge, exports, and audit. Requires
|
|
8757
|
+
typing \`scratch\` in the REPL or passing \`--confirm\` one-shot. Triggers
|
|
8758
|
+
onboarding on next interactive use.`
|
|
8759
|
+
},
|
|
8760
|
+
{
|
|
8761
|
+
name: "cleanup",
|
|
8762
|
+
raw: `---
|
|
8763
|
+
name: cleanup
|
|
8764
|
+
description: Close all active sessions
|
|
8765
|
+
section: Admin
|
|
8766
|
+
args: [--confirm]
|
|
8767
|
+
handler: ../commands/cleanup.ts
|
|
8768
|
+
hidden: true
|
|
8769
|
+
---
|
|
8770
|
+
|
|
8771
|
+
Mark every in-progress session as ended without deleting transcripts or dataset
|
|
8772
|
+
files. Interactive REPL only. Confirm with y/N or \`--confirm\` one-shot.`
|
|
8773
|
+
},
|
|
8774
|
+
{
|
|
8775
|
+
name: "deactivate-demo",
|
|
8776
|
+
raw: `---
|
|
8777
|
+
name: deactivate-demo
|
|
8778
|
+
description: Disable demo data generators
|
|
8779
|
+
section: Admin
|
|
8780
|
+
args: [--confirm]
|
|
8781
|
+
handler: ../commands/deactivate-demo.ts
|
|
8782
|
+
hidden: true
|
|
8783
|
+
---
|
|
8784
|
+
|
|
8785
|
+
Persistently disable demo generators (\`/ingest --demo\`, \`/new --demo\`, NL
|
|
8786
|
+
"use demo data"). Re-enable with \`/config set demo-enabled true\`.`
|
|
8787
|
+
},
|
|
8788
|
+
{
|
|
8789
|
+
name: "reset",
|
|
8790
|
+
raw: `---
|
|
8791
|
+
name: reset
|
|
8792
|
+
description: Clear all data and start fresh
|
|
8793
|
+
section: More
|
|
8794
|
+
args: [--force]
|
|
8795
|
+
handler: ../commands/reset.ts
|
|
8796
|
+
---
|
|
8797
|
+
|
|
8798
|
+
Drop all rows from every table in the local DuckDB database. Requires
|
|
8799
|
+
\`--force\` to proceed.`
|
|
8800
|
+
},
|
|
8801
|
+
{
|
|
8802
|
+
name: "playbook",
|
|
8803
|
+
raw: `---
|
|
8804
|
+
name: playbook
|
|
8805
|
+
description: Show or extend recommended plays
|
|
8806
|
+
section: More
|
|
8807
|
+
args: [--vital-sign <name>] [play-id] | add
|
|
8808
|
+
handler: ../commands/playbook.ts
|
|
8809
|
+
---
|
|
8810
|
+
|
|
8811
|
+
Show the playbook of recommended plays keyed to each vital sign. Pass a
|
|
8812
|
+
play-id to drill into a single play's steps and expected outcome. Run
|
|
8813
|
+
\`/playbook add\` and the analyst walks you through capturing a new play, step by
|
|
8814
|
+
step \u2014 no flags or quoting needed. Learned plays become recommendable during
|
|
8815
|
+
analysis. (Power users can still pass everything as flags in one shot.)`
|
|
8816
|
+
},
|
|
8817
|
+
{
|
|
8818
|
+
name: "export",
|
|
8819
|
+
raw: `---
|
|
8820
|
+
name: export
|
|
8821
|
+
description: Save diagnosis to Obsidian notes
|
|
8822
|
+
section: More
|
|
8823
|
+
args: [--dir <path>] [--segment <name>]
|
|
8824
|
+
handler: ../commands/export.ts
|
|
8825
|
+
---
|
|
8826
|
+
|
|
8827
|
+
Write the most recent diagnosis to your configured notes directory as
|
|
8828
|
+
markdown, ready for Obsidian, Logseq, or any other note tool.`
|
|
8829
|
+
},
|
|
8830
|
+
{
|
|
8831
|
+
name: "publish",
|
|
8832
|
+
raw: `---
|
|
8833
|
+
name: publish
|
|
8834
|
+
description: Preview and propose repository exports
|
|
8835
|
+
section: More
|
|
8836
|
+
args: [preview|propose|targets] [--target markdown] [--dir <path>]
|
|
8837
|
+
handler: ../commands/publish.ts
|
|
8838
|
+
---
|
|
8839
|
+
|
|
8840
|
+
Build a full repository export package from the latest diagnosis, findings,
|
|
8841
|
+
strategies, evidence, and action receipts. \`preview\` shows the write plan;
|
|
8842
|
+
\`propose\` creates an approval-gated action proposal. The first executable
|
|
8843
|
+
target is local markdown for Obsidian-compatible repositories. Notion,
|
|
8844
|
+
Airtable, and GitHub mappings are documented via \`/publish targets\`.`
|
|
8845
|
+
},
|
|
8846
|
+
{
|
|
8847
|
+
name: "backmeup",
|
|
8848
|
+
raw: `---
|
|
8849
|
+
name: backmeup
|
|
8850
|
+
description: Export diagnosis receipts as CSV
|
|
8851
|
+
section: More
|
|
8852
|
+
args: [--output <dir>]
|
|
8853
|
+
handler: ../commands/backmeup.ts
|
|
8854
|
+
---
|
|
8855
|
+
|
|
8856
|
+
Export your latest diagnosis as a folder of CSV files you can attach to a
|
|
8857
|
+
Slack thread, email, or slide deck. Creates a timestamped folder under
|
|
8858
|
+
~/.ntrp/exports/ containing a cover sheet with headline numbers, a findings
|
|
8859
|
+
file, and per-vital-sign evidence CSVs showing exactly which deals, contacts,
|
|
8860
|
+
or orgs drove each score. Use --output <dir> to write somewhere else.`
|
|
8861
|
+
},
|
|
8862
|
+
{
|
|
8863
|
+
name: "profile",
|
|
8864
|
+
raw: `---
|
|
8865
|
+
name: profile
|
|
8866
|
+
description: Set sales motion
|
|
8867
|
+
section: Settings
|
|
8868
|
+
args: [list|set|show] [preset]
|
|
8869
|
+
handler: ../commands/profile.ts
|
|
8870
|
+
---
|
|
8871
|
+
|
|
8872
|
+
Choose a sales motion preset (PLG, SMB Velocity, Mid-Market, Enterprise). Each
|
|
8873
|
+
preset adjusts the vital-sign thresholds to match your deal cycle.`
|
|
8874
|
+
},
|
|
8875
|
+
{
|
|
8876
|
+
name: "connect",
|
|
8877
|
+
raw: `---
|
|
8878
|
+
name: connect
|
|
8879
|
+
description: Connect an AI provider (paste any key)
|
|
8880
|
+
section: Settings
|
|
8881
|
+
args: [provider] [--key <key>] [--base-url <url> --id <name>]
|
|
8882
|
+
handler: ../commands/connect.ts
|
|
8883
|
+
---
|
|
8884
|
+
|
|
8885
|
+
Paste any provider's API key \u2014 NTRP identifies the provider from the key
|
|
8886
|
+
format (probing ambiguous ones), validates it, discovers which models the key
|
|
8887
|
+
can use, and builds the HIGH/MEDIUM/LOW tier stack automatically.
|
|
8888
|
+
|
|
8889
|
+
Works with Anthropic, OpenAI, Google Gemini, Groq, Mistral, DeepSeek, xAI,
|
|
8890
|
+
OpenRouter, Together, and Fireworks out of the box. \`/connect ollama\` wires a
|
|
8891
|
+
local Ollama; \`/connect --base-url <url> --id <name>\` registers any other
|
|
8892
|
+
OpenAI-compatible endpoint.`
|
|
8893
|
+
},
|
|
8894
|
+
{
|
|
8895
|
+
name: "config",
|
|
8896
|
+
raw: `---
|
|
8897
|
+
name: config
|
|
8898
|
+
description: Get/set config values
|
|
8899
|
+
section: Settings
|
|
8900
|
+
args: [get|set|list|delete] <key> [value]
|
|
8901
|
+
handler: ../commands/config.ts
|
|
8902
|
+
---
|
|
8903
|
+
|
|
8904
|
+
Manage CLI configuration stored at \`~/.ntrp/config.json\`. Useful keys:
|
|
8905
|
+
\`api-key\` (Anthropic), \`openai-api-key\` (and \`groq-api-key\`, \`google-api-key\`, ...),
|
|
8906
|
+
\`llm-primary\` (default engine), \`llm-tier\`, \`llm-auto-failover\`,
|
|
8907
|
+
\`default-format\`, \`export-dir\`.
|
|
8908
|
+
|
|
8909
|
+
Setting a provider key opens a hidden prompt and auto-discovers that
|
|
8910
|
+
provider's models. Prefer \`/connect\` \u2014 it detects the provider for you.`
|
|
8911
|
+
},
|
|
8912
|
+
{
|
|
8913
|
+
name: "provider",
|
|
8914
|
+
raw: `---
|
|
8915
|
+
name: provider
|
|
8916
|
+
description: Switch active LLM engine
|
|
8917
|
+
section: Settings
|
|
8918
|
+
args: [<id>|list|reset|save|failover on|off]
|
|
8919
|
+
handler: ../commands/provider.ts
|
|
8920
|
+
---
|
|
8921
|
+
|
|
8922
|
+
Choose which connected engine answers this session \u2014 any provider added via
|
|
8923
|
+
\`/connect\` (anthropic, openai, groq, google, ollama, custom endpoints, ...).
|
|
8924
|
+
Session-scoped by default; \`/provider save\` writes the default to config.
|
|
8925
|
+
\`/provider failover on\` enables rate-limit auto-failover.`
|
|
8926
|
+
},
|
|
8927
|
+
{
|
|
8928
|
+
name: "tier",
|
|
8929
|
+
raw: `---
|
|
8930
|
+
name: tier
|
|
8931
|
+
description: Set inference tier (HIGH/MEDIUM/LOW)
|
|
8932
|
+
section: Settings
|
|
8933
|
+
args: [high|medium|low|list] [--default]
|
|
8934
|
+
handler: ../commands/tier.ts
|
|
8935
|
+
---
|
|
8936
|
+
|
|
8937
|
+
Set quality/cost tier for this REPL session. Agentic surfaces respect your tier;
|
|
8938
|
+
some single-shot surfaces keep fixed defaults. \`/tier list\` highlights the
|
|
8939
|
+
active stack. Add \`--default\` to persist to config.`
|
|
8940
|
+
},
|
|
8941
|
+
{
|
|
8942
|
+
name: "model",
|
|
8943
|
+
raw: `---
|
|
8944
|
+
name: model
|
|
8945
|
+
description: Override the active LLM model
|
|
8946
|
+
section: Settings
|
|
8947
|
+
args: [list|set <id>|refresh|clear] [--default]
|
|
8948
|
+
handler: ../commands/model.ts
|
|
8949
|
+
---
|
|
8950
|
+
|
|
8951
|
+
\`/model list\` shows the models discovered for the active engine with their
|
|
8952
|
+
tier assignments. \`/model refresh\` re-discovers the live list. \`/model set <id>\`
|
|
8953
|
+
pins a model on the **active engine**; cross-provider IDs are rejected \u2014
|
|
8954
|
+
switch with \`/provider\` first.`
|
|
8955
|
+
},
|
|
8956
|
+
{
|
|
8957
|
+
name: "activate",
|
|
8958
|
+
raw: `---
|
|
8959
|
+
name: activate
|
|
8960
|
+
description: Enter license key
|
|
8961
|
+
section: Settings
|
|
8962
|
+
args: <license>
|
|
8963
|
+
handler: ../commands/activate.ts
|
|
8964
|
+
---
|
|
8965
|
+
|
|
8966
|
+
Activate NTRP with your license key (format: NTRP-XXXX-XXXX-XXXX). Most
|
|
8967
|
+
commands require a valid license.`
|
|
8968
|
+
},
|
|
8969
|
+
{
|
|
8970
|
+
name: "upgrade",
|
|
8971
|
+
raw: `---
|
|
8972
|
+
name: upgrade
|
|
8973
|
+
description: Upgrade trial to Pro \u2014 checkout + paste key
|
|
8974
|
+
section: Settings
|
|
8975
|
+
handler: ../commands/upgrade.ts
|
|
8976
|
+
---
|
|
8977
|
+
|
|
8978
|
+
Open the Pro checkout page and paste your new license key without leaving
|
|
8979
|
+
the REPL. Use during trial grace or after cutoff. Flags: --url (print checkout URL only).`
|
|
8980
|
+
},
|
|
8981
|
+
{
|
|
8982
|
+
name: "checkout",
|
|
8983
|
+
raw: `---
|
|
8984
|
+
name: checkout
|
|
8985
|
+
description: Open signup checkout in your browser
|
|
8986
|
+
section: Settings
|
|
8987
|
+
handler: ../commands/checkout.ts
|
|
8988
|
+
---
|
|
8989
|
+
|
|
8990
|
+
Opens the Lemon Squeezy checkout page in your default browser. Use anytime
|
|
8991
|
+
you need a trial or Pro license key.`
|
|
8992
|
+
},
|
|
8993
|
+
{
|
|
8994
|
+
name: "feedback",
|
|
8995
|
+
raw: `---
|
|
8996
|
+
name: feedback
|
|
8997
|
+
description: Correct your profile in plain English
|
|
8998
|
+
section: Settings
|
|
8999
|
+
args: <correction>
|
|
9000
|
+
handler: ../commands/feedback.ts
|
|
9001
|
+
---
|
|
9002
|
+
|
|
9003
|
+
Apply natural-language corrections to your company profile. Maps structured
|
|
9004
|
+
fields when possible (e.g. "our sales cycle is 6 months" updates
|
|
9005
|
+
sales_cycle_days) and merges remaining nuances into a custom_context
|
|
9006
|
+
paragraph that flows into all AI surfaces.`
|
|
9007
|
+
}
|
|
9008
|
+
];
|
|
9009
|
+
}
|
|
9010
|
+
});
|
|
9011
|
+
|
|
8282
9012
|
// src/ai/prompt-parts.ts
|
|
8283
9013
|
function buildCompanyProfileBlock() {
|
|
8284
9014
|
const p = loadProfile();
|
|
@@ -8305,12 +9035,41 @@ function buildPlaybookBlock() {
|
|
|
8305
9035
|
Learned plays (added from this team's experience and ingested case studies \u2014 recommend these when they fit):
|
|
8306
9036
|
${learned}`;
|
|
8307
9037
|
}
|
|
8308
|
-
|
|
9038
|
+
function buildCommandCatalogBlock() {
|
|
9039
|
+
const GROUP_ANALYSIS = "Analysis & data";
|
|
9040
|
+
const GROUP_SESSION = "Session, memory & outputs";
|
|
9041
|
+
const GROUP_SETTINGS = "Settings & providers";
|
|
9042
|
+
const groupOrder = [GROUP_ANALYSIS, GROUP_SESSION, GROUP_SETTINGS];
|
|
9043
|
+
const groupFor = (section) => {
|
|
9044
|
+
if (section === "Hidden" || section === "Getting Started") return GROUP_ANALYSIS;
|
|
9045
|
+
if (section === "Settings") return GROUP_SETTINGS;
|
|
9046
|
+
return GROUP_SESSION;
|
|
9047
|
+
};
|
|
9048
|
+
const groups = new Map(groupOrder.map((label) => [label, []]));
|
|
9049
|
+
for (const meta of listWorkflows(true)) {
|
|
9050
|
+
if (meta.name === "ask") continue;
|
|
9051
|
+
if (meta.section === "Admin") continue;
|
|
9052
|
+
groups.get(groupFor(meta.section)).push(formatCatalogLine(meta));
|
|
9053
|
+
}
|
|
9054
|
+
groups.get(GROUP_SESSION).push(
|
|
9055
|
+
"- /help \u2014 Show the shortcut list",
|
|
9056
|
+
"- /home \u2014 Show the welcome dashboard and current session status"
|
|
9057
|
+
);
|
|
9058
|
+
return groupOrder.filter((label) => groups.get(label).length > 0).map((label) => `${label}:
|
|
9059
|
+
${groups.get(label).join("\n")}`).join("\n\n");
|
|
9060
|
+
}
|
|
9061
|
+
function formatCatalogLine(meta) {
|
|
9062
|
+
const args = meta.args?.trim() ? ` ${meta.args.trim()}` : "";
|
|
9063
|
+
const note = DESTRUCTIVE_COMMAND_NOTES[meta.name] ? ` (${DESTRUCTIVE_COMMAND_NOTES[meta.name]})` : "";
|
|
9064
|
+
return `- /${meta.name}${args} \u2014 ${meta.description}${note}`;
|
|
9065
|
+
}
|
|
9066
|
+
var ANALYST_INSTINCT_BLOCK, VITAL_SIGNS_BLOCK, PLAYBOOK_BLOCK, DESTRUCTIVE_COMMAND_NOTES, METRICS_BLOCK, FINDINGS_SCHEMA_BLOCK;
|
|
8309
9067
|
var init_prompt_parts = __esm({
|
|
8310
9068
|
"src/ai/prompt-parts.ts"() {
|
|
8311
9069
|
"use strict";
|
|
8312
9070
|
init_profile();
|
|
8313
9071
|
init_playbook();
|
|
9072
|
+
init_registry();
|
|
8314
9073
|
ANALYST_INSTINCT_BLOCK = `A search box returns numbers; a world-class analyst returns insight. The instincts below are what separate the two. Bring them PROACTIVELY \u2014 surface the connection, the chain, and the priority without waiting to be asked, because the user often doesn't know to ask.
|
|
8315
9074
|
|
|
8316
9075
|
- ROOT CAUSE OVER SYMPTOM LIST. GTM problems are rarely independent \u2014 they're usually one failure wearing several masks. When two or more vital signs are red or yellow, your first instinct is to ask "is this the same underlying problem showing up in different places?" and, when it is, name the single cause. (Classic shape: a broken lead handoff starves reps of new pipeline \u2192 they work only what they can already see \u2192 everything else ages into stale, zombie deals \u2192 the forecast inflates with deals nobody is touching \u2192 the quarter is quietly at risk. One cause, four symptoms.)
|
|
@@ -8333,6 +9092,9 @@ Restraint matters: NTRP is a stethoscope, not a surgeon. Observe, connect, and r
|
|
|
8333
9092
|
- "Fix the Handoff Gap" (id: fix-handoff-gap) \u2014 when drop_rate is high
|
|
8334
9093
|
- "Retarget Misdirected Effort" (id: retarget-effort) \u2014 when signal_to_noise is low
|
|
8335
9094
|
- "Unstick the Pipeline" (id: unstick-pipeline) \u2014 when flow_rate is low`;
|
|
9095
|
+
DESTRUCTIVE_COMMAND_NOTES = {
|
|
9096
|
+
reset: "destructive \u2014 wipes all data, requires --force"
|
|
9097
|
+
};
|
|
8336
9098
|
METRICS_BLOCK = `Revenue metrics measure GTM output \u2014 the standard SaaS metrics:
|
|
8337
9099
|
- ARR: Total closed-won revenue. New ARR + Expansion ARR = growth; Churned + Contraction = leakage.
|
|
8338
9100
|
- NRR (Net Revenue Retention): >100% means growing from existing customers. Best-in-class: 110%+.
|
|
@@ -13889,15 +14651,18 @@ function inferHandoffTarget(input) {
|
|
|
13889
14651
|
return "plan";
|
|
13890
14652
|
}
|
|
13891
14653
|
function isShipIntent(input) {
|
|
13892
|
-
|
|
13893
|
-
|
|
13894
|
-
);
|
|
14654
|
+
const line = input.trim();
|
|
14655
|
+
if (/\?\s*$/.test(line) || QUESTION_LEAD_RE.test(line)) return false;
|
|
14656
|
+
return SHIP_INTENT_RE.test(line);
|
|
13895
14657
|
}
|
|
14658
|
+
var QUESTION_LEAD_RE, SHIP_INTENT_RE;
|
|
13896
14659
|
var init_handoff_draft = __esm({
|
|
13897
14660
|
"src/conversation/handoff-draft.ts"() {
|
|
13898
14661
|
"use strict";
|
|
13899
14662
|
init_profile();
|
|
13900
14663
|
init_session_analysis();
|
|
14664
|
+
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;
|
|
14665
|
+
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;
|
|
13901
14666
|
}
|
|
13902
14667
|
});
|
|
13903
14668
|
|
|
@@ -14459,6 +15224,16 @@ FORMATTING (brief mode \u2014 scannable, not chunky):
|
|
|
14459
15224
|
- No ### headings, no tables, no --- footer, no closing "want me to dig deeper?" question.
|
|
14460
15225
|
- Single-point answers can stay one sentence \u2014 don't force bullets when one line is enough.
|
|
14461
15226
|
`;
|
|
15227
|
+
const commandStyleRule = responseMode === "brief" ? '- Style in brief mode: a suggestion is at most ONE short trailing line after your answer (e.g. "`/segment compare enterprise smb` gives the full side-by-side.") \u2014 no heading, no footer.' : "- Style: weave the suggestion naturally into your close \u2014 one sentence with the exact invocation in `inline code`.";
|
|
15228
|
+
const commandSection = `PRESET COMMANDS (slash commands the user can type at the prompt \u2014 you may SUGGEST these; you have no ability to run them):
|
|
15229
|
+
${buildCommandCatalogBlock()}
|
|
15230
|
+
|
|
15231
|
+
COMMAND SUGGESTION RULES (suggest-only):
|
|
15232
|
+
- Evaluate whether the user's question overlaps a preset command's capability. Answer the question yourself first, from tools and context; suggest a command only when it adds capability beyond your answer \u2014 writing files, opening a wizard, running a heavier analysis, or changing settings.
|
|
15233
|
+
- If the message is primarily a request to DO what a command does (e.g. "export this to my notes"), say plainly that you don't execute commands and give the exact invocation to type, e.g. \`/handoff notes\`.
|
|
15234
|
+
- Never claim a command was run. Never invent flags \u2014 use only the documented arguments above. At most ONE command suggestion per answer; most answers need none.
|
|
15235
|
+
- Only suggest a command that can work right now given the session state you know (e.g. no \`/diagnose\` or \`/export\` before data or a diagnosis exists).
|
|
15236
|
+
${commandStyleRule}`;
|
|
14462
15237
|
const outputRules = responseMode === "brief" ? `OUTPUT RULES:
|
|
14463
15238
|
- Respond in plain text markdown (not JSON). Lead sentence + bullets when helpful \u2014 never a dense paragraph wall.
|
|
14464
15239
|
- Pull numbers from the completed session analysis or conversation history \u2014 never guess.
|
|
@@ -14482,6 +15257,8 @@ ${VITAL_SIGNS_BLOCK}
|
|
|
14482
15257
|
|
|
14483
15258
|
PLAYBOOK \u2014 recommend these plays when appropriate:
|
|
14484
15259
|
${buildPlaybookBlock()}
|
|
15260
|
+
|
|
15261
|
+
${commandSection}
|
|
14485
15262
|
${formattingSection}
|
|
14486
15263
|
${outputRules}`;
|
|
14487
15264
|
}
|
|
@@ -14742,6 +15519,136 @@ var init_embeddings = __esm({
|
|
|
14742
15519
|
}
|
|
14743
15520
|
});
|
|
14744
15521
|
|
|
15522
|
+
// src/memory/retrieval.ts
|
|
15523
|
+
function tokenize(text) {
|
|
15524
|
+
return text.toLowerCase().split(/[^a-z0-9]+/).filter((t) => t.length >= 3 && !STOPWORDS.has(t));
|
|
15525
|
+
}
|
|
15526
|
+
function keywordRank(query, items, topK) {
|
|
15527
|
+
const queryTokens = new Set(tokenize(query));
|
|
15528
|
+
if (queryTokens.size === 0) {
|
|
15529
|
+
return items.slice(-topK).reverse().map((it) => ({ id: it.id, score: 0 }));
|
|
15530
|
+
}
|
|
15531
|
+
const scored = items.map((it) => {
|
|
15532
|
+
const tokens = tokenize(it.text);
|
|
15533
|
+
if (tokens.length === 0) return { id: it.id, score: 0 };
|
|
15534
|
+
let overlap = 0;
|
|
15535
|
+
const seen = /* @__PURE__ */ new Set();
|
|
15536
|
+
for (const t of tokens) {
|
|
15537
|
+
if (queryTokens.has(t) && !seen.has(t)) {
|
|
15538
|
+
overlap++;
|
|
15539
|
+
seen.add(t);
|
|
15540
|
+
}
|
|
15541
|
+
}
|
|
15542
|
+
return { id: it.id, score: overlap / Math.sqrt(tokens.length) };
|
|
15543
|
+
});
|
|
15544
|
+
const positive = scored.filter((s) => s.score > 0).sort((a, b) => b.score - a.score);
|
|
15545
|
+
if (positive.length > 0) return positive.slice(0, topK);
|
|
15546
|
+
return items.slice(-topK).reverse().map((it) => ({ id: it.id, score: 0 }));
|
|
15547
|
+
}
|
|
15548
|
+
function cosine(a, b) {
|
|
15549
|
+
let dot = 0;
|
|
15550
|
+
let na = 0;
|
|
15551
|
+
let nb = 0;
|
|
15552
|
+
const len = Math.min(a.length, b.length);
|
|
15553
|
+
for (let i = 0; i < len; i++) {
|
|
15554
|
+
dot += a[i] * b[i];
|
|
15555
|
+
na += a[i] * a[i];
|
|
15556
|
+
nb += b[i] * b[i];
|
|
15557
|
+
}
|
|
15558
|
+
if (na === 0 || nb === 0) return 0;
|
|
15559
|
+
return dot / (Math.sqrt(na) * Math.sqrt(nb));
|
|
15560
|
+
}
|
|
15561
|
+
async function rankByRelevance(query, items, topK) {
|
|
15562
|
+
if (items.length === 0) return [];
|
|
15563
|
+
try {
|
|
15564
|
+
const { isEmbeddingsEnabled: isEmbeddingsEnabled2, embedText: embedText2, embedItems: embedItems2 } = await Promise.resolve().then(() => (init_embeddings(), embeddings_exports));
|
|
15565
|
+
if (isEmbeddingsEnabled2()) {
|
|
15566
|
+
const queryVec = await embedText2(query);
|
|
15567
|
+
if (queryVec) {
|
|
15568
|
+
const withVecs = await embedItems2(items);
|
|
15569
|
+
const scored = withVecs.filter((it) => it.embedding && it.embedding.length > 0).map((it) => ({ id: it.id, score: cosine(queryVec, it.embedding) })).sort((a, b) => b.score - a.score);
|
|
15570
|
+
if (scored.length > 0) return scored.slice(0, topK);
|
|
15571
|
+
}
|
|
15572
|
+
}
|
|
15573
|
+
} catch {
|
|
15574
|
+
}
|
|
15575
|
+
return keywordRank(query, items, topK);
|
|
15576
|
+
}
|
|
15577
|
+
var STOPWORDS;
|
|
15578
|
+
var init_retrieval = __esm({
|
|
15579
|
+
"src/memory/retrieval.ts"() {
|
|
15580
|
+
"use strict";
|
|
15581
|
+
STOPWORDS = /* @__PURE__ */ new Set([
|
|
15582
|
+
"the",
|
|
15583
|
+
"and",
|
|
15584
|
+
"for",
|
|
15585
|
+
"are",
|
|
15586
|
+
"was",
|
|
15587
|
+
"were",
|
|
15588
|
+
"with",
|
|
15589
|
+
"that",
|
|
15590
|
+
"this",
|
|
15591
|
+
"from",
|
|
15592
|
+
"have",
|
|
15593
|
+
"has",
|
|
15594
|
+
"had",
|
|
15595
|
+
"our",
|
|
15596
|
+
"your",
|
|
15597
|
+
"you",
|
|
15598
|
+
"what",
|
|
15599
|
+
"which",
|
|
15600
|
+
"how",
|
|
15601
|
+
"why",
|
|
15602
|
+
"who",
|
|
15603
|
+
"when",
|
|
15604
|
+
"where",
|
|
15605
|
+
"into",
|
|
15606
|
+
"about",
|
|
15607
|
+
"over",
|
|
15608
|
+
"than",
|
|
15609
|
+
"then",
|
|
15610
|
+
"they",
|
|
15611
|
+
"them",
|
|
15612
|
+
"their",
|
|
15613
|
+
"there",
|
|
15614
|
+
"here",
|
|
15615
|
+
"been",
|
|
15616
|
+
"being",
|
|
15617
|
+
"does",
|
|
15618
|
+
"did",
|
|
15619
|
+
"doing",
|
|
15620
|
+
"can",
|
|
15621
|
+
"could",
|
|
15622
|
+
"would",
|
|
15623
|
+
"should",
|
|
15624
|
+
"will",
|
|
15625
|
+
"shall",
|
|
15626
|
+
"may",
|
|
15627
|
+
"might",
|
|
15628
|
+
"must",
|
|
15629
|
+
"not",
|
|
15630
|
+
"but",
|
|
15631
|
+
"all",
|
|
15632
|
+
"any",
|
|
15633
|
+
"some",
|
|
15634
|
+
"more",
|
|
15635
|
+
"most",
|
|
15636
|
+
"much",
|
|
15637
|
+
"many",
|
|
15638
|
+
"very",
|
|
15639
|
+
"just",
|
|
15640
|
+
"like",
|
|
15641
|
+
"out",
|
|
15642
|
+
"off",
|
|
15643
|
+
"per",
|
|
15644
|
+
"via",
|
|
15645
|
+
"use",
|
|
15646
|
+
"get",
|
|
15647
|
+
"got"
|
|
15648
|
+
]);
|
|
15649
|
+
}
|
|
15650
|
+
});
|
|
15651
|
+
|
|
14745
15652
|
// src/strategies/readers.ts
|
|
14746
15653
|
import { createHash } from "crypto";
|
|
14747
15654
|
import { existsSync as existsSync15, readFileSync as readFileSync13 } from "fs";
|
|
@@ -14822,6 +15729,37 @@ var init_readers = __esm({
|
|
|
14822
15729
|
}
|
|
14823
15730
|
});
|
|
14824
15731
|
|
|
15732
|
+
// src/memory/knowledge.ts
|
|
15733
|
+
import { existsSync as existsSync16, readFileSync as readFileSync14, appendFileSync as appendFileSync3, readdirSync as readdirSync2 } from "fs";
|
|
15734
|
+
import { join as join13 } from "path";
|
|
15735
|
+
import { randomUUID as randomUUID4 } from "crypto";
|
|
15736
|
+
function knowledgePath() {
|
|
15737
|
+
return join13(getMemoryDir(), KNOWLEDGE_FILE);
|
|
15738
|
+
}
|
|
15739
|
+
function loadKnowledgeChunks() {
|
|
15740
|
+
const path = knowledgePath();
|
|
15741
|
+
if (!existsSync16(path)) return [];
|
|
15742
|
+
const out = [];
|
|
15743
|
+
for (const line of readFileSync14(path, "utf-8").split("\n")) {
|
|
15744
|
+
const trimmed = line.trim();
|
|
15745
|
+
if (!trimmed) continue;
|
|
15746
|
+
try {
|
|
15747
|
+
out.push(JSON.parse(trimmed));
|
|
15748
|
+
} catch {
|
|
15749
|
+
}
|
|
15750
|
+
}
|
|
15751
|
+
return out;
|
|
15752
|
+
}
|
|
15753
|
+
var KNOWLEDGE_FILE;
|
|
15754
|
+
var init_knowledge = __esm({
|
|
15755
|
+
"src/memory/knowledge.ts"() {
|
|
15756
|
+
"use strict";
|
|
15757
|
+
init_store();
|
|
15758
|
+
init_readers();
|
|
15759
|
+
KNOWLEDGE_FILE = "knowledge.jsonl";
|
|
15760
|
+
}
|
|
15761
|
+
});
|
|
15762
|
+
|
|
14825
15763
|
// src/ai/strategy-normalize.ts
|
|
14826
15764
|
function stripFences2(text) {
|
|
14827
15765
|
const trimmed = text.trim();
|
|
@@ -15352,175 +16290,10 @@ var init_strategy = __esm({
|
|
|
15352
16290
|
}
|
|
15353
16291
|
});
|
|
15354
16292
|
|
|
15355
|
-
// src/investigation/verbosity-cli.ts
|
|
15356
|
-
init_context2();
|
|
15357
|
-
init_schema();
|
|
15358
|
-
|
|
15359
|
-
// src/services/verbosity-investigation.ts
|
|
15360
|
-
init_context2();
|
|
15361
|
-
init_schema();
|
|
15362
|
-
init_agentic_loop();
|
|
15363
|
-
init_health_score();
|
|
15364
|
-
init_divergence();
|
|
15365
|
-
|
|
15366
16293
|
// src/memory/store.ts
|
|
15367
|
-
init_store();
|
|
15368
16294
|
import { existsSync as existsSync17, readFileSync as readFileSync15, appendFileSync as appendFileSync4, readdirSync as readdirSync4, writeFileSync as writeFileSync11 } from "fs";
|
|
15369
16295
|
import { join as join16 } from "path";
|
|
15370
16296
|
import { randomUUID as randomUUID5 } from "crypto";
|
|
15371
|
-
|
|
15372
|
-
// src/memory/retrieval.ts
|
|
15373
|
-
var STOPWORDS = /* @__PURE__ */ new Set([
|
|
15374
|
-
"the",
|
|
15375
|
-
"and",
|
|
15376
|
-
"for",
|
|
15377
|
-
"are",
|
|
15378
|
-
"was",
|
|
15379
|
-
"were",
|
|
15380
|
-
"with",
|
|
15381
|
-
"that",
|
|
15382
|
-
"this",
|
|
15383
|
-
"from",
|
|
15384
|
-
"have",
|
|
15385
|
-
"has",
|
|
15386
|
-
"had",
|
|
15387
|
-
"our",
|
|
15388
|
-
"your",
|
|
15389
|
-
"you",
|
|
15390
|
-
"what",
|
|
15391
|
-
"which",
|
|
15392
|
-
"how",
|
|
15393
|
-
"why",
|
|
15394
|
-
"who",
|
|
15395
|
-
"when",
|
|
15396
|
-
"where",
|
|
15397
|
-
"into",
|
|
15398
|
-
"about",
|
|
15399
|
-
"over",
|
|
15400
|
-
"than",
|
|
15401
|
-
"then",
|
|
15402
|
-
"they",
|
|
15403
|
-
"them",
|
|
15404
|
-
"their",
|
|
15405
|
-
"there",
|
|
15406
|
-
"here",
|
|
15407
|
-
"been",
|
|
15408
|
-
"being",
|
|
15409
|
-
"does",
|
|
15410
|
-
"did",
|
|
15411
|
-
"doing",
|
|
15412
|
-
"can",
|
|
15413
|
-
"could",
|
|
15414
|
-
"would",
|
|
15415
|
-
"should",
|
|
15416
|
-
"will",
|
|
15417
|
-
"shall",
|
|
15418
|
-
"may",
|
|
15419
|
-
"might",
|
|
15420
|
-
"must",
|
|
15421
|
-
"not",
|
|
15422
|
-
"but",
|
|
15423
|
-
"all",
|
|
15424
|
-
"any",
|
|
15425
|
-
"some",
|
|
15426
|
-
"more",
|
|
15427
|
-
"most",
|
|
15428
|
-
"much",
|
|
15429
|
-
"many",
|
|
15430
|
-
"very",
|
|
15431
|
-
"just",
|
|
15432
|
-
"like",
|
|
15433
|
-
"out",
|
|
15434
|
-
"off",
|
|
15435
|
-
"per",
|
|
15436
|
-
"via",
|
|
15437
|
-
"use",
|
|
15438
|
-
"get",
|
|
15439
|
-
"got"
|
|
15440
|
-
]);
|
|
15441
|
-
function tokenize(text) {
|
|
15442
|
-
return text.toLowerCase().split(/[^a-z0-9]+/).filter((t) => t.length >= 3 && !STOPWORDS.has(t));
|
|
15443
|
-
}
|
|
15444
|
-
function keywordRank(query, items, topK) {
|
|
15445
|
-
const queryTokens = new Set(tokenize(query));
|
|
15446
|
-
if (queryTokens.size === 0) {
|
|
15447
|
-
return items.slice(-topK).reverse().map((it) => ({ id: it.id, score: 0 }));
|
|
15448
|
-
}
|
|
15449
|
-
const scored = items.map((it) => {
|
|
15450
|
-
const tokens = tokenize(it.text);
|
|
15451
|
-
if (tokens.length === 0) return { id: it.id, score: 0 };
|
|
15452
|
-
let overlap = 0;
|
|
15453
|
-
const seen = /* @__PURE__ */ new Set();
|
|
15454
|
-
for (const t of tokens) {
|
|
15455
|
-
if (queryTokens.has(t) && !seen.has(t)) {
|
|
15456
|
-
overlap++;
|
|
15457
|
-
seen.add(t);
|
|
15458
|
-
}
|
|
15459
|
-
}
|
|
15460
|
-
return { id: it.id, score: overlap / Math.sqrt(tokens.length) };
|
|
15461
|
-
});
|
|
15462
|
-
const positive = scored.filter((s) => s.score > 0).sort((a, b) => b.score - a.score);
|
|
15463
|
-
if (positive.length > 0) return positive.slice(0, topK);
|
|
15464
|
-
return items.slice(-topK).reverse().map((it) => ({ id: it.id, score: 0 }));
|
|
15465
|
-
}
|
|
15466
|
-
function cosine(a, b) {
|
|
15467
|
-
let dot = 0;
|
|
15468
|
-
let na = 0;
|
|
15469
|
-
let nb = 0;
|
|
15470
|
-
const len = Math.min(a.length, b.length);
|
|
15471
|
-
for (let i = 0; i < len; i++) {
|
|
15472
|
-
dot += a[i] * b[i];
|
|
15473
|
-
na += a[i] * a[i];
|
|
15474
|
-
nb += b[i] * b[i];
|
|
15475
|
-
}
|
|
15476
|
-
if (na === 0 || nb === 0) return 0;
|
|
15477
|
-
return dot / (Math.sqrt(na) * Math.sqrt(nb));
|
|
15478
|
-
}
|
|
15479
|
-
async function rankByRelevance(query, items, topK) {
|
|
15480
|
-
if (items.length === 0) return [];
|
|
15481
|
-
try {
|
|
15482
|
-
const { isEmbeddingsEnabled: isEmbeddingsEnabled2, embedText: embedText2, embedItems: embedItems2 } = await Promise.resolve().then(() => (init_embeddings(), embeddings_exports));
|
|
15483
|
-
if (isEmbeddingsEnabled2()) {
|
|
15484
|
-
const queryVec = await embedText2(query);
|
|
15485
|
-
if (queryVec) {
|
|
15486
|
-
const withVecs = await embedItems2(items);
|
|
15487
|
-
const scored = withVecs.filter((it) => it.embedding && it.embedding.length > 0).map((it) => ({ id: it.id, score: cosine(queryVec, it.embedding) })).sort((a, b) => b.score - a.score);
|
|
15488
|
-
if (scored.length > 0) return scored.slice(0, topK);
|
|
15489
|
-
}
|
|
15490
|
-
}
|
|
15491
|
-
} catch {
|
|
15492
|
-
}
|
|
15493
|
-
return keywordRank(query, items, topK);
|
|
15494
|
-
}
|
|
15495
|
-
|
|
15496
|
-
// src/memory/knowledge.ts
|
|
15497
|
-
init_store();
|
|
15498
|
-
init_readers();
|
|
15499
|
-
import { existsSync as existsSync16, readFileSync as readFileSync14, appendFileSync as appendFileSync3, readdirSync as readdirSync2 } from "fs";
|
|
15500
|
-
import { join as join13 } from "path";
|
|
15501
|
-
import { randomUUID as randomUUID4 } from "crypto";
|
|
15502
|
-
var KNOWLEDGE_FILE = "knowledge.jsonl";
|
|
15503
|
-
function knowledgePath() {
|
|
15504
|
-
return join13(getMemoryDir(), KNOWLEDGE_FILE);
|
|
15505
|
-
}
|
|
15506
|
-
function loadKnowledgeChunks() {
|
|
15507
|
-
const path = knowledgePath();
|
|
15508
|
-
if (!existsSync16(path)) return [];
|
|
15509
|
-
const out = [];
|
|
15510
|
-
for (const line of readFileSync14(path, "utf-8").split("\n")) {
|
|
15511
|
-
const trimmed = line.trim();
|
|
15512
|
-
if (!trimmed) continue;
|
|
15513
|
-
try {
|
|
15514
|
-
out.push(JSON.parse(trimmed));
|
|
15515
|
-
} catch {
|
|
15516
|
-
}
|
|
15517
|
-
}
|
|
15518
|
-
return out;
|
|
15519
|
-
}
|
|
15520
|
-
|
|
15521
|
-
// src/memory/store.ts
|
|
15522
|
-
var FACTS_FILE = "facts.jsonl";
|
|
15523
|
-
var LEDGER_FILE = "ledger.jsonl";
|
|
15524
16297
|
function memPath(file) {
|
|
15525
16298
|
return join16(getMemoryDir(), file);
|
|
15526
16299
|
}
|
|
@@ -15637,14 +16410,19 @@ async function buildMemoryBlock(query, opts = {}) {
|
|
|
15637
16410
|
}
|
|
15638
16411
|
return sections.join("\n\n");
|
|
15639
16412
|
}
|
|
15640
|
-
|
|
15641
|
-
|
|
15642
|
-
|
|
15643
|
-
|
|
16413
|
+
var FACTS_FILE, LEDGER_FILE;
|
|
16414
|
+
var init_store2 = __esm({
|
|
16415
|
+
"src/memory/store.ts"() {
|
|
16416
|
+
"use strict";
|
|
16417
|
+
init_store();
|
|
16418
|
+
init_retrieval();
|
|
16419
|
+
init_knowledge();
|
|
16420
|
+
FACTS_FILE = "facts.jsonl";
|
|
16421
|
+
LEDGER_FILE = "ledger.jsonl";
|
|
16422
|
+
}
|
|
16423
|
+
});
|
|
15644
16424
|
|
|
15645
16425
|
// src/ai/thread.ts
|
|
15646
|
-
var DEFAULT_THREAD_CHAR_BUDGET = 24e3;
|
|
15647
|
-
var MIN_RECENT_MESSAGES = 6;
|
|
15648
16426
|
function summarizeAssistantTurn(text, max = 360) {
|
|
15649
16427
|
const plain = text.replace(/#{1,6}\s+/g, "").replace(/\*\*([^*]+)\*\*/g, "$1").replace(/\*([^*]+)\*/g, "$1").replace(/`([^`]+)`/g, "$1").replace(/^---\s*$/gm, "").replace(/\s+/g, " ").trim();
|
|
15650
16428
|
if (plain.length <= max) return plain;
|
|
@@ -15658,7 +16436,6 @@ function summarizeAssistantTurn(text, max = 360) {
|
|
|
15658
16436
|
const trimmed = out.trim();
|
|
15659
16437
|
return trimmed.length > 0 ? trimmed : plain.slice(0, max).replace(/\s+\S*$/, "") + "\u2026";
|
|
15660
16438
|
}
|
|
15661
|
-
var ASSISTANT_SUMMARIZE_THRESHOLD = 420;
|
|
15662
16439
|
function mergeConsecutive(messages) {
|
|
15663
16440
|
const out = [];
|
|
15664
16441
|
for (const message of messages) {
|
|
@@ -15712,6 +16489,58 @@ function boundConversation(messages, charBudget = DEFAULT_THREAD_CHAR_BUDGET) {
|
|
|
15712
16489
|
function distillThread(rawMessages, charBudget = DEFAULT_THREAD_CHAR_BUDGET) {
|
|
15713
16490
|
return boundConversation(compactConversation(rawMessages), charBudget);
|
|
15714
16491
|
}
|
|
16492
|
+
var DEFAULT_THREAD_CHAR_BUDGET, MIN_RECENT_MESSAGES, ASSISTANT_SUMMARIZE_THRESHOLD;
|
|
16493
|
+
var init_thread = __esm({
|
|
16494
|
+
"src/ai/thread.ts"() {
|
|
16495
|
+
"use strict";
|
|
16496
|
+
DEFAULT_THREAD_CHAR_BUDGET = 24e3;
|
|
16497
|
+
MIN_RECENT_MESSAGES = 6;
|
|
16498
|
+
ASSISTANT_SUMMARIZE_THRESHOLD = 420;
|
|
16499
|
+
}
|
|
16500
|
+
});
|
|
16501
|
+
|
|
16502
|
+
// src/ai/explore-mode.ts
|
|
16503
|
+
function isDeepDiveQuestion(question) {
|
|
16504
|
+
return DEEP_DIVE_PATTERNS.some((p) => p.test(question.trim()));
|
|
16505
|
+
}
|
|
16506
|
+
function resolveExploreResponseMode(question, ctx, priorTurnCount) {
|
|
16507
|
+
if (isDeepDiveQuestion(question)) return "deep";
|
|
16508
|
+
if (isAnalysisReady(ctx)) return "brief";
|
|
16509
|
+
if (ctx.stage === "analyzed" || ctx.analysis.completed.length > 0) return "brief";
|
|
16510
|
+
if (priorTurnCount === 0) return "deep";
|
|
16511
|
+
return "brief";
|
|
16512
|
+
}
|
|
16513
|
+
function parsePromptExperiment(raw) {
|
|
16514
|
+
if (!raw || raw === "production") return "production";
|
|
16515
|
+
if (raw === "baseline" || raw === "a" || raw === "b" || raw === "c") return raw;
|
|
16516
|
+
return "production";
|
|
16517
|
+
}
|
|
16518
|
+
var DEEP_DIVE_PATTERNS;
|
|
16519
|
+
var init_explore_mode = __esm({
|
|
16520
|
+
"src/ai/explore-mode.ts"() {
|
|
16521
|
+
"use strict";
|
|
16522
|
+
init_context2();
|
|
16523
|
+
DEEP_DIVE_PATTERNS = [
|
|
16524
|
+
/\b(break down|breakdown|drill|dig deeper|show me|list all|by rep|by stage|by segment|query|sql|detail|expand|elaborate|full analysis|walk me through|pull up|give me the data)\b/i,
|
|
16525
|
+
/\b(how many|which deals|which accounts|who owns|top \d+|every deal|all stuck)\b/i
|
|
16526
|
+
];
|
|
16527
|
+
}
|
|
16528
|
+
});
|
|
16529
|
+
|
|
16530
|
+
// src/investigation/verbosity-cli.ts
|
|
16531
|
+
init_context2();
|
|
16532
|
+
init_schema();
|
|
16533
|
+
|
|
16534
|
+
// src/services/verbosity-investigation.ts
|
|
16535
|
+
init_context2();
|
|
16536
|
+
init_schema();
|
|
16537
|
+
init_agentic_loop();
|
|
16538
|
+
init_health_score();
|
|
16539
|
+
init_divergence();
|
|
16540
|
+
init_store2();
|
|
16541
|
+
init_context2();
|
|
16542
|
+
init_phase();
|
|
16543
|
+
init_thread();
|
|
15715
16544
|
|
|
15716
16545
|
// src/ai/verbosity-metrics.ts
|
|
15717
16546
|
function analyzeAnswerText(text) {
|
|
@@ -15744,29 +16573,8 @@ function formatInvestigationTable(rows) {
|
|
|
15744
16573
|
return [header, ...lines].join("\n");
|
|
15745
16574
|
}
|
|
15746
16575
|
|
|
15747
|
-
// src/ai/explore-mode.ts
|
|
15748
|
-
init_context2();
|
|
15749
|
-
var DEEP_DIVE_PATTERNS = [
|
|
15750
|
-
/\b(break down|breakdown|drill|dig deeper|show me|list all|by rep|by stage|by segment|query|sql|detail|expand|elaborate|full analysis|walk me through|pull up|give me the data)\b/i,
|
|
15751
|
-
/\b(how many|which deals|which accounts|who owns|top \d+|every deal|all stuck)\b/i
|
|
15752
|
-
];
|
|
15753
|
-
function isDeepDiveQuestion(question) {
|
|
15754
|
-
return DEEP_DIVE_PATTERNS.some((p) => p.test(question.trim()));
|
|
15755
|
-
}
|
|
15756
|
-
function resolveExploreResponseMode(question, ctx, priorTurnCount) {
|
|
15757
|
-
if (isDeepDiveQuestion(question)) return "deep";
|
|
15758
|
-
if (isAnalysisReady(ctx)) return "brief";
|
|
15759
|
-
if (ctx.stage === "analyzed" || ctx.analysis.completed.length > 0) return "brief";
|
|
15760
|
-
if (priorTurnCount === 0) return "deep";
|
|
15761
|
-
return "brief";
|
|
15762
|
-
}
|
|
15763
|
-
function parsePromptExperiment(raw) {
|
|
15764
|
-
if (!raw || raw === "production") return "production";
|
|
15765
|
-
if (raw === "baseline" || raw === "a" || raw === "b" || raw === "c") return raw;
|
|
15766
|
-
return "production";
|
|
15767
|
-
}
|
|
15768
|
-
|
|
15769
16576
|
// src/services/verbosity-investigation.ts
|
|
16577
|
+
init_explore_mode();
|
|
15770
16578
|
init_session_analysis();
|
|
15771
16579
|
init_agentic_loop();
|
|
15772
16580
|
var FOLLOW_UP_QUESTIONS = [
|
|
@@ -15895,6 +16703,7 @@ ${turn.answer}`;
|
|
|
15895
16703
|
}
|
|
15896
16704
|
|
|
15897
16705
|
// src/investigation/verbosity-cli.ts
|
|
16706
|
+
init_explore_mode();
|
|
15898
16707
|
function runUnitChecks() {
|
|
15899
16708
|
const verbose = analyzeAnswerText(`### Risk
|
|
15900
16709
|
|