@whisperr/wizard 0.1.0 → 0.1.2
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/README.md +25 -133
- package/dist/index.js +151 -93
- package/package.json +1 -1
- package/dist/index.js.map +0 -1
package/README.md
CHANGED
|
@@ -1,151 +1,43 @@
|
|
|
1
|
-
#
|
|
1
|
+
# @whisperr/wizard
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
> authenticates with their onboarded account, and an AI coding agent wires up
|
|
5
|
-
> `identify()` and the exact business events Whisperr needs — then they push and
|
|
6
|
-
> deploy. Inspired by the PostHog wizard, but onboarding-aware.
|
|
7
|
-
|
|
8
|
-
Run it in your project root. No install, no setup — `npx` fetches the wizard and
|
|
9
|
-
its dependencies (including the coding-agent runtime) and runs it from the
|
|
10
|
-
current directory. The only thing you do is authenticate.
|
|
3
|
+
Integrate Whisperr into your app with one command.
|
|
11
4
|
|
|
12
5
|
```bash
|
|
13
|
-
cd your-app
|
|
14
6
|
npx @whisperr/wizard
|
|
15
7
|
```
|
|
16
8
|
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
detect stack ─▶ authenticate (browser) ─▶ load manifest ─▶ git checkpoint ─▶ agent edits ─▶ verify first event
|
|
21
|
-
```
|
|
22
|
-
|
|
23
|
-
1. **Detect** the repo's language/framework (Flutter, Web/JS, Next.js, React
|
|
24
|
-
Native, Swift…).
|
|
25
|
-
2. **Authenticate** via an OAuth device flow — the browser opens, the user (who
|
|
26
|
-
is already logged in from onboarding) approves, and the CLI gets a short-lived
|
|
27
|
-
session token bound to their app.
|
|
28
|
-
3. **Load the integration manifest** from whisperr-go: the ingestion API key, the
|
|
29
|
-
specific snake_case events to instrument, the interventions each event drives,
|
|
30
|
-
and `identify()` guidance — all derived from the user's onboarding.
|
|
31
|
-
4. **Checkpoint** with git so the auto-applied edits are reversible.
|
|
32
|
-
5. **Run the coding agent** (Claude Agent SDK) inside the repo: add the SDK
|
|
33
|
-
dependency, initialize it, wire `identify()`, and place `track()` calls at the
|
|
34
|
-
correct call sites for each manifest event.
|
|
35
|
-
6. **Verify** by polling for the first event — the "Whisperr is receiving
|
|
36
|
-
events ✓" moment, which also lights up the dashboard.
|
|
37
|
-
|
|
38
|
-
## Why it's better than a generic wizard
|
|
39
|
-
|
|
40
|
-
Whisperr already knows **what the app needs to track** (from onboarding), so the
|
|
41
|
-
agent doesn't just install a snippet — it instruments the *specific named events*
|
|
42
|
-
that drive this customer's churn interventions, at the right places in their code.
|
|
9
|
+
Run it in your project's root. The wizard detects your framework, opens your
|
|
10
|
+
browser to sign in to Whisperr, and wires up the Whisperr SDK for you — then you
|
|
11
|
+
review the changes, commit, and deploy.
|
|
43
12
|
|
|
44
|
-
##
|
|
45
|
-
|
|
46
|
-
The core is dumb about any specific SDK. All SDK knowledge lives in **playbooks**
|
|
47
|
-
(`src/core/playbooks/`). Each playbook carries:
|
|
48
|
-
|
|
49
|
-
- a `detect()` function (how to recognize the stack),
|
|
50
|
-
- the package reference,
|
|
51
|
-
- an SDK-specific **system prompt** (the best-practice integration guide), and
|
|
52
|
-
- an optional verify command.
|
|
53
|
-
|
|
54
|
-
**Adding a new SDK = adding one playbook file** and registering it in
|
|
55
|
-
`playbooks/index.ts`. Nothing else changes.
|
|
56
|
-
|
|
57
|
-
```
|
|
58
|
-
src/
|
|
59
|
-
index.ts CLI entry + arg parsing
|
|
60
|
-
cli.ts orchestration + terminal UX (@clack/prompts)
|
|
61
|
-
types.ts shared contracts
|
|
62
|
-
ui/ theme + banner (signal-blue, tasteful)
|
|
63
|
-
core/
|
|
64
|
-
config.ts env/flag resolution
|
|
65
|
-
detect.ts runs all playbook detectors
|
|
66
|
-
auth.ts device-authorization flow (+ offline stub)
|
|
67
|
-
manifest.ts fetch integration manifest (+ mock)
|
|
68
|
-
agent.ts Claude Agent SDK runner (file edits)
|
|
69
|
-
git.ts checkpoint + diff + revert hint
|
|
70
|
-
verify.ts first-event activation poll
|
|
71
|
-
playbooks/ ← the only SDK-aware code
|
|
72
|
-
shared-prompt.ts base wizard prompt + manifest renderer
|
|
73
|
-
flutter.ts (available)
|
|
74
|
-
web.ts (planned)
|
|
75
|
-
nextjs.ts (planned)
|
|
76
|
-
react-native.ts (planned)
|
|
77
|
-
swift.ts (planned)
|
|
78
|
-
```
|
|
13
|
+
## What it does
|
|
79
14
|
|
|
80
|
-
|
|
15
|
+
- **Detects your stack** — Flutter, and more frameworks coming soon.
|
|
16
|
+
- **Authenticates** with your Whisperr account in the browser.
|
|
17
|
+
- **Sets up the SDK** — installs it, initializes it, identifies your users, and
|
|
18
|
+
instruments the product events your Whisperr workspace is configured for.
|
|
81
19
|
|
|
82
|
-
|
|
83
|
-
Anthropic key**:
|
|
20
|
+
## Requirements
|
|
84
21
|
|
|
85
|
-
-
|
|
86
|
-
|
|
87
|
-
`ANTHROPIC_AUTH_TOKEN`. The gateway injects the real key server-side and meters
|
|
88
|
-
usage per app.
|
|
89
|
-
- **Local dev:** set `WHISPERR_WIZARD_DIRECT_ANTHROPIC_KEY` (or `ANTHROPIC_API_KEY`)
|
|
90
|
-
and run `--offline` to exercise the whole flow against a real model + a mock
|
|
91
|
-
manifest, with no backend.
|
|
22
|
+
- Node.js 18.17+
|
|
23
|
+
- A Whisperr account with onboarding completed
|
|
92
24
|
|
|
93
|
-
|
|
94
|
-
`claude-sonnet-4-6`).
|
|
95
|
-
|
|
96
|
-
## Contributing / local dev
|
|
97
|
-
|
|
98
|
-
Only needed if you're working **on** the wizard (not using it). End users just
|
|
99
|
-
run `npx @whisperr/wizard`.
|
|
25
|
+
## Usage
|
|
100
26
|
|
|
101
27
|
```bash
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
npm run dev:local -- --offline ../some-flutter-app
|
|
28
|
+
# in your project root
|
|
29
|
+
npx @whisperr/wizard
|
|
105
30
|
```
|
|
106
31
|
|
|
107
|
-
|
|
108
|
-
payment_failed, subscription_cancelled) so you can watch the agent integrate a
|
|
109
|
-
real repo end-to-end without the backend.
|
|
110
|
-
|
|
111
|
-
To publish: `npm publish` (the package is scoped `@whisperr` with restricted
|
|
112
|
-
access; `prepublishOnly` runs the build). Once published, `npx @whisperr/wizard`
|
|
113
|
-
works for anyone with access.
|
|
114
|
-
|
|
115
|
-
## Backend (whisperr-go — IMPLEMENTED)
|
|
116
|
-
|
|
117
|
-
The wizard talks to these endpoints on the runtime API (default
|
|
118
|
-
`https://api.whisperr.net`), all implemented in `whisperr-go/internal/wizard/`:
|
|
32
|
+
Options:
|
|
119
33
|
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
| `GET /wizard/manifest` | wizard session | → `IntegrationManifest`: **mints the ingestion key** + events + interventions + identify |
|
|
126
|
-
| `GET /wizard/first-event` | wizard session | → `{ received, event_type? }` for the activation poll |
|
|
127
|
-
| `POST /wizard/llm/*` | wizard session | Anthropic-compatible gateway: injects Whisperr's real key, streams the response |
|
|
128
|
-
|
|
129
|
-
whisperr-go env to set:
|
|
130
|
-
- `WHISPERR_WIZARD_ANTHROPIC_API_KEY` (or `ANTHROPIC_API_KEY`) — the gateway's real key
|
|
131
|
-
- `WHISPERR_WIZARD_ACTIVATE_URL` — the watch approval page (default `https://app.whisperr.net/wizard/activate`)
|
|
132
|
-
- `WHISPERR_PUBLIC_API_BASE_URL` — base URL surfaced in the manifest for the SDK (default `https://api.whisperr.net`)
|
|
133
|
-
- `WHISPERR_WIZARD_LLM_UPSTREAM` — Anthropic base (default `https://api.anthropic.com`)
|
|
134
|
-
|
|
135
|
-
Run migration `0010_wizard_device_authorizations.sql` (`whisperr migrate up`).
|
|
136
|
-
|
|
137
|
-
## Still to build
|
|
138
|
-
|
|
139
|
-
- The `/wizard/activate` page in **whisperr-watch** (reuse existing Supabase
|
|
140
|
-
login; show the `user_code`, let the user pick an app, POST to
|
|
141
|
-
`/dashboard/wizard/approve`).
|
|
142
|
-
- The `@whisperr/web` SDK (then flip the web/nextjs playbooks to `available`).
|
|
34
|
+
```
|
|
35
|
+
--api <url> Point at a specific Whisperr API (advanced)
|
|
36
|
+
-h, --help Show help
|
|
37
|
+
-v, --version Show version
|
|
38
|
+
```
|
|
143
39
|
|
|
144
|
-
|
|
40
|
+
---
|
|
145
41
|
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
- ✅ whisperr-go backend: device auth, manifest (with key minting), first-event,
|
|
149
|
-
LLM gateway, migration. Builds + vets clean.
|
|
150
|
-
- ⏳ whisperr-watch activate page.
|
|
151
|
-
- ⏳ `@whisperr/web` SDK (Flutter is the first live target — SDK on pub.dev).
|
|
42
|
+
Whisperr — predict churn, automate interventions, recover revenue.
|
|
43
|
+
[whisperr.net](https://whisperr.net)
|
package/dist/index.js
CHANGED
|
@@ -6,7 +6,7 @@ import * as p from "@clack/prompts";
|
|
|
6
6
|
|
|
7
7
|
// src/core/config.ts
|
|
8
8
|
var DEFAULT_API_BASE = "https://api.whisperr.net";
|
|
9
|
-
var DEFAULT_MODEL = "claude-
|
|
9
|
+
var DEFAULT_MODEL = "claude-sonnet-4-6";
|
|
10
10
|
function resolveConfig(flags = {}) {
|
|
11
11
|
const apiBaseUrl = (flags.apiBaseUrl ?? process.env.WHISPERR_WIZARD_API_BASE ?? DEFAULT_API_BASE).replace(/\/+$/, "");
|
|
12
12
|
const llmBaseUrl = (process.env.WHISPERR_WIZARD_LLM_BASE ?? `${apiBaseUrl}/wizard/llm`).replace(/\/+$/, "");
|
|
@@ -18,7 +18,7 @@ function resolveConfig(flags = {}) {
|
|
|
18
18
|
apiBaseUrl,
|
|
19
19
|
llmBaseUrl,
|
|
20
20
|
model: flags.model ?? process.env.WHISPERR_WIZARD_MODEL ?? DEFAULT_MODEL,
|
|
21
|
-
maxTurns: Number(process.env.WHISPERR_WIZARD_MAX_TURNS ??
|
|
21
|
+
maxTurns: Number(process.env.WHISPERR_WIZARD_MAX_TURNS ?? 55),
|
|
22
22
|
directAnthropicKey,
|
|
23
23
|
offline
|
|
24
24
|
};
|
|
@@ -596,84 +596,83 @@ import { query } from "@anthropic-ai/claude-agent-sdk";
|
|
|
596
596
|
|
|
597
597
|
// src/core/playbooks/shared-prompt.ts
|
|
598
598
|
var BASE_WIZARD_PROMPT = `
|
|
599
|
-
You are the Whisperr Wizard \u2014 an expert integration agent
|
|
600
|
-
|
|
601
|
-
|
|
599
|
+
You are the Whisperr Wizard \u2014 an expert integration agent running INSIDE a
|
|
600
|
+
customer's code repository. Your job: wire up the Whisperr SDK so the product
|
|
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).
|
|
602
603
|
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
plus traits and contact channels, called right after the user is known
|
|
606
|
-
(login / session restore / signup).
|
|
607
|
-
2. track() \u2014 emit the specific business events listed in the integration
|
|
608
|
-
manifest, at the exact places in the code where those user actions happen.
|
|
609
|
-
These named events drive the customer's churn interventions, so placing
|
|
610
|
-
them at the correct call sites matters more than anything else you do.
|
|
604
|
+
You are billed per step and the user is watching the clock. Work FAST and
|
|
605
|
+
DECISIVELY. Most failures come from over-exploring \u2014 do not do that.
|
|
611
606
|
|
|
612
|
-
|
|
613
|
-
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
commented TODO with the exact Whisperr call to make, and report it at the end
|
|
620
|
-
rather than firing the event from a wrong place.
|
|
621
|
-
- Never invent event types. Use the exact snake_case event_type strings from the
|
|
622
|
-
manifest. Custom data goes under properties.
|
|
623
|
-
- Keep the API key out of source where the SDK/platform supports env/secrets;
|
|
624
|
-
otherwise place it where the manifest guidance says and flag it.
|
|
625
|
-
- Match the repository's existing conventions (imports, formatting, state
|
|
626
|
-
management, file layout).
|
|
607
|
+
EFFICIENCY \u2014 non-negotiable:
|
|
608
|
+
- Use Grep and Glob to find code. Do NOT read whole files; read the smallest
|
|
609
|
+
slice you need.
|
|
610
|
+
- NEVER re-read a file you just edited. Trust your edit.
|
|
611
|
+
- Do not run the app, build, or heavy test suites. At most one quick static
|
|
612
|
+
check, and only if the SDK guide tells you to.
|
|
613
|
+
- Make several related edits in a row before pausing to think.
|
|
627
614
|
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
615
|
+
DECISIVENESS:
|
|
616
|
+
- When you find the right spot, edit it and move on immediately.
|
|
617
|
+
- If you cannot find a clear place for something within ~2 searches, STOP
|
|
618
|
+
looking for it. Either drop a single-line \`// TODO(whisperr): track(...)\`
|
|
619
|
+
if an obvious spot exists, or just note it as a follow-up. Do not keep hunting.
|
|
620
|
+
|
|
621
|
+
SCOPE REALISM:
|
|
622
|
+
- Not every event exists in THIS app. Many are server-side (billing webhooks,
|
|
623
|
+
delivery callbacks) or simply absent. That is expected. Instrument what
|
|
624
|
+
clearly exists on the client; list the rest as follow-ups. Do not treat a
|
|
625
|
+
missing call site as a problem to solve.
|
|
626
|
+
|
|
627
|
+
CORRECTNESS:
|
|
628
|
+
- Match the app's existing conventions (imports, state management, file layout).
|
|
629
|
+
- Use the EXACT snake_case event names given. Never invent or rename events.
|
|
630
|
+
- A wrongly-placed business event corrupts churn data \u2014 worse than a missing one.
|
|
631
|
+
Only place an event where you are confident the user action truly happens.
|
|
632
|
+
|
|
633
|
+
End every task with a short summary: what you changed, and any follow-ups the
|
|
634
|
+
human should handle (events you intentionally skipped + why).
|
|
633
635
|
`.trim();
|
|
634
|
-
function
|
|
636
|
+
function renderIdentifyBrief(m) {
|
|
635
637
|
const lines = [];
|
|
636
|
-
lines.push(
|
|
637
|
-
lines.push("");
|
|
638
|
+
lines.push(`App: ${m.appName ?? m.appId}`);
|
|
638
639
|
lines.push(`Ingestion base URL: ${m.ingestionBaseUrl}`);
|
|
639
640
|
lines.push(`Ingestion API key: ${m.ingestionApiKey}`);
|
|
640
641
|
if (m.businessContext) {
|
|
641
642
|
lines.push("");
|
|
642
|
-
lines.push(
|
|
643
|
-
lines.push(m.businessContext);
|
|
643
|
+
lines.push(`Context: ${m.businessContext}`);
|
|
644
644
|
}
|
|
645
645
|
lines.push("");
|
|
646
|
-
lines.push("
|
|
646
|
+
lines.push("identify():");
|
|
647
647
|
if (m.identify.traits?.length) {
|
|
648
|
-
lines.push("
|
|
648
|
+
lines.push(" Send these traits when available:");
|
|
649
649
|
for (const t of m.identify.traits) {
|
|
650
|
-
lines.push(`
|
|
650
|
+
lines.push(` - ${t.name}${t.description ? ` (${t.description})` : ""}`);
|
|
651
651
|
}
|
|
652
652
|
}
|
|
653
653
|
if (m.identify.channels?.length) {
|
|
654
|
-
lines.push(`Channels in use: ${m.identify.channels.join(", ")}`);
|
|
654
|
+
lines.push(` Channels in use: ${m.identify.channels.join(", ")}`);
|
|
655
655
|
}
|
|
656
|
-
if (m.identify.notes) lines.push(m.identify.notes);
|
|
657
|
-
lines.
|
|
658
|
-
|
|
656
|
+
if (m.identify.notes) lines.push(` ${m.identify.notes}`);
|
|
657
|
+
return lines.join("\n");
|
|
658
|
+
}
|
|
659
|
+
function renderEventsBrief(m) {
|
|
659
660
|
const events = [...m.events].sort(
|
|
660
661
|
(a, b) => (b.importance ?? 0) - (a.importance ?? 0)
|
|
661
662
|
);
|
|
663
|
+
const lines = [];
|
|
664
|
+
lines.push(
|
|
665
|
+
`${events.length} candidate events (highest-impact first). Emit each with its EXACT name via track('<event_type>', { ...properties }).`
|
|
666
|
+
);
|
|
667
|
+
lines.push("");
|
|
662
668
|
for (const e of events) {
|
|
663
|
-
|
|
664
|
-
lines.push(
|
|
665
|
-
if (e.description) lines.push(e.description);
|
|
669
|
+
const drivers = e.interventions?.length ? ` \u2014 drives: ${e.interventions.map((i) => i.label ?? i.code).join(", ")}` : "";
|
|
670
|
+
lines.push(`- ${e.eventType}${e.label ? ` (${e.label})` : ""}${drivers}`);
|
|
671
|
+
if (e.description) lines.push(` ${e.description}`);
|
|
666
672
|
if (e.properties?.length) {
|
|
667
|
-
lines.push(
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
` - ${p2.name}${p2.required ? " (required)" : ""}${p2.description ? ` \u2014 ${p2.description}` : ""}`
|
|
671
|
-
);
|
|
672
|
-
}
|
|
673
|
-
}
|
|
674
|
-
if (e.interventions?.length) {
|
|
675
|
-
const drv = e.interventions.map((i) => i.label ?? i.code).join(", ");
|
|
676
|
-
lines.push(`Drives: ${drv}`);
|
|
673
|
+
lines.push(
|
|
674
|
+
` properties: ${e.properties.map((p2) => p2.name).join(", ")}`
|
|
675
|
+
);
|
|
677
676
|
}
|
|
678
677
|
}
|
|
679
678
|
return lines.join("\n");
|
|
@@ -684,40 +683,94 @@ async function runIntegrationAgent(opts) {
|
|
|
684
683
|
const { repoPath, config, session, playbook, manifest, progress } = opts;
|
|
685
684
|
applyModelAuthEnv(config, session);
|
|
686
685
|
const systemPrompt6 = [BASE_WIZARD_PROMPT, playbook.systemPrompt].join("\n\n");
|
|
687
|
-
const
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
686
|
+
const started = Date.now();
|
|
687
|
+
let costUsd = 0;
|
|
688
|
+
const summaries = [];
|
|
689
|
+
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
|
+
const corePrompt = [
|
|
692
|
+
`Integrate the Whisperr ${playbook.target.displayName} SDK \u2014 CORE SETUP ONLY.`,
|
|
692
693
|
`Project root: ${repoPath}`,
|
|
693
694
|
"",
|
|
694
|
-
"
|
|
695
|
-
|
|
696
|
-
"
|
|
695
|
+
"Do exactly these, then stop:",
|
|
696
|
+
`1. Add the Whisperr SDK dependency and install it (${playbook.packageRef}).`,
|
|
697
|
+
"2. Initialize it once at app startup with the key + base URL below.",
|
|
698
|
+
"3. Wire identify() where the end-user becomes known (login / signup /",
|
|
699
|
+
" session restore), and reset() on logout.",
|
|
697
700
|
"",
|
|
698
|
-
"
|
|
699
|
-
|
|
700
|
-
"-----
|
|
701
|
-
|
|
701
|
+
"Do NOT instrument product events yet \u2014 that is a separate step." + verifyHint,
|
|
702
|
+
"",
|
|
703
|
+
"----- IDENTIFY -----",
|
|
704
|
+
renderIdentifyBrief(manifest),
|
|
705
|
+
"----- END -----"
|
|
702
706
|
].join("\n");
|
|
703
|
-
const
|
|
707
|
+
const core = await runPass({
|
|
708
|
+
prompt: corePrompt,
|
|
709
|
+
systemPrompt: systemPrompt6,
|
|
710
|
+
repoPath,
|
|
711
|
+
model: config.model,
|
|
712
|
+
maxTurns: 20,
|
|
713
|
+
progress
|
|
714
|
+
});
|
|
715
|
+
costUsd += core.costUsd;
|
|
716
|
+
if (core.summary) summaries.push(`Core setup:
|
|
717
|
+
${core.summary}`);
|
|
718
|
+
let eventsComplete = true;
|
|
719
|
+
if (manifest.events.length > 0) {
|
|
720
|
+
progress?.onPhase?.("Instrumenting your events");
|
|
721
|
+
const eventsPrompt = [
|
|
722
|
+
`The Whisperr ${playbook.target.displayName} SDK is already installed,`,
|
|
723
|
+
"initialized, and identify() is wired. Now add track() calls for product",
|
|
724
|
+
"events.",
|
|
725
|
+
"",
|
|
726
|
+
"How to work:",
|
|
727
|
+
"- Go in the order listed (highest-impact first).",
|
|
728
|
+
"- For each event, Grep for where that user action happens. If there is a",
|
|
729
|
+
" clear client-side call site, add the track() call with the exact name.",
|
|
730
|
+
" If not, SKIP it (most server-side events won't exist here) \u2014 do not hunt.",
|
|
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.",
|
|
733
|
+
"",
|
|
734
|
+
"----- EVENTS -----",
|
|
735
|
+
renderEventsBrief(manifest),
|
|
736
|
+
"----- END -----"
|
|
737
|
+
].join("\n");
|
|
738
|
+
const events = await runPass({
|
|
739
|
+
prompt: eventsPrompt,
|
|
740
|
+
systemPrompt: systemPrompt6,
|
|
741
|
+
repoPath,
|
|
742
|
+
model: config.model,
|
|
743
|
+
maxTurns: config.maxTurns,
|
|
744
|
+
progress
|
|
745
|
+
});
|
|
746
|
+
costUsd += events.costUsd;
|
|
747
|
+
eventsComplete = !events.maxedOut;
|
|
748
|
+
if (events.summary) summaries.push(`Events:
|
|
749
|
+
${events.summary}`);
|
|
750
|
+
}
|
|
751
|
+
return {
|
|
752
|
+
summary: summaries.join("\n\n"),
|
|
753
|
+
costUsd,
|
|
754
|
+
durationMs: Date.now() - started,
|
|
755
|
+
coreOk: core.ok,
|
|
756
|
+
eventsComplete
|
|
757
|
+
};
|
|
758
|
+
}
|
|
759
|
+
async function runPass(opts) {
|
|
760
|
+
const { prompt, systemPrompt: systemPrompt6, repoPath, model, maxTurns, progress } = opts;
|
|
704
761
|
let summary = "";
|
|
705
|
-
let costUsd;
|
|
762
|
+
let costUsd = 0;
|
|
706
763
|
let ok = false;
|
|
764
|
+
let maxedOut = false;
|
|
707
765
|
const response = query({
|
|
708
766
|
prompt,
|
|
709
767
|
options: {
|
|
710
|
-
model
|
|
768
|
+
model,
|
|
711
769
|
cwd: repoPath,
|
|
712
770
|
systemPrompt: systemPrompt6,
|
|
713
|
-
// Full file-system agent toolset.
|
|
714
771
|
allowedTools: ["Read", "Edit", "Write", "Bash", "Glob", "Grep"],
|
|
715
|
-
// Fully autonomous edits (the user opted into auto-apply; the wizard takes
|
|
716
|
-
// a git checkpoint before this runs so revert is one command).
|
|
717
772
|
permissionMode: "bypassPermissions",
|
|
718
|
-
maxTurns
|
|
719
|
-
// Don't pick up the user's own CLAUDE.md / settings — the wizard's prompt
|
|
720
|
-
// is the single source of truth.
|
|
773
|
+
maxTurns,
|
|
721
774
|
settingSources: []
|
|
722
775
|
}
|
|
723
776
|
});
|
|
@@ -732,12 +785,12 @@ When finished, run \`${playbook.verifyCommand}\` and fix anything it flags.` : "
|
|
|
732
785
|
}
|
|
733
786
|
} else if (message.type === "result") {
|
|
734
787
|
ok = message.subtype === "success";
|
|
735
|
-
|
|
788
|
+
maxedOut = (message.subtype ?? "").includes("max_turns");
|
|
789
|
+
costUsd = message.total_cost_usd ?? 0;
|
|
736
790
|
summary = message.result ?? extractLastText(message) ?? summary;
|
|
737
791
|
}
|
|
738
792
|
}
|
|
739
|
-
|
|
740
|
-
return { summary, costUsd, ok, durationMs: Date.now() - started };
|
|
793
|
+
return { summary, costUsd, ok, maxedOut };
|
|
741
794
|
}
|
|
742
795
|
function applyModelAuthEnv(config, session) {
|
|
743
796
|
if (config.directAnthropicKey) {
|
|
@@ -937,8 +990,8 @@ Flutter is live today; ${theme.bright(
|
|
|
937
990
|
);
|
|
938
991
|
}
|
|
939
992
|
const spin = p.spinner();
|
|
940
|
-
|
|
941
|
-
|
|
993
|
+
let phaseLabel = "Integrating";
|
|
994
|
+
spin.start(theme.bright(phaseLabel) + theme.muted(" \u2014 the agent is working in your code"));
|
|
942
995
|
const outcome = await runIntegrationAgent({
|
|
943
996
|
repoPath,
|
|
944
997
|
config,
|
|
@@ -946,20 +999,22 @@ Flutter is live today; ${theme.bright(
|
|
|
946
999
|
playbook: chosen.playbook,
|
|
947
1000
|
manifest,
|
|
948
1001
|
progress: {
|
|
1002
|
+
onPhase(label) {
|
|
1003
|
+
phaseLabel = label;
|
|
1004
|
+
spin.message(theme.bright(label));
|
|
1005
|
+
},
|
|
949
1006
|
onActivity(line) {
|
|
950
|
-
|
|
951
|
-
spin.message(theme.bright("Integrating") + theme.muted(` \u2014 ${line}`));
|
|
1007
|
+
spin.message(theme.bright(phaseLabel) + theme.muted(` \u2014 ${line}`));
|
|
952
1008
|
}
|
|
953
1009
|
}
|
|
954
1010
|
}).catch((err) => {
|
|
955
|
-
spin.stop(theme.alert("Integration
|
|
1011
|
+
spin.stop(theme.alert("Integration stopped"));
|
|
956
1012
|
p.log.error(err.message);
|
|
957
1013
|
return null;
|
|
958
1014
|
});
|
|
959
1015
|
if (!outcome) return 1;
|
|
960
|
-
|
|
961
|
-
|
|
962
|
-
);
|
|
1016
|
+
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
|
+
spin.stop(stopLabel);
|
|
963
1018
|
const files = await changedFiles(repoPath, checkpoint);
|
|
964
1019
|
if (files.length) {
|
|
965
1020
|
p.note(files.map((f) => theme.muted("\u2022 ") + f).join("\n"), "Files changed");
|
|
@@ -967,6 +1022,11 @@ Flutter is live today; ${theme.bright(
|
|
|
967
1022
|
if (outcome.summary.trim()) {
|
|
968
1023
|
p.log.message(outcome.summary.trim());
|
|
969
1024
|
}
|
|
1025
|
+
p.log.info(
|
|
1026
|
+
theme.muted(
|
|
1027
|
+
`${files.length} file${files.length === 1 ? "" : "s"} changed \xB7 ~$${outcome.costUsd.toFixed(2)} \xB7 ${Math.round(outcome.durationMs / 1e3)}s`
|
|
1028
|
+
)
|
|
1029
|
+
);
|
|
970
1030
|
if (!config.offline) {
|
|
971
1031
|
p.log.step(
|
|
972
1032
|
theme.bright("Run your app once") + theme.muted(" and trigger any tracked action \u2014 I'll watch for the first event.")
|
|
@@ -997,7 +1057,6 @@ Flutter is live today; ${theme.bright(
|
|
|
997
1057
|
].filter(Boolean).join("\n");
|
|
998
1058
|
p.note(nextSteps, theme.bright("Next"));
|
|
999
1059
|
p.outro(theme.signal("\u2301 ") + theme.bright("Whisperr is wired in."));
|
|
1000
|
-
void lastLine;
|
|
1001
1060
|
return 0;
|
|
1002
1061
|
}
|
|
1003
1062
|
async function chooseTarget(detections) {
|
|
@@ -1134,7 +1193,7 @@ async function main() {
|
|
|
1134
1193
|
return;
|
|
1135
1194
|
}
|
|
1136
1195
|
if ("version" in parsed) {
|
|
1137
|
-
console.log("0.1.
|
|
1196
|
+
console.log("0.1.2");
|
|
1138
1197
|
return;
|
|
1139
1198
|
}
|
|
1140
1199
|
try {
|
|
@@ -1146,4 +1205,3 @@ async function main() {
|
|
|
1146
1205
|
}
|
|
1147
1206
|
}
|
|
1148
1207
|
void main();
|
|
1149
|
-
//# sourceMappingURL=index.js.map
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@whisperr/wizard",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.2",
|
|
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,
|
package/dist/index.js.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/cli.ts","../src/core/config.ts","../src/core/detect.ts","../src/core/playbooks/flutter.ts","../src/core/playbooks/nextjs.ts","../src/core/playbooks/web.ts","../src/core/playbooks/react-native.ts","../src/core/playbooks/swift.ts","../src/core/playbooks/index.ts","../src/core/auth.ts","../src/ui/theme.ts","../src/core/manifest.ts","../src/core/agent.ts","../src/core/playbooks/shared-prompt.ts","../src/core/git.ts","../src/core/verify.ts","../src/ui/banner.ts","../src/index.ts"],"sourcesContent":["import { resolve } from \"node:path\";\nimport * as p from \"@clack/prompts\";\nimport type { CliFlags } from \"./core/config.js\";\nimport { resolveConfig } from \"./core/config.js\";\nimport { detectStack } from \"./core/detect.js\";\nimport { playbookByTargetId, ALL_PLAYBOOKS } from \"./core/playbooks/index.js\";\nimport { authenticate } from \"./core/auth.js\";\nimport { fetchManifest } from \"./core/manifest.js\";\nimport { runIntegrationAgent } from \"./core/agent.js\";\nimport { takeCheckpoint, changedFiles, revertHint } from \"./core/git.js\";\nimport { pollFirstEvent } from \"./core/verify.js\";\nimport { banner } from \"./ui/banner.js\";\nimport { theme } from \"./ui/theme.js\";\nimport type { Detection, Playbook } from \"./types.js\";\n\nexport interface RunOptions extends CliFlags {\n /** Target repo (defaults to cwd). */\n path?: string;\n}\n\nexport async function run(options: RunOptions): Promise<number> {\n const repoPath = resolve(options.path ?? process.cwd());\n const config = resolveConfig(options);\n\n // eslint-disable-next-line no-console\n console.log(banner());\n p.intro(theme.signal(\"Let's wire Whisperr into your app.\"));\n\n if (config.offline) {\n p.log.warn(\n theme.warn(\"offline mode\") +\n theme.muted(\" — using a demo manifest, no account needed.\"),\n );\n }\n\n // 1. Detect the stack.\n const detections = await withSpinner(\"Scanning your repository\", () =>\n detectStack(repoPath),\n );\n const chosen = await chooseTarget(detections);\n if (!chosen) {\n p.cancel(\"No supported stack selected.\");\n return 1;\n }\n\n if (chosen.playbook.target.availability === \"planned\") {\n p.note(\n `We can detect ${theme.bright(chosen.playbook.target.displayName)} but the ` +\n `Whisperr SDK for it isn't shipped yet.\\n` +\n `Flutter is live today; ${theme.bright(\n chosen.playbook.target.displayName,\n )} is next on the roadmap.`,\n theme.warn(\"SDK coming soon\"),\n );\n const cont = await p.confirm({\n message: \"Continue anyway and let the agent scaffold against the planned SDK?\",\n initialValue: false,\n });\n if (p.isCancel(cont) || !cont) {\n p.outro(theme.muted(\"No problem — come back when the SDK lands.\"));\n return 0;\n }\n } else {\n p.log.success(\n `Detected ${theme.bright(chosen.playbook.target.displayName)} ` +\n theme.muted(`(${chosen.detection?.evidence.join(\", \") ?? \"selected\"})`),\n );\n }\n\n // 2. Authenticate (device flow opens the browser).\n let session;\n try {\n session = config.offline\n ? await authenticate(config)\n : await withBrowserAuth(config);\n } catch (err) {\n p.cancel(theme.alert((err as Error).message));\n return 1;\n }\n\n // 3. Pull the integration manifest (events/interventions/key).\n const manifest = await withSpinner(\"Loading your onboarding context\", () =>\n fetchManifest(config, session, chosen.playbook.target.id),\n );\n p.note(\n summarizeManifest(manifest),\n theme.signal(`Plan for ${manifest.appName ?? manifest.appId}`),\n );\n\n // 4. Git checkpoint so auto-applied edits are reversible.\n const checkpoint = await takeCheckpoint(repoPath);\n if (!checkpoint.isRepo) {\n p.log.warn(\n theme.warn(\"not a git repo\") +\n theme.muted(\" — edits will apply with no automatic undo.\"),\n );\n }\n\n // 5. Run the coding agent.\n const spin = p.spinner();\n spin.start(theme.bright(\"Integrating\") + theme.muted(\" — the agent is working in your code\"));\n let lastLine = \"\";\n const outcome = await runIntegrationAgent({\n repoPath,\n config,\n session,\n playbook: chosen.playbook,\n manifest,\n progress: {\n onActivity(line) {\n lastLine = line;\n spin.message(theme.bright(\"Integrating\") + theme.muted(` — ${line}`));\n },\n },\n }).catch((err) => {\n spin.stop(theme.alert(\"Integration failed\"));\n p.log.error((err as Error).message);\n return null;\n });\n\n if (!outcome) return 1;\n spin.stop(\n outcome.ok\n ? theme.success(\"Integration complete\")\n : theme.warn(\"Integration finished with notes\"),\n );\n\n // 6. Show what changed + the agent's summary.\n const files = await changedFiles(repoPath, checkpoint);\n if (files.length) {\n p.note(files.map((f) => theme.muted(\"• \") + f).join(\"\\n\"), \"Files changed\");\n }\n if (outcome.summary.trim()) {\n p.log.message(outcome.summary.trim());\n }\n\n // 7. Activation: wait for the first event (skipped offline).\n if (!config.offline) {\n p.log.step(\n theme.bright(\"Run your app once\") +\n theme.muted(\" and trigger any tracked action — I'll watch for the first event.\"),\n );\n const verifySpin = p.spinner();\n verifySpin.start(\"Waiting for the first event from your app\");\n const first = await pollFirstEvent(config, session, { timeoutMs: 120_000 });\n if (first.received) {\n verifySpin.stop(\n theme.success(\n `Whisperr is receiving events ✓` +\n (first.eventType ? theme.muted(` (first: ${first.eventType})`) : \"\"),\n ),\n );\n } else {\n verifySpin.stop(\n theme.warn(\n \"No events yet — that's fine. They'll flow once you run the app with the changes.\",\n ),\n );\n }\n }\n\n // 8. Outro with next steps + undo.\n const undo = revertHint(checkpoint);\n const nextSteps = [\n `${theme.signal(\"1.\")} Review the diff above.`,\n `${theme.signal(\"2.\")} Run / rebuild your app to start sending events.`,\n `${theme.signal(\"3.\")} Commit & deploy — Whisperr starts working on real users.`,\n undo ? theme.muted(`Undo everything: ${undo}`) : \"\",\n ]\n .filter(Boolean)\n .join(\"\\n\");\n p.note(nextSteps, theme.bright(\"Next\"));\n p.outro(theme.signal(\"⌁ \") + theme.bright(\"Whisperr is wired in.\"));\n void lastLine;\n return 0;\n}\n\n// --- helpers ---\n\ninterface Chosen {\n playbook: Playbook;\n detection?: Detection;\n}\n\nasync function chooseTarget(detections: Detection[]): Promise<Chosen | null> {\n const top = detections[0];\n // Strong single match -> auto-pick.\n if (top && (detections.length === 1 || top.confidence >= 0.9)) {\n const playbook = playbookByTargetId(top.target.id);\n if (playbook) return { playbook, detection: top };\n }\n\n // Ambiguous or none -> ask, seeded by detections then all targets.\n const seen = new Set<string>();\n const options: { value: string; label: string; hint?: string }[] = [];\n for (const d of detections) {\n if (seen.has(d.target.id)) continue;\n seen.add(d.target.id);\n options.push({\n value: d.target.id,\n label: d.target.displayName,\n hint:\n (d.target.availability === \"available\" ? \"\" : \"coming soon · \") +\n `${Math.round(d.confidence * 100)}% match`,\n });\n }\n for (const pb of ALL_PLAYBOOKS) {\n if (seen.has(pb.target.id)) continue;\n options.push({\n value: pb.target.id,\n label: pb.target.displayName,\n hint: pb.target.availability === \"available\" ? undefined : \"coming soon\",\n });\n }\n\n const answer = await p.select({\n message: detections.length\n ? \"I found more than one possible stack — which should I integrate?\"\n : \"I couldn't auto-detect your stack. Which are you using?\",\n options,\n });\n if (p.isCancel(answer)) return null;\n const playbook = playbookByTargetId(answer as string);\n if (!playbook) return null;\n return {\n playbook,\n detection: detections.find((d) => d.target.id === answer),\n };\n}\n\nasync function withBrowserAuth(config: ReturnType<typeof resolveConfig>) {\n p.log.step(\n theme.bright(\"Authenticate\") +\n theme.muted(\" — opening your browser to approve this device…\"),\n );\n const spin = p.spinner();\n spin.start(\"Waiting for you to approve in the browser\");\n try {\n const session = await authenticate(config);\n spin.stop(theme.success(\"Authenticated ✓\"));\n return session;\n } catch (err) {\n spin.stop(theme.alert(\"Authentication failed\"));\n throw err;\n }\n}\n\nasync function withSpinner<T>(label: string, fn: () => Promise<T>): Promise<T> {\n const spin = p.spinner();\n spin.start(label);\n try {\n const result = await fn();\n spin.stop(theme.muted(label) + \" \" + theme.success(\"✓\"));\n return result;\n } catch (err) {\n spin.stop(theme.alert(`${label} — failed`));\n throw err;\n }\n}\n\nfunction summarizeManifest(m: {\n events: { eventType: string; importance?: number }[];\n identify: { channels?: string[] };\n}): string {\n const events = [...m.events]\n .sort((a, b) => (b.importance ?? 0) - (a.importance ?? 0))\n .map((e) => theme.muted(\"• \") + e.eventType);\n const channels = m.identify.channels?.length\n ? theme.muted(`channels: ${m.identify.channels.join(\", \")}`)\n : \"\";\n return [\n theme.bright(\"identify()\") + theme.muted(\" + \") + theme.bright(`${m.events.length} events`),\n ...events,\n channels,\n ]\n .filter(Boolean)\n .join(\"\\n\");\n}\n","import type { WizardConfig } from \"../types.js\";\n\n/**\n * Resolve wizard configuration from env + flags.\n *\n * Production: the CLI never carries an Anthropic key. It authenticates the user\n * (device flow), receives a short-lived wizard token, and points the Agent SDK\n * at Whisperr's Anthropic-compatible gateway via ANTHROPIC_BASE_URL +\n * ANTHROPIC_AUTH_TOKEN. The gateway injects the real key server-side and meters\n * usage per app.\n *\n * Local dev: set WHISPERR_WIZARD_DIRECT_ANTHROPIC_KEY (or ANTHROPIC_API_KEY) and\n * --offline to run the whole flow against a real model + a mock manifest, with\n * no backend required.\n */\n\nconst DEFAULT_API_BASE = \"https://api.whisperr.net\";\n// Current coding-agent default. Override with WHISPERR_WIZARD_MODEL.\n// claude-opus-4-8 — most capable, best for autonomous agentic edits\n// claude-sonnet-4-6 — faster/cheaper workhorse\nconst DEFAULT_MODEL = \"claude-opus-4-8\";\n\nexport interface CliFlags {\n offline?: boolean;\n apiBaseUrl?: string;\n model?: string;\n}\n\nexport function resolveConfig(flags: CliFlags = {}): WizardConfig {\n const apiBaseUrl = (\n flags.apiBaseUrl ??\n process.env.WHISPERR_WIZARD_API_BASE ??\n DEFAULT_API_BASE\n ).replace(/\\/+$/, \"\");\n\n const llmBaseUrl = (\n process.env.WHISPERR_WIZARD_LLM_BASE ?? `${apiBaseUrl}/wizard/llm`\n ).replace(/\\/+$/, \"\");\n\n const directAnthropicKey =\n process.env.WHISPERR_WIZARD_DIRECT_ANTHROPIC_KEY ??\n process.env.ANTHROPIC_API_KEY;\n\n const offline =\n flags.offline ??\n [\"1\", \"true\", \"yes\"].includes(\n (process.env.WHISPERR_WIZARD_OFFLINE ?? \"\").toLowerCase(),\n );\n\n return {\n apiBaseUrl,\n llmBaseUrl,\n model: flags.model ?? process.env.WHISPERR_WIZARD_MODEL ?? DEFAULT_MODEL,\n maxTurns: Number(process.env.WHISPERR_WIZARD_MAX_TURNS ?? 40),\n directAnthropicKey,\n offline,\n };\n}\n","import { readFile, stat, readdir } from \"node:fs/promises\";\nimport { join } from \"node:path\";\nimport type { Detection, DetectContext } from \"../types.js\";\nimport { ALL_PLAYBOOKS } from \"./playbooks/index.js\";\n\n/** Build a cached, bounded filesystem context rooted at `repoPath`. */\nexport function makeDetectContext(repoPath: string): DetectContext {\n const cache = new Map<string, string>();\n\n return {\n repoPath,\n async read(relPath: string): Promise<string> {\n if (cache.has(relPath)) return cache.get(relPath)!;\n let content = \"\";\n try {\n content = await readFile(join(repoPath, relPath), \"utf8\");\n } catch {\n content = \"\";\n }\n cache.set(relPath, content);\n return content;\n },\n async exists(relPath: string): Promise<boolean> {\n try {\n await stat(join(repoPath, relPath));\n return true;\n } catch {\n return false;\n }\n },\n async list(relDir: string): Promise<string[]> {\n try {\n const entries = await readdir(join(repoPath, relDir), {\n withFileTypes: true,\n });\n return entries.map((e) => e.name);\n } catch {\n return [];\n }\n },\n };\n}\n\n/**\n * Run every playbook's detector and return matches, highest confidence first.\n * Multiple matches are possible (e.g. a Next.js repo also matches generic web);\n * the caller decides whether to auto-pick or ask.\n */\nexport async function detectStack(repoPath: string): Promise<Detection[]> {\n const ctx = makeDetectContext(repoPath);\n const results = await Promise.all(\n ALL_PLAYBOOKS.map((p) => p.detect(ctx).catch(() => null)),\n );\n return results\n .filter((d): d is Detection => d !== null)\n .sort((a, b) => b.confidence - a.confidence);\n}\n","import type { Playbook, Detection, DetectContext } from \"../../types.js\";\n\nconst target = {\n id: \"flutter\",\n displayName: \"Flutter\",\n language: \"dart\",\n availability: \"available\" as const,\n};\n\nasync function detect(ctx: DetectContext): Promise<Detection | null> {\n const evidence: string[] = [];\n let confidence = 0;\n\n if (await ctx.exists(\"pubspec.yaml\")) {\n evidence.push(\"pubspec.yaml\");\n confidence += 0.6;\n const pubspec = await ctx.read(\"pubspec.yaml\");\n if (/\\bflutter\\s*:/.test(pubspec) || /sdk:\\s*flutter/.test(pubspec)) {\n evidence.push(\"flutter sdk in pubspec\");\n confidence += 0.3;\n }\n }\n if (await ctx.exists(\"lib/main.dart\")) {\n evidence.push(\"lib/main.dart\");\n confidence += 0.2;\n }\n if (confidence === 0) return null;\n return { target, confidence: Math.min(confidence, 1), evidence };\n}\n\nconst systemPrompt = `\n## SDK: Whisperr for Flutter (Dart) — package \\`whisperr\\`\n\nThis SDK is published on pub.dev. Integrate it as follows.\n\n1) Dependency. Add to pubspec.yaml under dependencies:\n whisperr: ^0.2.0\n Then the SDK expects \\`flutter pub get\\` to be run (run it via bash).\n\n2) Initialize once, early, before runApp(), in lib/main.dart:\n import 'package:whisperr/whisperr.dart';\n await Whisperr.initialize(\n apiKey: '<INGESTION_API_KEY from manifest>',\n baseUrl: '<INGESTION_BASE_URL from manifest>', // omit if it equals the SDK default\n );\n If main() is not async, make it async and \\`await\\` the initialize call, or use\n WidgetsFlutterBinding.ensureInitialized() first.\n\n3) identify(). Call right after the end-user is known — after successful\n login/signup and on session restore at startup:\n await Whisperr.instance.identify(\n '<stable external user id>',\n traits: { /* manifest traits, e.g. 'plan': user.plan */ },\n email: user.email, // optional shortcut -> opted-in email channel\n phone: user.phone, // optional shortcut -> SMS channel\n pushToken: fcmToken, // optional shortcut -> push channel\n );\n Use the app's real stable id (auth uid / customer id), never a device id.\n On logout call \\`await Whisperr.instance.reset();\\`.\n\n4) track(). For each manifest event, find where that user action actually\n happens and add:\n await Whisperr.instance.track('event_type_from_manifest', properties: { ... });\n event_type must be snake_case and copied verbatim from the manifest.\n\nNotes / gotchas:\n- The SDK queues + batches automatically; you do NOT need to await every track\n for delivery — awaiting just enqueues. Fire-and-forget is fine in UI handlers.\n- Do not call track() inside build() methods or render loops — only in event\n handlers / business logic (onPressed, on success of an API call, in a bloc/\n cubit/provider action, etc.).\n- Hardcoding the API key in Dart is acceptable for a client SDK (it is an\n ingestion key, scoped + rate-limited), but prefer --dart-define / a config\n file if the project already uses one.\n- Verify with \\`flutter analyze\\`.\n`.trim();\n\nexport const flutterPlaybook: Playbook = {\n target,\n detect,\n packageRef: \"whisperr (pub.dev)\",\n systemPrompt,\n verifyCommand: \"flutter analyze\",\n};\n","import type { Playbook, Detection, DetectContext } from \"../../types.js\";\n\nconst target = {\n id: \"nextjs\",\n displayName: \"Next.js\",\n language: \"typescript\",\n availability: \"planned\" as const,\n};\n\nasync function detect(ctx: DetectContext): Promise<Detection | null> {\n if (!(await ctx.exists(\"package.json\"))) return null;\n const pkgJson = await ctx.read(\"package.json\");\n let isNext = false;\n try {\n const pkg = JSON.parse(pkgJson);\n const all = { ...(pkg.dependencies ?? {}), ...(pkg.devDependencies ?? {}) };\n isNext = \"next\" in all;\n } catch {\n isNext = false;\n }\n if (!isNext && !(await ctx.exists(\"next.config.js\")) && !(await ctx.exists(\"next.config.mjs\")))\n return null;\n const evidence = [\"next dependency\"];\n let confidence = 0.85;\n if (await ctx.exists(\"app\")) {\n evidence.push(\"app/ router\");\n confidence += 0.1;\n } else if (await ctx.exists(\"pages\")) {\n evidence.push(\"pages/ router\");\n confidence += 0.05;\n }\n return { target, confidence: Math.min(confidence, 1), evidence };\n}\n\nconst systemPrompt = `\n## SDK: Whisperr for Web in Next.js — package \\`@whisperr/web\\`\n\nSame SDK as plain web, with Next-specific wiring.\n\n1) Install \\`@whisperr/web\\`. Put the key in NEXT_PUBLIC_WHISPERR_KEY in\n .env.local (and add it to .env.example).\n\n2) Whisperr runs client-side. Create a client provider:\n - App Router: a 'use client' component (e.g. app/whisperr-provider.tsx) that\n calls Whisperr.init(...) in a useEffect and renders children; mount it in\n app/layout.tsx.\n - Pages Router: init in pages/_app.tsx inside a useEffect.\n\n3) identify() after auth resolves on the client (e.g. in your auth/session\n hook). reset() on sign-out.\n\n4) track() in client components / event handlers / mutation callbacks. Do NOT\n call track from server components, route handlers, or getServerSideProps —\n this SDK is a browser client. Copy event_type verbatim from the manifest.\n\nNotes:\n- Guard against double-init under React Strict Mode (init is idempotent, but\n wire it so it only runs once).\n`.trim();\n\nexport const nextjsPlaybook: Playbook = {\n target,\n detect,\n packageRef: \"@whisperr/web (npm)\",\n systemPrompt,\n};\n","import type { Playbook, Detection, DetectContext } from \"../../types.js\";\n\nconst target = {\n id: \"web-js\",\n displayName: \"Web (JavaScript / TypeScript)\",\n language: \"typescript\",\n // Flip to \"available\" once @whisperr/web ships (Phase 2).\n availability: \"planned\" as const,\n};\n\n/** True if package.json depends on any of `names`. */\nfunction dependsOn(pkgJson: string, names: string[]): boolean {\n try {\n const pkg = JSON.parse(pkgJson);\n const all = {\n ...(pkg.dependencies ?? {}),\n ...(pkg.devDependencies ?? {}),\n } as Record<string, string>;\n return names.some((n) => n in all);\n } catch {\n return false;\n }\n}\n\nasync function detect(ctx: DetectContext): Promise<Detection | null> {\n if (!(await ctx.exists(\"package.json\"))) return null;\n const pkgJson = await ctx.read(\"package.json\");\n const evidence = [\"package.json\"];\n let confidence = 0.4;\n\n // Defer to the more specific playbooks when their frameworks are present.\n if (dependsOn(pkgJson, [\"next\"])) return null;\n if (dependsOn(pkgJson, [\"react-native\", \"expo\"])) return null;\n\n if (dependsOn(pkgJson, [\"react\", \"vue\", \"svelte\", \"@angular/core\"])) {\n evidence.push(\"frontend framework dependency\");\n confidence += 0.4;\n }\n if (await ctx.exists(\"index.html\")) {\n evidence.push(\"index.html\");\n confidence += 0.1;\n }\n return { target, confidence: Math.min(confidence, 0.95), evidence };\n}\n\nconst systemPrompt = `\n## SDK: Whisperr for Web (TypeScript/JavaScript) — package \\`@whisperr/web\\`\n\n1) Dependency. Install with the repo's package manager (detect pnpm/yarn/npm\n from the lockfile): \\`<pm> add @whisperr/web\\`.\n\n2) Initialize once at app entry (e.g. src/main.tsx, src/index.ts, or the root\n layout). Create a singleton client:\n import { Whisperr } from '@whisperr/web';\n export const whisperr = Whisperr.init({\n apiKey: import.meta.env.VITE_WHISPERR_KEY, // or process.env.NEXT_PUBLIC_... etc\n baseUrl: '<INGESTION_BASE_URL from manifest>',\n });\n Put the key in the project's env mechanism (Vite VITE_*, CRA REACT_APP_*,\n plain .env) and write the example var into .env / .env.example.\n\n3) identify(). After auth resolves (login success, and on app load if a session\n is restored):\n whisperr.identify(user.id, {\n traits: { /* manifest traits */ },\n email: user.email, phone: user.phone,\n });\n Call whisperr.reset() on logout.\n\n4) track(). For each manifest event, instrument the real handler:\n whisperr.track('event_type_from_manifest', { /* properties */ });\n In React, this belongs in event handlers / effects / mutation callbacks, not\n in render. Copy event_type verbatim (snake_case) from the manifest.\n\nNotes:\n- The SDK batches via /v1/events/batch and flushes on a timer + on\n visibilitychange/unload (sendBeacon). Fire-and-forget is fine.\n- Only the NEXT_PUBLIC_/VITE_-style public env vars are exposed to the browser;\n the ingestion key is a public, rate-limited key so that is expected.\n- If the project uses an analytics wrapper/provider pattern, follow it rather\n than scattering raw whisperr.track calls.\n`.trim();\n\nexport const webPlaybook: Playbook = {\n target,\n detect,\n packageRef: \"@whisperr/web (npm)\",\n systemPrompt,\n verifyCommand: undefined,\n};\n","import type { Playbook, Detection, DetectContext } from \"../../types.js\";\n\nconst target = {\n id: \"react-native\",\n displayName: \"React Native\",\n language: \"typescript\",\n availability: \"planned\" as const,\n};\n\nasync function detect(ctx: DetectContext): Promise<Detection | null> {\n if (!(await ctx.exists(\"package.json\"))) return null;\n const pkgJson = await ctx.read(\"package.json\");\n let match = false;\n try {\n const pkg = JSON.parse(pkgJson);\n const all = { ...(pkg.dependencies ?? {}), ...(pkg.devDependencies ?? {}) };\n match = \"react-native\" in all || \"expo\" in all;\n } catch {\n match = false;\n }\n if (!match) return null;\n const evidence = [\"react-native / expo dependency\"];\n let confidence = 0.85;\n if (await ctx.exists(\"app.json\")) {\n evidence.push(\"app.json\");\n confidence += 0.1;\n }\n return { target, confidence: Math.min(confidence, 1), evidence };\n}\n\nconst systemPrompt = `\n## SDK: Whisperr for React Native — package \\`@whisperr/react-native\\`\n\n1) Install \\`@whisperr/react-native\\` with the repo's package manager. If it has\n native modules, run pod install for iOS (mention it; don't attempt to build).\n\n2) Initialize once at the app root (App.tsx / app/_layout.tsx for Expo Router):\n import { Whisperr } from '@whisperr/react-native';\n Whisperr.init({ apiKey: <key from manifest/env>, baseUrl: <manifest baseUrl> });\n\n3) identify() after auth resolves / on session restore; reset() on logout.\n For channels: email/phone where known, and the push token (expo-notifications\n or @react-native-firebase/messaging) if the app has push set up.\n\n4) track() in event handlers / business logic, never in render. event_type\n verbatim from the manifest.\n\nNotes:\n- Persisted offline queue is built in (AsyncStorage); fire-and-forget is fine.\n- Prefer an env/config mechanism (react-native-config, app.config) for the key\n if one exists; otherwise a constants file is acceptable.\n`.trim();\n\nexport const reactNativePlaybook: Playbook = {\n target,\n detect,\n packageRef: \"@whisperr/react-native (npm)\",\n systemPrompt,\n};\n","import type { Playbook, Detection, DetectContext } from \"../../types.js\";\n\nconst target = {\n id: \"swift\",\n displayName: \"Swift (iOS)\",\n language: \"swift\",\n availability: \"planned\" as const,\n};\n\nasync function detect(ctx: DetectContext): Promise<Detection | null> {\n const evidence: string[] = [];\n let confidence = 0;\n\n if (await ctx.exists(\"Package.swift\")) {\n evidence.push(\"Package.swift\");\n confidence += 0.6;\n }\n const root = await ctx.list(\".\");\n if (root.some((f) => f.endsWith(\".xcodeproj\"))) {\n evidence.push(\"xcodeproj\");\n confidence += 0.5;\n }\n if (root.some((f) => f.endsWith(\".xcworkspace\"))) {\n evidence.push(\"xcworkspace\");\n confidence += 0.3;\n }\n if (await ctx.exists(\"Podfile\")) {\n evidence.push(\"Podfile\");\n confidence += 0.2;\n }\n if (confidence === 0) return null;\n return { target, confidence: Math.min(confidence, 1), evidence };\n}\n\nconst systemPrompt = `\n## SDK: Whisperr for Swift (iOS) — Swift Package \\`Whisperr\\`\n\n1) Add the Swift Package dependency (github.com/WhisperrAI/whisperr-swift). If the\n project uses Package.swift, add it there; if it's an .xcodeproj/.xcworkspace,\n you cannot edit the project graph reliably from the shell — instead leave a\n clearly commented setup note and the exact import + init code, and report it\n as a manual follow-up.\n\n2) Initialize once at launch in the App struct (SwiftUI) or\n application(_:didFinishLaunchingWithOptions:) (UIKit):\n import Whisperr\n Whisperr.initialize(apiKey: \"<key>\", baseUrl: \"<manifest baseUrl>\")\n\n3) identify() after the user is known / on session restore:\n Whisperr.shared.identify(\"<external user id>\", traits: [...], email: ..., phone: ...)\n reset() on logout. Push channel = APNs device token if the app registers for\n remote notifications.\n\n4) track() in action handlers / view-model methods, not in view body:\n Whisperr.shared.track(\"event_type_from_manifest\", properties: [ ... ])\n event_type verbatim (snake_case) from the manifest.\n\nNotes:\n- Prefer reading the key from Info.plist / an xcconfig if the project uses one.\n- Do not attempt to run xcodebuild; verification is the human's step here.\n`.trim();\n\nexport const swiftPlaybook: Playbook = {\n target,\n detect,\n packageRef: \"Whisperr (Swift Package)\",\n systemPrompt,\n};\n","import type { Playbook } from \"../../types.js\";\nimport { flutterPlaybook } from \"./flutter.js\";\nimport { nextjsPlaybook } from \"./nextjs.js\";\nimport { webPlaybook } from \"./web.js\";\nimport { reactNativePlaybook } from \"./react-native.js\";\nimport { swiftPlaybook } from \"./swift.js\";\n\n/**\n * The playbook registry. This is the ONLY place that knows about specific SDKs.\n * To add support for a new language/framework, add one playbook file and append\n * it here — detection, the SDK-specific system prompt, and verification all\n * travel with the playbook.\n *\n * Order matters only for display; detection confidence decides what's chosen.\n * More-specific playbooks (nextjs, react-native) return null when a more\n * specific match should win, so the generic web playbook doesn't shadow them.\n */\nexport const ALL_PLAYBOOKS: Playbook[] = [\n flutterPlaybook,\n nextjsPlaybook,\n reactNativePlaybook,\n webPlaybook,\n swiftPlaybook,\n];\n\nexport function playbookByTargetId(id: string): Playbook | undefined {\n return ALL_PLAYBOOKS.find((p) => p.target.id === id);\n}\n","import open from \"open\";\nimport type { WizardConfig, WizardSession } from \"../types.js\";\nimport { theme } from \"../ui/theme.js\";\n\n/**\n * Device-authorization flow against whisperr-go.\n *\n * Backend contract (to be built in whisperr-go, Phase 0):\n * POST {api}/v1/wizard/device/authorize\n * -> { device_code, user_code, verification_uri, verification_uri_complete,\n * interval, expires_in }\n * POST {api}/v1/wizard/device/token { device_code }\n * -> 200 { token, app_id, expires_at } when approved\n * -> 428 { error: \"authorization_pending\" } while waiting\n * -> 429 { error: \"slow_down\" } back off\n * -> 403/410 on deny/expiry\n *\n * The user is already logged in from onboarding, so the browser step is a single\n * \"Approve this device\" click that also binds the session to their app.\n */\n\nexport interface AuthHandle {\n /** Begin the flow; returns the URL to open + a poller. */\n start(): Promise<{ verificationUrl: string; userCode: string; poll: () => Promise<WizardSession> }>;\n}\n\ninterface AuthorizeResponse {\n device_code: string;\n user_code: string;\n verification_uri: string;\n verification_uri_complete?: string;\n interval?: number;\n expires_in?: number;\n}\n\ninterface TokenResponse {\n token: string;\n app_id: string;\n expires_at: number;\n}\n\nexport async function authenticate(config: WizardConfig): Promise<WizardSession> {\n if (config.offline) return offlineSession();\n\n const authorize = await fetchJson<AuthorizeResponse>(\n `${config.apiBaseUrl}/wizard/device/authorize`,\n { method: \"POST\", body: JSON.stringify({ client: \"whisperr-wizard\" }) },\n );\n\n const url = authorize.verification_uri_complete ?? authorize.verification_uri;\n const intervalMs = (authorize.interval ?? 5) * 1000;\n const deadline =\n Date.now() + (authorize.expires_in ?? 600) * 1000;\n\n // Open the browser (best-effort; print the URL regardless).\n try {\n await open(url);\n } catch {\n /* headless / no browser — the printed URL covers it */\n }\n\n // Poll for approval.\n let wait = intervalMs;\n while (Date.now() < deadline) {\n await sleep(wait);\n const res = await fetch(\n `${config.apiBaseUrl}/wizard/device/token`,\n {\n method: \"POST\",\n headers: { \"content-type\": \"application/json\" },\n body: JSON.stringify({ device_code: authorize.device_code }),\n },\n );\n if (res.ok) {\n const tok = (await res.json()) as TokenResponse;\n return { token: tok.token, appId: tok.app_id, expiresAt: tok.expires_at };\n }\n if (res.status === 429) {\n wait += 2000; // slow_down\n continue;\n }\n if (res.status === 428) continue; // authorization_pending\n // 403 deny / 410 expired / anything else: stop.\n throw new Error(\n `Authorization failed (${res.status}). Run the wizard again to retry.`,\n );\n }\n throw new Error(\"Authorization timed out. Run the wizard again.\");\n}\n\n/** Used in --offline mode: no backend, fixed dev app. */\nfunction offlineSession(): WizardSession {\n return {\n token: \"offline-dev-token\",\n appId: \"app_offline_dev\",\n expiresAt: Math.floor(Date.now() / 1000) + 3600,\n };\n}\n\nasync function fetchJson<T>(url: string, init: RequestInit): Promise<T> {\n const res = await fetch(url, {\n ...init,\n headers: { \"content-type\": \"application/json\", ...(init.headers ?? {}) },\n });\n if (!res.ok) {\n throw new Error(\n `${theme.alert(\"Request failed\")}: ${init.method ?? \"GET\"} ${url} -> ${res.status}`,\n );\n }\n return (await res.json()) as T;\n}\n\nfunction sleep(ms: number): Promise<void> {\n return new Promise((r) => setTimeout(r, ms));\n}\n","import pc from \"picocolors\";\n\n/**\n * Whisperr terminal palette. Restrained and tasteful — signal blue accent on a\n * mostly monochrome canvas, matching the product's dark, editorial brand.\n * Truecolor where supported, graceful fallback to picocolors' 16-color set.\n */\n\nconst truecolor =\n process.env.COLORTERM === \"truecolor\" || process.env.COLORTERM === \"24bit\";\n\nfunction rgb(r: number, g: number, b: number, text: string): string {\n if (!truecolor || !pc.isColorSupported) return text;\n return `\\x1b[38;2;${r};${g};${b}m${text}\\x1b[39m`;\n}\n\nexport const theme = {\n /** Signal blue — the one accent. */\n signal: (s: string) => rgb(91, 141, 239, s),\n /** Muted hairline grey for secondary text. */\n muted: (s: string) => rgb(140, 140, 150, s),\n /** Near-white primary text. */\n bright: (s: string) => pc.white(pc.bold(s)),\n dim: (s: string) => pc.dim(s),\n success: (s: string) => rgb(110, 207, 159, s),\n warn: (s: string) => rgb(240, 191, 105, s),\n alert: (s: string) => rgb(248, 113, 113, s),\n bold: (s: string) => pc.bold(s),\n} as const;\n\n/** The waveform mark, rendered in signal blue. */\nexport const wordmark = (() => {\n const mark = theme.signal(\"⌁\");\n return `${mark} ${theme.bright(\"whisperr\")} ${theme.muted(\"wizard\")}`;\n})();\n\n/** A thin divider. */\nexport const divider = theme.dim(\"─\".repeat(48));\n","import type {\n IntegrationManifest,\n WizardConfig,\n WizardSession,\n} from \"../types.js\";\n\n/**\n * Fetch the integration manifest for the authed app from whisperr-go.\n *\n * Backend contract (Phase 0, whisperr-go):\n * GET {api}/v1/wizard/manifest (Authorization: Bearer <wizard token>)\n * ?target=<detected sdk id> (optional hint)\n * -> IntegrationManifest\n *\n * The manifest is served from whisperr-go's own materialized runtime config:\n * app_runtime_event_definitions (code,label,description,event_schema_json)\n * + app_runtime_event_aliases (the literal event_type to emit)\n * + app_runtime_event_weights -> app_runtime_interventions (why it matters)\n * plus a freshly minted/returned ingestion API key for the app.\n */\nexport async function fetchManifest(\n config: WizardConfig,\n session: WizardSession,\n targetId?: string,\n): Promise<IntegrationManifest> {\n if (config.offline) return mockManifest(session.appId, config);\n\n const url = new URL(`${config.apiBaseUrl}/wizard/manifest`);\n if (targetId) url.searchParams.set(\"target\", targetId);\n\n const res = await fetch(url, {\n headers: { authorization: `Bearer ${session.token}` },\n });\n if (!res.ok) {\n throw new Error(\n `Could not load your integration manifest (${res.status}). ` +\n `Make sure onboarding is complete for this app.`,\n );\n }\n return (await res.json()) as IntegrationManifest;\n}\n\n/**\n * A representative manifest for --offline runs and demos. Shaped exactly like\n * what whisperr-go will return, so the agent + UI behave identically.\n */\nexport function mockManifest(\n appId: string,\n config: WizardConfig,\n): IntegrationManifest {\n return {\n appId,\n appName: \"Acme (dev)\",\n ingestionApiKey: \"wrk_offline_demo_key_do_not_use\",\n ingestionBaseUrl: config.apiBaseUrl,\n businessContext:\n \"B2C subscription app. Free trial converts to a paid monthly plan. \" +\n \"Churn risk concentrates around trial end and the first failed payment.\",\n identify: {\n traits: [\n { name: \"plan\", description: \"free | trial | pro\" },\n { name: \"signup_date\", description: \"ISO date the user signed up\" },\n ],\n channels: [\"email\", \"push\"],\n notes:\n \"Call identify on login and on session restore. Send email always; \" +\n \"push token when notifications are enabled.\",\n },\n events: [\n {\n eventType: \"trial_started\",\n label: \"Trial started\",\n description: \"User begins their free trial.\",\n importance: 0.7,\n properties: [{ name: \"plan\", description: \"trial tier chosen\" }],\n interventions: [{ code: \"onboarding_nudge\", label: \"Onboarding nudge\" }],\n },\n {\n eventType: \"feature_activated\",\n label: \"Core feature activated\",\n description:\n \"User completes the core 'aha' action for the first time. Strong \" +\n \"retention signal; fire on first successful completion only.\",\n importance: 0.6,\n interventions: [{ code: \"activation_boost\", label: \"Activation boost\" }],\n },\n {\n eventType: \"payment_failed\",\n label: \"Payment failed\",\n description:\n \"A subscription charge was declined. Fire from the billing webhook \" +\n \"handler / failed-charge path.\",\n importance: 0.95,\n properties: [\n { name: \"amount\", description: \"charge amount\" },\n { name: \"reason\", description: \"decline reason if known\" },\n ],\n interventions: [{ code: \"dunning_recovery\", label: \"Dunning recovery\", weight: 0.9 }],\n },\n {\n eventType: \"subscription_cancelled\",\n label: \"Subscription cancelled\",\n description: \"User cancels their paid plan. Fire at the moment of cancel.\",\n importance: 1,\n properties: [{ name: \"reason\", description: \"stated cancel reason\" }],\n interventions: [{ code: \"win_back\", label: \"Win-back\", weight: 1 }],\n },\n ],\n supportedTargets: [\"flutter\", \"web-js\", \"nextjs\", \"react-native\", \"swift\"],\n };\n}\n","import { query } from \"@anthropic-ai/claude-agent-sdk\";\nimport type {\n IntegrationManifest,\n Playbook,\n WizardConfig,\n WizardSession,\n} from \"../types.js\";\nimport { BASE_WIZARD_PROMPT, renderManifestBrief } from \"./playbooks/shared-prompt.js\";\n\nexport interface AgentProgress {\n /** Free-text line, e.g. \"Editing lib/main.dart\". */\n onActivity?(line: string): void;\n /** Final result summary text from the agent. */\n onResult?(summary: string, costUsd?: number): void;\n}\n\nexport interface AgentRunOutcome {\n summary: string;\n costUsd?: number;\n durationMs?: number;\n ok: boolean;\n}\n\n/**\n * Run the coding agent over `repoPath` to perform the integration.\n *\n * Auth model:\n * - production: point the Agent SDK at Whisperr's Anthropic-compatible gateway\n * via ANTHROPIC_BASE_URL, with the short-lived wizard token as\n * ANTHROPIC_AUTH_TOKEN. The CLI never holds an Anthropic key; the gateway\n * injects it and meters usage per app.\n * - local dev: if a direct Anthropic key is configured, use it as-is.\n */\nexport async function runIntegrationAgent(opts: {\n repoPath: string;\n config: WizardConfig;\n session: WizardSession;\n playbook: Playbook;\n manifest: IntegrationManifest;\n progress?: AgentProgress;\n}): Promise<AgentRunOutcome> {\n const { repoPath, config, session, playbook, manifest, progress } = opts;\n\n applyModelAuthEnv(config, session);\n\n const systemPrompt = [BASE_WIZARD_PROMPT, playbook.systemPrompt].join(\"\\n\\n\");\n\n const verifyHint = playbook.verifyCommand\n ? `\\n\\nWhen finished, run \\`${playbook.verifyCommand}\\` and fix anything it flags.`\n : \"\";\n\n const prompt = [\n `Integrate the Whisperr ${playbook.target.displayName} SDK into this repository.`,\n `Project root: ${repoPath}`,\n \"\",\n \"Here is the integration manifest derived from this customer's Whisperr\",\n \"onboarding. Wire identify() and instrument every event below at the correct\",\n \"call site, following the SDK guide in your system prompt.\",\n \"\",\n \"----- MANIFEST -----\",\n renderManifestBrief(manifest),\n \"----- END MANIFEST -----\",\n verifyHint,\n ].join(\"\\n\");\n\n const started = Date.now();\n let summary = \"\";\n let costUsd: number | undefined;\n let ok = false;\n\n const response = query({\n prompt,\n options: {\n model: config.model,\n cwd: repoPath,\n systemPrompt,\n // Full file-system agent toolset.\n allowedTools: [\"Read\", \"Edit\", \"Write\", \"Bash\", \"Glob\", \"Grep\"],\n // Fully autonomous edits (the user opted into auto-apply; the wizard takes\n // a git checkpoint before this runs so revert is one command).\n permissionMode: \"bypassPermissions\",\n maxTurns: config.maxTurns,\n // Don't pick up the user's own CLAUDE.md / settings — the wizard's prompt\n // is the single source of truth.\n settingSources: [],\n },\n });\n\n for await (const message of response as AsyncIterable<AgentMessage>) {\n if (message.type === \"assistant\") {\n for (const block of message.message?.content ?? []) {\n if (isTextBlock(block) && block.text?.trim()) {\n progress?.onActivity?.(firstLine(block.text));\n } else if (isToolUseBlock(block)) {\n progress?.onActivity?.(describeToolUse(block));\n }\n }\n } else if (message.type === \"result\") {\n ok = message.subtype === \"success\";\n costUsd = message.total_cost_usd;\n summary = message.result ?? extractLastText(message) ?? summary;\n }\n }\n\n progress?.onResult?.(summary, costUsd);\n return { summary, costUsd, ok, durationMs: Date.now() - started };\n}\n\n/** Configure where the Agent SDK sends model calls. */\nfunction applyModelAuthEnv(config: WizardConfig, session: WizardSession): void {\n if (config.directAnthropicKey) {\n process.env.ANTHROPIC_API_KEY = config.directAnthropicKey;\n // Make sure a stale gateway base from a prior run doesn't leak in.\n delete process.env.ANTHROPIC_AUTH_TOKEN;\n return;\n }\n // Gateway mode: token is the wizard session token, base is our proxy.\n process.env.ANTHROPIC_BASE_URL = config.llmBaseUrl;\n process.env.ANTHROPIC_AUTH_TOKEN = session.token;\n delete process.env.ANTHROPIC_API_KEY;\n}\n\n// --- minimal structural typings over the Agent SDK message stream ---\n// We keep these local + permissive so a minor SDK shape change doesn't break the\n// build; we only read a handful of fields.\ninterface AgentTextBlock {\n type: \"text\";\n text?: string;\n}\ninterface AgentToolUseBlock {\n type: \"tool_use\";\n name?: string;\n input?: Record<string, unknown>;\n}\ntype AgentBlock = AgentTextBlock | AgentToolUseBlock | { type: string };\n\nfunction isTextBlock(b: AgentBlock): b is AgentTextBlock {\n return b.type === \"text\";\n}\nfunction isToolUseBlock(b: AgentBlock): b is AgentToolUseBlock {\n return b.type === \"tool_use\";\n}\n\ninterface AgentMessage {\n type: \"assistant\" | \"result\" | string;\n subtype?: string;\n result?: string;\n total_cost_usd?: number;\n message?: { content?: AgentBlock[] };\n}\n\nfunction describeToolUse(block: AgentToolUseBlock): string {\n const file =\n (block.input?.file_path as string) ??\n (block.input?.path as string) ??\n (block.input?.pattern as string) ??\n \"\";\n switch (block.name) {\n case \"Edit\":\n return `Editing ${short(file)}`;\n case \"Write\":\n return `Writing ${short(file)}`;\n case \"Read\":\n return `Reading ${short(file)}`;\n case \"Bash\":\n return `Running ${short((block.input?.command as string) ?? \"command\")}`;\n case \"Grep\":\n return `Searching for ${short(file)}`;\n case \"Glob\":\n return `Scanning ${short(file)}`;\n default:\n return `${block.name ?? \"Working\"}…`;\n }\n}\n\nfunction extractLastText(message: AgentMessage): string | undefined {\n const blocks = message.message?.content ?? [];\n for (let i = blocks.length - 1; i >= 0; i--) {\n const b = blocks[i] as AgentTextBlock;\n if (b?.type === \"text\" && b.text) return b.text;\n }\n return undefined;\n}\n\nfunction firstLine(text: string): string {\n const line = text.split(\"\\n\").find((l) => l.trim()) ?? text;\n return short(line.trim(), 100);\n}\n\nfunction short(s: string, max = 60): string {\n return s.length > max ? `${s.slice(0, max - 1)}…` : s;\n}\n","import type { IntegrationManifest } from \"../../types.js\";\n\n/**\n * The base wizard system prompt — SDK-agnostic. Each playbook appends its own\n * SDK-specific guide on top of this. The manifest (events/identify/key) is\n * injected separately as a structured user message so it stays cache-friendly\n * and unambiguous.\n */\nexport const BASE_WIZARD_PROMPT = `\nYou are the Whisperr Wizard — an expert integration agent. You are running\nINSIDE a customer's code repository. Your single job: integrate the Whisperr SDK\nso the product starts sending the right events to Whisperr, then stop.\n\nWhisperr is a churn-prevention engine. It needs two things from the app:\n 1. identify() — tell Whisperr who the end-user is (a stable external user id)\n plus traits and contact channels, called right after the user is known\n (login / session restore / signup).\n 2. track() — emit the specific business events listed in the integration\n manifest, at the exact places in the code where those user actions happen.\n These named events drive the customer's churn interventions, so placing\n them at the correct call sites matters more than anything else you do.\n\nOperating rules:\n- Work autonomously. Make the edits directly; do not ask for confirmation.\n- Be surgical. Add the SDK dependency, initialize it once at app startup, wire\n identify() where the user becomes known, and add track() calls for the\n manifest events. Do not refactor unrelated code, restyle, or \"improve\" things.\n- Find the RIGHT call site for each named event by reading the code, not by\n guessing. If you genuinely cannot find where an event occurs, add a clearly\n commented TODO with the exact Whisperr call to make, and report it at the end\n rather than firing the event from a wrong place.\n- Never invent event types. Use the exact snake_case event_type strings from the\n manifest. Custom data goes under properties.\n- Keep the API key out of source where the SDK/platform supports env/secrets;\n otherwise place it where the manifest guidance says and flag it.\n- Match the repository's existing conventions (imports, formatting, state\n management, file layout).\n\nWhen done, output a concise summary:\n - files changed\n - where identify() was wired\n - each manifest event -> the call site you instrumented (or TODO + reason)\n - any follow-ups the human must do (env var, push token, etc.)\n`.trim();\n\n/** Render the manifest into a compact, agent-readable brief. */\nexport function renderManifestBrief(m: IntegrationManifest): string {\n const lines: string[] = [];\n lines.push(`# Whisperr integration manifest for app: ${m.appName ?? m.appId}`);\n lines.push(\"\");\n lines.push(`Ingestion base URL: ${m.ingestionBaseUrl}`);\n lines.push(`Ingestion API key: ${m.ingestionApiKey}`);\n if (m.businessContext) {\n lines.push(\"\");\n lines.push(\"## Business context\");\n lines.push(m.businessContext);\n }\n\n lines.push(\"\");\n lines.push(\"## identify()\");\n if (m.identify.traits?.length) {\n lines.push(\"Traits to send when available:\");\n for (const t of m.identify.traits) {\n lines.push(` - ${t.name}${t.description ? ` — ${t.description}` : \"\"}`);\n }\n }\n if (m.identify.channels?.length) {\n lines.push(`Channels in use: ${m.identify.channels.join(\", \")}`);\n }\n if (m.identify.notes) lines.push(m.identify.notes);\n\n lines.push(\"\");\n lines.push(\"## Events to instrument (use these exact event_type strings)\");\n // Most important first so the agent prioritizes the load-bearing events.\n const events = [...m.events].sort(\n (a, b) => (b.importance ?? 0) - (a.importance ?? 0),\n );\n for (const e of events) {\n lines.push(\"\");\n lines.push(`### ${e.eventType}${e.label ? ` (${e.label})` : \"\"}`);\n if (e.description) lines.push(e.description);\n if (e.properties?.length) {\n lines.push(\"Properties:\");\n for (const p of e.properties) {\n lines.push(\n ` - ${p.name}${p.required ? \" (required)\" : \"\"}${\n p.description ? ` — ${p.description}` : \"\"\n }`,\n );\n }\n }\n if (e.interventions?.length) {\n const drv = e.interventions\n .map((i) => i.label ?? i.code)\n .join(\", \");\n lines.push(`Drives: ${drv}`);\n }\n }\n\n return lines.join(\"\\n\");\n}\n","import { spawn } from \"node:child_process\";\n\n/**\n * The wizard auto-applies edits (user's choice). To make that safe, we take a\n * checkpoint before the agent runs so a revert is one command, and we can show\n * a clean diff of exactly what changed afterward.\n */\n\nexport interface GitCheckpoint {\n isRepo: boolean;\n /** Commit sha captured before edits (if a repo + had commits). */\n baseRef?: string;\n /** Branch the wizard is operating on. */\n branch?: string;\n}\n\nexport async function takeCheckpoint(repoPath: string): Promise<GitCheckpoint> {\n const isRepo = (await run(repoPath, [\"rev-parse\", \"--is-inside-work-tree\"])).ok;\n if (!isRepo) return { isRepo: false };\n\n const branch = (\n await run(repoPath, [\"rev-parse\", \"--abbrev-ref\", \"HEAD\"])\n ).stdout.trim();\n const head = await run(repoPath, [\"rev-parse\", \"HEAD\"]);\n return {\n isRepo: true,\n baseRef: head.ok ? head.stdout.trim() : undefined,\n branch: branch || undefined,\n };\n}\n\n/** Names of files changed since the checkpoint (working tree + untracked). */\nexport async function changedFiles(\n repoPath: string,\n checkpoint: GitCheckpoint,\n): Promise<string[]> {\n if (!checkpoint.isRepo) return [];\n const tracked = await run(repoPath, [\"diff\", \"--name-only\"]);\n const untracked = await run(repoPath, [\n \"ls-files\",\n \"--others\",\n \"--exclude-standard\",\n ]);\n const set = new Set<string>();\n for (const f of `${tracked.stdout}\\n${untracked.stdout}`.split(\"\\n\")) {\n if (f.trim()) set.add(f.trim());\n }\n return [...set];\n}\n\n/** The exact command a user runs to undo everything the wizard did. */\nexport function revertHint(checkpoint: GitCheckpoint): string | undefined {\n if (!checkpoint.isRepo || !checkpoint.baseRef) return undefined;\n return `git restore . && git clean -fd (back to ${checkpoint.baseRef.slice(0, 7)})`;\n}\n\nfunction run(\n cwd: string,\n args: string[],\n): Promise<{ ok: boolean; stdout: string; stderr: string }> {\n return new Promise((resolve) => {\n const child = spawn(\"git\", args, { cwd });\n let stdout = \"\";\n let stderr = \"\";\n child.stdout.on(\"data\", (d) => (stdout += d.toString()));\n child.stderr.on(\"data\", (d) => (stderr += d.toString()));\n child.on(\"close\", (code) => resolve({ ok: code === 0, stdout, stderr }));\n child.on(\"error\", () => resolve({ ok: false, stdout, stderr }));\n });\n}\n","import type { WizardConfig, WizardSession } from \"../types.js\";\n\n/**\n * The activation moment: after integration, poll Whisperr for the first event\n * received from this app. In a real run the customer would run their app once;\n * the wizard waits for the green light and the dashboard lights up too.\n *\n * Backend contract (Phase 0, whisperr-go):\n * GET {api}/v1/wizard/first-event (Authorization: Bearer <wizard token>)\n * -> { received: boolean, event_type?: string, at?: string }\n *\n * Skipped in --offline mode.\n */\nexport async function pollFirstEvent(\n config: WizardConfig,\n session: WizardSession,\n opts: { timeoutMs?: number; intervalMs?: number; signal?: AbortSignal } = {},\n): Promise<{ received: boolean; eventType?: string }> {\n if (config.offline) return { received: false };\n\n const timeoutMs = opts.timeoutMs ?? 120_000;\n const intervalMs = opts.intervalMs ?? 3_000;\n const deadline = Date.now() + timeoutMs;\n\n while (Date.now() < deadline) {\n if (opts.signal?.aborted) return { received: false };\n try {\n const res = await fetch(`${config.apiBaseUrl}/wizard/first-event`, {\n headers: { authorization: `Bearer ${session.token}` },\n });\n if (res.ok) {\n const body = (await res.json()) as {\n received: boolean;\n event_type?: string;\n };\n if (body.received) {\n return { received: true, eventType: body.event_type };\n }\n }\n } catch {\n /* transient — keep polling */\n }\n await new Promise((r) => setTimeout(r, intervalMs));\n }\n return { received: false };\n}\n","import { theme } from \"./theme.js\";\n\n/**\n * Intro banner. Kept small and tasteful — a waveform glyph, the wordmark, and a\n * one-line promise. No oversized ASCII art.\n */\nexport function banner(): string {\n const wave = theme.signal(\"∿∿∿\");\n const title = `${theme.bright(\"Whisperr\")} ${theme.signal(\"Wizard\")}`;\n const tagline = theme.muted(\n \"Integrate Whisperr into your app — one command, fully wired.\",\n );\n return [\n \"\",\n ` ${wave} ${title}`,\n ` ${tagline}`,\n \"\",\n ].join(\"\\n\");\n}\n\n/** A compact step header, e.g. step(\"1\", \"Detecting your stack\"). */\nexport function step(n: string, label: string): string {\n return `${theme.signal(theme.bold(n))} ${theme.bright(label)}`;\n}\n","import { run, type RunOptions } from \"./cli.js\";\nimport { theme } from \"./ui/theme.js\";\n\n/**\n * Entry point for `npx @whisperr/wizard`.\n *\n * Usage:\n * npx @whisperr/wizard [init] [path] [--offline] [--api <url>] [--model <id>]\n *\n * `init` is accepted as an optional verb for readability; the wizard runs the\n * same flow with or without it.\n */\nfunction parseArgs(argv: string[]): RunOptions | { help: true } | { version: true } {\n const args = [...argv];\n const opts: RunOptions = {};\n\n // Drop a leading \"init\" verb.\n if (args[0] === \"init\") args.shift();\n\n for (let i = 0; i < args.length; i++) {\n const a = args[i];\n switch (a) {\n case \"-h\":\n case \"--help\":\n return { help: true };\n case \"-v\":\n case \"--version\":\n return { version: true };\n case \"--offline\":\n opts.offline = true;\n break;\n case \"--api\":\n opts.apiBaseUrl = args[++i];\n break;\n case \"--model\":\n opts.model = args[++i];\n break;\n default:\n if (a && !a.startsWith(\"-\") && !opts.path) {\n opts.path = a; // positional: target repo path\n }\n }\n }\n return opts;\n}\n\nfunction printHelp(): void {\n // eslint-disable-next-line no-console\n console.log(\n [\n \"\",\n ` ${theme.bright(\"Whisperr Wizard\")} — integrate Whisperr into your app.`,\n \"\",\n ` ${theme.bright(\"Usage\")}`,\n \" npx @whisperr/wizard [init] [path] [options]\",\n \"\",\n ` ${theme.bright(\"Options\")}`,\n \" --offline Use a demo manifest, no account/browser needed\",\n \" --api <url> Override the Whisperr API base URL\",\n \" --model <id> Override the coding-agent model\",\n \" -h, --help Show this help\",\n \" -v, --version Show version\",\n \"\",\n ` ${theme.muted(\"The wizard detects your stack, authenticates with your\")}`,\n ` ${theme.muted(\"onboarded account, and wires up identify() + your events.\")}`,\n \"\",\n ].join(\"\\n\"),\n );\n}\n\nasync function main(): Promise<void> {\n const parsed = parseArgs(process.argv.slice(2));\n\n if (\"help\" in parsed) {\n printHelp();\n return;\n }\n if (\"version\" in parsed) {\n // eslint-disable-next-line no-console\n console.log(\"0.1.0\");\n return;\n }\n\n try {\n const code = await run(parsed);\n process.exit(code);\n } catch (err) {\n // eslint-disable-next-line no-console\n console.error(\"\\n\" + theme.alert(\"Unexpected error: \") + (err as Error).message);\n process.exit(1);\n }\n}\n\nvoid main();\n"],"mappings":"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","names":["target","detect","systemPrompt","target","detect","systemPrompt","target","detect","systemPrompt","target","detect","systemPrompt","p","p","p","systemPrompt","resolve","run","playbook","run"]}
|