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