@mhosaic/feedback-cli 0.14.1 → 0.17.0

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.
Files changed (27) hide show
  1. package/dist/bin.js +3 -3
  2. package/dist/{doctor-HTTAR4ZB.js → doctor-K2C6LBR2.js} +2 -2
  3. package/dist/doctor-K2C6LBR2.js.map +1 -0
  4. package/dist/{init-P45CEXDP.js → init-RS7BKALE.js} +3 -3
  5. package/dist/init-RS7BKALE.js.map +1 -0
  6. package/dist/{install-skill-3OPVFMTK.js → install-skill-QJ4ZDVVR.js} +4 -2
  7. package/dist/{install-skill-3OPVFMTK.js.map → install-skill-QJ4ZDVVR.js.map} +1 -1
  8. package/package.json +1 -1
  9. package/skills/feedback-close/SKILL.md +58 -0
  10. package/skills/feedback-fix/SKILL.md +121 -0
  11. package/skills/feedback-pull/SKILL.md +56 -0
  12. package/skills/feedback-watch-merges/SKILL.md +66 -0
  13. package/skills/integrate-feedback/SKILL.md +100 -75
  14. package/skills/integrate-feedback/references/consumer-install-astro.md +26 -6
  15. package/skills/integrate-feedback/references/consumer-install-next.md +39 -14
  16. package/skills/integrate-feedback/references/consumer-install-nuxt.md +35 -12
  17. package/skills/integrate-feedback/references/consumer-install-plain.md +55 -30
  18. package/skills/integrate-feedback/references/consumer-install-remix.md +32 -7
  19. package/skills/integrate-feedback/references/consumer-install-svelte.md +27 -7
  20. package/skills/integrate-feedback/references/consumer-install-vite.md +23 -14
  21. package/skills/integrate-feedback/references/consumer-install-vue.md +34 -11
  22. package/skills/integrate-feedback/references/consumer-install.md +74 -27
  23. package/skills/integrate-feedback/references/identify-snippets.md +3 -3
  24. package/skills/integrate-feedback/references/operator-provision.md +125 -33
  25. package/skills/issue-pull/SKILL.md +46 -0
  26. package/dist/doctor-HTTAR4ZB.js.map +0 -1
  27. package/dist/init-P45CEXDP.js.map +0 -1
@@ -1,118 +1,139 @@
1
1
  ---
2
2
  name: integrate-feedback
3
- description: Walk a teammate through integrating @mhosaic/feedback either provisioning a project as the platform operator, or installing the widget in a host app as the consumer. Use when the user mentions adding the Mhosaic feedback widget, onboarding a new app, getting a public key, or wiring the widget into their codebase.
3
+ description: Full guide for integrating @mhosaic/feedback. Covers operator-side provisioning (Company/Project/pk_proj_ key creation via the admin SPA), consumer-side install (CLI run + framework-specific wiring + identify() recipes + smoke test), and every common question a teammate might ask — what permissions are needed, where to run from, how the operator→consumer handoff works, what Chrome MCP is for, external clients vs internal apps, why the FAB isn't appearing, CORS errors, etc. Use whenever the user mentions: the Mhosaic feedback widget, adding feedback to an app, onboarding a new client/app, getting a pk_proj_ key, deploying for a company, configuring identify(), CORS errors on the widget, or any troubleshooting around the integration. Equally useful as a slash-command runbook and as a reference Claude reads to answer ad-hoc questions.
4
+ user-invocable: true
4
5
  ---
5
6
 
6
7
  # /integrate-feedback — guided widget integration
7
8
 
8
- You are guiding a teammate through integrating the **@mhosaic/feedback** widget into a project. The workflow splits into two modes:
9
+ End-to-end flow for getting the **@mhosaic/feedback** widget into a host app. There are two phases, run in two separate places:
9
10
 
10
- - **Operator mode** — the platform admin (Victor or a delegate). Provisions a new Company / Project / public key on the Mhosaic backend. Has admin access to the software-factory admin SPA.
11
- - **Consumer mode** — a teammate. Installs the widget in their own app, given a key + endpoint from the operator.
11
+ 1. **Operator phase** — provisions Company / Project / `pk_proj_…` key on the Mhosaic backend (https://software-factory-3tbbu.ondigitalocean.app). Run from **inside `feedback-tool-mhosaic`**. The skill drives Chrome at the admin SPA, no filesystem changes. At the end, it outputs a Markdown handoff payload and offers to install itself globally so phase 2 is invokable in the client's repo.
12
12
 
13
- **Both modes are guided. Never assume confirm at every checkpoint.**
13
+ 2. **Consumer phase** installs the widget in the host app. Run from **inside the client's app directory** (NOT feedback-tool-mhosaic). The skill detects the framework, runs the CLI's `init`, pastes the right entry-point snippet, wires `identify()` against the client's auth provider, and runs a live smoke test in Chrome.
14
+
15
+ This skill is **both** a slash-command runbook AND a reference doc. When the user invokes `/integrate-feedback`, run the procedural flow. When they just ask an integration question, answer from the Q&A below.
14
16
 
15
17
  ---
16
18
 
17
- ## Step 0: Identify the mode
19
+ ## Quick Q&A
18
20
 
19
- Ask the user which mode they're in. Use `AskUserQuestion` with:
21
+ **"What does this skill do?"**
22
+ End-to-end widget integration. Operator phase: creates Company/Project/key on the backend. Consumer phase: installs the widget in a host app + runs a smoke test that confirms a report lands in admin. Framework-aware (Vite/Next/Nuxt/Astro/Remix/Vue/SvelteKit/plain HTML). Auth-aware (Auth0/Clerk/Supabase/Firebase/NextAuth/Django/JWT/anonymous).
20
23
 
21
- - question: `Which side of the integration are you working on?`
22
- - header: `Mode`
23
- - options:
24
- - label: `Provision (operator)`, description: `Create a new Company/Project/key in the Mhosaic backend. Requires admin SPA access.`
25
- - label: `Install (consumer)`, description: `Add the widget to a host app. You already have a pk_proj_… key + endpoint from the operator.`
26
- - label: `Both — start with operator, then consumer`, description: `Provision first, then keep going.`
24
+ **"Do I need to install anything first?"**
27
25
 
28
- Branch on the answer. Each branch is documented in detail under `references/`.
26
+ - Inside `feedback-tool-mhosaic`: no. The skill is committed under `.claude/skills/integrate-feedback/`; `/integrate-feedback` works directly.
27
+ - In the client's repo: optional. If you want `/integrate-feedback` invokable there too (recommended for the consumer phase), the operator phase will offer to install it globally for you (one shell command, `npx @mhosaic/feedback-cli@latest install-skill`). Skip the install and the consumer phase can still happen — you just run `npx @mhosaic/feedback-cli@latest init …` directly in the client's repo and follow the framework-specific snippet from `references/`.
29
28
 
30
- ---
29
+ **"Where do I run `/integrate-feedback` from?"**
31
30
 
32
- ## Step 0.5: Pre-flight (operator mode only)
31
+ - Operator phase: **inside `feedback-tool-mhosaic`** (any directory in the monorepo).
32
+ - Consumer phase: **inside the client's app's project root**.
33
+ - Don't run consumer phase inside `feedback-tool-mhosaic` — the CLI would install the widget into our own monorepo. The skill detects this (via `package.json` name check) and refuses.
33
34
 
34
- If the user picked operator (or "Both"), confirm Chrome MCP is configured before going further. Operator mode hard-depends on `mcp__claude-in-chrome__*` tools — without them, the API-driving step at the heart of `references/operator-provision.md` can't run.
35
+ **"What permissions do I need on the backend?"**
35
36
 
36
- Check: call `mcp__claude-in-chrome__tabs_context_mcp` once. Possible outcomes:
37
+ - Login: Feedback Django group.
38
+ - Create a Project/key under an existing company: Feedback group + Membership for that company.
39
+ - Create a brand-new Company (external client): `is_staff=True` + `is_superuser=True`. Held by the platform maintainers.
37
40
 
38
- - **Tool returns a tab list (even an empty one)** — Chrome MCP is installed. Proceed.
39
- - **Tool isn't available / errors with "tool not found"** Chrome MCP isn't configured on this Claude Code install. Tell the user:
41
+ **"What's Chrome MCP and why do I need it?"**
42
+ A Claude Code MCP server that lets the skill drive a real Chrome tab. Operator phase uses it to call our admin API from the authenticated browser session (no token paste). Consumer phase uses it for the smoke test. If MCP isn't configured, operator phase stops at Step 0.5 with an install hint; consumer phase degrades to "open the dev URL yourself and confirm visually."
40
43
 
41
- > "Operator mode needs the Chrome MCP server (`@anthropic/claude-in-chrome` or equivalent). Without it, the skill can't drive the admin SPA programmatically. Two options:
42
- > 1. Install Chrome MCP (Claude Code MCP settings add the chrome connector), then re-run `/integrate-feedback`.
43
- > 2. Provision manually via `docs/INTEGRATING.md` (operator-side: open the admin SPA, create the project, mint the key) and then re-run `/integrate-feedback` in consumer mode with the resulting handoff payload."
44
+ **"How does the handoff between phases work?"**
45
+ End of operator phase produces a Markdown payload (endpoint, `pk_proj_…`, slug, allowed origins). The skill prints it AND offers to install itself globally. The operator then closes this Claude session, `cd`s into the client's repo, opens a fresh `claude`, runs `/integrate-feedback`, picks "Install (consumer)", and pastes the payload as the first message.
44
46
 
45
- Stop. Don't continue operator mode without MCP.
47
+ **"My FAB isn't appearing what's wrong?"**
48
+ The FAB stays hidden until `fb.identify({id: 'something'})` runs with a non-empty `id`. Check `references/identify-snippets.md` for your auth provider's recipe. For testing, hardcode `fb.identify({id: 'test'})` to force-render.
46
49
 
47
- Consumer mode (install) does NOT need Chrome MCP for the install itself — only for the live smoke test in `references/verify-install.md`. If MCP isn't available, the skill can still do everything up to step 9 of consumer-install.md, and the smoke test degrades to "open this URL in your browser yourself and confirm the FAB renders."
50
+ **"CORS errors / 403 / origin not allowed."**
51
+ The project's `allowed_origins` doesn't include the URL the request comes from. Usual causes: forgot the dev URL (e.g. `http://localhost:5173`), trailing slash, path included, wildcard (rejected by design). Operator opens admin SPA → Edit project → adds the missing origin.
48
52
 
49
- ---
53
+ **"External client vs internal Mhosaic app?"**
50
54
 
51
- ## Operator mode provisioning
55
+ - Internal: use existing companies (`mhosaic`, `arime`, `norag`); create a new project under one. Needs Feedback group + Membership.
56
+ - External: create a brand-new Company. Needs `is_staff=True`. The client can't log into software-factory (it's @mhosaic.com-only SSO); Mhosaic acts as report-viewer for v1.
52
57
 
53
- **Goal:** end-to-end, produce a Markdown "handoff payload" the teammate can paste into Claude Code (or follow manually) to install the widget.
58
+ **"Do clients get email digests?"**
59
+ Not by default — notification dispatcher is `NoopDispatcher` on prod. Ping the operator if the client wants the weekly digest (it's a backend env var change).
54
60
 
55
- **Read** the full step-by-step procedure at:
61
+ **"Where's the human-readable doc?"**
62
+ `docs/INTEGRATING.md` in this repo. Covers the skill, the manual fallback, the CLI command reference, and a troubleshooting matrix.
56
63
 
57
- - `references/operator-provision.md`
64
+ ---
58
65
 
59
- The procedure walks through:
66
+ ## Step 0 — Identify the phase
60
67
 
61
- 1. Confirming the company exists (or creating it)
62
- 2. Collecting the project name, slug, and `allowed_origins` (the teammate's deployment URL[s])
63
- 3. Driving Chrome MCP to the admin SPA at https://software-factory-3tbbu.ondigitalocean.app
64
- 4. Calling the DRF API from the *already-authenticated* browser context (no token copy-paste)
65
- 5. Minting a `pk_proj_…` widget key and capturing its plaintext value
66
- 6. Assembling and printing the handoff payload
68
+ `AskUserQuestion`:
67
69
 
68
- **Crucial:** the operator must already be SSO-logged-in to software-factory before the skill drives Chrome. If they're not, the skill detects this and walks them through `/login` first.
70
+ - question: `Which phase are you running?`
71
+ - header: `Phase`
72
+ - options:
73
+ - label: `Provision (operator)`, description: `Create a new Company/Project/key on the Mhosaic backend. Run this from inside feedback-tool-mhosaic.`
74
+ - label: `Install (consumer)`, description: `Install the widget into a host app. Run this from inside the client's app directory, with the handoff payload from the operator phase.`
69
75
 
70
- After the operator branch finishes, ask whether they want to continue into consumer mode in the same conversation (helpful when they're integrating into one of their own apps).
76
+ Branch on the answer.
71
77
 
72
78
  ---
73
79
 
74
- ## Consumer modeinstalling
80
+ ## Step 0.5Pre-flight
75
81
 
76
- **Goal:** end-to-end, the widget loads on the host app and a smoke-test report lands in the admin.
82
+ **For operator phase:** confirm Chrome MCP is configured. Call `mcp__claude-in-chrome__tabs_context_mcp` once. If the tool isn't available, tell the user:
77
83
 
78
- **Read** the framework-aware install procedure at:
84
+ > "Operator phase needs Chrome MCP. Two options:
85
+ >
86
+ > 1. Install Chrome MCP (Claude Code → MCP settings → add the chrome connector), then re-run `/integrate-feedback`.
87
+ > 2. Provision manually via `docs/INTEGRATING.md` (open the admin SPA, create the project, mint the key)."
79
88
 
80
- - `references/consumer-install.md`
89
+ Stop. Don't continue without MCP.
90
+
91
+ **For consumer phase:** read `package.json` in the cwd. If its `name` is `@mhosaic/feedback-tool-mhosaic` or any of our own monorepo packages, the user is inside our own repo — refuse and redirect:
92
+
93
+ > "Looks like we're inside `feedback-tool-mhosaic`. The consumer phase writes to the current directory; running it here would install the widget into our own monorepo. Close Claude and re-open in the client app's root, then run `/integrate-feedback` again."
94
+
95
+ Stop. Don't continue.
96
+
97
+ ---
81
98
 
82
- The procedure walks through:
99
+ ## Operator phase
83
100
 
84
- 1. Verifying we're in a JS/TS project (reads `package.json`)
85
- 2. Detecting the framework (Vite/Next/Remix/Vue/SvelteKit/CDN)
86
- 3. Accepting the handoff payload — either pasted verbatim or via `--from-clipboard`
87
- 4. Running `npx @mhosaic/feedback-cli init --api-key=… --endpoint=… --yes` (non-interactive)
88
- 5. For non-Vite-React frameworks: pasting the framework-specific snippet from `references/consumer-install-<framework>.md` and asking the user to apply it
89
- 6. Wiring `fb.identify()` against the consumer's auth provider — recipes in `references/identify-snippets.md`
90
- 7. Optionally wiring `withErrorTracking` + `withReplay` + `withWebVitals`
91
- 8. Running `mhosaic-feedback verify --origin <consumer dev origin>` to confirm CORS, key, and endpoint
92
- 9. Starting the consumer's dev server and driving Chrome to verify the FAB renders
93
- 10. Submitting a `[INTEGRATE-FEEDBACK SMOKE TEST]` synthetic report from the FAB
94
- 11. Driving Chrome to software-factory's `/reports` and confirming the smoke-test report appears
101
+ Read **`references/operator-provision.md`** and follow it step by step:
95
102
 
96
- The smoke-test workflow lives at:
103
+ 1. Confirm operator is SSO-logged-in to software-factory in Chrome.
104
+ 2. Collect: company (existing or new), project name + slug, allowed origins, `share_reports_with_widget`.
105
+ 3. Drive Chrome MCP to the admin SPA, refresh the JWT via `/api/auth/token/refresh/`, call `POST /companies/`, `POST /projects/`, `POST /project-keys/create/` from the in-tab `fetch()` (uses the existing cookies).
106
+ 4. Capture the plaintext `pk_proj_…` from the keys response.
107
+ 5. Verify the project + key show up in the admin SPA.
108
+ 6. Print the Markdown handoff payload (format below).
109
+ 7. **(Optional, gated)** Offer server-log forwarding into the Observabilité tab: confirm the client is DO-hosted + pass the governance gate (internal/dogfood or signed DPA), call `POST /projects/<id>/observability/enable/`, then hand the client's DevOps a `log_destinations` block for their DO app spec. Details in `references/operator-provision.md` Step 5.5.
110
+ 8. Offer to install the skill globally so the operator can run `/integrate-feedback` in the client's repo for the consumer phase. The full offer logic is at the end of `references/operator-provision.md`.
97
111
 
98
- - `references/verify-install.md`
112
+ After this phase the operator has: (a) the handoff payload on their clipboard / in the chat, (b) optionally `/integrate-feedback` available globally so the next `claude` in the client repo picks it up.
99
113
 
100
114
  ---
101
115
 
102
- ## Principles to follow
116
+ ## Consumer phase
103
117
 
104
- - **Confirm at every fork.** Never assume a default. Use `AskUserQuestion` for choices, plain text questions for free-form input.
105
- - **Show, don't tell.** When you can run a command and show output, run it. When you can drive Chrome and screenshot, drive Chrome.
106
- - **Idempotent and reversible.** Every state-changing step has a stated rollback (e.g. revoke key in admin SPA, run `mhosaic-feedback eject`). Surface that to the user.
107
- - **Stop on first red.** If the smoke test fails, do not silently retry. Read `references/verify-install.md`'s diagnostic table, narrow the cause, fix it, then re-run from the failing step.
108
- - **Don't paste secret keys into git.** Walk the user through adding `.env.local` to `.gitignore` (CLI does this automatically; double-check).
109
- - **Don't claim "done" without proof.** End the workflow with a screenshot of the smoke-test report landing in admin OR with a clearly enumerated red checklist of what's still broken.
118
+ Read **`references/consumer-install.md`** and follow it step by step:
119
+
120
+ 1. Confirm cwd is the client app's root (NOT feedback-tool-mhosaic see Step 0.5).
121
+ 2. Parse the pasted handoff payload (endpoint, key, origins).
122
+ 3. Detect the framework from `package.json` route to `references/consumer-install-<framework>.md` (Vite, Next, Nuxt, Astro, Remix, Vue, SvelteKit, or plain/CDN).
123
+ 4. Run `npx @mhosaic/feedback-cli@latest init --api-key=… --endpoint=… --yes`.
124
+ 5. Apply the framework-specific snippet (CLI auto-wraps Vite+React; other frameworks need a manual paste — Claude does this via `Edit`).
125
+ 6. Wire `fb.identify()` per `references/identify-snippets.md` — pick the recipe matching the client's auth provider.
126
+ 7. Run `mhosaic-feedback verify --origin <dev-url> --with-test-report` — all 6 checks must be green before proceeding.
127
+ 8. Start the dev server, drive Chrome to the dev URL, screenshot the FAB, submit a `[INTEGRATE-FEEDBACK SMOKE TEST]` report.
128
+ 9. Drive Chrome to software-factory `/reports`, confirm the report landed.
129
+
130
+ Smoke test details + diagnostic table: **`references/verify-install.md`**.
110
131
 
111
132
  ---
112
133
 
113
134
  ## Operator → consumer handoff payload format
114
135
 
115
- Always produce this Markdown block at the end of operator mode. The consumer pastes it as the very first message to Claude after running `/integrate-feedback` in their own repo:
136
+ Always print this Markdown block at the end of operator phase:
116
137
 
117
138
  ````markdown
118
139
  ## Mhosaic Feedback handoff
@@ -122,7 +143,8 @@ Always produce this Markdown block at the end of operator mode. The consumer pas
122
143
  **Public widget key:** `pk_proj_xxxxxxxxxxxxxxxxxxxxxxxxxx`
123
144
  **Allowed origin(s):** `https://your-app.example.com`, `http://localhost:5173`
124
145
 
125
- To install:
146
+ To install (from the client's app directory):
147
+
126
148
  ```bash
127
149
  npx @mhosaic/feedback-cli@latest init \
128
150
  --api-key pk_proj_xxxxxxxxxxxxxxxxxxxxxxxxxx \
@@ -130,23 +152,26 @@ npx @mhosaic/feedback-cli@latest init \
130
152
  --yes
131
153
  ```
132
154
 
133
- Then verify:
134
- ```bash
135
- npx @mhosaic/feedback-cli@latest verify \
136
- --origin http://localhost:5173 \
137
- --with-test-report
138
- ```
155
+ Or for the full guided flow: `/integrate-feedback` → "Install (consumer)" → paste this block as your first message.
139
156
  ````
140
157
 
141
- The skill's consumer mode parses this block automatically when pasted as input.
158
+ ---
159
+
160
+ ## Principles
161
+
162
+ - **Never commit the API key to git. Ever.** Even `pk_proj_…` "public" keys. Full rules + scenario table at the top of `references/consumer-install.md` under "CRITICAL: API key handling". If the user asks "can I commit this?" the answer is NO; do not rationalize. If `.env.local` is excluded from the host's build context (common with Docker), use the deploy-platform's env-var UI, NOT a tracked `.env`.
163
+ - **Confirm at every fork.** Never assume a default. Use `AskUserQuestion` for choices, plain text for free-form input.
164
+ - **Show, don't tell.** When you can run a command and show output, run it. When you can drive Chrome and screenshot, drive Chrome.
165
+ - **Stop on first red.** If the smoke test fails, don't silently retry. Read `references/verify-install.md`'s diagnostic table, narrow the cause, fix it.
166
+ - **Don't claim "done" without proof.** End the workflow with a screenshot of the smoke-test report landing in admin OR an enumerated checklist of what's still broken.
142
167
 
143
168
  ---
144
169
 
145
170
  ## Skill files
146
171
 
147
172
  ```
148
- skills/integrate-feedback/
149
- ├── SKILL.md # this file (mode router)
173
+ .claude/skills/integrate-feedback/
174
+ ├── SKILL.md # this file (phase router)
150
175
  ├── references/
151
176
  │ ├── operator-provision.md # Chrome MCP → admin SPA → DRF
152
177
  │ ├── consumer-install.md # framework router
@@ -19,7 +19,7 @@ Astro uses Vite under the hood. Env vars meant for the browser need the `PUBLIC_
19
19
 
20
20
  ## Step 2 — Add a client-only widget mount
21
21
 
22
- Astro doesn't ship JS by default; you have to opt-in with `client:*` directives or via `<script>` blocks. The cleanest path is a `<script>` block in your shared layout:
22
+ Astro doesn't ship JS by default; you have to opt-in with `client:*` directives or via `<script>` blocks. The cleanest path is a `<script>` block in your shared layout. Error-tracking is **on by default** — the script wraps `withErrorTracking` so uncaught errors + unhandled rejections auto-file as synthetic reports:
23
23
 
24
24
  ```astro
25
25
  ---
@@ -33,17 +33,15 @@ const endpoint = import.meta.env.PUBLIC_FEEDBACK_ENDPOINT
33
33
  <slot />
34
34
 
35
35
  <script>
36
- import { createFeedback } from '@mhosaic/feedback'
36
+ import { createFeedback } from '@mhosaic/feedback/loader'
37
37
  import { withErrorTracking } from '@mhosaic/feedback/error-tracking'
38
- import { withReplay } from '@mhosaic/feedback/replay'
39
- import { withWebVitals } from '@mhosaic/feedback/webvitals'
40
38
 
41
39
  const apiKey = import.meta.env.PUBLIC_FEEDBACK_API_KEY
42
40
  const endpoint = import.meta.env.PUBLIC_FEEDBACK_ENDPOINT
43
41
  if (apiKey && endpoint) {
44
- withErrorTracking(withReplay(withWebVitals(createFeedback({
42
+ withErrorTracking(createFeedback({
45
43
  apiKey, endpoint, env: import.meta.env.PROD ? 'prod' : 'dev',
46
- }))))
44
+ }))
47
45
  }
48
46
  </script>
49
47
  </body>
@@ -54,6 +52,28 @@ Astro will bundle the script and run it on the client. The widget mounts to its
54
52
 
55
53
  ---
56
54
 
55
+ ## Optional — add web-vitals / replay
56
+
57
+ `withWebVitals` and `withReplay` stay **opt-in** (heavier / more sensitive). Add them inside the `<script>` block by chaining around the default instance, error-tracking innermost so it arms first:
58
+
59
+ ```astro
60
+ <script>
61
+ import { withReplay } from '@mhosaic/feedback/replay'
62
+ import { withWebVitals } from '@mhosaic/feedback/webvitals'
63
+
64
+ // replacing the default wrap:
65
+ withReplay(withWebVitals(withErrorTracking(createFeedback({
66
+ apiKey, endpoint, env: import.meta.env.PROD ? 'prod' : 'dev',
67
+ }))))
68
+ </script>
69
+ ```
70
+
71
+ Replay (rrweb, ~30 KiB gzip) captures DOM — confirm the privacy posture before enabling it.
72
+
73
+ **Opting out of error-tracking** (collect-on-consent posture): omit the `withErrorTracking` wrap and pass the bare `createFeedback(...)` instance. Note error-tracking POSTs error messages + stack traces — fine for most apps, but flag it if your error strings can carry end-user PII.
74
+
75
+ ---
76
+
57
77
  ## Step 3 — Wire identify()
58
78
 
59
79
  How you do this depends on what auth backs your Astro site:
@@ -21,16 +21,14 @@ Use the `Edit` tool to make the change.
21
21
 
22
22
  ## Step 2 — App Router (Next.js 13+) — paste the client component
23
23
 
24
- Create a new file `app/components/FeedbackMount.tsx`:
24
+ Create a new file `app/components/FeedbackMount.tsx`. Error-tracking is **on by default** — the default mount wraps `withErrorTracking` so uncaught errors + unhandled rejections auto-file as synthetic reports:
25
25
 
26
26
  ```typescript
27
27
  'use client'
28
28
 
29
29
  import { useEffect } from 'react'
30
- import { createFeedback } from '@mhosaic/feedback'
30
+ import { createFeedback } from '@mhosaic/feedback/loader'
31
31
  import { withErrorTracking } from '@mhosaic/feedback/error-tracking'
32
- import { withReplay } from '@mhosaic/feedback/replay'
33
- import { withWebVitals } from '@mhosaic/feedback/webvitals'
34
32
 
35
33
  export function FeedbackMount() {
36
34
  useEffect(() => {
@@ -39,9 +37,11 @@ export function FeedbackMount() {
39
37
  if (!apiKey || !endpoint) return
40
38
 
41
39
  withErrorTracking(
42
- withReplay(
43
- withWebVitals(createFeedback({ apiKey, endpoint, env: process.env.NODE_ENV === 'development' ? 'dev' : 'prod' }))
44
- )
40
+ createFeedback({
41
+ apiKey,
42
+ endpoint,
43
+ env: process.env.NODE_ENV === 'development' ? 'dev' : 'prod',
44
+ }),
45
45
  )
46
46
  }, [])
47
47
  return null
@@ -71,24 +71,22 @@ The `'use client'` directive in `FeedbackMount.tsx` is critical — it tells Nex
71
71
 
72
72
  ## Step 3 — Pages Router (Next.js 12 or App-Router-free 13+)
73
73
 
74
- Edit `pages/_app.tsx`:
74
+ Edit `pages/_app.tsx` (same default — `withErrorTracking` on by default):
75
75
 
76
76
  ```typescript
77
77
  import { useEffect } from 'react'
78
78
  import type { AppProps } from 'next/app'
79
- import { createFeedback } from '@mhosaic/feedback'
79
+ import { createFeedback } from '@mhosaic/feedback/loader'
80
80
  import { withErrorTracking } from '@mhosaic/feedback/error-tracking'
81
- import { withReplay } from '@mhosaic/feedback/replay'
82
- import { withWebVitals } from '@mhosaic/feedback/webvitals'
83
81
 
84
82
  export default function App({ Component, pageProps }: AppProps) {
85
83
  useEffect(() => {
86
84
  const apiKey = process.env.NEXT_PUBLIC_FEEDBACK_API_KEY
87
85
  const endpoint = process.env.NEXT_PUBLIC_FEEDBACK_ENDPOINT
88
86
  if (!apiKey || !endpoint) return
89
- withErrorTracking(withReplay(withWebVitals(createFeedback({
87
+ withErrorTracking(createFeedback({
90
88
  apiKey, endpoint, env: process.env.NODE_ENV === 'development' ? 'dev' : 'prod'
91
- }))))
89
+ }))
92
90
  }, [])
93
91
  return <Component {...pageProps} />
94
92
  }
@@ -96,6 +94,33 @@ export default function App({ Component, pageProps }: AppProps) {
96
94
 
97
95
  ---
98
96
 
97
+ ## Optional — add web-vitals / replay
98
+
99
+ `withWebVitals` and `withReplay` stay **opt-in** (heavier / more sensitive). Add them by chaining around the default instance, error-tracking innermost so it arms first. Import the extra wrappers and replace the `withErrorTracking(createFeedback(...))` call (in either router's snippet) with:
100
+
101
+ ```typescript
102
+ import { withReplay } from '@mhosaic/feedback/replay'
103
+ import { withWebVitals } from '@mhosaic/feedback/webvitals'
104
+
105
+ withReplay(
106
+ withWebVitals(
107
+ withErrorTracking(
108
+ createFeedback({
109
+ apiKey,
110
+ endpoint,
111
+ env: process.env.NODE_ENV === 'development' ? 'dev' : 'prod',
112
+ }),
113
+ ),
114
+ ),
115
+ )
116
+ ```
117
+
118
+ Replay (rrweb, ~30 KiB gzip) captures DOM — confirm the privacy posture before enabling it.
119
+
120
+ **Opting out of error-tracking** (collect-on-consent posture): omit the `withErrorTracking` wrap and pass the bare `createFeedback(...)` instance. Note error-tracking POSTs error messages + stack traces — fine for most apps, but flag it if your error strings can carry end-user PII.
121
+
122
+ ---
123
+
99
124
  ## Step 4 — Wire identify()
100
125
 
101
126
  See `references/identify-snippets.md`. Most Next.js apps use NextAuth or Clerk; both snippets there assume a client component with `useSession()` / `useUser()`.
@@ -107,7 +132,7 @@ If you're using NextAuth, the cleanest pattern is to add a `<FeedbackIdentity />
107
132
  ## Gotchas
108
133
 
109
134
  - **Server Components can't mount the widget.** Any module that touches the DOM (the widget's mount routine, rrweb's MutationObserver) must run on the client. Either wrap in `'use client'` or call inside a `useEffect`.
110
- - **Static generation:** if you use `getStaticProps` or App Router's static rendering, the widget's identity will only attach on hydration. Server-rendered pages will briefly show *without* the FAB until the client effect fires. That's expected.
135
+ - **Static generation:** if you use `getStaticProps` or App Router's static rendering, the widget's identity will only attach on hydration. Server-rendered pages will briefly show _without_ the FAB until the client effect fires. That's expected.
111
136
  - **Edge runtime:** the widget assumes `window` exists. Don't import it from anything that runs on the Edge runtime (middleware, route handlers with `runtime = 'edge'`).
112
137
  - **CSP:** if your `next.config.js` sets a strict CSP, you'll need to allow `connect-src` to the feedback endpoint and `worker-src 'blob:'` for rrweb. The widget docs cover this; cite them when CSP errors appear in DevTools.
113
138
 
@@ -34,24 +34,20 @@ Nuxt auto-binds `NUXT_PUBLIC_*` env vars to the matching `runtimeConfig.public.*
34
34
 
35
35
  ## Step 2 — Add a client-only plugin
36
36
 
37
- Create `plugins/feedback.client.ts` (the `.client.ts` suffix tells Nuxt to bundle this only for the browser, not SSR):
37
+ Create `plugins/feedback.client.ts` (the `.client.ts` suffix tells Nuxt to bundle this only for the browser, not SSR). Error-tracking is **on by default** — the plugin wraps `withErrorTracking` so uncaught errors + unhandled rejections auto-file as synthetic reports:
38
38
 
39
39
  ```typescript
40
- import { createFeedback } from '@mhosaic/feedback'
40
+ import { createFeedback } from '@mhosaic/feedback/loader'
41
41
  import { withErrorTracking } from '@mhosaic/feedback/error-tracking'
42
- import { withReplay } from '@mhosaic/feedback/replay'
43
- import { withWebVitals } from '@mhosaic/feedback/webvitals'
44
42
 
45
43
  export default defineNuxtPlugin(() => {
46
44
  const config = useRuntimeConfig()
47
45
  const fb = withErrorTracking(
48
- withReplay(
49
- withWebVitals(createFeedback({
50
- apiKey: config.public.feedbackApiKey as string,
51
- endpoint: config.public.feedbackEndpoint as string,
52
- env: process.dev ? 'dev' : 'prod',
53
- }))
54
- )
46
+ createFeedback({
47
+ apiKey: config.public.feedbackApiKey as string,
48
+ endpoint: config.public.feedbackEndpoint as string,
49
+ env: process.dev ? 'dev' : 'prod',
50
+ }),
55
51
  )
56
52
  return { provide: { feedback: fb } }
57
53
  })
@@ -61,6 +57,33 @@ Now `useNuxtApp().$feedback` is available across the app.
61
57
 
62
58
  ---
63
59
 
60
+ ## Optional — add web-vitals / replay
61
+
62
+ `withWebVitals` and `withReplay` stay **opt-in** (heavier / more sensitive). Add them by chaining around the default instance, error-tracking innermost so it arms first:
63
+
64
+ ```typescript
65
+ import { withReplay } from '@mhosaic/feedback/replay'
66
+ import { withWebVitals } from '@mhosaic/feedback/webvitals'
67
+
68
+ const fb = withReplay(
69
+ withWebVitals(
70
+ withErrorTracking(
71
+ createFeedback({
72
+ apiKey: config.public.feedbackApiKey as string,
73
+ endpoint: config.public.feedbackEndpoint as string,
74
+ env: process.dev ? 'dev' : 'prod',
75
+ }),
76
+ ),
77
+ ),
78
+ )
79
+ ```
80
+
81
+ Replay (rrweb, ~30 KiB gzip) captures DOM — confirm the privacy posture before enabling it.
82
+
83
+ **Opting out of error-tracking** (collect-on-consent posture): omit the `withErrorTracking` wrap and provide the bare `createFeedback(...)` instance. Note error-tracking POSTs error messages + stack traces — fine for most apps, but flag it if your error strings can carry end-user PII.
84
+
85
+ ---
86
+
64
87
  ## Step 3 — Wire identify()
65
88
 
66
89
  Nuxt's auth is whatever you bolt on (sidebase/nuxt-auth, custom, Supabase, etc.). Use the matching snippet from `references/identify-snippets.md`, called from a `watchEffect` inside `app.vue` or a top-level `<script setup>` that re-runs on auth state changes:
@@ -68,7 +91,7 @@ Nuxt's auth is whatever you bolt on (sidebase/nuxt-auth, custom, Supabase, etc.)
68
91
  ```vue
69
92
  <!-- app.vue -->
70
93
  <script setup lang="ts">
71
- import { useAuthStore } from '~/stores/auth' // adapt to your auth source
94
+ import { useAuthStore } from '~/stores/auth' // adapt to your auth source
72
95
 
73
96
  const auth = useAuthStore()
74
97
  const { $feedback } = useNuxtApp()