@viren/claude-code-dashboard 0.0.5 → 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/README.md +12 -0
- package/generate-dashboard.mjs +248 -625
- package/package.json +1 -1
- package/src/analysis.mjs +19 -12
- package/src/assembler.mjs +8 -4
- package/src/constants.mjs +1 -1
- package/src/demo.mjs +191 -248
- package/src/helpers.mjs +21 -0
- package/src/pipeline.mjs +500 -0
- package/src/sections.mjs +7 -3
- package/template/dashboard.css +73 -4
- package/template/dashboard.js +46 -0
package/src/demo.mjs
CHANGED
|
@@ -1,15 +1,16 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Generate realistic
|
|
3
|
-
*
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
239
|
-
|
|
240
|
-
|
|
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
|
-
|
|
249
|
-
|
|
250
|
-
|
|
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
|
-
|
|
259
|
-
|
|
260
|
-
|
|
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
|
|
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(
|
|
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 +
|
|
332
|
+
counts[String(h)] = Math.round(peak * 120 + rng() * 20);
|
|
366
333
|
}
|
|
367
334
|
return counts;
|
|
368
335
|
}
|
|
369
336
|
|
|
370
|
-
|
|
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
|
-
|
|
380
|
-
|
|
381
|
-
const
|
|
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
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
chains: [
|
|
390
|
-
{ nodes: ["shared-ui", "acme-web", "marketing-site"], arrow: "→" },
|
|
391
|
-
{ nodes: ["payments-api", "acme-web"], arrow: "→" },
|
|
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
|
-
|
|
420
|
-
|
|
421
|
-
{ name: "
|
|
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
|
-
|
|
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
|
-
|
|
427
|
-
|
|
428
|
-
|
|
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
|
-
|
|
433
|
-
|
|
434
|
-
|
|
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
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
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
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
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: "→" },
|
|
492
|
+
{ nodes: ["payments-api", "acme-web"], arrow: "→" },
|
|
516
493
|
],
|
|
517
|
-
|
|
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
|
}
|
package/src/helpers.mjs
CHANGED
|
@@ -33,6 +33,27 @@ export function gitCmd(repoDir, ...args) {
|
|
|
33
33
|
}
|
|
34
34
|
}
|
|
35
35
|
|
|
36
|
+
const INSIGHT_ICONS = {
|
|
37
|
+
warning: "\u26A0\uFE0F",
|
|
38
|
+
tip: "\u2728",
|
|
39
|
+
promote: "\u2B06",
|
|
40
|
+
info: "\u2139\uFE0F",
|
|
41
|
+
};
|
|
42
|
+
|
|
43
|
+
/** Convert an insights array to a markdown string suitable for pasting into Claude Code. */
|
|
44
|
+
export function insightsToMarkdown(insights) {
|
|
45
|
+
if (!insights || !insights.length) return "";
|
|
46
|
+
const lines = ["# Dashboard Insights\n"];
|
|
47
|
+
for (const i of insights) {
|
|
48
|
+
const icon = INSIGHT_ICONS[i.type] || INSIGHT_ICONS.info;
|
|
49
|
+
lines.push(`## ${icon} ${i.title}`);
|
|
50
|
+
if (i.detail) lines.push(i.detail);
|
|
51
|
+
if (i.action) lines.push(`**Action:** ${i.action}`);
|
|
52
|
+
lines.push("");
|
|
53
|
+
}
|
|
54
|
+
return lines.join("\n");
|
|
55
|
+
}
|
|
56
|
+
|
|
36
57
|
export function anonymizePath(p) {
|
|
37
58
|
return p
|
|
38
59
|
.replace(/^\/Users\/[^/]+\//, "~/")
|