@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
@@ -685,13 +685,21 @@ var init_theme = __esm({
685
685
  });
686
686
 
687
687
  // src/services/handoff-skill.ts
688
- import { mkdirSync as mkdirSync3 } from "fs";
688
+ import { existsSync as existsSync2, mkdirSync as mkdirSync3 } from "fs";
689
689
  import { basename, join as join2 } from "path";
690
690
  import { homedir as homedir3 } from "os";
691
691
  import chalk2 from "chalk";
692
- function defaultAiInboxDir() {
692
+ function legacyAiInboxDir() {
693
693
  return join2(homedir3(), "Documents", "Claude", "ntrp-inbox");
694
694
  }
695
+ function defaultAiInboxDirDisplay() {
696
+ return "~/Documents/ntrp-inbox";
697
+ }
698
+ function defaultAiInboxDir() {
699
+ const legacy = legacyAiInboxDir();
700
+ if (existsSync2(legacy)) return legacy;
701
+ return join2(homedir3(), "Documents", "ntrp-inbox");
702
+ }
695
703
  function handoffLocations() {
696
704
  const archiveRoot = getExportsDir();
697
705
  const archiveLatestDir = join2(archiveRoot, "latest");
@@ -784,7 +792,7 @@ function filenamePattern(ctx) {
784
792
  return `${datePrefix}*${ext}`;
785
793
  }
786
794
  function buildStandingSkillMarkdown(loc = handoffLocations()) {
787
- const inboxBlock = loc.inboxDir ? `Inbox (preferred \u2014 point Claude Desktop / a project / Cursor at this folder):
795
+ const inboxBlock = loc.inboxDir ? `Inbox (preferred \u2014 point Claude, ChatGPT, Cursor, or another desktop AI at this folder):
788
796
  \`${loc.inboxDir}\`
789
797
 
790
798
  Start with:
@@ -907,7 +915,9 @@ function maybePrintAiInboxNudge() {
907
915
  if (getConfigValue("ai-inbox-nudge-seen") === "true") return;
908
916
  setConfigValue("ai-inbox-nudge-seen", "true");
909
917
  console.log(
910
- " " + chalk2.dim("Set a pickup folder. Type /inbox set ~/Documents/Claude/ntrp-inbox then /inbox skill")
918
+ " " + chalk2.dim(
919
+ `Set a pickup folder. Type /inbox set ${defaultAiInboxDirDisplay()} then /inbox skill`
920
+ )
911
921
  );
912
922
  }
913
923
  function printStandingSkill() {
@@ -916,7 +926,9 @@ function printStandingSkill() {
916
926
  console.log();
917
927
  console.log(" " + bold("NTRP handoff finder skill"));
918
928
  console.log(
919
- " " + chalk2.dim("Paste this skill once into Claude, ChatGPT, or Cursor. Later handoffs do not print it again.")
929
+ " " + chalk2.dim(
930
+ "Paste this skill once into your desktop AI (Claude, ChatGPT, Cursor, \u2026). Later handoffs do not print it again."
931
+ )
920
932
  );
921
933
  printPlainBlock(buildStandingSkillMarkdown(loc));
922
934
  console.log(" " + chalk2.dim("Written: ") + (loc.inboxSkill ?? loc.archiveSkill));
@@ -944,7 +956,7 @@ import {
944
956
  appendFileSync,
945
957
  copyFileSync,
946
958
  cpSync,
947
- existsSync as existsSync2,
959
+ existsSync as existsSync3,
948
960
  mkdirSync as mkdirSync4,
949
961
  readFileSync as readFileSync2,
950
962
  readdirSync,
@@ -962,13 +974,13 @@ function ensureExportsLayout(root = getExportsDir()) {
962
974
  mkdirSync4(join3(root, sub), { recursive: true });
963
975
  }
964
976
  const readme = join3(root, "README.md");
965
- if (!existsSync2(readme)) {
977
+ if (!existsSync3(readme)) {
966
978
  writeFileSync3(readme, ARCHIVE_README, "utf-8");
967
979
  }
968
- if (!existsSync2(join3(root, "INDEX.md"))) {
980
+ if (!existsSync3(join3(root, "INDEX.md"))) {
969
981
  writeFileSync3(join3(root, "INDEX.md"), "# NTRP exports\n\n_No exports yet._\n", "utf-8");
970
982
  }
971
- if (!existsSync2(join3(root, "manifest.jsonl"))) {
983
+ if (!existsSync3(join3(root, "manifest.jsonl"))) {
972
984
  writeFileSync3(join3(root, "manifest.jsonl"), "", "utf-8");
973
985
  }
974
986
  return root;
@@ -990,7 +1002,7 @@ function ensureInboxLayout(inbox) {
990
1002
  mkdirSync4(join3(inbox, "archive"), { recursive: true });
991
1003
  const readme = join3(inbox, "README.md");
992
1004
  writeFileSync3(readme, buildInboxReadme(), "utf-8");
993
- if (!existsSync2(join3(inbox, "INDEX.md"))) {
1005
+ if (!existsSync3(join3(inbox, "INDEX.md"))) {
994
1006
  writeFileSync3(join3(inbox, "INDEX.md"), "# NTRP AI inbox\n\n_No exports synced yet._\n", "utf-8");
995
1007
  }
996
1008
  }
@@ -999,7 +1011,7 @@ function manifestPath(root = getExportsDir()) {
999
1011
  }
1000
1012
  function readManifestEvents(root = getExportsDir()) {
1001
1013
  const path = manifestPath(root);
1002
- if (!existsSync2(path)) return [];
1014
+ if (!existsSync3(path)) return [];
1003
1015
  const text = readFileSync2(path, "utf-8");
1004
1016
  const events = [];
1005
1017
  for (const line of text.split("\n")) {
@@ -1033,7 +1045,7 @@ function listExports(opts = {}) {
1033
1045
  }
1034
1046
  function copyPath(src, dest) {
1035
1047
  mkdirSync4(dirname2(dest), { recursive: true });
1036
- if (existsSync2(dest)) {
1048
+ if (existsSync3(dest)) {
1037
1049
  rmSync(dest, { recursive: true, force: true });
1038
1050
  }
1039
1051
  const st = statSync(src);
@@ -1053,7 +1065,7 @@ function regenerateIndex(root = getExportsDir()) {
1053
1065
  const withHistory = items.filter((e) => (e.previous_paths?.length ?? 0) > 0);
1054
1066
  const latestDir = join3(root, "latest");
1055
1067
  const latestLines = [];
1056
- if (existsSync2(latestDir)) {
1068
+ if (existsSync3(latestDir)) {
1057
1069
  for (const name of readdirSync(latestDir).sort()) {
1058
1070
  latestLines.push(`- \`latest/${name}\` \u2192 \`${join3(latestDir, name)}\``);
1059
1071
  }
@@ -1063,7 +1075,7 @@ function regenerateIndex(root = getExportsDir()) {
1063
1075
  "",
1064
1076
  `Archive root: \`${root}\``,
1065
1077
  "",
1066
- "Set an inbox with `/onboard` or `/inbox set`. Paste `SKILL.md` once into Claude. Later handoffs overwrite `latest-handoff.md`.",
1078
+ "Set an inbox with `/onboard` or `/inbox set`. Paste `SKILL.md` once into your desktop AI. Later handoffs overwrite `latest-handoff.md`.",
1067
1079
  "",
1068
1080
  "## Latest pointers",
1069
1081
  ""
@@ -1113,18 +1125,18 @@ function regenerateInboxIndex(inbox) {
1113
1125
  const lines = [
1114
1126
  "# NTRP AI inbox",
1115
1127
  "",
1116
- "Start here. Install `SKILL.md` once in Claude. Newest prompt: `latest-handoff.md`. Catalog: `INDEX.md`.",
1128
+ "Start here. Install `SKILL.md` once in your desktop AI. Newest prompt: `latest-handoff.md`. Catalog: `INDEX.md`.",
1117
1129
  "",
1118
1130
  `Canonical archive: \`${archiveRoot}\` (see \`${join3(archiveRoot, "INDEX.md")}\`).`,
1119
1131
  "",
1120
1132
  "## Latest pointers",
1121
1133
  ""
1122
1134
  ];
1123
- if (existsSync2(join3(inbox, "SKILL.md"))) {
1135
+ if (existsSync3(join3(inbox, "SKILL.md"))) {
1124
1136
  lines.push("- [`SKILL.md`](./SKILL.md) \u2014 standing finder. Paste once into your agent.");
1125
1137
  }
1126
1138
  const latestNames = readdirSync(inbox).filter((n) => n.startsWith("latest-")).sort();
1127
- if (latestNames.length === 0 && !existsSync2(join3(inbox, "SKILL.md"))) {
1139
+ if (latestNames.length === 0 && !existsSync3(join3(inbox, "SKILL.md"))) {
1128
1140
  lines.push("_None yet. Run a handoff after `/inbox set`._");
1129
1141
  } else {
1130
1142
  for (const name of latestNames) {
@@ -1152,7 +1164,7 @@ This folder is the landing folder for NTRP handoffs. Desktop AI tools read files
1152
1164
 
1153
1165
  ## Start here
1154
1166
 
1155
- 1. Install \`SKILL.md\` once in Claude, ChatGPT, or Cursor. Then tell the agent to open the latest NTRP handoff.
1167
+ 1. Install \`SKILL.md\` once in Claude, ChatGPT, Cursor, or another desktop AI. Then tell the agent to open the latest NTRP handoff.
1156
1168
  2. The newest prompt is \`latest-handoff.md\` (or \`latest-handoff-deck.md\` and similar).
1157
1169
  3. \`latest-pickup.md\` names the file that was just written.
1158
1170
 
@@ -1170,7 +1182,7 @@ Set the folder with \`/inbox set <path>\`. Print the skill with \`/inbox skill\`
1170
1182
  `;
1171
1183
  }
1172
1184
  function pruneInboxArchive(archiveDir, keep = INBOX_ARCHIVE_KEEP) {
1173
- if (!existsSync2(archiveDir)) return;
1185
+ if (!existsSync3(archiveDir)) return;
1174
1186
  const entries2 = readdirSync(archiveDir).map((name) => {
1175
1187
  const p = join3(archiveDir, name);
1176
1188
  try {
@@ -1186,7 +1198,7 @@ function pruneInboxArchive(archiveDir, keep = INBOX_ARCHIVE_KEEP) {
1186
1198
  function syncAiInbox(entry) {
1187
1199
  const inbox = getAiInboxDir();
1188
1200
  if (!inbox) return null;
1189
- if (!existsSync2(entry.path)) return null;
1201
+ if (!existsSync3(entry.path)) return null;
1190
1202
  ensureInboxLayout(inbox);
1191
1203
  const archiveDir = join3(inbox, "archive");
1192
1204
  mkdirSync4(archiveDir, { recursive: true });
@@ -1210,7 +1222,7 @@ function syncRecentToInbox(limit = 10) {
1210
1222
  const items = listExports({ limit });
1211
1223
  let n = 0;
1212
1224
  for (const item of items) {
1213
- if (!existsSync2(item.path)) continue;
1225
+ if (!existsSync3(item.path)) continue;
1214
1226
  const inboxPath = syncAiInbox(item);
1215
1227
  if (inboxPath) {
1216
1228
  appendManifestEvent({
@@ -1224,7 +1236,7 @@ function syncRecentToInbox(limit = 10) {
1224
1236
  }
1225
1237
  regenerateInboxIndex(inbox);
1226
1238
  regenerateIndex();
1227
- const newest = items.find((e) => existsSync2(e.path));
1239
+ const newest = items.find((e) => existsSync3(e.path));
1228
1240
  if (newest) persistHandoffSkillFiles(newest);
1229
1241
  return n;
1230
1242
  }
@@ -1259,10 +1271,10 @@ This archive stores handoffs, reports, notes, CSV receipts, and publish packages
1259
1271
  Set a dedicated inbox for desktop AI instead of this folder:
1260
1272
 
1261
1273
  \`\`\`
1262
- /inbox set ~/Documents/Claude/ntrp-inbox
1274
+ /inbox set ~/Documents/ntrp-inbox
1263
1275
  \`\`\`
1264
1276
 
1265
- 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.
1277
+ 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.
1266
1278
  `;
1267
1279
  }
1268
1280
  });
@@ -1481,12 +1493,12 @@ var init_context_doc = __esm({
1481
1493
  });
1482
1494
 
1483
1495
  // src/services/transcript.ts
1484
- import { existsSync as existsSync3, readFileSync as readFileSync3, writeFileSync as writeFileSync5, rmSync as rmSync2 } from "fs";
1496
+ import { existsSync as existsSync4, readFileSync as readFileSync3, writeFileSync as writeFileSync5, rmSync as rmSync2 } from "fs";
1485
1497
  import { join as join4 } from "path";
1486
1498
  function rebindSessionTranscript(ctx) {
1487
1499
  if (!state || state.sessionId === ctx.sessionId) return;
1488
1500
  const priorJson = join4(getSessionsDir(), `${state.sessionId}.json`);
1489
- if (existsSync3(priorJson)) {
1501
+ if (existsSync4(priorJson)) {
1490
1502
  finalizeCurrentFile("switched session");
1491
1503
  } else {
1492
1504
  discardSessionTranscript(state.sessionId);
@@ -1507,7 +1519,7 @@ function discardSessionTranscript(sessionId) {
1507
1519
  function createState(sessionId) {
1508
1520
  const filePath = transcriptPathForSession(sessionId);
1509
1521
  let base = "";
1510
- if (existsSync3(filePath)) {
1522
+ if (existsSync4(filePath)) {
1511
1523
  try {
1512
1524
  base = readFileSync3(filePath, "utf-8").trimEnd() + "\n";
1513
1525
  } catch {
@@ -1608,12 +1620,12 @@ __export(connection_exports, {
1608
1620
  run: () => run,
1609
1621
  setActiveDbPath: () => setActiveDbPath
1610
1622
  });
1611
- import { mkdirSync as mkdirSync5, existsSync as existsSync4, rmSync as rmSync3 } from "fs";
1623
+ import { mkdirSync as mkdirSync5, existsSync as existsSync5, rmSync as rmSync3 } from "fs";
1612
1624
  import { dirname as dirname3, join as join5, resolve as resolve4 } from "path";
1613
1625
  function ensureDir2() {
1614
1626
  secureNtrpHome();
1615
1627
  const dir = dirname3(activeDbPath);
1616
- if (!existsSync4(dir)) {
1628
+ if (!existsSync5(dir)) {
1617
1629
  mkdirSync5(dir, { recursive: true, mode: 448 });
1618
1630
  }
1619
1631
  }
@@ -2881,14 +2893,14 @@ CREATE INDEX IF NOT EXISTS idx_health_readings_batch ON health_readings(upload_b
2881
2893
 
2882
2894
  // src/config/install.ts
2883
2895
  import { randomUUID as randomUUID3 } from "crypto";
2884
- import { existsSync as existsSync5, mkdirSync as mkdirSync6, readFileSync as readFileSync4, unlinkSync, writeFileSync as writeFileSync6 } from "fs";
2896
+ import { existsSync as existsSync6, mkdirSync as mkdirSync6, readFileSync as readFileSync4, unlinkSync, writeFileSync as writeFileSync6 } from "fs";
2885
2897
  import { join as join6 } from "path";
2886
2898
  function installPath() {
2887
2899
  return join6(ntrpHome(), "install.json");
2888
2900
  }
2889
2901
  function ensureDir3() {
2890
2902
  const dir = ntrpHome();
2891
- if (!existsSync5(dir)) {
2903
+ if (!existsSync6(dir)) {
2892
2904
  mkdirSync6(dir, { recursive: true });
2893
2905
  }
2894
2906
  }
@@ -2900,7 +2912,7 @@ function isValidInstall(value) {
2900
2912
  function ensureInstall() {
2901
2913
  if (cachedInstall) return cachedInstall;
2902
2914
  const path = installPath();
2903
- if (existsSync5(path)) {
2915
+ if (existsSync6(path)) {
2904
2916
  try {
2905
2917
  const parsed = JSON.parse(readFileSync4(path, "utf-8"));
2906
2918
  if (isValidInstall(parsed)) {
@@ -2933,7 +2945,7 @@ var init_install = __esm({
2933
2945
  });
2934
2946
 
2935
2947
  // src/config/progress-migrate.ts
2936
- import { existsSync as existsSync6, readFileSync as readFileSync5, renameSync as renameSync2, writeFileSync as writeFileSync7 } from "fs";
2948
+ import { existsSync as existsSync7, readFileSync as readFileSync5, renameSync as renameSync2, writeFileSync as writeFileSync7 } from "fs";
2937
2949
  import { join as join7 } from "path";
2938
2950
  function legacyStatePath() {
2939
2951
  return join7(ntrpHome(), "state.json");
@@ -2950,9 +2962,9 @@ function isValidLegacyState(value) {
2950
2962
  return s.schema_version === 1 && typeof s.total_minutes_saved === "number" && Array.isArray(s.credits) && Array.isArray(s.milestones_unlocked);
2951
2963
  }
2952
2964
  function migrateLegacyStateIfNeeded(installId) {
2953
- if (existsSync6(progressPath())) return null;
2965
+ if (existsSync7(progressPath())) return null;
2954
2966
  const legacyPath = legacyStatePath();
2955
- if (!existsSync6(legacyPath)) return null;
2967
+ if (!existsSync7(legacyPath)) return null;
2956
2968
  try {
2957
2969
  const parsed = JSON.parse(readFileSync5(legacyPath, "utf-8"));
2958
2970
  if (!isValidLegacyState(parsed)) return null;
@@ -3069,14 +3081,14 @@ var init_usage_backfill = __esm({
3069
3081
  });
3070
3082
 
3071
3083
  // src/config/progress.ts
3072
- import { existsSync as existsSync7, mkdirSync as mkdirSync7, readFileSync as readFileSync6, unlinkSync as unlinkSync2, writeFileSync as writeFileSync8 } from "fs";
3084
+ import { existsSync as existsSync8, mkdirSync as mkdirSync7, readFileSync as readFileSync6, unlinkSync as unlinkSync2, writeFileSync as writeFileSync8 } from "fs";
3073
3085
  import { join as join8 } from "path";
3074
3086
  function progressPath2() {
3075
3087
  return join8(ntrpHome(), "progress.json");
3076
3088
  }
3077
3089
  function ensureDir4() {
3078
3090
  const dir = ntrpHome();
3079
- if (!existsSync7(dir)) {
3091
+ if (!existsSync8(dir)) {
3080
3092
  mkdirSync7(dir, { recursive: true });
3081
3093
  }
3082
3094
  }
@@ -3107,7 +3119,7 @@ function reconcileInstallId(state2) {
3107
3119
  }
3108
3120
  function readProgressFile() {
3109
3121
  const path = progressPath2();
3110
- if (!existsSync7(path)) return { state: null, changed: false };
3122
+ if (!existsSync8(path)) return { state: null, changed: false };
3111
3123
  try {
3112
3124
  const parsed = JSON.parse(readFileSync6(path, "utf-8"));
3113
3125
  if (!isValidProgress(parsed)) return { state: null, changed: false };
@@ -3828,7 +3840,7 @@ var init_time_bank = __esm({
3828
3840
  });
3829
3841
 
3830
3842
  // src/ai/llm/providers.ts
3831
- import { existsSync as existsSync8, readFileSync as readFileSync7 } from "fs";
3843
+ import { existsSync as existsSync9, readFileSync as readFileSync7 } from "fs";
3832
3844
  import { join as join9 } from "path";
3833
3845
  function providersPath() {
3834
3846
  return join9(ntrpHome(), "providers.json");
@@ -3836,7 +3848,7 @@ function providersPath() {
3836
3848
  function loadCustomProviders() {
3837
3849
  if (cachedEntries) return cachedEntries;
3838
3850
  const path = providersPath();
3839
- if (!existsSync8(path)) {
3851
+ if (!existsSync9(path)) {
3840
3852
  cachedEntries = [];
3841
3853
  return cachedEntries;
3842
3854
  }
@@ -4602,7 +4614,7 @@ var init_openai_compat = __esm({
4602
4614
  });
4603
4615
 
4604
4616
  // src/ai/llm/models-cache.ts
4605
- import { existsSync as existsSync9, readFileSync as readFileSync8, writeFileSync as writeFileSync9 } from "fs";
4617
+ import { existsSync as existsSync10, readFileSync as readFileSync8, writeFileSync as writeFileSync9 } from "fs";
4606
4618
  import { join as join10 } from "path";
4607
4619
  function cachePath() {
4608
4620
  return join10(ntrpHome(), "models.json");
@@ -4610,7 +4622,7 @@ function cachePath() {
4610
4622
  function loadFile() {
4611
4623
  if (cached) return cached;
4612
4624
  const path = cachePath();
4613
- if (!existsSync9(path)) {
4625
+ if (!existsSync10(path)) {
4614
4626
  cached = { version: 1, providers: {} };
4615
4627
  return cached;
4616
4628
  }
@@ -5228,7 +5240,7 @@ __export(pseudonymize_exports, {
5228
5240
  wipePrivacyStore: () => wipePrivacyStore
5229
5241
  });
5230
5242
  import { createHmac, randomBytes } from "crypto";
5231
- import { existsSync as existsSync10, mkdirSync as mkdirSync8, readFileSync as readFileSync10, rmSync as rmSync4, chmodSync as chmodSync2 } from "fs";
5243
+ import { existsSync as existsSync11, mkdirSync as mkdirSync8, readFileSync as readFileSync10, rmSync as rmSync4, chmodSync as chmodSync2 } from "fs";
5232
5244
  import { join as join11 } from "path";
5233
5245
  function privacyDir() {
5234
5246
  return join11(ntrpHome(), "privacy");
@@ -5247,7 +5259,7 @@ function chmodQuiet2(path, mode) {
5247
5259
  }
5248
5260
  function ensurePrivacyDir() {
5249
5261
  const dir = privacyDir();
5250
- if (!existsSync10(dir)) {
5262
+ if (!existsSync11(dir)) {
5251
5263
  mkdirSync8(dir, { recursive: true, mode: 448 });
5252
5264
  }
5253
5265
  chmodQuiet2(dir, 448);
@@ -5284,7 +5296,7 @@ function rebuildIndexes() {
5284
5296
  function loadSecret() {
5285
5297
  ensurePrivacyDir();
5286
5298
  const path = secretPath();
5287
- if (existsSync10(path)) {
5299
+ if (existsSync11(path)) {
5288
5300
  const raw = readFileSync10(path);
5289
5301
  const buf = raw.length >= 32 ? raw.subarray(0, 32) : Buffer.from(raw.toString("utf-8").trim(), "hex");
5290
5302
  if (buf.length >= 16) return buf.length === 32 ? buf : Buffer.concat([buf], 32);
@@ -5298,7 +5310,7 @@ function loadLexicon() {
5298
5310
  ensurePrivacyDir();
5299
5311
  secret = loadSecret();
5300
5312
  entries = [];
5301
- if (existsSync10(lexiconPath())) {
5313
+ if (existsSync11(lexiconPath())) {
5302
5314
  try {
5303
5315
  const parsed = JSON.parse(readFileSync10(lexiconPath(), "utf-8"));
5304
5316
  if (parsed?.version === 1 && Array.isArray(parsed.entries)) {
@@ -5590,7 +5602,7 @@ function wipePrivacyStore() {
5590
5602
  loaded = false;
5591
5603
  dirty = false;
5592
5604
  const dir = privacyDir();
5593
- if (existsSync10(dir)) {
5605
+ if (existsSync11(dir)) {
5594
5606
  rmSync4(dir, { recursive: true, force: true });
5595
5607
  }
5596
5608
  }
@@ -5730,20 +5742,34 @@ var init_pseudonymize = __esm({
5730
5742
  });
5731
5743
 
5732
5744
  // src/config/profile.ts
5733
- import { readFileSync as readFileSync11, writeFileSync as writeFileSync10, existsSync as existsSync11, mkdirSync as mkdirSync9 } from "fs";
5745
+ var profile_exports = {};
5746
+ __export(profile_exports, {
5747
+ isProfileConfigured: () => isProfileConfigured,
5748
+ loadProfile: () => loadProfile,
5749
+ profileExists: () => profileExists,
5750
+ profilePath: () => profilePath,
5751
+ saveProfile: () => saveProfile,
5752
+ updateProfile: () => updateProfile
5753
+ });
5754
+ import { readFileSync as readFileSync11, writeFileSync as writeFileSync10, existsSync as existsSync12, mkdirSync as mkdirSync9 } from "fs";
5734
5755
  import { join as join12 } from "path";
5756
+ function ensureDir5() {
5757
+ if (!existsSync12(NTRP_DIR3)) {
5758
+ mkdirSync9(NTRP_DIR3, { recursive: true });
5759
+ }
5760
+ }
5735
5761
  function profilePath() {
5736
5762
  return PROFILE_PATH;
5737
5763
  }
5738
5764
  function profileExists() {
5739
- return existsSync11(PROFILE_PATH);
5765
+ return existsSync12(PROFILE_PATH);
5740
5766
  }
5741
5767
  function isProfileConfigured(profile = loadProfile()) {
5742
5768
  if (!profile) return false;
5743
5769
  return profile.company_name.trim().length > 0;
5744
5770
  }
5745
5771
  function loadProfile() {
5746
- if (!existsSync11(PROFILE_PATH)) return null;
5772
+ if (!existsSync12(PROFILE_PATH)) return null;
5747
5773
  try {
5748
5774
  const parsed = JSON.parse(readFileSync11(PROFILE_PATH, "utf-8"));
5749
5775
  if (!parsed || typeof parsed !== "object") return null;
@@ -5752,6 +5778,35 @@ function loadProfile() {
5752
5778
  return null;
5753
5779
  }
5754
5780
  }
5781
+ function saveProfile(profile) {
5782
+ ensureDir5();
5783
+ const now2 = (/* @__PURE__ */ new Date()).toISOString();
5784
+ const toWrite = {
5785
+ ...profile,
5786
+ schema_version: 1,
5787
+ created_at: profile.created_at || now2,
5788
+ updated_at: now2
5789
+ };
5790
+ writeFileSync10(PROFILE_PATH, JSON.stringify(toWrite, null, 2) + "\n");
5791
+ }
5792
+ function updateProfile(patch) {
5793
+ const existing = loadProfile();
5794
+ const now2 = (/* @__PURE__ */ new Date()).toISOString();
5795
+ const merged = {
5796
+ schema_version: 1,
5797
+ company_name: "",
5798
+ industry: "",
5799
+ product_description: "",
5800
+ target_customer: "",
5801
+ sales_motion: "mid_market",
5802
+ created_at: now2,
5803
+ updated_at: now2,
5804
+ ...existing ?? {},
5805
+ ...patch
5806
+ };
5807
+ saveProfile(merged);
5808
+ return merged;
5809
+ }
5755
5810
  var NTRP_DIR3, PROFILE_PATH;
5756
5811
  var init_profile = __esm({
5757
5812
  "src/config/profile.ts"() {
@@ -5770,7 +5825,7 @@ __export(lexicon_seed_exports, {
5770
5825
  seedOperatorIdentity: () => seedOperatorIdentity,
5771
5826
  wipePrivacyStore: () => wipePrivacyStore2
5772
5827
  });
5773
- import { existsSync as existsSync12 } from "fs";
5828
+ import { existsSync as existsSync13 } from "fs";
5774
5829
  function invalidateLexiconSeed() {
5775
5830
  dbSeedGeneration = -1;
5776
5831
  }
@@ -5832,7 +5887,7 @@ function seedOperatorIdentity(name, url) {
5832
5887
  }
5833
5888
  async function seedFromDuckDB() {
5834
5889
  const dbPath = getActiveDbPath();
5835
- if (!existsSync12(dbPath)) return;
5890
+ if (!existsSync13(dbPath)) return;
5836
5891
  const { all: all2, getConnectionGeneration: getConnectionGeneration2 } = await Promise.resolve().then(() => (init_connection(), connection_exports));
5837
5892
  const gen = getConnectionGeneration2();
5838
5893
  if (dbSeedGeneration !== -1 && gen === dbSeedGeneration) return;
@@ -6511,7 +6566,7 @@ var init_errors2 = __esm({
6511
6566
 
6512
6567
  // src/strategies/readers.ts
6513
6568
  import { createHash } from "crypto";
6514
- import { existsSync as existsSync13, readFileSync as readFileSync12 } from "fs";
6569
+ import { existsSync as existsSync14, readFileSync as readFileSync12 } from "fs";
6515
6570
  import { extname, resolve as resolve5 } from "path";
6516
6571
  import { parse as parseYaml } from "yaml";
6517
6572
  import { PDFParse } from "pdf-parse";
@@ -6521,7 +6576,7 @@ async function readStrategyFile(pathOrDash) {
6521
6576
  return createDocument("stdin", null, text2, {});
6522
6577
  }
6523
6578
  const sourcePath = resolve5(pathOrDash);
6524
- if (!existsSync13(sourcePath)) {
6579
+ if (!existsSync14(sourcePath)) {
6525
6580
  throw new NtrpError("strategy_file_not_found", `Strategy file not found: ${pathOrDash}`, 2 /* Usage */);
6526
6581
  }
6527
6582
  const ext = extname(sourcePath).toLowerCase();
@@ -6590,7 +6645,7 @@ var init_readers = __esm({
6590
6645
  });
6591
6646
 
6592
6647
  // src/memory/knowledge.ts
6593
- import { existsSync as existsSync14, readFileSync as readFileSync13, appendFileSync as appendFileSync2, readdirSync as readdirSync2 } from "fs";
6648
+ import { existsSync as existsSync15, readFileSync as readFileSync13, appendFileSync as appendFileSync2, readdirSync as readdirSync2 } from "fs";
6594
6649
  import { join as join13 } from "path";
6595
6650
  import { randomUUID as randomUUID4 } from "crypto";
6596
6651
  function knowledgePath() {
@@ -6598,7 +6653,7 @@ function knowledgePath() {
6598
6653
  }
6599
6654
  function loadKnowledgeChunks() {
6600
6655
  const path = knowledgePath();
6601
- if (!existsSync14(path)) return [];
6656
+ if (!existsSync15(path)) return [];
6602
6657
  const out = [];
6603
6658
  for (const line of readFileSync13(path, "utf-8").split("\n")) {
6604
6659
  const trimmed = line.trim();
@@ -6621,7 +6676,7 @@ var init_knowledge = __esm({
6621
6676
  });
6622
6677
 
6623
6678
  // src/ai/privacy.ts
6624
- import { existsSync as existsSync15, mkdirSync as mkdirSync10, appendFileSync as appendFileSync3 } from "fs";
6679
+ import { existsSync as existsSync16, mkdirSync as mkdirSync10, appendFileSync as appendFileSync3 } from "fs";
6625
6680
  import { homedir as homedir4 } from "os";
6626
6681
  import { join as join14 } from "path";
6627
6682
  function scrubSensitiveText(text) {
@@ -6642,7 +6697,7 @@ function stripPII(obj) {
6642
6697
  return out;
6643
6698
  }
6644
6699
  function ensureAuditDir() {
6645
- if (!existsSync15(AUDIT_DIR)) {
6700
+ if (!existsSync16(AUDIT_DIR)) {
6646
6701
  mkdirSync10(AUDIT_DIR, { recursive: true });
6647
6702
  }
6648
6703
  }
@@ -6758,14 +6813,14 @@ __export(playbook_exports, {
6758
6813
  matchTriggeredPlays: () => matchTriggeredPlays,
6759
6814
  withKnownRecommendedPlays: () => withKnownRecommendedPlays
6760
6815
  });
6761
- import { existsSync as existsSync16, readFileSync as readFileSync14, appendFileSync as appendFileSync4 } from "fs";
6816
+ import { existsSync as existsSync17, readFileSync as readFileSync14, appendFileSync as appendFileSync4 } from "fs";
6762
6817
  import { join as join15 } from "path";
6763
6818
  function playsPath() {
6764
6819
  return join15(getMemoryDir(), PLAYS_FILE);
6765
6820
  }
6766
6821
  function getCustomPlays() {
6767
6822
  const path = playsPath();
6768
- if (!existsSync16(path)) return [];
6823
+ if (!existsSync17(path)) return [];
6769
6824
  const out = [];
6770
6825
  for (const line of readFileSync14(path, "utf-8").split("\n")) {
6771
6826
  const trimmed = line.trim();
@@ -7653,7 +7708,7 @@ __export(store_exports2, {
7653
7708
  rewriteJsonl: () => rewriteJsonl,
7654
7709
  scrubText: () => scrubText
7655
7710
  });
7656
- import { existsSync as existsSync17, readFileSync as readFileSync15, appendFileSync as appendFileSync5, readdirSync as readdirSync4, writeFileSync as writeFileSync12 } from "fs";
7711
+ import { existsSync as existsSync18, readFileSync as readFileSync15, appendFileSync as appendFileSync5, readdirSync as readdirSync4, writeFileSync as writeFileSync12 } from "fs";
7657
7712
  import { join as join18 } from "path";
7658
7713
  import { randomUUID as randomUUID5 } from "crypto";
7659
7714
  function memPath(file) {
@@ -7661,7 +7716,7 @@ function memPath(file) {
7661
7716
  }
7662
7717
  function readJsonl(file) {
7663
7718
  const path = memPath(file);
7664
- if (!existsSync17(path)) return [];
7719
+ if (!existsSync18(path)) return [];
7665
7720
  const out = [];
7666
7721
  for (const line of readFileSync15(path, "utf-8").split("\n")) {
7667
7722
  const trimmed = line.trim();
@@ -8040,7 +8095,7 @@ __export(context_exports, {
8040
8095
  transcriptPathForSession: () => transcriptPathForSession
8041
8096
  });
8042
8097
  import { basename as basename4, join as join19, resolve as resolve7, sep as sep4 } from "path";
8043
- import { existsSync as existsSync18, mkdirSync as mkdirSync11, writeFileSync as writeFileSync13, readFileSync as readFileSync16, readdirSync as readdirSync5, statSync as statSync3, rmSync as rmSync5 } from "fs";
8098
+ import { existsSync as existsSync19, mkdirSync as mkdirSync11, writeFileSync as writeFileSync13, readFileSync as readFileSync16, readdirSync as readdirSync5, statSync as statSync3, rmSync as rmSync5 } from "fs";
8044
8099
  import { homedir as homedir6 } from "os";
8045
8100
  import { randomUUID as randomUUID6 } from "crypto";
8046
8101
  function isSessionStale(s) {
@@ -8059,14 +8114,14 @@ function ntrpHomeDir() {
8059
8114
  }
8060
8115
  function getSessionsDir() {
8061
8116
  const dir = join19(ntrpHomeDir(), "sessions");
8062
- if (!existsSync18(dir)) {
8117
+ if (!existsSync19(dir)) {
8063
8118
  mkdirSync11(dir, { recursive: true });
8064
8119
  }
8065
8120
  return dir;
8066
8121
  }
8067
8122
  function getDatasetsDir() {
8068
8123
  const dir = join19(ntrpHomeDir(), "datasets");
8069
- if (!existsSync18(dir)) {
8124
+ if (!existsSync19(dir)) {
8070
8125
  mkdirSync11(dir, { recursive: true });
8071
8126
  }
8072
8127
  return dir;
@@ -8586,6 +8641,15 @@ var init_context2 = __esm({
8586
8641
  });
8587
8642
 
8588
8643
  // src/pipeline/segments.ts
8644
+ function resolveSegmentByName(query, segments) {
8645
+ const lower = query.toLowerCase();
8646
+ const exact = segments.find((s) => s.segment.name.toLowerCase() === lower);
8647
+ if (exact) return { type: "exact", segment: exact };
8648
+ const matches = segments.filter((s) => s.segment.name.toLowerCase().includes(lower));
8649
+ if (matches.length === 1) return { type: "exact", segment: matches[0] };
8650
+ if (matches.length > 1) return { type: "ambiguous", matches };
8651
+ return { type: "none" };
8652
+ }
8589
8653
  function resolveSegmentScopeFromSnapshot(segment, snapshot) {
8590
8654
  const orgIds = [];
8591
8655
  const peopleIds = [];
@@ -8933,8 +8997,9 @@ function resolveThresholds(salesMotion, computedBaselines) {
8933
8997
  return resolved;
8934
8998
  }
8935
8999
  async function getResolvedThresholds() {
9000
+ const { loadProfile: loadProfile2 } = await Promise.resolve().then(() => (init_profile(), profile_exports));
8936
9001
  const { getConfigValue: getConfigValue2 } = await Promise.resolve().then(() => (init_store(), store_exports));
8937
- const motion = getConfigValue2("sales-motion");
9002
+ const motion = loadProfile2()?.sales_motion ?? getConfigValue2("sales-motion");
8938
9003
  return resolveThresholds(motion ?? null, {});
8939
9004
  }
8940
9005
  var init_resolve = __esm({
@@ -9659,7 +9724,7 @@ __export(play_outcomes_exports, {
9659
9724
  listPlayOutcomes: () => listPlayOutcomes,
9660
9725
  recordPlayOutcomes: () => recordPlayOutcomes
9661
9726
  });
9662
- import { existsSync as existsSync19, readFileSync as readFileSync17, appendFileSync as appendFileSync6 } from "fs";
9727
+ import { existsSync as existsSync20, readFileSync as readFileSync17, appendFileSync as appendFileSync6 } from "fs";
9663
9728
  import { join as join20 } from "path";
9664
9729
  import { randomUUID as randomUUID7 } from "crypto";
9665
9730
  function outcomesPath() {
@@ -9667,7 +9732,7 @@ function outcomesPath() {
9667
9732
  }
9668
9733
  function listPlayOutcomes() {
9669
9734
  const path = outcomesPath();
9670
- if (!existsSync19(path)) return [];
9735
+ if (!existsSync20(path)) return [];
9671
9736
  const out = [];
9672
9737
  for (const line of readFileSync17(path, "utf-8").split("\n")) {
9673
9738
  const trimmed = line.trim();
@@ -9888,12 +9953,12 @@ args: [show|set <path>|skill|clear]
9888
9953
  handler: ../commands/exports.ts
9889
9954
  ---
9890
9955
 
9891
- Set a folder that Claude Desktop or any desktop AI can read.
9956
+ Set a folder that any desktop AI can read (Claude, ChatGPT, Cursor, \u2026).
9892
9957
  NTRP copies each handoff to that folder.
9893
9958
  NTRP overwrites \`latest-handoff.md\` and \`latest-handoff-deck.md\`. The app then finds the newest file.
9894
9959
  NTRP also writes \`SKILL.md\`. That file holds finder instructions with your paths.
9895
9960
  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.
9896
- Paste that skill once into Claude, ChatGPT, or Cursor. Later handoffs need no new paste.
9961
+ Paste that skill once into Claude, ChatGPT, Cursor, or another desktop AI. Later handoffs need no new paste.
9897
9962
  \`INDEX.md\` in that folder links to the archive.
9898
9963
  A path outside ~/.ntrp needs a confirm. Handoffs carry analysis text.
9899
9964
  \`clear\` does not delete files. It only removes the config pointer.`
@@ -10244,9 +10309,9 @@ args: [<metric>|guide|list|tour]
10244
10309
  handler: ../commands/deepdive.ts
10245
10310
  ---
10246
10311
 
10247
- CLI slide deck: SaaS refresher, five vital signs (definition, formula, visual, dollar translation), then a how-to section (talk, ask, ship to Claude, loop).
10248
- Bare \`/deepdive\` runs the full onboarding tour. Type \`/deepdive guide\` to jump to how to use NTRP (includes teaching Claude the inbox once).
10249
- Type \`/deepdive handoff\` for the ship-to-Claude slide. Type \`/deepdive <metric>\` to jump to one metric card.
10312
+ 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).
10313
+ Bare \`/deepdive\` runs the full onboarding tour. Type \`/deepdive guide\` to jump to how to use NTRP (includes teaching the inbox once).
10314
+ Type \`/deepdive handoff\` for the ship-to-AI slide. Type \`/deepdive <metric>\` to jump to one metric card.
10250
10315
  Type \`/deepdive list\` to print the catalog. Works without an AI key. Re-run anytime from the homescreen.
10251
10316
  Live values overlay when an analysis exists.`
10252
10317
  },
@@ -10450,6 +10515,7 @@ Manage CLI configuration stored at \`~/.ntrp/config.json\`. Useful keys:
10450
10515
  \`api-key\` (Anthropic), \`openai-api-key\` (and \`groq-api-key\`, \`google-api-key\`, ...),
10451
10516
  \`llm-primary\` (default provider), \`llm-tier\`, \`llm-auto-failover\`,
10452
10517
  \`voice-personality\`, \`voice-roast\`,
10518
+ \`sales-motion\`, \`rep_hourly_cost\`, \`hours_per_activity\`,
10453
10519
  \`default-format\`, \`export-dir\`, \`ai-inbox-dir\` (or type \`/inbox set\`).
10454
10520
 
10455
10521
  Setting a provider key opens a hidden prompt and auto-discovers that provider's models.
@@ -10567,7 +10633,7 @@ Remaining nuances merge into a custom_context paragraph that flows into all AI s
10567
10633
  });
10568
10634
 
10569
10635
  // src/ai/prompt-parts.ts
10570
- import { existsSync as existsSync20, readFileSync as readFileSync18 } from "fs";
10636
+ import { existsSync as existsSync21, readFileSync as readFileSync18 } from "fs";
10571
10637
  import { join as join21 } from "path";
10572
10638
  function buildCompanyProfileBlock() {
10573
10639
  const p = loadProfile();
@@ -10588,7 +10654,7 @@ function buildCompanyProfileBlock() {
10588
10654
  function loadAnalystFile() {
10589
10655
  const path = join21(ntrpHome(), ANALYST_FILE_NAME);
10590
10656
  try {
10591
- if (!existsSync20(path)) return null;
10657
+ if (!existsSync21(path)) return null;
10592
10658
  const raw = sanitizeExternalText(readFileSync18(path, "utf-8").trim());
10593
10659
  if (!raw) return null;
10594
10660
  if (raw.length <= ANALYST_FILE_MAX_CHARS) return raw;
@@ -11110,7 +11176,7 @@ function buildUserMessage(input) {
11110
11176
  async function* streamFindings(input, ctx) {
11111
11177
  assertReplAi(ctx);
11112
11178
  const userMessage = buildUserMessage(input);
11113
- let meta = { model_used: "unknown", provider_used: "anthropic", failover: false };
11179
+ let meta = { model_used: "unknown", provider_used: "unknown", failover: false };
11114
11180
  const scanner = new StreamingJsonArrayParser();
11115
11181
  let streamed = 0;
11116
11182
  for await (const event of streamWithFailover(
@@ -14683,7 +14749,7 @@ var init_markdown = __esm({
14683
14749
  import chalk11 from "chalk";
14684
14750
  function formatLlmAttribution(meta) {
14685
14751
  if (!meta.model_used) return null;
14686
- const provider = meta.provider_used ?? "anthropic";
14752
+ const provider = meta.provider_used ?? "unknown";
14687
14753
  let line = `via ${formatModelLabel(provider, meta.model_used)}`;
14688
14754
  if (meta.failover) {
14689
14755
  line += " (auto-failover)";
@@ -18015,7 +18081,7 @@ async function* strategistPlanSession(options) {
18015
18081
  })
18016
18082
  }
18017
18083
  ];
18018
- let lastMeta = { provider_used: "anthropic", model_used: "unknown" };
18084
+ let lastMeta = { provider_used: "unknown", model_used: "unknown" };
18019
18085
  const loopGuard = new ToolLoopGuard();
18020
18086
  const allowedTools = new Set(tools2.map((t) => t.name));
18021
18087
  const callLlm = async (surface, withTools, maxTokens = STAGE_MAX_TOKENS) => {
@@ -23276,16 +23342,16 @@ var init_scenario_fit = __esm({
23276
23342
  });
23277
23343
 
23278
23344
  // src/demo/taxonomy-cache.ts
23279
- import { readFileSync as readFileSync19, writeFileSync as writeFileSync15, existsSync as existsSync21, mkdirSync as mkdirSync13, unlinkSync as unlinkSync3 } from "fs";
23345
+ import { readFileSync as readFileSync19, writeFileSync as writeFileSync15, existsSync as existsSync22, mkdirSync as mkdirSync13, unlinkSync as unlinkSync3 } from "fs";
23280
23346
  import { homedir as homedir7 } from "os";
23281
23347
  import { join as join24 } from "path";
23282
- function ensureDir5() {
23283
- if (!existsSync21(NTRP_DIR4)) {
23348
+ function ensureDir6() {
23349
+ if (!existsSync22(NTRP_DIR4)) {
23284
23350
  mkdirSync13(NTRP_DIR4, { recursive: true });
23285
23351
  }
23286
23352
  }
23287
23353
  function loadCachedTaxonomy(profile) {
23288
- if (!existsSync21(TAXONOMY_PATH)) return null;
23354
+ if (!existsSync22(TAXONOMY_PATH)) return null;
23289
23355
  try {
23290
23356
  const parsed = JSON.parse(readFileSync19(TAXONOMY_PATH, "utf-8"));
23291
23357
  if (!parsed || typeof parsed !== "object") return null;
@@ -23296,7 +23362,7 @@ function loadCachedTaxonomy(profile) {
23296
23362
  }
23297
23363
  }
23298
23364
  function saveCachedTaxonomy(taxonomy) {
23299
- ensureDir5();
23365
+ ensureDir6();
23300
23366
  writeFileSync15(TAXONOMY_PATH, JSON.stringify(taxonomy, null, 2) + "\n");
23301
23367
  }
23302
23368
  var NTRP_DIR4, TAXONOMY_PATH;
@@ -23762,7 +23828,7 @@ __export(inbox_setup_exports, {
23762
23828
  shouldOfferInboxSkillSetup: () => shouldOfferInboxSkillSetup
23763
23829
  });
23764
23830
  import chalk27 from "chalk";
23765
- import { existsSync as existsSync22 } from "fs";
23831
+ import { existsSync as existsSync23 } from "fs";
23766
23832
  function markDemoOffered() {
23767
23833
  setConfigValue("ai-inbox-nudge-seen", "true");
23768
23834
  }
@@ -23779,7 +23845,7 @@ function shouldOfferInboxSkillSetup(beat) {
23779
23845
  return !hasProductionOffered();
23780
23846
  }
23781
23847
  function printSkipHint(beat) {
23782
- const setCmd = paint("accent", "/inbox set ~/Documents/Claude/ntrp-inbox");
23848
+ const setCmd = paint("accent", `/inbox set ${defaultAiInboxDirDisplay()}`);
23783
23849
  const skillCmd = paint("accent", "/inbox skill");
23784
23850
  if (beat === "demo") {
23785
23851
  console.log(
@@ -23791,13 +23857,15 @@ function printSkipHint(beat) {
23791
23857
  " " + chalk27.dim("Skipped. NTRP will not ask again. Type ") + setCmd + chalk27.dim(" then ") + skillCmd + chalk27.dim(" at any time.")
23792
23858
  );
23793
23859
  }
23794
- async function reuseInboxFolderIfPresent(session, beat, folderPath = defaultAiInboxDir()) {
23860
+ async function reuseInboxFolderIfPresent(session, beat, folderPath) {
23795
23861
  if (getAiInboxDir()) return false;
23796
- if (!existsSync22(folderPath)) return false;
23797
- console.log(" " + chalk27.dim("Pickup folder still on disk: ") + folderPath);
23862
+ const candidates = folderPath ? [folderPath] : [.../* @__PURE__ */ new Set([defaultAiInboxDir(), legacyAiInboxDir()])];
23863
+ const existing = candidates.find((p) => existsSync23(p));
23864
+ if (!existing) return false;
23865
+ console.log(" " + chalk27.dim("Pickup folder still on disk: ") + existing);
23798
23866
  const reuse = await session.confirm("Reuse this pickup folder?", true);
23799
23867
  if (!reuse) return false;
23800
- const resolved = setAiInboxDir(folderPath);
23868
+ const resolved = setAiInboxDir(existing);
23801
23869
  markDemoOffered();
23802
23870
  if (beat === "production") markProductionOffered();
23803
23871
  console.log();
@@ -23812,7 +23880,7 @@ async function offerInboxSkillSetup(session, opts = {}) {
23812
23880
  const beat = opts.beat ?? "production";
23813
23881
  if (!shouldOfferInboxSkillSetup(beat)) return;
23814
23882
  console.log();
23815
- console.log(" " + bold("Teach Claude where handoffs live"));
23883
+ console.log(" " + bold("Teach your AI where handoffs live"));
23816
23884
  console.log(
23817
23885
  " " + chalk27.dim(
23818
23886
  "Optional. NTRP copies every handoff into one folder. You paste instructions once; later /handoff just writes the file."
@@ -23823,7 +23891,10 @@ async function offerInboxSkillSetup(session, opts = {}) {
23823
23891
  }
23824
23892
  console.log();
23825
23893
  if (await reuseInboxFolderIfPresent(session, beat)) return;
23826
- const want = await session.confirm("Set a pickup folder for Claude, ChatGPT, or Cursor?", true);
23894
+ const want = await session.confirm(
23895
+ "Set a pickup folder for Claude, ChatGPT, Cursor, or another desktop AI?",
23896
+ true
23897
+ );
23827
23898
  if (!want) {
23828
23899
  if (beat === "demo") markDemoOffered();
23829
23900
  else markProductionOffered();
@@ -23855,10 +23926,12 @@ async function offerInboxSkillSetup(session, opts = {}) {
23855
23926
  console.log(" " + chalk27.dim(`Synced ${n} recent export${n === 1 ? "" : "s"}.`));
23856
23927
  }
23857
23928
  console.log(
23858
- " " + chalk27.dim("Paste this skill into Claude once. Later, tell Claude to open the latest NTRP handoff.")
23929
+ " " + chalk27.dim(
23930
+ "Paste this skill into your desktop AI once. Later, tell it to open the latest NTRP handoff."
23931
+ )
23859
23932
  );
23860
23933
  printStandingSkill();
23861
- await session.askPressEnter("Paste the skill into Claude. Then continue");
23934
+ await session.askPressEnter("Paste the skill into your AI. Then continue");
23862
23935
  console.log(" " + chalk27.dim("Done. Later handoffs write to that folder. The skill is not printed again."));
23863
23936
  console.log();
23864
23937
  }
@@ -23893,7 +23966,7 @@ __export(ingest_exports, {
23893
23966
  handler: () => handler3
23894
23967
  });
23895
23968
  import chalk28 from "chalk";
23896
- import { readFileSync as readFileSync20, existsSync as existsSync23 } from "fs";
23969
+ import { readFileSync as readFileSync20, existsSync as existsSync24 } from "fs";
23897
23970
  import { basename as basename6 } from "path";
23898
23971
  async function handler3(args, ctx) {
23899
23972
  const { positional, flags } = parseArgs(args, [
@@ -23917,7 +23990,7 @@ async function handler3(args, ctx) {
23917
23990
  console.error(chalk28.dim(" /ingest --demo [--scenario <name>]"));
23918
23991
  process.exit(1);
23919
23992
  }
23920
- if (!existsSync23(file)) {
23993
+ if (!existsSync24(file)) {
23921
23994
  console.error(chalk28.red(` File not found: ${file}`));
23922
23995
  process.exit(1);
23923
23996
  }
@@ -24303,7 +24376,7 @@ __export(ingest_chat_exports, {
24303
24376
  loadDemoFromChat: () => loadDemoFromChat,
24304
24377
  looksLikeFilePath: () => looksLikeFilePath
24305
24378
  });
24306
- import { existsSync as existsSync24 } from "fs";
24379
+ import { existsSync as existsSync25 } from "fs";
24307
24380
  import { basename as basename7, resolve as resolve9 } from "path";
24308
24381
  import { homedir as homedir8 } from "os";
24309
24382
  import chalk30 from "chalk";
@@ -24323,11 +24396,11 @@ function extractFilePath(input) {
24323
24396
  const m = trimmed.match(re);
24324
24397
  if (m?.[1]) {
24325
24398
  const p = expandPath(m[1]);
24326
- if (existsSync24(p)) return p;
24399
+ if (existsSync25(p)) return p;
24327
24400
  }
24328
24401
  if (!m?.[1] && re.test(trimmed) && trimmed.toLowerCase().endsWith(".csv")) {
24329
24402
  const p = expandPath(trimmed.replace(/^["']|["']$/g, ""));
24330
- if (existsSync24(p)) return p;
24403
+ if (existsSync25(p)) return p;
24331
24404
  }
24332
24405
  }
24333
24406
  return null;
@@ -25246,9 +25319,9 @@ async function handleGetSessionBrief(input) {
25246
25319
  if (!target) {
25247
25320
  return { error: `No session matching "${raw}".` };
25248
25321
  }
25249
- const { existsSync: existsSync27, readFileSync: readFileSync23 } = await import("fs");
25322
+ const { existsSync: existsSync28, readFileSync: readFileSync23 } = await import("fs");
25250
25323
  const briefPath = contextDocPathForSession2(target.id);
25251
- if (!existsSync27(briefPath)) {
25324
+ if (!existsSync28(briefPath)) {
25252
25325
  return {
25253
25326
  session_id: target.id,
25254
25327
  error: "No context brief on disk for this session (created before brief storage existed).",
@@ -25819,7 +25892,7 @@ async function* agenticFindings(computeResult, divergences, options) {
25819
25892
  const priorMessages = priorRaw.length > 0 && typeof priorRaw[0] === "object" && priorRaw[0] !== null && "content" in priorRaw[0] ? normalizeThread(priorRaw) : priorRaw;
25820
25893
  const conversational = mode === "fresh" || mode === "think";
25821
25894
  const messages = conversational && priorMessages.length > 0 && options.userQuestion ? [...priorMessages, { role: "user", content: options.userQuestion }] : [{ role: "user", content: initialContext }];
25822
- let lastMeta = { provider_used: "anthropic", model_used: "unknown" };
25895
+ let lastMeta = { provider_used: "unknown", model_used: "unknown" };
25823
25896
  const loopGuard = new ToolLoopGuard();
25824
25897
  const allowedTools = new Set(tools2.map((t) => t.name));
25825
25898
  async function callLlm(msgs, tokens, withTools) {
@@ -26077,22 +26150,19 @@ async function loadReportData(segmentName) {
26077
26150
  let { health, segments, findings, entityCounts } = diagnosis;
26078
26151
  let scopedSegment = null;
26079
26152
  if (segmentName) {
26080
- const lower = segmentName.toLowerCase();
26081
- let match = segments.find((s) => s.segment.name.toLowerCase() === lower);
26082
- if (!match) {
26083
- const subs = segments.filter((s) => s.segment.name.toLowerCase().includes(lower));
26084
- if (subs.length === 1) match = subs[0];
26085
- else if (subs.length > 1) {
26086
- throw new NtrpError("ambiguous_segment", `"${segmentName}" matches multiple segments.`, 2 /* Usage */, {
26087
- matches: subs.map((s) => s.segment.name)
26088
- });
26089
- }
26153
+ const resolved = resolveSegmentByName(segmentName, segments);
26154
+ if (resolved.type === "ambiguous") {
26155
+ throw new NtrpError("ambiguous_segment", `"${segmentName}" matches multiple segments.`, 2 /* Usage */, {
26156
+ matches: resolved.matches.map((s) => s.segment.name)
26157
+ });
26090
26158
  }
26091
- if (!match) {
26159
+ if (resolved.type === "none") {
26092
26160
  throw new NtrpError("segment_not_found", `No segment matching "${segmentName}".`, 2 /* Usage */, {
26093
26161
  available_segments: segments.map((s) => s.segment.name)
26094
26162
  });
26095
26163
  }
26164
+ const match = resolved.segment;
26165
+ const lower = segmentName.toLowerCase();
26096
26166
  scopedSegment = match;
26097
26167
  health = match.result;
26098
26168
  segments = [match];
@@ -26118,6 +26188,7 @@ var init_report = __esm({
26118
26188
  init_errors2();
26119
26189
  init_types2();
26120
26190
  init_session_analysis();
26191
+ init_segments();
26121
26192
  }
26122
26193
  });
26123
26194
 
@@ -26220,7 +26291,7 @@ var init_ask = __esm({
26220
26291
  });
26221
26292
 
26222
26293
  // src/services/setup.ts
26223
- import { existsSync as existsSync25, mkdirSync as mkdirSync14, readFileSync as readFileSync21, writeFileSync as writeFileSync16 } from "fs";
26294
+ import { existsSync as existsSync26, mkdirSync as mkdirSync14, readFileSync as readFileSync21, writeFileSync as writeFileSync16 } from "fs";
26224
26295
  import { join as join25 } from "path";
26225
26296
  function setupCheck() {
26226
26297
  const home = ntrpHome();
@@ -26279,11 +26350,11 @@ var init_setup = __esm({
26279
26350
  });
26280
26351
 
26281
26352
  // src/version.ts
26282
- import { existsSync as existsSync26, readFileSync as readFileSync22 } from "fs";
26353
+ import { existsSync as existsSync27, readFileSync as readFileSync22 } from "fs";
26283
26354
  import { dirname as dirname5, join as join26 } from "path";
26284
26355
  import { fileURLToPath } from "url";
26285
26356
  function readVersionFromPackageJson(packageJsonPath) {
26286
- if (!existsSync26(packageJsonPath)) return null;
26357
+ if (!existsSync27(packageJsonPath)) return null;
26287
26358
  try {
26288
26359
  const pkg = JSON.parse(readFileSync22(packageJsonPath, "utf-8"));
26289
26360
  if (typeof pkg.version === "string" && pkg.version.length > 0) return pkg.version;