@sonnechasser/ntrp 1.4.1 → 1.4.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (3) hide show
  1. package/dist/index.js +300 -190
  2. package/dist/mcp/server.js +140 -114
  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,10 @@ 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
+ import { readFileSync as readFileSync11, writeFileSync as writeFileSync10, existsSync as existsSync12, mkdirSync as mkdirSync9 } from "fs";
6271
6283
  import { join as join12 } from "path";
6272
6284
  function ensureDir5() {
6273
- if (!existsSync11(NTRP_DIR3)) {
6285
+ if (!existsSync12(NTRP_DIR3)) {
6274
6286
  mkdirSync9(NTRP_DIR3, { recursive: true });
6275
6287
  }
6276
6288
  }
@@ -6278,14 +6290,14 @@ function profilePath() {
6278
6290
  return PROFILE_PATH;
6279
6291
  }
6280
6292
  function profileExists() {
6281
- return existsSync11(PROFILE_PATH);
6293
+ return existsSync12(PROFILE_PATH);
6282
6294
  }
6283
6295
  function isProfileConfigured(profile = loadProfile()) {
6284
6296
  if (!profile) return false;
6285
6297
  return profile.company_name.trim().length > 0;
6286
6298
  }
6287
6299
  function loadProfile() {
6288
- if (!existsSync11(PROFILE_PATH)) return null;
6300
+ if (!existsSync12(PROFILE_PATH)) return null;
6289
6301
  try {
6290
6302
  const parsed = JSON.parse(readFileSync11(PROFILE_PATH, "utf-8"));
6291
6303
  if (!parsed || typeof parsed !== "object") return null;
@@ -6341,7 +6353,7 @@ __export(lexicon_seed_exports, {
6341
6353
  seedOperatorIdentity: () => seedOperatorIdentity,
6342
6354
  wipePrivacyStore: () => wipePrivacyStore2
6343
6355
  });
6344
- import { existsSync as existsSync12 } from "fs";
6356
+ import { existsSync as existsSync13 } from "fs";
6345
6357
  function invalidateLexiconSeed() {
6346
6358
  dbSeedGeneration = -1;
6347
6359
  }
@@ -6403,7 +6415,7 @@ function seedOperatorIdentity(name, url) {
6403
6415
  }
6404
6416
  async function seedFromDuckDB() {
6405
6417
  const dbPath = getActiveDbPath();
6406
- if (!existsSync12(dbPath)) return;
6418
+ if (!existsSync13(dbPath)) return;
6407
6419
  const { all: all2, getConnectionGeneration: getConnectionGeneration2 } = await Promise.resolve().then(() => (init_connection(), connection_exports));
6408
6420
  const gen = getConnectionGeneration2();
6409
6421
  if (dbSeedGeneration !== -1 && gen === dbSeedGeneration) return;
@@ -6816,7 +6828,7 @@ async function llmCompleteText(surface, system, userMessage, max_tokens, ctx) {
6816
6828
  }
6817
6829
  async function llmStreamText(surface, system, userMessage, max_tokens, ctx) {
6818
6830
  let fullText = "";
6819
- let meta = { model_used: "unknown", provider_used: "anthropic", failover: false };
6831
+ let meta = { model_used: "unknown", provider_used: "unknown", failover: false };
6820
6832
  for await (const event of streamWithFailover(
6821
6833
  {
6822
6834
  surface,
@@ -7120,7 +7132,7 @@ var init_errors2 = __esm({
7120
7132
 
7121
7133
  // src/strategies/readers.ts
7122
7134
  import { createHash } from "crypto";
7123
- import { existsSync as existsSync13, readFileSync as readFileSync12 } from "fs";
7135
+ import { existsSync as existsSync14, readFileSync as readFileSync12 } from "fs";
7124
7136
  import { extname, resolve as resolve5 } from "path";
7125
7137
  import { parse as parseYaml } from "yaml";
7126
7138
  import { PDFParse } from "pdf-parse";
@@ -7130,7 +7142,7 @@ async function readStrategyFile(pathOrDash) {
7130
7142
  return createDocument("stdin", null, text2, {});
7131
7143
  }
7132
7144
  const sourcePath = resolve5(pathOrDash);
7133
- if (!existsSync13(sourcePath)) {
7145
+ if (!existsSync14(sourcePath)) {
7134
7146
  throw new NtrpError("strategy_file_not_found", `Strategy file not found: ${pathOrDash}`, 2 /* Usage */);
7135
7147
  }
7136
7148
  const ext = extname(sourcePath).toLowerCase();
@@ -7199,7 +7211,7 @@ var init_readers = __esm({
7199
7211
  });
7200
7212
 
7201
7213
  // src/memory/knowledge.ts
7202
- import { existsSync as existsSync14, readFileSync as readFileSync13, appendFileSync as appendFileSync2, readdirSync as readdirSync2 } from "fs";
7214
+ import { existsSync as existsSync15, readFileSync as readFileSync13, appendFileSync as appendFileSync2, readdirSync as readdirSync2 } from "fs";
7203
7215
  import { join as join13 } from "path";
7204
7216
  import { randomUUID as randomUUID4 } from "crypto";
7205
7217
  function knowledgePath() {
@@ -7207,7 +7219,7 @@ function knowledgePath() {
7207
7219
  }
7208
7220
  function loadKnowledgeChunks() {
7209
7221
  const path = knowledgePath();
7210
- if (!existsSync14(path)) return [];
7222
+ if (!existsSync15(path)) return [];
7211
7223
  const out = [];
7212
7224
  for (const line of readFileSync13(path, "utf-8").split("\n")) {
7213
7225
  const trimmed = line.trim();
@@ -7298,7 +7310,7 @@ var init_knowledge = __esm({
7298
7310
  });
7299
7311
 
7300
7312
  // src/ai/privacy.ts
7301
- import { existsSync as existsSync15, mkdirSync as mkdirSync10, appendFileSync as appendFileSync3 } from "fs";
7313
+ import { existsSync as existsSync16, mkdirSync as mkdirSync10, appendFileSync as appendFileSync3 } from "fs";
7302
7314
  import { homedir as homedir4 } from "os";
7303
7315
  import { join as join14 } from "path";
7304
7316
  function scrubSensitiveText(text) {
@@ -7319,7 +7331,7 @@ function stripPII(obj) {
7319
7331
  return out;
7320
7332
  }
7321
7333
  function ensureAuditDir() {
7322
- if (!existsSync15(AUDIT_DIR)) {
7334
+ if (!existsSync16(AUDIT_DIR)) {
7323
7335
  mkdirSync10(AUDIT_DIR, { recursive: true });
7324
7336
  }
7325
7337
  }
@@ -7435,14 +7447,14 @@ __export(playbook_exports, {
7435
7447
  matchTriggeredPlays: () => matchTriggeredPlays,
7436
7448
  withKnownRecommendedPlays: () => withKnownRecommendedPlays
7437
7449
  });
7438
- import { existsSync as existsSync16, readFileSync as readFileSync14, appendFileSync as appendFileSync4 } from "fs";
7450
+ import { existsSync as existsSync17, readFileSync as readFileSync14, appendFileSync as appendFileSync4 } from "fs";
7439
7451
  import { join as join15 } from "path";
7440
7452
  function playsPath() {
7441
7453
  return join15(getMemoryDir(), PLAYS_FILE);
7442
7454
  }
7443
7455
  function getCustomPlays() {
7444
7456
  const path = playsPath();
7445
- if (!existsSync16(path)) return [];
7457
+ if (!existsSync17(path)) return [];
7446
7458
  const out = [];
7447
7459
  for (const line of readFileSync14(path, "utf-8").split("\n")) {
7448
7460
  const trimmed = line.trim();
@@ -8330,7 +8342,7 @@ __export(store_exports2, {
8330
8342
  rewriteJsonl: () => rewriteJsonl,
8331
8343
  scrubText: () => scrubText
8332
8344
  });
8333
- import { existsSync as existsSync17, readFileSync as readFileSync15, appendFileSync as appendFileSync5, readdirSync as readdirSync4, writeFileSync as writeFileSync12 } from "fs";
8345
+ import { existsSync as existsSync18, readFileSync as readFileSync15, appendFileSync as appendFileSync5, readdirSync as readdirSync4, writeFileSync as writeFileSync12 } from "fs";
8334
8346
  import { join as join18 } from "path";
8335
8347
  import { randomUUID as randomUUID5 } from "crypto";
8336
8348
  function memPath(file) {
@@ -8338,7 +8350,7 @@ function memPath(file) {
8338
8350
  }
8339
8351
  function readJsonl(file) {
8340
8352
  const path = memPath(file);
8341
- if (!existsSync17(path)) return [];
8353
+ if (!existsSync18(path)) return [];
8342
8354
  const out = [];
8343
8355
  for (const line of readFileSync15(path, "utf-8").split("\n")) {
8344
8356
  const trimmed = line.trim();
@@ -8717,7 +8729,7 @@ __export(context_exports, {
8717
8729
  transcriptPathForSession: () => transcriptPathForSession
8718
8730
  });
8719
8731
  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";
8732
+ 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
8733
  import { homedir as homedir6 } from "os";
8722
8734
  import { randomUUID as randomUUID6 } from "crypto";
8723
8735
  function isSessionStale(s) {
@@ -8736,14 +8748,14 @@ function ntrpHomeDir() {
8736
8748
  }
8737
8749
  function getSessionsDir() {
8738
8750
  const dir = join19(ntrpHomeDir(), "sessions");
8739
- if (!existsSync18(dir)) {
8751
+ if (!existsSync19(dir)) {
8740
8752
  mkdirSync11(dir, { recursive: true });
8741
8753
  }
8742
8754
  return dir;
8743
8755
  }
8744
8756
  function getDatasetsDir() {
8745
8757
  const dir = join19(ntrpHomeDir(), "datasets");
8746
- if (!existsSync18(dir)) {
8758
+ if (!existsSync19(dir)) {
8747
8759
  mkdirSync11(dir, { recursive: true });
8748
8760
  }
8749
8761
  return dir;
@@ -9754,7 +9766,7 @@ var init_admin_confirm = __esm({
9754
9766
  });
9755
9767
 
9756
9768
  // src/services/scratch-wipe.ts
9757
- import { existsSync as existsSync19, rmSync as rmSync6, unlinkSync as unlinkSync3 } from "fs";
9769
+ import { existsSync as existsSync20, rmSync as rmSync6, unlinkSync as unlinkSync3 } from "fs";
9758
9770
  import { join as join20 } from "path";
9759
9771
  async function performScratchWipe(opts = {}) {
9760
9772
  const home = ntrpHome();
@@ -9784,7 +9796,7 @@ async function performScratchWipe(opts = {}) {
9784
9796
  );
9785
9797
  }
9786
9798
  for (const { path, kind } of targets) {
9787
- if (!existsSync19(path)) continue;
9799
+ if (!existsSync20(path)) continue;
9788
9800
  try {
9789
9801
  if (kind === "dir") {
9790
9802
  rmSync6(path, { recursive: true, force: true });
@@ -9887,11 +9899,15 @@ function resolveCardWidth(opts = {}) {
9887
9899
  const usable = Math.max(20, termWidth() - margin);
9888
9900
  return Math.max(Math.min(min, usable), Math.min(usable, max));
9889
9901
  }
9890
- var ANSI_RE;
9902
+ function clearTerminalHome() {
9903
+ process.stdout.write(TERMINAL_HOME_CLEAR);
9904
+ }
9905
+ var ANSI_RE, TERMINAL_HOME_CLEAR;
9891
9906
  var init_layout = __esm({
9892
9907
  "src/ui/layout.ts"() {
9893
9908
  "use strict";
9894
9909
  ANSI_RE = /\u001b\[[0-9;]*m/g;
9910
+ TERMINAL_HOME_CLEAR = "\x1B[2J\x1B[H";
9895
9911
  }
9896
9912
  });
9897
9913
 
@@ -12332,11 +12348,11 @@ var init_guide_slides = __esm({
12332
12348
  };
12333
12349
  HANDOFF = {
12334
12350
  id: "handoff",
12335
- label: "Ship work to Claude",
12336
- tagline: "Teach the inbox once. Later, tell Claude to pick it up.",
12351
+ label: "Ship work to your AI",
12352
+ tagline: "Teach the inbox once. Later, tell your AI to pick it up.",
12337
12353
  visual: {
12338
12354
  kind: "layer_stack",
12339
- caption: "You and Claude find and open the file. NTRP only writes.",
12355
+ caption: "You and your AI find and open the file. NTRP only writes.",
12340
12356
  layers: [
12341
12357
  { label: "Set a pickup folder (demo, onboard, or /inbox set)" },
12342
12358
  { label: "Paste the finder skill once (/inbox skill)", highlight: true },
@@ -12345,18 +12361,18 @@ var init_guide_slides = __esm({
12345
12361
  ]
12346
12362
  },
12347
12363
  lines: [
12348
- 'Type "ship a board deck" or type /handoff. Files land in ~/Documents/Claude/ntrp-inbox by default. Type /inbox set to change the folder.',
12364
+ 'Type "ship a board deck" or type /handoff. Files land in ~/Documents/ntrp-inbox by default. Type /inbox set to change the folder.',
12349
12365
  "",
12350
- "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.",
12366
+ "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.",
12351
12367
  "",
12352
- 'After that, each /handoff prints "Handoff ready". You will not get a paste block every write. Tell Claude: pick up the latest NTRP handoff.',
12368
+ '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.',
12353
12369
  "",
12354
- "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."
12370
+ "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."
12355
12371
  ],
12356
12372
  deepdive: [
12357
12373
  "Inbox copies: latest-handoff.md, latest-pickup.md, SKILL.md, INDEX.md. Dated files also live under ~/.ntrp/exports/.",
12358
12374
  "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.",
12359
- "Audience-framed Metric definitions append to decks and reports. Claude then has the same glossary you walked.",
12375
+ "Audience-framed Metric definitions append to decks and reports. Your AI then has the same glossary you walked.",
12360
12376
  "Type /end to close a session. NTRP writes a transcript plus a 1-page context brief under ~/.ntrp/sessions/."
12361
12377
  ]
12362
12378
  };
@@ -12552,7 +12568,7 @@ function paintIntro(opts) {
12552
12568
  "",
12553
12569
  "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.",
12554
12570
  "",
12555
- "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.",
12571
+ "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.",
12556
12572
  "",
12557
12573
  chalk10.dim("No AI key needed for this tour. Press Enter to advance; type /deepdive (or d) on any slide for more.")
12558
12574
  ],
@@ -12572,7 +12588,7 @@ function paintClose(opts) {
12572
12588
  ` ${paint("accent", "/diagnose")} \u2014 five vital signs + dollars at risk`,
12573
12589
  ` ${paint("accent", "/metrics")} \u2014 SaaS scorecard (ARR, NRR, coverage\u2026)`,
12574
12590
  ` ${paint("accent", "/deepdive")} \u2014 replay this tour \xB7 ${paint("accent", "/deepdive guide")} how-to only`,
12575
- ` ${paint("accent", "/handoff")} \u2014 ship a file; teach Claude the inbox once (/inbox skill)`,
12591
+ ` ${paint("accent", "/handoff")} \u2014 ship a file; teach the inbox once (/inbox skill)`,
12576
12592
  ` ${paint("accent", "/strategy")} \u2014 measurable plan \xB7 ${paint("accent", "/help")} shortcuts`,
12577
12593
  "",
12578
12594
  "Fourteen more SaaS metrics live behind /deepdive list \u2014 including unit economics that unlock when spend data lands.",
@@ -12740,7 +12756,7 @@ async function offerFirstRunTour(ctx) {
12740
12756
  console.log();
12741
12757
  console.log(" " + bold("Onboarding tour") + chalk10.dim(" \u2014 ~3 minutes, no AI key required"));
12742
12758
  console.log(
12743
- " " + chalk10.dim("SaaS numbers, five vitals, then how to talk to NTRP and ship work to Claude.")
12759
+ " " + chalk10.dim("SaaS numbers, five vitals, then how to talk to NTRP and ship work to your AI.")
12744
12760
  );
12745
12761
  const tourDefault = firstRunTourDefaultChoice();
12746
12762
  if (tourDefault === "skip") {
@@ -16233,7 +16249,7 @@ __export(play_outcomes_exports, {
16233
16249
  listPlayOutcomes: () => listPlayOutcomes,
16234
16250
  recordPlayOutcomes: () => recordPlayOutcomes
16235
16251
  });
16236
- import { existsSync as existsSync20, readFileSync as readFileSync17, appendFileSync as appendFileSync6 } from "fs";
16252
+ import { existsSync as existsSync21, readFileSync as readFileSync17, appendFileSync as appendFileSync6 } from "fs";
16237
16253
  import { join as join21 } from "path";
16238
16254
  import { randomUUID as randomUUID7 } from "crypto";
16239
16255
  function outcomesPath() {
@@ -16241,7 +16257,7 @@ function outcomesPath() {
16241
16257
  }
16242
16258
  function listPlayOutcomes() {
16243
16259
  const path = outcomesPath();
16244
- if (!existsSync20(path)) return [];
16260
+ if (!existsSync21(path)) return [];
16245
16261
  const out = [];
16246
16262
  for (const line of readFileSync17(path, "utf-8").split("\n")) {
16247
16263
  const trimmed = line.trim();
@@ -19234,7 +19250,7 @@ var init_markdown = __esm({
19234
19250
  import chalk16 from "chalk";
19235
19251
  function formatLlmAttribution(meta) {
19236
19252
  if (!meta.model_used) return null;
19237
- const provider = meta.provider_used ?? "anthropic";
19253
+ const provider = meta.provider_used ?? "unknown";
19238
19254
  let line = `via ${formatModelLabel(provider, meta.model_used)}`;
19239
19255
  if (meta.failover) {
19240
19256
  line += " (auto-failover)";
@@ -19710,16 +19726,16 @@ var init_terminal = __esm({
19710
19726
  });
19711
19727
 
19712
19728
  // src/demo/taxonomy-cache.ts
19713
- import { readFileSync as readFileSync18, writeFileSync as writeFileSync14, existsSync as existsSync21, mkdirSync as mkdirSync12, unlinkSync as unlinkSync4 } from "fs";
19729
+ import { readFileSync as readFileSync18, writeFileSync as writeFileSync14, existsSync as existsSync22, mkdirSync as mkdirSync12, unlinkSync as unlinkSync4 } from "fs";
19714
19730
  import { homedir as homedir7 } from "os";
19715
19731
  import { join as join22 } from "path";
19716
19732
  function ensureDir6() {
19717
- if (!existsSync21(NTRP_DIR4)) {
19733
+ if (!existsSync22(NTRP_DIR4)) {
19718
19734
  mkdirSync12(NTRP_DIR4, { recursive: true });
19719
19735
  }
19720
19736
  }
19721
19737
  function loadCachedTaxonomy(profile) {
19722
- if (!existsSync21(TAXONOMY_PATH)) return null;
19738
+ if (!existsSync22(TAXONOMY_PATH)) return null;
19723
19739
  try {
19724
19740
  const parsed = JSON.parse(readFileSync18(TAXONOMY_PATH, "utf-8"));
19725
19741
  if (!parsed || typeof parsed !== "object") return null;
@@ -19734,7 +19750,7 @@ function saveCachedTaxonomy(taxonomy) {
19734
19750
  writeFileSync14(TAXONOMY_PATH, JSON.stringify(taxonomy, null, 2) + "\n");
19735
19751
  }
19736
19752
  function invalidateTaxonomy() {
19737
- if (existsSync21(TAXONOMY_PATH)) {
19753
+ if (existsSync22(TAXONOMY_PATH)) {
19738
19754
  try {
19739
19755
  unlinkSync4(TAXONOMY_PATH);
19740
19756
  } catch {
@@ -20204,7 +20220,7 @@ __export(inbox_setup_exports, {
20204
20220
  shouldOfferInboxSkillSetup: () => shouldOfferInboxSkillSetup
20205
20221
  });
20206
20222
  import chalk19 from "chalk";
20207
- import { existsSync as existsSync22 } from "fs";
20223
+ import { existsSync as existsSync23 } from "fs";
20208
20224
  function markDemoOffered() {
20209
20225
  setConfigValue("ai-inbox-nudge-seen", "true");
20210
20226
  }
@@ -20221,7 +20237,7 @@ function shouldOfferInboxSkillSetup(beat) {
20221
20237
  return !hasProductionOffered();
20222
20238
  }
20223
20239
  function printSkipHint(beat) {
20224
- const setCmd = paint("accent", "/inbox set ~/Documents/Claude/ntrp-inbox");
20240
+ const setCmd = paint("accent", `/inbox set ${defaultAiInboxDirDisplay()}`);
20225
20241
  const skillCmd = paint("accent", "/inbox skill");
20226
20242
  if (beat === "demo") {
20227
20243
  console.log(
@@ -20233,13 +20249,15 @@ function printSkipHint(beat) {
20233
20249
  " " + chalk19.dim("Skipped. NTRP will not ask again. Type ") + setCmd + chalk19.dim(" then ") + skillCmd + chalk19.dim(" at any time.")
20234
20250
  );
20235
20251
  }
20236
- async function reuseInboxFolderIfPresent(session, beat, folderPath = defaultAiInboxDir()) {
20252
+ async function reuseInboxFolderIfPresent(session, beat, folderPath) {
20237
20253
  if (getAiInboxDir()) return false;
20238
- if (!existsSync22(folderPath)) return false;
20239
- console.log(" " + chalk19.dim("Pickup folder still on disk: ") + folderPath);
20254
+ const candidates = folderPath ? [folderPath] : [.../* @__PURE__ */ new Set([defaultAiInboxDir(), legacyAiInboxDir()])];
20255
+ const existing = candidates.find((p) => existsSync23(p));
20256
+ if (!existing) return false;
20257
+ console.log(" " + chalk19.dim("Pickup folder still on disk: ") + existing);
20240
20258
  const reuse = await session.confirm("Reuse this pickup folder?", true);
20241
20259
  if (!reuse) return false;
20242
- const resolved = setAiInboxDir(folderPath);
20260
+ const resolved = setAiInboxDir(existing);
20243
20261
  markDemoOffered();
20244
20262
  if (beat === "production") markProductionOffered();
20245
20263
  console.log();
@@ -20254,7 +20272,7 @@ async function offerInboxSkillSetup(session, opts = {}) {
20254
20272
  const beat = opts.beat ?? "production";
20255
20273
  if (!shouldOfferInboxSkillSetup(beat)) return;
20256
20274
  console.log();
20257
- console.log(" " + bold("Teach Claude where handoffs live"));
20275
+ console.log(" " + bold("Teach your AI where handoffs live"));
20258
20276
  console.log(
20259
20277
  " " + chalk19.dim(
20260
20278
  "Optional. NTRP copies every handoff into one folder. You paste instructions once; later /handoff just writes the file."
@@ -20265,7 +20283,10 @@ async function offerInboxSkillSetup(session, opts = {}) {
20265
20283
  }
20266
20284
  console.log();
20267
20285
  if (await reuseInboxFolderIfPresent(session, beat)) return;
20268
- const want = await session.confirm("Set a pickup folder for Claude, ChatGPT, or Cursor?", true);
20286
+ const want = await session.confirm(
20287
+ "Set a pickup folder for Claude, ChatGPT, Cursor, or another desktop AI?",
20288
+ true
20289
+ );
20269
20290
  if (!want) {
20270
20291
  if (beat === "demo") markDemoOffered();
20271
20292
  else markProductionOffered();
@@ -20297,10 +20318,12 @@ async function offerInboxSkillSetup(session, opts = {}) {
20297
20318
  console.log(" " + chalk19.dim(`Synced ${n} recent export${n === 1 ? "" : "s"}.`));
20298
20319
  }
20299
20320
  console.log(
20300
- " " + chalk19.dim("Paste this skill into Claude once. Later, tell Claude to open the latest NTRP handoff.")
20321
+ " " + chalk19.dim(
20322
+ "Paste this skill into your desktop AI once. Later, tell it to open the latest NTRP handoff."
20323
+ )
20301
20324
  );
20302
20325
  printStandingSkill();
20303
- await session.askPressEnter("Paste the skill into Claude. Then continue");
20326
+ await session.askPressEnter("Paste the skill into your AI. Then continue");
20304
20327
  console.log(" " + chalk19.dim("Done. Later handoffs write to that folder. The skill is not printed again."));
20305
20328
  console.log();
20306
20329
  }
@@ -20335,7 +20358,7 @@ __export(ingest_exports, {
20335
20358
  handler: () => handler2
20336
20359
  });
20337
20360
  import chalk20 from "chalk";
20338
- import { readFileSync as readFileSync19, existsSync as existsSync23 } from "fs";
20361
+ import { readFileSync as readFileSync19, existsSync as existsSync24 } from "fs";
20339
20362
  import { basename as basename6 } from "path";
20340
20363
  async function handler2(args, ctx) {
20341
20364
  const { positional, flags } = parseArgs2(args, [
@@ -20359,7 +20382,7 @@ async function handler2(args, ctx) {
20359
20382
  console.error(chalk20.dim(" /ingest --demo [--scenario <name>]"));
20360
20383
  process.exit(1);
20361
20384
  }
20362
- if (!existsSync23(file)) {
20385
+ if (!existsSync24(file)) {
20363
20386
  console.error(chalk20.red(` File not found: ${file}`));
20364
20387
  process.exit(1);
20365
20388
  }
@@ -20947,7 +20970,7 @@ function buildUserMessage(input) {
20947
20970
  async function* streamFindings(input, ctx) {
20948
20971
  assertReplAi(ctx);
20949
20972
  const userMessage = buildUserMessage(input);
20950
- let meta = { model_used: "unknown", provider_used: "anthropic", failover: false };
20973
+ let meta = { model_used: "unknown", provider_used: "unknown", failover: false };
20951
20974
  const scanner = new StreamingJsonArrayParser();
20952
20975
  let streamed = 0;
20953
20976
  for await (const event of streamWithFailover(
@@ -22445,9 +22468,9 @@ async function handleGetSessionBrief(input) {
22445
22468
  if (!target) {
22446
22469
  return { error: `No session matching "${raw}".` };
22447
22470
  }
22448
- const { existsSync: existsSync34, readFileSync: readFileSync24 } = await import("fs");
22471
+ const { existsSync: existsSync36, readFileSync: readFileSync24 } = await import("fs");
22449
22472
  const briefPath = contextDocPathForSession2(target.id);
22450
- if (!existsSync34(briefPath)) {
22473
+ if (!existsSync36(briefPath)) {
22451
22474
  return {
22452
22475
  session_id: target.id,
22453
22476
  error: "No context brief on disk for this session (created before brief storage existed).",
@@ -23162,7 +23185,7 @@ async function* agenticFindings(computeResult, divergences, options) {
23162
23185
  const priorMessages = priorRaw.length > 0 && typeof priorRaw[0] === "object" && priorRaw[0] !== null && "content" in priorRaw[0] ? normalizeThread(priorRaw) : priorRaw;
23163
23186
  const conversational = mode === "fresh" || mode === "think";
23164
23187
  const messages = conversational && priorMessages.length > 0 && options.userQuestion ? [...priorMessages, { role: "user", content: options.userQuestion }] : [{ role: "user", content: initialContext }];
23165
- let lastMeta = { provider_used: "anthropic", model_used: "unknown" };
23188
+ let lastMeta = { provider_used: "unknown", model_used: "unknown" };
23166
23189
  const loopGuard = new ToolLoopGuard();
23167
23190
  const allowedTools = new Set(tools.map((t) => t.name));
23168
23191
  async function callLlm(msgs, tokens, withTools) {
@@ -25128,7 +25151,7 @@ __export(new_exports, {
25128
25151
  handler: () => handler6
25129
25152
  });
25130
25153
  import chalk26 from "chalk";
25131
- import { existsSync as existsSync24 } from "fs";
25154
+ import { existsSync as existsSync25 } from "fs";
25132
25155
  import { basename as basename7 } from "path";
25133
25156
  async function handler6(args, ctx) {
25134
25157
  const { positional, flags } = parseArgs2(args, ["demo", "empty", "list-scenarios", "regen-taxonomy"]);
@@ -25150,7 +25173,7 @@ async function handler6(args, ctx) {
25150
25173
  console.error(chalk26.red(" Usage: /new <file.csv> | --demo [--scenario <name>] | --empty [--lens health|metrics]"));
25151
25174
  return;
25152
25175
  }
25153
- if (source.kind === "file" && !existsSync24(source.path)) {
25176
+ if (source.kind === "file" && !existsSync25(source.path)) {
25154
25177
  console.error(chalk26.red(` File not found: ${source.path}`));
25155
25178
  return;
25156
25179
  }
@@ -25377,7 +25400,7 @@ __export(end_exports, {
25377
25400
  handler: () => handler7
25378
25401
  });
25379
25402
  import chalk27 from "chalk";
25380
- import { existsSync as existsSync25 } from "fs";
25403
+ import { existsSync as existsSync26 } from "fs";
25381
25404
  async function handler7(args, ctx) {
25382
25405
  if (args.length > 0) {
25383
25406
  console.error(chalk27.red(" Usage: /end"));
@@ -25414,10 +25437,10 @@ async function handler7(args, ctx) {
25414
25437
  if (summary) {
25415
25438
  console.log(" " + chalk27.dim(summary));
25416
25439
  }
25417
- if (existsSync25(transcriptPathForSession(endedId))) {
25440
+ if (existsSync26(transcriptPathForSession(endedId))) {
25418
25441
  console.log(" " + chalk27.dim("Transcript: ") + chalk27.dim(transcriptPathForSession(endedId)));
25419
25442
  }
25420
- if (existsSync25(contextDocPathForSession(endedId))) {
25443
+ if (existsSync26(contextDocPathForSession(endedId))) {
25421
25444
  console.log(" " + chalk27.dim("Context brief: ") + chalk27.dim(contextDocPathForSession(endedId)));
25422
25445
  }
25423
25446
  console.log();
@@ -25439,7 +25462,7 @@ __export(session_exports, {
25439
25462
  });
25440
25463
  import chalk28 from "chalk";
25441
25464
  import { join as join23 } from "path";
25442
- import { existsSync as existsSync26 } from "fs";
25465
+ import { existsSync as existsSync27 } from "fs";
25443
25466
  async function handler8(args, ctx) {
25444
25467
  const sub = args[0];
25445
25468
  if (!sub) return listSessionsView(ctx);
@@ -25585,7 +25608,7 @@ async function pickUp(idArg, ctx) {
25585
25608
  );
25586
25609
  }
25587
25610
  const contextPath = contextDocPathForSession(target.id);
25588
- if (existsSync26(contextPath)) {
25611
+ if (existsSync27(contextPath)) {
25589
25612
  console.log(" " + chalk28.dim("Context brief: ") + chalk28.dim(contextPath));
25590
25613
  }
25591
25614
  console.log();
@@ -28795,7 +28818,7 @@ async function* strategistPlanSession(options) {
28795
28818
  })
28796
28819
  }
28797
28820
  ];
28798
- let lastMeta = { provider_used: "anthropic", model_used: "unknown" };
28821
+ let lastMeta = { provider_used: "unknown", model_used: "unknown" };
28799
28822
  const loopGuard = new ToolLoopGuard();
28800
28823
  const allowedTools = new Set(tools.map((t) => t.name));
28801
28824
  const callLlm = async (surface, withTools, maxTokens = STAGE_MAX_TOKENS) => {
@@ -31127,7 +31150,7 @@ var init_checkout = __esm({
31127
31150
  });
31128
31151
 
31129
31152
  // src/services/setup.ts
31130
- import { existsSync as existsSync27, mkdirSync as mkdirSync17, readFileSync as readFileSync20, writeFileSync as writeFileSync19 } from "fs";
31153
+ import { existsSync as existsSync28, mkdirSync as mkdirSync17, readFileSync as readFileSync20, writeFileSync as writeFileSync19 } from "fs";
31131
31154
  import { join as join29 } from "path";
31132
31155
  function setupCheck() {
31133
31156
  const home = ntrpHome();
@@ -31830,7 +31853,7 @@ async function handleDeliverFlow(input, ctx) {
31830
31853
  }
31831
31854
  console.log();
31832
31855
  console.log(" " + chalk52.bold("Deliverable preview"));
31833
- 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."));
31856
+ 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."));
31834
31857
  console.log(" " + chalk52.dim("\u2500".repeat(56)));
31835
31858
  const preview = draft.markdown.split("\n").slice(0, 12);
31836
31859
  for (const l of preview) {
@@ -33130,7 +33153,7 @@ __export(sessions_exports, {
33130
33153
  handler: () => handler35
33131
33154
  });
33132
33155
  import chalk62 from "chalk";
33133
- import { existsSync as existsSync28 } from "fs";
33156
+ import { existsSync as existsSync29 } from "fs";
33134
33157
  async function handler35(args, _ctx) {
33135
33158
  const sub = args[0] ?? "list";
33136
33159
  if (sub === "list" || !args[0]) {
@@ -33237,12 +33260,12 @@ function showSession(idArg) {
33237
33260
  console.log();
33238
33261
  const transcriptPath = transcriptPathForSession(session.id);
33239
33262
  const contextPath = contextDocPathForSession(session.id);
33240
- if (existsSync28(transcriptPath) || existsSync28(contextPath)) {
33263
+ if (existsSync29(transcriptPath) || existsSync29(contextPath)) {
33241
33264
  console.log(" " + chalk62.dim("\u2500".repeat(40)));
33242
- if (existsSync28(contextPath)) {
33265
+ if (existsSync29(contextPath)) {
33243
33266
  console.log(" " + chalk62.dim("Context brief: ") + chalk62.dim(contextPath));
33244
33267
  }
33245
- if (existsSync28(transcriptPath)) {
33268
+ if (existsSync29(transcriptPath)) {
33246
33269
  console.log(" " + chalk62.dim("Full transcript: ") + chalk62.dim(transcriptPath));
33247
33270
  }
33248
33271
  console.log();
@@ -33514,7 +33537,7 @@ var init_privacy_notice = __esm({
33514
33537
  "Computed scores and dollar aggregates still go to the provider you connected.",
33515
33538
  "If you turn on web retrieval, named-account queries are refused. Generic GTM terms may go to Tavily or Brave.",
33516
33539
  "A custom --base-url receives the same tokenized payload.",
33517
- "MCP hosts (for example Claude Desktop) see tokens, not account names. Use the CLI to read real names.",
33540
+ "MCP hosts (for example Claude Desktop or Cursor) see tokens, not account names. Use the CLI to read real names.",
33518
33541
  "Keys stay in ~/.ntrp/config.json on this machine (mode 600).",
33519
33542
  "NTRP is a diagnostic. It does not change CRM records or send email.",
33520
33543
  "Type /privacy to read this notice again."
@@ -34578,20 +34601,20 @@ var init_model = __esm({
34578
34601
  });
34579
34602
 
34580
34603
  // src/config/update-check.ts
34581
- import { existsSync as existsSync29, mkdirSync as mkdirSync18, readFileSync as readFileSync21, unlinkSync as unlinkSync5, writeFileSync as writeFileSync20 } from "fs";
34604
+ import { existsSync as existsSync30, mkdirSync as mkdirSync18, readFileSync as readFileSync21, unlinkSync as unlinkSync5, writeFileSync as writeFileSync20 } from "fs";
34582
34605
  import { join as join33 } from "path";
34583
34606
  function cachePath2() {
34584
34607
  return join33(ntrpHome(), "update-check.json");
34585
34608
  }
34586
34609
  function ensureDir7() {
34587
34610
  const dir = ntrpHome();
34588
- if (!existsSync29(dir)) {
34611
+ if (!existsSync30(dir)) {
34589
34612
  mkdirSync18(dir, { recursive: true });
34590
34613
  }
34591
34614
  }
34592
34615
  function loadUpdateCheckCache() {
34593
34616
  const path = cachePath2();
34594
- if (!existsSync29(path)) return null;
34617
+ if (!existsSync30(path)) return null;
34595
34618
  try {
34596
34619
  const parsed = JSON.parse(readFileSync21(path, "utf-8"));
34597
34620
  if (!parsed || typeof parsed !== "object" || typeof parsed.lastCheck !== "number" || typeof parsed.latestVersion !== "string") {
@@ -34612,7 +34635,7 @@ function isCacheFresh(cache2, ttlMs = CACHE_TTL_MS2) {
34612
34635
  }
34613
34636
  function invalidateUpdateCheckCache() {
34614
34637
  const path = cachePath2();
34615
- if (existsSync29(path)) {
34638
+ if (existsSync30(path)) {
34616
34639
  unlinkSync5(path);
34617
34640
  }
34618
34641
  }
@@ -34626,25 +34649,32 @@ var init_update_check = __esm({
34626
34649
  });
34627
34650
 
34628
34651
  // src/version.ts
34629
- import { existsSync as existsSync30, readFileSync as readFileSync22 } from "fs";
34652
+ import { existsSync as existsSync31, readFileSync as readFileSync22 } from "fs";
34630
34653
  import { dirname as dirname6, join as join34 } from "path";
34631
34654
  import { fileURLToPath } from "url";
34655
+ function readVersionFromPackageJson(packageJsonPath) {
34656
+ if (!existsSync31(packageJsonPath)) return null;
34657
+ try {
34658
+ const pkg = JSON.parse(readFileSync22(packageJsonPath, "utf-8"));
34659
+ if (typeof pkg.version === "string" && pkg.version.length > 0) return pkg.version;
34660
+ } catch {
34661
+ }
34662
+ return null;
34663
+ }
34664
+ function readVersionNearEntry(entryPath) {
34665
+ const start = dirname6(entryPath);
34666
+ for (const rel of [join34(start, "..", "package.json"), join34(start, "../..", "package.json")]) {
34667
+ const version = readVersionFromPackageJson(rel);
34668
+ if (version) return version;
34669
+ }
34670
+ return null;
34671
+ }
34672
+ function readInstalledVersionFromDisk() {
34673
+ return readVersionNearEntry(fileURLToPath(import.meta.url));
34674
+ }
34632
34675
  function getInstalledVersion() {
34633
34676
  if (cachedVersion) return cachedVersion;
34634
- const start = dirname6(fileURLToPath(import.meta.url));
34635
- for (const rel of ["../package.json", "../../package.json"]) {
34636
- const path = join34(start, rel);
34637
- if (!existsSync30(path)) continue;
34638
- try {
34639
- const pkg = JSON.parse(readFileSync22(path, "utf-8"));
34640
- if (typeof pkg.version === "string" && pkg.version.length > 0) {
34641
- cachedVersion = pkg.version;
34642
- return cachedVersion;
34643
- }
34644
- } catch {
34645
- }
34646
- }
34647
- cachedVersion = "0.0.0";
34677
+ cachedVersion = readInstalledVersionFromDisk() ?? "0.0.0";
34648
34678
  return cachedVersion;
34649
34679
  }
34650
34680
  var cachedVersion;
@@ -34756,9 +34786,16 @@ __export(relaunch_exports, {
34756
34786
  JUST_UPDATED_ENV: () => JUST_UPDATED_ENV,
34757
34787
  consumeJustUpdatedEnv: () => consumeJustUpdatedEnv,
34758
34788
  encodeJustUpdated: () => encodeJustUpdated,
34789
+ reclaimStdinAfterFailedRelaunch: () => reclaimStdinAfterFailedRelaunch,
34790
+ relaunchArgv: () => relaunchArgv,
34759
34791
  relaunchIntoHome: () => relaunchIntoHome,
34792
+ releaseStdinForRelaunch: () => releaseStdinForRelaunch,
34793
+ resolveRelaunchEntry: () => resolveRelaunchEntry,
34760
34794
  updateRestartSummary: () => updateRestartSummary
34761
34795
  });
34796
+ import { existsSync as existsSync32 } from "fs";
34797
+ import { join as join35 } from "path";
34798
+ import { fileURLToPath as fileURLToPath2 } from "url";
34762
34799
  import { spawnSync } from "child_process";
34763
34800
  function encodeJustUpdated(fromVersion, toVersion) {
34764
34801
  return `${fromVersion}\u2192${toVersion}`;
@@ -34778,16 +34815,53 @@ function consumeJustUpdatedEnv() {
34778
34815
  function updateRestartSummary(toVersion) {
34779
34816
  return `Restart NTRP to use v${toVersion}`;
34780
34817
  }
34818
+ function npmGlobalEntry() {
34819
+ const listed = spawnSync("npm", ["root", "-g"], { encoding: "utf-8" });
34820
+ if (listed.status !== 0) return null;
34821
+ const entry = join35(listed.stdout.trim(), NPM_PACKAGE, "dist/index.js");
34822
+ return existsSync32(entry) ? entry : null;
34823
+ }
34824
+ function thisBundleEntry() {
34825
+ return fileURLToPath2(import.meta.url);
34826
+ }
34827
+ function resolveRelaunchEntry(toVersion) {
34828
+ const candidates = [thisBundleEntry(), npmGlobalEntry(), process.argv[1]].filter(
34829
+ (p) => Boolean(p)
34830
+ );
34831
+ for (const entry of candidates) {
34832
+ if (!existsSync32(entry)) continue;
34833
+ if (readVersionNearEntry(entry) === toVersion) return entry;
34834
+ }
34835
+ return candidates.find((p) => existsSync32(p)) ?? thisBundleEntry();
34836
+ }
34837
+ function relaunchArgv(toVersion) {
34838
+ return [resolveRelaunchEntry(toVersion)];
34839
+ }
34840
+ function releaseStdinForRelaunch(rl) {
34841
+ try {
34842
+ if (process.stdin.isTTY && process.stdin.isRaw === true) {
34843
+ process.stdin.setRawMode(false);
34844
+ }
34845
+ } catch {
34846
+ }
34847
+ try {
34848
+ rl?.pause();
34849
+ } catch {
34850
+ }
34851
+ }
34852
+ function reclaimStdinAfterFailedRelaunch(rl) {
34853
+ try {
34854
+ rl?.resume();
34855
+ } catch {
34856
+ }
34857
+ }
34781
34858
  async function relaunchIntoHome(opts) {
34782
34859
  const { stopSessionTranscript: stopSessionTranscript2 } = await Promise.resolve().then(() => (init_transcript(), transcript_exports));
34783
34860
  const { close: close2 } = await Promise.resolve().then(() => (init_connection(), connection_exports));
34784
34861
  stopSessionTranscript2();
34785
34862
  await close2();
34786
- try {
34787
- opts.rl?.pause();
34788
- } catch {
34789
- }
34790
- const result = spawnSync(process.execPath, process.argv.slice(1), {
34863
+ releaseStdinForRelaunch(opts.rl);
34864
+ const result = spawnSync(process.execPath, relaunchArgv(opts.toVersion), {
34791
34865
  stdio: "inherit",
34792
34866
  env: {
34793
34867
  ...process.env,
@@ -34795,10 +34869,7 @@ async function relaunchIntoHome(opts) {
34795
34869
  }
34796
34870
  });
34797
34871
  if (result.error) {
34798
- try {
34799
- opts.rl?.resume();
34800
- } catch {
34801
- }
34872
+ reclaimStdinAfterFailedRelaunch(opts.rl);
34802
34873
  return "failed";
34803
34874
  }
34804
34875
  process.exit(result.status ?? 0);
@@ -34808,6 +34879,8 @@ var JUST_UPDATED_ENV;
34808
34879
  var init_relaunch = __esm({
34809
34880
  "src/update/relaunch.ts"() {
34810
34881
  "use strict";
34882
+ init_version();
34883
+ init_registry();
34811
34884
  JUST_UPDATED_ENV = "NTRP_JUST_UPDATED";
34812
34885
  }
34813
34886
  });
@@ -34864,6 +34937,7 @@ async function handler44(_args, ctx) {
34864
34937
  return;
34865
34938
  }
34866
34939
  console.log();
34940
+ console.log(chalk73.dim(` Starting v${latest}\u2026`));
34867
34941
  const failed = await relaunchIntoHome({
34868
34942
  fromVersion: current,
34869
34943
  toVersion: latest,
@@ -34872,6 +34946,7 @@ async function handler44(_args, ctx) {
34872
34946
  if (failed) {
34873
34947
  const restart = updateRestartSummary(latest);
34874
34948
  console.log(chalk73.green(` \u2713 Updated! ${restart}`));
34949
+ console.log(chalk73.dim(" Type /exit, then ntrp. This window is still the old process."));
34875
34950
  console.log();
34876
34951
  return restart;
34877
34952
  }
@@ -35280,8 +35355,8 @@ __export(exports_exports, {
35280
35355
  handler: () => handler48
35281
35356
  });
35282
35357
  import chalk78 from "chalk";
35283
- import { existsSync as existsSync31 } from "fs";
35284
- import { join as join35 } from "path";
35358
+ import { existsSync as existsSync33 } from "fs";
35359
+ import { join as join36 } from "path";
35285
35360
  function usage4() {
35286
35361
  console.log(chalk78.dim(" Usage:"));
35287
35362
  console.log(chalk78.dim(" /exports list [kind]"));
@@ -35338,7 +35413,7 @@ function printInboxShow() {
35338
35413
  console.log(" " + paint("accent", "AI inbox: ") + inbox);
35339
35414
  const latest = inboxLatestHandoffPath();
35340
35415
  if (latest) console.log(" " + chalk78.dim("Latest handoff: ") + latest);
35341
- console.log(" " + chalk78.dim("Finder skill: ") + join35(inbox, "SKILL.md"));
35416
+ console.log(" " + chalk78.dim("Finder skill: ") + join36(inbox, "SKILL.md"));
35342
35417
  console.log(" " + chalk78.dim("Reprint: ") + paint("accent", "/inbox skill"));
35343
35418
  } else {
35344
35419
  console.log(" " + chalk78.dim("AI inbox: (not set). Type ") + paint("accent", "/inbox set <folder>"));
@@ -35360,8 +35435,8 @@ function printOpen() {
35360
35435
  console.log(" " + chalk78.dim("AI inbox: ") + inbox);
35361
35436
  const latest = inboxLatestHandoffPath();
35362
35437
  if (latest) console.log(" " + chalk78.dim("Inbox latest: ") + latest);
35363
- console.log(" " + chalk78.dim("Pickup skill: ") + join35(inbox, "latest-pickup.md"));
35364
- console.log(" " + chalk78.dim("Finder skill: ") + join35(inbox, "SKILL.md"));
35438
+ console.log(" " + chalk78.dim("Pickup skill: ") + join36(inbox, "latest-pickup.md"));
35439
+ console.log(" " + chalk78.dim("Finder skill: ") + join36(inbox, "SKILL.md"));
35365
35440
  } else {
35366
35441
  console.log(" " + chalk78.dim("AI inbox is not set. Type ") + paint("accent", "/inbox set <folder>"));
35367
35442
  const loc = handoffLocations();
@@ -35392,7 +35467,7 @@ async function runInboxSet(args, ctx) {
35392
35467
  const pathArg = args.join(" ").trim();
35393
35468
  if (!pathArg) {
35394
35469
  console.error(chalk78.red(" Usage: /inbox set <path>"));
35395
- console.error(chalk78.dim(" Example: /inbox set ~/Documents/Claude/ntrp-inbox"));
35470
+ console.error(chalk78.dim(" Example: /inbox set ~/Documents/ntrp-inbox"));
35396
35471
  if (ctx.oneShot) process.exit(1);
35397
35472
  return;
35398
35473
  }
@@ -35422,7 +35497,7 @@ async function runInboxSet(args, ctx) {
35422
35497
  console.log();
35423
35498
  console.log(" " + paint("accent", "AI inbox set"));
35424
35499
  console.log(" " + chalk78.dim(setTo));
35425
- console.log(" " + chalk78.dim("Point Claude Desktop or another desktop AI at this folder."));
35500
+ console.log(" " + chalk78.dim("Point Claude, ChatGPT, Cursor, or another desktop AI at this folder."));
35426
35501
  if (n > 0) {
35427
35502
  console.log(" " + chalk78.dim(`Synced ${n} recent export${n === 1 ? "" : "s"} into the inbox.`));
35428
35503
  }
@@ -35439,7 +35514,7 @@ function runMove(args, ctx) {
35439
35514
  }
35440
35515
  try {
35441
35516
  const destDir = resolveUserPath(dest);
35442
- if (!existsSync31(destDir)) {
35517
+ if (!existsSync33(destDir)) {
35443
35518
  }
35444
35519
  const event = moveExport(idOrName, destDir);
35445
35520
  console.log();
@@ -35792,12 +35867,12 @@ args: [show|set <path>|skill|clear]
35792
35867
  handler: ../commands/exports.ts
35793
35868
  ---
35794
35869
 
35795
- Set a folder that Claude Desktop or any desktop AI can read.
35870
+ Set a folder that any desktop AI can read (Claude, ChatGPT, Cursor, \u2026).
35796
35871
  NTRP copies each handoff to that folder.
35797
35872
  NTRP overwrites \`latest-handoff.md\` and \`latest-handoff-deck.md\`. The app then finds the newest file.
35798
35873
  NTRP also writes \`SKILL.md\`. That file holds finder instructions with your paths.
35799
35874
  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.
35800
- Paste that skill once into Claude, ChatGPT, or Cursor. Later handoffs need no new paste.
35875
+ Paste that skill once into Claude, ChatGPT, Cursor, or another desktop AI. Later handoffs need no new paste.
35801
35876
  \`INDEX.md\` in that folder links to the archive.
35802
35877
  A path outside ~/.ntrp needs a confirm. Handoffs carry analysis text.
35803
35878
  \`clear\` does not delete files. It only removes the config pointer.`
@@ -36148,9 +36223,9 @@ args: [<metric>|guide|list|tour]
36148
36223
  handler: ../commands/deepdive.ts
36149
36224
  ---
36150
36225
 
36151
- CLI slide deck: SaaS refresher, five vital signs (definition, formula, visual, dollar translation), then a how-to section (talk, ask, ship to Claude, loop).
36152
- Bare \`/deepdive\` runs the full onboarding tour. Type \`/deepdive guide\` to jump to how to use NTRP (includes teaching Claude the inbox once).
36153
- Type \`/deepdive handoff\` for the ship-to-Claude slide. Type \`/deepdive <metric>\` to jump to one metric card.
36226
+ 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).
36227
+ Bare \`/deepdive\` runs the full onboarding tour. Type \`/deepdive guide\` to jump to how to use NTRP (includes teaching the inbox once).
36228
+ Type \`/deepdive handoff\` for the ship-to-AI slide. Type \`/deepdive <metric>\` to jump to one metric card.
36154
36229
  Type \`/deepdive list\` to print the catalog. Works without an AI key. Re-run anytime from the homescreen.
36155
36230
  Live values overlay when an analysis exists.`
36156
36231
  },
@@ -36471,8 +36546,8 @@ Remaining nuances merge into a custom_context paragraph that flows into all AI s
36471
36546
  });
36472
36547
 
36473
36548
  // src/ai/prompt-parts.ts
36474
- import { existsSync as existsSync32, readFileSync as readFileSync23 } from "fs";
36475
- import { join as join36 } from "path";
36549
+ import { existsSync as existsSync34, readFileSync as readFileSync23 } from "fs";
36550
+ import { join as join37 } from "path";
36476
36551
  function buildCompanyProfileBlock() {
36477
36552
  const p = loadProfile();
36478
36553
  if (!p) return "";
@@ -36490,9 +36565,9 @@ function buildCompanyProfileBlock() {
36490
36565
  return lines.join("\n");
36491
36566
  }
36492
36567
  function loadAnalystFile() {
36493
- const path = join36(ntrpHome(), ANALYST_FILE_NAME);
36568
+ const path = join37(ntrpHome(), ANALYST_FILE_NAME);
36494
36569
  try {
36495
- if (!existsSync32(path)) return null;
36570
+ if (!existsSync34(path)) return null;
36496
36571
  const raw = sanitizeExternalText(readFileSync23(path, "utf-8").trim());
36497
36572
  if (!raw) return null;
36498
36573
  if (raw.length <= ANALYST_FILE_MAX_CHARS) return raw;
@@ -36998,7 +37073,7 @@ __export(ingest_chat_exports, {
36998
37073
  loadDemoFromChat: () => loadDemoFromChat,
36999
37074
  looksLikeFilePath: () => looksLikeFilePath
37000
37075
  });
37001
- import { existsSync as existsSync33 } from "fs";
37076
+ import { existsSync as existsSync35 } from "fs";
37002
37077
  import { basename as basename9, resolve as resolve9 } from "path";
37003
37078
  import { homedir as homedir8 } from "os";
37004
37079
  import chalk80 from "chalk";
@@ -37018,11 +37093,11 @@ function extractFilePath(input) {
37018
37093
  const m = trimmed.match(re);
37019
37094
  if (m?.[1]) {
37020
37095
  const p = expandPath(m[1]);
37021
- if (existsSync33(p)) return p;
37096
+ if (existsSync35(p)) return p;
37022
37097
  }
37023
37098
  if (!m?.[1] && re.test(trimmed) && trimmed.toLowerCase().endsWith(".csv")) {
37024
37099
  const p = expandPath(trimmed.replace(/^["']|["']$/g, ""));
37025
- if (existsSync33(p)) return p;
37100
+ if (existsSync35(p)) return p;
37026
37101
  }
37027
37102
  }
37028
37103
  return null;
@@ -38264,7 +38339,8 @@ __export(welcome_exports, {
38264
38339
  formatEmptyDataHomeHint: () => formatEmptyDataHomeHint,
38265
38340
  formatHomeEntityCounts: () => formatHomeEntityCounts,
38266
38341
  printWelcome: () => printWelcome,
38267
- resolveWelcomeNextAction: () => resolveWelcomeNextAction
38342
+ resolveWelcomeNextAction: () => resolveWelcomeNextAction,
38343
+ staleProcessHomeNotice: () => staleProcessHomeNotice
38268
38344
  });
38269
38345
  import chalk87 from "chalk";
38270
38346
  function formatHomeEntityCounts(counts) {
@@ -38287,19 +38363,41 @@ function formatEmptyDataHomeHint(savedSessionCount, opts = {}) {
38287
38363
  return chalk87.dim("Type ") + paint("accent", "use demo data") + chalk87.dim(" to load a sample pipeline") + onboard;
38288
38364
  }
38289
38365
  function ntrpStatusRow(version, update) {
38366
+ const disk = readInstalledVersionFromDisk();
38290
38367
  if (update && isNewerVersion(update.latest, version)) {
38368
+ if (disk && !isNewerVersion(update.latest, disk)) {
38369
+ return {
38370
+ label: "ntrp",
38371
+ state: badge("RESTART", "warning"),
38372
+ detail: `v${disk} installed \xB7 type /exit`
38373
+ };
38374
+ }
38291
38375
  return {
38292
38376
  label: "ntrp",
38293
38377
  state: badge("UPDATE", "warning"),
38294
38378
  detail: `v${update.latest} \xB7 type /update`
38295
38379
  };
38296
38380
  }
38381
+ if (disk && isNewerVersion(disk, getInstalledVersion())) {
38382
+ return {
38383
+ label: "ntrp",
38384
+ state: badge("RESTART", "warning"),
38385
+ detail: `v${disk} installed \xB7 type /exit`
38386
+ };
38387
+ }
38297
38388
  return {
38298
38389
  label: "ntrp",
38299
38390
  state: chalk87.dim(`v${version}`),
38300
38391
  detail: ""
38301
38392
  };
38302
38393
  }
38394
+ function staleProcessHomeNotice() {
38395
+ const running = getInstalledVersion();
38396
+ const disk = readInstalledVersionFromDisk();
38397
+ if (!disk || disk === running) return null;
38398
+ if (!isNewerVersion(disk, running)) return null;
38399
+ return `This window is still v${running}. Type /exit, then ntrp (v${disk} is installed).`;
38400
+ }
38303
38401
  function resolveSessionSummary(input) {
38304
38402
  if (input.scope?.intent_summary?.trim()) return input.scope.intent_summary.trim();
38305
38403
  if (input.summary?.trim()) return input.summary.trim();
@@ -38612,6 +38710,10 @@ async function printWelcome(ctx, version) {
38612
38710
  cardW
38613
38711
  )
38614
38712
  );
38713
+ const stale = staleProcessHomeNotice();
38714
+ if (stale) {
38715
+ push(truncateVisible(` ${paint("warning", "\u2691")} ${stale}`, cardW));
38716
+ }
38615
38717
  if (strategyNudge) {
38616
38718
  push(
38617
38719
  truncateVisible(
@@ -38654,6 +38756,7 @@ var init_welcome = __esm({
38654
38756
  init_queries();
38655
38757
  init_schema();
38656
38758
  init_registry();
38759
+ init_version();
38657
38760
  NO_SUMMARY = "(no summary)";
38658
38761
  CARD_MAX_W = 128;
38659
38762
  CARD_SIDE_MARGIN = 6;
@@ -38818,7 +38921,7 @@ __export(repl_exports, {
38818
38921
  import { createInterface as createInterface2 } from "readline/promises";
38819
38922
  import { clearLine as clearLine2, cursorTo as cursorTo2 } from "readline";
38820
38923
  import chalk88 from "chalk";
38821
- import { join as join37 } from "path";
38924
+ import { join as join38 } from "path";
38822
38925
  function buildPrompt(ctx) {
38823
38926
  return buildConversationPrompt(ctx);
38824
38927
  }
@@ -38921,7 +39024,7 @@ async function goHome(ctx, version, history, opts) {
38921
39024
  ctx.wizardDepth = 0;
38922
39025
  ctx.secretInputActive = false;
38923
39026
  history.length = 0;
38924
- process.stdout.write("\x1B[2J\x1B[H");
39027
+ clearTerminalHome();
38925
39028
  if (opts?.banner) {
38926
39029
  console.log();
38927
39030
  console.log(" " + paint("accent", "\u2713") + " " + chalk88.dim(opts.banner));
@@ -39183,7 +39286,7 @@ function printHelp() {
39183
39286
  ["/remember <fact>", "Store a fact, a decision, or a preference"],
39184
39287
  ["/recall [topic]", "Show what NTRP stores about your business"],
39185
39288
  ["/rate good|bad <note>", "Correct the last answer. A bad note becomes a calibration"],
39186
- [`${join37(ntrpHome(), ANALYST_FILE_NAME)}`, "Standing operator instructions (tone, priorities, house rules)"]
39289
+ [`${join38(ntrpHome(), ANALYST_FILE_NAME)}`, "Standing operator instructions (tone, priorities, house rules)"]
39187
39290
  ];
39188
39291
  const teachMaxW = Math.max(...teach.map(([c]) => c.length)) + 2;
39189
39292
  for (const [cmd, desc] of teach) {
@@ -39301,6 +39404,7 @@ init_activation();
39301
39404
  init_gate2();
39302
39405
  init_profile();
39303
39406
  init_theme();
39407
+ init_layout();
39304
39408
  init_emit();
39305
39409
  init_errors2();
39306
39410
  init_types2();
@@ -39348,13 +39452,13 @@ async function main() {
39348
39452
  if (args.oneShot) {
39349
39453
  const cmd = firstToken(args.input);
39350
39454
  if (!UNGATED.has(cmd)) {
39351
- const lic2 = await refreshLicenseOnline();
39352
- if (!lic2.valid) {
39455
+ const lic = await refreshLicenseOnline();
39456
+ if (!lic.valid) {
39353
39457
  if (isStructuredOutput(ctx.execution)) {
39354
- emitError(cmd || "ntrp", new NtrpError("license_invalid", lic2.message, 3 /* Auth */));
39458
+ emitError(cmd || "ntrp", new NtrpError("license_invalid", lic.message, 3 /* Auth */));
39355
39459
  }
39356
39460
  console.error(chalk89.red(`
39357
- ${lic2.message}`));
39461
+ ${lic.message}`));
39358
39462
  console.error(chalk89.dim(" Trial's over \u2014 /upgrade and paste your key.\n"));
39359
39463
  process.exit(1);
39360
39464
  }
@@ -39400,31 +39504,37 @@ async function main() {
39400
39504
  }
39401
39505
  return DB_COMMANDS.has(cmd);
39402
39506
  }
39403
- const showedActivation = await ensureLicenseActivated(ctx);
39404
- const lic = await refreshLicenseOnline();
39405
- if (lic.shouldNudgeUpgrade) {
39406
- const { printTrialNudge: printTrialNudge2 } = await Promise.resolve().then(() => (init_upgrade(), upgrade_exports));
39407
- printTrialNudge2(lic);
39507
+ const { consumeJustUpdatedEnv: consumeJustUpdatedEnv2 } = await Promise.resolve().then(() => (init_relaunch(), relaunch_exports));
39508
+ const justUpdated = consumeJustUpdatedEnv2();
39509
+ let showedActivation = false;
39510
+ if (!justUpdated) {
39511
+ showedActivation = await ensureLicenseActivated(ctx);
39512
+ const lic = await refreshLicenseOnline();
39513
+ if (lic.shouldNudgeUpgrade) {
39514
+ const { printTrialNudge: printTrialNudge2 } = await Promise.resolve().then(() => (init_upgrade(), upgrade_exports));
39515
+ printTrialNudge2(lic);
39516
+ }
39408
39517
  }
39409
39518
  void Promise.resolve().then(() => (init_discovery(), discovery_exports)).then((m) => m.refreshStaleProviderCaches()).catch(() => void 0);
39410
39519
  const { setActiveDbPath: setActiveDbPath2 } = await Promise.resolve().then(() => (init_connection(), connection_exports));
39411
39520
  const { datasetPathForSession: datasetPathForSession2 } = await Promise.resolve().then(() => (init_context2(), context_exports));
39412
39521
  ctx.datasetPath = datasetPathForSession2(ctx.sessionId);
39413
39522
  await setActiveDbPath2(ctx.datasetPath);
39414
- const { completeInteractiveSetup: completeInteractiveSetup2 } = await Promise.resolve().then(() => (init_first_run(), first_run_exports));
39415
39523
  const {
39416
39524
  hydrateUpdateAvailableFromCache: hydrateUpdateAvailableFromCache2,
39417
39525
  startBackgroundUpdateCheck: startBackgroundUpdateCheck2
39418
39526
  } = await Promise.resolve().then(() => (init_registry(), registry_exports));
39419
- const { consumeJustUpdatedEnv: consumeJustUpdatedEnv2 } = await Promise.resolve().then(() => (init_relaunch(), relaunch_exports));
39420
39527
  ctx.updateAvailable = hydrateUpdateAvailableFromCache2(VERSION);
39421
39528
  startBackgroundUpdateCheck2(ctx);
39422
- await completeInteractiveSetup2(ctx, { skipBrand: showedActivation });
39529
+ if (!justUpdated) {
39530
+ const { completeInteractiveSetup: completeInteractiveSetup2 } = await Promise.resolve().then(() => (init_first_run(), first_run_exports));
39531
+ await completeInteractiveSetup2(ctx, { skipBrand: showedActivation });
39532
+ }
39423
39533
  const { startSessionTranscript: startSessionTranscript2, stopSessionTranscript: stopSessionTranscript2 } = await Promise.resolve().then(() => (init_transcript(), transcript_exports));
39424
39534
  startSessionTranscript2(ctx);
39425
39535
  const { printWelcome: printWelcome2 } = await Promise.resolve().then(() => (init_welcome(), welcome_exports));
39426
- const justUpdated = consumeJustUpdatedEnv2();
39427
39536
  if (justUpdated) {
39537
+ clearTerminalHome();
39428
39538
  console.log();
39429
39539
  console.log(" " + paint("accent", "\u2713") + " " + chalk89.dim(`Now running v${justUpdated.to}`));
39430
39540
  }