userflow-mcp 0.2.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 (87) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +177 -0
  3. package/dist/analysis/clarity.d.ts +7 -0
  4. package/dist/analysis/clarity.d.ts.map +1 -0
  5. package/dist/analysis/clarity.js +114 -0
  6. package/dist/analysis/clarity.js.map +1 -0
  7. package/dist/analysis/cognitive-load.d.ts +7 -0
  8. package/dist/analysis/cognitive-load.d.ts.map +1 -0
  9. package/dist/analysis/cognitive-load.js +45 -0
  10. package/dist/analysis/cognitive-load.js.map +1 -0
  11. package/dist/analysis/emotional-arc.d.ts +10 -0
  12. package/dist/analysis/emotional-arc.d.ts.map +1 -0
  13. package/dist/analysis/emotional-arc.js +95 -0
  14. package/dist/analysis/emotional-arc.js.map +1 -0
  15. package/dist/analysis/friction.d.ts +23 -0
  16. package/dist/analysis/friction.d.ts.map +1 -0
  17. package/dist/analysis/friction.js +61 -0
  18. package/dist/analysis/friction.js.map +1 -0
  19. package/dist/feedback/comparison.d.ts +11 -0
  20. package/dist/feedback/comparison.d.ts.map +1 -0
  21. package/dist/feedback/comparison.js +156 -0
  22. package/dist/feedback/comparison.js.map +1 -0
  23. package/dist/feedback/generator.d.ts +19 -0
  24. package/dist/feedback/generator.d.ts.map +1 -0
  25. package/dist/feedback/generator.js +139 -0
  26. package/dist/feedback/generator.js.map +1 -0
  27. package/dist/feedback/report.d.ts +10 -0
  28. package/dist/feedback/report.d.ts.map +1 -0
  29. package/dist/feedback/report.js +123 -0
  30. package/dist/feedback/report.js.map +1 -0
  31. package/dist/index.d.ts +3 -0
  32. package/dist/index.d.ts.map +1 -0
  33. package/dist/index.js +13 -0
  34. package/dist/index.js.map +1 -0
  35. package/dist/personas/engine.d.ts +39 -0
  36. package/dist/personas/engine.d.ts.map +1 -0
  37. package/dist/personas/engine.js +58 -0
  38. package/dist/personas/engine.js.map +1 -0
  39. package/dist/personas/presets.d.ts +11 -0
  40. package/dist/personas/presets.d.ts.map +1 -0
  41. package/dist/personas/presets.js +251 -0
  42. package/dist/personas/presets.js.map +1 -0
  43. package/dist/personas/types.d.ts +11 -0
  44. package/dist/personas/types.d.ts.map +1 -0
  45. package/dist/personas/types.js +23 -0
  46. package/dist/personas/types.js.map +1 -0
  47. package/dist/server.d.ts +3 -0
  48. package/dist/server.d.ts.map +1 -0
  49. package/dist/server.js +288 -0
  50. package/dist/server.js.map +1 -0
  51. package/dist/session/session-manager.d.ts +54 -0
  52. package/dist/session/session-manager.d.ts.map +1 -0
  53. package/dist/session/session-manager.js +228 -0
  54. package/dist/session/session-manager.js.map +1 -0
  55. package/dist/session/types.d.ts +35 -0
  56. package/dist/session/types.d.ts.map +1 -0
  57. package/dist/session/types.js +2 -0
  58. package/dist/session/types.js.map +1 -0
  59. package/dist/types.d.ts +284 -0
  60. package/dist/types.d.ts.map +1 -0
  61. package/dist/types.js +3 -0
  62. package/dist/types.js.map +1 -0
  63. package/dist/utils/actions.d.ts +17 -0
  64. package/dist/utils/actions.d.ts.map +1 -0
  65. package/dist/utils/actions.js +89 -0
  66. package/dist/utils/actions.js.map +1 -0
  67. package/dist/utils/browser.d.ts +22 -0
  68. package/dist/utils/browser.d.ts.map +1 -0
  69. package/dist/utils/browser.js +122 -0
  70. package/dist/utils/browser.js.map +1 -0
  71. package/dist/utils/page-snapshot.d.ts +11 -0
  72. package/dist/utils/page-snapshot.d.ts.map +1 -0
  73. package/dist/utils/page-snapshot.js +102 -0
  74. package/dist/utils/page-snapshot.js.map +1 -0
  75. package/dist/walker/action-planner.d.ts +15 -0
  76. package/dist/walker/action-planner.d.ts.map +1 -0
  77. package/dist/walker/action-planner.js +236 -0
  78. package/dist/walker/action-planner.js.map +1 -0
  79. package/dist/walker/flow-walker.d.ts +10 -0
  80. package/dist/walker/flow-walker.d.ts.map +1 -0
  81. package/dist/walker/flow-walker.js +107 -0
  82. package/dist/walker/flow-walker.js.map +1 -0
  83. package/dist/walker/session-recorder.d.ts +28 -0
  84. package/dist/walker/session-recorder.d.ts.map +1 -0
  85. package/dist/walker/session-recorder.js +90 -0
  86. package/dist/walker/session-recorder.js.map +1 -0
  87. package/package.json +71 -0
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 ARISTONE
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,177 @@
1
+ # UserFlow MCP
2
+
3
+ **Simulates real users navigating your app and delivers qualitative UX feedback.** Built as an MCP server for Claude Code.
4
+
5
+ UserFlow doesn't run Lighthouse scores or check WCAG compliance — it puts itself in your user's shoes. It clicks through your app as different personas (a first-time user, a busy executive, a senior citizen, an accessibility-dependent user) and tells you where they'd get confused, frustrated, or give up.
6
+
7
+ > **Free for Claude Pro users.** No API keys, no external services. Just install and go.
8
+
9
+ ## Quick Start
10
+
11
+ ```bash
12
+ # Install globally
13
+ npm install -g userflow-mcp
14
+
15
+ # Or use directly with npx
16
+ npx -y userflow-mcp
17
+ ```
18
+
19
+ ### Add to Claude Code
20
+
21
+ In your Claude Code MCP settings:
22
+
23
+ ```json
24
+ {
25
+ "mcpServers": {
26
+ "userflow": {
27
+ "command": "npx",
28
+ "args": ["-y", "userflow-mcp"]
29
+ }
30
+ }
31
+ }
32
+ ```
33
+
34
+ Then in Claude Code:
35
+
36
+ ```
37
+ → simulate_user url:"https://myapp.com" persona:"Alex"
38
+ → quick_impression url:"https://myapp.com"
39
+ → compare_personas url:"https://myapp.com" personas:["Alex", "Morgan", "Patricia"]
40
+ ```
41
+
42
+ ## What You Get
43
+
44
+ Instead of scores and violations, you get feedback like this:
45
+
46
+ ```
47
+ Step 1: Landing Page (3.2s)
48
+ 🔍 curious
49
+ > "Hmm, 'Supercharge your workflow' — but what does this product actually do?"
50
+ ⚠️ MEDIUM: Value prop unclear — heading doesn't explain the product
51
+ → Rewrite heading to describe what the product does, not how it makes you feel
52
+
53
+ Step 2: Signup (12.1s)
54
+ 😐 neutral
55
+ > "Alright, let me sign up and see..."
56
+ 🛑 HIGH: Form asks for company size during signup — feels invasive
57
+ → Remove non-essential fields from signup, ask later during onboarding
58
+
59
+ Step 3: Dashboard (8.4s)
60
+ 😤 frustrated
61
+ > "Okay I'm in... now what? There's nothing here."
62
+ 🚨 CRITICAL: Empty state with no guidance
63
+ → Add a getting-started checklist or demo project
64
+ ```
65
+
66
+ ## 11 Tools
67
+
68
+ | Tool | Description |
69
+ |------|-------------|
70
+ | **simulate_user** | Full persona simulation — walks the entire flow, records every step |
71
+ | **quick_impression** | 30-second first impression — would this user stay or bounce? |
72
+ | **test_onboarding** | Test signup/onboarding as a first-time user |
73
+ | **test_checkout** | Test purchase/conversion flow |
74
+ | **compare_personas** | Run 2-5 personas on the same flow, compare experiences |
75
+ | **find_dead_ends** | Discover pages with no exit, empty states, broken flows |
76
+ | **rate_clarity** | Evaluate value prop, CTAs, cognitive load — with scores |
77
+ | **session_transcript** | Detailed step-by-step journey log with thoughts |
78
+ | **custom_persona** | Define your own persona with specific traits and goals |
79
+ | **export_report** | Generate standalone HTML report |
80
+ | **list_personas** | See all built-in personas with descriptions |
81
+
82
+ ## 8 Built-in Personas
83
+
84
+ | Name | Description | Tech | Patience | Device |
85
+ |------|-------------|------|----------|--------|
86
+ | **Alex** | The First-Timer — never used SaaS before | Novice | Moderate | Mobile |
87
+ | **Morgan** | The Power User — developer, expects excellence | Expert | Low | Desktop |
88
+ | **Patricia** | The Senior Explorer — 68, low vision | Basic | High | Desktop |
89
+ | **Jordan** | The Busy Executive — 10 seconds to impress | Intermediate | Very Low | Mobile |
90
+ | **Sam** | The Accessibility Tester — screen reader user | Advanced | Moderate | Desktop |
91
+ | **Riley** | The Skeptical Evaluator — looking for red flags | Intermediate | Moderate | Desktop |
92
+ | **Casey** | The International User — potential language barriers | Basic | High | Mobile |
93
+ | **Taylor** | The Return Visitor — knows the app, wants efficiency | Advanced | Moderate | Desktop |
94
+
95
+ ## How It Works
96
+
97
+ 1. **Puppeteer** opens your URL in a real browser
98
+ 2. The **persona engine** creates a user with specific traits (tech literacy, patience, goals, device)
99
+ 3. The **flow walker** autonomously navigates — clicking buttons, filling forms, scrolling — based on what the persona would actually do
100
+ 4. At each step, the **analysis engine** checks for:
101
+ - **Friction points** — confusing CTAs, too many form fields, empty states, slow loads
102
+ - **Cognitive load** — how overwhelming the page is
103
+ - **Clarity** — is the value prop clear? Can the user find the CTA?
104
+ - **Emotional state** — curious → confident → confused → frustrated
105
+ 5. The **feedback generator** compiles everything into a structured report
106
+
107
+ No LLM calls during simulation — all analysis uses deterministic heuristics. This means it's **fast**, **free**, and **consistent**.
108
+
109
+ ## Custom Personas
110
+
111
+ ```
112
+ → custom_persona url:"https://myapp.com"
113
+ name:"Startup Founder"
114
+ description:"Technical founder evaluating tools for their team"
115
+ goals:["understand pricing", "evaluate team features", "check integrations"]
116
+ tech_literacy:"advanced"
117
+ patience:"low"
118
+ device:"desktop"
119
+ ```
120
+
121
+ ## Multi-Persona Comparison
122
+
123
+ The `compare_personas` tool reveals how different users experience the same flow:
124
+
125
+ ```
126
+ → compare_personas url:"https://myapp.com" personas:["Alex", "Jordan", "Patricia"]
127
+ ```
128
+
129
+ Output includes:
130
+ - **Shared friction** — issues ALL users hit (fix these first)
131
+ - **Divergence points** — moments where different users interpret the UI differently
132
+ - **Per-persona summaries** — friction scores, emotional trends, goal achievement
133
+
134
+ ## Architecture
135
+
136
+ ```
137
+ src/
138
+ ├── server.ts # MCP tool registrations (11 tools)
139
+ ├── personas/
140
+ │ ├── presets.ts # 8 built-in personas
141
+ │ └── engine.ts # Persona creation + utilities
142
+ ├── walker/
143
+ │ ├── flow-walker.ts # Autonomous page traversal
144
+ │ ├─�� action-planner.ts # Decides what a persona would do
145
+ │ └── session-recorder.ts # Journey tracking
146
+ ├── analysis/
147
+ │ ├── friction.ts # Friction detection + scoring
148
+ │ ├── cognitive-load.ts # Page complexity assessment
149
+ │ ��── clarity.ts # CTA + value prop evaluation
150
+ │ └── emotional-arc.ts # Sentiment tracking
151
+ ├── feedback/
152
+ │ ├── generator.ts # Markdown report generation
153
+ │ ├── comparison.ts # Multi-persona comparison
154
+ │ └── report.ts # HTML report generation
155
+ └── utils/
156
+ └── browser.ts # Puppeteer browser management
157
+ ```
158
+
159
+ ## Requirements
160
+
161
+ - **Node.js** >= 18.0.0
162
+ - **Google Chrome** or Chromium installed
163
+ - **Claude Code** with MCP support
164
+
165
+ ## Development
166
+
167
+ ```bash
168
+ git clone https://github.com/prembobby39-gif/userflow-mcp.git
169
+ cd userflow-mcp
170
+ npm install
171
+ npm run build
172
+ npm test
173
+ ```
174
+
175
+ ## License
176
+
177
+ MIT — ARISTONE
@@ -0,0 +1,7 @@
1
+ import type { PageSnapshot, ClarityAssessment } from "../types.js";
2
+ /**
3
+ * Assess how clear a page's purpose, CTAs, and navigation are.
4
+ * Returns a score from 0 (completely unclear) to 10 (crystal clear).
5
+ */
6
+ export declare function assessClarity(page: PageSnapshot): ClarityAssessment;
7
+ //# sourceMappingURL=clarity.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"clarity.d.ts","sourceRoot":"","sources":["../../src/analysis/clarity.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAC;AAEnE;;;GAGG;AACH,wBAAgB,aAAa,CAAC,IAAI,EAAE,YAAY,GAAG,iBAAiB,CAsGnE"}
@@ -0,0 +1,114 @@
1
+ /**
2
+ * Assess how clear a page's purpose, CTAs, and navigation are.
3
+ * Returns a score from 0 (completely unclear) to 10 (crystal clear).
4
+ */
5
+ export function assessClarity(page) {
6
+ let score = 10; // Start perfect, deduct for issues
7
+ const issues = [];
8
+ // Value proposition (from headings)
9
+ let valueProposition;
10
+ if (page.headings.length > 0) {
11
+ const h1 = page.headings[0];
12
+ const hasAction = /\b(get|start|build|create|manage|track|discover|learn|grow|save|boost)\b/i.test(h1);
13
+ const isVague = /\b(welcome|hello|home|dashboard)\b/i.test(h1) && h1.split(" ").length < 5;
14
+ if (isVague) {
15
+ valueProposition = `Vague heading: "${h1}" — doesn't explain what the product does`;
16
+ score -= 2;
17
+ }
18
+ else if (hasAction) {
19
+ valueProposition = `Clear action-oriented heading: "${h1}"`;
20
+ }
21
+ else if (h1.length > 10) {
22
+ valueProposition = `Descriptive heading: "${h1}"`;
23
+ score -= 0.5;
24
+ }
25
+ else {
26
+ valueProposition = `Short heading: "${h1}" — may not communicate enough`;
27
+ score -= 1;
28
+ }
29
+ }
30
+ else {
31
+ valueProposition = "No heading found — users won't know what this page is about";
32
+ score -= 3;
33
+ }
34
+ // CTA clarity
35
+ let ctaClarity;
36
+ const visibleButtons = page.buttons.filter((b) => b.isVisible);
37
+ if (visibleButtons.length === 0) {
38
+ ctaClarity = "No visible buttons — unclear what action to take";
39
+ score -= 2;
40
+ }
41
+ else if (visibleButtons.length === 1) {
42
+ ctaClarity = `Single clear CTA: "${visibleButtons[0].text}"`;
43
+ score += 0.5;
44
+ }
45
+ else if (visibleButtons.length <= 3) {
46
+ ctaClarity = `${visibleButtons.length} buttons — clear hierarchy if primary CTA is visually distinct`;
47
+ }
48
+ else {
49
+ ctaClarity = `${visibleButtons.length} buttons — too many competing CTAs, consider reducing`;
50
+ score -= 1;
51
+ }
52
+ // Navigation logic
53
+ let navigationLogic;
54
+ const navLinks = page.links.filter((l) => l.isVisible);
55
+ if (navLinks.length === 0 && page.buttons.length === 0) {
56
+ navigationLogic = "No navigation options — user is stuck";
57
+ score -= 3;
58
+ }
59
+ else if (navLinks.length > 20) {
60
+ navigationLogic = `${navLinks.length} links — navigation is cluttered and overwhelming`;
61
+ score -= 1.5;
62
+ }
63
+ else if (navLinks.length > 0) {
64
+ navigationLogic = `${navLinks.length} links available — reasonable navigation density`;
65
+ }
66
+ else {
67
+ navigationLogic = "Navigation relies only on buttons, no standard links";
68
+ score -= 0.5;
69
+ }
70
+ // Heading structure
71
+ let headingStructure;
72
+ if (page.headings.length >= 3) {
73
+ headingStructure = `Good structure: ${page.headings.length} headings organize the content`;
74
+ }
75
+ else if (page.headings.length > 0) {
76
+ headingStructure = `Minimal structure: only ${page.headings.length} heading(s)`;
77
+ score -= 0.5;
78
+ }
79
+ else {
80
+ headingStructure = "No heading structure — content is unorganized";
81
+ score -= 1;
82
+ }
83
+ // Error state
84
+ if (page.errorMessages.length > 0) {
85
+ score -= 2;
86
+ issues.push(`${page.errorMessages.length} error messages visible`);
87
+ }
88
+ // Empty page
89
+ if (page.mainText.trim().length < 30) {
90
+ score -= 2;
91
+ issues.push("Extremely sparse content — page appears empty or broken");
92
+ }
93
+ const clampedScore = Math.max(0, Math.min(10, Math.round(score * 10) / 10));
94
+ const assessmentParts = [];
95
+ if (clampedScore >= 8)
96
+ assessmentParts.push("Page purpose and next steps are clear.");
97
+ else if (clampedScore >= 6)
98
+ assessmentParts.push("Page is mostly clear but has some ambiguity.");
99
+ else if (clampedScore >= 4)
100
+ assessmentParts.push("Page clarity needs improvement — users may be confused.");
101
+ else
102
+ assessmentParts.push("Page is unclear — users won't know what to do.");
103
+ if (issues.length > 0)
104
+ assessmentParts.push(`Issues: ${issues.join("; ")}.`);
105
+ return {
106
+ score: clampedScore,
107
+ valueProposition,
108
+ ctaClarity,
109
+ navigationLogic,
110
+ headingStructure,
111
+ assessment: assessmentParts.join(" "),
112
+ };
113
+ }
114
+ //# sourceMappingURL=clarity.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"clarity.js","sourceRoot":"","sources":["../../src/analysis/clarity.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,7 @@
1
+ import type { PageSnapshot, CognitiveLoadAssessment } from "../types.js";
2
+ /**
3
+ * Assess the cognitive load of a page snapshot.
4
+ * Returns a score from 0 (minimal) to 10 (overwhelming).
5
+ */
6
+ export declare function assessCognitiveLoad(page: PageSnapshot): CognitiveLoadAssessment;
7
+ //# sourceMappingURL=cognitive-load.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cognitive-load.d.ts","sourceRoot":"","sources":["../../src/analysis/cognitive-load.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,uBAAuB,EAAE,MAAM,aAAa,CAAC;AAEzE;;;GAGG;AACH,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,YAAY,GAAG,uBAAuB,CAsC/E"}
@@ -0,0 +1,45 @@
1
+ /**
2
+ * Assess the cognitive load of a page snapshot.
3
+ * Returns a score from 0 (minimal) to 10 (overwhelming).
4
+ */
5
+ export function assessCognitiveLoad(page) {
6
+ const elementCount = page.interactiveElements.length + page.headings.length;
7
+ const interactiveCount = page.interactiveElements.length;
8
+ const textLength = page.mainText.length;
9
+ const textDensity = textLength / Math.max(1, page.headings.length + 1); // chars per section
10
+ const decisionPoints = page.buttons.length + page.links.filter((l) => l.isVisible).length;
11
+ // Score components (each 0-2, sum to 0-10)
12
+ const elementScore = elementCount > 50 ? 2 : elementCount > 30 ? 1.5 : elementCount > 15 ? 1 : 0.5;
13
+ const interactiveScore = interactiveCount > 20 ? 2 : interactiveCount > 12 ? 1.5 : interactiveCount > 6 ? 1 : 0.5;
14
+ const textScore = textDensity > 500 ? 2 : textDensity > 300 ? 1.5 : textDensity > 150 ? 1 : 0.5;
15
+ const decisionScore = decisionPoints > 15 ? 2 : decisionPoints > 8 ? 1.5 : decisionPoints > 4 ? 1 : 0.5;
16
+ const formScore = page.formFields.length > 6 ? 2 : page.formFields.length > 3 ? 1.5 : page.formFields.length > 0 ? 1 : 0;
17
+ const rawScore = elementScore + interactiveScore + textScore + decisionScore + formScore;
18
+ const score = Math.min(10, Math.round(rawScore * 10) / 10);
19
+ const visualComplexity = score > 7 ? "Very High" : score > 5 ? "High" : score > 3 ? "Moderate" : "Low";
20
+ const assessmentParts = [];
21
+ if (score > 7)
22
+ assessmentParts.push("This page is overwhelming — too many elements competing for attention.");
23
+ else if (score > 5)
24
+ assessmentParts.push("This page has high cognitive load — users may feel decision fatigue.");
25
+ else if (score > 3)
26
+ assessmentParts.push("This page has moderate cognitive load — manageable for most users.");
27
+ else
28
+ assessmentParts.push("This page has low cognitive load — clean and focused.");
29
+ if (decisionPoints > 10)
30
+ assessmentParts.push(`${decisionPoints} clickable elements create decision paralysis.`);
31
+ if (page.formFields.length > 5)
32
+ assessmentParts.push(`Form with ${page.formFields.length} fields adds significant friction.`);
33
+ if (page.headings.length === 0)
34
+ assessmentParts.push("No headings to organize content — users lack visual anchors.");
35
+ return {
36
+ score,
37
+ elementCount,
38
+ interactiveCount,
39
+ textDensity: Math.round(textDensity),
40
+ decisionPoints,
41
+ visualComplexity,
42
+ assessment: assessmentParts.join(" "),
43
+ };
44
+ }
45
+ //# sourceMappingURL=cognitive-load.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cognitive-load.js","sourceRoot":"","sources":["../../src/analysis/cognitive-load.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,10 @@
1
+ import type { EmotionalArc, SessionStep } from "../types.js";
2
+ /**
3
+ * Analyze the emotional arc across a session's steps.
4
+ */
5
+ export declare function analyzeEmotionalArc(steps: readonly SessionStep[]): EmotionalArc;
6
+ /**
7
+ * Get a human-readable summary of the emotional journey.
8
+ */
9
+ export declare function summarizeEmotionalArc(arc: EmotionalArc): string;
10
+ //# sourceMappingURL=emotional-arc.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"emotional-arc.d.ts","sourceRoot":"","sources":["../../src/analysis/emotional-arc.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAkB,YAAY,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAa7E;;GAEG;AACH,wBAAgB,mBAAmB,CAAC,KAAK,EAAE,SAAS,WAAW,EAAE,GAAG,YAAY,CAuC/E;AA+BD;;GAEG;AACH,wBAAgB,qBAAqB,CAAC,GAAG,EAAE,YAAY,GAAG,MAAM,CAsB/D"}
@@ -0,0 +1,95 @@
1
+ const EMOTION_VALENCE = {
2
+ delighted: 5,
3
+ confident: 4,
4
+ curious: 3,
5
+ neutral: 2,
6
+ anxious: 1,
7
+ confused: 0,
8
+ bored: -1,
9
+ frustrated: -2,
10
+ };
11
+ /**
12
+ * Analyze the emotional arc across a session's steps.
13
+ */
14
+ export function analyzeEmotionalArc(steps) {
15
+ if (steps.length === 0) {
16
+ return {
17
+ states: [],
18
+ trend: "stable",
19
+ lowestPoint: { step: 0, state: "neutral" },
20
+ highestPoint: { step: 0, state: "neutral" },
21
+ };
22
+ }
23
+ const states = steps.map((s) => s.emotionalState);
24
+ // Find highest and lowest points
25
+ let lowestStep = 0;
26
+ let lowestValence = Infinity;
27
+ let highestStep = 0;
28
+ let highestValence = -Infinity;
29
+ for (let i = 0; i < states.length; i++) {
30
+ const valence = EMOTION_VALENCE[states[i]] ?? 2;
31
+ if (valence < lowestValence) {
32
+ lowestValence = valence;
33
+ lowestStep = i;
34
+ }
35
+ if (valence > highestValence) {
36
+ highestValence = valence;
37
+ highestStep = i;
38
+ }
39
+ }
40
+ // Determine trend
41
+ const trend = determineTrend(states);
42
+ return {
43
+ states,
44
+ trend,
45
+ lowestPoint: { step: lowestStep, state: states[lowestStep] },
46
+ highestPoint: { step: highestStep, state: states[highestStep] },
47
+ };
48
+ }
49
+ /**
50
+ * Determine the overall emotional trend of the session.
51
+ */
52
+ function determineTrend(states) {
53
+ if (states.length < 2)
54
+ return "stable";
55
+ const valences = states.map((s) => EMOTION_VALENCE[s] ?? 2);
56
+ // Calculate changes between consecutive steps
57
+ const changes = [];
58
+ for (let i = 1; i < valences.length; i++) {
59
+ changes.push(valences[i] - valences[i - 1]);
60
+ }
61
+ const avgChange = changes.reduce((sum, c) => sum + c, 0) / changes.length;
62
+ const volatility = changes.reduce((sum, c) => sum + Math.abs(c), 0) / changes.length;
63
+ // High volatility = volatile
64
+ if (volatility > 2)
65
+ return "volatile";
66
+ // Consistent direction
67
+ if (avgChange > 0.3)
68
+ return "improving";
69
+ if (avgChange < -0.3)
70
+ return "declining";
71
+ return "stable";
72
+ }
73
+ /**
74
+ * Get a human-readable summary of the emotional journey.
75
+ */
76
+ export function summarizeEmotionalArc(arc) {
77
+ if (arc.states.length === 0)
78
+ return "No emotional data recorded.";
79
+ const trendDescriptions = {
80
+ improving: "The user's experience improved over the session",
81
+ stable: "The user's emotional state remained relatively consistent",
82
+ declining: "The user became increasingly frustrated or disengaged",
83
+ volatile: "The user's experience was inconsistent — swinging between positive and negative",
84
+ };
85
+ const parts = [trendDescriptions[arc.trend] ?? "Unknown trend"];
86
+ parts.push(`Lowest point: step ${arc.lowestPoint.step} (${arc.lowestPoint.state})`);
87
+ parts.push(`Highest point: step ${arc.highestPoint.step} (${arc.highestPoint.state})`);
88
+ const startEmotion = arc.states[0];
89
+ const endEmotion = arc.states[arc.states.length - 1];
90
+ if (startEmotion !== endEmotion) {
91
+ parts.push(`Journey: ${startEmotion} → ${endEmotion}`);
92
+ }
93
+ return parts.join(". ") + ".";
94
+ }
95
+ //# sourceMappingURL=emotional-arc.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"emotional-arc.js","sourceRoot":"","sources":["../../src/analysis/emotional-arc.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,23 @@
1
+ import type { FrictionPoint, SessionStep, FrictionSeverity } from "../types.js";
2
+ /**
3
+ * Calculate an overall friction score for a set of steps.
4
+ * Returns 0-10 where 10 is maximum friction.
5
+ */
6
+ export declare function calculateFrictionScore(steps: readonly SessionStep[]): number;
7
+ /**
8
+ * Get all friction points sorted by severity (critical first).
9
+ */
10
+ export declare function getSortedFriction(steps: readonly SessionStep[]): readonly FrictionPoint[];
11
+ /**
12
+ * Identify which steps have the highest friction concentration.
13
+ */
14
+ export declare function getHighFrictionSteps(steps: readonly SessionStep[]): readonly SessionStep[];
15
+ /**
16
+ * Group friction points by their location (URL).
17
+ */
18
+ export declare function groupFrictionByLocation(steps: readonly SessionStep[]): ReadonlyMap<string, readonly FrictionPoint[]>;
19
+ /**
20
+ * Get a count summary of friction by severity level.
21
+ */
22
+ export declare function countBySeverity(steps: readonly SessionStep[]): Readonly<Record<FrictionSeverity, number>>;
23
+ //# sourceMappingURL=friction.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"friction.d.ts","sourceRoot":"","sources":["../../src/analysis/friction.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,WAAW,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAC;AAShF;;;GAGG;AACH,wBAAgB,sBAAsB,CAAC,KAAK,EAAE,SAAS,WAAW,EAAE,GAAG,MAAM,CAY5E;AAED;;GAEG;AACH,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,SAAS,WAAW,EAAE,GAAG,SAAS,aAAa,EAAE,CAKzF;AAED;;GAEG;AACH,wBAAgB,oBAAoB,CAAC,KAAK,EAAE,SAAS,WAAW,EAAE,GAAG,SAAS,WAAW,EAAE,CAI1F;AAED;;GAEG;AACH,wBAAgB,uBAAuB,CACrC,KAAK,EAAE,SAAS,WAAW,EAAE,GAC5B,WAAW,CAAC,MAAM,EAAE,SAAS,aAAa,EAAE,CAAC,CAU/C;AAED;;GAEG;AACH,wBAAgB,eAAe,CAC7B,KAAK,EAAE,SAAS,WAAW,EAAE,GAC5B,QAAQ,CAAC,MAAM,CAAC,gBAAgB,EAAE,MAAM,CAAC,CAAC,CAQ5C"}
@@ -0,0 +1,61 @@
1
+ const SEVERITY_WEIGHTS = {
2
+ low: 1,
3
+ medium: 2,
4
+ high: 4,
5
+ critical: 7,
6
+ };
7
+ /**
8
+ * Calculate an overall friction score for a set of steps.
9
+ * Returns 0-10 where 10 is maximum friction.
10
+ */
11
+ export function calculateFrictionScore(steps) {
12
+ const allFriction = steps.flatMap((s) => s.frictionPoints);
13
+ if (allFriction.length === 0)
14
+ return 0;
15
+ const totalWeight = allFriction.reduce((sum, f) => sum + (SEVERITY_WEIGHTS[f.severity] ?? 1), 0);
16
+ // Normalize: scale by step count, cap at 10
17
+ const normalized = (totalWeight / Math.max(1, steps.length)) * 2;
18
+ return Math.min(10, Math.round(normalized * 10) / 10);
19
+ }
20
+ /**
21
+ * Get all friction points sorted by severity (critical first).
22
+ */
23
+ export function getSortedFriction(steps) {
24
+ const severityOrder = { critical: 0, high: 1, medium: 2, low: 3 };
25
+ return steps
26
+ .flatMap((s) => s.frictionPoints)
27
+ .sort((a, b) => (severityOrder[a.severity] ?? 3) - (severityOrder[b.severity] ?? 3));
28
+ }
29
+ /**
30
+ * Identify which steps have the highest friction concentration.
31
+ */
32
+ export function getHighFrictionSteps(steps) {
33
+ return steps.filter((s) => s.frictionPoints.some((f) => f.severity === "high" || f.severity === "critical"));
34
+ }
35
+ /**
36
+ * Group friction points by their location (URL).
37
+ */
38
+ export function groupFrictionByLocation(steps) {
39
+ const map = new Map();
40
+ for (const step of steps) {
41
+ for (const friction of step.frictionPoints) {
42
+ const existing = map.get(friction.location) ?? [];
43
+ existing.push(friction);
44
+ map.set(friction.location, existing);
45
+ }
46
+ }
47
+ return map;
48
+ }
49
+ /**
50
+ * Get a count summary of friction by severity level.
51
+ */
52
+ export function countBySeverity(steps) {
53
+ const counts = { low: 0, medium: 0, high: 0, critical: 0 };
54
+ for (const step of steps) {
55
+ for (const friction of step.frictionPoints) {
56
+ counts[friction.severity]++;
57
+ }
58
+ }
59
+ return counts;
60
+ }
61
+ //# sourceMappingURL=friction.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"friction.js","sourceRoot":"","sources":["../../src/analysis/friction.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,11 @@
1
+ import type { UserSession, PersonaComparison } from "../types.js";
2
+ /**
3
+ * Compare sessions from multiple personas on the same flow.
4
+ * Identifies shared friction points and divergence points.
5
+ */
6
+ export declare function comparePersonaSessions(sessions: readonly UserSession[]): PersonaComparison;
7
+ /**
8
+ * Generate a markdown comparison report.
9
+ */
10
+ export declare function generateComparisonReport(comparison: PersonaComparison): string;
11
+ //# sourceMappingURL=comparison.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"comparison.d.ts","sourceRoot":"","sources":["../../src/feedback/comparison.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,iBAAiB,EAAiB,MAAM,aAAa,CAAC;AAEjF;;;GAGG;AACH,wBAAgB,sBAAsB,CACpC,QAAQ,EAAE,SAAS,WAAW,EAAE,GAC/B,iBAAiB,CAuHnB;AAED;;GAEG;AACH,wBAAgB,wBAAwB,CAAC,UAAU,EAAE,iBAAiB,GAAG,MAAM,CA0D9E"}