@whisperr/wizard 0.1.2 → 0.1.4
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 +84 -51
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -597,41 +597,53 @@ import { query } from "@anthropic-ai/claude-agent-sdk";
|
|
|
597
597
|
// src/core/playbooks/shared-prompt.ts
|
|
598
598
|
var BASE_WIZARD_PROMPT = `
|
|
599
599
|
You are the Whisperr Wizard \u2014 an expert integration agent running INSIDE a
|
|
600
|
-
customer's code repository.
|
|
601
|
-
sends the right events to Whisperr, then stop. Whisperr is a churn-prevention
|
|
602
|
-
engine that needs identify() (who the user is) and track() (what they do).
|
|
600
|
+
customer's code repository.
|
|
603
601
|
|
|
604
|
-
|
|
605
|
-
|
|
602
|
+
MISSION:
|
|
603
|
+
Whisperr predicts churn and fires retention interventions from product events.
|
|
604
|
+
Two things make that work: (1) identify() tying events to a real user, and
|
|
605
|
+
(2) track() events fired at the right moment WITH the properties that give them
|
|
606
|
+
meaning. A well-placed event carrying its real properties is gold; a bare or
|
|
607
|
+
mis-placed event is noise that corrupts the customer's churn data. Your mission:
|
|
608
|
+
wire up everything in the plan that genuinely exists in this app, and for each
|
|
609
|
+
event you place, capture the properties that are actually available right there
|
|
610
|
+
in the code. You will NOT find everything \u2014 some events live server-side or
|
|
611
|
+
aren't in this codebase at all. That is expected and fine. Get what is here, get
|
|
612
|
+
it right, report the rest. Know exactly what you're doing; never hunt blindly.
|
|
613
|
+
|
|
614
|
+
You are billed per step and the user is watching. Work FAST and DECISIVELY.
|
|
606
615
|
|
|
607
616
|
EFFICIENCY \u2014 non-negotiable:
|
|
608
617
|
- Use Grep and Glob to find code. Do NOT read whole files; read the smallest
|
|
609
618
|
slice you need.
|
|
610
619
|
- NEVER re-read a file you just edited. Trust your edit.
|
|
611
|
-
- Do not run the app, build, or
|
|
612
|
-
|
|
620
|
+
- Do not run the app, build, or the analyzer/test suites. The human reviews the
|
|
621
|
+
diff.
|
|
613
622
|
- Make several related edits in a row before pausing to think.
|
|
614
623
|
|
|
615
624
|
DECISIVENESS:
|
|
616
625
|
- When you find the right spot, edit it and move on immediately.
|
|
617
|
-
- If you
|
|
618
|
-
|
|
619
|
-
|
|
626
|
+
- If you can't find a clear place for something within ~2 searches, STOP looking.
|
|
627
|
+
Leave a one-line \`// TODO(whisperr): ...\` only if an obvious spot exists,
|
|
628
|
+
otherwise just note it as a follow-up. Do not keep hunting.
|
|
620
629
|
|
|
621
|
-
|
|
622
|
-
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
630
|
+
PROPERTY CAPTURE (this is the craft):
|
|
631
|
+
- For each event you place, look at what's in scope AT THAT CALL SITE \u2014 function
|
|
632
|
+
arguments, the object/model/state you're inside, nearby local variables \u2014 and
|
|
633
|
+
populate every listed property you can source from those values.
|
|
634
|
+
- Omit a property only if it genuinely isn't available there. Do NOT fetch,
|
|
635
|
+
compute, or invent data to fill a property, and do NOT wander off to find it.
|
|
636
|
+
- If a property marked (required) can't be sourced at your chosen site, that's a
|
|
637
|
+
strong hint you've picked the wrong site \u2014 reconsider; if it's still the best
|
|
638
|
+
spot, add the track() call and note the missing property.
|
|
626
639
|
|
|
627
640
|
CORRECTNESS:
|
|
628
641
|
- Match the app's existing conventions (imports, state management, file layout).
|
|
629
642
|
- Use the EXACT snake_case event names given. Never invent or rename events.
|
|
630
|
-
-
|
|
631
|
-
Only place an event where you are confident the user action truly happens.
|
|
643
|
+
- Only place an event where you're confident the user action truly happens.
|
|
632
644
|
|
|
633
|
-
End every task with a short summary: what you changed,
|
|
634
|
-
|
|
645
|
+
End every task with a short summary: what you changed, which events you wired
|
|
646
|
+
(and the properties you attached), and the follow-ups you intentionally left.
|
|
635
647
|
`.trim();
|
|
636
648
|
function renderIdentifyBrief(m) {
|
|
637
649
|
const lines = [];
|
|
@@ -640,14 +652,14 @@ function renderIdentifyBrief(m) {
|
|
|
640
652
|
lines.push(`Ingestion API key: ${m.ingestionApiKey}`);
|
|
641
653
|
if (m.businessContext) {
|
|
642
654
|
lines.push("");
|
|
643
|
-
lines.push(`
|
|
655
|
+
lines.push(`Business context: ${m.businessContext}`);
|
|
644
656
|
}
|
|
645
657
|
lines.push("");
|
|
646
658
|
lines.push("identify():");
|
|
647
659
|
if (m.identify.traits?.length) {
|
|
648
|
-
lines.push(" Send these traits when available:");
|
|
660
|
+
lines.push(" Send these traits when readily available:");
|
|
649
661
|
for (const t of m.identify.traits) {
|
|
650
|
-
lines.push(` - ${t.name}${t.description ? `
|
|
662
|
+
lines.push(` - ${t.name}${t.description ? ` \u2014 ${t.description}` : ""}`);
|
|
651
663
|
}
|
|
652
664
|
}
|
|
653
665
|
if (m.identify.channels?.length) {
|
|
@@ -661,18 +673,25 @@ function renderEventsBrief(m) {
|
|
|
661
673
|
(a, b) => (b.importance ?? 0) - (a.importance ?? 0)
|
|
662
674
|
);
|
|
663
675
|
const lines = [];
|
|
676
|
+
if (m.businessContext) {
|
|
677
|
+
lines.push(`Business context: ${m.businessContext}`);
|
|
678
|
+
lines.push("");
|
|
679
|
+
}
|
|
664
680
|
lines.push(
|
|
665
|
-
`${events.length} candidate events (highest-impact first).
|
|
681
|
+
`${events.length} candidate events (highest-impact first). For each: fire track('<event_type>', { ...properties }) at the real moment it happens, and attach every listed property you can source from values in scope there. Skip an event entirely if it has no clear client-side trigger.`
|
|
666
682
|
);
|
|
667
|
-
lines.push("");
|
|
668
683
|
for (const e of events) {
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
684
|
+
lines.push("");
|
|
685
|
+
const why = e.interventions?.length ? ` \xB7 drives: ${e.interventions.map((i) => i.label ?? i.code).join(", ")}` : "";
|
|
686
|
+
lines.push(`\u25A0 ${e.eventType}${e.label ? ` (${e.label})` : ""}${why}`);
|
|
687
|
+
if (e.description) lines.push(` ${e.description}`);
|
|
672
688
|
if (e.properties?.length) {
|
|
673
|
-
lines.push(
|
|
674
|
-
|
|
675
|
-
|
|
689
|
+
lines.push(" properties to capture if available:");
|
|
690
|
+
for (const p2 of e.properties) {
|
|
691
|
+
const req = p2.required ? " (required)" : "";
|
|
692
|
+
const desc = p2.description ? ` \u2014 ${p2.description}` : "";
|
|
693
|
+
lines.push(` \xB7 ${p2.name}${req}${desc}`);
|
|
694
|
+
}
|
|
676
695
|
}
|
|
677
696
|
}
|
|
678
697
|
return lines.join("\n");
|
|
@@ -687,7 +706,6 @@ async function runIntegrationAgent(opts) {
|
|
|
687
706
|
let costUsd = 0;
|
|
688
707
|
const summaries = [];
|
|
689
708
|
progress?.onPhase?.("Installing the SDK & wiring identify()");
|
|
690
|
-
const verifyHint = playbook.verifyCommand ? ` Finish with a single \`${playbook.verifyCommand}\` and fix only what it flags.` : "";
|
|
691
709
|
const corePrompt = [
|
|
692
710
|
`Integrate the Whisperr ${playbook.target.displayName} SDK \u2014 CORE SETUP ONLY.`,
|
|
693
711
|
`Project root: ${repoPath}`,
|
|
@@ -695,10 +713,16 @@ async function runIntegrationAgent(opts) {
|
|
|
695
713
|
"Do exactly these, then stop:",
|
|
696
714
|
`1. Add the Whisperr SDK dependency and install it (${playbook.packageRef}).`,
|
|
697
715
|
"2. Initialize it once at app startup with the key + base URL below.",
|
|
698
|
-
"3. Wire identify()
|
|
699
|
-
"
|
|
716
|
+
"3. Wire identify() at the PRIMARY place the user becomes authenticated",
|
|
717
|
+
" (after a successful login) and on session restore at app startup; call",
|
|
718
|
+
" reset() on logout. Do NOT enumerate every auth method \u2014 one primary",
|
|
719
|
+
" login path plus session restore is enough. Keep channels minimal: pass",
|
|
720
|
+
" email if it's readily at hand; phone/push are optional, skip them if not",
|
|
721
|
+
" obvious. Do not go hunting through every auth file.",
|
|
700
722
|
"",
|
|
701
|
-
"
|
|
723
|
+
"This is a real app \u2014 be quick and decisive, aim to finish in ~15 steps.",
|
|
724
|
+
"Do NOT instrument product events yet \u2014 that is a separate step. Do not run",
|
|
725
|
+
"the analyzer or build; the human will review the diff.",
|
|
702
726
|
"",
|
|
703
727
|
"----- IDENTIFY -----",
|
|
704
728
|
renderIdentifyBrief(manifest),
|
|
@@ -709,7 +733,7 @@ async function runIntegrationAgent(opts) {
|
|
|
709
733
|
systemPrompt: systemPrompt6,
|
|
710
734
|
repoPath,
|
|
711
735
|
model: config.model,
|
|
712
|
-
maxTurns:
|
|
736
|
+
maxTurns: 35,
|
|
713
737
|
progress
|
|
714
738
|
});
|
|
715
739
|
costUsd += core.costUsd;
|
|
@@ -719,17 +743,15 @@ ${core.summary}`);
|
|
|
719
743
|
if (manifest.events.length > 0) {
|
|
720
744
|
progress?.onPhase?.("Instrumenting your events");
|
|
721
745
|
const eventsPrompt = [
|
|
722
|
-
`The Whisperr ${playbook.target.displayName} SDK is already installed
|
|
723
|
-
"initialized, and identify() is wired. Now
|
|
724
|
-
"
|
|
746
|
+
`The Whisperr ${playbook.target.displayName} SDK is already installed and`,
|
|
747
|
+
"initialized, and identify() is wired. Now instrument the product events",
|
|
748
|
+
"below with track().",
|
|
725
749
|
"",
|
|
726
|
-
"
|
|
727
|
-
"
|
|
728
|
-
"-
|
|
729
|
-
"
|
|
730
|
-
"
|
|
731
|
-
"- You have a limited number of steps; spend them placing events, not",
|
|
732
|
-
" exploring. Stop once the events that clearly exist in this app are wired.",
|
|
750
|
+
"Work in importance order. Place each event at its real call site and",
|
|
751
|
+
"attach the properties you can source from what's in scope there (see the",
|
|
752
|
+
"property-capture rule in your instructions). Skip fast when an event has",
|
|
753
|
+
"no clear client-side trigger \u2014 spend steps placing events, not exploring.",
|
|
754
|
+
"Stop once the events that clearly exist in this app are wired.",
|
|
733
755
|
"",
|
|
734
756
|
"----- EVENTS -----",
|
|
735
757
|
renderEventsBrief(manifest),
|
|
@@ -991,7 +1013,15 @@ Flutter is live today; ${theme.bright(
|
|
|
991
1013
|
}
|
|
992
1014
|
const spin = p.spinner();
|
|
993
1015
|
let phaseLabel = "Integrating";
|
|
994
|
-
|
|
1016
|
+
let lastLine = "";
|
|
1017
|
+
const startedAt = Date.now();
|
|
1018
|
+
spin.start(theme.bright(phaseLabel));
|
|
1019
|
+
const tick = setInterval(() => {
|
|
1020
|
+
const secs = Math.round((Date.now() - startedAt) / 1e3);
|
|
1021
|
+
spin.message(
|
|
1022
|
+
theme.bright(phaseLabel) + theme.muted(` \xB7 ${secs}s`) + (lastLine ? theme.muted(` \u2014 ${lastLine}`) : "")
|
|
1023
|
+
);
|
|
1024
|
+
}, 1e3);
|
|
995
1025
|
const outcome = await runIntegrationAgent({
|
|
996
1026
|
repoPath,
|
|
997
1027
|
config,
|
|
@@ -1001,18 +1031,21 @@ Flutter is live today; ${theme.bright(
|
|
|
1001
1031
|
progress: {
|
|
1002
1032
|
onPhase(label) {
|
|
1003
1033
|
phaseLabel = label;
|
|
1004
|
-
|
|
1034
|
+
lastLine = "";
|
|
1005
1035
|
},
|
|
1006
1036
|
onActivity(line) {
|
|
1007
|
-
|
|
1037
|
+
lastLine = line;
|
|
1008
1038
|
}
|
|
1009
1039
|
}
|
|
1010
1040
|
}).catch((err) => {
|
|
1011
|
-
spin.stop(theme.alert("Integration stopped"));
|
|
1012
1041
|
p.log.error(err.message);
|
|
1013
1042
|
return null;
|
|
1014
1043
|
});
|
|
1015
|
-
|
|
1044
|
+
clearInterval(tick);
|
|
1045
|
+
if (!outcome) {
|
|
1046
|
+
spin.stop(theme.alert("Integration stopped"));
|
|
1047
|
+
return 1;
|
|
1048
|
+
}
|
|
1016
1049
|
const stopLabel = !outcome.coreOk ? theme.warn("Stopped early \u2014 partial setup is in your working tree") : outcome.eventsComplete ? theme.success("Integration complete") : theme.success("Core integration done") + theme.muted(" \u2014 some events left as follow-ups");
|
|
1017
1050
|
spin.stop(stopLabel);
|
|
1018
1051
|
const files = await changedFiles(repoPath, checkpoint);
|
|
@@ -1193,7 +1226,7 @@ async function main() {
|
|
|
1193
1226
|
return;
|
|
1194
1227
|
}
|
|
1195
1228
|
if ("version" in parsed) {
|
|
1196
|
-
console.log("0.1.
|
|
1229
|
+
console.log("0.1.4");
|
|
1197
1230
|
return;
|
|
1198
1231
|
}
|
|
1199
1232
|
try {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@whisperr/wizard",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.4",
|
|
4
4
|
"description": "Whisperr Wizard — one command to integrate the Whisperr SDK into your app. Authenticates with your onboarded account and uses an AI coding agent to wire up identify() and your business events automatically.",
|
|
5
5
|
"license": "UNLICENSED",
|
|
6
6
|
"private": false,
|