@whisperr/wizard 0.1.9 → 0.1.11
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 +117 -12
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -7,7 +7,7 @@ import * as p from "@clack/prompts";
|
|
|
7
7
|
// src/core/config.ts
|
|
8
8
|
var DEFAULT_API_BASE = "https://api.whisperr.net";
|
|
9
9
|
var DEFAULT_MODEL = "claude-sonnet-4-6";
|
|
10
|
-
var DEFAULT_EFFORT = "
|
|
10
|
+
var DEFAULT_EFFORT = "high";
|
|
11
11
|
var EFFORT_LEVELS = ["low", "medium", "high", "xhigh", "max"];
|
|
12
12
|
function resolveEffort() {
|
|
13
13
|
const raw = (process.env.WHISPERR_WIZARD_EFFORT ?? "").toLowerCase();
|
|
@@ -995,6 +995,58 @@ in the code. You will NOT find everything \u2014 some events live server-side or
|
|
|
995
995
|
aren't in this codebase at all. That is expected and fine. Get what is here, get
|
|
996
996
|
it right, report the rest. Know exactly what you're doing; never hunt blindly.
|
|
997
997
|
|
|
998
|
+
WHO COUNTS AS "THE USER" \u2014 decide this BEFORE you place anything:
|
|
999
|
+
Whisperr models churn for the product's END USERS: the customers/consumers who
|
|
1000
|
+
use the app and could leave. identify() and EVERY event must key to that person.
|
|
1001
|
+
Internal actors are NOT the user and must never be instrumented \u2014 doing so fills
|
|
1002
|
+
the churn model with the wrong people and is a serious miss:
|
|
1003
|
+
admins \xB7 staff \xB7 operators \xB7 support / back-office agents \xB7 superusers \xB7
|
|
1004
|
+
sellers/merchants/partners (unless the product is FOR them) \xB7 CMS / management
|
|
1005
|
+
/ admin-panel consoles.
|
|
1006
|
+
Real apps usually have SEVERAL auth/identity paths. Before wiring identify(),
|
|
1007
|
+
enumerate them (grep for login / signup / register / authenticate / session
|
|
1008
|
+
handlers) and pick the END-USER one. Treat any of these in a path, route,
|
|
1009
|
+
namespace, class, guard, model, or table name as a strong "WRONG subject \u2014 skip
|
|
1010
|
+
it" signal:
|
|
1011
|
+
admin \xB7 adminpanel \xB7 backoffice \xB7 internal \xB7 staff \xB7 operator \xB7 seller \xB7
|
|
1012
|
+
merchant \xB7 partner \xB7 cms \xB7 manage/management \xB7 console \xB7 dashboard \xB7 support \xB7
|
|
1013
|
+
superuser \xB7 a dedicated admin guard/middleware (e.g. Laravel \`auth:admin\`) or
|
|
1014
|
+
a separate Admin/Staff user model or table.
|
|
1015
|
+
If the only auth you can see sits on such a path, the real end-user auth is
|
|
1016
|
+
almost certainly elsewhere (a customer-facing API, the mobile backend, an SSO
|
|
1017
|
+
callback) \u2014 keep looking instead of settling for it. Only treat an operator as
|
|
1018
|
+
the user when the product itself is genuinely for operators (an internal tool).
|
|
1019
|
+
|
|
1020
|
+
CONSISTENCY CHECK: identify() and the signup/login events (e.g. account_created)
|
|
1021
|
+
describe the SAME person, so they belong on the SAME surface. If you wired
|
|
1022
|
+
account_created into one controller and are about to put identify() in a
|
|
1023
|
+
different one, stop \u2014 that's the tell you've split the subject. Co-locate them.
|
|
1024
|
+
|
|
1025
|
+
WHAT THE EVENT MEANS \u2014 place by SEMANTICS, not by keyword. This is where most
|
|
1026
|
+
mistakes happen. An event names a SPECIFIC moment, not "something happened
|
|
1027
|
+
nearby." For each event, before you place it:
|
|
1028
|
+
- Model the exact transition. \`subscription_started\` is the FIRST subscription,
|
|
1029
|
+
NOT every payment \u2014 renewals, retries, and reactivations are DIFFERENT events.
|
|
1030
|
+
Firing one event on a path that also handles the other cases is a real bug.
|
|
1031
|
+
- Read the control flow and find the branch that separates look-alike cases.
|
|
1032
|
+
Payment/callback handlers routinely route initial vs recurring/renewal, success
|
|
1033
|
+
vs retry, trial vs paid through ONE function. Key the event to the right branch
|
|
1034
|
+
(e.g. a \`RENEW_CARD\` / \`is_recurring\` / \`type\` check), or branch yourself and
|
|
1035
|
+
emit the correct event per case. Do not drop one event on the shared success
|
|
1036
|
+
line and move on \u2014 open the function and see which cases it actually covers.
|
|
1037
|
+
- Read the codebase's own flags literally. A field like \`type: promo|standard\`
|
|
1038
|
+
is pricing tier, not lifecycle \u2014 confirm what a flag means before keying on it.
|
|
1039
|
+
- Capture the discriminators as properties. The fields that tell cases apart \u2014
|
|
1040
|
+
charge_type, is_recurring, payment_source/gateway, amount, plan, currency \u2014 are
|
|
1041
|
+
exactly what makes the event usable for churn (a failed RENEWAL is a churn
|
|
1042
|
+
signal; a failed first charge is not). If the code sets such a field right
|
|
1043
|
+
there (e.g. \`type => 'initial'\`), pass it. A billing event without them is
|
|
1044
|
+
half-blind.
|
|
1045
|
+
- Cover the whole lifecycle. Two gateways each with an initial and a recurring
|
|
1046
|
+
path is FOUR call sites \u2014 instrument all of them. A recurring/secondary
|
|
1047
|
+
callback with no events is a churn blind spot. If you deliberately skip a path,
|
|
1048
|
+
say so in the summary.
|
|
1049
|
+
|
|
998
1050
|
Every step costs time and the user is watching. Work FAST and DECISIVELY.
|
|
999
1051
|
|
|
1000
1052
|
EFFICIENCY \u2014 non-negotiable:
|
|
@@ -1010,6 +1062,10 @@ DECISIVENESS:
|
|
|
1010
1062
|
- If you can't find a clear place for something within ~2 searches, STOP looking.
|
|
1011
1063
|
Leave a one-line \`// TODO(whisperr): ...\` only if an obvious spot exists,
|
|
1012
1064
|
otherwise just note it as a follow-up. Do not keep hunting.
|
|
1065
|
+
- ONE exception: choosing the correct user/subject (see WHO COUNTS AS "THE
|
|
1066
|
+
USER") is worth a few extra searches. Do not grab the first thing named
|
|
1067
|
+
"login" \u2014 confirm it's the end-user path first. Getting the subject wrong is
|
|
1068
|
+
far more costly than a missed event.
|
|
1013
1069
|
|
|
1014
1070
|
PROPERTY CAPTURE (this is the craft):
|
|
1015
1071
|
- For each event you place, look at what's in scope AT THAT CALL SITE \u2014 function
|
|
@@ -1025,6 +1081,9 @@ CORRECTNESS:
|
|
|
1025
1081
|
- Match the app's existing conventions (imports, state management, file layout).
|
|
1026
1082
|
- Use the EXACT snake_case event names given. Never invent or rename events.
|
|
1027
1083
|
- Only place an event where you're confident the user action truly happens.
|
|
1084
|
+
- Before writing a call, confirm the id in scope is the END-USER's \u2014 not an
|
|
1085
|
+
admin/staff/operator/seller id. An admin acting on a user's behalf is still an
|
|
1086
|
+
admin action; instrument where the end user themselves acts.
|
|
1028
1087
|
|
|
1029
1088
|
FINAL SUMMARY (this is shown verbatim to a non-technical customer in a
|
|
1030
1089
|
dashboard \u2014 write for a human, not a changelog):
|
|
@@ -1122,12 +1181,18 @@ async function runIntegrationAgent(opts) {
|
|
|
1122
1181
|
"Do exactly these, then stop:",
|
|
1123
1182
|
`1. Add the Whisperr SDK dependency and install it (${playbook.packageRef}).`,
|
|
1124
1183
|
"2. Initialize it once at app startup with the key + base URL below.",
|
|
1125
|
-
"3. Wire identify()
|
|
1126
|
-
|
|
1127
|
-
"
|
|
1128
|
-
"
|
|
1129
|
-
|
|
1130
|
-
"
|
|
1184
|
+
"3. Wire identify() for the END USER \u2014 the paying customer/consumer, NEVER an",
|
|
1185
|
+
' admin, staff, operator, seller, or partner (see WHO COUNTS AS "THE USER"',
|
|
1186
|
+
" in your instructions). This app likely has several auth paths: enumerate",
|
|
1187
|
+
" them first (grep login/signup/register/authenticate) and instrument the",
|
|
1188
|
+
' end-user one \u2014 do NOT just take the first thing named "login". A path',
|
|
1189
|
+
" under admin/backoffice/staff/seller/cms is the wrong subject. Sanity",
|
|
1190
|
+
" check: identify() belongs on the SAME surface as account_created \u2014 if",
|
|
1191
|
+
" they'd land in different controllers, you've picked the wrong login.",
|
|
1192
|
+
" Wire it at the primary end-user login success and on session restore at",
|
|
1193
|
+
" startup; call reset() on logout. One end-user login path plus session",
|
|
1194
|
+
" restore is enough. Keep channels minimal: email if readily at hand;",
|
|
1195
|
+
" phone/push optional.",
|
|
1131
1196
|
"",
|
|
1132
1197
|
"This is a real app \u2014 be quick and decisive, aim to finish in ~15 steps.",
|
|
1133
1198
|
"Do NOT instrument product events yet \u2014 that is a separate step. Do not run",
|
|
@@ -1157,10 +1222,12 @@ ${core.summary}`);
|
|
|
1157
1222
|
"initialized, and identify() is wired. Now instrument the product events",
|
|
1158
1223
|
"below with track().",
|
|
1159
1224
|
"",
|
|
1160
|
-
"Work in importance order. Place each event at
|
|
1161
|
-
"
|
|
1162
|
-
"
|
|
1163
|
-
"
|
|
1225
|
+
"Work in importance order. Place each event at the real call site where the",
|
|
1226
|
+
"END USER performs the action (not an admin/staff path \u2014 see WHO COUNTS AS",
|
|
1227
|
+
`"THE USER"), and attach the properties you can source from what's in scope`,
|
|
1228
|
+
"there (see the property-capture rule). These events describe the same",
|
|
1229
|
+
"person identify() did, so they live on the same end-user surface. Skip fast",
|
|
1230
|
+
"when an event has no clear trigger here \u2014 spend steps placing, not exploring.",
|
|
1164
1231
|
"Stop once the events that clearly exist in this app are wired.",
|
|
1165
1232
|
"",
|
|
1166
1233
|
"----- EVENTS -----",
|
|
@@ -1180,6 +1247,44 @@ ${core.summary}`);
|
|
|
1180
1247
|
eventsComplete = !events.maxedOut;
|
|
1181
1248
|
if (events.summary) summaries.push(`Events:
|
|
1182
1249
|
${events.summary}`);
|
|
1250
|
+
}
|
|
1251
|
+
if (core.ok) {
|
|
1252
|
+
progress?.onPhase?.("Reviewing & correcting placements");
|
|
1253
|
+
const reviewPrompt = [
|
|
1254
|
+
`You wired the Whisperr ${playbook.target.displayName} SDK into this repo.`,
|
|
1255
|
+
"Now AUDIT YOUR OWN WORK and fix mistakes before finishing.",
|
|
1256
|
+
`Project root: ${repoPath}`,
|
|
1257
|
+
"",
|
|
1258
|
+
"Run `git diff` and read the surrounding code to see exactly what you added.",
|
|
1259
|
+
"For every identify() and track() call, verify \u2014 and FIX in place:",
|
|
1260
|
+
"1. Subject \u2014 keys to the END USER, never an admin/staff/operator/seller.",
|
|
1261
|
+
" identify() sits on the SAME surface as account_created (same person).",
|
|
1262
|
+
"2. Lifecycle \u2014 the event fires ONLY on its real moment. A 'first time'",
|
|
1263
|
+
" event (e.g. subscription_started) must NOT fire on a renewal/recurring",
|
|
1264
|
+
" path; look for RENEW_CARD / is_recurring / type branches in the SAME",
|
|
1265
|
+
" handler. If one path handles both cases, branch and emit the right",
|
|
1266
|
+
" event per case.",
|
|
1267
|
+
"3. Discriminators \u2014 billing/subscription events carry the properties that",
|
|
1268
|
+
" tell cases apart when the code exposes them (charge_type / is_recurring",
|
|
1269
|
+
" / payment_source / amount / plan). Add the ones you missed.",
|
|
1270
|
+
"4. Coverage \u2014 every gateway/callback path that should emit an event does;",
|
|
1271
|
+
" no recurring or secondary path left as a dead zone.",
|
|
1272
|
+
"Change ONLY what is genuinely wrong or incomplete \u2014 do not churn correct",
|
|
1273
|
+
"calls or re-explore the whole repo. Be surgical. End with a one-line,",
|
|
1274
|
+
"plain-text note of what you corrected, or 'No corrections needed.'."
|
|
1275
|
+
].join("\n");
|
|
1276
|
+
const review = await runPass({
|
|
1277
|
+
prompt: reviewPrompt,
|
|
1278
|
+
systemPrompt: systemPrompt9,
|
|
1279
|
+
repoPath,
|
|
1280
|
+
model: config.model,
|
|
1281
|
+
effort: config.effort,
|
|
1282
|
+
maxTurns: 25,
|
|
1283
|
+
progress
|
|
1284
|
+
});
|
|
1285
|
+
costUsd += review.costUsd;
|
|
1286
|
+
if (review.summary) summaries.push(`Review:
|
|
1287
|
+
${review.summary}`);
|
|
1183
1288
|
}
|
|
1184
1289
|
return {
|
|
1185
1290
|
summary: summaries.join("\n\n"),
|
|
@@ -1785,7 +1890,7 @@ async function main() {
|
|
|
1785
1890
|
return;
|
|
1786
1891
|
}
|
|
1787
1892
|
if ("version" in parsed) {
|
|
1788
|
-
console.log("0.1.
|
|
1893
|
+
console.log("0.1.11");
|
|
1789
1894
|
return;
|
|
1790
1895
|
}
|
|
1791
1896
|
try {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@whisperr/wizard",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.11",
|
|
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,
|