@sonnechasser/ntrp 1.3.5 → 1.3.6
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/LICENSE +24 -0
- package/README.md +4 -2
- package/dist/index.js +983 -923
- package/dist/mcp/server.js +719 -48
- package/package.json +6 -3
- package/dist/ai/findings-stream-smoke.js +0 -185
- package/dist/ai/findings-stream-smoke.js.map +0 -1
- package/dist/ai/guardrails-smoke.js +0 -25584
- package/dist/ai/guardrails-smoke.js.map +0 -1
- package/dist/conversation/deepdive-smoke.js +0 -3416
- package/dist/conversation/deepdive-smoke.js.map +0 -1
- package/dist/conversation/loop-guard-smoke.js +0 -37627
- package/dist/conversation/loop-guard-smoke.js.map +0 -1
- package/dist/demo/whimsy-smoke.js +0 -692
- package/dist/demo/whimsy-smoke.js.map +0 -1
- package/dist/index.js.map +0 -1
- package/dist/investigation/quality-eval-cli.js +0 -24564
- package/dist/investigation/quality-eval-cli.js.map +0 -1
- package/dist/investigation/verbosity-cli.js +0 -24373
- package/dist/investigation/verbosity-cli.js.map +0 -1
- package/dist/mcp/server.js.map +0 -1
- package/dist/services/exports-registry-smoke.js +0 -1205
- package/dist/services/exports-registry-smoke.js.map +0 -1
- package/dist/services/transcript-smoke.js +0 -1101
- package/dist/services/transcript-smoke.js.map +0 -1
- package/dist/strategist/strategist-smoke.js +0 -3073
- package/dist/strategist/strategist-smoke.js.map +0 -1
- package/dist/whimsy/time-bank-smoke.js +0 -36126
- package/dist/whimsy/time-bank-smoke.js.map +0 -1
package/dist/mcp/server.js
CHANGED
|
@@ -527,6 +527,16 @@ var init_terminal_capture = __esm({
|
|
|
527
527
|
// src/output/redact-write.ts
|
|
528
528
|
import { mkdirSync as mkdirSync2, writeFileSync as writeFileSync2 } from "fs";
|
|
529
529
|
import { dirname } from "path";
|
|
530
|
+
function redactExportText(content) {
|
|
531
|
+
return redactSecrets(content);
|
|
532
|
+
}
|
|
533
|
+
function writeRedactedText(path, content) {
|
|
534
|
+
mkdirSync2(dirname(path), { recursive: true });
|
|
535
|
+
writeFileSync2(path, redactExportText(content), "utf-8");
|
|
536
|
+
}
|
|
537
|
+
function isRedactableExportPath(path) {
|
|
538
|
+
return /\.(md|markdown|txt)$/i.test(path);
|
|
539
|
+
}
|
|
530
540
|
var init_redact_write = __esm({
|
|
531
541
|
"src/output/redact-write.ts"() {
|
|
532
542
|
"use strict";
|
|
@@ -537,6 +547,17 @@ var init_redact_write = __esm({
|
|
|
537
547
|
// src/output/path-safety.ts
|
|
538
548
|
import { homedir as homedir2 } from "os";
|
|
539
549
|
import { resolve as resolve2, sep } from "path";
|
|
550
|
+
function resolveUserPath(path) {
|
|
551
|
+
if (path === "~" || path.startsWith("~/") || path.startsWith("~\\")) {
|
|
552
|
+
return resolve2(homedir2(), path.slice(2));
|
|
553
|
+
}
|
|
554
|
+
return resolve2(path);
|
|
555
|
+
}
|
|
556
|
+
function isInsideNtrp(path) {
|
|
557
|
+
const home = ntrpHome();
|
|
558
|
+
const resolved = resolve2(path);
|
|
559
|
+
return resolved === home || resolved.startsWith(home + sep);
|
|
560
|
+
}
|
|
540
561
|
var NTRP_HOME;
|
|
541
562
|
var init_path_safety = __esm({
|
|
542
563
|
"src/output/path-safety.ts"() {
|
|
@@ -547,6 +568,28 @@ var init_path_safety = __esm({
|
|
|
547
568
|
});
|
|
548
569
|
|
|
549
570
|
// src/services/export-kinds.ts
|
|
571
|
+
function latestBasenameForKind(kind) {
|
|
572
|
+
if (kind.startsWith("prompt:")) {
|
|
573
|
+
const target = kind.slice("prompt:".length);
|
|
574
|
+
return target ? `handoff-${target}.md` : "handoff.md";
|
|
575
|
+
}
|
|
576
|
+
if (kind === "report") return "report.md";
|
|
577
|
+
if (kind === "notes") return "notes.md";
|
|
578
|
+
if (kind === "csv") return "csv";
|
|
579
|
+
if (kind === "publish") return "publish";
|
|
580
|
+
return "handoff.md";
|
|
581
|
+
}
|
|
582
|
+
function inboxLatestNameForKind(kind) {
|
|
583
|
+
if (kind.startsWith("prompt:")) {
|
|
584
|
+
const target = kind.slice("prompt:".length);
|
|
585
|
+
return target ? `latest-handoff-${target}.md` : "latest-handoff.md";
|
|
586
|
+
}
|
|
587
|
+
if (kind === "report") return "latest-report.md";
|
|
588
|
+
if (kind === "notes") return "latest-notes.md";
|
|
589
|
+
if (kind === "csv") return "latest-csv";
|
|
590
|
+
if (kind === "publish") return "latest-publish";
|
|
591
|
+
return "latest-handoff.md";
|
|
592
|
+
}
|
|
550
593
|
var init_export_kinds = __esm({
|
|
551
594
|
"src/services/export-kinds.ts"() {
|
|
552
595
|
"use strict";
|
|
@@ -646,6 +689,243 @@ import { mkdirSync as mkdirSync3 } from "fs";
|
|
|
646
689
|
import { basename, join as join2 } from "path";
|
|
647
690
|
import { homedir as homedir3 } from "os";
|
|
648
691
|
import chalk2 from "chalk";
|
|
692
|
+
function defaultAiInboxDir() {
|
|
693
|
+
return join2(homedir3(), "Documents", "Claude", "ntrp-inbox");
|
|
694
|
+
}
|
|
695
|
+
function handoffLocations() {
|
|
696
|
+
const archiveRoot = getExportsDir();
|
|
697
|
+
const archiveLatestDir = join2(archiveRoot, "latest");
|
|
698
|
+
const inboxDir = getConfiguredAiInboxDir();
|
|
699
|
+
return {
|
|
700
|
+
archiveRoot,
|
|
701
|
+
archiveIndex: join2(archiveRoot, "INDEX.md"),
|
|
702
|
+
archiveLatestDir,
|
|
703
|
+
archiveSkill: join2(archiveLatestDir, STANDING_SKILL_NAME),
|
|
704
|
+
archivePickup: join2(archiveLatestDir, ARCHIVE_PICKUP_NAME),
|
|
705
|
+
inboxDir,
|
|
706
|
+
inboxIndex: inboxDir ? join2(inboxDir, "INDEX.md") : null,
|
|
707
|
+
inboxSkill: inboxDir ? join2(inboxDir, STANDING_SKILL_NAME) : null,
|
|
708
|
+
inboxPickup: inboxDir ? join2(inboxDir, INBOX_PICKUP_NAME) : null
|
|
709
|
+
};
|
|
710
|
+
}
|
|
711
|
+
function pickupContextFromEvent(event) {
|
|
712
|
+
const loc = handoffLocations();
|
|
713
|
+
const writtenAt = event.at || (/* @__PURE__ */ new Date()).toISOString();
|
|
714
|
+
const dateUtc = writtenAt.slice(0, 10);
|
|
715
|
+
const timeUtc = writtenAt.slice(11, 16);
|
|
716
|
+
const inboxLatest = loc.inboxDir ? join2(loc.inboxDir, inboxLatestNameForKind(event.kind)) : null;
|
|
717
|
+
const inboxGenericLatest = loc.inboxDir && event.kind.startsWith("prompt:") ? join2(loc.inboxDir, "latest-handoff.md") : inboxLatest;
|
|
718
|
+
return {
|
|
719
|
+
...loc,
|
|
720
|
+
kind: event.kind,
|
|
721
|
+
title: event.title,
|
|
722
|
+
writtenAt,
|
|
723
|
+
archivePath: event.path,
|
|
724
|
+
archiveLatest: join2(loc.archiveLatestDir, latestBasenameForKind(event.kind)),
|
|
725
|
+
inboxLatest,
|
|
726
|
+
inboxGenericLatest,
|
|
727
|
+
datedBasename: basename(event.path),
|
|
728
|
+
dateUtc,
|
|
729
|
+
timeUtc
|
|
730
|
+
};
|
|
731
|
+
}
|
|
732
|
+
function kindLabel(kind) {
|
|
733
|
+
if (kind.startsWith("prompt:")) {
|
|
734
|
+
const target = kind.slice("prompt:".length);
|
|
735
|
+
if (target === "deck") return "deck prompt";
|
|
736
|
+
if (target === "plan") return "action-plan prompt";
|
|
737
|
+
if (target === "asana") return "Asana project prompt";
|
|
738
|
+
if (target === "clay") return "Clay table prompt";
|
|
739
|
+
return target ? `${target} prompt` : "agent prompt";
|
|
740
|
+
}
|
|
741
|
+
if (kind === "report") return "markdown report";
|
|
742
|
+
if (kind === "notes") return "notes export";
|
|
743
|
+
if (kind === "csv") return "CSV receipts";
|
|
744
|
+
if (kind === "publish") return "repository export";
|
|
745
|
+
return kind;
|
|
746
|
+
}
|
|
747
|
+
function jobForKind(kind) {
|
|
748
|
+
if (kind === "prompt:deck") {
|
|
749
|
+
return "Open that file and follow its instructions to build an executive review deck. Do not invent numbers.";
|
|
750
|
+
}
|
|
751
|
+
if (kind === "prompt:plan") {
|
|
752
|
+
return "Open that file and follow its instructions to build a prioritized action plan. Do not invent numbers.";
|
|
753
|
+
}
|
|
754
|
+
if (kind === "prompt:asana") {
|
|
755
|
+
return "Open that file and follow its instructions to create the Asana project (sections + tasks). Do not invent numbers.";
|
|
756
|
+
}
|
|
757
|
+
if (kind === "prompt:clay") {
|
|
758
|
+
return "Open that file and follow its instructions to spec the Clay table. Do not invent numbers.";
|
|
759
|
+
}
|
|
760
|
+
if (kind.startsWith("prompt:")) {
|
|
761
|
+
return "Open that file and follow its instructions to build the deliverable. Do not invent numbers.";
|
|
762
|
+
}
|
|
763
|
+
if (kind === "report") {
|
|
764
|
+
return "Open the markdown report. Brief or restyle it as asked; do not invent numbers.";
|
|
765
|
+
}
|
|
766
|
+
if (kind === "notes") {
|
|
767
|
+
return "Open the notes file (Obsidian-style GTM write-up). Use it as source material; do not invent numbers.";
|
|
768
|
+
}
|
|
769
|
+
if (kind === "csv") {
|
|
770
|
+
return "Open the CSV receipts folder (cover-sheet.csv plus per-vital evidence). Use those files as source data; do not invent numbers.";
|
|
771
|
+
}
|
|
772
|
+
if (kind === "publish") {
|
|
773
|
+
return "Open the repository export package folder and work from the files inside.";
|
|
774
|
+
}
|
|
775
|
+
return "Open the file and use it as source material. Do not invent numbers.";
|
|
776
|
+
}
|
|
777
|
+
function filenamePattern(ctx) {
|
|
778
|
+
const base = ctx.datedBasename;
|
|
779
|
+
const dot = base.lastIndexOf(".");
|
|
780
|
+
if (dot <= 0) return `${base}*`;
|
|
781
|
+
const stem = base.slice(0, dot);
|
|
782
|
+
const ext = base.slice(dot);
|
|
783
|
+
const datePrefix = stem.includes(ctx.dateUtc) ? `${stem.split(ctx.dateUtc)[0]}${ctx.dateUtc}` : stem.slice(0, 12);
|
|
784
|
+
return `${datePrefix}*${ext}`;
|
|
785
|
+
}
|
|
786
|
+
function buildStandingSkillMarkdown(loc = handoffLocations()) {
|
|
787
|
+
const inboxBlock = loc.inboxDir ? `Inbox (preferred \u2014 point Claude Desktop / a project / Cursor at this folder):
|
|
788
|
+
\`${loc.inboxDir}\`
|
|
789
|
+
|
|
790
|
+
Start with:
|
|
791
|
+
- \`SKILL.md\` \u2014 this file
|
|
792
|
+
- \`latest-pickup.md\` \u2014 the handoff that was just written (date + exact paths)
|
|
793
|
+
- \`latest-handoff.md\` / \`latest-handoff-<target>.md\` \u2014 newest agent prompt
|
|
794
|
+
- \`latest-report.md\`, \`latest-notes.md\`, \`latest-csv\` \u2014 other kinds
|
|
795
|
+
- \`INDEX.md\` \u2014 catalog with timestamps
|
|
796
|
+
- \`archive/\` \u2014 dated copies` : `No AI inbox is configured yet. Canonical archive (always written):
|
|
797
|
+
\`${loc.archiveRoot}\`
|
|
798
|
+
|
|
799
|
+
Ask the operator to run \`/inbox set <folder>\` in ntrp so copies land in a folder you can see. Until then, use the archive paths below.`;
|
|
800
|
+
return `---
|
|
801
|
+
name: ntrp-handoff
|
|
802
|
+
description: Find and execute NTRP GTM analysis handoffs (deck, plan, Asana, Clay, report, notes, CSV) from the local inbox or exports archive. Use when the user mentions an NTRP handoff, board deck, action plan, or a file ntrp just wrote.
|
|
803
|
+
---
|
|
804
|
+
|
|
805
|
+
# Find an NTRP handoff
|
|
806
|
+
|
|
807
|
+
NTRP writes GTM analysis deliverables to disk. Your job is to open the file and follow it \u2014 do not invent numbers.
|
|
808
|
+
|
|
809
|
+
## Where to look (this machine)
|
|
810
|
+
|
|
811
|
+
${inboxBlock}
|
|
812
|
+
|
|
813
|
+
Canonical archive:
|
|
814
|
+
\`${loc.archiveRoot}\`
|
|
815
|
+
|
|
816
|
+
- \`latest/SKILL.md\` \u2014 this finder
|
|
817
|
+
- \`latest/pickup.md\` \u2014 the handoff that was just written
|
|
818
|
+
- \`latest/handoff.md\` / \`latest/handoff-<target>.md\` \u2014 newest prompt
|
|
819
|
+
- \`INDEX.md\` \u2014 catalog with timestamps and move history
|
|
820
|
+
- \`handoffs/\`, \`reports/\`, \`notes/\`, \`csv/\`, \`publish/\` \u2014 dated files by kind
|
|
821
|
+
|
|
822
|
+
## How to pick the file
|
|
823
|
+
|
|
824
|
+
1. If they just ran a handoff, open \`latest-pickup.md\` (inbox) or \`latest/pickup.md\` (archive). It names the exact file and date.
|
|
825
|
+
2. Otherwise prefer the stable pointer for what they asked for:
|
|
826
|
+
- deck / slides \u2192 \`latest-handoff-deck.md\` (inbox) or \`latest/handoff-deck.md\` (archive)
|
|
827
|
+
- action plan \u2192 \`latest-handoff-plan.md\`
|
|
828
|
+
- Asana \u2192 \`latest-handoff-asana.md\`
|
|
829
|
+
- Clay \u2192 \`latest-handoff-clay.md\`
|
|
830
|
+
- any prompt \u2192 \`latest-handoff.md\` / \`latest/handoff.md\`
|
|
831
|
+
- report / notes / CSV \u2192 \`latest-report.md\`, \`latest-notes.md\`, \`latest-csv\`
|
|
832
|
+
3. If they mention a date, open \`INDEX.md\` and pick the newest row on that UTC date. Dated filenames look like \`handoff-deck-2026-08-13-150123.md\`.
|
|
833
|
+
4. If none of those paths are in your workspace, ask them to attach the file or to \`/inbox set\` a folder you can read.
|
|
834
|
+
|
|
835
|
+
You are not given a fresh path on every handoff. Prefer the stable \`latest-*\` pointers.
|
|
836
|
+
|
|
837
|
+
Then execute the instructions in that file.
|
|
838
|
+
`;
|
|
839
|
+
}
|
|
840
|
+
function buildPickupPrompt(ctx) {
|
|
841
|
+
const lines = [
|
|
842
|
+
`Find the NTRP GTM handoff written ${ctx.dateUtc} at ${ctx.timeUtc} UTC.`,
|
|
843
|
+
`Kind: ${kindLabel(ctx.kind)}${ctx.title ? ` (${ctx.title})` : ""}.`,
|
|
844
|
+
"",
|
|
845
|
+
jobForKind(ctx.kind),
|
|
846
|
+
"",
|
|
847
|
+
"Look in this order (this machine):",
|
|
848
|
+
""
|
|
849
|
+
];
|
|
850
|
+
let n = 1;
|
|
851
|
+
if (ctx.inboxLatest) {
|
|
852
|
+
lines.push(`${n}. Inbox pointer: ${ctx.inboxLatest}`);
|
|
853
|
+
n++;
|
|
854
|
+
}
|
|
855
|
+
if (ctx.inboxGenericLatest && ctx.inboxGenericLatest !== ctx.inboxLatest) {
|
|
856
|
+
lines.push(`${n}. Inbox generic: ${ctx.inboxGenericLatest}`);
|
|
857
|
+
n++;
|
|
858
|
+
}
|
|
859
|
+
lines.push(`${n}. Archive pointer: ${ctx.archiveLatest}`);
|
|
860
|
+
n++;
|
|
861
|
+
lines.push(`${n}. Dated file: ${ctx.archivePath}`);
|
|
862
|
+
lines.push(
|
|
863
|
+
"",
|
|
864
|
+
"If those paths are not in your workspace:",
|
|
865
|
+
`- Open INDEX.md in ${ctx.inboxDir ?? ctx.archiveRoot}`,
|
|
866
|
+
`- Pick the newest row dated ${ctx.dateUtc} matching ${ctx.kind}`,
|
|
867
|
+
`- Or search for ${filenamePattern(ctx)}`,
|
|
868
|
+
"",
|
|
869
|
+
"Standing finder skill (same folders, install once):",
|
|
870
|
+
`- ${ctx.inboxSkill ?? ctx.archiveSkill}`
|
|
871
|
+
);
|
|
872
|
+
if (ctx.inboxSkill) {
|
|
873
|
+
lines.push(`- ${ctx.archiveSkill}`);
|
|
874
|
+
} else {
|
|
875
|
+
lines.push("- No AI inbox yet \u2014 in ntrp run `/inbox set <folder>` so copies land where your agent can see them.");
|
|
876
|
+
}
|
|
877
|
+
return lines.join("\n") + "\n";
|
|
878
|
+
}
|
|
879
|
+
function persistStandingSkill(loc = handoffLocations()) {
|
|
880
|
+
mkdirSync3(loc.archiveLatestDir, { recursive: true });
|
|
881
|
+
const md = buildStandingSkillMarkdown(loc);
|
|
882
|
+
writeRedactedText(loc.archiveSkill, md);
|
|
883
|
+
if (loc.inboxDir && loc.inboxSkill) {
|
|
884
|
+
mkdirSync3(loc.inboxDir, { recursive: true });
|
|
885
|
+
writeRedactedText(loc.inboxSkill, md);
|
|
886
|
+
}
|
|
887
|
+
}
|
|
888
|
+
function persistHandoffSkillFiles(event) {
|
|
889
|
+
const ctx = pickupContextFromEvent(event);
|
|
890
|
+
persistStandingSkill(ctx);
|
|
891
|
+
writeRedactedText(ctx.archivePickup, buildPickupPrompt(ctx));
|
|
892
|
+
if (ctx.inboxDir && ctx.inboxPickup) {
|
|
893
|
+
mkdirSync3(ctx.inboxDir, { recursive: true });
|
|
894
|
+
writeRedactedText(ctx.inboxPickup, buildPickupPrompt(ctx));
|
|
895
|
+
}
|
|
896
|
+
}
|
|
897
|
+
function printPlainBlock(text) {
|
|
898
|
+
const rule = " " + chalk2.dim("\u2500".repeat(60));
|
|
899
|
+
console.log(rule);
|
|
900
|
+
for (const line of text.replace(/\n$/, "").split("\n")) {
|
|
901
|
+
console.log(line.length > 0 ? ` ${line}` : " ");
|
|
902
|
+
}
|
|
903
|
+
console.log(rule);
|
|
904
|
+
}
|
|
905
|
+
function maybePrintAiInboxNudge() {
|
|
906
|
+
if (getConfiguredAiInboxDir()) return;
|
|
907
|
+
if (getConfigValue("ai-inbox-nudge-seen") === "true") return;
|
|
908
|
+
setConfigValue("ai-inbox-nudge-seen", "true");
|
|
909
|
+
console.log(
|
|
910
|
+
" " + chalk2.dim("Set a pickup folder. Type /inbox set ~/Documents/Claude/ntrp-inbox then /inbox skill")
|
|
911
|
+
);
|
|
912
|
+
}
|
|
913
|
+
function printStandingSkill() {
|
|
914
|
+
persistStandingSkill();
|
|
915
|
+
const loc = handoffLocations();
|
|
916
|
+
console.log();
|
|
917
|
+
console.log(" " + bold("NTRP handoff finder skill"));
|
|
918
|
+
console.log(
|
|
919
|
+
" " + chalk2.dim("Paste this skill once into Claude, ChatGPT, or Cursor. Later handoffs do not print it again.")
|
|
920
|
+
);
|
|
921
|
+
printPlainBlock(buildStandingSkillMarkdown(loc));
|
|
922
|
+
console.log(" " + chalk2.dim("Written: ") + (loc.inboxSkill ?? loc.archiveSkill));
|
|
923
|
+
if (!loc.inboxDir) {
|
|
924
|
+
maybePrintAiInboxNudge();
|
|
925
|
+
}
|
|
926
|
+
console.log();
|
|
927
|
+
}
|
|
928
|
+
var STANDING_SKILL_NAME, ARCHIVE_PICKUP_NAME, INBOX_PICKUP_NAME;
|
|
649
929
|
var init_handoff_skill = __esm({
|
|
650
930
|
"src/services/handoff-skill.ts"() {
|
|
651
931
|
"use strict";
|
|
@@ -653,6 +933,9 @@ var init_handoff_skill = __esm({
|
|
|
653
933
|
init_store();
|
|
654
934
|
init_theme();
|
|
655
935
|
init_export_kinds();
|
|
936
|
+
STANDING_SKILL_NAME = "SKILL.md";
|
|
937
|
+
ARCHIVE_PICKUP_NAME = "pickup.md";
|
|
938
|
+
INBOX_PICKUP_NAME = "latest-pickup.md";
|
|
656
939
|
}
|
|
657
940
|
});
|
|
658
941
|
|
|
@@ -693,10 +976,262 @@ function ensureExportsLayout(root = getExportsDir()) {
|
|
|
693
976
|
function getAiInboxDir() {
|
|
694
977
|
return getConfiguredAiInboxDir();
|
|
695
978
|
}
|
|
979
|
+
function setAiInboxDir(path) {
|
|
980
|
+
const resolved = resolveUserPath(path);
|
|
981
|
+
mkdirSync4(resolved, { recursive: true });
|
|
982
|
+
setConfigValue("ai-inbox-dir", resolved);
|
|
983
|
+
ensureInboxLayout(resolved);
|
|
984
|
+
persistStandingSkill();
|
|
985
|
+
setConfigValue("ai-inbox-nudge-seen", "true");
|
|
986
|
+
return resolved;
|
|
987
|
+
}
|
|
988
|
+
function ensureInboxLayout(inbox) {
|
|
989
|
+
mkdirSync4(inbox, { recursive: true });
|
|
990
|
+
mkdirSync4(join3(inbox, "archive"), { recursive: true });
|
|
991
|
+
const readme = join3(inbox, "README.md");
|
|
992
|
+
writeFileSync3(readme, buildInboxReadme(), "utf-8");
|
|
993
|
+
if (!existsSync2(join3(inbox, "INDEX.md"))) {
|
|
994
|
+
writeFileSync3(join3(inbox, "INDEX.md"), "# NTRP AI inbox\n\n_No exports synced yet._\n", "utf-8");
|
|
995
|
+
}
|
|
996
|
+
}
|
|
997
|
+
function manifestPath(root = getExportsDir()) {
|
|
998
|
+
return join3(root, "manifest.jsonl");
|
|
999
|
+
}
|
|
1000
|
+
function readManifestEvents(root = getExportsDir()) {
|
|
1001
|
+
const path = manifestPath(root);
|
|
1002
|
+
if (!existsSync2(path)) return [];
|
|
1003
|
+
const text = readFileSync2(path, "utf-8");
|
|
1004
|
+
const events = [];
|
|
1005
|
+
for (const line of text.split("\n")) {
|
|
1006
|
+
const trimmed = line.trim();
|
|
1007
|
+
if (!trimmed) continue;
|
|
1008
|
+
try {
|
|
1009
|
+
events.push(JSON.parse(trimmed));
|
|
1010
|
+
} catch {
|
|
1011
|
+
}
|
|
1012
|
+
}
|
|
1013
|
+
return events;
|
|
1014
|
+
}
|
|
1015
|
+
function appendManifestEvent(event, root = getExportsDir()) {
|
|
1016
|
+
ensureExportsLayout(root);
|
|
1017
|
+
appendFileSync(manifestPath(root), JSON.stringify(event) + "\n", "utf-8");
|
|
1018
|
+
}
|
|
1019
|
+
function listExports(opts = {}) {
|
|
1020
|
+
const limit = opts.limit ?? 20;
|
|
1021
|
+
const events = readManifestEvents();
|
|
1022
|
+
const byId2 = /* @__PURE__ */ new Map();
|
|
1023
|
+
for (const e of events) {
|
|
1024
|
+
if (e.op === "inbox_sync") continue;
|
|
1025
|
+
byId2.set(e.id, e);
|
|
1026
|
+
}
|
|
1027
|
+
let items = [...byId2.values()].sort((a, b) => a.at < b.at ? 1 : a.at > b.at ? -1 : 0);
|
|
1028
|
+
if (opts.kind) {
|
|
1029
|
+
const k = opts.kind.toLowerCase();
|
|
1030
|
+
items = items.filter((e) => e.kind === opts.kind || e.kind.startsWith(k) || e.kind.includes(k));
|
|
1031
|
+
}
|
|
1032
|
+
return items.slice(0, limit);
|
|
1033
|
+
}
|
|
1034
|
+
function copyPath(src, dest) {
|
|
1035
|
+
mkdirSync4(dirname2(dest), { recursive: true });
|
|
1036
|
+
if (existsSync2(dest)) {
|
|
1037
|
+
rmSync(dest, { recursive: true, force: true });
|
|
1038
|
+
}
|
|
1039
|
+
const st = statSync(src);
|
|
1040
|
+
if (st.isDirectory()) {
|
|
1041
|
+
cpSync(src, dest, { recursive: true });
|
|
1042
|
+
} else if (isRedactableExportPath(src)) {
|
|
1043
|
+
const text = readFileSync2(src, "utf-8");
|
|
1044
|
+
writeFileSync3(dest, redactExportText(text), "utf-8");
|
|
1045
|
+
} else {
|
|
1046
|
+
copyFileSync(src, dest);
|
|
1047
|
+
}
|
|
1048
|
+
}
|
|
1049
|
+
function regenerateIndex(root = getExportsDir()) {
|
|
1050
|
+
ensureExportsLayout(root);
|
|
1051
|
+
const items = listExports({ limit: 50 });
|
|
1052
|
+
const events = readManifestEvents(root);
|
|
1053
|
+
const withHistory = items.filter((e) => (e.previous_paths?.length ?? 0) > 0);
|
|
1054
|
+
const latestDir = join3(root, "latest");
|
|
1055
|
+
const latestLines = [];
|
|
1056
|
+
if (existsSync2(latestDir)) {
|
|
1057
|
+
for (const name of readdirSync(latestDir).sort()) {
|
|
1058
|
+
latestLines.push(`- \`latest/${name}\` \u2192 \`${join3(latestDir, name)}\``);
|
|
1059
|
+
}
|
|
1060
|
+
}
|
|
1061
|
+
const lines = [
|
|
1062
|
+
"# NTRP exports",
|
|
1063
|
+
"",
|
|
1064
|
+
`Archive root: \`${root}\``,
|
|
1065
|
+
"",
|
|
1066
|
+
"Set an inbox with `/onboard` or `/inbox set`. Paste `SKILL.md` once into Claude. Later handoffs overwrite `latest-handoff.md`.",
|
|
1067
|
+
"",
|
|
1068
|
+
"## Latest pointers",
|
|
1069
|
+
""
|
|
1070
|
+
];
|
|
1071
|
+
if (latestLines.length > 0) lines.push(...latestLines);
|
|
1072
|
+
else lines.push("_None yet._");
|
|
1073
|
+
lines.push("", "## Recent exports", "");
|
|
1074
|
+
if (items.length === 0) {
|
|
1075
|
+
lines.push("_No exports yet._");
|
|
1076
|
+
} else {
|
|
1077
|
+
for (const e of items) {
|
|
1078
|
+
const title = e.title ? ` \u2014 ${e.title}` : "";
|
|
1079
|
+
const session = e.session_id ? ` \xB7 session ${e.session_id.slice(-4)}` : "";
|
|
1080
|
+
lines.push(`- **${e.kind}** (${e.at})${title}${session}`);
|
|
1081
|
+
lines.push(` - id: \`${e.id}\``);
|
|
1082
|
+
lines.push(` - path: \`${e.path}\``);
|
|
1083
|
+
if (e.inbox_path) lines.push(` - inbox: \`${e.inbox_path}\``);
|
|
1084
|
+
}
|
|
1085
|
+
}
|
|
1086
|
+
lines.push("", "## Location history", "");
|
|
1087
|
+
if (withHistory.length === 0) {
|
|
1088
|
+
lines.push("_No moves recorded._");
|
|
1089
|
+
} else {
|
|
1090
|
+
for (const e of withHistory) {
|
|
1091
|
+
lines.push(`- **${e.kind}** \`${e.id}\``);
|
|
1092
|
+
for (const prev of e.previous_paths ?? []) {
|
|
1093
|
+
lines.push(` - was: \`${prev}\``);
|
|
1094
|
+
}
|
|
1095
|
+
lines.push(` - now: \`${e.path}\``);
|
|
1096
|
+
}
|
|
1097
|
+
}
|
|
1098
|
+
const moveOps = events.filter((e) => e.op === "move").slice(-20).reverse();
|
|
1099
|
+
if (moveOps.length > 0) {
|
|
1100
|
+
lines.push("", "## Recent moves", "");
|
|
1101
|
+
for (const e of moveOps) {
|
|
1102
|
+
const from = e.previous_paths?.[e.previous_paths.length - 1] ?? "?";
|
|
1103
|
+
lines.push(`- ${e.at}: \`${from}\` \u2192 \`${e.path}\` (${e.kind}, \`${e.id}\`)`);
|
|
1104
|
+
}
|
|
1105
|
+
}
|
|
1106
|
+
lines.push("");
|
|
1107
|
+
writeFileSync3(join3(root, "INDEX.md"), lines.join("\n"), "utf-8");
|
|
1108
|
+
}
|
|
1109
|
+
function regenerateInboxIndex(inbox) {
|
|
1110
|
+
ensureInboxLayout(inbox);
|
|
1111
|
+
const items = listExports({ limit: 15 });
|
|
1112
|
+
const archiveRoot = getExportsDir();
|
|
1113
|
+
const lines = [
|
|
1114
|
+
"# NTRP AI inbox",
|
|
1115
|
+
"",
|
|
1116
|
+
"Start here. Install `SKILL.md` once in Claude. Newest prompt: `latest-handoff.md`. Catalog: `INDEX.md`.",
|
|
1117
|
+
"",
|
|
1118
|
+
`Canonical archive: \`${archiveRoot}\` (see \`${join3(archiveRoot, "INDEX.md")}\`).`,
|
|
1119
|
+
"",
|
|
1120
|
+
"## Latest pointers",
|
|
1121
|
+
""
|
|
1122
|
+
];
|
|
1123
|
+
if (existsSync2(join3(inbox, "SKILL.md"))) {
|
|
1124
|
+
lines.push("- [`SKILL.md`](./SKILL.md) \u2014 standing finder. Paste once into your agent.");
|
|
1125
|
+
}
|
|
1126
|
+
const latestNames = readdirSync(inbox).filter((n) => n.startsWith("latest-")).sort();
|
|
1127
|
+
if (latestNames.length === 0 && !existsSync2(join3(inbox, "SKILL.md"))) {
|
|
1128
|
+
lines.push("_None yet. Run a handoff after `/inbox set`._");
|
|
1129
|
+
} else {
|
|
1130
|
+
for (const name of latestNames) {
|
|
1131
|
+
const note = name === "latest-pickup.md" ? " \u2014 names the file that was just written" : "";
|
|
1132
|
+
lines.push(`- [\`${name}\`](./${name})${note}`);
|
|
1133
|
+
}
|
|
1134
|
+
}
|
|
1135
|
+
lines.push("", "## Recent exports", "");
|
|
1136
|
+
if (items.length === 0) lines.push("_No exports yet._");
|
|
1137
|
+
else {
|
|
1138
|
+
for (const e of items) {
|
|
1139
|
+
lines.push(`- **${e.kind}** (${e.at}): \`${e.path}\``);
|
|
1140
|
+
if (e.inbox_path) lines.push(` - inbox copy: \`${e.inbox_path}\``);
|
|
1141
|
+
}
|
|
1142
|
+
}
|
|
1143
|
+
lines.push("");
|
|
1144
|
+
writeFileSync3(join3(inbox, "INDEX.md"), lines.join("\n"), "utf-8");
|
|
1145
|
+
writeFileSync3(join3(inbox, "README.md"), buildInboxReadme(), "utf-8");
|
|
1146
|
+
}
|
|
1147
|
+
function buildInboxReadme() {
|
|
1148
|
+
const archive = getExportsDir();
|
|
1149
|
+
return `# NTRP AI inbox
|
|
1150
|
+
|
|
1151
|
+
This folder is the landing folder for NTRP handoffs. Desktop AI tools read files here.
|
|
1152
|
+
|
|
1153
|
+
## Start here
|
|
1154
|
+
|
|
1155
|
+
1. Install \`SKILL.md\` once in Claude, ChatGPT, or Cursor. Then tell the agent to open the latest NTRP handoff.
|
|
1156
|
+
2. The newest prompt is \`latest-handoff.md\` (or \`latest-handoff-deck.md\` and similar).
|
|
1157
|
+
3. \`latest-pickup.md\` names the file that was just written.
|
|
1158
|
+
|
|
1159
|
+
Each export overwrites the \`latest-*\` files. Dated copies are in \`archive/\`.
|
|
1160
|
+
|
|
1161
|
+
## Canonical archive
|
|
1162
|
+
|
|
1163
|
+
The full history with the move trail is at:
|
|
1164
|
+
|
|
1165
|
+
\`${archive}\`
|
|
1166
|
+
|
|
1167
|
+
See \`${join3(archive, "INDEX.md")}\` and \`${join3(archive, "manifest.jsonl")}\`.
|
|
1168
|
+
|
|
1169
|
+
Set the folder with \`/inbox set <path>\`. Print the skill with \`/inbox skill\`. Clear with \`/inbox clear\`. List files with \`/exports\`.
|
|
1170
|
+
`;
|
|
1171
|
+
}
|
|
1172
|
+
function pruneInboxArchive(archiveDir, keep = INBOX_ARCHIVE_KEEP) {
|
|
1173
|
+
if (!existsSync2(archiveDir)) return;
|
|
1174
|
+
const entries2 = readdirSync(archiveDir).map((name) => {
|
|
1175
|
+
const p = join3(archiveDir, name);
|
|
1176
|
+
try {
|
|
1177
|
+
return { name, path: p, mtime: statSync(p).mtimeMs };
|
|
1178
|
+
} catch {
|
|
1179
|
+
return null;
|
|
1180
|
+
}
|
|
1181
|
+
}).filter((e) => e != null).sort((a, b) => b.mtime - a.mtime);
|
|
1182
|
+
for (const old of entries2.slice(keep)) {
|
|
1183
|
+
rmSync(old.path, { recursive: true, force: true });
|
|
1184
|
+
}
|
|
1185
|
+
}
|
|
1186
|
+
function syncAiInbox(entry) {
|
|
1187
|
+
const inbox = getAiInboxDir();
|
|
1188
|
+
if (!inbox) return null;
|
|
1189
|
+
if (!existsSync2(entry.path)) return null;
|
|
1190
|
+
ensureInboxLayout(inbox);
|
|
1191
|
+
const archiveDir = join3(inbox, "archive");
|
|
1192
|
+
mkdirSync4(archiveDir, { recursive: true });
|
|
1193
|
+
const base = basename2(entry.path);
|
|
1194
|
+
const archiveDest = join3(archiveDir, base);
|
|
1195
|
+
copyPath(entry.path, archiveDest);
|
|
1196
|
+
pruneInboxArchive(archiveDir);
|
|
1197
|
+
const latestName = inboxLatestNameForKind(entry.kind);
|
|
1198
|
+
const latestDest = join3(inbox, latestName);
|
|
1199
|
+
copyPath(entry.path, latestDest);
|
|
1200
|
+
if (entry.kind.startsWith("prompt:")) {
|
|
1201
|
+
copyPath(entry.path, join3(inbox, "latest-handoff.md"));
|
|
1202
|
+
}
|
|
1203
|
+
regenerateInboxIndex(inbox);
|
|
1204
|
+
return latestDest;
|
|
1205
|
+
}
|
|
1206
|
+
function syncRecentToInbox(limit = 10) {
|
|
1207
|
+
const inbox = getAiInboxDir();
|
|
1208
|
+
if (!inbox) return 0;
|
|
1209
|
+
ensureInboxLayout(inbox);
|
|
1210
|
+
const items = listExports({ limit });
|
|
1211
|
+
let n = 0;
|
|
1212
|
+
for (const item of items) {
|
|
1213
|
+
if (!existsSync2(item.path)) continue;
|
|
1214
|
+
const inboxPath = syncAiInbox(item);
|
|
1215
|
+
if (inboxPath) {
|
|
1216
|
+
appendManifestEvent({
|
|
1217
|
+
...item,
|
|
1218
|
+
op: "inbox_sync",
|
|
1219
|
+
at: (/* @__PURE__ */ new Date()).toISOString(),
|
|
1220
|
+
inbox_path: inboxPath
|
|
1221
|
+
});
|
|
1222
|
+
n++;
|
|
1223
|
+
}
|
|
1224
|
+
}
|
|
1225
|
+
regenerateInboxIndex(inbox);
|
|
1226
|
+
regenerateIndex();
|
|
1227
|
+
const newest = items.find((e) => existsSync2(e.path));
|
|
1228
|
+
if (newest) persistHandoffSkillFiles(newest);
|
|
1229
|
+
return n;
|
|
1230
|
+
}
|
|
696
1231
|
function archiveIndexPath() {
|
|
697
1232
|
return join3(ensureExportsLayout(), "INDEX.md");
|
|
698
1233
|
}
|
|
699
|
-
var KIND_DIRS, ARCHIVE_README;
|
|
1234
|
+
var KIND_DIRS, INBOX_ARCHIVE_KEEP, ARCHIVE_README;
|
|
700
1235
|
var init_exports_registry = __esm({
|
|
701
1236
|
"src/services/exports-registry.ts"() {
|
|
702
1237
|
"use strict";
|
|
@@ -707,6 +1242,7 @@ var init_exports_registry = __esm({
|
|
|
707
1242
|
init_handoff_skill();
|
|
708
1243
|
init_export_kinds();
|
|
709
1244
|
KIND_DIRS = ["handoffs", "reports", "notes", "csv", "publish"];
|
|
1245
|
+
INBOX_ARCHIVE_KEEP = 20;
|
|
710
1246
|
ARCHIVE_README = `# NTRP exports archive
|
|
711
1247
|
|
|
712
1248
|
This archive stores handoffs, reports, notes, CSV receipts, and publish packages by kind:
|
|
@@ -7999,7 +8535,6 @@ function resetContextForSwitch(ctx, opts) {
|
|
|
7999
8535
|
ctx.deliverIntent = false;
|
|
8000
8536
|
ctx.computeInProgress = false;
|
|
8001
8537
|
ctx.wizardDepth = 0;
|
|
8002
|
-
ctx.welcomeLogoShown = false;
|
|
8003
8538
|
ctx.snapshot = { computeResult: null, divergences: [] };
|
|
8004
8539
|
rebindSessionTranscript(ctx);
|
|
8005
8540
|
}
|
|
@@ -9289,8 +9824,8 @@ handler: ../commands/handoff.ts
|
|
|
9289
9824
|
Write the analysis as an output. Choose a markdown report, notes, CSV receipts, or a repository package.
|
|
9290
9825
|
Or write a prompt. Another agent can turn that prompt into a review deck, Asana project, Clay table, or action plan.
|
|
9291
9826
|
Files go to \`export-dir\`. If set, files also go to the inbox.
|
|
9292
|
-
Type \`/inbox set\` to set the inbox.
|
|
9293
|
-
Type \`/inbox skill\` once. Or paste the skill during
|
|
9827
|
+
Type \`/inbox set\` to set the inbox. Demo setup offers this once. The first time you load your own data, NTRP asks again if the folder is not set. Skip then, and NTRP will not ask again \u2014 type \`/inbox set\` later.
|
|
9828
|
+
Type \`/inbox skill\` once. Or paste the skill during demo or that first production step.
|
|
9294
9829
|
Later \`/handoff\` writes to \`latest-handoff.md\` with no new paste.
|
|
9295
9830
|
Type \`/handoff prompt <target> --print\` to include the file body.
|
|
9296
9831
|
An output marks the session as delivered.`
|
|
@@ -9323,7 +9858,7 @@ Set a folder that Claude Desktop or any desktop AI can read.
|
|
|
9323
9858
|
NTRP copies each handoff to that folder.
|
|
9324
9859
|
NTRP overwrites \`latest-handoff.md\` and \`latest-handoff-deck.md\`. The app then finds the newest file.
|
|
9325
9860
|
NTRP also writes \`SKILL.md\`. That file holds finder instructions with your paths.
|
|
9326
|
-
\`/onboard\`
|
|
9861
|
+
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.
|
|
9327
9862
|
Paste that skill once into Claude, ChatGPT, or Cursor. Later handoffs need no new paste.
|
|
9328
9863
|
\`INDEX.md\` in that folder links to the archive.
|
|
9329
9864
|
A path outside ~/.ntrp needs a confirm. Handoffs carry analysis text.
|
|
@@ -9341,7 +9876,7 @@ handler: ../commands/onboard.ts
|
|
|
9341
9876
|
Start the first-run wizard. It builds a company profile.
|
|
9342
9877
|
Connect one or two keys (Anthropic, OpenAI, or another provider).
|
|
9343
9878
|
Then answer a few seed questions. AI drafts industry, ICP, deal size, and stack guesses.
|
|
9344
|
-
Optional last step: pick a folder for desktop-AI handoffs
|
|
9879
|
+
Optional last step: pick a folder for desktop-AI handoffs if it is not already set. Skip, and type \`/inbox set\` later \u2014 NTRP will not ask again.
|
|
9345
9880
|
The profile is stored at \`~/.ntrp/profile.json\`. It flows into findings, NL answers, and demo data.`
|
|
9346
9881
|
},
|
|
9347
9882
|
{
|
|
@@ -9922,7 +10457,7 @@ args: <license>
|
|
|
9922
10457
|
handler: ../commands/activate.ts
|
|
9923
10458
|
---
|
|
9924
10459
|
|
|
9925
|
-
Activate NTRP with
|
|
10460
|
+
Activate NTRP with the key from your purchase email. Most commands need a valid license.`
|
|
9926
10461
|
},
|
|
9927
10462
|
{
|
|
9928
10463
|
name: "upgrade",
|
|
@@ -22157,12 +22692,126 @@ var init_revenue_importer = __esm({
|
|
|
22157
22692
|
}
|
|
22158
22693
|
});
|
|
22159
22694
|
|
|
22695
|
+
// src/conversation/inbox-setup.ts
|
|
22696
|
+
var inbox_setup_exports = {};
|
|
22697
|
+
__export(inbox_setup_exports, {
|
|
22698
|
+
maybeOfferInboxOnProduction: () => maybeOfferInboxOnProduction,
|
|
22699
|
+
offerInboxSkillSetup: () => offerInboxSkillSetup,
|
|
22700
|
+
shouldOfferInboxSkillSetup: () => shouldOfferInboxSkillSetup
|
|
22701
|
+
});
|
|
22702
|
+
import chalk22 from "chalk";
|
|
22703
|
+
function markDemoOffered() {
|
|
22704
|
+
setConfigValue("ai-inbox-nudge-seen", "true");
|
|
22705
|
+
}
|
|
22706
|
+
function markProductionOffered() {
|
|
22707
|
+
setConfigValue(PRODUCTION_OFFERED_KEY, "true");
|
|
22708
|
+
setConfigValue("ai-inbox-nudge-seen", "true");
|
|
22709
|
+
}
|
|
22710
|
+
function hasProductionOffered() {
|
|
22711
|
+
return getConfigValue(PRODUCTION_OFFERED_KEY) === "true";
|
|
22712
|
+
}
|
|
22713
|
+
function shouldOfferInboxSkillSetup(beat) {
|
|
22714
|
+
if (getAiInboxDir()) return false;
|
|
22715
|
+
if (beat === "demo") return getConfigValue("ai-inbox-nudge-seen") !== "true";
|
|
22716
|
+
return !hasProductionOffered();
|
|
22717
|
+
}
|
|
22718
|
+
function printSkipHint(beat) {
|
|
22719
|
+
const setCmd = paint("accent", "/inbox set ~/Documents/Claude/ntrp-inbox");
|
|
22720
|
+
const skillCmd = paint("accent", "/inbox skill");
|
|
22721
|
+
if (beat === "demo") {
|
|
22722
|
+
console.log(
|
|
22723
|
+
" " + chalk22.dim("Skipped. NTRP will ask once when you load your own data. Or type ") + setCmd + chalk22.dim(" then ") + skillCmd
|
|
22724
|
+
);
|
|
22725
|
+
return;
|
|
22726
|
+
}
|
|
22727
|
+
console.log(
|
|
22728
|
+
" " + chalk22.dim("Skipped. NTRP will not ask again. Type ") + setCmd + chalk22.dim(" then ") + skillCmd + chalk22.dim(" at any time.")
|
|
22729
|
+
);
|
|
22730
|
+
}
|
|
22731
|
+
async function offerInboxSkillSetup(session, opts = {}) {
|
|
22732
|
+
const beat = opts.beat ?? "production";
|
|
22733
|
+
if (!shouldOfferInboxSkillSetup(beat)) return;
|
|
22734
|
+
console.log();
|
|
22735
|
+
console.log(" " + bold("Set the handoff inbox"));
|
|
22736
|
+
console.log(
|
|
22737
|
+
" " + chalk22.dim(
|
|
22738
|
+
"Optional. NTRP copies each handoff into one folder. Paste the skill once. Later /handoff only writes the file."
|
|
22739
|
+
)
|
|
22740
|
+
);
|
|
22741
|
+
if (beat === "production" && getConfigValue("ai-inbox-nudge-seen") === "true") {
|
|
22742
|
+
console.log(" " + chalk22.dim("You skipped this during demo."));
|
|
22743
|
+
}
|
|
22744
|
+
console.log();
|
|
22745
|
+
const want = await session.confirm("Set a pickup folder for Claude, ChatGPT, or Cursor?", true);
|
|
22746
|
+
if (!want) {
|
|
22747
|
+
if (beat === "demo") markDemoOffered();
|
|
22748
|
+
else markProductionOffered();
|
|
22749
|
+
printSkipHint(beat);
|
|
22750
|
+
return;
|
|
22751
|
+
}
|
|
22752
|
+
const raw = await session.ask("Folder", { default: defaultAiInboxDir() });
|
|
22753
|
+
const folder = raw.trim() || defaultAiInboxDir();
|
|
22754
|
+
const resolvedPreview = resolveUserPath(folder);
|
|
22755
|
+
if (!isInsideNtrp(resolvedPreview)) {
|
|
22756
|
+
const ok = await session.confirm(
|
|
22757
|
+
`Inbox is outside ~/.ntrp (${resolvedPreview}). Handoffs with analysis text will be written here. Continue?`,
|
|
22758
|
+
true
|
|
22759
|
+
);
|
|
22760
|
+
if (!ok) {
|
|
22761
|
+
if (beat === "demo") markDemoOffered();
|
|
22762
|
+
else markProductionOffered();
|
|
22763
|
+
printSkipHint(beat);
|
|
22764
|
+
return;
|
|
22765
|
+
}
|
|
22766
|
+
}
|
|
22767
|
+
const resolved = setAiInboxDir(folder);
|
|
22768
|
+
const n = syncRecentToInbox(10);
|
|
22769
|
+
markDemoOffered();
|
|
22770
|
+
if (beat === "production") markProductionOffered();
|
|
22771
|
+
console.log();
|
|
22772
|
+
console.log(" " + paint("accent", "Inbox ready") + chalk22.dim(" ") + resolved);
|
|
22773
|
+
if (n > 0) {
|
|
22774
|
+
console.log(" " + chalk22.dim(`Synced ${n} recent export${n === 1 ? "" : "s"}.`));
|
|
22775
|
+
}
|
|
22776
|
+
console.log(
|
|
22777
|
+
" " + chalk22.dim("Paste this skill into Claude once. Later, tell Claude to open the latest NTRP handoff.")
|
|
22778
|
+
);
|
|
22779
|
+
printStandingSkill();
|
|
22780
|
+
await session.askPressEnter("Paste the skill into Claude. Then continue");
|
|
22781
|
+
console.log(" " + chalk22.dim("Done. Later handoffs write to that folder. The skill is not printed again."));
|
|
22782
|
+
console.log();
|
|
22783
|
+
}
|
|
22784
|
+
async function maybeOfferInboxOnProduction(ctx) {
|
|
22785
|
+
if (!shouldOfferInboxSkillSetup("production")) return;
|
|
22786
|
+
if (ctx.execution.mode === "headless" || ctx.execution.strictStdout) return;
|
|
22787
|
+
if (!ctx.rl && !process.stdin.isTTY) return;
|
|
22788
|
+
const session = createPromptSession(ctx.rl, ctx);
|
|
22789
|
+
try {
|
|
22790
|
+
await offerInboxSkillSetup(session, { beat: "production" });
|
|
22791
|
+
} finally {
|
|
22792
|
+
session.close();
|
|
22793
|
+
}
|
|
22794
|
+
}
|
|
22795
|
+
var PRODUCTION_OFFERED_KEY;
|
|
22796
|
+
var init_inbox_setup = __esm({
|
|
22797
|
+
"src/conversation/inbox-setup.ts"() {
|
|
22798
|
+
"use strict";
|
|
22799
|
+
init_prompts();
|
|
22800
|
+
init_store();
|
|
22801
|
+
init_theme();
|
|
22802
|
+
init_exports_registry();
|
|
22803
|
+
init_handoff_skill();
|
|
22804
|
+
init_path_safety();
|
|
22805
|
+
PRODUCTION_OFFERED_KEY = "ai-inbox-production-offered";
|
|
22806
|
+
}
|
|
22807
|
+
});
|
|
22808
|
+
|
|
22160
22809
|
// src/commands/ingest.ts
|
|
22161
22810
|
var ingest_exports = {};
|
|
22162
22811
|
__export(ingest_exports, {
|
|
22163
22812
|
handler: () => handler3
|
|
22164
22813
|
});
|
|
22165
|
-
import
|
|
22814
|
+
import chalk23 from "chalk";
|
|
22166
22815
|
import { readFileSync as readFileSync20, existsSync as existsSync22 } from "fs";
|
|
22167
22816
|
import { basename as basename6 } from "path";
|
|
22168
22817
|
async function handler3(args, ctx) {
|
|
@@ -22183,21 +22832,21 @@ async function handler3(args, ctx) {
|
|
|
22183
22832
|
const source = getString(flags, "source", "s") ?? "salesforce";
|
|
22184
22833
|
const skipResolve = getBool(flags, "skip-resolve");
|
|
22185
22834
|
if (!file) {
|
|
22186
|
-
console.error(
|
|
22187
|
-
console.error(
|
|
22835
|
+
console.error(chalk23.red(" Usage: /ingest <file> [--source salesforce|hubspot|outreach]"));
|
|
22836
|
+
console.error(chalk23.dim(" /ingest --demo [--scenario <name>]"));
|
|
22188
22837
|
process.exit(1);
|
|
22189
22838
|
}
|
|
22190
22839
|
if (!existsSync22(file)) {
|
|
22191
|
-
console.error(
|
|
22840
|
+
console.error(chalk23.red(` File not found: ${file}`));
|
|
22192
22841
|
process.exit(1);
|
|
22193
22842
|
}
|
|
22194
22843
|
const profile = loadProfile();
|
|
22195
22844
|
const skipProfile = getFalse(flags, "profile");
|
|
22196
22845
|
if (!profile && !skipProfile) {
|
|
22197
22846
|
console.error();
|
|
22198
|
-
console.error(" " +
|
|
22199
|
-
console.error(" " +
|
|
22200
|
-
console.error(" " +
|
|
22847
|
+
console.error(" " + chalk23.red("No company profile found."));
|
|
22848
|
+
console.error(" " + chalk23.dim("Run ") + paint("accent", "/onboard") + chalk23.dim(" first for better column mapping,"));
|
|
22849
|
+
console.error(" " + chalk23.dim("or pass ") + paint("accent", "--no-profile") + chalk23.dim(" to skip."));
|
|
22201
22850
|
console.error();
|
|
22202
22851
|
process.exit(1);
|
|
22203
22852
|
}
|
|
@@ -22229,22 +22878,24 @@ async function handler3(args, ctx) {
|
|
|
22229
22878
|
row_count: result2.imported
|
|
22230
22879
|
});
|
|
22231
22880
|
spinner.succeed(
|
|
22232
|
-
`Imported ${
|
|
22881
|
+
`Imported ${chalk23.bold(result2.imported.toString())} revenue events from ${chalk23.dim(basename6(file))}`
|
|
22233
22882
|
);
|
|
22234
22883
|
if (result2.errors.length > 0) {
|
|
22235
|
-
console.log(
|
|
22884
|
+
console.log(chalk23.yellow(` ${result2.errors.length} rows skipped`));
|
|
22236
22885
|
}
|
|
22237
22886
|
if (ctx.analysis) {
|
|
22238
22887
|
ctx.analysis.data_source_type = "revenue_ledger";
|
|
22239
22888
|
}
|
|
22240
|
-
console.log(
|
|
22889
|
+
console.log(chalk23.dim(" Run ") + chalk23.cyan("/metrics") + chalk23.dim(" for SaaS metrics with ledger-backed retention."));
|
|
22890
|
+
const { maybeOfferInboxOnProduction: maybeOfferInboxOnProduction3 } = await Promise.resolve().then(() => (init_inbox_setup(), inbox_setup_exports));
|
|
22891
|
+
await maybeOfferInboxOnProduction3(ctx);
|
|
22241
22892
|
return `${result2.imported} revenue events from ${basename6(file)}`;
|
|
22242
22893
|
}
|
|
22243
22894
|
spinner.text = "Detecting entity type\u2026";
|
|
22244
22895
|
const detection = detectEntityType(headers, source);
|
|
22245
22896
|
if (!detection) {
|
|
22246
22897
|
spinner.fail(`Could not auto-detect entity type for source: ${source}`);
|
|
22247
|
-
console.log(
|
|
22898
|
+
console.log(chalk23.dim(" Headers found: " + headers.join(", ")));
|
|
22248
22899
|
process.exit(1);
|
|
22249
22900
|
}
|
|
22250
22901
|
spinner.text = `Importing ${rows.length} ${detection.entityType} rows...`;
|
|
@@ -22267,15 +22918,15 @@ async function handler3(args, ctx) {
|
|
|
22267
22918
|
row_count: result.imported
|
|
22268
22919
|
});
|
|
22269
22920
|
spinner.succeed(
|
|
22270
|
-
`Imported ${
|
|
22921
|
+
`Imported ${chalk23.bold(result.imported.toString())} ${detection.entityType} from ${chalk23.dim(basename6(file))} (${source})`
|
|
22271
22922
|
);
|
|
22272
22923
|
if (result.errors.length > 0) {
|
|
22273
|
-
console.log(
|
|
22924
|
+
console.log(chalk23.yellow(` ${result.errors.length} rows skipped`));
|
|
22274
22925
|
for (const err of result.errors.slice(0, 3)) {
|
|
22275
|
-
console.log(
|
|
22926
|
+
console.log(chalk23.dim(` - ${err}`));
|
|
22276
22927
|
}
|
|
22277
22928
|
if (result.errors.length > 3) {
|
|
22278
|
-
console.log(
|
|
22929
|
+
console.log(chalk23.dim(` ... and ${result.errors.length - 3} more`));
|
|
22279
22930
|
}
|
|
22280
22931
|
}
|
|
22281
22932
|
if (!skipResolve) {
|
|
@@ -22289,10 +22940,12 @@ async function handler3(args, ctx) {
|
|
|
22289
22940
|
resolveSpinner.succeed("No duplicates found");
|
|
22290
22941
|
}
|
|
22291
22942
|
}
|
|
22943
|
+
const { maybeOfferInboxOnProduction: maybeOfferInboxOnProduction2 } = await Promise.resolve().then(() => (init_inbox_setup(), inbox_setup_exports));
|
|
22944
|
+
await maybeOfferInboxOnProduction2(ctx);
|
|
22292
22945
|
return `${result.imported} ${detection.entityType} from ${basename6(file)}`;
|
|
22293
22946
|
} catch (err) {
|
|
22294
22947
|
spinner.fail("Import failed");
|
|
22295
|
-
console.error(
|
|
22948
|
+
console.error(chalk23.red(String(err)));
|
|
22296
22949
|
process.exit(1);
|
|
22297
22950
|
}
|
|
22298
22951
|
}
|
|
@@ -22404,13 +23057,13 @@ __export(demo_fit_exports, {
|
|
|
22404
23057
|
resolveDemoScenarioForLoad: () => resolveDemoScenarioForLoad,
|
|
22405
23058
|
runDemoFitQuiz: () => runDemoFitQuiz
|
|
22406
23059
|
});
|
|
22407
|
-
import
|
|
23060
|
+
import chalk24 from "chalk";
|
|
22408
23061
|
async function runDemoFitQuiz(session, opts = {}) {
|
|
22409
23062
|
if (opts.intro !== false) {
|
|
22410
23063
|
console.log();
|
|
22411
23064
|
console.log(" " + bold("Fit a sample book of business"));
|
|
22412
23065
|
console.log(
|
|
22413
|
-
" " +
|
|
23066
|
+
" " + chalk24.dim(
|
|
22414
23067
|
"No API key is required. Answer two questions about how you sell. Then pick the closest of seven sample pipelines."
|
|
22415
23068
|
)
|
|
22416
23069
|
);
|
|
@@ -22428,8 +23081,8 @@ async function runDemoFitQuiz(session, opts = {}) {
|
|
|
22428
23081
|
);
|
|
22429
23082
|
const recommended = inferDemoScenario({ salesMotion: motion, dealBand });
|
|
22430
23083
|
console.log();
|
|
22431
|
-
console.log(" " +
|
|
22432
|
-
console.log(" " +
|
|
23084
|
+
console.log(" " + chalk24.dim("Recommended: ") + paint("accent", getScenario(recommended.scenario).label));
|
|
23085
|
+
console.log(" " + chalk24.dim(recommended.reason));
|
|
22433
23086
|
const scenario = await session.choose(
|
|
22434
23087
|
"Which of these sample books feels closest to the one you manage?",
|
|
22435
23088
|
scenarioMenuChoices(),
|
|
@@ -22449,8 +23102,8 @@ async function offerFittedDemoAfterOnboard(session, ctx, profile) {
|
|
|
22449
23102
|
const s = getScenario(fit.scenario);
|
|
22450
23103
|
console.log();
|
|
22451
23104
|
console.log(" " + bold("A sample pipeline that looks like you"));
|
|
22452
|
-
console.log(" " + paint("accent", s.label) +
|
|
22453
|
-
console.log(" " +
|
|
23105
|
+
console.log(" " + paint("accent", s.label) + chalk24.dim(" \u2014 " + s.hook));
|
|
23106
|
+
console.log(" " + chalk24.dim(fit.reason));
|
|
22454
23107
|
console.log();
|
|
22455
23108
|
const action = await session.choose(
|
|
22456
23109
|
"Try NTRP on that book of business?",
|
|
@@ -22507,8 +23160,8 @@ async function resolveDemoScenarioForLoad(ctx, opts = {}) {
|
|
|
22507
23160
|
if (!opts.forceQuiz && isProfileConfigured(profile) && profile) {
|
|
22508
23161
|
const fit = await resolveProfileFit(profile, ctx);
|
|
22509
23162
|
console.log();
|
|
22510
|
-
console.log(" " +
|
|
22511
|
-
console.log(" " +
|
|
23163
|
+
console.log(" " + chalk24.dim("Recommended: ") + paint("accent", getScenario(fit.scenario).label));
|
|
23164
|
+
console.log(" " + chalk24.dim(fit.reason));
|
|
22512
23165
|
const scenario = await session.choose(
|
|
22513
23166
|
"Which sample book of business?",
|
|
22514
23167
|
scenarioMenuChoices(),
|
|
@@ -22572,7 +23225,7 @@ __export(ingest_chat_exports, {
|
|
|
22572
23225
|
import { existsSync as existsSync23 } from "fs";
|
|
22573
23226
|
import { basename as basename7, resolve as resolve9 } from "path";
|
|
22574
23227
|
import { homedir as homedir8 } from "os";
|
|
22575
|
-
import
|
|
23228
|
+
import chalk25 from "chalk";
|
|
22576
23229
|
function extractFilePath(input) {
|
|
22577
23230
|
const trimmed = input.trim();
|
|
22578
23231
|
const patterns = [
|
|
@@ -22607,7 +23260,7 @@ function looksLikeFilePath(input) {
|
|
|
22607
23260
|
}
|
|
22608
23261
|
async function ingestFromChat(ctx, filePath) {
|
|
22609
23262
|
if (!ctx.rl) {
|
|
22610
|
-
console.log(" " +
|
|
23263
|
+
console.log(" " + chalk25.red("Ingest confirm requires interactive mode."));
|
|
22611
23264
|
return false;
|
|
22612
23265
|
}
|
|
22613
23266
|
const name = basename7(filePath);
|
|
@@ -22615,7 +23268,7 @@ async function ingestFromChat(ctx, filePath) {
|
|
|
22615
23268
|
try {
|
|
22616
23269
|
const ok = await prompts.confirm(`Ingest ${name} as CRM export?`, true);
|
|
22617
23270
|
if (!ok) {
|
|
22618
|
-
console.log(" " +
|
|
23271
|
+
console.log(" " + chalk25.dim("Ingest cancelled."));
|
|
22619
23272
|
return false;
|
|
22620
23273
|
}
|
|
22621
23274
|
} finally {
|
|
@@ -22643,7 +23296,7 @@ async function ingestFromChat(ctx, filePath) {
|
|
|
22643
23296
|
true
|
|
22644
23297
|
);
|
|
22645
23298
|
if (useAi) {
|
|
22646
|
-
console.log(" " +
|
|
23299
|
+
console.log(" " + chalk25.dim("AI column mapping is not wired to ingest yet \u2014 trying standard ingest."));
|
|
22647
23300
|
}
|
|
22648
23301
|
} finally {
|
|
22649
23302
|
prompts2.close();
|
|
@@ -22670,7 +23323,7 @@ async function ingestFromChat(ctx, filePath) {
|
|
|
22670
23323
|
invalidateGapAudit(ctx);
|
|
22671
23324
|
saveSessionState(ctx);
|
|
22672
23325
|
console.log();
|
|
22673
|
-
console.log(" " + paint("accent", "\u2713 Data loaded") +
|
|
23326
|
+
console.log(" " + paint("accent", "\u2713 Data loaded") + chalk25.dim(` \u2014 ${name}`));
|
|
22674
23327
|
recordMessage(ctx, "user", `[ingested ${name}]`);
|
|
22675
23328
|
recordMessage(ctx, "agent", `Loaded ${name}. Checking what we can analyze\u2026`);
|
|
22676
23329
|
const audit = await refreshGapAudit(ctx);
|
|
@@ -22678,7 +23331,7 @@ async function ingestFromChat(ctx, filePath) {
|
|
|
22678
23331
|
if (audit.can_compute && ctx.scope?.confirmed_at) {
|
|
22679
23332
|
if (ctx.pendingAsk) {
|
|
22680
23333
|
console.log();
|
|
22681
|
-
console.log(" " +
|
|
23334
|
+
console.log(" " + chalk25.dim("Computing so I can answer\u2026"));
|
|
22682
23335
|
await runConversationCompute(ctx);
|
|
22683
23336
|
return true;
|
|
22684
23337
|
}
|
|
@@ -22711,11 +23364,22 @@ async function loadDemoFromChat(ctx, scenario, opts = {}) {
|
|
|
22711
23364
|
const { resolveDemoScenarioForLoad: resolveDemoScenarioForLoad2 } = await Promise.resolve().then(() => (init_demo_fit(), demo_fit_exports));
|
|
22712
23365
|
chosen = await resolveDemoScenarioForLoad2(ctx) ?? void 0;
|
|
22713
23366
|
}
|
|
23367
|
+
if (ctx.rl) {
|
|
23368
|
+
const { shouldOfferInboxSkillSetup: shouldOfferInboxSkillSetup2, offerInboxSkillSetup: offerInboxSkillSetup2 } = await Promise.resolve().then(() => (init_inbox_setup(), inbox_setup_exports));
|
|
23369
|
+
if (shouldOfferInboxSkillSetup2("demo")) {
|
|
23370
|
+
const session = createPromptSession(ctx.rl, ctx);
|
|
23371
|
+
try {
|
|
23372
|
+
await offerInboxSkillSetup2(session, { beat: "demo" });
|
|
23373
|
+
} finally {
|
|
23374
|
+
session.close();
|
|
23375
|
+
}
|
|
23376
|
+
}
|
|
23377
|
+
}
|
|
22714
23378
|
if (chosen) {
|
|
22715
23379
|
const { getScenario: getScenario2 } = await Promise.resolve().then(() => (init_scenarios(), scenarios_exports));
|
|
22716
23380
|
const s = getScenario2(chosen);
|
|
22717
23381
|
console.log();
|
|
22718
|
-
console.log(" " + paint("accent", "Fitting ") + s.label +
|
|
23382
|
+
console.log(" " + paint("accent", "Fitting ") + s.label + chalk25.dim(" \u2014 " + s.hook));
|
|
22719
23383
|
}
|
|
22720
23384
|
const { handler: demo } = await Promise.resolve().then(() => (init_generate(), generate_exports));
|
|
22721
23385
|
const args = ["--no-profile", "--brief"];
|
|
@@ -22751,7 +23415,7 @@ async function loadDemoFromChat(ctx, scenario, opts = {}) {
|
|
|
22751
23415
|
const shouldAuto = opts.autoCompute || Boolean(ctx.pendingAsk && audit.can_compute && ctx.scope?.confirmed_at);
|
|
22752
23416
|
if (shouldAuto && audit.can_compute) {
|
|
22753
23417
|
console.log();
|
|
22754
|
-
console.log(" " +
|
|
23418
|
+
console.log(" " + chalk25.dim("Computing so I can answer\u2026"));
|
|
22755
23419
|
await runConversationCompute(ctx);
|
|
22756
23420
|
return true;
|
|
22757
23421
|
}
|
|
@@ -22784,7 +23448,7 @@ __export(pending_ask_exports, {
|
|
|
22784
23448
|
queuePendingAsk: () => queuePendingAsk,
|
|
22785
23449
|
resumePendingAsk: () => resumePendingAsk
|
|
22786
23450
|
});
|
|
22787
|
-
import
|
|
23451
|
+
import chalk26 from "chalk";
|
|
22788
23452
|
function looksLikeQuestion(input) {
|
|
22789
23453
|
const text = input.trim();
|
|
22790
23454
|
if (!text) return false;
|
|
@@ -22820,7 +23484,7 @@ function printFocusChip(ctx) {
|
|
|
22820
23484
|
const period = ctx.scope.time_horizon ? ` \xB7 ${ctx.scope.time_horizon}` : "";
|
|
22821
23485
|
console.log();
|
|
22822
23486
|
console.log(
|
|
22823
|
-
" " +
|
|
23487
|
+
" " + chalk26.dim("Focus: ") + paint("accent", lens) + chalk26.dim(period) + chalk26.dim(" \u2014 type ") + chalk26.cyan("adjust") + chalk26.dim(" to change")
|
|
22824
23488
|
);
|
|
22825
23489
|
console.log();
|
|
22826
23490
|
}
|
|
@@ -22831,7 +23495,7 @@ async function resumePendingAsk(ctx) {
|
|
|
22831
23495
|
if (canUseReplAi(ctx)) {
|
|
22832
23496
|
console.log();
|
|
22833
23497
|
console.log(
|
|
22834
|
-
" " +
|
|
23498
|
+
" " + chalk26.dim(
|
|
22835
23499
|
pending.keylessAnswered ? "Picking up your question with the connected engine\u2026" : "Picking up your question\u2026"
|
|
22836
23500
|
)
|
|
22837
23501
|
);
|
|
@@ -22864,7 +23528,7 @@ async function offerDemoToAnswer(ctx) {
|
|
|
22864
23528
|
const go = await prompts.confirm("Use demo data to answer this?", true);
|
|
22865
23529
|
if (!go) {
|
|
22866
23530
|
console.log(
|
|
22867
|
-
" " +
|
|
23531
|
+
" " + chalk26.dim("Paste a CSV path when ready, or say ") + chalk26.cyan("use demo data") + chalk26.dim(".")
|
|
22868
23532
|
);
|
|
22869
23533
|
console.log();
|
|
22870
23534
|
return false;
|
|
@@ -22896,7 +23560,7 @@ __export(compute_exports2, {
|
|
|
22896
23560
|
isComputeIntent: () => isComputeIntent,
|
|
22897
23561
|
runConversationCompute: () => runConversationCompute
|
|
22898
23562
|
});
|
|
22899
|
-
import
|
|
23563
|
+
import chalk27 from "chalk";
|
|
22900
23564
|
async function runConversationCompute(ctx) {
|
|
22901
23565
|
const lens = ctx.scope?.primary_lens ?? ctx.analysis.primary;
|
|
22902
23566
|
ctx.computeInProgress = true;
|
|
@@ -22951,7 +23615,7 @@ async function runConversationCompute(ctx) {
|
|
|
22951
23615
|
creditGapCompute(ctx);
|
|
22952
23616
|
return typeof summary === "string" ? summary : "Health analysis ready";
|
|
22953
23617
|
} catch (err) {
|
|
22954
|
-
console.error(" " +
|
|
23618
|
+
console.error(" " + chalk27.red(String(err.message ?? err)));
|
|
22955
23619
|
return;
|
|
22956
23620
|
} finally {
|
|
22957
23621
|
ctx.computeInProgress = false;
|
|
@@ -24355,6 +25019,11 @@ var init_lemonsqueezy = __esm({
|
|
|
24355
25019
|
|
|
24356
25020
|
// src/license/verify.ts
|
|
24357
25021
|
import { createHmac as createHmac2 } from "crypto";
|
|
25022
|
+
function signingSecret() {
|
|
25023
|
+
const secret2 = process.env.NTRP_SIGNING_SECRET;
|
|
25024
|
+
if (!secret2) return null;
|
|
25025
|
+
return secret2;
|
|
25026
|
+
}
|
|
24358
25027
|
function validateLicenseKey(key) {
|
|
24359
25028
|
const invalid = (msg) => ({
|
|
24360
25029
|
valid: false,
|
|
@@ -24365,13 +25034,17 @@ function validateLicenseKey(key) {
|
|
|
24365
25034
|
if (!key || !key.startsWith("NTRP-")) {
|
|
24366
25035
|
return invalid("Invalid key format");
|
|
24367
25036
|
}
|
|
25037
|
+
const secret2 = signingSecret();
|
|
25038
|
+
if (!secret2) {
|
|
25039
|
+
return invalid("Invalid key format");
|
|
25040
|
+
}
|
|
24368
25041
|
const parts = key.replace("NTRP-", "").split("-");
|
|
24369
25042
|
if (parts.length !== 3) {
|
|
24370
25043
|
return invalid("Invalid key format");
|
|
24371
25044
|
}
|
|
24372
25045
|
const [payload, meta, signature] = parts;
|
|
24373
25046
|
const dataToSign = `${payload}-${meta}`;
|
|
24374
|
-
const expectedSig = createHmac2("sha256",
|
|
25047
|
+
const expectedSig = createHmac2("sha256", secret2).update(dataToSign).digest("hex").slice(0, 8);
|
|
24375
25048
|
if (signature !== expectedSig) {
|
|
24376
25049
|
return invalid("Invalid license key");
|
|
24377
25050
|
}
|
|
@@ -24468,7 +25141,6 @@ function checkLicense() {
|
|
|
24468
25141
|
}
|
|
24469
25142
|
return applyTrialPolicy(result);
|
|
24470
25143
|
}
|
|
24471
|
-
var SIGNING_SECRET;
|
|
24472
25144
|
var init_verify = __esm({
|
|
24473
25145
|
"src/license/verify.ts"() {
|
|
24474
25146
|
"use strict";
|
|
@@ -24477,7 +25149,6 @@ var init_verify = __esm({
|
|
|
24477
25149
|
init_upgrade_whimsy();
|
|
24478
25150
|
init_normalize();
|
|
24479
25151
|
init_lemonsqueezy();
|
|
24480
|
-
SIGNING_SECRET = "ntrp-gtm-health-2026";
|
|
24481
25152
|
}
|
|
24482
25153
|
});
|
|
24483
25154
|
|