@viren/claude-code-dashboard 0.0.6 → 0.0.7

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@viren/claude-code-dashboard",
3
- "version": "0.0.6",
3
+ "version": "0.0.7",
4
4
  "description": "A visual dashboard for your Claude Code configuration across all repos",
5
5
  "type": "module",
6
6
  "bin": {
package/src/analysis.mjs CHANGED
@@ -90,22 +90,29 @@ export function detectTechStack(repoDir) {
90
90
  return { stacks: [...stacks] };
91
91
  }
92
92
 
93
- export function computeDrift(repoDir, configTimestamp) {
94
- if (!configTimestamp) return { level: "unknown", commitsSince: 0 };
93
+ export function classifyDrift(commitCount) {
94
+ if (commitCount === null || commitCount === undefined || commitCount < 0) {
95
+ return { level: "unknown", commitsSince: 0 };
96
+ }
97
+ const n = Math.max(0, Number(commitCount) || 0);
98
+ if (n === 0) return { level: "synced", commitsSince: 0 };
99
+ if (n <= 5) return { level: "low", commitsSince: n };
100
+ if (n <= 20) return { level: "medium", commitsSince: n };
101
+ return { level: "high", commitsSince: n };
102
+ }
95
103
 
96
- // Count commits since the config was last updated
104
+ /** Count commits since config was last updated. Returns null if unknown. */
105
+ export function getGitRevCount(repoDir, configTimestamp) {
106
+ if (!configTimestamp) return null;
97
107
  const countStr = gitCmd(repoDir, "rev-list", "--count", `--since=${configTimestamp}`, "HEAD");
98
- if (!countStr) return { level: "unknown", commitsSince: 0 };
99
-
108
+ if (!countStr) return null;
100
109
  const parsed = Number(countStr);
101
- if (!Number.isFinite(parsed)) return { level: "unknown", commitsSince: 0 };
102
-
103
- const commitsSince = Math.max(0, parsed - 1); // -1 to exclude the config commit itself
110
+ if (!Number.isFinite(parsed)) return null;
111
+ return Math.max(0, parsed - 1); // -1 to exclude the config commit itself
112
+ }
104
113
 
105
- if (commitsSince === 0) return { level: "synced", commitsSince: 0 };
106
- if (commitsSince <= 5) return { level: "low", commitsSince };
107
- if (commitsSince <= 20) return { level: "medium", commitsSince };
108
- return { level: "high", commitsSince };
114
+ export function computeDrift(repoDir, configTimestamp) {
115
+ return classifyDrift(getGitRevCount(repoDir, configTimestamp));
109
116
  }
110
117
 
111
118
  export function findExemplar(stack, configuredRepos) {
package/src/constants.mjs CHANGED
@@ -1,7 +1,7 @@
1
1
  import { join } from "path";
2
2
  import { homedir } from "os";
3
3
 
4
- export const VERSION = "0.0.6";
4
+ export const VERSION = "0.0.7";
5
5
  export const REPO_URL = "https://github.com/VirenMohindra/claude-code-dashboard";
6
6
 
7
7
  export const HOME = homedir();
package/src/demo.mjs CHANGED
@@ -1,15 +1,16 @@
1
1
  /**
2
- * Generate realistic fake data for --demo flag.
3
- * Produces a complete data object ready for generateDashboardHtml().
2
+ * Generate realistic raw inputs for --demo flag.
3
+ * Returns the same shape that collectRawInputs() returns in production,
4
+ * so the caller can pass it through buildDashboardData() for pipeline parity.
4
5
  */
5
6
 
6
7
  function daysAgo(n) {
7
8
  return Math.floor(Date.now() / 1000) - n * 86400;
8
9
  }
9
10
 
10
- const DEMO_CONFIGURED = [
11
+ const DEMO_REPOS = [
12
+ // ── Configured repos ────────────────────────────────────────────────────
11
13
  {
12
- key: "acme-web",
13
14
  name: "acme-web",
14
15
  path: "~/work/acme-web",
15
16
  shortPath: "~/work/acme-web",
@@ -28,6 +29,7 @@ const DEMO_CONFIGURED = [
28
29
  },
29
30
  { name: "styling", desc: "Tailwind utility-first, no inline styles", filepath: "" },
30
31
  ],
32
+ agentsFile: "~/work/acme-web/CLAUDE.md",
31
33
  desc: [
32
34
  "Customer-facing web application built with Next.js 15 and React Server Components.",
33
35
  "Uses Supabase for auth and database, deployed on Vercel.",
@@ -51,22 +53,10 @@ const DEMO_CONFIGURED = [
51
53
  },
52
54
  ],
53
55
  freshness: daysAgo(3),
54
- freshnessText: "3 days ago",
55
- freshnessClass: "fresh",
56
+ gitRevCount: 0,
56
57
  techStack: ["next", "react"],
57
- healthScore: 95,
58
- healthReasons: ["Has CLAUDE.md", "Has commands", "Has rules", "Recently updated"],
59
- hasAgentsFile: true,
60
- configPattern: "modular",
61
- drift: { level: "synced", commitsSince: 0 },
62
- similarRepos: [{ name: "marketing-site", similarity: 72 }],
63
- matchedSkills: [{ name: "e2e-test" }, { name: "react-doctor" }],
64
- mcpServers: [
65
- { name: "playwright", type: "stdio", scope: "project", source: "~/work/acme-web" },
66
- ],
67
58
  },
68
59
  {
69
- key: "payments-api",
70
60
  name: "payments-api",
71
61
  path: "~/work/payments-api",
72
62
  shortPath: "~/work/payments-api",
@@ -83,6 +73,7 @@ const DEMO_CONFIGURED = [
83
73
  filepath: "",
84
74
  },
85
75
  ],
76
+ agentsFile: "~/work/payments-api/CLAUDE.md",
86
77
  desc: ["Payment processing API handling Stripe integration and subscription management."],
87
78
  sections: [
88
79
  {
@@ -95,20 +86,10 @@ const DEMO_CONFIGURED = [
95
86
  },
96
87
  ],
97
88
  freshness: daysAgo(12),
98
- freshnessText: "12 days ago",
99
- freshnessClass: "fresh",
89
+ gitRevCount: 4,
100
90
  techStack: ["python"],
101
- healthScore: 80,
102
- healthReasons: ["Has CLAUDE.md", "Has commands", "Has rules"],
103
- hasAgentsFile: true,
104
- configPattern: "modular",
105
- drift: { level: "low", commitsSince: 4 },
106
- similarRepos: [],
107
- matchedSkills: [{ name: "systematic-debugging" }],
108
- mcpServers: [],
109
91
  },
110
92
  {
111
- key: "mobile-app",
112
93
  name: "mobile-app",
113
94
  path: "~/work/mobile-app",
114
95
  shortPath: "~/work/mobile-app",
@@ -120,6 +101,7 @@ const DEMO_CONFIGURED = [
120
101
  rules: [
121
102
  { name: "navigation", desc: "React Navigation v7 patterns and deep linking", filepath: "" },
122
103
  ],
104
+ agentsFile: "~/work/mobile-app/CLAUDE.md",
123
105
  desc: ["Cross-platform mobile app built with Expo and React Native."],
124
106
  sections: [
125
107
  {
@@ -128,20 +110,10 @@ const DEMO_CONFIGURED = [
128
110
  },
129
111
  ],
130
112
  freshness: daysAgo(45),
131
- freshnessText: "1 month ago",
132
- freshnessClass: "aging",
113
+ gitRevCount: 18,
133
114
  techStack: ["expo", "react"],
134
- healthScore: 60,
135
- healthReasons: ["Has CLAUDE.md", "Has commands"],
136
- hasAgentsFile: true,
137
- configPattern: "monolithic",
138
- drift: { level: "medium", commitsSince: 18 },
139
- similarRepos: [{ name: "acme-web", similarity: 45 }],
140
- matchedSkills: [{ name: "react-doctor" }],
141
- mcpServers: [],
142
115
  },
143
116
  {
144
- key: "infra-tools",
145
117
  name: "infra-tools",
146
118
  path: "~/work/infra-tools",
147
119
  shortPath: "~/work/infra-tools",
@@ -150,23 +122,14 @@ const DEMO_CONFIGURED = [
150
122
  { name: "test", desc: "Run go test ./...", filepath: "" },
151
123
  ],
152
124
  rules: [],
125
+ agentsFile: "~/work/infra-tools/CLAUDE.md",
153
126
  desc: ["Internal CLI tools for infrastructure automation."],
154
127
  sections: [{ name: "Build", preview: ["Go 1.22", "Multi-binary workspace layout"] }],
155
128
  freshness: daysAgo(90),
156
- freshnessText: "3 months ago",
157
- freshnessClass: "stale",
129
+ gitRevCount: 34,
158
130
  techStack: ["go"],
159
- healthScore: 40,
160
- healthReasons: ["Has CLAUDE.md", "Has commands"],
161
- hasAgentsFile: true,
162
- configPattern: "minimal",
163
- drift: { level: "high", commitsSince: 34 },
164
- similarRepos: [],
165
- matchedSkills: [],
166
- mcpServers: [],
167
131
  },
168
132
  {
169
- key: "marketing-site",
170
133
  name: "marketing-site",
171
134
  path: "~/work/marketing-site",
172
135
  shortPath: "~/work/marketing-site",
@@ -177,25 +140,16 @@ const DEMO_CONFIGURED = [
177
140
  rules: [
178
141
  { name: "content", desc: "All copy comes from CMS, never hardcode text", filepath: "" },
179
142
  ],
143
+ agentsFile: "~/work/marketing-site/CLAUDE.md",
180
144
  desc: ["Public marketing website with blog and documentation."],
181
145
  sections: [
182
146
  { name: "Content", preview: ["MDX for blog posts", "Contentlayer for type-safe content"] },
183
147
  ],
184
148
  freshness: daysAgo(7),
185
- freshnessText: "1 week ago",
186
- freshnessClass: "fresh",
149
+ gitRevCount: 2,
187
150
  techStack: ["next"],
188
- healthScore: 70,
189
- healthReasons: ["Has CLAUDE.md", "Has commands", "Has rules"],
190
- hasAgentsFile: true,
191
- configPattern: "modular",
192
- drift: { level: "low", commitsSince: 2 },
193
- similarRepos: [{ name: "acme-web", similarity: 68 }],
194
- matchedSkills: [{ name: "e2e-test" }],
195
- mcpServers: [],
196
151
  },
197
152
  {
198
- key: "shared-ui",
199
153
  name: "shared-ui",
200
154
  path: "~/work/shared-ui",
201
155
  shortPath: "~/work/shared-ui",
@@ -206,6 +160,7 @@ const DEMO_CONFIGURED = [
206
160
  rules: [
207
161
  { name: "components", desc: "All components must have stories and a11y tests", filepath: "" },
208
162
  ],
163
+ agentsFile: "~/work/shared-ui/CLAUDE.md",
209
164
  desc: ["Shared component library used across web projects."],
210
165
  sections: [
211
166
  {
@@ -214,50 +169,49 @@ const DEMO_CONFIGURED = [
214
169
  },
215
170
  ],
216
171
  freshness: daysAgo(14),
217
- freshnessText: "2 weeks ago",
218
- freshnessClass: "fresh",
172
+ gitRevCount: 0,
219
173
  techStack: ["react"],
220
- healthScore: 75,
221
- healthReasons: ["Has CLAUDE.md", "Has commands", "Has rules"],
222
- hasAgentsFile: true,
223
- configPattern: "modular",
224
- drift: { level: "synced", commitsSince: 0 },
225
- similarRepos: [{ name: "acme-web", similarity: 55 }],
226
- matchedSkills: [],
227
- mcpServers: [],
228
174
  },
229
- ];
230
175
 
231
- const DEMO_UNCONFIGURED = [
176
+ // ── Unconfigured repos ──────────────────────────────────────────────────
232
177
  {
233
- key: "data-scripts",
234
178
  name: "data-scripts",
235
179
  path: "~/work/data-scripts",
236
180
  shortPath: "~/work/data-scripts",
237
181
  techStack: ["python"],
238
- suggestions: ["Add CLAUDE.md with project overview", "Add commands for common tasks"],
239
- exemplarName: "payments-api",
240
- mcpServers: [],
182
+ agentsFile: null,
183
+ commands: [],
184
+ rules: [],
185
+ desc: [],
186
+ sections: [],
187
+ freshness: 0,
188
+ gitRevCount: null,
241
189
  },
242
190
  {
243
- key: "legacy-admin",
244
191
  name: "legacy-admin",
245
192
  path: "~/work/legacy-admin",
246
193
  shortPath: "~/work/legacy-admin",
247
194
  techStack: ["react"],
248
- suggestions: ["Add CLAUDE.md", "Add architecture rules"],
249
- exemplarName: "acme-web",
250
- mcpServers: [],
195
+ agentsFile: null,
196
+ commands: [],
197
+ rules: [],
198
+ desc: [],
199
+ sections: [],
200
+ freshness: 0,
201
+ gitRevCount: null,
251
202
  },
252
203
  {
253
- key: "ops-runbooks",
254
204
  name: "ops-runbooks",
255
205
  path: "~/work/ops-runbooks",
256
206
  shortPath: "~/work/ops-runbooks",
257
207
  techStack: [],
258
- suggestions: [],
259
- exemplarName: "",
260
- mcpServers: [],
208
+ agentsFile: null,
209
+ commands: [],
210
+ rules: [],
211
+ desc: [],
212
+ sections: [],
213
+ freshness: 0,
214
+ gitRevCount: null,
261
215
  },
262
216
  ];
263
217
 
@@ -341,126 +295,185 @@ const DEMO_GLOBAL_SKILLS = [
341
295
  },
342
296
  ];
343
297
 
298
+ // Simple seeded PRNG for deterministic demo output (mulberry32)
299
+ function seededRng(seed) {
300
+ let s = seed | 0;
301
+ return () => {
302
+ s = (s + 0x6d2b79f5) | 0;
303
+ let t = Math.imul(s ^ (s >>> 15), 1 | s);
304
+ t = (t + Math.imul(t ^ (t >>> 7), 61 | t)) ^ t;
305
+ return ((t ^ (t >>> 14)) >>> 0) / 4294967296;
306
+ };
307
+ }
308
+
344
309
  function generateDemoHeatmap() {
310
+ const rng = seededRng(42);
345
311
  const days = [];
346
312
  const now = new Date();
347
313
  for (let i = 364; i >= 0; i--) {
348
314
  const d = new Date(now);
349
315
  d.setDate(d.getDate() - i);
350
316
  const date = d.toISOString().slice(0, 10);
351
- // Weighted random: more activity on weekdays
317
+ // Weighted deterministic: more activity on weekdays
352
318
  const isWeekday = d.getDay() > 0 && d.getDay() < 6;
353
319
  const base = isWeekday ? 8 : 2;
354
- const messageCount = Math.floor(Math.random() * base * 3);
320
+ const messageCount = Math.floor(rng() * base * 3);
355
321
  if (messageCount > 0) days.push({ date, messageCount });
356
322
  }
357
323
  return days;
358
324
  }
359
325
 
360
326
  function generateDemoHourCounts() {
327
+ const rng = seededRng(99);
361
328
  const counts = {};
362
329
  // Peak at 10am and 2pm
363
330
  for (let h = 0; h < 24; h++) {
364
331
  const peak = Math.exp(-((h - 10) ** 2) / 18) + Math.exp(-((h - 14) ** 2) / 12);
365
- counts[String(h)] = Math.round(peak * 120 + Math.random() * 20);
332
+ counts[String(h)] = Math.round(peak * 120 + rng() * 20);
366
333
  }
367
334
  return counts;
368
335
  }
369
336
 
370
- export function generateDemoData() {
371
- const now = new Date();
372
- const timestamp =
373
- now
374
- .toLocaleDateString("en-US", { month: "short", day: "numeric", year: "numeric" })
375
- .toLowerCase() +
376
- " at " +
377
- now.toLocaleTimeString("en-US", { hour: "numeric", minute: "2-digit" }).toLowerCase();
337
+ // ── MCP raw data ─────────────────────────────────────────────────────────────
378
338
 
379
- const configured = DEMO_CONFIGURED;
380
- const unconfigured = DEMO_UNCONFIGURED;
381
- const totalRepos = configured.length + unconfigured.length;
339
+ function buildDemoMcpData() {
340
+ // User-level MCP servers (global)
341
+ const userMcpServers = [
342
+ { name: "playwright", type: "stdio", scope: "user", source: "~/.claude/mcp_config.json" },
343
+ { name: "sentry", type: "http", scope: "user", source: "~/.claude/mcp_config.json" },
344
+ ];
382
345
 
383
- return {
384
- configured,
385
- unconfigured,
386
- globalCmds: DEMO_GLOBAL_CMDS,
387
- globalRules: DEMO_GLOBAL_RULES,
388
- globalSkills: DEMO_GLOBAL_SKILLS,
389
- chains: [
390
- { nodes: ["shared-ui", "acme-web", "marketing-site"], arrow: "&rarr;" },
391
- { nodes: ["payments-api", "acme-web"], arrow: "&rarr;" },
392
- ],
393
- mcpSummary: [
394
- { name: "playwright", type: "stdio", projects: [], userLevel: true, disabledIn: 0 },
395
- {
396
- name: "github",
397
- type: "stdio",
398
- projects: ["~/work/acme-web", "~/work/payments-api"],
399
- userLevel: false,
400
- disabledIn: 0,
401
- },
402
- {
403
- name: "postgres",
404
- type: "stdio",
405
- projects: ["~/work/payments-api"],
406
- userLevel: false,
407
- disabledIn: 0,
408
- },
409
- { name: "sentry", type: "http", projects: [], userLevel: true, disabledIn: 0 },
410
- {
411
- name: "figma",
412
- type: "stdio",
413
- projects: ["~/work/acme-web"],
414
- userLevel: false,
415
- disabledIn: 0,
416
- recentlyActive: true,
417
- },
346
+ // Project-level MCP servers
347
+ const projectMcpByRepo = {
348
+ "~/work/acme-web": [
349
+ { name: "playwright", type: "stdio", scope: "project", source: "~/work/acme-web" },
350
+ { name: "github", type: "stdio", scope: "project", source: "~/work/acme-web" },
351
+ { name: "figma", type: "stdio", scope: "project", source: "~/work/acme-web" },
418
352
  ],
419
- mcpPromotions: [{ name: "github", projects: ["~/work/acme-web", "~/work/payments-api"] }],
420
- formerMcpServers: [
421
- { name: "redis", projects: ["~/work/cache-service"], lastSeen: null },
422
- { name: "datadog", projects: [], lastSeen: null },
353
+ "~/work/payments-api": [
354
+ { name: "github", type: "stdio", scope: "project", source: "~/work/payments-api" },
355
+ { name: "postgres", type: "stdio", scope: "project", source: "~/work/payments-api" },
423
356
  ],
424
- consolidationGroups: [
357
+ };
358
+
359
+ // Disabled MCP servers
360
+ const disabledMcpByRepo = {};
361
+
362
+ // Historical MCP servers (former — no longer in any config)
363
+ const historicalMcpMap = new Map([
364
+ [
365
+ "redis",
425
366
  {
426
- stack: "next",
427
- repos: ["acme-web", "marketing-site"],
428
- avgSimilarity: 68,
429
- suggestion: "2 next repos with 68% avg similarity — consider shared global rules",
367
+ name: "redis",
368
+ projects: new Set(["~/work/cache-service"]),
369
+ lastSeen: null,
430
370
  },
371
+ ],
372
+ [
373
+ "datadog",
431
374
  {
432
- stack: "react",
433
- repos: ["acme-web", "shared-ui", "mobile-app"],
434
- avgSimilarity: 45,
435
- suggestion: "3 react repos with 45% avg similarity — consider shared global rules",
375
+ name: "datadog",
376
+ projects: new Set(),
377
+ lastSeen: null,
436
378
  },
437
379
  ],
438
- usageAnalytics: {
439
- totalSessions: 247,
440
- topTools: [
441
- { name: "Read", count: 1842 },
442
- { name: "Edit", count: 1356 },
443
- { name: "Bash", count: 987 },
444
- { name: "Grep", count: 654 },
445
- { name: "Write", count: 432 },
446
- { name: "Glob", count: 321 },
447
- { name: "Agent", count: 198 },
448
- { name: "WebSearch", count: 87 },
449
- ],
450
- topLanguages: [
451
- { name: "TypeScript", count: 4521 },
452
- { name: "Python", count: 2103 },
453
- { name: "JavaScript", count: 1456 },
454
- { name: "Go", count: 432 },
455
- { name: "Markdown", count: 321 },
456
- ],
457
- errorCategories: [
458
- { name: "lint_error", count: 45 },
459
- { name: "type_error", count: 32 },
460
- { name: "test_failure", count: 28 },
461
- ],
462
- heavySessions: 12,
463
- },
380
+ ]);
381
+
382
+ return { userMcpServers, projectMcpByRepo, disabledMcpByRepo, historicalMcpMap };
383
+ }
384
+
385
+ // ── Session meta raw data ────────────────────────────────────────────────────
386
+
387
+ function buildDemoSessionMeta() {
388
+ // Generate enough session entries that aggregateSessionMeta produces
389
+ // realistic analytics similar to the old hardcoded usageAnalytics.
390
+ const sessions = [];
391
+ const toolSets = [
392
+ { Read: 12, Edit: 8, Bash: 6, Grep: 4, Write: 3, Glob: 2 },
393
+ { Read: 8, Edit: 6, Bash: 4, Grep: 3, Write: 2, Agent: 1 },
394
+ { Read: 6, Edit: 5, Bash: 3, Grep: 2, Write: 1, WebSearch: 1 },
395
+ { Read: 10, Edit: 7, Bash: 5, Grep: 3, Glob: 2, Agent: 1 },
396
+ { Read: 5, Edit: 4, Bash: 3, Grep: 2, Write: 2 },
397
+ ];
398
+ const langSets = [
399
+ { TypeScript: 25, JavaScript: 8, Markdown: 2 },
400
+ { Python: 15, Markdown: 1 },
401
+ { TypeScript: 18, JavaScript: 6 },
402
+ { Go: 8 },
403
+ { TypeScript: 12, Python: 5, JavaScript: 3 },
404
+ ];
405
+ const errorSets = [
406
+ { lint_error: 1 },
407
+ { type_error: 1 },
408
+ { test_failure: 1 },
409
+ {},
410
+ { lint_error: 1, type_error: 1 },
411
+ ];
412
+
413
+ const rng = seededRng(247);
414
+ for (let i = 0; i < 247; i++) {
415
+ const dayOffset = Math.floor(rng() * 60);
416
+ const date = new Date();
417
+ date.setDate(date.getDate() - dayOffset);
418
+ const hour = 8 + Math.floor(rng() * 10);
419
+ date.setHours(hour, Math.floor(rng() * 60), 0, 0);
420
+
421
+ const variant = i % toolSets.length;
422
+ const userMsgs = 3 + Math.floor(rng() * 15);
423
+ const assistantMsgs = userMsgs + Math.floor(rng() * 5);
424
+ const duration = 5 + Math.floor(rng() * 40);
425
+
426
+ sessions.push({
427
+ start_time: date.toISOString(),
428
+ duration_minutes: duration,
429
+ user_message_count: userMsgs,
430
+ assistant_message_count: assistantMsgs,
431
+ tool_counts: { ...toolSets[variant] },
432
+ languages: { ...langSets[variant] },
433
+ tool_error_categories: { ...errorSets[variant] },
434
+ });
435
+ }
436
+
437
+ return sessions;
438
+ }
439
+
440
+ // ── Insights report HTML ─────────────────────────────────────────────────────
441
+
442
+ const DEMO_INSIGHTS_HTML = `<!DOCTYPE html>
443
+ <html>
444
+ <body>
445
+ <p class="subtitle">1,386 messages across 117 sessions (365 total) | 2026-02-23 to 2026-03-10</p>
446
+ <div class="stat-value">1,386</div><div class="stat-label">Messages</div>
447
+ <div class="stat-value">+33,424/-2,563</div><div class="stat-label">Lines</div>
448
+ <div class="stat-value">632</div><div class="stat-label">Files</div>
449
+ <div class="stat-value">14</div><div class="stat-label">Days</div>
450
+ <div class="stat-value">99</div><div class="stat-label">Msgs/Day</div>
451
+ <div class="glance-section"><strong>What's working:</strong> Full end-to-end shipping workflow — implementation through PR creation to production deployment in single sessions.<a class="see-more" href="#">more</a></div>
452
+ <div class="glance-section"><strong>What's hindering you:</strong> Claude frequently jumps into fixes without checking actual state first, costing correction cycles.<a class="see-more" href="#">more</a></div>
453
+ <div class="glance-section"><strong>Quick wins to try:</strong> Create custom slash commands for repeated workflows like PR reviews and Slack message drafting.<a class="see-more" href="#">more</a></div>
454
+ <div class="friction-title">Wrong Target / Misidentification</div>
455
+ <div class="friction-desc">Claude acts on the wrong file or setting before you catch the mistake.</div>
456
+ <div class="friction-title">Premature Solutions</div>
457
+ <div class="friction-desc">Jumps into fixes without first checking actual state of the codebase.</div>
458
+ </body>
459
+ </html>`;
460
+
461
+ // ── Main export ──────────────────────────────────────────────────────────────
462
+
463
+ export function generateDemoRawInputs() {
464
+ const { userMcpServers, projectMcpByRepo, disabledMcpByRepo, historicalMcpMap } =
465
+ buildDemoMcpData();
466
+
467
+ return {
468
+ repos: [...DEMO_REPOS],
469
+ globalCmds: DEMO_GLOBAL_CMDS,
470
+ globalRules: DEMO_GLOBAL_RULES,
471
+ globalSkills: DEMO_GLOBAL_SKILLS,
472
+ userMcpServers,
473
+ projectMcpByRepo,
474
+ disabledMcpByRepo,
475
+ historicalMcpMap,
476
+ sessionMetaFiles: buildDemoSessionMeta(),
464
477
  ccusageData: {
465
478
  totals: { totalCost: 47.82, totalTokens: 28_450_000 },
466
479
  daily: [],
@@ -473,81 +486,11 @@ export function generateDemoData() {
473
486
  "claude-haiku-4-5": { inputTokens: 2_400_000, outputTokens: 1_050_000 },
474
487
  },
475
488
  },
476
- timestamp,
477
- coveragePct: Math.round((configured.length / totalRepos) * 100),
478
- totalRepos,
479
- configuredCount: configured.length,
480
- unconfiguredCount: unconfigured.length,
481
- totalRepoCmds: configured.reduce((sum, r) => sum + r.commands.length, 0),
482
- avgHealth: Math.round(
483
- configured.reduce((sum, r) => sum + r.healthScore, 0) / configured.length,
484
- ),
485
- driftCount: configured.filter((r) => r.drift.level === "medium" || r.drift.level === "high")
486
- .length,
487
- mcpCount: 5,
488
- scanScope: "~/work (depth 5)",
489
- insights: [
490
- {
491
- type: "warning",
492
- title: "2 repos have high config drift",
493
- detail:
494
- "payments-api (23 commits since config update), acme-web (18 commits since config update)",
495
- action: "Review and update CLAUDE.md in these repos",
496
- },
497
- {
498
- type: "promote",
499
- title: "1 MCP server could be promoted to global",
500
- detail: "github (in 2 projects)",
501
- action: "Add to ~/.claude/mcp_config.json for all projects",
502
- },
503
- {
504
- type: "info",
505
- title: "12 repos unconfigured (52%)",
506
- detail: "Top candidates: mobile-app (expo), admin-portal (next), data-pipeline (python)",
507
- action: "Run claude-code-dashboard init --template <stack> in these repos",
508
- },
509
- {
510
- type: "tip",
511
- title: "Quick wins to improve config health",
512
- detail:
513
- "design-system (65/100): add commands; shared-utils (60/100): add CLAUDE.md description",
514
- action: "Small changes for measurable improvement",
515
- },
489
+ insightsReportHtml: DEMO_INSIGHTS_HTML,
490
+ chains: [
491
+ { nodes: ["shared-ui", "acme-web", "marketing-site"], arrow: "&rarr;" },
492
+ { nodes: ["payments-api", "acme-web"], arrow: "&rarr;" },
516
493
  ],
517
- insightsReport: {
518
- subtitle: "1,386 messages across 117 sessions (365 total) | 2026-02-23 to 2026-03-10",
519
- stats: [
520
- { value: "1,386", label: "Messages" },
521
- { value: "+33,424/-2,563", label: "Lines" },
522
- { value: "632", label: "Files" },
523
- { value: "14", label: "Days" },
524
- { value: "99", label: "Msgs/Day" },
525
- ],
526
- glance: [
527
- {
528
- label: "What's working",
529
- text: "Full end-to-end shipping workflow — implementation through PR creation to production deployment in single sessions.",
530
- },
531
- {
532
- label: "What's hindering you",
533
- text: "Claude frequently jumps into fixes without checking actual state first, costing correction cycles.",
534
- },
535
- {
536
- label: "Quick wins to try",
537
- text: "Create custom slash commands for repeated workflows like PR reviews and Slack message drafting.",
538
- },
539
- ],
540
- friction: [
541
- {
542
- title: "Wrong Target / Misidentification",
543
- desc: "Claude acts on the wrong file or setting before you catch the mistake.",
544
- },
545
- {
546
- title: "Premature Solutions",
547
- desc: "Jumps into fixes without first checking actual state of the codebase.",
548
- },
549
- ],
550
- filePath: "~/.claude/usage-data/report.html",
551
- },
494
+ scanScope: "~/work (depth 5)",
552
495
  };
553
496
  }