@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 CHANGED
@@ -1,151 +1,43 @@
1
- # Whisperr Wizard
1
+ # @whisperr/wizard
2
2
 
3
- > One command to integrate Whisperr into any app. The user pastes a command,
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
- ## What it does (the flow)
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
- ## Architecture (SDK-agnostic)
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
- ## Model & auth
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
- The agent runs on Claude via the **Claude Agent SDK**. The CLI **never ships an
83
- Anthropic key**:
20
+ ## Requirements
84
21
 
85
- - **Production:** the SDK is pointed at Whisperr's Anthropic-compatible gateway
86
- via `ANTHROPIC_BASE_URL`, with the wizard session token as
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
- Default model: `claude-opus-4-8` (override with `WHISPERR_WIZARD_MODEL`, e.g.
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
- npm install
103
- export ANTHROPIC_API_KEY=sk-ant-... # local dev only
104
- npm run dev:local -- --offline ../some-flutter-app
28
+ # in your project root
29
+ npx @whisperr/wizard
105
30
  ```
106
31
 
107
- `--offline` uses a demo manifest (trial_started, feature_activated,
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
- | Endpoint | Auth | Purpose |
121
- |---|---|---|
122
- | `POST /wizard/device/authorize` | public | Start device flow → `device_code`, `user_code`, `verification_uri` |
123
- | `POST /wizard/device/token` | public (polled) | → `{ token, app_id, expires_at }` once approved (`428` while pending) |
124
- | `POST /dashboard/wizard/approve` | Supabase JWT | The whisperr-watch approval page binds a `user_code` to the user's app |
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
- ## Status
40
+ ---
145
41
 
146
- - CLI: detection, playbook registry, terminal UX, agent runner, git
147
- checkpoint, offline demo manifest. Typechecks + builds; npx-standalone.
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-opus-4-8";
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 ?? 40),
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. You are running
600
- INSIDE a customer's code repository. Your single job: integrate the Whisperr SDK
601
- so the product starts sending the right events to Whisperr, then stop.
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
- Whisperr is a churn-prevention engine. It needs two things from the app:
604
- 1. identify() \u2014 tell Whisperr who the end-user is (a stable external user id)
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
- Operating rules:
613
- - Work autonomously. Make the edits directly; do not ask for confirmation.
614
- - Be surgical. Add the SDK dependency, initialize it once at app startup, wire
615
- identify() where the user becomes known, and add track() calls for the
616
- manifest events. Do not refactor unrelated code, restyle, or "improve" things.
617
- - Find the RIGHT call site for each named event by reading the code, not by
618
- guessing. If you genuinely cannot find where an event occurs, add a clearly
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
- When done, output a concise summary:
629
- - files changed
630
- - where identify() was wired
631
- - each manifest event -> the call site you instrumented (or TODO + reason)
632
- - any follow-ups the human must do (env var, push token, etc.)
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 renderManifestBrief(m) {
636
+ function renderIdentifyBrief(m) {
635
637
  const lines = [];
636
- lines.push(`# Whisperr integration manifest for app: ${m.appName ?? m.appId}`);
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("## Business context");
643
- lines.push(m.businessContext);
643
+ lines.push(`Context: ${m.businessContext}`);
644
644
  }
645
645
  lines.push("");
646
- lines.push("## identify()");
646
+ lines.push("identify():");
647
647
  if (m.identify.traits?.length) {
648
- lines.push("Traits to send when available:");
648
+ lines.push(" Send these traits when available:");
649
649
  for (const t of m.identify.traits) {
650
- lines.push(` - ${t.name}${t.description ? ` \u2014 ${t.description}` : ""}`);
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.push("");
658
- lines.push("## Events to instrument (use these exact event_type strings)");
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
- lines.push("");
664
- lines.push(`### ${e.eventType}${e.label ? ` (${e.label})` : ""}`);
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("Properties:");
668
- for (const p2 of e.properties) {
669
- lines.push(
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 verifyHint = playbook.verifyCommand ? `
688
-
689
- When finished, run \`${playbook.verifyCommand}\` and fix anything it flags.` : "";
690
- const prompt = [
691
- `Integrate the Whisperr ${playbook.target.displayName} SDK into this repository.`,
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
- "Here is the integration manifest derived from this customer's Whisperr",
695
- "onboarding. Wire identify() and instrument every event below at the correct",
696
- "call site, following the SDK guide in your system prompt.",
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
- "----- MANIFEST -----",
699
- renderManifestBrief(manifest),
700
- "----- END MANIFEST -----",
701
- verifyHint
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 started = Date.now();
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: config.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: config.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
- costUsd = message.total_cost_usd;
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
- progress?.onResult?.(summary, costUsd);
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
- spin.start(theme.bright("Integrating") + theme.muted(" \u2014 the agent is working in your code"));
941
- let lastLine = "";
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
- lastLine = line;
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 failed"));
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
- spin.stop(
961
- outcome.ok ? theme.success("Integration complete") : theme.warn("Integration finished with notes")
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.0");
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.0",
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"]}