@sonnechasser/ntrp 1.4.2 → 1.4.5

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.
Files changed (3) hide show
  1. package/dist/index.js +461 -225
  2. package/dist/mcp/server.js +186 -115
  3. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -849,13 +849,21 @@ var init_theme = __esm({
849
849
  });
850
850
 
851
851
  // src/services/handoff-skill.ts
852
- import { mkdirSync as mkdirSync3 } from "fs";
852
+ import { existsSync as existsSync2, mkdirSync as mkdirSync3 } from "fs";
853
853
  import { basename as basename2, join as join2 } from "path";
854
854
  import { homedir as homedir3 } from "os";
855
855
  import chalk2 from "chalk";
856
- function defaultAiInboxDir() {
856
+ function legacyAiInboxDir() {
857
857
  return join2(homedir3(), "Documents", "Claude", "ntrp-inbox");
858
858
  }
859
+ function defaultAiInboxDirDisplay() {
860
+ return "~/Documents/ntrp-inbox";
861
+ }
862
+ function defaultAiInboxDir() {
863
+ const legacy = legacyAiInboxDir();
864
+ if (existsSync2(legacy)) return legacy;
865
+ return join2(homedir3(), "Documents", "ntrp-inbox");
866
+ }
859
867
  function handoffLocations() {
860
868
  const archiveRoot = getExportsDir();
861
869
  const archiveLatestDir = join2(archiveRoot, "latest");
@@ -948,7 +956,7 @@ function filenamePattern(ctx) {
948
956
  return `${datePrefix}*${ext}`;
949
957
  }
950
958
  function buildStandingSkillMarkdown(loc = handoffLocations()) {
951
- const inboxBlock = loc.inboxDir ? `Inbox (preferred \u2014 point Claude Desktop / a project / Cursor at this folder):
959
+ const inboxBlock = loc.inboxDir ? `Inbox (preferred \u2014 point Claude, ChatGPT, Cursor, or another desktop AI at this folder):
952
960
  \`${loc.inboxDir}\`
953
961
 
954
962
  Start with:
@@ -1071,7 +1079,9 @@ function maybePrintAiInboxNudge() {
1071
1079
  if (getConfigValue("ai-inbox-nudge-seen") === "true") return;
1072
1080
  setConfigValue("ai-inbox-nudge-seen", "true");
1073
1081
  console.log(
1074
- " " + chalk2.dim("Set a pickup folder. Type /inbox set ~/Documents/Claude/ntrp-inbox then /inbox skill")
1082
+ " " + chalk2.dim(
1083
+ `Set a pickup folder. Type /inbox set ${defaultAiInboxDirDisplay()} then /inbox skill`
1084
+ )
1075
1085
  );
1076
1086
  }
1077
1087
  function printHandoffDelivered(event, opts = {}) {
@@ -1091,7 +1101,9 @@ function printStandingSkill() {
1091
1101
  console.log();
1092
1102
  console.log(" " + bold("NTRP handoff finder skill"));
1093
1103
  console.log(
1094
- " " + chalk2.dim("Paste this skill once into Claude, ChatGPT, or Cursor. Later handoffs do not print it again.")
1104
+ " " + chalk2.dim(
1105
+ "Paste this skill once into your desktop AI (Claude, ChatGPT, Cursor, \u2026). Later handoffs do not print it again."
1106
+ )
1095
1107
  );
1096
1108
  printPlainBlock(buildStandingSkillMarkdown(loc));
1097
1109
  console.log(" " + chalk2.dim("Written: ") + (loc.inboxSkill ?? loc.archiveSkill));
@@ -1144,7 +1156,7 @@ import {
1144
1156
  appendFileSync,
1145
1157
  copyFileSync,
1146
1158
  cpSync,
1147
- existsSync as existsSync2,
1159
+ existsSync as existsSync3,
1148
1160
  mkdirSync as mkdirSync4,
1149
1161
  readFileSync as readFileSync2,
1150
1162
  readdirSync,
@@ -1165,13 +1177,13 @@ function ensureExportsLayout(root = getExportsDir()) {
1165
1177
  mkdirSync4(join3(root, sub), { recursive: true });
1166
1178
  }
1167
1179
  const readme = join3(root, "README.md");
1168
- if (!existsSync2(readme)) {
1180
+ if (!existsSync3(readme)) {
1169
1181
  writeFileSync3(readme, ARCHIVE_README, "utf-8");
1170
1182
  }
1171
- if (!existsSync2(join3(root, "INDEX.md"))) {
1183
+ if (!existsSync3(join3(root, "INDEX.md"))) {
1172
1184
  writeFileSync3(join3(root, "INDEX.md"), "# NTRP exports\n\n_No exports yet._\n", "utf-8");
1173
1185
  }
1174
- if (!existsSync2(join3(root, "manifest.jsonl"))) {
1186
+ if (!existsSync3(join3(root, "manifest.jsonl"))) {
1175
1187
  writeFileSync3(join3(root, "manifest.jsonl"), "", "utf-8");
1176
1188
  }
1177
1189
  return root;
@@ -1205,7 +1217,7 @@ function ensureInboxLayout(inbox) {
1205
1217
  mkdirSync4(join3(inbox, "archive"), { recursive: true });
1206
1218
  const readme = join3(inbox, "README.md");
1207
1219
  writeFileSync3(readme, buildInboxReadme(), "utf-8");
1208
- if (!existsSync2(join3(inbox, "INDEX.md"))) {
1220
+ if (!existsSync3(join3(inbox, "INDEX.md"))) {
1209
1221
  writeFileSync3(join3(inbox, "INDEX.md"), "# NTRP AI inbox\n\n_No exports synced yet._\n", "utf-8");
1210
1222
  }
1211
1223
  }
@@ -1214,7 +1226,7 @@ function manifestPath(root = getExportsDir()) {
1214
1226
  }
1215
1227
  function readManifestEvents(root = getExportsDir()) {
1216
1228
  const path = manifestPath(root);
1217
- if (!existsSync2(path)) return [];
1229
+ if (!existsSync3(path)) return [];
1218
1230
  const text = readFileSync2(path, "utf-8");
1219
1231
  const events = [];
1220
1232
  for (const line of text.split("\n")) {
@@ -1269,7 +1281,7 @@ function updateArchiveLatest(kind, sourcePath, root) {
1269
1281
  }
1270
1282
  function copyPath(src, dest) {
1271
1283
  mkdirSync4(dirname2(dest), { recursive: true });
1272
- if (existsSync2(dest)) {
1284
+ if (existsSync3(dest)) {
1273
1285
  rmSync(dest, { recursive: true, force: true });
1274
1286
  }
1275
1287
  const st = statSync(src);
@@ -1289,7 +1301,7 @@ function regenerateIndex(root = getExportsDir()) {
1289
1301
  const withHistory = items.filter((e) => (e.previous_paths?.length ?? 0) > 0);
1290
1302
  const latestDir = join3(root, "latest");
1291
1303
  const latestLines = [];
1292
- if (existsSync2(latestDir)) {
1304
+ if (existsSync3(latestDir)) {
1293
1305
  for (const name of readdirSync(latestDir).sort()) {
1294
1306
  latestLines.push(`- \`latest/${name}\` \u2192 \`${join3(latestDir, name)}\``);
1295
1307
  }
@@ -1299,7 +1311,7 @@ function regenerateIndex(root = getExportsDir()) {
1299
1311
  "",
1300
1312
  `Archive root: \`${root}\``,
1301
1313
  "",
1302
- "Set an inbox with `/onboard` or `/inbox set`. Paste `SKILL.md` once into Claude. Later handoffs overwrite `latest-handoff.md`.",
1314
+ "Set an inbox with `/onboard` or `/inbox set`. Paste `SKILL.md` once into your desktop AI. Later handoffs overwrite `latest-handoff.md`.",
1303
1315
  "",
1304
1316
  "## Latest pointers",
1305
1317
  ""
@@ -1349,18 +1361,18 @@ function regenerateInboxIndex(inbox) {
1349
1361
  const lines = [
1350
1362
  "# NTRP AI inbox",
1351
1363
  "",
1352
- "Start here. Install `SKILL.md` once in Claude. Newest prompt: `latest-handoff.md`. Catalog: `INDEX.md`.",
1364
+ "Start here. Install `SKILL.md` once in your desktop AI. Newest prompt: `latest-handoff.md`. Catalog: `INDEX.md`.",
1353
1365
  "",
1354
1366
  `Canonical archive: \`${archiveRoot}\` (see \`${join3(archiveRoot, "INDEX.md")}\`).`,
1355
1367
  "",
1356
1368
  "## Latest pointers",
1357
1369
  ""
1358
1370
  ];
1359
- if (existsSync2(join3(inbox, "SKILL.md"))) {
1371
+ if (existsSync3(join3(inbox, "SKILL.md"))) {
1360
1372
  lines.push("- [`SKILL.md`](./SKILL.md) \u2014 standing finder. Paste once into your agent.");
1361
1373
  }
1362
1374
  const latestNames = readdirSync(inbox).filter((n) => n.startsWith("latest-")).sort();
1363
- if (latestNames.length === 0 && !existsSync2(join3(inbox, "SKILL.md"))) {
1375
+ if (latestNames.length === 0 && !existsSync3(join3(inbox, "SKILL.md"))) {
1364
1376
  lines.push("_None yet. Run a handoff after `/inbox set`._");
1365
1377
  } else {
1366
1378
  for (const name of latestNames) {
@@ -1388,7 +1400,7 @@ This folder is the landing folder for NTRP handoffs. Desktop AI tools read files
1388
1400
 
1389
1401
  ## Start here
1390
1402
 
1391
- 1. Install \`SKILL.md\` once in Claude, ChatGPT, or Cursor. Then tell the agent to open the latest NTRP handoff.
1403
+ 1. Install \`SKILL.md\` once in Claude, ChatGPT, Cursor, or another desktop AI. Then tell the agent to open the latest NTRP handoff.
1392
1404
  2. The newest prompt is \`latest-handoff.md\` (or \`latest-handoff-deck.md\` and similar).
1393
1405
  3. \`latest-pickup.md\` names the file that was just written.
1394
1406
 
@@ -1406,7 +1418,7 @@ Set the folder with \`/inbox set <path>\`. Print the skill with \`/inbox skill\`
1406
1418
  `;
1407
1419
  }
1408
1420
  function pruneInboxArchive(archiveDir, keep = INBOX_ARCHIVE_KEEP) {
1409
- if (!existsSync2(archiveDir)) return;
1421
+ if (!existsSync3(archiveDir)) return;
1410
1422
  const entries2 = readdirSync(archiveDir).map((name) => {
1411
1423
  const p = join3(archiveDir, name);
1412
1424
  try {
@@ -1422,7 +1434,7 @@ function pruneInboxArchive(archiveDir, keep = INBOX_ARCHIVE_KEEP) {
1422
1434
  function syncAiInbox(entry) {
1423
1435
  const inbox = getAiInboxDir();
1424
1436
  if (!inbox) return null;
1425
- if (!existsSync2(entry.path)) return null;
1437
+ if (!existsSync3(entry.path)) return null;
1426
1438
  ensureInboxLayout(inbox);
1427
1439
  const archiveDir = join3(inbox, "archive");
1428
1440
  mkdirSync4(archiveDir, { recursive: true });
@@ -1446,7 +1458,7 @@ function syncRecentToInbox(limit = 10) {
1446
1458
  const items = listExports({ limit });
1447
1459
  let n = 0;
1448
1460
  for (const item of items) {
1449
- if (!existsSync2(item.path)) continue;
1461
+ if (!existsSync3(item.path)) continue;
1450
1462
  const inboxPath = syncAiInbox(item);
1451
1463
  if (inboxPath) {
1452
1464
  appendManifestEvent({
@@ -1460,14 +1472,14 @@ function syncRecentToInbox(limit = 10) {
1460
1472
  }
1461
1473
  regenerateInboxIndex(inbox);
1462
1474
  regenerateIndex();
1463
- const newest = items.find((e) => existsSync2(e.path));
1475
+ const newest = items.find((e) => existsSync3(e.path));
1464
1476
  if (newest) persistHandoffSkillFiles(newest);
1465
1477
  return n;
1466
1478
  }
1467
1479
  function recordExportWrite(opts) {
1468
1480
  const root = ensureExportsLayout();
1469
1481
  const path = resolve3(opts.path);
1470
- if (!existsSync2(path)) {
1482
+ if (!existsSync3(path)) {
1471
1483
  throw new Error(`Export path does not exist: ${path}`);
1472
1484
  }
1473
1485
  updateArchiveLatest(opts.kind, path, root);
@@ -1492,14 +1504,14 @@ function moveExport(idOrPath, destDir) {
1492
1504
  if (!item) {
1493
1505
  throw new Error(`No export matching "${idOrPath}". Try /exports list.`);
1494
1506
  }
1495
- if (!existsSync2(item.path)) {
1507
+ if (!existsSync3(item.path)) {
1496
1508
  throw new Error(`Export file missing on disk: ${item.path}`);
1497
1509
  }
1498
1510
  const destRoot = resolveUserPath(destDir);
1499
1511
  mkdirSync4(destRoot, { recursive: true });
1500
1512
  const name = basename3(item.path);
1501
1513
  let destPath = join3(destRoot, name);
1502
- if (existsSync2(destPath)) {
1514
+ if (existsSync3(destPath)) {
1503
1515
  destPath = join3(destRoot, `${exportStamp()}-${name}`);
1504
1516
  }
1505
1517
  renameSync(item.path, destPath);
@@ -1527,13 +1539,13 @@ function archiveIndexPath() {
1527
1539
  }
1528
1540
  function archiveLatestHandoffPath() {
1529
1541
  const p = join3(ensureExportsLayout(), "latest", "handoff.md");
1530
- return existsSync2(p) ? p : null;
1542
+ return existsSync3(p) ? p : null;
1531
1543
  }
1532
1544
  function inboxLatestHandoffPath() {
1533
1545
  const inbox = getAiInboxDir();
1534
1546
  if (!inbox) return null;
1535
1547
  const p = join3(inbox, "latest-handoff.md");
1536
- return existsSync2(p) ? p : null;
1548
+ return existsSync3(p) ? p : null;
1537
1549
  }
1538
1550
  function formatExportLocationLines(event) {
1539
1551
  const lines = [`Archive: ${event.path}`];
@@ -1575,10 +1587,10 @@ This archive stores handoffs, reports, notes, CSV receipts, and publish packages
1575
1587
  Set a dedicated inbox for desktop AI instead of this folder:
1576
1588
 
1577
1589
  \`\`\`
1578
- /inbox set ~/Documents/Claude/ntrp-inbox
1590
+ /inbox set ~/Documents/ntrp-inbox
1579
1591
  \`\`\`
1580
1592
 
1581
- After \`/inbox set\` or the optional \`/onboard\` step, paste \`latest/SKILL.md\` once into Claude. Later \`/handoff\` overwrites \`latest-handoff.md\`. The skill is not printed again.
1593
+ After \`/inbox set\` or the optional \`/onboard\` step, paste \`latest/SKILL.md\` once into your desktop AI. Later \`/handoff\` overwrites \`latest-handoff.md\`. The skill is not printed again.
1582
1594
  `;
1583
1595
  }
1584
1596
  });
@@ -1808,7 +1820,7 @@ __export(transcript_exports, {
1808
1820
  startSessionTranscript: () => startSessionTranscript,
1809
1821
  stopSessionTranscript: () => stopSessionTranscript
1810
1822
  });
1811
- import { existsSync as existsSync3, readFileSync as readFileSync3, writeFileSync as writeFileSync5, rmSync as rmSync2 } from "fs";
1823
+ import { existsSync as existsSync4, readFileSync as readFileSync3, writeFileSync as writeFileSync5, rmSync as rmSync2 } from "fs";
1812
1824
  import { join as join4 } from "path";
1813
1825
  function startSessionTranscript(ctx) {
1814
1826
  if (ctx.oneShot || state) return;
@@ -1819,7 +1831,7 @@ function startSessionTranscript(ctx) {
1819
1831
  function rebindSessionTranscript(ctx) {
1820
1832
  if (!state || state.sessionId === ctx.sessionId) return;
1821
1833
  const priorJson = join4(getSessionsDir(), `${state.sessionId}.json`);
1822
- if (existsSync3(priorJson)) {
1834
+ if (existsSync4(priorJson)) {
1823
1835
  finalizeCurrentFile("switched session");
1824
1836
  } else {
1825
1837
  discardSessionTranscript(state.sessionId);
@@ -1893,7 +1905,7 @@ function removeTees() {
1893
1905
  function createState(sessionId) {
1894
1906
  const filePath = transcriptPathForSession(sessionId);
1895
1907
  let base = "";
1896
- if (existsSync3(filePath)) {
1908
+ if (existsSync4(filePath)) {
1897
1909
  try {
1898
1910
  base = readFileSync3(filePath, "utf-8").trimEnd() + "\n";
1899
1911
  } catch {
@@ -2012,12 +2024,12 @@ __export(connection_exports, {
2012
2024
  run: () => run,
2013
2025
  setActiveDbPath: () => setActiveDbPath
2014
2026
  });
2015
- import { mkdirSync as mkdirSync5, existsSync as existsSync4, rmSync as rmSync3 } from "fs";
2027
+ import { mkdirSync as mkdirSync5, existsSync as existsSync5, rmSync as rmSync3 } from "fs";
2016
2028
  import { dirname as dirname3, join as join5, resolve as resolve4 } from "path";
2017
2029
  function ensureDir2() {
2018
2030
  secureNtrpHome();
2019
2031
  const dir = dirname3(activeDbPath);
2020
- if (!existsSync4(dir)) {
2032
+ if (!existsSync5(dir)) {
2021
2033
  mkdirSync5(dir, { recursive: true, mode: 448 });
2022
2034
  }
2023
2035
  }
@@ -3285,14 +3297,14 @@ CREATE INDEX IF NOT EXISTS idx_health_readings_batch ON health_readings(upload_b
3285
3297
 
3286
3298
  // src/config/install.ts
3287
3299
  import { randomUUID as randomUUID3 } from "crypto";
3288
- import { existsSync as existsSync5, mkdirSync as mkdirSync6, readFileSync as readFileSync4, unlinkSync, writeFileSync as writeFileSync6 } from "fs";
3300
+ import { existsSync as existsSync6, mkdirSync as mkdirSync6, readFileSync as readFileSync4, unlinkSync, writeFileSync as writeFileSync6 } from "fs";
3289
3301
  import { join as join6 } from "path";
3290
3302
  function installPath() {
3291
3303
  return join6(ntrpHome(), "install.json");
3292
3304
  }
3293
3305
  function ensureDir3() {
3294
3306
  const dir = ntrpHome();
3295
- if (!existsSync5(dir)) {
3307
+ if (!existsSync6(dir)) {
3296
3308
  mkdirSync6(dir, { recursive: true });
3297
3309
  }
3298
3310
  }
@@ -3304,7 +3316,7 @@ function isValidInstall(value) {
3304
3316
  function ensureInstall() {
3305
3317
  if (cachedInstall) return cachedInstall;
3306
3318
  const path = installPath();
3307
- if (existsSync5(path)) {
3319
+ if (existsSync6(path)) {
3308
3320
  try {
3309
3321
  const parsed = JSON.parse(readFileSync4(path, "utf-8"));
3310
3322
  if (isValidInstall(parsed)) {
@@ -3340,7 +3352,7 @@ var init_install = __esm({
3340
3352
  });
3341
3353
 
3342
3354
  // src/config/progress-migrate.ts
3343
- import { existsSync as existsSync6, readFileSync as readFileSync5, renameSync as renameSync2, writeFileSync as writeFileSync7 } from "fs";
3355
+ import { existsSync as existsSync7, readFileSync as readFileSync5, renameSync as renameSync2, writeFileSync as writeFileSync7 } from "fs";
3344
3356
  import { join as join7 } from "path";
3345
3357
  function legacyStatePath() {
3346
3358
  return join7(ntrpHome(), "state.json");
@@ -3357,9 +3369,9 @@ function isValidLegacyState(value) {
3357
3369
  return s.schema_version === 1 && typeof s.total_minutes_saved === "number" && Array.isArray(s.credits) && Array.isArray(s.milestones_unlocked);
3358
3370
  }
3359
3371
  function migrateLegacyStateIfNeeded(installId) {
3360
- if (existsSync6(progressPath())) return null;
3372
+ if (existsSync7(progressPath())) return null;
3361
3373
  const legacyPath = legacyStatePath();
3362
- if (!existsSync6(legacyPath)) return null;
3374
+ if (!existsSync7(legacyPath)) return null;
3363
3375
  try {
3364
3376
  const parsed = JSON.parse(readFileSync5(legacyPath, "utf-8"));
3365
3377
  if (!isValidLegacyState(parsed)) return null;
@@ -3476,7 +3488,7 @@ var init_usage_backfill = __esm({
3476
3488
  });
3477
3489
 
3478
3490
  // src/config/progress.ts
3479
- import { existsSync as existsSync7, mkdirSync as mkdirSync7, readFileSync as readFileSync6, unlinkSync as unlinkSync2, writeFileSync as writeFileSync8 } from "fs";
3491
+ import { existsSync as existsSync8, mkdirSync as mkdirSync7, readFileSync as readFileSync6, unlinkSync as unlinkSync2, writeFileSync as writeFileSync8 } from "fs";
3480
3492
  import { join as join8 } from "path";
3481
3493
  function progressPath2() {
3482
3494
  return join8(ntrpHome(), "progress.json");
@@ -3489,7 +3501,7 @@ function legacyStateBackupPath2() {
3489
3501
  }
3490
3502
  function ensureDir4() {
3491
3503
  const dir = ntrpHome();
3492
- if (!existsSync7(dir)) {
3504
+ if (!existsSync8(dir)) {
3493
3505
  mkdirSync7(dir, { recursive: true });
3494
3506
  }
3495
3507
  }
@@ -3520,7 +3532,7 @@ function reconcileInstallId(state2) {
3520
3532
  }
3521
3533
  function readProgressFile() {
3522
3534
  const path = progressPath2();
3523
- if (!existsSync7(path)) return { state: null, changed: false };
3535
+ if (!existsSync8(path)) return { state: null, changed: false };
3524
3536
  try {
3525
3537
  const parsed = JSON.parse(readFileSync6(path, "utf-8"));
3526
3538
  if (!isValidProgress(parsed)) return { state: null, changed: false };
@@ -3568,7 +3580,7 @@ function saveProgress(state2) {
3568
3580
  function wipeProgressFiles() {
3569
3581
  installMismatchWarned = false;
3570
3582
  for (const path of [progressPath2(), legacyStatePath2(), legacyStateBackupPath2()]) {
3571
- if (existsSync7(path)) {
3583
+ if (existsSync8(path)) {
3572
3584
  unlinkSync2(path);
3573
3585
  }
3574
3586
  }
@@ -4283,7 +4295,7 @@ __export(providers_exports, {
4283
4295
  resetProvidersCache: () => resetProvidersCache,
4284
4296
  saveCustomProvider: () => saveCustomProvider
4285
4297
  });
4286
- import { existsSync as existsSync8, readFileSync as readFileSync7 } from "fs";
4298
+ import { existsSync as existsSync9, readFileSync as readFileSync7 } from "fs";
4287
4299
  import { join as join9 } from "path";
4288
4300
  function providersPath() {
4289
4301
  return join9(ntrpHome(), "providers.json");
@@ -4291,7 +4303,7 @@ function providersPath() {
4291
4303
  function loadCustomProviders() {
4292
4304
  if (cachedEntries) return cachedEntries;
4293
4305
  const path = providersPath();
4294
- if (!existsSync8(path)) {
4306
+ if (!existsSync9(path)) {
4295
4307
  cachedEntries = [];
4296
4308
  return cachedEntries;
4297
4309
  }
@@ -5080,7 +5092,7 @@ var init_openai_compat = __esm({
5080
5092
  });
5081
5093
 
5082
5094
  // src/ai/llm/models-cache.ts
5083
- import { existsSync as existsSync9, readFileSync as readFileSync8, writeFileSync as writeFileSync9 } from "fs";
5095
+ import { existsSync as existsSync10, readFileSync as readFileSync8, writeFileSync as writeFileSync9 } from "fs";
5084
5096
  import { join as join10 } from "path";
5085
5097
  function cachePath() {
5086
5098
  return join10(ntrpHome(), "models.json");
@@ -5088,7 +5100,7 @@ function cachePath() {
5088
5100
  function loadFile() {
5089
5101
  if (cached) return cached;
5090
5102
  const path = cachePath();
5091
- if (!existsSync9(path)) {
5103
+ if (!existsSync10(path)) {
5092
5104
  cached = { version: 1, providers: {} };
5093
5105
  return cached;
5094
5106
  }
@@ -5765,7 +5777,7 @@ __export(pseudonymize_exports, {
5765
5777
  wipePrivacyStore: () => wipePrivacyStore
5766
5778
  });
5767
5779
  import { createHmac, randomBytes } from "crypto";
5768
- import { existsSync as existsSync10, mkdirSync as mkdirSync8, readFileSync as readFileSync10, rmSync as rmSync4, chmodSync as chmodSync2 } from "fs";
5780
+ import { existsSync as existsSync11, mkdirSync as mkdirSync8, readFileSync as readFileSync10, rmSync as rmSync4, chmodSync as chmodSync2 } from "fs";
5769
5781
  import { join as join11 } from "path";
5770
5782
  function privacyDir() {
5771
5783
  return join11(ntrpHome(), "privacy");
@@ -5784,7 +5796,7 @@ function chmodQuiet2(path, mode) {
5784
5796
  }
5785
5797
  function ensurePrivacyDir() {
5786
5798
  const dir = privacyDir();
5787
- if (!existsSync10(dir)) {
5799
+ if (!existsSync11(dir)) {
5788
5800
  mkdirSync8(dir, { recursive: true, mode: 448 });
5789
5801
  }
5790
5802
  chmodQuiet2(dir, 448);
@@ -5821,7 +5833,7 @@ function rebuildIndexes() {
5821
5833
  function loadSecret() {
5822
5834
  ensurePrivacyDir();
5823
5835
  const path = secretPath();
5824
- if (existsSync10(path)) {
5836
+ if (existsSync11(path)) {
5825
5837
  const raw = readFileSync10(path);
5826
5838
  const buf = raw.length >= 32 ? raw.subarray(0, 32) : Buffer.from(raw.toString("utf-8").trim(), "hex");
5827
5839
  if (buf.length >= 16) return buf.length === 32 ? buf : Buffer.concat([buf], 32);
@@ -5835,7 +5847,7 @@ function loadLexicon() {
5835
5847
  ensurePrivacyDir();
5836
5848
  secret = loadSecret();
5837
5849
  entries = [];
5838
- if (existsSync10(lexiconPath())) {
5850
+ if (existsSync11(lexiconPath())) {
5839
5851
  try {
5840
5852
  const parsed = JSON.parse(readFileSync10(lexiconPath(), "utf-8"));
5841
5853
  if (parsed?.version === 1 && Array.isArray(parsed.entries)) {
@@ -6127,7 +6139,7 @@ function wipePrivacyStore() {
6127
6139
  loaded = false;
6128
6140
  dirty = false;
6129
6141
  const dir = privacyDir();
6130
- if (existsSync10(dir)) {
6142
+ if (existsSync11(dir)) {
6131
6143
  rmSync4(dir, { recursive: true, force: true });
6132
6144
  }
6133
6145
  }
@@ -6267,10 +6279,19 @@ var init_pseudonymize = __esm({
6267
6279
  });
6268
6280
 
6269
6281
  // src/config/profile.ts
6270
- import { readFileSync as readFileSync11, writeFileSync as writeFileSync10, existsSync as existsSync11, mkdirSync as mkdirSync9 } from "fs";
6282
+ var profile_exports = {};
6283
+ __export(profile_exports, {
6284
+ isProfileConfigured: () => isProfileConfigured,
6285
+ loadProfile: () => loadProfile,
6286
+ profileExists: () => profileExists,
6287
+ profilePath: () => profilePath,
6288
+ saveProfile: () => saveProfile,
6289
+ updateProfile: () => updateProfile
6290
+ });
6291
+ import { readFileSync as readFileSync11, writeFileSync as writeFileSync10, existsSync as existsSync12, mkdirSync as mkdirSync9 } from "fs";
6271
6292
  import { join as join12 } from "path";
6272
6293
  function ensureDir5() {
6273
- if (!existsSync11(NTRP_DIR3)) {
6294
+ if (!existsSync12(NTRP_DIR3)) {
6274
6295
  mkdirSync9(NTRP_DIR3, { recursive: true });
6275
6296
  }
6276
6297
  }
@@ -6278,14 +6299,14 @@ function profilePath() {
6278
6299
  return PROFILE_PATH;
6279
6300
  }
6280
6301
  function profileExists() {
6281
- return existsSync11(PROFILE_PATH);
6302
+ return existsSync12(PROFILE_PATH);
6282
6303
  }
6283
6304
  function isProfileConfigured(profile = loadProfile()) {
6284
6305
  if (!profile) return false;
6285
6306
  return profile.company_name.trim().length > 0;
6286
6307
  }
6287
6308
  function loadProfile() {
6288
- if (!existsSync11(PROFILE_PATH)) return null;
6309
+ if (!existsSync12(PROFILE_PATH)) return null;
6289
6310
  try {
6290
6311
  const parsed = JSON.parse(readFileSync11(PROFILE_PATH, "utf-8"));
6291
6312
  if (!parsed || typeof parsed !== "object") return null;
@@ -6341,7 +6362,7 @@ __export(lexicon_seed_exports, {
6341
6362
  seedOperatorIdentity: () => seedOperatorIdentity,
6342
6363
  wipePrivacyStore: () => wipePrivacyStore2
6343
6364
  });
6344
- import { existsSync as existsSync12 } from "fs";
6365
+ import { existsSync as existsSync13 } from "fs";
6345
6366
  function invalidateLexiconSeed() {
6346
6367
  dbSeedGeneration = -1;
6347
6368
  }
@@ -6403,7 +6424,7 @@ function seedOperatorIdentity(name, url) {
6403
6424
  }
6404
6425
  async function seedFromDuckDB() {
6405
6426
  const dbPath = getActiveDbPath();
6406
- if (!existsSync12(dbPath)) return;
6427
+ if (!existsSync13(dbPath)) return;
6407
6428
  const { all: all2, getConnectionGeneration: getConnectionGeneration2 } = await Promise.resolve().then(() => (init_connection(), connection_exports));
6408
6429
  const gen = getConnectionGeneration2();
6409
6430
  if (dbSeedGeneration !== -1 && gen === dbSeedGeneration) return;
@@ -6816,7 +6837,7 @@ async function llmCompleteText(surface, system, userMessage, max_tokens, ctx) {
6816
6837
  }
6817
6838
  async function llmStreamText(surface, system, userMessage, max_tokens, ctx) {
6818
6839
  let fullText = "";
6819
- let meta = { model_used: "unknown", provider_used: "anthropic", failover: false };
6840
+ let meta = { model_used: "unknown", provider_used: "unknown", failover: false };
6820
6841
  for await (const event of streamWithFailover(
6821
6842
  {
6822
6843
  surface,
@@ -7120,7 +7141,7 @@ var init_errors2 = __esm({
7120
7141
 
7121
7142
  // src/strategies/readers.ts
7122
7143
  import { createHash } from "crypto";
7123
- import { existsSync as existsSync13, readFileSync as readFileSync12 } from "fs";
7144
+ import { existsSync as existsSync14, readFileSync as readFileSync12 } from "fs";
7124
7145
  import { extname, resolve as resolve5 } from "path";
7125
7146
  import { parse as parseYaml } from "yaml";
7126
7147
  import { PDFParse } from "pdf-parse";
@@ -7130,7 +7151,7 @@ async function readStrategyFile(pathOrDash) {
7130
7151
  return createDocument("stdin", null, text2, {});
7131
7152
  }
7132
7153
  const sourcePath = resolve5(pathOrDash);
7133
- if (!existsSync13(sourcePath)) {
7154
+ if (!existsSync14(sourcePath)) {
7134
7155
  throw new NtrpError("strategy_file_not_found", `Strategy file not found: ${pathOrDash}`, 2 /* Usage */);
7135
7156
  }
7136
7157
  const ext = extname(sourcePath).toLowerCase();
@@ -7199,7 +7220,7 @@ var init_readers = __esm({
7199
7220
  });
7200
7221
 
7201
7222
  // src/memory/knowledge.ts
7202
- import { existsSync as existsSync14, readFileSync as readFileSync13, appendFileSync as appendFileSync2, readdirSync as readdirSync2 } from "fs";
7223
+ import { existsSync as existsSync15, readFileSync as readFileSync13, appendFileSync as appendFileSync2, readdirSync as readdirSync2 } from "fs";
7203
7224
  import { join as join13 } from "path";
7204
7225
  import { randomUUID as randomUUID4 } from "crypto";
7205
7226
  function knowledgePath() {
@@ -7207,7 +7228,7 @@ function knowledgePath() {
7207
7228
  }
7208
7229
  function loadKnowledgeChunks() {
7209
7230
  const path = knowledgePath();
7210
- if (!existsSync14(path)) return [];
7231
+ if (!existsSync15(path)) return [];
7211
7232
  const out = [];
7212
7233
  for (const line of readFileSync13(path, "utf-8").split("\n")) {
7213
7234
  const trimmed = line.trim();
@@ -7298,7 +7319,7 @@ var init_knowledge = __esm({
7298
7319
  });
7299
7320
 
7300
7321
  // src/ai/privacy.ts
7301
- import { existsSync as existsSync15, mkdirSync as mkdirSync10, appendFileSync as appendFileSync3 } from "fs";
7322
+ import { existsSync as existsSync16, mkdirSync as mkdirSync10, appendFileSync as appendFileSync3 } from "fs";
7302
7323
  import { homedir as homedir4 } from "os";
7303
7324
  import { join as join14 } from "path";
7304
7325
  function scrubSensitiveText(text) {
@@ -7319,7 +7340,7 @@ function stripPII(obj) {
7319
7340
  return out;
7320
7341
  }
7321
7342
  function ensureAuditDir() {
7322
- if (!existsSync15(AUDIT_DIR)) {
7343
+ if (!existsSync16(AUDIT_DIR)) {
7323
7344
  mkdirSync10(AUDIT_DIR, { recursive: true });
7324
7345
  }
7325
7346
  }
@@ -7435,14 +7456,14 @@ __export(playbook_exports, {
7435
7456
  matchTriggeredPlays: () => matchTriggeredPlays,
7436
7457
  withKnownRecommendedPlays: () => withKnownRecommendedPlays
7437
7458
  });
7438
- import { existsSync as existsSync16, readFileSync as readFileSync14, appendFileSync as appendFileSync4 } from "fs";
7459
+ import { existsSync as existsSync17, readFileSync as readFileSync14, appendFileSync as appendFileSync4 } from "fs";
7439
7460
  import { join as join15 } from "path";
7440
7461
  function playsPath() {
7441
7462
  return join15(getMemoryDir(), PLAYS_FILE);
7442
7463
  }
7443
7464
  function getCustomPlays() {
7444
7465
  const path = playsPath();
7445
- if (!existsSync16(path)) return [];
7466
+ if (!existsSync17(path)) return [];
7446
7467
  const out = [];
7447
7468
  for (const line of readFileSync14(path, "utf-8").split("\n")) {
7448
7469
  const trimmed = line.trim();
@@ -8330,7 +8351,7 @@ __export(store_exports2, {
8330
8351
  rewriteJsonl: () => rewriteJsonl,
8331
8352
  scrubText: () => scrubText
8332
8353
  });
8333
- import { existsSync as existsSync17, readFileSync as readFileSync15, appendFileSync as appendFileSync5, readdirSync as readdirSync4, writeFileSync as writeFileSync12 } from "fs";
8354
+ import { existsSync as existsSync18, readFileSync as readFileSync15, appendFileSync as appendFileSync5, readdirSync as readdirSync4, writeFileSync as writeFileSync12 } from "fs";
8334
8355
  import { join as join18 } from "path";
8335
8356
  import { randomUUID as randomUUID5 } from "crypto";
8336
8357
  function memPath(file) {
@@ -8338,7 +8359,7 @@ function memPath(file) {
8338
8359
  }
8339
8360
  function readJsonl(file) {
8340
8361
  const path = memPath(file);
8341
- if (!existsSync17(path)) return [];
8362
+ if (!existsSync18(path)) return [];
8342
8363
  const out = [];
8343
8364
  for (const line of readFileSync15(path, "utf-8").split("\n")) {
8344
8365
  const trimmed = line.trim();
@@ -8717,7 +8738,7 @@ __export(context_exports, {
8717
8738
  transcriptPathForSession: () => transcriptPathForSession
8718
8739
  });
8719
8740
  import { basename as basename5, join as join19, resolve as resolve7, sep as sep4 } from "path";
8720
- import { existsSync as existsSync18, mkdirSync as mkdirSync11, writeFileSync as writeFileSync13, readFileSync as readFileSync16, readdirSync as readdirSync5, statSync as statSync3, rmSync as rmSync5 } from "fs";
8741
+ import { existsSync as existsSync19, mkdirSync as mkdirSync11, writeFileSync as writeFileSync13, readFileSync as readFileSync16, readdirSync as readdirSync5, statSync as statSync3, rmSync as rmSync5 } from "fs";
8721
8742
  import { homedir as homedir6 } from "os";
8722
8743
  import { randomUUID as randomUUID6 } from "crypto";
8723
8744
  function isSessionStale(s) {
@@ -8736,14 +8757,14 @@ function ntrpHomeDir() {
8736
8757
  }
8737
8758
  function getSessionsDir() {
8738
8759
  const dir = join19(ntrpHomeDir(), "sessions");
8739
- if (!existsSync18(dir)) {
8760
+ if (!existsSync19(dir)) {
8740
8761
  mkdirSync11(dir, { recursive: true });
8741
8762
  }
8742
8763
  return dir;
8743
8764
  }
8744
8765
  function getDatasetsDir() {
8745
8766
  const dir = join19(ntrpHomeDir(), "datasets");
8746
- if (!existsSync18(dir)) {
8767
+ if (!existsSync19(dir)) {
8747
8768
  mkdirSync11(dir, { recursive: true });
8748
8769
  }
8749
8770
  return dir;
@@ -9754,7 +9775,7 @@ var init_admin_confirm = __esm({
9754
9775
  });
9755
9776
 
9756
9777
  // src/services/scratch-wipe.ts
9757
- import { existsSync as existsSync19, rmSync as rmSync6, unlinkSync as unlinkSync3 } from "fs";
9778
+ import { existsSync as existsSync20, rmSync as rmSync6, unlinkSync as unlinkSync3 } from "fs";
9758
9779
  import { join as join20 } from "path";
9759
9780
  async function performScratchWipe(opts = {}) {
9760
9781
  const home = ntrpHome();
@@ -9784,7 +9805,7 @@ async function performScratchWipe(opts = {}) {
9784
9805
  );
9785
9806
  }
9786
9807
  for (const { path, kind } of targets) {
9787
- if (!existsSync19(path)) continue;
9808
+ if (!existsSync20(path)) continue;
9788
9809
  try {
9789
9810
  if (kind === "dir") {
9790
9811
  rmSync6(path, { recursive: true, force: true });
@@ -12336,11 +12357,11 @@ var init_guide_slides = __esm({
12336
12357
  };
12337
12358
  HANDOFF = {
12338
12359
  id: "handoff",
12339
- label: "Ship work to Claude",
12340
- tagline: "Teach the inbox once. Later, tell Claude to pick it up.",
12360
+ label: "Ship work to your AI",
12361
+ tagline: "Teach the inbox once. Later, tell your AI to pick it up.",
12341
12362
  visual: {
12342
12363
  kind: "layer_stack",
12343
- caption: "You and Claude find and open the file. NTRP only writes.",
12364
+ caption: "You and your AI find and open the file. NTRP only writes.",
12344
12365
  layers: [
12345
12366
  { label: "Set a pickup folder (demo, onboard, or /inbox set)" },
12346
12367
  { label: "Paste the finder skill once (/inbox skill)", highlight: true },
@@ -12349,18 +12370,18 @@ var init_guide_slides = __esm({
12349
12370
  ]
12350
12371
  },
12351
12372
  lines: [
12352
- 'Type "ship a board deck" or type /handoff. Files land in ~/Documents/Claude/ntrp-inbox by default. Type /inbox set to change the folder.',
12373
+ 'Type "ship a board deck" or type /handoff. Files land in ~/Documents/ntrp-inbox by default. Type /inbox set to change the folder.',
12353
12374
  "",
12354
- "During demo or company setup, or any time, type /inbox skill. Paste a standing finder into Claude, ChatGPT, or Cursor once. That skill tells the tool to follow latest-handoff.md and INDEX.md.",
12375
+ "During demo or company setup, or any time, type /inbox skill. Paste a standing finder into Claude, ChatGPT, Cursor, or another desktop AI once. That skill tells the tool to follow latest-handoff.md and INDEX.md.",
12355
12376
  "",
12356
- 'After that, each /handoff prints "Handoff ready". You will not get a paste block every write. Tell Claude: pick up the latest NTRP handoff.',
12377
+ 'After that, each /handoff prints "Handoff ready". You will not get a paste block every write. Tell your AI: pick up the latest NTRP handoff.',
12357
12378
  "",
12358
- "If you skipped this step, NTRP asks once when you load your own data. Skip then, and type /inbox set ~/Documents/Claude/ntrp-inbox. Then type /inbox skill. Type /handoff skill to reprint the same finder."
12379
+ "If you skipped this step, NTRP asks once when you load your own data. Skip then, and type /inbox set ~/Documents/ntrp-inbox. Then type /inbox skill. Type /handoff skill to reprint the same finder."
12359
12380
  ],
12360
12381
  deepdive: [
12361
12382
  "Inbox copies: latest-handoff.md, latest-pickup.md, SKILL.md, INDEX.md. Dated files also live under ~/.ntrp/exports/.",
12362
12383
  "Type /exports to list writes. Type /inbox show to print the folder and the latest pointer. Type /handoff --print to show the prompt body in the terminal.",
12363
- "Audience-framed Metric definitions append to decks and reports. Claude then has the same glossary you walked.",
12384
+ "Audience-framed Metric definitions append to decks and reports. Your AI then has the same glossary you walked.",
12364
12385
  "Type /end to close a session. NTRP writes a transcript plus a 1-page context brief under ~/.ntrp/sessions/."
12365
12386
  ]
12366
12387
  };
@@ -12556,7 +12577,7 @@ function paintIntro(opts) {
12556
12577
  "",
12557
12578
  "Vital signs (after) \u2014 Freshness, Flow Rate, Drop Rate, Signal:Noise, Thread Depth \u2014 NTRP's own ontology. Each has a dollar translation. Scores gate in layer order: first red wins.",
12558
12579
  "",
12559
- "How to use NTRP (after vitals) \u2014 talk in English, ask without a key, ship a handoff to Claude once, stay in the diagnose \u2192 plan loop.",
12580
+ "How to use NTRP (after vitals) \u2014 talk in English, ask without a key, ship a handoff to your AI once, stay in the diagnose \u2192 plan loop.",
12560
12581
  "",
12561
12582
  chalk10.dim("No AI key needed for this tour. Press Enter to advance; type /deepdive (or d) on any slide for more.")
12562
12583
  ],
@@ -12576,7 +12597,7 @@ function paintClose(opts) {
12576
12597
  ` ${paint("accent", "/diagnose")} \u2014 five vital signs + dollars at risk`,
12577
12598
  ` ${paint("accent", "/metrics")} \u2014 SaaS scorecard (ARR, NRR, coverage\u2026)`,
12578
12599
  ` ${paint("accent", "/deepdive")} \u2014 replay this tour \xB7 ${paint("accent", "/deepdive guide")} how-to only`,
12579
- ` ${paint("accent", "/handoff")} \u2014 ship a file; teach Claude the inbox once (/inbox skill)`,
12600
+ ` ${paint("accent", "/handoff")} \u2014 ship a file; teach the inbox once (/inbox skill)`,
12580
12601
  ` ${paint("accent", "/strategy")} \u2014 measurable plan \xB7 ${paint("accent", "/help")} shortcuts`,
12581
12602
  "",
12582
12603
  "Fourteen more SaaS metrics live behind /deepdive list \u2014 including unit economics that unlock when spend data lands.",
@@ -12744,7 +12765,7 @@ async function offerFirstRunTour(ctx) {
12744
12765
  console.log();
12745
12766
  console.log(" " + bold("Onboarding tour") + chalk10.dim(" \u2014 ~3 minutes, no AI key required"));
12746
12767
  console.log(
12747
- " " + chalk10.dim("SaaS numbers, five vitals, then how to talk to NTRP and ship work to Claude.")
12768
+ " " + chalk10.dim("SaaS numbers, five vitals, then how to talk to NTRP and ship work to your AI.")
12748
12769
  );
12749
12770
  const tourDefault = firstRunTourDefaultChoice();
12750
12771
  if (tourDefault === "skip") {
@@ -13070,6 +13091,15 @@ var init_spinner = __esm({
13070
13091
  });
13071
13092
 
13072
13093
  // src/pipeline/segments.ts
13094
+ function resolveSegmentByName(query, segments) {
13095
+ const lower = query.toLowerCase();
13096
+ const exact = segments.find((s) => s.segment.name.toLowerCase() === lower);
13097
+ if (exact) return { type: "exact", segment: exact };
13098
+ const matches = segments.filter((s) => s.segment.name.toLowerCase().includes(lower));
13099
+ if (matches.length === 1) return { type: "exact", segment: matches[0] };
13100
+ if (matches.length > 1) return { type: "ambiguous", matches };
13101
+ return { type: "none" };
13102
+ }
13073
13103
  function resolveSegmentScopeFromSnapshot(segment, snapshot) {
13074
13104
  const orgIds = [];
13075
13105
  const peopleIds = [];
@@ -13382,8 +13412,9 @@ function resolveThresholds(salesMotion, computedBaselines) {
13382
13412
  return resolved;
13383
13413
  }
13384
13414
  async function getResolvedThresholds() {
13415
+ const { loadProfile: loadProfile2 } = await Promise.resolve().then(() => (init_profile(), profile_exports));
13385
13416
  const { getConfigValue: getConfigValue2 } = await Promise.resolve().then(() => (init_store(), store_exports));
13386
- const motion = getConfigValue2("sales-motion");
13417
+ const motion = loadProfile2()?.sales_motion ?? getConfigValue2("sales-motion");
13387
13418
  return resolveThresholds(motion ?? null, {});
13388
13419
  }
13389
13420
  var init_resolve = __esm({
@@ -16237,7 +16268,7 @@ __export(play_outcomes_exports, {
16237
16268
  listPlayOutcomes: () => listPlayOutcomes,
16238
16269
  recordPlayOutcomes: () => recordPlayOutcomes
16239
16270
  });
16240
- import { existsSync as existsSync20, readFileSync as readFileSync17, appendFileSync as appendFileSync6 } from "fs";
16271
+ import { existsSync as existsSync21, readFileSync as readFileSync17, appendFileSync as appendFileSync6 } from "fs";
16241
16272
  import { join as join21 } from "path";
16242
16273
  import { randomUUID as randomUUID7 } from "crypto";
16243
16274
  function outcomesPath() {
@@ -16245,7 +16276,7 @@ function outcomesPath() {
16245
16276
  }
16246
16277
  function listPlayOutcomes() {
16247
16278
  const path = outcomesPath();
16248
- if (!existsSync20(path)) return [];
16279
+ if (!existsSync21(path)) return [];
16249
16280
  const out = [];
16250
16281
  for (const line of readFileSync17(path, "utf-8").split("\n")) {
16251
16282
  const trimmed = line.trim();
@@ -19238,7 +19269,7 @@ var init_markdown = __esm({
19238
19269
  import chalk16 from "chalk";
19239
19270
  function formatLlmAttribution(meta) {
19240
19271
  if (!meta.model_used) return null;
19241
- const provider = meta.provider_used ?? "anthropic";
19272
+ const provider = meta.provider_used ?? "unknown";
19242
19273
  let line = `via ${formatModelLabel(provider, meta.model_used)}`;
19243
19274
  if (meta.failover) {
19244
19275
  line += " (auto-failover)";
@@ -19714,16 +19745,16 @@ var init_terminal = __esm({
19714
19745
  });
19715
19746
 
19716
19747
  // src/demo/taxonomy-cache.ts
19717
- import { readFileSync as readFileSync18, writeFileSync as writeFileSync14, existsSync as existsSync21, mkdirSync as mkdirSync12, unlinkSync as unlinkSync4 } from "fs";
19748
+ import { readFileSync as readFileSync18, writeFileSync as writeFileSync14, existsSync as existsSync22, mkdirSync as mkdirSync12, unlinkSync as unlinkSync4 } from "fs";
19718
19749
  import { homedir as homedir7 } from "os";
19719
19750
  import { join as join22 } from "path";
19720
19751
  function ensureDir6() {
19721
- if (!existsSync21(NTRP_DIR4)) {
19752
+ if (!existsSync22(NTRP_DIR4)) {
19722
19753
  mkdirSync12(NTRP_DIR4, { recursive: true });
19723
19754
  }
19724
19755
  }
19725
19756
  function loadCachedTaxonomy(profile) {
19726
- if (!existsSync21(TAXONOMY_PATH)) return null;
19757
+ if (!existsSync22(TAXONOMY_PATH)) return null;
19727
19758
  try {
19728
19759
  const parsed = JSON.parse(readFileSync18(TAXONOMY_PATH, "utf-8"));
19729
19760
  if (!parsed || typeof parsed !== "object") return null;
@@ -19738,7 +19769,7 @@ function saveCachedTaxonomy(taxonomy) {
19738
19769
  writeFileSync14(TAXONOMY_PATH, JSON.stringify(taxonomy, null, 2) + "\n");
19739
19770
  }
19740
19771
  function invalidateTaxonomy() {
19741
- if (existsSync21(TAXONOMY_PATH)) {
19772
+ if (existsSync22(TAXONOMY_PATH)) {
19742
19773
  try {
19743
19774
  unlinkSync4(TAXONOMY_PATH);
19744
19775
  } catch {
@@ -20208,7 +20239,7 @@ __export(inbox_setup_exports, {
20208
20239
  shouldOfferInboxSkillSetup: () => shouldOfferInboxSkillSetup
20209
20240
  });
20210
20241
  import chalk19 from "chalk";
20211
- import { existsSync as existsSync22 } from "fs";
20242
+ import { existsSync as existsSync23 } from "fs";
20212
20243
  function markDemoOffered() {
20213
20244
  setConfigValue("ai-inbox-nudge-seen", "true");
20214
20245
  }
@@ -20225,7 +20256,7 @@ function shouldOfferInboxSkillSetup(beat) {
20225
20256
  return !hasProductionOffered();
20226
20257
  }
20227
20258
  function printSkipHint(beat) {
20228
- const setCmd = paint("accent", "/inbox set ~/Documents/Claude/ntrp-inbox");
20259
+ const setCmd = paint("accent", `/inbox set ${defaultAiInboxDirDisplay()}`);
20229
20260
  const skillCmd = paint("accent", "/inbox skill");
20230
20261
  if (beat === "demo") {
20231
20262
  console.log(
@@ -20237,13 +20268,15 @@ function printSkipHint(beat) {
20237
20268
  " " + chalk19.dim("Skipped. NTRP will not ask again. Type ") + setCmd + chalk19.dim(" then ") + skillCmd + chalk19.dim(" at any time.")
20238
20269
  );
20239
20270
  }
20240
- async function reuseInboxFolderIfPresent(session, beat, folderPath = defaultAiInboxDir()) {
20271
+ async function reuseInboxFolderIfPresent(session, beat, folderPath) {
20241
20272
  if (getAiInboxDir()) return false;
20242
- if (!existsSync22(folderPath)) return false;
20243
- console.log(" " + chalk19.dim("Pickup folder still on disk: ") + folderPath);
20273
+ const candidates = folderPath ? [folderPath] : [.../* @__PURE__ */ new Set([defaultAiInboxDir(), legacyAiInboxDir()])];
20274
+ const existing = candidates.find((p) => existsSync23(p));
20275
+ if (!existing) return false;
20276
+ console.log(" " + chalk19.dim("Pickup folder still on disk: ") + existing);
20244
20277
  const reuse = await session.confirm("Reuse this pickup folder?", true);
20245
20278
  if (!reuse) return false;
20246
- const resolved = setAiInboxDir(folderPath);
20279
+ const resolved = setAiInboxDir(existing);
20247
20280
  markDemoOffered();
20248
20281
  if (beat === "production") markProductionOffered();
20249
20282
  console.log();
@@ -20258,7 +20291,7 @@ async function offerInboxSkillSetup(session, opts = {}) {
20258
20291
  const beat = opts.beat ?? "production";
20259
20292
  if (!shouldOfferInboxSkillSetup(beat)) return;
20260
20293
  console.log();
20261
- console.log(" " + bold("Teach Claude where handoffs live"));
20294
+ console.log(" " + bold("Teach your AI where handoffs live"));
20262
20295
  console.log(
20263
20296
  " " + chalk19.dim(
20264
20297
  "Optional. NTRP copies every handoff into one folder. You paste instructions once; later /handoff just writes the file."
@@ -20269,7 +20302,10 @@ async function offerInboxSkillSetup(session, opts = {}) {
20269
20302
  }
20270
20303
  console.log();
20271
20304
  if (await reuseInboxFolderIfPresent(session, beat)) return;
20272
- const want = await session.confirm("Set a pickup folder for Claude, ChatGPT, or Cursor?", true);
20305
+ const want = await session.confirm(
20306
+ "Set a pickup folder for Claude, ChatGPT, Cursor, or another desktop AI?",
20307
+ true
20308
+ );
20273
20309
  if (!want) {
20274
20310
  if (beat === "demo") markDemoOffered();
20275
20311
  else markProductionOffered();
@@ -20301,10 +20337,12 @@ async function offerInboxSkillSetup(session, opts = {}) {
20301
20337
  console.log(" " + chalk19.dim(`Synced ${n} recent export${n === 1 ? "" : "s"}.`));
20302
20338
  }
20303
20339
  console.log(
20304
- " " + chalk19.dim("Paste this skill into Claude once. Later, tell Claude to open the latest NTRP handoff.")
20340
+ " " + chalk19.dim(
20341
+ "Paste this skill into your desktop AI once. Later, tell it to open the latest NTRP handoff."
20342
+ )
20305
20343
  );
20306
20344
  printStandingSkill();
20307
- await session.askPressEnter("Paste the skill into Claude. Then continue");
20345
+ await session.askPressEnter("Paste the skill into your AI. Then continue");
20308
20346
  console.log(" " + chalk19.dim("Done. Later handoffs write to that folder. The skill is not printed again."));
20309
20347
  console.log();
20310
20348
  }
@@ -20339,7 +20377,7 @@ __export(ingest_exports, {
20339
20377
  handler: () => handler2
20340
20378
  });
20341
20379
  import chalk20 from "chalk";
20342
- import { readFileSync as readFileSync19, existsSync as existsSync23 } from "fs";
20380
+ import { readFileSync as readFileSync19, existsSync as existsSync24 } from "fs";
20343
20381
  import { basename as basename6 } from "path";
20344
20382
  async function handler2(args, ctx) {
20345
20383
  const { positional, flags } = parseArgs2(args, [
@@ -20363,7 +20401,7 @@ async function handler2(args, ctx) {
20363
20401
  console.error(chalk20.dim(" /ingest --demo [--scenario <name>]"));
20364
20402
  process.exit(1);
20365
20403
  }
20366
- if (!existsSync23(file)) {
20404
+ if (!existsSync24(file)) {
20367
20405
  console.error(chalk20.red(` File not found: ${file}`));
20368
20406
  process.exit(1);
20369
20407
  }
@@ -20951,7 +20989,7 @@ function buildUserMessage(input) {
20951
20989
  async function* streamFindings(input, ctx) {
20952
20990
  assertReplAi(ctx);
20953
20991
  const userMessage = buildUserMessage(input);
20954
- let meta = { model_used: "unknown", provider_used: "anthropic", failover: false };
20992
+ let meta = { model_used: "unknown", provider_used: "unknown", failover: false };
20955
20993
  const scanner = new StreamingJsonArrayParser();
20956
20994
  let streamed = 0;
20957
20995
  for await (const event of streamWithFailover(
@@ -22449,9 +22487,9 @@ async function handleGetSessionBrief(input) {
22449
22487
  if (!target) {
22450
22488
  return { error: `No session matching "${raw}".` };
22451
22489
  }
22452
- const { existsSync: existsSync35, readFileSync: readFileSync24 } = await import("fs");
22490
+ const { existsSync: existsSync36, readFileSync: readFileSync24 } = await import("fs");
22453
22491
  const briefPath = contextDocPathForSession2(target.id);
22454
- if (!existsSync35(briefPath)) {
22492
+ if (!existsSync36(briefPath)) {
22455
22493
  return {
22456
22494
  session_id: target.id,
22457
22495
  error: "No context brief on disk for this session (created before brief storage existed).",
@@ -23166,7 +23204,7 @@ async function* agenticFindings(computeResult, divergences, options) {
23166
23204
  const priorMessages = priorRaw.length > 0 && typeof priorRaw[0] === "object" && priorRaw[0] !== null && "content" in priorRaw[0] ? normalizeThread(priorRaw) : priorRaw;
23167
23205
  const conversational = mode === "fresh" || mode === "think";
23168
23206
  const messages = conversational && priorMessages.length > 0 && options.userQuestion ? [...priorMessages, { role: "user", content: options.userQuestion }] : [{ role: "user", content: initialContext }];
23169
- let lastMeta = { provider_used: "anthropic", model_used: "unknown" };
23207
+ let lastMeta = { provider_used: "unknown", model_used: "unknown" };
23170
23208
  const loopGuard = new ToolLoopGuard();
23171
23209
  const allowedTools = new Set(tools.map((t) => t.name));
23172
23210
  async function callLlm(msgs, tokens, withTools) {
@@ -24154,52 +24192,82 @@ OUTPUT FORMAT \u2014 respond with STRICT JSON only, no preamble, no markdown fen
24154
24192
  });
24155
24193
 
24156
24194
  // src/commands/profile.ts
24157
- var profile_exports = {};
24158
- __export(profile_exports, {
24195
+ var profile_exports2 = {};
24196
+ __export(profile_exports2, {
24159
24197
  PRESET_DESCRIPTIONS: () => PRESET_DESCRIPTIONS,
24160
24198
  PRESET_LABELS: () => PRESET_LABELS,
24161
24199
  handler: () => handler4
24162
24200
  });
24163
24201
  import chalk23 from "chalk";
24164
- async function handler4(args, _ctx) {
24202
+ async function handler4(args, ctx) {
24165
24203
  const { positional } = parseArgs2(args);
24166
24204
  const sub = positional[0] ?? "show";
24167
- switch (sub) {
24168
- case "list":
24169
- return listProfiles();
24170
- case "set":
24171
- return setProfile(positional[1]);
24172
- case "show":
24173
- return showProfile();
24174
- default: {
24175
- console.error(chalk23.red(` Unknown subcommand: ${sub}`));
24176
- console.log(chalk23.dim(" Usage: /profile <list|set|show> [preset]"));
24177
- process.exit(1);
24205
+ const structured = isStructuredOutput(ctx.execution);
24206
+ try {
24207
+ switch (sub) {
24208
+ case "list":
24209
+ return listProfiles(structured);
24210
+ case "set":
24211
+ return setProfile(positional[1], structured);
24212
+ case "show":
24213
+ return showProfile(structured);
24214
+ default: {
24215
+ const err = new NtrpError("unknown_subcommand", `Unknown subcommand: ${sub}`, 2 /* Usage */);
24216
+ if (structured) emitError("profile", err);
24217
+ console.error(chalk23.red(` Unknown subcommand: ${sub}`));
24218
+ console.log(chalk23.dim(" Usage: /profile <list|set|show> [preset]"));
24219
+ process.exit(1);
24220
+ }
24178
24221
  }
24222
+ } catch (err) {
24223
+ if (structured) emitError("profile", err);
24224
+ throw err;
24179
24225
  }
24180
24226
  }
24181
- function listProfiles() {
24227
+ function listProfiles(structured) {
24228
+ const current = loadProfile()?.sales_motion ?? getConfigValue("sales-motion");
24229
+ const presets = Object.entries(PRESET_DESCRIPTIONS).map(([id, description]) => ({
24230
+ id,
24231
+ label: PRESET_LABELS[id],
24232
+ description,
24233
+ active: current === id
24234
+ }));
24235
+ if (structured) {
24236
+ emitResult("profile", { action: "list", presets, current_preset: current ?? null });
24237
+ return;
24238
+ }
24182
24239
  console.log();
24183
24240
  console.log(chalk23.bold(" Sales Motion Presets"));
24184
24241
  console.log();
24185
- const current = getConfigValue("sales-motion");
24186
- for (const [key, desc] of Object.entries(PRESET_DESCRIPTIONS)) {
24187
- const marker2 = current === key ? chalk23.green(" (active)") : "";
24188
- console.log(` ${chalk23.bold(PRESET_LABELS[key].padEnd(16))} ${chalk23.dim(desc)}${marker2}`);
24189
- console.log(chalk23.dim(` ${"".padEnd(16)} id: ${key}`));
24242
+ for (const p of presets) {
24243
+ const marker2 = p.active ? chalk23.green(" (active)") : "";
24244
+ console.log(` ${chalk23.bold(p.label.padEnd(16))} ${chalk23.dim(p.description)}${marker2}`);
24245
+ console.log(chalk23.dim(` ${"".padEnd(16)} id: ${p.id}`));
24190
24246
  }
24191
24247
  console.log();
24192
24248
  console.log(chalk23.dim(" Run /profile set <preset> to activate a profile."));
24193
24249
  console.log();
24194
24250
  }
24195
- function setProfile(preset) {
24251
+ function setProfile(preset, structured) {
24196
24252
  if (!preset) {
24253
+ const err = new NtrpError(
24254
+ "preset_required",
24255
+ `/profile set requires a preset. Valid options: ${Object.keys(PROFILE_PRESETS).join(", ")}`,
24256
+ 2 /* Usage */
24257
+ );
24258
+ if (structured) emitError("profile", err);
24197
24259
  console.error(chalk23.red(" /profile set requires a preset."));
24198
24260
  console.error(chalk23.dim(` Valid options: ${Object.keys(PROFILE_PRESETS).join(", ")}`));
24199
24261
  process.exit(1);
24200
24262
  }
24201
24263
  const validPresets = Object.keys(PROFILE_PRESETS);
24202
24264
  if (!validPresets.includes(preset)) {
24265
+ const err = new NtrpError(
24266
+ "unknown_preset",
24267
+ `Unknown preset: ${preset}. Valid options: ${validPresets.join(", ")}`,
24268
+ 2 /* Usage */
24269
+ );
24270
+ if (structured) emitError("profile", err);
24203
24271
  console.error(chalk23.red(`
24204
24272
  Unknown preset: ${preset}`));
24205
24273
  console.error(chalk23.dim(` Valid options: ${validPresets.join(", ")}
@@ -24208,26 +24276,76 @@ function setProfile(preset) {
24208
24276
  }
24209
24277
  const motion = preset;
24210
24278
  setConfigValue("sales-motion", motion);
24279
+ const hadProfile = Boolean(loadProfile());
24280
+ if (hadProfile) {
24281
+ updateProfile({ sales_motion: motion });
24282
+ }
24211
24283
  const resolved = resolveThresholds(motion, {});
24212
24284
  const defaults = DEFAULT_THRESHOLDS;
24213
- console.log();
24214
- console.log(chalk23.green(` Profile set to ${PRESET_LABELS[motion]}`));
24215
- console.log();
24216
- const changes = [];
24285
+ const threshold_changes = {};
24217
24286
  if (resolved.freshness.people_window_days !== defaults.freshness.people_window_days) {
24218
- changes.push(` Freshness window: ${defaults.freshness.people_window_days}d \u2192 ${resolved.freshness.people_window_days}d`);
24287
+ threshold_changes.freshness_window_days = {
24288
+ from: defaults.freshness.people_window_days,
24289
+ to: resolved.freshness.people_window_days
24290
+ };
24219
24291
  }
24220
24292
  if (resolved.flow_rate.green_days !== defaults.flow_rate.green_days) {
24221
- changes.push(` Flow rate green: ${defaults.flow_rate.green_days}d \u2192 ${resolved.flow_rate.green_days}d`);
24293
+ threshold_changes.flow_rate_green_days = {
24294
+ from: defaults.flow_rate.green_days,
24295
+ to: resolved.flow_rate.green_days
24296
+ };
24222
24297
  }
24223
24298
  if (resolved.flow_rate.stuck_days !== defaults.flow_rate.stuck_days) {
24224
- changes.push(` Stuck threshold: ${defaults.flow_rate.stuck_days}d \u2192 ${resolved.flow_rate.stuck_days}d`);
24299
+ threshold_changes.flow_rate_stuck_days = {
24300
+ from: defaults.flow_rate.stuck_days,
24301
+ to: resolved.flow_rate.stuck_days
24302
+ };
24225
24303
  }
24226
24304
  if (resolved.signal_to_noise.lookback_days !== defaults.signal_to_noise.lookback_days) {
24227
- changes.push(` S/N lookback: ${defaults.signal_to_noise.lookback_days}d \u2192 ${resolved.signal_to_noise.lookback_days}d`);
24305
+ threshold_changes.signal_to_noise_lookback_days = {
24306
+ from: defaults.signal_to_noise.lookback_days,
24307
+ to: resolved.signal_to_noise.lookback_days
24308
+ };
24228
24309
  }
24229
24310
  if (resolved.thread_depth.activity_window_days !== defaults.thread_depth.activity_window_days) {
24230
- changes.push(` Thread depth window: ${defaults.thread_depth.activity_window_days}d \u2192 ${resolved.thread_depth.activity_window_days}d`);
24311
+ threshold_changes.thread_depth_window_days = {
24312
+ from: defaults.thread_depth.activity_window_days,
24313
+ to: resolved.thread_depth.activity_window_days
24314
+ };
24315
+ }
24316
+ if (structured) {
24317
+ emitResult("profile", {
24318
+ action: "set",
24319
+ motion,
24320
+ label: PRESET_LABELS[motion],
24321
+ threshold_changes,
24322
+ profile_synced: hadProfile
24323
+ });
24324
+ return;
24325
+ }
24326
+ console.log();
24327
+ console.log(chalk23.green(` Profile set to ${PRESET_LABELS[motion]}`));
24328
+ console.log();
24329
+ const changes = [];
24330
+ if (threshold_changes.freshness_window_days) {
24331
+ const c = threshold_changes.freshness_window_days;
24332
+ changes.push(` Freshness window: ${c.from}d \u2192 ${c.to}d`);
24333
+ }
24334
+ if (threshold_changes.flow_rate_green_days) {
24335
+ const c = threshold_changes.flow_rate_green_days;
24336
+ changes.push(` Flow rate green: ${c.from}d \u2192 ${c.to}d`);
24337
+ }
24338
+ if (threshold_changes.flow_rate_stuck_days) {
24339
+ const c = threshold_changes.flow_rate_stuck_days;
24340
+ changes.push(` Stuck threshold: ${c.from}d \u2192 ${c.to}d`);
24341
+ }
24342
+ if (threshold_changes.signal_to_noise_lookback_days) {
24343
+ const c = threshold_changes.signal_to_noise_lookback_days;
24344
+ changes.push(` S/N lookback: ${c.from}d \u2192 ${c.to}d`);
24345
+ }
24346
+ if (threshold_changes.thread_depth_window_days) {
24347
+ const c = threshold_changes.thread_depth_window_days;
24348
+ changes.push(` Thread depth window: ${c.from}d \u2192 ${c.to}d`);
24231
24349
  }
24232
24350
  if (changes.length > 0) {
24233
24351
  console.log(chalk23.dim(" Threshold changes:"));
@@ -24239,9 +24357,27 @@ function setProfile(preset) {
24239
24357
  console.log(chalk23.dim(" Run /diagnose to see results with the new profile."));
24240
24358
  console.log();
24241
24359
  }
24242
- function showProfile() {
24360
+ function showProfile(structured) {
24243
24361
  const companyProfile = loadProfile();
24244
24362
  const current = companyProfile?.sales_motion ?? getConfigValue("sales-motion");
24363
+ const resolved = resolveThresholds(current ?? null, {});
24364
+ if (structured) {
24365
+ emitResult("profile", {
24366
+ action: "show",
24367
+ motion: current ?? null,
24368
+ label: current && PRESET_LABELS[current] ? PRESET_LABELS[current] : null,
24369
+ company_profile: companyProfile,
24370
+ thresholds: {
24371
+ freshness_window_days: resolved.freshness.people_window_days,
24372
+ flow_rate_green_days: resolved.flow_rate.green_days,
24373
+ flow_rate_stuck_days: resolved.flow_rate.stuck_days,
24374
+ signal_to_noise_lookback_days: resolved.signal_to_noise.lookback_days,
24375
+ thread_depth_window_days: resolved.thread_depth.activity_window_days,
24376
+ multi_thread_min: resolved.thread_depth.multi_thread_threshold
24377
+ }
24378
+ });
24379
+ return;
24380
+ }
24245
24381
  if (companyProfile) {
24246
24382
  const lines = [];
24247
24383
  lines.push(`### ${companyProfile.company_name}`);
@@ -24271,7 +24407,6 @@ function showProfile() {
24271
24407
  console.log(chalk23.dim(" No sales motion set. Using default thresholds."));
24272
24408
  }
24273
24409
  console.log();
24274
- const resolved = resolveThresholds(current ?? null, {});
24275
24410
  console.log(chalk23.dim(" Key Thresholds:"));
24276
24411
  console.log(chalk23.dim(` Freshness window: ${resolved.freshness.people_window_days} days`));
24277
24412
  console.log(chalk23.dim(` Flow rate (green): ${resolved.flow_rate.green_days} days`));
@@ -24293,6 +24428,9 @@ var init_profile2 = __esm({
24293
24428
  init_argparse();
24294
24429
  init_markdown();
24295
24430
  init_theme();
24431
+ init_emit();
24432
+ init_errors2();
24433
+ init_types2();
24296
24434
  PRESET_DESCRIPTIONS = {
24297
24435
  plg: "Product-Led Growth \u2014 shorter cycles, high volume, self-serve focus",
24298
24436
  smb_velocity: "SMB Velocity \u2014 fast sales cycles, quick close, volume-oriented",
@@ -25132,7 +25270,7 @@ __export(new_exports, {
25132
25270
  handler: () => handler6
25133
25271
  });
25134
25272
  import chalk26 from "chalk";
25135
- import { existsSync as existsSync24 } from "fs";
25273
+ import { existsSync as existsSync25 } from "fs";
25136
25274
  import { basename as basename7 } from "path";
25137
25275
  async function handler6(args, ctx) {
25138
25276
  const { positional, flags } = parseArgs2(args, ["demo", "empty", "list-scenarios", "regen-taxonomy"]);
@@ -25154,7 +25292,7 @@ async function handler6(args, ctx) {
25154
25292
  console.error(chalk26.red(" Usage: /new <file.csv> | --demo [--scenario <name>] | --empty [--lens health|metrics]"));
25155
25293
  return;
25156
25294
  }
25157
- if (source.kind === "file" && !existsSync24(source.path)) {
25295
+ if (source.kind === "file" && !existsSync25(source.path)) {
25158
25296
  console.error(chalk26.red(` File not found: ${source.path}`));
25159
25297
  return;
25160
25298
  }
@@ -25381,7 +25519,7 @@ __export(end_exports, {
25381
25519
  handler: () => handler7
25382
25520
  });
25383
25521
  import chalk27 from "chalk";
25384
- import { existsSync as existsSync25 } from "fs";
25522
+ import { existsSync as existsSync26 } from "fs";
25385
25523
  async function handler7(args, ctx) {
25386
25524
  if (args.length > 0) {
25387
25525
  console.error(chalk27.red(" Usage: /end"));
@@ -25418,10 +25556,10 @@ async function handler7(args, ctx) {
25418
25556
  if (summary) {
25419
25557
  console.log(" " + chalk27.dim(summary));
25420
25558
  }
25421
- if (existsSync25(transcriptPathForSession(endedId))) {
25559
+ if (existsSync26(transcriptPathForSession(endedId))) {
25422
25560
  console.log(" " + chalk27.dim("Transcript: ") + chalk27.dim(transcriptPathForSession(endedId)));
25423
25561
  }
25424
- if (existsSync25(contextDocPathForSession(endedId))) {
25562
+ if (existsSync26(contextDocPathForSession(endedId))) {
25425
25563
  console.log(" " + chalk27.dim("Context brief: ") + chalk27.dim(contextDocPathForSession(endedId)));
25426
25564
  }
25427
25565
  console.log();
@@ -25443,7 +25581,7 @@ __export(session_exports, {
25443
25581
  });
25444
25582
  import chalk28 from "chalk";
25445
25583
  import { join as join23 } from "path";
25446
- import { existsSync as existsSync26 } from "fs";
25584
+ import { existsSync as existsSync27 } from "fs";
25447
25585
  async function handler8(args, ctx) {
25448
25586
  const sub = args[0];
25449
25587
  if (!sub) return listSessionsView(ctx);
@@ -25589,7 +25727,7 @@ async function pickUp(idArg, ctx) {
25589
25727
  );
25590
25728
  }
25591
25729
  const contextPath = contextDocPathForSession(target.id);
25592
- if (existsSync26(contextPath)) {
25730
+ if (existsSync27(contextPath)) {
25593
25731
  console.log(" " + chalk28.dim("Context brief: ") + chalk28.dim(contextPath));
25594
25732
  }
25595
25733
  console.log();
@@ -25765,22 +25903,19 @@ async function loadReportData(segmentName) {
25765
25903
  let { health, segments, findings, entityCounts } = diagnosis;
25766
25904
  let scopedSegment = null;
25767
25905
  if (segmentName) {
25768
- const lower = segmentName.toLowerCase();
25769
- let match = segments.find((s) => s.segment.name.toLowerCase() === lower);
25770
- if (!match) {
25771
- const subs = segments.filter((s) => s.segment.name.toLowerCase().includes(lower));
25772
- if (subs.length === 1) match = subs[0];
25773
- else if (subs.length > 1) {
25774
- throw new NtrpError("ambiguous_segment", `"${segmentName}" matches multiple segments.`, 2 /* Usage */, {
25775
- matches: subs.map((s) => s.segment.name)
25776
- });
25777
- }
25906
+ const resolved = resolveSegmentByName(segmentName, segments);
25907
+ if (resolved.type === "ambiguous") {
25908
+ throw new NtrpError("ambiguous_segment", `"${segmentName}" matches multiple segments.`, 2 /* Usage */, {
25909
+ matches: resolved.matches.map((s) => s.segment.name)
25910
+ });
25778
25911
  }
25779
- if (!match) {
25912
+ if (resolved.type === "none") {
25780
25913
  throw new NtrpError("segment_not_found", `No segment matching "${segmentName}".`, 2 /* Usage */, {
25781
25914
  available_segments: segments.map((s) => s.segment.name)
25782
25915
  });
25783
25916
  }
25917
+ const match = resolved.segment;
25918
+ const lower = segmentName.toLowerCase();
25784
25919
  scopedSegment = match;
25785
25920
  health = match.result;
25786
25921
  segments = [match];
@@ -25806,6 +25941,7 @@ var init_report = __esm({
25806
25941
  init_errors2();
25807
25942
  init_types2();
25808
25943
  init_session_analysis();
25944
+ init_segments();
25809
25945
  }
25810
25946
  });
25811
25947
 
@@ -26169,20 +26305,15 @@ async function handler10(args, _ctx) {
26169
26305
  let exportFindings = diagnosis.findings;
26170
26306
  let exportHealth = diagnosis.health;
26171
26307
  if (segmentName) {
26172
- const lower = segmentName.toLowerCase();
26173
- let match = diagnosis.segments.find((s) => s.segment.name.toLowerCase() === lower);
26174
- if (!match) {
26175
- const subs = diagnosis.segments.filter((s) => s.segment.name.toLowerCase().includes(lower));
26176
- if (subs.length === 1) match = subs[0];
26177
- else if (subs.length > 1) {
26178
- console.error(chalk30.yellow(`
26308
+ const resolved = resolveSegmentByName(segmentName, diagnosis.segments);
26309
+ if (resolved.type === "ambiguous") {
26310
+ console.error(chalk30.yellow(`
26179
26311
  "${segmentName}" matches multiple segments:`));
26180
- for (const s of subs) console.log(chalk30.dim(` - ${s.segment.name}`));
26181
- console.log();
26182
- process.exit(1);
26183
- }
26312
+ for (const s of resolved.matches) console.log(chalk30.dim(` - ${s.segment.name}`));
26313
+ console.log();
26314
+ process.exit(1);
26184
26315
  }
26185
- if (!match) {
26316
+ if (resolved.type === "none") {
26186
26317
  console.error(chalk30.red(`
26187
26318
  No segment matching "${segmentName}".`));
26188
26319
  if (diagnosis.segments.length > 0) {
@@ -26192,6 +26323,8 @@ async function handler10(args, _ctx) {
26192
26323
  console.log();
26193
26324
  process.exit(1);
26194
26325
  }
26326
+ const match = resolved.segment;
26327
+ const lower = segmentName.toLowerCase();
26195
26328
  exportHealth = match.result;
26196
26329
  exportSegments = [match];
26197
26330
  exportFindings = diagnosis.findings.filter((f) => f.segment.toLowerCase().includes(lower));
@@ -26248,6 +26381,7 @@ var init_export = __esm({
26248
26381
  init_path_safety();
26249
26382
  init_argparse();
26250
26383
  init_exports_registry();
26384
+ init_segments();
26251
26385
  }
26252
26386
  });
26253
26387
 
@@ -27600,15 +27734,6 @@ __export(segment_exports, {
27600
27734
  handler: () => handler15
27601
27735
  });
27602
27736
  import chalk35 from "chalk";
27603
- function resolveSegmentByName(query, segments) {
27604
- const lower = query.toLowerCase();
27605
- const exact = segments.find((s) => s.segment.name.toLowerCase() === lower);
27606
- if (exact) return { type: "exact", segment: exact };
27607
- const matches = segments.filter((s) => s.segment.name.toLowerCase().includes(lower));
27608
- if (matches.length === 1) return { type: "exact", segment: matches[0] };
27609
- if (matches.length > 1) return { type: "ambiguous", matches };
27610
- return { type: "none" };
27611
- }
27612
27737
  function handleNoMatch(query, segments) {
27613
27738
  console.error(chalk35.red(` No segment matching "${query}".`));
27614
27739
  console.log();
@@ -27932,6 +28057,7 @@ var init_segment = __esm({
27932
28057
  init_types2();
27933
28058
  init_serialize();
27934
28059
  init_session_analysis();
28060
+ init_segments();
27935
28061
  OPERATOR_ALIASES = {
27936
28062
  eq: "equals",
27937
28063
  equals: "equals",
@@ -28799,7 +28925,7 @@ async function* strategistPlanSession(options) {
28799
28925
  })
28800
28926
  }
28801
28927
  ];
28802
- let lastMeta = { provider_used: "anthropic", model_used: "unknown" };
28928
+ let lastMeta = { provider_used: "unknown", model_used: "unknown" };
28803
28929
  const loopGuard = new ToolLoopGuard();
28804
28930
  const allowedTools = new Set(tools.map((t) => t.name));
28805
28931
  const callLlm = async (surface, withTools, maxTokens = STAGE_MAX_TOKENS) => {
@@ -30546,42 +30672,85 @@ __export(reset_exports, {
30546
30672
  handler: () => handler20
30547
30673
  });
30548
30674
  import chalk43 from "chalk";
30549
- async function handler20(args, _ctx) {
30675
+ async function countRowsByTable() {
30676
+ const counts = {};
30677
+ for (const table of TABLES) {
30678
+ try {
30679
+ const rows = await all(`SELECT COUNT(*) AS cnt FROM ${table}`);
30680
+ counts[table] = Number(rows[0]?.cnt ?? 0);
30681
+ } catch {
30682
+ counts[table] = 0;
30683
+ }
30684
+ }
30685
+ return counts;
30686
+ }
30687
+ async function handler20(args, ctx) {
30550
30688
  const { flags } = parseArgs2(args, ["force"]);
30551
30689
  const force = getBool(flags, "force");
30690
+ const structured = isStructuredOutput(ctx.execution);
30552
30691
  if (!force) {
30692
+ const message = "This will delete ALL data from the local database. Re-run with --force to confirm.";
30693
+ if (structured) {
30694
+ emitError("reset", new NtrpError("force_required", message, 2 /* Usage */));
30695
+ }
30553
30696
  console.log(chalk43.yellow("\n This will delete ALL data from the local database."));
30554
30697
  console.log(chalk43.dim(" Re-run with --force to confirm.\n"));
30555
30698
  process.exit(1);
30556
30699
  }
30557
- const spinner = makeSpinner("Resetting database\u2026");
30700
+ const spinner = structured ? null : makeSpinner("Resetting database\u2026");
30558
30701
  try {
30559
30702
  await initSchema();
30703
+ const counts_by_table = await countRowsByTable();
30704
+ const total_rows_deleted = Object.values(counts_by_table).reduce((a, b) => a + b, 0);
30560
30705
  for (const table of TABLES) {
30561
30706
  await run(`DELETE FROM ${table}`);
30562
30707
  }
30563
- spinner.succeed("All data cleared");
30564
30708
  const { wipePrivacyStore: wipePrivacyStore3 } = await Promise.resolve().then(() => (init_lexicon_seed(), lexicon_seed_exports));
30565
30709
  wipePrivacyStore3();
30710
+ if (structured) {
30711
+ emitResult("reset", {
30712
+ status: "cleared",
30713
+ tables_cleared: TABLES,
30714
+ total_rows_deleted,
30715
+ counts_by_table
30716
+ });
30717
+ return;
30718
+ }
30719
+ spinner.succeed("All data cleared");
30566
30720
  console.log(chalk43.dim("\n Run ") + chalk43.cyan("/new") + chalk43.dim(" \u2192 pick Demo, then choose your analysis type.\n"));
30567
30721
  } catch (err) {
30568
30722
  if (force && isClosedConnectionError(err)) {
30569
30723
  try {
30570
- spinner.text = "Recreating database\u2026";
30724
+ if (spinner) spinner.text = "Recreating database\u2026";
30571
30725
  await recreateDatabaseFile();
30572
30726
  await initSchema();
30573
- spinner.succeed("Database recreated");
30574
30727
  const { wipePrivacyStore: wipePrivacyStore3 } = await Promise.resolve().then(() => (init_lexicon_seed(), lexicon_seed_exports));
30575
30728
  wipePrivacyStore3();
30729
+ if (structured) {
30730
+ emitResult("reset", {
30731
+ status: "recreated",
30732
+ tables_cleared: TABLES,
30733
+ total_rows_deleted: 0,
30734
+ counts_by_table: Object.fromEntries(TABLES.map((t) => [t, 0]))
30735
+ });
30736
+ return;
30737
+ }
30738
+ spinner.succeed("Database recreated");
30576
30739
  console.log(chalk43.dim("\n Run ") + chalk43.cyan("/new") + chalk43.dim(" \u2192 pick Demo, then choose your analysis type.\n"));
30577
30740
  return;
30578
30741
  } catch (recreateErr) {
30579
- spinner.fail("Reset failed");
30742
+ if (structured) {
30743
+ emitError("reset", recreateErr instanceof NtrpError ? recreateErr : new NtrpError("reset_failed", String(recreateErr), 1 /* RuntimeError */));
30744
+ }
30745
+ spinner?.fail("Reset failed");
30580
30746
  console.error(chalk43.red(String(recreateErr)));
30581
30747
  process.exit(1);
30582
30748
  }
30583
30749
  }
30584
- spinner.fail("Reset failed");
30750
+ if (structured) {
30751
+ emitError("reset", err instanceof NtrpError ? err : new NtrpError("reset_failed", String(err), 1 /* RuntimeError */));
30752
+ }
30753
+ spinner?.fail("Reset failed");
30585
30754
  console.error(chalk43.red(String(err)));
30586
30755
  process.exit(1);
30587
30756
  }
@@ -30594,6 +30763,9 @@ var init_reset = __esm({
30594
30763
  init_connection();
30595
30764
  init_schema();
30596
30765
  init_argparse();
30766
+ init_emit();
30767
+ init_errors2();
30768
+ init_types2();
30597
30769
  TABLES = [
30598
30770
  "findings",
30599
30771
  "health_readings",
@@ -30874,7 +31046,10 @@ function usage() {
30874
31046
  console.log(chalk45.dim(" Tip: ") + paint("accent", "/config set api-key") + chalk45.dim(" opens a hidden prompt (no inline paste)."));
30875
31047
  console.log(chalk45.dim(" Tip: ") + paint("accent", "/connect") + chalk45.dim(" auto-detects the provider from any pasted key."));
30876
31048
  }
30877
- function fail(message, ctx) {
31049
+ function fail(message, ctx, code = "usage_error") {
31050
+ if (isStructuredOutput(ctx.execution)) {
31051
+ emitError("config", new NtrpError(code, message, 2 /* Usage */));
31052
+ }
30878
31053
  console.error(chalk45.red(` ${message}`));
30879
31054
  if (ctx.oneShot) process.exit(1);
30880
31055
  }
@@ -30891,9 +31066,18 @@ async function promptSecretValue(key, ctx) {
30891
31066
  prompts.close();
30892
31067
  }
30893
31068
  }
31069
+ function redactConfig(config) {
31070
+ const secrets = secretKeys();
31071
+ const out = {};
31072
+ for (const [k, v] of Object.entries(config)) {
31073
+ out[k] = secrets.has(k) ? display(k, v) : v;
31074
+ }
31075
+ return out;
31076
+ }
30894
31077
  async function handler22(args, ctx) {
30895
31078
  const { positional } = parseArgs2(args);
30896
31079
  const sub = positional[0] ?? "list";
31080
+ const structured = isStructuredOutput(ctx.execution);
30897
31081
  switch (sub) {
30898
31082
  case "set": {
30899
31083
  const key = positional[1];
@@ -30905,6 +31089,10 @@ async function handler22(args, ctx) {
30905
31089
  }
30906
31090
  let value = inlineValue;
30907
31091
  if (!value && secretKeys().has(key)) {
31092
+ if (structured) {
31093
+ fail(`/config set ${key} requires an inline value in --json mode`, ctx, "value_required");
31094
+ return;
31095
+ }
30908
31096
  try {
30909
31097
  value = await promptSecretValue(key, ctx);
30910
31098
  } catch (err) {
@@ -30919,7 +31107,11 @@ async function handler22(args, ctx) {
30919
31107
  setConfigValue(key, value);
30920
31108
  const saved = getConfigValue(key);
30921
31109
  if (saved !== value) {
30922
- fail(`Failed to save ${key} \u2014 check permissions on ~/.ntrp/config.json`, ctx);
31110
+ fail(`Failed to save ${key} \u2014 check permissions on ~/.ntrp/config.json`, ctx, "save_failed");
31111
+ return;
31112
+ }
31113
+ if (structured) {
31114
+ emitResult("config", { action: "set", key, value: display(key, value) });
30923
31115
  return;
30924
31116
  }
30925
31117
  console.log();
@@ -30953,6 +31145,15 @@ async function handler22(args, ctx) {
30953
31145
  return;
30954
31146
  }
30955
31147
  const value = getConfigValue(key);
31148
+ if (structured) {
31149
+ emitResult("config", {
31150
+ action: "get",
31151
+ key,
31152
+ value: value === void 0 ? null : display(key, value),
31153
+ is_set: value !== void 0
31154
+ });
31155
+ return;
31156
+ }
30956
31157
  if (value === void 0) console.log(chalk45.dim(` ${key} is not set`));
30957
31158
  else console.log(` ${key} = ${display(key, value)}`);
30958
31159
  return;
@@ -30960,6 +31161,14 @@ async function handler22(args, ctx) {
30960
31161
  case "list": {
30961
31162
  const config = loadConfig();
30962
31163
  const entries2 = Object.entries(config);
31164
+ if (structured) {
31165
+ emitResult("config", {
31166
+ action: "list",
31167
+ config: redactConfig(config),
31168
+ count: entries2.length
31169
+ });
31170
+ return;
31171
+ }
30963
31172
  if (entries2.length === 0) {
30964
31173
  console.log(chalk45.dim(" No configuration set."));
30965
31174
  return;
@@ -30974,11 +31183,15 @@ async function handler22(args, ctx) {
30974
31183
  return;
30975
31184
  }
30976
31185
  deleteConfigValue(key);
31186
+ if (structured) {
31187
+ emitResult("config", { action: "delete", key });
31188
+ return;
31189
+ }
30977
31190
  console.log(chalk45.green(` Deleted ${key}`));
30978
31191
  return;
30979
31192
  }
30980
31193
  default: {
30981
- fail(`Unknown subcommand: ${sub}`, ctx);
31194
+ fail(`Unknown subcommand: ${sub}`, ctx, "unknown_subcommand");
30982
31195
  usage();
30983
31196
  return;
30984
31197
  }
@@ -30993,6 +31206,9 @@ var init_config = __esm({
30993
31206
  init_argparse();
30994
31207
  init_prompts();
30995
31208
  init_theme();
31209
+ init_emit();
31210
+ init_errors2();
31211
+ init_types2();
30996
31212
  }
30997
31213
  });
30998
31214
 
@@ -31010,8 +31226,13 @@ async function afterLicenseSuccess(ctx) {
31010
31226
  async function handler23(args, ctx) {
31011
31227
  const { positional } = parseArgs2(args);
31012
31228
  const key = positional[0];
31229
+ const structured = isStructuredOutput(ctx.execution);
31013
31230
  if (!key) {
31014
- if (ctx.oneShot || !process.stdin.isTTY) {
31231
+ if (ctx.oneShot || !process.stdin.isTTY || structured) {
31232
+ const message = "Usage: /activate <key>. Or type /activate in ntrp to paste a key.";
31233
+ if (structured) {
31234
+ emitError("activate", new NtrpError("key_required", message, 2 /* Usage */));
31235
+ }
31015
31236
  console.error(chalk46.red("\n Usage: /activate <key>"));
31016
31237
  console.error(chalk46.dim(" Or type /activate in ntrp to paste a key.\n"));
31017
31238
  if (ctx.oneShot) process.exit(1);
@@ -31037,18 +31258,29 @@ async function handler23(args, ctx) {
31037
31258
  try {
31038
31259
  const result = await activateLicenseKey(key);
31039
31260
  if (!result.valid) {
31261
+ if (structured) {
31262
+ emitResult("activate", { valid: false, message: result.message });
31263
+ process.exit(1);
31264
+ }
31040
31265
  console.error(chalk46.red(`
31041
31266
  ${result.message}
31042
31267
  `));
31043
31268
  if (ctx.oneShot) process.exit(1);
31044
31269
  return;
31045
31270
  }
31271
+ if (structured) {
31272
+ emitResult("activate", { valid: true, message: result.message });
31273
+ return;
31274
+ }
31046
31275
  console.log(chalk46.green(`
31047
31276
  License activated: ${result.message}
31048
31277
  `));
31049
31278
  await afterLicenseSuccess(ctx);
31050
31279
  } catch (err) {
31051
31280
  const message = err instanceof Error ? err.message : "License activation failed";
31281
+ if (structured) {
31282
+ emitError("activate", new NtrpError("activation_failed", message, 3 /* Auth */));
31283
+ }
31052
31284
  console.error(chalk46.red(`
31053
31285
  ${message}
31054
31286
  `));
@@ -31064,6 +31296,9 @@ var init_activate = __esm({
31064
31296
  init_activation();
31065
31297
  init_upgrade();
31066
31298
  init_theme();
31299
+ init_emit();
31300
+ init_errors2();
31301
+ init_types2();
31067
31302
  }
31068
31303
  });
31069
31304
 
@@ -31131,7 +31366,7 @@ var init_checkout = __esm({
31131
31366
  });
31132
31367
 
31133
31368
  // src/services/setup.ts
31134
- import { existsSync as existsSync27, mkdirSync as mkdirSync17, readFileSync as readFileSync20, writeFileSync as writeFileSync19 } from "fs";
31369
+ import { existsSync as existsSync28, mkdirSync as mkdirSync17, readFileSync as readFileSync20, writeFileSync as writeFileSync19 } from "fs";
31135
31370
  import { join as join29 } from "path";
31136
31371
  function setupCheck() {
31137
31372
  const home = ntrpHome();
@@ -31834,7 +32069,7 @@ async function handleDeliverFlow(input, ctx) {
31834
32069
  }
31835
32070
  console.log();
31836
32071
  console.log(" " + chalk52.bold("Deliverable preview"));
31837
- console.log(" " + chalk52.dim("NTRP writes the file to your inbox after you confirm. If you set the inbox during demo or company setup, Claude can find the folder."));
32072
+ console.log(" " + chalk52.dim("NTRP writes the file to your inbox after you confirm. If you set the inbox during demo or company setup, your AI can find the folder."));
31838
32073
  console.log(" " + chalk52.dim("\u2500".repeat(56)));
31839
32074
  const preview = draft.markdown.split("\n").slice(0, 12);
31840
32075
  for (const l of preview) {
@@ -33134,7 +33369,7 @@ __export(sessions_exports, {
33134
33369
  handler: () => handler35
33135
33370
  });
33136
33371
  import chalk62 from "chalk";
33137
- import { existsSync as existsSync28 } from "fs";
33372
+ import { existsSync as existsSync29 } from "fs";
33138
33373
  async function handler35(args, _ctx) {
33139
33374
  const sub = args[0] ?? "list";
33140
33375
  if (sub === "list" || !args[0]) {
@@ -33241,12 +33476,12 @@ function showSession(idArg) {
33241
33476
  console.log();
33242
33477
  const transcriptPath = transcriptPathForSession(session.id);
33243
33478
  const contextPath = contextDocPathForSession(session.id);
33244
- if (existsSync28(transcriptPath) || existsSync28(contextPath)) {
33479
+ if (existsSync29(transcriptPath) || existsSync29(contextPath)) {
33245
33480
  console.log(" " + chalk62.dim("\u2500".repeat(40)));
33246
- if (existsSync28(contextPath)) {
33481
+ if (existsSync29(contextPath)) {
33247
33482
  console.log(" " + chalk62.dim("Context brief: ") + chalk62.dim(contextPath));
33248
33483
  }
33249
- if (existsSync28(transcriptPath)) {
33484
+ if (existsSync29(transcriptPath)) {
33250
33485
  console.log(" " + chalk62.dim("Full transcript: ") + chalk62.dim(transcriptPath));
33251
33486
  }
33252
33487
  console.log();
@@ -33518,7 +33753,7 @@ var init_privacy_notice = __esm({
33518
33753
  "Computed scores and dollar aggregates still go to the provider you connected.",
33519
33754
  "If you turn on web retrieval, named-account queries are refused. Generic GTM terms may go to Tavily or Brave.",
33520
33755
  "A custom --base-url receives the same tokenized payload.",
33521
- "MCP hosts (for example Claude Desktop) see tokens, not account names. Use the CLI to read real names.",
33756
+ "MCP hosts (for example Claude Desktop or Cursor) see tokens, not account names. Use the CLI to read real names.",
33522
33757
  "Keys stay in ~/.ntrp/config.json on this machine (mode 600).",
33523
33758
  "NTRP is a diagnostic. It does not change CRM records or send email.",
33524
33759
  "Type /privacy to read this notice again."
@@ -34582,20 +34817,20 @@ var init_model = __esm({
34582
34817
  });
34583
34818
 
34584
34819
  // src/config/update-check.ts
34585
- import { existsSync as existsSync29, mkdirSync as mkdirSync18, readFileSync as readFileSync21, unlinkSync as unlinkSync5, writeFileSync as writeFileSync20 } from "fs";
34820
+ import { existsSync as existsSync30, mkdirSync as mkdirSync18, readFileSync as readFileSync21, unlinkSync as unlinkSync5, writeFileSync as writeFileSync20 } from "fs";
34586
34821
  import { join as join33 } from "path";
34587
34822
  function cachePath2() {
34588
34823
  return join33(ntrpHome(), "update-check.json");
34589
34824
  }
34590
34825
  function ensureDir7() {
34591
34826
  const dir = ntrpHome();
34592
- if (!existsSync29(dir)) {
34827
+ if (!existsSync30(dir)) {
34593
34828
  mkdirSync18(dir, { recursive: true });
34594
34829
  }
34595
34830
  }
34596
34831
  function loadUpdateCheckCache() {
34597
34832
  const path = cachePath2();
34598
- if (!existsSync29(path)) return null;
34833
+ if (!existsSync30(path)) return null;
34599
34834
  try {
34600
34835
  const parsed = JSON.parse(readFileSync21(path, "utf-8"));
34601
34836
  if (!parsed || typeof parsed !== "object" || typeof parsed.lastCheck !== "number" || typeof parsed.latestVersion !== "string") {
@@ -34616,7 +34851,7 @@ function isCacheFresh(cache2, ttlMs = CACHE_TTL_MS2) {
34616
34851
  }
34617
34852
  function invalidateUpdateCheckCache() {
34618
34853
  const path = cachePath2();
34619
- if (existsSync29(path)) {
34854
+ if (existsSync30(path)) {
34620
34855
  unlinkSync5(path);
34621
34856
  }
34622
34857
  }
@@ -34630,11 +34865,11 @@ var init_update_check = __esm({
34630
34865
  });
34631
34866
 
34632
34867
  // src/version.ts
34633
- import { existsSync as existsSync30, readFileSync as readFileSync22 } from "fs";
34868
+ import { existsSync as existsSync31, readFileSync as readFileSync22 } from "fs";
34634
34869
  import { dirname as dirname6, join as join34 } from "path";
34635
34870
  import { fileURLToPath } from "url";
34636
34871
  function readVersionFromPackageJson(packageJsonPath) {
34637
- if (!existsSync30(packageJsonPath)) return null;
34872
+ if (!existsSync31(packageJsonPath)) return null;
34638
34873
  try {
34639
34874
  const pkg = JSON.parse(readFileSync22(packageJsonPath, "utf-8"));
34640
34875
  if (typeof pkg.version === "string" && pkg.version.length > 0) return pkg.version;
@@ -34774,7 +35009,7 @@ __export(relaunch_exports, {
34774
35009
  resolveRelaunchEntry: () => resolveRelaunchEntry,
34775
35010
  updateRestartSummary: () => updateRestartSummary
34776
35011
  });
34777
- import { existsSync as existsSync31 } from "fs";
35012
+ import { existsSync as existsSync32 } from "fs";
34778
35013
  import { join as join35 } from "path";
34779
35014
  import { fileURLToPath as fileURLToPath2 } from "url";
34780
35015
  import { spawnSync } from "child_process";
@@ -34800,7 +35035,7 @@ function npmGlobalEntry() {
34800
35035
  const listed = spawnSync("npm", ["root", "-g"], { encoding: "utf-8" });
34801
35036
  if (listed.status !== 0) return null;
34802
35037
  const entry = join35(listed.stdout.trim(), NPM_PACKAGE, "dist/index.js");
34803
- return existsSync31(entry) ? entry : null;
35038
+ return existsSync32(entry) ? entry : null;
34804
35039
  }
34805
35040
  function thisBundleEntry() {
34806
35041
  return fileURLToPath2(import.meta.url);
@@ -34810,10 +35045,10 @@ function resolveRelaunchEntry(toVersion) {
34810
35045
  (p) => Boolean(p)
34811
35046
  );
34812
35047
  for (const entry of candidates) {
34813
- if (!existsSync31(entry)) continue;
35048
+ if (!existsSync32(entry)) continue;
34814
35049
  if (readVersionNearEntry(entry) === toVersion) return entry;
34815
35050
  }
34816
- return candidates.find((p) => existsSync31(p)) ?? thisBundleEntry();
35051
+ return candidates.find((p) => existsSync32(p)) ?? thisBundleEntry();
34817
35052
  }
34818
35053
  function relaunchArgv(toVersion) {
34819
35054
  return [resolveRelaunchEntry(toVersion)];
@@ -35336,7 +35571,7 @@ __export(exports_exports, {
35336
35571
  handler: () => handler48
35337
35572
  });
35338
35573
  import chalk78 from "chalk";
35339
- import { existsSync as existsSync32 } from "fs";
35574
+ import { existsSync as existsSync33 } from "fs";
35340
35575
  import { join as join36 } from "path";
35341
35576
  function usage4() {
35342
35577
  console.log(chalk78.dim(" Usage:"));
@@ -35448,7 +35683,7 @@ async function runInboxSet(args, ctx) {
35448
35683
  const pathArg = args.join(" ").trim();
35449
35684
  if (!pathArg) {
35450
35685
  console.error(chalk78.red(" Usage: /inbox set <path>"));
35451
- console.error(chalk78.dim(" Example: /inbox set ~/Documents/Claude/ntrp-inbox"));
35686
+ console.error(chalk78.dim(" Example: /inbox set ~/Documents/ntrp-inbox"));
35452
35687
  if (ctx.oneShot) process.exit(1);
35453
35688
  return;
35454
35689
  }
@@ -35478,7 +35713,7 @@ async function runInboxSet(args, ctx) {
35478
35713
  console.log();
35479
35714
  console.log(" " + paint("accent", "AI inbox set"));
35480
35715
  console.log(" " + chalk78.dim(setTo));
35481
- console.log(" " + chalk78.dim("Point Claude Desktop or another desktop AI at this folder."));
35716
+ console.log(" " + chalk78.dim("Point Claude, ChatGPT, Cursor, or another desktop AI at this folder."));
35482
35717
  if (n > 0) {
35483
35718
  console.log(" " + chalk78.dim(`Synced ${n} recent export${n === 1 ? "" : "s"} into the inbox.`));
35484
35719
  }
@@ -35495,7 +35730,7 @@ function runMove(args, ctx) {
35495
35730
  }
35496
35731
  try {
35497
35732
  const destDir = resolveUserPath(dest);
35498
- if (!existsSync32(destDir)) {
35733
+ if (!existsSync33(destDir)) {
35499
35734
  }
35500
35735
  const event = moveExport(idOrName, destDir);
35501
35736
  console.log();
@@ -35686,7 +35921,7 @@ async function importHandler(runtimePath) {
35686
35921
  case "../commands/publish.js":
35687
35922
  return Promise.resolve().then(() => (init_publish2(), publish_exports));
35688
35923
  case "../commands/profile.js":
35689
- return Promise.resolve().then(() => (init_profile2(), profile_exports));
35924
+ return Promise.resolve().then(() => (init_profile2(), profile_exports2));
35690
35925
  case "../commands/config.js":
35691
35926
  return Promise.resolve().then(() => (init_config(), config_exports));
35692
35927
  case "../commands/activate.js":
@@ -35848,12 +36083,12 @@ args: [show|set <path>|skill|clear]
35848
36083
  handler: ../commands/exports.ts
35849
36084
  ---
35850
36085
 
35851
- Set a folder that Claude Desktop or any desktop AI can read.
36086
+ Set a folder that any desktop AI can read (Claude, ChatGPT, Cursor, \u2026).
35852
36087
  NTRP copies each handoff to that folder.
35853
36088
  NTRP overwrites \`latest-handoff.md\` and \`latest-handoff-deck.md\`. The app then finds the newest file.
35854
36089
  NTRP also writes \`SKILL.md\`. That file holds finder instructions with your paths.
35855
36090
  Demo setup offers this once. Loading your own data (CSV ingest or \`/onboard\`) asks again if the folder is not set. Skip on that production step and NTRP will not ask again. Type \`/inbox skill\` or \`/handoff skill\` to print the finder. Type \`/inbox set\` at any time.
35856
- Paste that skill once into Claude, ChatGPT, or Cursor. Later handoffs need no new paste.
36091
+ Paste that skill once into Claude, ChatGPT, Cursor, or another desktop AI. Later handoffs need no new paste.
35857
36092
  \`INDEX.md\` in that folder links to the archive.
35858
36093
  A path outside ~/.ntrp needs a confirm. Handoffs carry analysis text.
35859
36094
  \`clear\` does not delete files. It only removes the config pointer.`
@@ -36204,9 +36439,9 @@ args: [<metric>|guide|list|tour]
36204
36439
  handler: ../commands/deepdive.ts
36205
36440
  ---
36206
36441
 
36207
- CLI slide deck: SaaS refresher, five vital signs (definition, formula, visual, dollar translation), then a how-to section (talk, ask, ship to Claude, loop).
36208
- Bare \`/deepdive\` runs the full onboarding tour. Type \`/deepdive guide\` to jump to how to use NTRP (includes teaching Claude the inbox once).
36209
- Type \`/deepdive handoff\` for the ship-to-Claude slide. Type \`/deepdive <metric>\` to jump to one metric card.
36442
+ CLI slide deck: SaaS refresher, five vital signs (definition, formula, visual, dollar translation), then a how-to section (talk, ask, ship to your AI, loop).
36443
+ Bare \`/deepdive\` runs the full onboarding tour. Type \`/deepdive guide\` to jump to how to use NTRP (includes teaching the inbox once).
36444
+ Type \`/deepdive handoff\` for the ship-to-AI slide. Type \`/deepdive <metric>\` to jump to one metric card.
36210
36445
  Type \`/deepdive list\` to print the catalog. Works without an AI key. Re-run anytime from the homescreen.
36211
36446
  Live values overlay when an analysis exists.`
36212
36447
  },
@@ -36410,6 +36645,7 @@ Manage CLI configuration stored at \`~/.ntrp/config.json\`. Useful keys:
36410
36645
  \`api-key\` (Anthropic), \`openai-api-key\` (and \`groq-api-key\`, \`google-api-key\`, ...),
36411
36646
  \`llm-primary\` (default provider), \`llm-tier\`, \`llm-auto-failover\`,
36412
36647
  \`voice-personality\`, \`voice-roast\`,
36648
+ \`sales-motion\`, \`rep_hourly_cost\`, \`hours_per_activity\`,
36413
36649
  \`default-format\`, \`export-dir\`, \`ai-inbox-dir\` (or type \`/inbox set\`).
36414
36650
 
36415
36651
  Setting a provider key opens a hidden prompt and auto-discovers that provider's models.
@@ -36527,7 +36763,7 @@ Remaining nuances merge into a custom_context paragraph that flows into all AI s
36527
36763
  });
36528
36764
 
36529
36765
  // src/ai/prompt-parts.ts
36530
- import { existsSync as existsSync33, readFileSync as readFileSync23 } from "fs";
36766
+ import { existsSync as existsSync34, readFileSync as readFileSync23 } from "fs";
36531
36767
  import { join as join37 } from "path";
36532
36768
  function buildCompanyProfileBlock() {
36533
36769
  const p = loadProfile();
@@ -36548,7 +36784,7 @@ function buildCompanyProfileBlock() {
36548
36784
  function loadAnalystFile() {
36549
36785
  const path = join37(ntrpHome(), ANALYST_FILE_NAME);
36550
36786
  try {
36551
- if (!existsSync33(path)) return null;
36787
+ if (!existsSync34(path)) return null;
36552
36788
  const raw = sanitizeExternalText(readFileSync23(path, "utf-8").trim());
36553
36789
  if (!raw) return null;
36554
36790
  if (raw.length <= ANALYST_FILE_MAX_CHARS) return raw;
@@ -37054,7 +37290,7 @@ __export(ingest_chat_exports, {
37054
37290
  loadDemoFromChat: () => loadDemoFromChat,
37055
37291
  looksLikeFilePath: () => looksLikeFilePath
37056
37292
  });
37057
- import { existsSync as existsSync34 } from "fs";
37293
+ import { existsSync as existsSync35 } from "fs";
37058
37294
  import { basename as basename9, resolve as resolve9 } from "path";
37059
37295
  import { homedir as homedir8 } from "os";
37060
37296
  import chalk80 from "chalk";
@@ -37074,11 +37310,11 @@ function extractFilePath(input) {
37074
37310
  const m = trimmed.match(re);
37075
37311
  if (m?.[1]) {
37076
37312
  const p = expandPath(m[1]);
37077
- if (existsSync34(p)) return p;
37313
+ if (existsSync35(p)) return p;
37078
37314
  }
37079
37315
  if (!m?.[1] && re.test(trimmed) && trimmed.toLowerCase().endsWith(".csv")) {
37080
37316
  const p = expandPath(trimmed.replace(/^["']|["']$/g, ""));
37081
- if (existsSync34(p)) return p;
37317
+ if (existsSync35(p)) return p;
37082
37318
  }
37083
37319
  }
37084
37320
  return null;