@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.
- package/dist/index.js +461 -225
- package/dist/mcp/server.js +186 -115
- 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
|
|
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
|
|
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(
|
|
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(
|
|
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
|
|
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 (!
|
|
1180
|
+
if (!existsSync3(readme)) {
|
|
1169
1181
|
writeFileSync3(readme, ARCHIVE_README, "utf-8");
|
|
1170
1182
|
}
|
|
1171
|
-
if (!
|
|
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 (!
|
|
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 (!
|
|
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 (!
|
|
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 (
|
|
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 (
|
|
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
|
|
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
|
|
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 (
|
|
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 && !
|
|
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
|
|
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 (!
|
|
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 (!
|
|
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 (!
|
|
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) =>
|
|
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 (!
|
|
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 (!
|
|
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 (
|
|
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
|
|
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
|
|
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/
|
|
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
|
|
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
|
|
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 (
|
|
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 (
|
|
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
|
|
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 (!
|
|
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
|
|
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 (!
|
|
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 (
|
|
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
|
|
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 (
|
|
3372
|
+
if (existsSync7(progressPath())) return null;
|
|
3361
3373
|
const legacyPath = legacyStatePath();
|
|
3362
|
-
if (!
|
|
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
|
|
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 (!
|
|
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 (!
|
|
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 (
|
|
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
|
|
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 (!
|
|
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
|
|
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 (!
|
|
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
|
|
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 (!
|
|
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 (
|
|
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 (
|
|
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 (
|
|
6142
|
+
if (existsSync11(dir)) {
|
|
6131
6143
|
rmSync4(dir, { recursive: true, force: true });
|
|
6132
6144
|
}
|
|
6133
6145
|
}
|
|
@@ -6267,10 +6279,19 @@ var init_pseudonymize = __esm({
|
|
|
6267
6279
|
});
|
|
6268
6280
|
|
|
6269
6281
|
// src/config/profile.ts
|
|
6270
|
-
|
|
6282
|
+
var profile_exports = {};
|
|
6283
|
+
__export(profile_exports, {
|
|
6284
|
+
isProfileConfigured: () => isProfileConfigured,
|
|
6285
|
+
loadProfile: () => loadProfile,
|
|
6286
|
+
profileExists: () => profileExists,
|
|
6287
|
+
profilePath: () => profilePath,
|
|
6288
|
+
saveProfile: () => saveProfile,
|
|
6289
|
+
updateProfile: () => updateProfile
|
|
6290
|
+
});
|
|
6291
|
+
import { readFileSync as readFileSync11, writeFileSync as writeFileSync10, existsSync as existsSync12, mkdirSync as mkdirSync9 } from "fs";
|
|
6271
6292
|
import { join as join12 } from "path";
|
|
6272
6293
|
function ensureDir5() {
|
|
6273
|
-
if (!
|
|
6294
|
+
if (!existsSync12(NTRP_DIR3)) {
|
|
6274
6295
|
mkdirSync9(NTRP_DIR3, { recursive: true });
|
|
6275
6296
|
}
|
|
6276
6297
|
}
|
|
@@ -6278,14 +6299,14 @@ function profilePath() {
|
|
|
6278
6299
|
return PROFILE_PATH;
|
|
6279
6300
|
}
|
|
6280
6301
|
function profileExists() {
|
|
6281
|
-
return
|
|
6302
|
+
return existsSync12(PROFILE_PATH);
|
|
6282
6303
|
}
|
|
6283
6304
|
function isProfileConfigured(profile = loadProfile()) {
|
|
6284
6305
|
if (!profile) return false;
|
|
6285
6306
|
return profile.company_name.trim().length > 0;
|
|
6286
6307
|
}
|
|
6287
6308
|
function loadProfile() {
|
|
6288
|
-
if (!
|
|
6309
|
+
if (!existsSync12(PROFILE_PATH)) return null;
|
|
6289
6310
|
try {
|
|
6290
6311
|
const parsed = JSON.parse(readFileSync11(PROFILE_PATH, "utf-8"));
|
|
6291
6312
|
if (!parsed || typeof parsed !== "object") return null;
|
|
@@ -6341,7 +6362,7 @@ __export(lexicon_seed_exports, {
|
|
|
6341
6362
|
seedOperatorIdentity: () => seedOperatorIdentity,
|
|
6342
6363
|
wipePrivacyStore: () => wipePrivacyStore2
|
|
6343
6364
|
});
|
|
6344
|
-
import { existsSync as
|
|
6365
|
+
import { existsSync as existsSync13 } from "fs";
|
|
6345
6366
|
function invalidateLexiconSeed() {
|
|
6346
6367
|
dbSeedGeneration = -1;
|
|
6347
6368
|
}
|
|
@@ -6403,7 +6424,7 @@ function seedOperatorIdentity(name, url) {
|
|
|
6403
6424
|
}
|
|
6404
6425
|
async function seedFromDuckDB() {
|
|
6405
6426
|
const dbPath = getActiveDbPath();
|
|
6406
|
-
if (!
|
|
6427
|
+
if (!existsSync13(dbPath)) return;
|
|
6407
6428
|
const { all: all2, getConnectionGeneration: getConnectionGeneration2 } = await Promise.resolve().then(() => (init_connection(), connection_exports));
|
|
6408
6429
|
const gen = getConnectionGeneration2();
|
|
6409
6430
|
if (dbSeedGeneration !== -1 && gen === dbSeedGeneration) return;
|
|
@@ -6816,7 +6837,7 @@ async function llmCompleteText(surface, system, userMessage, max_tokens, ctx) {
|
|
|
6816
6837
|
}
|
|
6817
6838
|
async function llmStreamText(surface, system, userMessage, max_tokens, ctx) {
|
|
6818
6839
|
let fullText = "";
|
|
6819
|
-
let meta = { model_used: "unknown", provider_used: "
|
|
6840
|
+
let meta = { model_used: "unknown", provider_used: "unknown", failover: false };
|
|
6820
6841
|
for await (const event of streamWithFailover(
|
|
6821
6842
|
{
|
|
6822
6843
|
surface,
|
|
@@ -7120,7 +7141,7 @@ var init_errors2 = __esm({
|
|
|
7120
7141
|
|
|
7121
7142
|
// src/strategies/readers.ts
|
|
7122
7143
|
import { createHash } from "crypto";
|
|
7123
|
-
import { existsSync as
|
|
7144
|
+
import { existsSync as existsSync14, readFileSync as readFileSync12 } from "fs";
|
|
7124
7145
|
import { extname, resolve as resolve5 } from "path";
|
|
7125
7146
|
import { parse as parseYaml } from "yaml";
|
|
7126
7147
|
import { PDFParse } from "pdf-parse";
|
|
@@ -7130,7 +7151,7 @@ async function readStrategyFile(pathOrDash) {
|
|
|
7130
7151
|
return createDocument("stdin", null, text2, {});
|
|
7131
7152
|
}
|
|
7132
7153
|
const sourcePath = resolve5(pathOrDash);
|
|
7133
|
-
if (!
|
|
7154
|
+
if (!existsSync14(sourcePath)) {
|
|
7134
7155
|
throw new NtrpError("strategy_file_not_found", `Strategy file not found: ${pathOrDash}`, 2 /* Usage */);
|
|
7135
7156
|
}
|
|
7136
7157
|
const ext = extname(sourcePath).toLowerCase();
|
|
@@ -7199,7 +7220,7 @@ var init_readers = __esm({
|
|
|
7199
7220
|
});
|
|
7200
7221
|
|
|
7201
7222
|
// src/memory/knowledge.ts
|
|
7202
|
-
import { existsSync as
|
|
7223
|
+
import { existsSync as existsSync15, readFileSync as readFileSync13, appendFileSync as appendFileSync2, readdirSync as readdirSync2 } from "fs";
|
|
7203
7224
|
import { join as join13 } from "path";
|
|
7204
7225
|
import { randomUUID as randomUUID4 } from "crypto";
|
|
7205
7226
|
function knowledgePath() {
|
|
@@ -7207,7 +7228,7 @@ function knowledgePath() {
|
|
|
7207
7228
|
}
|
|
7208
7229
|
function loadKnowledgeChunks() {
|
|
7209
7230
|
const path = knowledgePath();
|
|
7210
|
-
if (!
|
|
7231
|
+
if (!existsSync15(path)) return [];
|
|
7211
7232
|
const out = [];
|
|
7212
7233
|
for (const line of readFileSync13(path, "utf-8").split("\n")) {
|
|
7213
7234
|
const trimmed = line.trim();
|
|
@@ -7298,7 +7319,7 @@ var init_knowledge = __esm({
|
|
|
7298
7319
|
});
|
|
7299
7320
|
|
|
7300
7321
|
// src/ai/privacy.ts
|
|
7301
|
-
import { existsSync as
|
|
7322
|
+
import { existsSync as existsSync16, mkdirSync as mkdirSync10, appendFileSync as appendFileSync3 } from "fs";
|
|
7302
7323
|
import { homedir as homedir4 } from "os";
|
|
7303
7324
|
import { join as join14 } from "path";
|
|
7304
7325
|
function scrubSensitiveText(text) {
|
|
@@ -7319,7 +7340,7 @@ function stripPII(obj) {
|
|
|
7319
7340
|
return out;
|
|
7320
7341
|
}
|
|
7321
7342
|
function ensureAuditDir() {
|
|
7322
|
-
if (!
|
|
7343
|
+
if (!existsSync16(AUDIT_DIR)) {
|
|
7323
7344
|
mkdirSync10(AUDIT_DIR, { recursive: true });
|
|
7324
7345
|
}
|
|
7325
7346
|
}
|
|
@@ -7435,14 +7456,14 @@ __export(playbook_exports, {
|
|
|
7435
7456
|
matchTriggeredPlays: () => matchTriggeredPlays,
|
|
7436
7457
|
withKnownRecommendedPlays: () => withKnownRecommendedPlays
|
|
7437
7458
|
});
|
|
7438
|
-
import { existsSync as
|
|
7459
|
+
import { existsSync as existsSync17, readFileSync as readFileSync14, appendFileSync as appendFileSync4 } from "fs";
|
|
7439
7460
|
import { join as join15 } from "path";
|
|
7440
7461
|
function playsPath() {
|
|
7441
7462
|
return join15(getMemoryDir(), PLAYS_FILE);
|
|
7442
7463
|
}
|
|
7443
7464
|
function getCustomPlays() {
|
|
7444
7465
|
const path = playsPath();
|
|
7445
|
-
if (!
|
|
7466
|
+
if (!existsSync17(path)) return [];
|
|
7446
7467
|
const out = [];
|
|
7447
7468
|
for (const line of readFileSync14(path, "utf-8").split("\n")) {
|
|
7448
7469
|
const trimmed = line.trim();
|
|
@@ -8330,7 +8351,7 @@ __export(store_exports2, {
|
|
|
8330
8351
|
rewriteJsonl: () => rewriteJsonl,
|
|
8331
8352
|
scrubText: () => scrubText
|
|
8332
8353
|
});
|
|
8333
|
-
import { existsSync as
|
|
8354
|
+
import { existsSync as existsSync18, readFileSync as readFileSync15, appendFileSync as appendFileSync5, readdirSync as readdirSync4, writeFileSync as writeFileSync12 } from "fs";
|
|
8334
8355
|
import { join as join18 } from "path";
|
|
8335
8356
|
import { randomUUID as randomUUID5 } from "crypto";
|
|
8336
8357
|
function memPath(file) {
|
|
@@ -8338,7 +8359,7 @@ function memPath(file) {
|
|
|
8338
8359
|
}
|
|
8339
8360
|
function readJsonl(file) {
|
|
8340
8361
|
const path = memPath(file);
|
|
8341
|
-
if (!
|
|
8362
|
+
if (!existsSync18(path)) return [];
|
|
8342
8363
|
const out = [];
|
|
8343
8364
|
for (const line of readFileSync15(path, "utf-8").split("\n")) {
|
|
8344
8365
|
const trimmed = line.trim();
|
|
@@ -8717,7 +8738,7 @@ __export(context_exports, {
|
|
|
8717
8738
|
transcriptPathForSession: () => transcriptPathForSession
|
|
8718
8739
|
});
|
|
8719
8740
|
import { basename as basename5, join as join19, resolve as resolve7, sep as sep4 } from "path";
|
|
8720
|
-
import { existsSync as
|
|
8741
|
+
import { existsSync as existsSync19, mkdirSync as mkdirSync11, writeFileSync as writeFileSync13, readFileSync as readFileSync16, readdirSync as readdirSync5, statSync as statSync3, rmSync as rmSync5 } from "fs";
|
|
8721
8742
|
import { homedir as homedir6 } from "os";
|
|
8722
8743
|
import { randomUUID as randomUUID6 } from "crypto";
|
|
8723
8744
|
function isSessionStale(s) {
|
|
@@ -8736,14 +8757,14 @@ function ntrpHomeDir() {
|
|
|
8736
8757
|
}
|
|
8737
8758
|
function getSessionsDir() {
|
|
8738
8759
|
const dir = join19(ntrpHomeDir(), "sessions");
|
|
8739
|
-
if (!
|
|
8760
|
+
if (!existsSync19(dir)) {
|
|
8740
8761
|
mkdirSync11(dir, { recursive: true });
|
|
8741
8762
|
}
|
|
8742
8763
|
return dir;
|
|
8743
8764
|
}
|
|
8744
8765
|
function getDatasetsDir() {
|
|
8745
8766
|
const dir = join19(ntrpHomeDir(), "datasets");
|
|
8746
|
-
if (!
|
|
8767
|
+
if (!existsSync19(dir)) {
|
|
8747
8768
|
mkdirSync11(dir, { recursive: true });
|
|
8748
8769
|
}
|
|
8749
8770
|
return dir;
|
|
@@ -9754,7 +9775,7 @@ var init_admin_confirm = __esm({
|
|
|
9754
9775
|
});
|
|
9755
9776
|
|
|
9756
9777
|
// src/services/scratch-wipe.ts
|
|
9757
|
-
import { existsSync as
|
|
9778
|
+
import { existsSync as existsSync20, rmSync as rmSync6, unlinkSync as unlinkSync3 } from "fs";
|
|
9758
9779
|
import { join as join20 } from "path";
|
|
9759
9780
|
async function performScratchWipe(opts = {}) {
|
|
9760
9781
|
const home = ntrpHome();
|
|
@@ -9784,7 +9805,7 @@ async function performScratchWipe(opts = {}) {
|
|
|
9784
9805
|
);
|
|
9785
9806
|
}
|
|
9786
9807
|
for (const { path, kind } of targets) {
|
|
9787
|
-
if (!
|
|
9808
|
+
if (!existsSync20(path)) continue;
|
|
9788
9809
|
try {
|
|
9789
9810
|
if (kind === "dir") {
|
|
9790
9811
|
rmSync6(path, { recursive: true, force: true });
|
|
@@ -12336,11 +12357,11 @@ var init_guide_slides = __esm({
|
|
|
12336
12357
|
};
|
|
12337
12358
|
HANDOFF = {
|
|
12338
12359
|
id: "handoff",
|
|
12339
|
-
label: "Ship work to
|
|
12340
|
-
tagline: "Teach the inbox once. Later, tell
|
|
12360
|
+
label: "Ship work to your AI",
|
|
12361
|
+
tagline: "Teach the inbox once. Later, tell your AI to pick it up.",
|
|
12341
12362
|
visual: {
|
|
12342
12363
|
kind: "layer_stack",
|
|
12343
|
-
caption: "You and
|
|
12364
|
+
caption: "You and your AI find and open the file. NTRP only writes.",
|
|
12344
12365
|
layers: [
|
|
12345
12366
|
{ label: "Set a pickup folder (demo, onboard, or /inbox set)" },
|
|
12346
12367
|
{ label: "Paste the finder skill once (/inbox skill)", highlight: true },
|
|
@@ -12349,18 +12370,18 @@ var init_guide_slides = __esm({
|
|
|
12349
12370
|
]
|
|
12350
12371
|
},
|
|
12351
12372
|
lines: [
|
|
12352
|
-
'Type "ship a board deck" or type /handoff. Files land in ~/Documents/
|
|
12373
|
+
'Type "ship a board deck" or type /handoff. Files land in ~/Documents/ntrp-inbox by default. Type /inbox set to change the folder.',
|
|
12353
12374
|
"",
|
|
12354
|
-
"During demo or company setup, or any time, type /inbox skill. Paste a standing finder into Claude, ChatGPT, or
|
|
12375
|
+
"During demo or company setup, or any time, type /inbox skill. Paste a standing finder into Claude, ChatGPT, Cursor, or another desktop AI once. That skill tells the tool to follow latest-handoff.md and INDEX.md.",
|
|
12355
12376
|
"",
|
|
12356
|
-
'After that, each /handoff prints "Handoff ready". You will not get a paste block every write. Tell
|
|
12377
|
+
'After that, each /handoff prints "Handoff ready". You will not get a paste block every write. Tell your AI: pick up the latest NTRP handoff.',
|
|
12357
12378
|
"",
|
|
12358
|
-
"If you skipped this step, NTRP asks once when you load your own data. Skip then, and type /inbox set ~/Documents/
|
|
12379
|
+
"If you skipped this step, NTRP asks once when you load your own data. Skip then, and type /inbox set ~/Documents/ntrp-inbox. Then type /inbox skill. Type /handoff skill to reprint the same finder."
|
|
12359
12380
|
],
|
|
12360
12381
|
deepdive: [
|
|
12361
12382
|
"Inbox copies: latest-handoff.md, latest-pickup.md, SKILL.md, INDEX.md. Dated files also live under ~/.ntrp/exports/.",
|
|
12362
12383
|
"Type /exports to list writes. Type /inbox show to print the folder and the latest pointer. Type /handoff --print to show the prompt body in the terminal.",
|
|
12363
|
-
"Audience-framed Metric definitions append to decks and reports.
|
|
12384
|
+
"Audience-framed Metric definitions append to decks and reports. Your AI then has the same glossary you walked.",
|
|
12364
12385
|
"Type /end to close a session. NTRP writes a transcript plus a 1-page context brief under ~/.ntrp/sessions/."
|
|
12365
12386
|
]
|
|
12366
12387
|
};
|
|
@@ -12556,7 +12577,7 @@ function paintIntro(opts) {
|
|
|
12556
12577
|
"",
|
|
12557
12578
|
"Vital signs (after) \u2014 Freshness, Flow Rate, Drop Rate, Signal:Noise, Thread Depth \u2014 NTRP's own ontology. Each has a dollar translation. Scores gate in layer order: first red wins.",
|
|
12558
12579
|
"",
|
|
12559
|
-
"How to use NTRP (after vitals) \u2014 talk in English, ask without a key, ship a handoff to
|
|
12580
|
+
"How to use NTRP (after vitals) \u2014 talk in English, ask without a key, ship a handoff to your AI once, stay in the diagnose \u2192 plan loop.",
|
|
12560
12581
|
"",
|
|
12561
12582
|
chalk10.dim("No AI key needed for this tour. Press Enter to advance; type /deepdive (or d) on any slide for more.")
|
|
12562
12583
|
],
|
|
@@ -12576,7 +12597,7 @@ function paintClose(opts) {
|
|
|
12576
12597
|
` ${paint("accent", "/diagnose")} \u2014 five vital signs + dollars at risk`,
|
|
12577
12598
|
` ${paint("accent", "/metrics")} \u2014 SaaS scorecard (ARR, NRR, coverage\u2026)`,
|
|
12578
12599
|
` ${paint("accent", "/deepdive")} \u2014 replay this tour \xB7 ${paint("accent", "/deepdive guide")} how-to only`,
|
|
12579
|
-
` ${paint("accent", "/handoff")} \u2014 ship a file; teach
|
|
12600
|
+
` ${paint("accent", "/handoff")} \u2014 ship a file; teach the inbox once (/inbox skill)`,
|
|
12580
12601
|
` ${paint("accent", "/strategy")} \u2014 measurable plan \xB7 ${paint("accent", "/help")} shortcuts`,
|
|
12581
12602
|
"",
|
|
12582
12603
|
"Fourteen more SaaS metrics live behind /deepdive list \u2014 including unit economics that unlock when spend data lands.",
|
|
@@ -12744,7 +12765,7 @@ async function offerFirstRunTour(ctx) {
|
|
|
12744
12765
|
console.log();
|
|
12745
12766
|
console.log(" " + bold("Onboarding tour") + chalk10.dim(" \u2014 ~3 minutes, no AI key required"));
|
|
12746
12767
|
console.log(
|
|
12747
|
-
" " + chalk10.dim("SaaS numbers, five vitals, then how to talk to NTRP and ship work to
|
|
12768
|
+
" " + chalk10.dim("SaaS numbers, five vitals, then how to talk to NTRP and ship work to your AI.")
|
|
12748
12769
|
);
|
|
12749
12770
|
const tourDefault = firstRunTourDefaultChoice();
|
|
12750
12771
|
if (tourDefault === "skip") {
|
|
@@ -13070,6 +13091,15 @@ var init_spinner = __esm({
|
|
|
13070
13091
|
});
|
|
13071
13092
|
|
|
13072
13093
|
// src/pipeline/segments.ts
|
|
13094
|
+
function resolveSegmentByName(query, segments) {
|
|
13095
|
+
const lower = query.toLowerCase();
|
|
13096
|
+
const exact = segments.find((s) => s.segment.name.toLowerCase() === lower);
|
|
13097
|
+
if (exact) return { type: "exact", segment: exact };
|
|
13098
|
+
const matches = segments.filter((s) => s.segment.name.toLowerCase().includes(lower));
|
|
13099
|
+
if (matches.length === 1) return { type: "exact", segment: matches[0] };
|
|
13100
|
+
if (matches.length > 1) return { type: "ambiguous", matches };
|
|
13101
|
+
return { type: "none" };
|
|
13102
|
+
}
|
|
13073
13103
|
function resolveSegmentScopeFromSnapshot(segment, snapshot) {
|
|
13074
13104
|
const orgIds = [];
|
|
13075
13105
|
const peopleIds = [];
|
|
@@ -13382,8 +13412,9 @@ function resolveThresholds(salesMotion, computedBaselines) {
|
|
|
13382
13412
|
return resolved;
|
|
13383
13413
|
}
|
|
13384
13414
|
async function getResolvedThresholds() {
|
|
13415
|
+
const { loadProfile: loadProfile2 } = await Promise.resolve().then(() => (init_profile(), profile_exports));
|
|
13385
13416
|
const { getConfigValue: getConfigValue2 } = await Promise.resolve().then(() => (init_store(), store_exports));
|
|
13386
|
-
const motion = getConfigValue2("sales-motion");
|
|
13417
|
+
const motion = loadProfile2()?.sales_motion ?? getConfigValue2("sales-motion");
|
|
13387
13418
|
return resolveThresholds(motion ?? null, {});
|
|
13388
13419
|
}
|
|
13389
13420
|
var init_resolve = __esm({
|
|
@@ -16237,7 +16268,7 @@ __export(play_outcomes_exports, {
|
|
|
16237
16268
|
listPlayOutcomes: () => listPlayOutcomes,
|
|
16238
16269
|
recordPlayOutcomes: () => recordPlayOutcomes
|
|
16239
16270
|
});
|
|
16240
|
-
import { existsSync as
|
|
16271
|
+
import { existsSync as existsSync21, readFileSync as readFileSync17, appendFileSync as appendFileSync6 } from "fs";
|
|
16241
16272
|
import { join as join21 } from "path";
|
|
16242
16273
|
import { randomUUID as randomUUID7 } from "crypto";
|
|
16243
16274
|
function outcomesPath() {
|
|
@@ -16245,7 +16276,7 @@ function outcomesPath() {
|
|
|
16245
16276
|
}
|
|
16246
16277
|
function listPlayOutcomes() {
|
|
16247
16278
|
const path = outcomesPath();
|
|
16248
|
-
if (!
|
|
16279
|
+
if (!existsSync21(path)) return [];
|
|
16249
16280
|
const out = [];
|
|
16250
16281
|
for (const line of readFileSync17(path, "utf-8").split("\n")) {
|
|
16251
16282
|
const trimmed = line.trim();
|
|
@@ -19238,7 +19269,7 @@ var init_markdown = __esm({
|
|
|
19238
19269
|
import chalk16 from "chalk";
|
|
19239
19270
|
function formatLlmAttribution(meta) {
|
|
19240
19271
|
if (!meta.model_used) return null;
|
|
19241
|
-
const provider = meta.provider_used ?? "
|
|
19272
|
+
const provider = meta.provider_used ?? "unknown";
|
|
19242
19273
|
let line = `via ${formatModelLabel(provider, meta.model_used)}`;
|
|
19243
19274
|
if (meta.failover) {
|
|
19244
19275
|
line += " (auto-failover)";
|
|
@@ -19714,16 +19745,16 @@ var init_terminal = __esm({
|
|
|
19714
19745
|
});
|
|
19715
19746
|
|
|
19716
19747
|
// src/demo/taxonomy-cache.ts
|
|
19717
|
-
import { readFileSync as readFileSync18, writeFileSync as writeFileSync14, existsSync as
|
|
19748
|
+
import { readFileSync as readFileSync18, writeFileSync as writeFileSync14, existsSync as existsSync22, mkdirSync as mkdirSync12, unlinkSync as unlinkSync4 } from "fs";
|
|
19718
19749
|
import { homedir as homedir7 } from "os";
|
|
19719
19750
|
import { join as join22 } from "path";
|
|
19720
19751
|
function ensureDir6() {
|
|
19721
|
-
if (!
|
|
19752
|
+
if (!existsSync22(NTRP_DIR4)) {
|
|
19722
19753
|
mkdirSync12(NTRP_DIR4, { recursive: true });
|
|
19723
19754
|
}
|
|
19724
19755
|
}
|
|
19725
19756
|
function loadCachedTaxonomy(profile) {
|
|
19726
|
-
if (!
|
|
19757
|
+
if (!existsSync22(TAXONOMY_PATH)) return null;
|
|
19727
19758
|
try {
|
|
19728
19759
|
const parsed = JSON.parse(readFileSync18(TAXONOMY_PATH, "utf-8"));
|
|
19729
19760
|
if (!parsed || typeof parsed !== "object") return null;
|
|
@@ -19738,7 +19769,7 @@ function saveCachedTaxonomy(taxonomy) {
|
|
|
19738
19769
|
writeFileSync14(TAXONOMY_PATH, JSON.stringify(taxonomy, null, 2) + "\n");
|
|
19739
19770
|
}
|
|
19740
19771
|
function invalidateTaxonomy() {
|
|
19741
|
-
if (
|
|
19772
|
+
if (existsSync22(TAXONOMY_PATH)) {
|
|
19742
19773
|
try {
|
|
19743
19774
|
unlinkSync4(TAXONOMY_PATH);
|
|
19744
19775
|
} catch {
|
|
@@ -20208,7 +20239,7 @@ __export(inbox_setup_exports, {
|
|
|
20208
20239
|
shouldOfferInboxSkillSetup: () => shouldOfferInboxSkillSetup
|
|
20209
20240
|
});
|
|
20210
20241
|
import chalk19 from "chalk";
|
|
20211
|
-
import { existsSync as
|
|
20242
|
+
import { existsSync as existsSync23 } from "fs";
|
|
20212
20243
|
function markDemoOffered() {
|
|
20213
20244
|
setConfigValue("ai-inbox-nudge-seen", "true");
|
|
20214
20245
|
}
|
|
@@ -20225,7 +20256,7 @@ function shouldOfferInboxSkillSetup(beat) {
|
|
|
20225
20256
|
return !hasProductionOffered();
|
|
20226
20257
|
}
|
|
20227
20258
|
function printSkipHint(beat) {
|
|
20228
|
-
const setCmd = paint("accent",
|
|
20259
|
+
const setCmd = paint("accent", `/inbox set ${defaultAiInboxDirDisplay()}`);
|
|
20229
20260
|
const skillCmd = paint("accent", "/inbox skill");
|
|
20230
20261
|
if (beat === "demo") {
|
|
20231
20262
|
console.log(
|
|
@@ -20237,13 +20268,15 @@ function printSkipHint(beat) {
|
|
|
20237
20268
|
" " + chalk19.dim("Skipped. NTRP will not ask again. Type ") + setCmd + chalk19.dim(" then ") + skillCmd + chalk19.dim(" at any time.")
|
|
20238
20269
|
);
|
|
20239
20270
|
}
|
|
20240
|
-
async function reuseInboxFolderIfPresent(session, beat, folderPath
|
|
20271
|
+
async function reuseInboxFolderIfPresent(session, beat, folderPath) {
|
|
20241
20272
|
if (getAiInboxDir()) return false;
|
|
20242
|
-
|
|
20243
|
-
|
|
20273
|
+
const candidates = folderPath ? [folderPath] : [.../* @__PURE__ */ new Set([defaultAiInboxDir(), legacyAiInboxDir()])];
|
|
20274
|
+
const existing = candidates.find((p) => existsSync23(p));
|
|
20275
|
+
if (!existing) return false;
|
|
20276
|
+
console.log(" " + chalk19.dim("Pickup folder still on disk: ") + existing);
|
|
20244
20277
|
const reuse = await session.confirm("Reuse this pickup folder?", true);
|
|
20245
20278
|
if (!reuse) return false;
|
|
20246
|
-
const resolved = setAiInboxDir(
|
|
20279
|
+
const resolved = setAiInboxDir(existing);
|
|
20247
20280
|
markDemoOffered();
|
|
20248
20281
|
if (beat === "production") markProductionOffered();
|
|
20249
20282
|
console.log();
|
|
@@ -20258,7 +20291,7 @@ async function offerInboxSkillSetup(session, opts = {}) {
|
|
|
20258
20291
|
const beat = opts.beat ?? "production";
|
|
20259
20292
|
if (!shouldOfferInboxSkillSetup(beat)) return;
|
|
20260
20293
|
console.log();
|
|
20261
|
-
console.log(" " + bold("Teach
|
|
20294
|
+
console.log(" " + bold("Teach your AI where handoffs live"));
|
|
20262
20295
|
console.log(
|
|
20263
20296
|
" " + chalk19.dim(
|
|
20264
20297
|
"Optional. NTRP copies every handoff into one folder. You paste instructions once; later /handoff just writes the file."
|
|
@@ -20269,7 +20302,10 @@ async function offerInboxSkillSetup(session, opts = {}) {
|
|
|
20269
20302
|
}
|
|
20270
20303
|
console.log();
|
|
20271
20304
|
if (await reuseInboxFolderIfPresent(session, beat)) return;
|
|
20272
|
-
const want = await session.confirm(
|
|
20305
|
+
const want = await session.confirm(
|
|
20306
|
+
"Set a pickup folder for Claude, ChatGPT, Cursor, or another desktop AI?",
|
|
20307
|
+
true
|
|
20308
|
+
);
|
|
20273
20309
|
if (!want) {
|
|
20274
20310
|
if (beat === "demo") markDemoOffered();
|
|
20275
20311
|
else markProductionOffered();
|
|
@@ -20301,10 +20337,12 @@ async function offerInboxSkillSetup(session, opts = {}) {
|
|
|
20301
20337
|
console.log(" " + chalk19.dim(`Synced ${n} recent export${n === 1 ? "" : "s"}.`));
|
|
20302
20338
|
}
|
|
20303
20339
|
console.log(
|
|
20304
|
-
" " + chalk19.dim(
|
|
20340
|
+
" " + chalk19.dim(
|
|
20341
|
+
"Paste this skill into your desktop AI once. Later, tell it to open the latest NTRP handoff."
|
|
20342
|
+
)
|
|
20305
20343
|
);
|
|
20306
20344
|
printStandingSkill();
|
|
20307
|
-
await session.askPressEnter("Paste the skill into
|
|
20345
|
+
await session.askPressEnter("Paste the skill into your AI. Then continue");
|
|
20308
20346
|
console.log(" " + chalk19.dim("Done. Later handoffs write to that folder. The skill is not printed again."));
|
|
20309
20347
|
console.log();
|
|
20310
20348
|
}
|
|
@@ -20339,7 +20377,7 @@ __export(ingest_exports, {
|
|
|
20339
20377
|
handler: () => handler2
|
|
20340
20378
|
});
|
|
20341
20379
|
import chalk20 from "chalk";
|
|
20342
|
-
import { readFileSync as readFileSync19, existsSync as
|
|
20380
|
+
import { readFileSync as readFileSync19, existsSync as existsSync24 } from "fs";
|
|
20343
20381
|
import { basename as basename6 } from "path";
|
|
20344
20382
|
async function handler2(args, ctx) {
|
|
20345
20383
|
const { positional, flags } = parseArgs2(args, [
|
|
@@ -20363,7 +20401,7 @@ async function handler2(args, ctx) {
|
|
|
20363
20401
|
console.error(chalk20.dim(" /ingest --demo [--scenario <name>]"));
|
|
20364
20402
|
process.exit(1);
|
|
20365
20403
|
}
|
|
20366
|
-
if (!
|
|
20404
|
+
if (!existsSync24(file)) {
|
|
20367
20405
|
console.error(chalk20.red(` File not found: ${file}`));
|
|
20368
20406
|
process.exit(1);
|
|
20369
20407
|
}
|
|
@@ -20951,7 +20989,7 @@ function buildUserMessage(input) {
|
|
|
20951
20989
|
async function* streamFindings(input, ctx) {
|
|
20952
20990
|
assertReplAi(ctx);
|
|
20953
20991
|
const userMessage = buildUserMessage(input);
|
|
20954
|
-
let meta = { model_used: "unknown", provider_used: "
|
|
20992
|
+
let meta = { model_used: "unknown", provider_used: "unknown", failover: false };
|
|
20955
20993
|
const scanner = new StreamingJsonArrayParser();
|
|
20956
20994
|
let streamed = 0;
|
|
20957
20995
|
for await (const event of streamWithFailover(
|
|
@@ -22449,9 +22487,9 @@ async function handleGetSessionBrief(input) {
|
|
|
22449
22487
|
if (!target) {
|
|
22450
22488
|
return { error: `No session matching "${raw}".` };
|
|
22451
22489
|
}
|
|
22452
|
-
const { existsSync:
|
|
22490
|
+
const { existsSync: existsSync36, readFileSync: readFileSync24 } = await import("fs");
|
|
22453
22491
|
const briefPath = contextDocPathForSession2(target.id);
|
|
22454
|
-
if (!
|
|
22492
|
+
if (!existsSync36(briefPath)) {
|
|
22455
22493
|
return {
|
|
22456
22494
|
session_id: target.id,
|
|
22457
22495
|
error: "No context brief on disk for this session (created before brief storage existed).",
|
|
@@ -23166,7 +23204,7 @@ async function* agenticFindings(computeResult, divergences, options) {
|
|
|
23166
23204
|
const priorMessages = priorRaw.length > 0 && typeof priorRaw[0] === "object" && priorRaw[0] !== null && "content" in priorRaw[0] ? normalizeThread(priorRaw) : priorRaw;
|
|
23167
23205
|
const conversational = mode === "fresh" || mode === "think";
|
|
23168
23206
|
const messages = conversational && priorMessages.length > 0 && options.userQuestion ? [...priorMessages, { role: "user", content: options.userQuestion }] : [{ role: "user", content: initialContext }];
|
|
23169
|
-
let lastMeta = { provider_used: "
|
|
23207
|
+
let lastMeta = { provider_used: "unknown", model_used: "unknown" };
|
|
23170
23208
|
const loopGuard = new ToolLoopGuard();
|
|
23171
23209
|
const allowedTools = new Set(tools.map((t) => t.name));
|
|
23172
23210
|
async function callLlm(msgs, tokens, withTools) {
|
|
@@ -24154,52 +24192,82 @@ OUTPUT FORMAT \u2014 respond with STRICT JSON only, no preamble, no markdown fen
|
|
|
24154
24192
|
});
|
|
24155
24193
|
|
|
24156
24194
|
// src/commands/profile.ts
|
|
24157
|
-
var
|
|
24158
|
-
__export(
|
|
24195
|
+
var profile_exports2 = {};
|
|
24196
|
+
__export(profile_exports2, {
|
|
24159
24197
|
PRESET_DESCRIPTIONS: () => PRESET_DESCRIPTIONS,
|
|
24160
24198
|
PRESET_LABELS: () => PRESET_LABELS,
|
|
24161
24199
|
handler: () => handler4
|
|
24162
24200
|
});
|
|
24163
24201
|
import chalk23 from "chalk";
|
|
24164
|
-
async function handler4(args,
|
|
24202
|
+
async function handler4(args, ctx) {
|
|
24165
24203
|
const { positional } = parseArgs2(args);
|
|
24166
24204
|
const sub = positional[0] ?? "show";
|
|
24167
|
-
|
|
24168
|
-
|
|
24169
|
-
|
|
24170
|
-
|
|
24171
|
-
|
|
24172
|
-
|
|
24173
|
-
|
|
24174
|
-
|
|
24175
|
-
|
|
24176
|
-
|
|
24177
|
-
|
|
24205
|
+
const structured = isStructuredOutput(ctx.execution);
|
|
24206
|
+
try {
|
|
24207
|
+
switch (sub) {
|
|
24208
|
+
case "list":
|
|
24209
|
+
return listProfiles(structured);
|
|
24210
|
+
case "set":
|
|
24211
|
+
return setProfile(positional[1], structured);
|
|
24212
|
+
case "show":
|
|
24213
|
+
return showProfile(structured);
|
|
24214
|
+
default: {
|
|
24215
|
+
const err = new NtrpError("unknown_subcommand", `Unknown subcommand: ${sub}`, 2 /* Usage */);
|
|
24216
|
+
if (structured) emitError("profile", err);
|
|
24217
|
+
console.error(chalk23.red(` Unknown subcommand: ${sub}`));
|
|
24218
|
+
console.log(chalk23.dim(" Usage: /profile <list|set|show> [preset]"));
|
|
24219
|
+
process.exit(1);
|
|
24220
|
+
}
|
|
24178
24221
|
}
|
|
24222
|
+
} catch (err) {
|
|
24223
|
+
if (structured) emitError("profile", err);
|
|
24224
|
+
throw err;
|
|
24179
24225
|
}
|
|
24180
24226
|
}
|
|
24181
|
-
function listProfiles() {
|
|
24227
|
+
function listProfiles(structured) {
|
|
24228
|
+
const current = loadProfile()?.sales_motion ?? getConfigValue("sales-motion");
|
|
24229
|
+
const presets = Object.entries(PRESET_DESCRIPTIONS).map(([id, description]) => ({
|
|
24230
|
+
id,
|
|
24231
|
+
label: PRESET_LABELS[id],
|
|
24232
|
+
description,
|
|
24233
|
+
active: current === id
|
|
24234
|
+
}));
|
|
24235
|
+
if (structured) {
|
|
24236
|
+
emitResult("profile", { action: "list", presets, current_preset: current ?? null });
|
|
24237
|
+
return;
|
|
24238
|
+
}
|
|
24182
24239
|
console.log();
|
|
24183
24240
|
console.log(chalk23.bold(" Sales Motion Presets"));
|
|
24184
24241
|
console.log();
|
|
24185
|
-
const
|
|
24186
|
-
|
|
24187
|
-
|
|
24188
|
-
console.log(` ${
|
|
24189
|
-
console.log(chalk23.dim(` ${"".padEnd(16)} id: ${key}`));
|
|
24242
|
+
for (const p of presets) {
|
|
24243
|
+
const marker2 = p.active ? chalk23.green(" (active)") : "";
|
|
24244
|
+
console.log(` ${chalk23.bold(p.label.padEnd(16))} ${chalk23.dim(p.description)}${marker2}`);
|
|
24245
|
+
console.log(chalk23.dim(` ${"".padEnd(16)} id: ${p.id}`));
|
|
24190
24246
|
}
|
|
24191
24247
|
console.log();
|
|
24192
24248
|
console.log(chalk23.dim(" Run /profile set <preset> to activate a profile."));
|
|
24193
24249
|
console.log();
|
|
24194
24250
|
}
|
|
24195
|
-
function setProfile(preset) {
|
|
24251
|
+
function setProfile(preset, structured) {
|
|
24196
24252
|
if (!preset) {
|
|
24253
|
+
const err = new NtrpError(
|
|
24254
|
+
"preset_required",
|
|
24255
|
+
`/profile set requires a preset. Valid options: ${Object.keys(PROFILE_PRESETS).join(", ")}`,
|
|
24256
|
+
2 /* Usage */
|
|
24257
|
+
);
|
|
24258
|
+
if (structured) emitError("profile", err);
|
|
24197
24259
|
console.error(chalk23.red(" /profile set requires a preset."));
|
|
24198
24260
|
console.error(chalk23.dim(` Valid options: ${Object.keys(PROFILE_PRESETS).join(", ")}`));
|
|
24199
24261
|
process.exit(1);
|
|
24200
24262
|
}
|
|
24201
24263
|
const validPresets = Object.keys(PROFILE_PRESETS);
|
|
24202
24264
|
if (!validPresets.includes(preset)) {
|
|
24265
|
+
const err = new NtrpError(
|
|
24266
|
+
"unknown_preset",
|
|
24267
|
+
`Unknown preset: ${preset}. Valid options: ${validPresets.join(", ")}`,
|
|
24268
|
+
2 /* Usage */
|
|
24269
|
+
);
|
|
24270
|
+
if (structured) emitError("profile", err);
|
|
24203
24271
|
console.error(chalk23.red(`
|
|
24204
24272
|
Unknown preset: ${preset}`));
|
|
24205
24273
|
console.error(chalk23.dim(` Valid options: ${validPresets.join(", ")}
|
|
@@ -24208,26 +24276,76 @@ function setProfile(preset) {
|
|
|
24208
24276
|
}
|
|
24209
24277
|
const motion = preset;
|
|
24210
24278
|
setConfigValue("sales-motion", motion);
|
|
24279
|
+
const hadProfile = Boolean(loadProfile());
|
|
24280
|
+
if (hadProfile) {
|
|
24281
|
+
updateProfile({ sales_motion: motion });
|
|
24282
|
+
}
|
|
24211
24283
|
const resolved = resolveThresholds(motion, {});
|
|
24212
24284
|
const defaults = DEFAULT_THRESHOLDS;
|
|
24213
|
-
|
|
24214
|
-
console.log(chalk23.green(` Profile set to ${PRESET_LABELS[motion]}`));
|
|
24215
|
-
console.log();
|
|
24216
|
-
const changes = [];
|
|
24285
|
+
const threshold_changes = {};
|
|
24217
24286
|
if (resolved.freshness.people_window_days !== defaults.freshness.people_window_days) {
|
|
24218
|
-
|
|
24287
|
+
threshold_changes.freshness_window_days = {
|
|
24288
|
+
from: defaults.freshness.people_window_days,
|
|
24289
|
+
to: resolved.freshness.people_window_days
|
|
24290
|
+
};
|
|
24219
24291
|
}
|
|
24220
24292
|
if (resolved.flow_rate.green_days !== defaults.flow_rate.green_days) {
|
|
24221
|
-
|
|
24293
|
+
threshold_changes.flow_rate_green_days = {
|
|
24294
|
+
from: defaults.flow_rate.green_days,
|
|
24295
|
+
to: resolved.flow_rate.green_days
|
|
24296
|
+
};
|
|
24222
24297
|
}
|
|
24223
24298
|
if (resolved.flow_rate.stuck_days !== defaults.flow_rate.stuck_days) {
|
|
24224
|
-
|
|
24299
|
+
threshold_changes.flow_rate_stuck_days = {
|
|
24300
|
+
from: defaults.flow_rate.stuck_days,
|
|
24301
|
+
to: resolved.flow_rate.stuck_days
|
|
24302
|
+
};
|
|
24225
24303
|
}
|
|
24226
24304
|
if (resolved.signal_to_noise.lookback_days !== defaults.signal_to_noise.lookback_days) {
|
|
24227
|
-
|
|
24305
|
+
threshold_changes.signal_to_noise_lookback_days = {
|
|
24306
|
+
from: defaults.signal_to_noise.lookback_days,
|
|
24307
|
+
to: resolved.signal_to_noise.lookback_days
|
|
24308
|
+
};
|
|
24228
24309
|
}
|
|
24229
24310
|
if (resolved.thread_depth.activity_window_days !== defaults.thread_depth.activity_window_days) {
|
|
24230
|
-
|
|
24311
|
+
threshold_changes.thread_depth_window_days = {
|
|
24312
|
+
from: defaults.thread_depth.activity_window_days,
|
|
24313
|
+
to: resolved.thread_depth.activity_window_days
|
|
24314
|
+
};
|
|
24315
|
+
}
|
|
24316
|
+
if (structured) {
|
|
24317
|
+
emitResult("profile", {
|
|
24318
|
+
action: "set",
|
|
24319
|
+
motion,
|
|
24320
|
+
label: PRESET_LABELS[motion],
|
|
24321
|
+
threshold_changes,
|
|
24322
|
+
profile_synced: hadProfile
|
|
24323
|
+
});
|
|
24324
|
+
return;
|
|
24325
|
+
}
|
|
24326
|
+
console.log();
|
|
24327
|
+
console.log(chalk23.green(` Profile set to ${PRESET_LABELS[motion]}`));
|
|
24328
|
+
console.log();
|
|
24329
|
+
const changes = [];
|
|
24330
|
+
if (threshold_changes.freshness_window_days) {
|
|
24331
|
+
const c = threshold_changes.freshness_window_days;
|
|
24332
|
+
changes.push(` Freshness window: ${c.from}d \u2192 ${c.to}d`);
|
|
24333
|
+
}
|
|
24334
|
+
if (threshold_changes.flow_rate_green_days) {
|
|
24335
|
+
const c = threshold_changes.flow_rate_green_days;
|
|
24336
|
+
changes.push(` Flow rate green: ${c.from}d \u2192 ${c.to}d`);
|
|
24337
|
+
}
|
|
24338
|
+
if (threshold_changes.flow_rate_stuck_days) {
|
|
24339
|
+
const c = threshold_changes.flow_rate_stuck_days;
|
|
24340
|
+
changes.push(` Stuck threshold: ${c.from}d \u2192 ${c.to}d`);
|
|
24341
|
+
}
|
|
24342
|
+
if (threshold_changes.signal_to_noise_lookback_days) {
|
|
24343
|
+
const c = threshold_changes.signal_to_noise_lookback_days;
|
|
24344
|
+
changes.push(` S/N lookback: ${c.from}d \u2192 ${c.to}d`);
|
|
24345
|
+
}
|
|
24346
|
+
if (threshold_changes.thread_depth_window_days) {
|
|
24347
|
+
const c = threshold_changes.thread_depth_window_days;
|
|
24348
|
+
changes.push(` Thread depth window: ${c.from}d \u2192 ${c.to}d`);
|
|
24231
24349
|
}
|
|
24232
24350
|
if (changes.length > 0) {
|
|
24233
24351
|
console.log(chalk23.dim(" Threshold changes:"));
|
|
@@ -24239,9 +24357,27 @@ function setProfile(preset) {
|
|
|
24239
24357
|
console.log(chalk23.dim(" Run /diagnose to see results with the new profile."));
|
|
24240
24358
|
console.log();
|
|
24241
24359
|
}
|
|
24242
|
-
function showProfile() {
|
|
24360
|
+
function showProfile(structured) {
|
|
24243
24361
|
const companyProfile = loadProfile();
|
|
24244
24362
|
const current = companyProfile?.sales_motion ?? getConfigValue("sales-motion");
|
|
24363
|
+
const resolved = resolveThresholds(current ?? null, {});
|
|
24364
|
+
if (structured) {
|
|
24365
|
+
emitResult("profile", {
|
|
24366
|
+
action: "show",
|
|
24367
|
+
motion: current ?? null,
|
|
24368
|
+
label: current && PRESET_LABELS[current] ? PRESET_LABELS[current] : null,
|
|
24369
|
+
company_profile: companyProfile,
|
|
24370
|
+
thresholds: {
|
|
24371
|
+
freshness_window_days: resolved.freshness.people_window_days,
|
|
24372
|
+
flow_rate_green_days: resolved.flow_rate.green_days,
|
|
24373
|
+
flow_rate_stuck_days: resolved.flow_rate.stuck_days,
|
|
24374
|
+
signal_to_noise_lookback_days: resolved.signal_to_noise.lookback_days,
|
|
24375
|
+
thread_depth_window_days: resolved.thread_depth.activity_window_days,
|
|
24376
|
+
multi_thread_min: resolved.thread_depth.multi_thread_threshold
|
|
24377
|
+
}
|
|
24378
|
+
});
|
|
24379
|
+
return;
|
|
24380
|
+
}
|
|
24245
24381
|
if (companyProfile) {
|
|
24246
24382
|
const lines = [];
|
|
24247
24383
|
lines.push(`### ${companyProfile.company_name}`);
|
|
@@ -24271,7 +24407,6 @@ function showProfile() {
|
|
|
24271
24407
|
console.log(chalk23.dim(" No sales motion set. Using default thresholds."));
|
|
24272
24408
|
}
|
|
24273
24409
|
console.log();
|
|
24274
|
-
const resolved = resolveThresholds(current ?? null, {});
|
|
24275
24410
|
console.log(chalk23.dim(" Key Thresholds:"));
|
|
24276
24411
|
console.log(chalk23.dim(` Freshness window: ${resolved.freshness.people_window_days} days`));
|
|
24277
24412
|
console.log(chalk23.dim(` Flow rate (green): ${resolved.flow_rate.green_days} days`));
|
|
@@ -24293,6 +24428,9 @@ var init_profile2 = __esm({
|
|
|
24293
24428
|
init_argparse();
|
|
24294
24429
|
init_markdown();
|
|
24295
24430
|
init_theme();
|
|
24431
|
+
init_emit();
|
|
24432
|
+
init_errors2();
|
|
24433
|
+
init_types2();
|
|
24296
24434
|
PRESET_DESCRIPTIONS = {
|
|
24297
24435
|
plg: "Product-Led Growth \u2014 shorter cycles, high volume, self-serve focus",
|
|
24298
24436
|
smb_velocity: "SMB Velocity \u2014 fast sales cycles, quick close, volume-oriented",
|
|
@@ -25132,7 +25270,7 @@ __export(new_exports, {
|
|
|
25132
25270
|
handler: () => handler6
|
|
25133
25271
|
});
|
|
25134
25272
|
import chalk26 from "chalk";
|
|
25135
|
-
import { existsSync as
|
|
25273
|
+
import { existsSync as existsSync25 } from "fs";
|
|
25136
25274
|
import { basename as basename7 } from "path";
|
|
25137
25275
|
async function handler6(args, ctx) {
|
|
25138
25276
|
const { positional, flags } = parseArgs2(args, ["demo", "empty", "list-scenarios", "regen-taxonomy"]);
|
|
@@ -25154,7 +25292,7 @@ async function handler6(args, ctx) {
|
|
|
25154
25292
|
console.error(chalk26.red(" Usage: /new <file.csv> | --demo [--scenario <name>] | --empty [--lens health|metrics]"));
|
|
25155
25293
|
return;
|
|
25156
25294
|
}
|
|
25157
|
-
if (source.kind === "file" && !
|
|
25295
|
+
if (source.kind === "file" && !existsSync25(source.path)) {
|
|
25158
25296
|
console.error(chalk26.red(` File not found: ${source.path}`));
|
|
25159
25297
|
return;
|
|
25160
25298
|
}
|
|
@@ -25381,7 +25519,7 @@ __export(end_exports, {
|
|
|
25381
25519
|
handler: () => handler7
|
|
25382
25520
|
});
|
|
25383
25521
|
import chalk27 from "chalk";
|
|
25384
|
-
import { existsSync as
|
|
25522
|
+
import { existsSync as existsSync26 } from "fs";
|
|
25385
25523
|
async function handler7(args, ctx) {
|
|
25386
25524
|
if (args.length > 0) {
|
|
25387
25525
|
console.error(chalk27.red(" Usage: /end"));
|
|
@@ -25418,10 +25556,10 @@ async function handler7(args, ctx) {
|
|
|
25418
25556
|
if (summary) {
|
|
25419
25557
|
console.log(" " + chalk27.dim(summary));
|
|
25420
25558
|
}
|
|
25421
|
-
if (
|
|
25559
|
+
if (existsSync26(transcriptPathForSession(endedId))) {
|
|
25422
25560
|
console.log(" " + chalk27.dim("Transcript: ") + chalk27.dim(transcriptPathForSession(endedId)));
|
|
25423
25561
|
}
|
|
25424
|
-
if (
|
|
25562
|
+
if (existsSync26(contextDocPathForSession(endedId))) {
|
|
25425
25563
|
console.log(" " + chalk27.dim("Context brief: ") + chalk27.dim(contextDocPathForSession(endedId)));
|
|
25426
25564
|
}
|
|
25427
25565
|
console.log();
|
|
@@ -25443,7 +25581,7 @@ __export(session_exports, {
|
|
|
25443
25581
|
});
|
|
25444
25582
|
import chalk28 from "chalk";
|
|
25445
25583
|
import { join as join23 } from "path";
|
|
25446
|
-
import { existsSync as
|
|
25584
|
+
import { existsSync as existsSync27 } from "fs";
|
|
25447
25585
|
async function handler8(args, ctx) {
|
|
25448
25586
|
const sub = args[0];
|
|
25449
25587
|
if (!sub) return listSessionsView(ctx);
|
|
@@ -25589,7 +25727,7 @@ async function pickUp(idArg, ctx) {
|
|
|
25589
25727
|
);
|
|
25590
25728
|
}
|
|
25591
25729
|
const contextPath = contextDocPathForSession(target.id);
|
|
25592
|
-
if (
|
|
25730
|
+
if (existsSync27(contextPath)) {
|
|
25593
25731
|
console.log(" " + chalk28.dim("Context brief: ") + chalk28.dim(contextPath));
|
|
25594
25732
|
}
|
|
25595
25733
|
console.log();
|
|
@@ -25765,22 +25903,19 @@ async function loadReportData(segmentName) {
|
|
|
25765
25903
|
let { health, segments, findings, entityCounts } = diagnosis;
|
|
25766
25904
|
let scopedSegment = null;
|
|
25767
25905
|
if (segmentName) {
|
|
25768
|
-
const
|
|
25769
|
-
|
|
25770
|
-
|
|
25771
|
-
|
|
25772
|
-
|
|
25773
|
-
else if (subs.length > 1) {
|
|
25774
|
-
throw new NtrpError("ambiguous_segment", `"${segmentName}" matches multiple segments.`, 2 /* Usage */, {
|
|
25775
|
-
matches: subs.map((s) => s.segment.name)
|
|
25776
|
-
});
|
|
25777
|
-
}
|
|
25906
|
+
const resolved = resolveSegmentByName(segmentName, segments);
|
|
25907
|
+
if (resolved.type === "ambiguous") {
|
|
25908
|
+
throw new NtrpError("ambiguous_segment", `"${segmentName}" matches multiple segments.`, 2 /* Usage */, {
|
|
25909
|
+
matches: resolved.matches.map((s) => s.segment.name)
|
|
25910
|
+
});
|
|
25778
25911
|
}
|
|
25779
|
-
if (
|
|
25912
|
+
if (resolved.type === "none") {
|
|
25780
25913
|
throw new NtrpError("segment_not_found", `No segment matching "${segmentName}".`, 2 /* Usage */, {
|
|
25781
25914
|
available_segments: segments.map((s) => s.segment.name)
|
|
25782
25915
|
});
|
|
25783
25916
|
}
|
|
25917
|
+
const match = resolved.segment;
|
|
25918
|
+
const lower = segmentName.toLowerCase();
|
|
25784
25919
|
scopedSegment = match;
|
|
25785
25920
|
health = match.result;
|
|
25786
25921
|
segments = [match];
|
|
@@ -25806,6 +25941,7 @@ var init_report = __esm({
|
|
|
25806
25941
|
init_errors2();
|
|
25807
25942
|
init_types2();
|
|
25808
25943
|
init_session_analysis();
|
|
25944
|
+
init_segments();
|
|
25809
25945
|
}
|
|
25810
25946
|
});
|
|
25811
25947
|
|
|
@@ -26169,20 +26305,15 @@ async function handler10(args, _ctx) {
|
|
|
26169
26305
|
let exportFindings = diagnosis.findings;
|
|
26170
26306
|
let exportHealth = diagnosis.health;
|
|
26171
26307
|
if (segmentName) {
|
|
26172
|
-
const
|
|
26173
|
-
|
|
26174
|
-
|
|
26175
|
-
const subs = diagnosis.segments.filter((s) => s.segment.name.toLowerCase().includes(lower));
|
|
26176
|
-
if (subs.length === 1) match = subs[0];
|
|
26177
|
-
else if (subs.length > 1) {
|
|
26178
|
-
console.error(chalk30.yellow(`
|
|
26308
|
+
const resolved = resolveSegmentByName(segmentName, diagnosis.segments);
|
|
26309
|
+
if (resolved.type === "ambiguous") {
|
|
26310
|
+
console.error(chalk30.yellow(`
|
|
26179
26311
|
"${segmentName}" matches multiple segments:`));
|
|
26180
|
-
|
|
26181
|
-
|
|
26182
|
-
|
|
26183
|
-
}
|
|
26312
|
+
for (const s of resolved.matches) console.log(chalk30.dim(` - ${s.segment.name}`));
|
|
26313
|
+
console.log();
|
|
26314
|
+
process.exit(1);
|
|
26184
26315
|
}
|
|
26185
|
-
if (
|
|
26316
|
+
if (resolved.type === "none") {
|
|
26186
26317
|
console.error(chalk30.red(`
|
|
26187
26318
|
No segment matching "${segmentName}".`));
|
|
26188
26319
|
if (diagnosis.segments.length > 0) {
|
|
@@ -26192,6 +26323,8 @@ async function handler10(args, _ctx) {
|
|
|
26192
26323
|
console.log();
|
|
26193
26324
|
process.exit(1);
|
|
26194
26325
|
}
|
|
26326
|
+
const match = resolved.segment;
|
|
26327
|
+
const lower = segmentName.toLowerCase();
|
|
26195
26328
|
exportHealth = match.result;
|
|
26196
26329
|
exportSegments = [match];
|
|
26197
26330
|
exportFindings = diagnosis.findings.filter((f) => f.segment.toLowerCase().includes(lower));
|
|
@@ -26248,6 +26381,7 @@ var init_export = __esm({
|
|
|
26248
26381
|
init_path_safety();
|
|
26249
26382
|
init_argparse();
|
|
26250
26383
|
init_exports_registry();
|
|
26384
|
+
init_segments();
|
|
26251
26385
|
}
|
|
26252
26386
|
});
|
|
26253
26387
|
|
|
@@ -27600,15 +27734,6 @@ __export(segment_exports, {
|
|
|
27600
27734
|
handler: () => handler15
|
|
27601
27735
|
});
|
|
27602
27736
|
import chalk35 from "chalk";
|
|
27603
|
-
function resolveSegmentByName(query, segments) {
|
|
27604
|
-
const lower = query.toLowerCase();
|
|
27605
|
-
const exact = segments.find((s) => s.segment.name.toLowerCase() === lower);
|
|
27606
|
-
if (exact) return { type: "exact", segment: exact };
|
|
27607
|
-
const matches = segments.filter((s) => s.segment.name.toLowerCase().includes(lower));
|
|
27608
|
-
if (matches.length === 1) return { type: "exact", segment: matches[0] };
|
|
27609
|
-
if (matches.length > 1) return { type: "ambiguous", matches };
|
|
27610
|
-
return { type: "none" };
|
|
27611
|
-
}
|
|
27612
27737
|
function handleNoMatch(query, segments) {
|
|
27613
27738
|
console.error(chalk35.red(` No segment matching "${query}".`));
|
|
27614
27739
|
console.log();
|
|
@@ -27932,6 +28057,7 @@ var init_segment = __esm({
|
|
|
27932
28057
|
init_types2();
|
|
27933
28058
|
init_serialize();
|
|
27934
28059
|
init_session_analysis();
|
|
28060
|
+
init_segments();
|
|
27935
28061
|
OPERATOR_ALIASES = {
|
|
27936
28062
|
eq: "equals",
|
|
27937
28063
|
equals: "equals",
|
|
@@ -28799,7 +28925,7 @@ async function* strategistPlanSession(options) {
|
|
|
28799
28925
|
})
|
|
28800
28926
|
}
|
|
28801
28927
|
];
|
|
28802
|
-
let lastMeta = { provider_used: "
|
|
28928
|
+
let lastMeta = { provider_used: "unknown", model_used: "unknown" };
|
|
28803
28929
|
const loopGuard = new ToolLoopGuard();
|
|
28804
28930
|
const allowedTools = new Set(tools.map((t) => t.name));
|
|
28805
28931
|
const callLlm = async (surface, withTools, maxTokens = STAGE_MAX_TOKENS) => {
|
|
@@ -30546,42 +30672,85 @@ __export(reset_exports, {
|
|
|
30546
30672
|
handler: () => handler20
|
|
30547
30673
|
});
|
|
30548
30674
|
import chalk43 from "chalk";
|
|
30549
|
-
async function
|
|
30675
|
+
async function countRowsByTable() {
|
|
30676
|
+
const counts = {};
|
|
30677
|
+
for (const table of TABLES) {
|
|
30678
|
+
try {
|
|
30679
|
+
const rows = await all(`SELECT COUNT(*) AS cnt FROM ${table}`);
|
|
30680
|
+
counts[table] = Number(rows[0]?.cnt ?? 0);
|
|
30681
|
+
} catch {
|
|
30682
|
+
counts[table] = 0;
|
|
30683
|
+
}
|
|
30684
|
+
}
|
|
30685
|
+
return counts;
|
|
30686
|
+
}
|
|
30687
|
+
async function handler20(args, ctx) {
|
|
30550
30688
|
const { flags } = parseArgs2(args, ["force"]);
|
|
30551
30689
|
const force = getBool(flags, "force");
|
|
30690
|
+
const structured = isStructuredOutput(ctx.execution);
|
|
30552
30691
|
if (!force) {
|
|
30692
|
+
const message = "This will delete ALL data from the local database. Re-run with --force to confirm.";
|
|
30693
|
+
if (structured) {
|
|
30694
|
+
emitError("reset", new NtrpError("force_required", message, 2 /* Usage */));
|
|
30695
|
+
}
|
|
30553
30696
|
console.log(chalk43.yellow("\n This will delete ALL data from the local database."));
|
|
30554
30697
|
console.log(chalk43.dim(" Re-run with --force to confirm.\n"));
|
|
30555
30698
|
process.exit(1);
|
|
30556
30699
|
}
|
|
30557
|
-
const spinner = makeSpinner("Resetting database\u2026");
|
|
30700
|
+
const spinner = structured ? null : makeSpinner("Resetting database\u2026");
|
|
30558
30701
|
try {
|
|
30559
30702
|
await initSchema();
|
|
30703
|
+
const counts_by_table = await countRowsByTable();
|
|
30704
|
+
const total_rows_deleted = Object.values(counts_by_table).reduce((a, b) => a + b, 0);
|
|
30560
30705
|
for (const table of TABLES) {
|
|
30561
30706
|
await run(`DELETE FROM ${table}`);
|
|
30562
30707
|
}
|
|
30563
|
-
spinner.succeed("All data cleared");
|
|
30564
30708
|
const { wipePrivacyStore: wipePrivacyStore3 } = await Promise.resolve().then(() => (init_lexicon_seed(), lexicon_seed_exports));
|
|
30565
30709
|
wipePrivacyStore3();
|
|
30710
|
+
if (structured) {
|
|
30711
|
+
emitResult("reset", {
|
|
30712
|
+
status: "cleared",
|
|
30713
|
+
tables_cleared: TABLES,
|
|
30714
|
+
total_rows_deleted,
|
|
30715
|
+
counts_by_table
|
|
30716
|
+
});
|
|
30717
|
+
return;
|
|
30718
|
+
}
|
|
30719
|
+
spinner.succeed("All data cleared");
|
|
30566
30720
|
console.log(chalk43.dim("\n Run ") + chalk43.cyan("/new") + chalk43.dim(" \u2192 pick Demo, then choose your analysis type.\n"));
|
|
30567
30721
|
} catch (err) {
|
|
30568
30722
|
if (force && isClosedConnectionError(err)) {
|
|
30569
30723
|
try {
|
|
30570
|
-
spinner.text = "Recreating database\u2026";
|
|
30724
|
+
if (spinner) spinner.text = "Recreating database\u2026";
|
|
30571
30725
|
await recreateDatabaseFile();
|
|
30572
30726
|
await initSchema();
|
|
30573
|
-
spinner.succeed("Database recreated");
|
|
30574
30727
|
const { wipePrivacyStore: wipePrivacyStore3 } = await Promise.resolve().then(() => (init_lexicon_seed(), lexicon_seed_exports));
|
|
30575
30728
|
wipePrivacyStore3();
|
|
30729
|
+
if (structured) {
|
|
30730
|
+
emitResult("reset", {
|
|
30731
|
+
status: "recreated",
|
|
30732
|
+
tables_cleared: TABLES,
|
|
30733
|
+
total_rows_deleted: 0,
|
|
30734
|
+
counts_by_table: Object.fromEntries(TABLES.map((t) => [t, 0]))
|
|
30735
|
+
});
|
|
30736
|
+
return;
|
|
30737
|
+
}
|
|
30738
|
+
spinner.succeed("Database recreated");
|
|
30576
30739
|
console.log(chalk43.dim("\n Run ") + chalk43.cyan("/new") + chalk43.dim(" \u2192 pick Demo, then choose your analysis type.\n"));
|
|
30577
30740
|
return;
|
|
30578
30741
|
} catch (recreateErr) {
|
|
30579
|
-
|
|
30742
|
+
if (structured) {
|
|
30743
|
+
emitError("reset", recreateErr instanceof NtrpError ? recreateErr : new NtrpError("reset_failed", String(recreateErr), 1 /* RuntimeError */));
|
|
30744
|
+
}
|
|
30745
|
+
spinner?.fail("Reset failed");
|
|
30580
30746
|
console.error(chalk43.red(String(recreateErr)));
|
|
30581
30747
|
process.exit(1);
|
|
30582
30748
|
}
|
|
30583
30749
|
}
|
|
30584
|
-
|
|
30750
|
+
if (structured) {
|
|
30751
|
+
emitError("reset", err instanceof NtrpError ? err : new NtrpError("reset_failed", String(err), 1 /* RuntimeError */));
|
|
30752
|
+
}
|
|
30753
|
+
spinner?.fail("Reset failed");
|
|
30585
30754
|
console.error(chalk43.red(String(err)));
|
|
30586
30755
|
process.exit(1);
|
|
30587
30756
|
}
|
|
@@ -30594,6 +30763,9 @@ var init_reset = __esm({
|
|
|
30594
30763
|
init_connection();
|
|
30595
30764
|
init_schema();
|
|
30596
30765
|
init_argparse();
|
|
30766
|
+
init_emit();
|
|
30767
|
+
init_errors2();
|
|
30768
|
+
init_types2();
|
|
30597
30769
|
TABLES = [
|
|
30598
30770
|
"findings",
|
|
30599
30771
|
"health_readings",
|
|
@@ -30874,7 +31046,10 @@ function usage() {
|
|
|
30874
31046
|
console.log(chalk45.dim(" Tip: ") + paint("accent", "/config set api-key") + chalk45.dim(" opens a hidden prompt (no inline paste)."));
|
|
30875
31047
|
console.log(chalk45.dim(" Tip: ") + paint("accent", "/connect") + chalk45.dim(" auto-detects the provider from any pasted key."));
|
|
30876
31048
|
}
|
|
30877
|
-
function fail(message, ctx) {
|
|
31049
|
+
function fail(message, ctx, code = "usage_error") {
|
|
31050
|
+
if (isStructuredOutput(ctx.execution)) {
|
|
31051
|
+
emitError("config", new NtrpError(code, message, 2 /* Usage */));
|
|
31052
|
+
}
|
|
30878
31053
|
console.error(chalk45.red(` ${message}`));
|
|
30879
31054
|
if (ctx.oneShot) process.exit(1);
|
|
30880
31055
|
}
|
|
@@ -30891,9 +31066,18 @@ async function promptSecretValue(key, ctx) {
|
|
|
30891
31066
|
prompts.close();
|
|
30892
31067
|
}
|
|
30893
31068
|
}
|
|
31069
|
+
function redactConfig(config) {
|
|
31070
|
+
const secrets = secretKeys();
|
|
31071
|
+
const out = {};
|
|
31072
|
+
for (const [k, v] of Object.entries(config)) {
|
|
31073
|
+
out[k] = secrets.has(k) ? display(k, v) : v;
|
|
31074
|
+
}
|
|
31075
|
+
return out;
|
|
31076
|
+
}
|
|
30894
31077
|
async function handler22(args, ctx) {
|
|
30895
31078
|
const { positional } = parseArgs2(args);
|
|
30896
31079
|
const sub = positional[0] ?? "list";
|
|
31080
|
+
const structured = isStructuredOutput(ctx.execution);
|
|
30897
31081
|
switch (sub) {
|
|
30898
31082
|
case "set": {
|
|
30899
31083
|
const key = positional[1];
|
|
@@ -30905,6 +31089,10 @@ async function handler22(args, ctx) {
|
|
|
30905
31089
|
}
|
|
30906
31090
|
let value = inlineValue;
|
|
30907
31091
|
if (!value && secretKeys().has(key)) {
|
|
31092
|
+
if (structured) {
|
|
31093
|
+
fail(`/config set ${key} requires an inline value in --json mode`, ctx, "value_required");
|
|
31094
|
+
return;
|
|
31095
|
+
}
|
|
30908
31096
|
try {
|
|
30909
31097
|
value = await promptSecretValue(key, ctx);
|
|
30910
31098
|
} catch (err) {
|
|
@@ -30919,7 +31107,11 @@ async function handler22(args, ctx) {
|
|
|
30919
31107
|
setConfigValue(key, value);
|
|
30920
31108
|
const saved = getConfigValue(key);
|
|
30921
31109
|
if (saved !== value) {
|
|
30922
|
-
fail(`Failed to save ${key} \u2014 check permissions on ~/.ntrp/config.json`, ctx);
|
|
31110
|
+
fail(`Failed to save ${key} \u2014 check permissions on ~/.ntrp/config.json`, ctx, "save_failed");
|
|
31111
|
+
return;
|
|
31112
|
+
}
|
|
31113
|
+
if (structured) {
|
|
31114
|
+
emitResult("config", { action: "set", key, value: display(key, value) });
|
|
30923
31115
|
return;
|
|
30924
31116
|
}
|
|
30925
31117
|
console.log();
|
|
@@ -30953,6 +31145,15 @@ async function handler22(args, ctx) {
|
|
|
30953
31145
|
return;
|
|
30954
31146
|
}
|
|
30955
31147
|
const value = getConfigValue(key);
|
|
31148
|
+
if (structured) {
|
|
31149
|
+
emitResult("config", {
|
|
31150
|
+
action: "get",
|
|
31151
|
+
key,
|
|
31152
|
+
value: value === void 0 ? null : display(key, value),
|
|
31153
|
+
is_set: value !== void 0
|
|
31154
|
+
});
|
|
31155
|
+
return;
|
|
31156
|
+
}
|
|
30956
31157
|
if (value === void 0) console.log(chalk45.dim(` ${key} is not set`));
|
|
30957
31158
|
else console.log(` ${key} = ${display(key, value)}`);
|
|
30958
31159
|
return;
|
|
@@ -30960,6 +31161,14 @@ async function handler22(args, ctx) {
|
|
|
30960
31161
|
case "list": {
|
|
30961
31162
|
const config = loadConfig();
|
|
30962
31163
|
const entries2 = Object.entries(config);
|
|
31164
|
+
if (structured) {
|
|
31165
|
+
emitResult("config", {
|
|
31166
|
+
action: "list",
|
|
31167
|
+
config: redactConfig(config),
|
|
31168
|
+
count: entries2.length
|
|
31169
|
+
});
|
|
31170
|
+
return;
|
|
31171
|
+
}
|
|
30963
31172
|
if (entries2.length === 0) {
|
|
30964
31173
|
console.log(chalk45.dim(" No configuration set."));
|
|
30965
31174
|
return;
|
|
@@ -30974,11 +31183,15 @@ async function handler22(args, ctx) {
|
|
|
30974
31183
|
return;
|
|
30975
31184
|
}
|
|
30976
31185
|
deleteConfigValue(key);
|
|
31186
|
+
if (structured) {
|
|
31187
|
+
emitResult("config", { action: "delete", key });
|
|
31188
|
+
return;
|
|
31189
|
+
}
|
|
30977
31190
|
console.log(chalk45.green(` Deleted ${key}`));
|
|
30978
31191
|
return;
|
|
30979
31192
|
}
|
|
30980
31193
|
default: {
|
|
30981
|
-
fail(`Unknown subcommand: ${sub}`, ctx);
|
|
31194
|
+
fail(`Unknown subcommand: ${sub}`, ctx, "unknown_subcommand");
|
|
30982
31195
|
usage();
|
|
30983
31196
|
return;
|
|
30984
31197
|
}
|
|
@@ -30993,6 +31206,9 @@ var init_config = __esm({
|
|
|
30993
31206
|
init_argparse();
|
|
30994
31207
|
init_prompts();
|
|
30995
31208
|
init_theme();
|
|
31209
|
+
init_emit();
|
|
31210
|
+
init_errors2();
|
|
31211
|
+
init_types2();
|
|
30996
31212
|
}
|
|
30997
31213
|
});
|
|
30998
31214
|
|
|
@@ -31010,8 +31226,13 @@ async function afterLicenseSuccess(ctx) {
|
|
|
31010
31226
|
async function handler23(args, ctx) {
|
|
31011
31227
|
const { positional } = parseArgs2(args);
|
|
31012
31228
|
const key = positional[0];
|
|
31229
|
+
const structured = isStructuredOutput(ctx.execution);
|
|
31013
31230
|
if (!key) {
|
|
31014
|
-
if (ctx.oneShot || !process.stdin.isTTY) {
|
|
31231
|
+
if (ctx.oneShot || !process.stdin.isTTY || structured) {
|
|
31232
|
+
const message = "Usage: /activate <key>. Or type /activate in ntrp to paste a key.";
|
|
31233
|
+
if (structured) {
|
|
31234
|
+
emitError("activate", new NtrpError("key_required", message, 2 /* Usage */));
|
|
31235
|
+
}
|
|
31015
31236
|
console.error(chalk46.red("\n Usage: /activate <key>"));
|
|
31016
31237
|
console.error(chalk46.dim(" Or type /activate in ntrp to paste a key.\n"));
|
|
31017
31238
|
if (ctx.oneShot) process.exit(1);
|
|
@@ -31037,18 +31258,29 @@ async function handler23(args, ctx) {
|
|
|
31037
31258
|
try {
|
|
31038
31259
|
const result = await activateLicenseKey(key);
|
|
31039
31260
|
if (!result.valid) {
|
|
31261
|
+
if (structured) {
|
|
31262
|
+
emitResult("activate", { valid: false, message: result.message });
|
|
31263
|
+
process.exit(1);
|
|
31264
|
+
}
|
|
31040
31265
|
console.error(chalk46.red(`
|
|
31041
31266
|
${result.message}
|
|
31042
31267
|
`));
|
|
31043
31268
|
if (ctx.oneShot) process.exit(1);
|
|
31044
31269
|
return;
|
|
31045
31270
|
}
|
|
31271
|
+
if (structured) {
|
|
31272
|
+
emitResult("activate", { valid: true, message: result.message });
|
|
31273
|
+
return;
|
|
31274
|
+
}
|
|
31046
31275
|
console.log(chalk46.green(`
|
|
31047
31276
|
License activated: ${result.message}
|
|
31048
31277
|
`));
|
|
31049
31278
|
await afterLicenseSuccess(ctx);
|
|
31050
31279
|
} catch (err) {
|
|
31051
31280
|
const message = err instanceof Error ? err.message : "License activation failed";
|
|
31281
|
+
if (structured) {
|
|
31282
|
+
emitError("activate", new NtrpError("activation_failed", message, 3 /* Auth */));
|
|
31283
|
+
}
|
|
31052
31284
|
console.error(chalk46.red(`
|
|
31053
31285
|
${message}
|
|
31054
31286
|
`));
|
|
@@ -31064,6 +31296,9 @@ var init_activate = __esm({
|
|
|
31064
31296
|
init_activation();
|
|
31065
31297
|
init_upgrade();
|
|
31066
31298
|
init_theme();
|
|
31299
|
+
init_emit();
|
|
31300
|
+
init_errors2();
|
|
31301
|
+
init_types2();
|
|
31067
31302
|
}
|
|
31068
31303
|
});
|
|
31069
31304
|
|
|
@@ -31131,7 +31366,7 @@ var init_checkout = __esm({
|
|
|
31131
31366
|
});
|
|
31132
31367
|
|
|
31133
31368
|
// src/services/setup.ts
|
|
31134
|
-
import { existsSync as
|
|
31369
|
+
import { existsSync as existsSync28, mkdirSync as mkdirSync17, readFileSync as readFileSync20, writeFileSync as writeFileSync19 } from "fs";
|
|
31135
31370
|
import { join as join29 } from "path";
|
|
31136
31371
|
function setupCheck() {
|
|
31137
31372
|
const home = ntrpHome();
|
|
@@ -31834,7 +32069,7 @@ async function handleDeliverFlow(input, ctx) {
|
|
|
31834
32069
|
}
|
|
31835
32070
|
console.log();
|
|
31836
32071
|
console.log(" " + chalk52.bold("Deliverable preview"));
|
|
31837
|
-
console.log(" " + chalk52.dim("NTRP writes the file to your inbox after you confirm. If you set the inbox during demo or company setup,
|
|
32072
|
+
console.log(" " + chalk52.dim("NTRP writes the file to your inbox after you confirm. If you set the inbox during demo or company setup, your AI can find the folder."));
|
|
31838
32073
|
console.log(" " + chalk52.dim("\u2500".repeat(56)));
|
|
31839
32074
|
const preview = draft.markdown.split("\n").slice(0, 12);
|
|
31840
32075
|
for (const l of preview) {
|
|
@@ -33134,7 +33369,7 @@ __export(sessions_exports, {
|
|
|
33134
33369
|
handler: () => handler35
|
|
33135
33370
|
});
|
|
33136
33371
|
import chalk62 from "chalk";
|
|
33137
|
-
import { existsSync as
|
|
33372
|
+
import { existsSync as existsSync29 } from "fs";
|
|
33138
33373
|
async function handler35(args, _ctx) {
|
|
33139
33374
|
const sub = args[0] ?? "list";
|
|
33140
33375
|
if (sub === "list" || !args[0]) {
|
|
@@ -33241,12 +33476,12 @@ function showSession(idArg) {
|
|
|
33241
33476
|
console.log();
|
|
33242
33477
|
const transcriptPath = transcriptPathForSession(session.id);
|
|
33243
33478
|
const contextPath = contextDocPathForSession(session.id);
|
|
33244
|
-
if (
|
|
33479
|
+
if (existsSync29(transcriptPath) || existsSync29(contextPath)) {
|
|
33245
33480
|
console.log(" " + chalk62.dim("\u2500".repeat(40)));
|
|
33246
|
-
if (
|
|
33481
|
+
if (existsSync29(contextPath)) {
|
|
33247
33482
|
console.log(" " + chalk62.dim("Context brief: ") + chalk62.dim(contextPath));
|
|
33248
33483
|
}
|
|
33249
|
-
if (
|
|
33484
|
+
if (existsSync29(transcriptPath)) {
|
|
33250
33485
|
console.log(" " + chalk62.dim("Full transcript: ") + chalk62.dim(transcriptPath));
|
|
33251
33486
|
}
|
|
33252
33487
|
console.log();
|
|
@@ -33518,7 +33753,7 @@ var init_privacy_notice = __esm({
|
|
|
33518
33753
|
"Computed scores and dollar aggregates still go to the provider you connected.",
|
|
33519
33754
|
"If you turn on web retrieval, named-account queries are refused. Generic GTM terms may go to Tavily or Brave.",
|
|
33520
33755
|
"A custom --base-url receives the same tokenized payload.",
|
|
33521
|
-
"MCP hosts (for example Claude Desktop) see tokens, not account names. Use the CLI to read real names.",
|
|
33756
|
+
"MCP hosts (for example Claude Desktop or Cursor) see tokens, not account names. Use the CLI to read real names.",
|
|
33522
33757
|
"Keys stay in ~/.ntrp/config.json on this machine (mode 600).",
|
|
33523
33758
|
"NTRP is a diagnostic. It does not change CRM records or send email.",
|
|
33524
33759
|
"Type /privacy to read this notice again."
|
|
@@ -34582,20 +34817,20 @@ var init_model = __esm({
|
|
|
34582
34817
|
});
|
|
34583
34818
|
|
|
34584
34819
|
// src/config/update-check.ts
|
|
34585
|
-
import { existsSync as
|
|
34820
|
+
import { existsSync as existsSync30, mkdirSync as mkdirSync18, readFileSync as readFileSync21, unlinkSync as unlinkSync5, writeFileSync as writeFileSync20 } from "fs";
|
|
34586
34821
|
import { join as join33 } from "path";
|
|
34587
34822
|
function cachePath2() {
|
|
34588
34823
|
return join33(ntrpHome(), "update-check.json");
|
|
34589
34824
|
}
|
|
34590
34825
|
function ensureDir7() {
|
|
34591
34826
|
const dir = ntrpHome();
|
|
34592
|
-
if (!
|
|
34827
|
+
if (!existsSync30(dir)) {
|
|
34593
34828
|
mkdirSync18(dir, { recursive: true });
|
|
34594
34829
|
}
|
|
34595
34830
|
}
|
|
34596
34831
|
function loadUpdateCheckCache() {
|
|
34597
34832
|
const path = cachePath2();
|
|
34598
|
-
if (!
|
|
34833
|
+
if (!existsSync30(path)) return null;
|
|
34599
34834
|
try {
|
|
34600
34835
|
const parsed = JSON.parse(readFileSync21(path, "utf-8"));
|
|
34601
34836
|
if (!parsed || typeof parsed !== "object" || typeof parsed.lastCheck !== "number" || typeof parsed.latestVersion !== "string") {
|
|
@@ -34616,7 +34851,7 @@ function isCacheFresh(cache2, ttlMs = CACHE_TTL_MS2) {
|
|
|
34616
34851
|
}
|
|
34617
34852
|
function invalidateUpdateCheckCache() {
|
|
34618
34853
|
const path = cachePath2();
|
|
34619
|
-
if (
|
|
34854
|
+
if (existsSync30(path)) {
|
|
34620
34855
|
unlinkSync5(path);
|
|
34621
34856
|
}
|
|
34622
34857
|
}
|
|
@@ -34630,11 +34865,11 @@ var init_update_check = __esm({
|
|
|
34630
34865
|
});
|
|
34631
34866
|
|
|
34632
34867
|
// src/version.ts
|
|
34633
|
-
import { existsSync as
|
|
34868
|
+
import { existsSync as existsSync31, readFileSync as readFileSync22 } from "fs";
|
|
34634
34869
|
import { dirname as dirname6, join as join34 } from "path";
|
|
34635
34870
|
import { fileURLToPath } from "url";
|
|
34636
34871
|
function readVersionFromPackageJson(packageJsonPath) {
|
|
34637
|
-
if (!
|
|
34872
|
+
if (!existsSync31(packageJsonPath)) return null;
|
|
34638
34873
|
try {
|
|
34639
34874
|
const pkg = JSON.parse(readFileSync22(packageJsonPath, "utf-8"));
|
|
34640
34875
|
if (typeof pkg.version === "string" && pkg.version.length > 0) return pkg.version;
|
|
@@ -34774,7 +35009,7 @@ __export(relaunch_exports, {
|
|
|
34774
35009
|
resolveRelaunchEntry: () => resolveRelaunchEntry,
|
|
34775
35010
|
updateRestartSummary: () => updateRestartSummary
|
|
34776
35011
|
});
|
|
34777
|
-
import { existsSync as
|
|
35012
|
+
import { existsSync as existsSync32 } from "fs";
|
|
34778
35013
|
import { join as join35 } from "path";
|
|
34779
35014
|
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
34780
35015
|
import { spawnSync } from "child_process";
|
|
@@ -34800,7 +35035,7 @@ function npmGlobalEntry() {
|
|
|
34800
35035
|
const listed = spawnSync("npm", ["root", "-g"], { encoding: "utf-8" });
|
|
34801
35036
|
if (listed.status !== 0) return null;
|
|
34802
35037
|
const entry = join35(listed.stdout.trim(), NPM_PACKAGE, "dist/index.js");
|
|
34803
|
-
return
|
|
35038
|
+
return existsSync32(entry) ? entry : null;
|
|
34804
35039
|
}
|
|
34805
35040
|
function thisBundleEntry() {
|
|
34806
35041
|
return fileURLToPath2(import.meta.url);
|
|
@@ -34810,10 +35045,10 @@ function resolveRelaunchEntry(toVersion) {
|
|
|
34810
35045
|
(p) => Boolean(p)
|
|
34811
35046
|
);
|
|
34812
35047
|
for (const entry of candidates) {
|
|
34813
|
-
if (!
|
|
35048
|
+
if (!existsSync32(entry)) continue;
|
|
34814
35049
|
if (readVersionNearEntry(entry) === toVersion) return entry;
|
|
34815
35050
|
}
|
|
34816
|
-
return candidates.find((p) =>
|
|
35051
|
+
return candidates.find((p) => existsSync32(p)) ?? thisBundleEntry();
|
|
34817
35052
|
}
|
|
34818
35053
|
function relaunchArgv(toVersion) {
|
|
34819
35054
|
return [resolveRelaunchEntry(toVersion)];
|
|
@@ -35336,7 +35571,7 @@ __export(exports_exports, {
|
|
|
35336
35571
|
handler: () => handler48
|
|
35337
35572
|
});
|
|
35338
35573
|
import chalk78 from "chalk";
|
|
35339
|
-
import { existsSync as
|
|
35574
|
+
import { existsSync as existsSync33 } from "fs";
|
|
35340
35575
|
import { join as join36 } from "path";
|
|
35341
35576
|
function usage4() {
|
|
35342
35577
|
console.log(chalk78.dim(" Usage:"));
|
|
@@ -35448,7 +35683,7 @@ async function runInboxSet(args, ctx) {
|
|
|
35448
35683
|
const pathArg = args.join(" ").trim();
|
|
35449
35684
|
if (!pathArg) {
|
|
35450
35685
|
console.error(chalk78.red(" Usage: /inbox set <path>"));
|
|
35451
|
-
console.error(chalk78.dim(" Example: /inbox set ~/Documents/
|
|
35686
|
+
console.error(chalk78.dim(" Example: /inbox set ~/Documents/ntrp-inbox"));
|
|
35452
35687
|
if (ctx.oneShot) process.exit(1);
|
|
35453
35688
|
return;
|
|
35454
35689
|
}
|
|
@@ -35478,7 +35713,7 @@ async function runInboxSet(args, ctx) {
|
|
|
35478
35713
|
console.log();
|
|
35479
35714
|
console.log(" " + paint("accent", "AI inbox set"));
|
|
35480
35715
|
console.log(" " + chalk78.dim(setTo));
|
|
35481
|
-
console.log(" " + chalk78.dim("Point Claude
|
|
35716
|
+
console.log(" " + chalk78.dim("Point Claude, ChatGPT, Cursor, or another desktop AI at this folder."));
|
|
35482
35717
|
if (n > 0) {
|
|
35483
35718
|
console.log(" " + chalk78.dim(`Synced ${n} recent export${n === 1 ? "" : "s"} into the inbox.`));
|
|
35484
35719
|
}
|
|
@@ -35495,7 +35730,7 @@ function runMove(args, ctx) {
|
|
|
35495
35730
|
}
|
|
35496
35731
|
try {
|
|
35497
35732
|
const destDir = resolveUserPath(dest);
|
|
35498
|
-
if (!
|
|
35733
|
+
if (!existsSync33(destDir)) {
|
|
35499
35734
|
}
|
|
35500
35735
|
const event = moveExport(idOrName, destDir);
|
|
35501
35736
|
console.log();
|
|
@@ -35686,7 +35921,7 @@ async function importHandler(runtimePath) {
|
|
|
35686
35921
|
case "../commands/publish.js":
|
|
35687
35922
|
return Promise.resolve().then(() => (init_publish2(), publish_exports));
|
|
35688
35923
|
case "../commands/profile.js":
|
|
35689
|
-
return Promise.resolve().then(() => (init_profile2(),
|
|
35924
|
+
return Promise.resolve().then(() => (init_profile2(), profile_exports2));
|
|
35690
35925
|
case "../commands/config.js":
|
|
35691
35926
|
return Promise.resolve().then(() => (init_config(), config_exports));
|
|
35692
35927
|
case "../commands/activate.js":
|
|
@@ -35848,12 +36083,12 @@ args: [show|set <path>|skill|clear]
|
|
|
35848
36083
|
handler: ../commands/exports.ts
|
|
35849
36084
|
---
|
|
35850
36085
|
|
|
35851
|
-
Set a folder that
|
|
36086
|
+
Set a folder that any desktop AI can read (Claude, ChatGPT, Cursor, \u2026).
|
|
35852
36087
|
NTRP copies each handoff to that folder.
|
|
35853
36088
|
NTRP overwrites \`latest-handoff.md\` and \`latest-handoff-deck.md\`. The app then finds the newest file.
|
|
35854
36089
|
NTRP also writes \`SKILL.md\`. That file holds finder instructions with your paths.
|
|
35855
36090
|
Demo setup offers this once. Loading your own data (CSV ingest or \`/onboard\`) asks again if the folder is not set. Skip on that production step and NTRP will not ask again. Type \`/inbox skill\` or \`/handoff skill\` to print the finder. Type \`/inbox set\` at any time.
|
|
35856
|
-
Paste that skill once into Claude, ChatGPT, or
|
|
36091
|
+
Paste that skill once into Claude, ChatGPT, Cursor, or another desktop AI. Later handoffs need no new paste.
|
|
35857
36092
|
\`INDEX.md\` in that folder links to the archive.
|
|
35858
36093
|
A path outside ~/.ntrp needs a confirm. Handoffs carry analysis text.
|
|
35859
36094
|
\`clear\` does not delete files. It only removes the config pointer.`
|
|
@@ -36204,9 +36439,9 @@ args: [<metric>|guide|list|tour]
|
|
|
36204
36439
|
handler: ../commands/deepdive.ts
|
|
36205
36440
|
---
|
|
36206
36441
|
|
|
36207
|
-
CLI slide deck: SaaS refresher, five vital signs (definition, formula, visual, dollar translation), then a how-to section (talk, ask, ship to
|
|
36208
|
-
Bare \`/deepdive\` runs the full onboarding tour. Type \`/deepdive guide\` to jump to how to use NTRP (includes teaching
|
|
36209
|
-
Type \`/deepdive handoff\` for the ship-to-
|
|
36442
|
+
CLI slide deck: SaaS refresher, five vital signs (definition, formula, visual, dollar translation), then a how-to section (talk, ask, ship to your AI, loop).
|
|
36443
|
+
Bare \`/deepdive\` runs the full onboarding tour. Type \`/deepdive guide\` to jump to how to use NTRP (includes teaching the inbox once).
|
|
36444
|
+
Type \`/deepdive handoff\` for the ship-to-AI slide. Type \`/deepdive <metric>\` to jump to one metric card.
|
|
36210
36445
|
Type \`/deepdive list\` to print the catalog. Works without an AI key. Re-run anytime from the homescreen.
|
|
36211
36446
|
Live values overlay when an analysis exists.`
|
|
36212
36447
|
},
|
|
@@ -36410,6 +36645,7 @@ Manage CLI configuration stored at \`~/.ntrp/config.json\`. Useful keys:
|
|
|
36410
36645
|
\`api-key\` (Anthropic), \`openai-api-key\` (and \`groq-api-key\`, \`google-api-key\`, ...),
|
|
36411
36646
|
\`llm-primary\` (default provider), \`llm-tier\`, \`llm-auto-failover\`,
|
|
36412
36647
|
\`voice-personality\`, \`voice-roast\`,
|
|
36648
|
+
\`sales-motion\`, \`rep_hourly_cost\`, \`hours_per_activity\`,
|
|
36413
36649
|
\`default-format\`, \`export-dir\`, \`ai-inbox-dir\` (or type \`/inbox set\`).
|
|
36414
36650
|
|
|
36415
36651
|
Setting a provider key opens a hidden prompt and auto-discovers that provider's models.
|
|
@@ -36527,7 +36763,7 @@ Remaining nuances merge into a custom_context paragraph that flows into all AI s
|
|
|
36527
36763
|
});
|
|
36528
36764
|
|
|
36529
36765
|
// src/ai/prompt-parts.ts
|
|
36530
|
-
import { existsSync as
|
|
36766
|
+
import { existsSync as existsSync34, readFileSync as readFileSync23 } from "fs";
|
|
36531
36767
|
import { join as join37 } from "path";
|
|
36532
36768
|
function buildCompanyProfileBlock() {
|
|
36533
36769
|
const p = loadProfile();
|
|
@@ -36548,7 +36784,7 @@ function buildCompanyProfileBlock() {
|
|
|
36548
36784
|
function loadAnalystFile() {
|
|
36549
36785
|
const path = join37(ntrpHome(), ANALYST_FILE_NAME);
|
|
36550
36786
|
try {
|
|
36551
|
-
if (!
|
|
36787
|
+
if (!existsSync34(path)) return null;
|
|
36552
36788
|
const raw = sanitizeExternalText(readFileSync23(path, "utf-8").trim());
|
|
36553
36789
|
if (!raw) return null;
|
|
36554
36790
|
if (raw.length <= ANALYST_FILE_MAX_CHARS) return raw;
|
|
@@ -37054,7 +37290,7 @@ __export(ingest_chat_exports, {
|
|
|
37054
37290
|
loadDemoFromChat: () => loadDemoFromChat,
|
|
37055
37291
|
looksLikeFilePath: () => looksLikeFilePath
|
|
37056
37292
|
});
|
|
37057
|
-
import { existsSync as
|
|
37293
|
+
import { existsSync as existsSync35 } from "fs";
|
|
37058
37294
|
import { basename as basename9, resolve as resolve9 } from "path";
|
|
37059
37295
|
import { homedir as homedir8 } from "os";
|
|
37060
37296
|
import chalk80 from "chalk";
|
|
@@ -37074,11 +37310,11 @@ function extractFilePath(input) {
|
|
|
37074
37310
|
const m = trimmed.match(re);
|
|
37075
37311
|
if (m?.[1]) {
|
|
37076
37312
|
const p = expandPath(m[1]);
|
|
37077
|
-
if (
|
|
37313
|
+
if (existsSync35(p)) return p;
|
|
37078
37314
|
}
|
|
37079
37315
|
if (!m?.[1] && re.test(trimmed) && trimmed.toLowerCase().endsWith(".csv")) {
|
|
37080
37316
|
const p = expandPath(trimmed.replace(/^["']|["']$/g, ""));
|
|
37081
|
-
if (
|
|
37317
|
+
if (existsSync35(p)) return p;
|
|
37082
37318
|
}
|
|
37083
37319
|
}
|
|
37084
37320
|
return null;
|