copilot-lens 1.0.0 → 1.0.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,6 +1,16 @@
1
1
  # Copilot Lens 👓
2
2
 
3
- A local dashboard to visualize and analyze your GitHub Copilot CLI sessions.
3
+ A local web dashboard to visualize, explore, and analyze your **GitHub Copilot CLI** terminal sessions. See your full conversation history, tool usage patterns, and usage analytics — all without leaving your machine.
4
+
5
+ ## Why Copilot Lens?
6
+
7
+ GitHub Copilot CLI stores session data locally, but there's no built-in way to browse or analyze it. Copilot Lens gives you a clean, interactive dashboard to:
8
+
9
+ - **Review past sessions** — What did you ask? What did Copilot do?
10
+ - **Understand your usage patterns** — Which repos, branches, and tools do you use most?
11
+ - **Track your productivity** — How much active time are you spending with Copilot?
12
+
13
+ Everything runs locally. No data leaves your machine. No cloud. No sign-in.
4
14
 
5
15
  ## Install
6
16
 
@@ -14,14 +24,17 @@ npm install -g copilot-lens
14
24
  # Start the dashboard
15
25
  copilot-lens
16
26
 
17
- # With options
18
- copilot-lens --port 8080 --open
27
+ # Auto-open in browser
28
+ copilot-lens --open
29
+
30
+ # Custom port
31
+ copilot-lens --port 8080
19
32
 
20
33
  # Or use npx (no install needed)
21
- npx copilot-lens
34
+ npx copilot-lens --open
22
35
  ```
23
36
 
24
- ### Options
37
+ ### CLI Options
25
38
 
26
39
  | Flag | Default | Description |
27
40
  |------|---------|-------------|
@@ -31,15 +44,89 @@ npx copilot-lens
31
44
 
32
45
  ## Features
33
46
 
34
- - **Session list** — Browse all your Copilot CLI sessions with search and filtering
35
- - **Session details** — View full conversation history, tool calls, errors, and plans
36
- - **Analytics dashboard** Charts showing session activity, tool usage, top directories, and branch activity
37
- - **Auto-refresh** — Dashboard updates every 5 seconds
38
- - **Local only** — All data stays on your machine
47
+ ### 📋 Session Browser
48
+
49
+ - Browse all your Copilot CLI sessions in a searchable, filterable list
50
+ - **Color-coded by directory** — each project gets a unique accent color
51
+ - **Status detection** — see which sessions are Running, Completed, or Error
52
+ - **Three filter dimensions** — filter by time range, status, and directory
53
+ - Click any session to view full details
54
+
55
+ ### 💬 Conversation View
56
+
57
+ - Chat-style layout with your prompts on the right and Copilot responses on the left
58
+ - View tool calls made during the session
59
+ - See any errors that occurred
60
+ - Read session plans (if created)
61
+
62
+ ### 📊 Analytics Dashboard
63
+
64
+ Eight interactive charts powered by Chart.js, arranged in a 2-column grid:
65
+
66
+ | Chart | Type | What It Shows |
67
+ |-------|------|---------------|
68
+ | **Sessions Per Day** | Bar (compact) | Daily session activity over time |
69
+ | **Activity by Hour of Day** | Bar (compact) | When during the day you use Copilot most |
70
+ | **Tool Usage** | Doughnut | Most-used tools (grep, edit, powershell, etc.) |
71
+ | **Model Usage** | Doughnut | Which AI models you've used (Claude, GPT, etc.) |
72
+ | **Top Working Directories** | Horizontal bar (full-width) | Which project folders you use Copilot in most |
73
+ | **Time Per Branch** | Horizontal bar | Active Copilot time spent on each git branch |
74
+ | **Time Per Repo** | Horizontal bar | Active Copilot time per repository |
75
+ | **MCP Servers Used** | Doughnut | Which MCP servers are configured across sessions |
76
+
77
+ Doughnut chart legends are interactive — click a label to toggle that segment's visibility.
78
+
79
+ #### Glimpse of Analytics Dashboard
80
+
81
+ <img width="1184" height="1011" alt="image" src="https://github.com/user-attachments/assets/136205ca-f206-4fb1-88a7-71640314a9b5" />
82
+
39
83
 
40
- ## Data Source
84
+ ### 🎨 UI Features
41
85
 
42
- Reads session data from `~/.copilot/session-state/` (the default GitHub Copilot CLI session directory).
86
+ - **Dark & Light mode** toggle with one click, preference is saved
87
+ - **Manual refresh** — refresh button to reload data on demand
88
+ - **Responsive layout** — works on any screen size
89
+ - **2-column grid layout** — compact charts with no wasted space
90
+
91
+ ## How It Works
92
+
93
+ Copilot Lens reads session data from `~/.copilot/session-state/`, where GitHub Copilot CLI stores:
94
+
95
+ - **`workspace.yaml`** — Session metadata (directory, git branch, timestamps)
96
+ - **`events.jsonl`** — Full event log (messages, tool calls, errors)
97
+ - **`plan.md`** — Session plans (if created)
98
+
99
+ A local Express server parses these files and serves a static frontend dashboard.
100
+
101
+ ### Duration Calculation
102
+
103
+ Session durations are calculated from **actual event activity**, not wall-clock time. Gaps longer than 5 minutes between events are excluded, so resumed sessions don't show inflated durations.
104
+
105
+ ### Status Detection
106
+
107
+ | Status | How It's Detected |
108
+ |--------|-------------------|
109
+ | **Running** | `session.db` exists and was modified within 10 min, or `events.jsonl` modified within 5 min |
110
+ | **Completed** | Has an `abort` event with "user initiated" reason, or no recent activity |
111
+ | **Error** | Has an `abort` event with a non-user-initiated reason |
112
+
113
+ ## Tech Stack
114
+
115
+ - **Backend**: Node.js + Express + TypeScript
116
+ - **Frontend**: Vanilla HTML/CSS/JavaScript
117
+ - **Charts**: Chart.js
118
+ - **Data**: YAML + JSONL parsing (no database required)
119
+
120
+ ## Development
121
+
122
+ ```bash
123
+ git clone https://github.com/pavanvamsi3/copilot-lens.git
124
+ cd copilot-lens
125
+ npm install
126
+ npm run dev # Start with tsx (no build needed)
127
+ npm run build # Compile TypeScript
128
+ npm start # Run compiled version
129
+ ```
43
130
 
44
131
  ## License
45
132
 
@@ -53,3 +140,4 @@ If you'd like a prettier URL like `http://copilot.lens:3000`, add this to your h
53
140
  - **macOS/Linux**: `echo "127.0.0.1 copilot.lens" | sudo tee -a /etc/hosts`
54
141
 
55
142
  Then run: `copilot-lens --host copilot.lens`
143
+
@@ -33,7 +33,17 @@ export interface AnalyticsData {
33
33
  totalDuration: number;
34
34
  toolUsage: Record<string, number>;
35
35
  topDirectories: Record<string, number>;
36
- branchActivity: Record<string, number>;
36
+ branchTime: Record<string, number>;
37
+ repoTime: Record<string, number>;
38
+ modelUsage: Record<string, number>;
39
+ mcpServers: Record<string, number>;
40
+ toolSuccessRate: Record<string, {
41
+ success: number;
42
+ failure: number;
43
+ }>;
44
+ turnsPerSession: number[];
45
+ hourOfDay: Record<string, number>;
46
+ errorTypes: Record<string, number>;
37
47
  }
38
48
  export declare function listSessions(): SessionMeta[];
39
49
  export declare function getSession(sessionId: string): SessionDetail | null;
package/dist/sessions.js CHANGED
@@ -45,12 +45,19 @@ function getSessionDir() {
45
45
  }
46
46
  function detectStatus(sessionDir, _updatedAt) {
47
47
  try {
48
- // session.db only exists for actively running sessions — strongest signal
49
- if (fs.existsSync(path.join(sessionDir, "session.db")))
50
- return "running";
51
48
  const eventsPath = path.join(sessionDir, "events.jsonl");
52
- if (!fs.existsSync(eventsPath))
49
+ // Check session.db + recent activity (session.db can be stale if not cleaned up)
50
+ const dbPath = path.join(sessionDir, "session.db");
51
+ if (fs.existsSync(dbPath)) {
52
+ const dbAge = Date.now() - fs.statSync(dbPath).mtimeMs;
53
+ if (dbAge < 600000)
54
+ return "running"; // session.db modified within 10 min
55
+ }
56
+ if (!fs.existsSync(eventsPath)) {
57
+ // No events yet — only "running" if session.db exists (checked above)
58
+ // A workspace.yaml alone with no events means the session was never active
53
59
  return "completed";
60
+ }
54
61
  // Only read the last 2KB to check for abort events (avoid reading huge files)
55
62
  const stat = fs.statSync(eventsPath);
56
63
  const readSize = Math.min(stat.size, 2048);
@@ -70,7 +77,7 @@ function detectStatus(sessionDir, _updatedAt) {
70
77
  }
71
78
  catch { }
72
79
  }
73
- // No abort — check if recently modified
80
+ // No abort — check if events.jsonl recently modified
74
81
  const age = Date.now() - stat.mtimeMs;
75
82
  if (age < 300000)
76
83
  return "running";
@@ -165,10 +172,18 @@ function getSession(sessionId) {
165
172
  for (const e of events) {
166
173
  eventCounts[e.type] = (eventCounts[e.type] || 0) + 1;
167
174
  }
168
- // Calculate duration
169
- const created = new Date(ws.created_at).getTime();
170
- const updated = new Date(ws.updated_at).getTime();
171
- const duration = updated - created;
175
+ // Calculate active duration from event timestamps (cap gaps at 5 min)
176
+ let duration = 0;
177
+ const timestamps = events
178
+ .map((e) => new Date(e.timestamp).getTime())
179
+ .filter((t) => t > 0)
180
+ .sort((a, b) => a - b);
181
+ if (timestamps.length >= 2) {
182
+ const MAX_GAP = 300000;
183
+ for (let i = 1; i < timestamps.length; i++) {
184
+ duration += Math.min(timestamps[i] - timestamps[i - 1], MAX_GAP);
185
+ }
186
+ }
172
187
  return {
173
188
  id: ws.id || sessionId,
174
189
  cwd: ws.cwd || "",
@@ -191,7 +206,14 @@ function getAnalytics() {
191
206
  const sessionsPerDay = {};
192
207
  const toolUsage = {};
193
208
  const topDirectories = {};
194
- const branchActivity = {};
209
+ const branchTime = {};
210
+ const repoTime = {};
211
+ const modelUsage = {};
212
+ const mcpServers = {};
213
+ const toolSuccessRate = {};
214
+ const turnsPerSession = [];
215
+ const hourOfDay = {};
216
+ const errorTypes = {};
195
217
  const durations = [];
196
218
  const sessionDir = getSessionDir();
197
219
  for (const s of sessions) {
@@ -199,33 +221,104 @@ function getAnalytics() {
199
221
  const day = s.createdAt.slice(0, 10);
200
222
  if (day)
201
223
  sessionsPerDay[day] = (sessionsPerDay[day] || 0) + 1;
202
- // Duration
203
- const dur = new Date(s.updatedAt).getTime() - new Date(s.createdAt).getTime();
204
- if (dur > 0)
205
- durations.push(dur);
224
+ // Hour of day
225
+ try {
226
+ const hour = new Date(s.createdAt).getHours().toString().padStart(2, "0") + ":00";
227
+ hourOfDay[hour] = (hourOfDay[hour] || 0) + 1;
228
+ }
229
+ catch { }
230
+ // Duration — calculated from actual event activity, not updated_at - created_at
231
+ // (sessions can be resumed days later, inflating the duration)
232
+ // Computed below after scanning events.jsonl
206
233
  // Top directories
207
234
  const dirName = s.cwd || "unknown";
208
235
  topDirectories[dirName] = (topDirectories[dirName] || 0) + 1;
209
236
  // Branch activity
210
237
  const branch = s.branch || "unknown";
211
- branchActivity[branch] = (branchActivity[branch] || 0) + 1;
212
- // Tool usage — scan events.jsonl line by line without loading full detail
238
+ // Scan events.jsonl for all metrics
213
239
  try {
214
240
  const eventsPath = path.join(sessionDir, s.id, "events.jsonl");
215
241
  if (fs.existsSync(eventsPath)) {
216
242
  const content = fs.readFileSync(eventsPath, "utf-8");
243
+ let turnCount = 0;
244
+ const timestamps = [];
217
245
  for (const line of content.split("\n")) {
218
- if (!line.includes("tool.execution_start"))
246
+ if (!line.trim())
219
247
  continue;
220
248
  try {
221
249
  const event = JSON.parse(line);
250
+ // Collect timestamps for active duration
251
+ if (event.timestamp) {
252
+ const t = new Date(event.timestamp).getTime();
253
+ if (t > 0)
254
+ timestamps.push(t);
255
+ }
256
+ // Tool usage
222
257
  if (event.type === "tool.execution_start") {
223
258
  const tool = event.data?.tool || event.data?.toolName || "unknown";
224
259
  toolUsage[tool] = (toolUsage[tool] || 0) + 1;
225
260
  }
261
+ // Tool success rate
262
+ if (event.type === "tool.execution_complete") {
263
+ const tool = event.data?.tool || event.data?.toolName || "unknown";
264
+ if (!toolSuccessRate[tool])
265
+ toolSuccessRate[tool] = { success: 0, failure: 0 };
266
+ if (event.data?.success)
267
+ toolSuccessRate[tool].success++;
268
+ else
269
+ toolSuccessRate[tool].failure++;
270
+ }
271
+ // MCP servers
272
+ if (event.type === "session.info" && event.data?.infoType === "mcp") {
273
+ const msg = event.data?.message || "";
274
+ const match = msg.match(/Configured MCP servers?:\s*(.+)/i);
275
+ if (match) {
276
+ for (const server of match[1].split(",").map((s) => s.trim())) {
277
+ if (server)
278
+ mcpServers[server] = (mcpServers[server] || 0) + 1;
279
+ }
280
+ }
281
+ }
282
+ // Model usage
283
+ if (event.type === "session.info" && event.data?.infoType === "model") {
284
+ const msg = event.data?.message || "";
285
+ const match = msg.match(/Model changed to:\s*([^\s.]+(?:[-.][^\s.]+)*)/i);
286
+ if (match)
287
+ modelUsage[match[1]] = (modelUsage[match[1]] || 0) + 1;
288
+ }
289
+ if (event.type === "session.start" && event.data?.model) {
290
+ const model = event.data.model;
291
+ modelUsage[model] = (modelUsage[model] || 0) + 1;
292
+ }
293
+ // Turn counting
294
+ if (event.type === "assistant.turn_start")
295
+ turnCount++;
296
+ // Errors
297
+ if (event.type === "session.error") {
298
+ const errType = event.data?.errorType || "unknown";
299
+ errorTypes[errType] = (errorTypes[errType] || 0) + 1;
300
+ }
226
301
  }
227
302
  catch { }
228
303
  }
304
+ if (turnCount > 0)
305
+ turnsPerSession.push(turnCount);
306
+ // Calculate active duration: sum gaps between events, cap each gap at 5 min
307
+ if (timestamps.length >= 2) {
308
+ timestamps.sort((a, b) => a - b);
309
+ let activeDur = 0;
310
+ const MAX_GAP = 300000; // 5 minutes
311
+ for (let i = 1; i < timestamps.length; i++) {
312
+ const gap = timestamps[i] - timestamps[i - 1];
313
+ activeDur += Math.min(gap, MAX_GAP);
314
+ }
315
+ if (activeDur > 0) {
316
+ durations.push(activeDur);
317
+ branchTime[branch] = (branchTime[branch] || 0) + activeDur;
318
+ const repo = s.gitRoot || s.cwd || "unknown";
319
+ repoTime[repo] = (repoTime[repo] || 0) + activeDur;
320
+ }
321
+ }
229
322
  }
230
323
  }
231
324
  catch { }
@@ -240,7 +333,14 @@ function getAnalytics() {
240
333
  totalDuration,
241
334
  toolUsage,
242
335
  topDirectories,
243
- branchActivity,
336
+ branchTime,
337
+ repoTime,
338
+ modelUsage,
339
+ mcpServers,
340
+ toolSuccessRate,
341
+ turnsPerSession,
342
+ hourOfDay,
343
+ errorTypes,
244
344
  };
245
345
  }
246
346
  //# sourceMappingURL=sessions.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"sessions.js","sourceRoot":"","sources":["../src/sessions.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"sessions.js","sourceRoot":"","sources":["../src/sessions.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "copilot-lens",
3
- "version": "1.0.0",
3
+ "version": "1.0.2",
4
4
  "description": "A local dashboard to visualize and analyze your GitHub Copilot CLI sessions",
5
5
  "main": "dist/server.js",
6
6
  "bin": {
@@ -12,14 +12,25 @@
12
12
  "start": "node dist/cli.js",
13
13
  "prepublishOnly": "npm run build"
14
14
  },
15
- "keywords": ["copilot", "github", "cli", "dashboard", "sessions", "analytics"],
15
+ "keywords": [
16
+ "copilot",
17
+ "github",
18
+ "cli",
19
+ "dashboard",
20
+ "sessions",
21
+ "analytics"
22
+ ],
16
23
  "author": "pavanvamsi3",
17
24
  "license": "MIT",
18
25
  "repository": {
19
26
  "type": "git",
20
27
  "url": "https://github.com/pavanvamsi3/copilot-lens"
21
28
  },
22
- "files": ["dist/", "public/", "README.md"],
29
+ "files": [
30
+ "dist/",
31
+ "public/",
32
+ "README.md"
33
+ ],
23
34
  "dependencies": {
24
35
  "cors": "^2.8.6",
25
36
  "express": "^5.2.1",
package/public/app.js CHANGED
@@ -39,6 +39,7 @@ const detailModal = document.getElementById("detailModal");
39
39
  const detailContent = document.getElementById("detailContent");
40
40
  const modalClose = document.getElementById("modalClose");
41
41
  const refreshBtn = document.getElementById("refreshBtn");
42
+ const statsCards = document.getElementById("statsCards");
42
43
 
43
44
  // Navigation
44
45
  document.querySelectorAll(".nav-btn").forEach((btn) => {
@@ -181,7 +182,7 @@ function renderDetail(s) {
181
182
 
182
183
  // Interleave conversation messages in order
183
184
  const conversation = s.events
184
- .filter((e) => e.type === "user.message" || e.type === "assistant.message")
185
+ .filter((e) => (e.type === "user.message" || e.type === "assistant.message") && (e.data?.content || "").trim())
185
186
  .map((e) => {
186
187
  const isUser = e.type === "user.message";
187
188
  const content = e.data?.content || "";
@@ -222,7 +223,9 @@ function renderDetail(s) {
222
223
  </div>
223
224
 
224
225
  <div class="detail-panel active" id="panel-conversation">
226
+ <div class="conversation-list">
225
227
  ${conversation || '<div style="color:var(--text-dim)">No messages in this session</div>'}
228
+ </div>
226
229
  </div>
227
230
 
228
231
  <div class="detail-panel" id="panel-tools">
@@ -299,6 +302,9 @@ function renderCharts() {
299
302
  charts = {};
300
303
 
301
304
  const chartColors = ["#58a6ff", "#3fb950", "#d29922", "#f85149", "#bc8cff", "#f0883e", "#56d4dd", "#db61a2"];
305
+ const isLight = document.documentElement.getAttribute("data-theme") === "light";
306
+ const tickColor = isLight ? "#656d76" : "#8b949e";
307
+ const legendColor = isLight ? "#1f2328" : "#e6edf3";
302
308
 
303
309
  // Sessions per day
304
310
  const days = Object.keys(analytics.sessionsPerDay).sort();
@@ -308,7 +314,7 @@ function renderCharts() {
308
314
  labels: days.map((d) => d.slice(5)), // MM-DD
309
315
  datasets: [{ label: "Sessions", data: days.map((d) => analytics.sessionsPerDay[d]), backgroundColor: "#58a6ff88", borderColor: "#58a6ff", borderWidth: 1 }],
310
316
  },
311
- options: { responsive: true, plugins: { legend: { display: false } }, scales: { y: { beginAtZero: true, ticks: { color: "#8b949e" } }, x: { ticks: { color: "#8b949e" } } } },
317
+ options: { responsive: true, maintainAspectRatio: false, plugins: { legend: { display: false } }, scales: { y: { beginAtZero: true, ticks: { color: tickColor } }, x: { ticks: { color: tickColor } } } },
312
318
  });
313
319
 
314
320
  // Tool usage (top 10)
@@ -322,7 +328,7 @@ function renderCharts() {
322
328
  labels: tools.map((t) => t[0]),
323
329
  datasets: [{ data: tools.map((t) => t[1]), backgroundColor: chartColors }],
324
330
  },
325
- options: { responsive: true, plugins: { legend: { position: "right", labels: { color: "#e6edf3", font: { size: 11 } } } } },
331
+ options: { responsive: true, plugins: { legend: { position: "bottom", labels: { color: legendColor, font: { size: 13 }, padding: 14, boxWidth: 14 } } } },
326
332
  });
327
333
  }
328
334
 
@@ -337,12 +343,12 @@ function renderCharts() {
337
343
  labels: dirs.map((d) => shortDir(d[0])),
338
344
  datasets: [{ label: "Sessions", data: dirs.map((d) => d[1]), backgroundColor: "#3fb95088", borderColor: "#3fb950", borderWidth: 1 }],
339
345
  },
340
- options: { indexAxis: "y", responsive: true, plugins: { legend: { display: false } }, scales: { x: { beginAtZero: true, ticks: { color: "#8b949e" } }, y: { ticks: { color: "#8b949e", font: { size: 11 } } } } },
346
+ options: { indexAxis: "y", responsive: true, plugins: { legend: { display: false } }, scales: { x: { beginAtZero: true, ticks: { color: tickColor } }, y: { ticks: { color: tickColor, font: { size: 12 } } } } },
341
347
  });
342
348
  }
343
349
 
344
- // Branch activity (top 8)
345
- const branches = Object.entries(analytics.branchActivity)
350
+ // Branch time (top 8)
351
+ const branches = Object.entries(analytics.branchTime || {})
346
352
  .sort((a, b) => b[1] - a[1])
347
353
  .slice(0, 8);
348
354
  if (branches.length) {
@@ -350,11 +356,68 @@ function renderCharts() {
350
356
  type: "bar",
351
357
  data: {
352
358
  labels: branches.map((b) => b[0]),
353
- datasets: [{ label: "Sessions", data: branches.map((b) => b[1]), backgroundColor: "#d2992288", borderColor: "#d29922", borderWidth: 1 }],
359
+ datasets: [{ label: "Time", data: branches.map((b) => Math.round(b[1] / 60000)), backgroundColor: "#d2992288", borderColor: "#d29922", borderWidth: 1 }],
360
+ },
361
+ options: { indexAxis: "y", responsive: true, plugins: { legend: { display: false }, tooltip: { callbacks: { label: (ctx) => formatDuration(ctx.raw * 60000) } } }, scales: { x: { beginAtZero: true, title: { display: true, text: "minutes", color: tickColor }, ticks: { color: tickColor } }, y: { ticks: { color: tickColor, font: { size: 12 } } } } },
362
+ });
363
+ }
364
+
365
+ // Time per repo (top 8)
366
+ const repos = Object.entries(analytics.repoTime || {})
367
+ .sort((a, b) => b[1] - a[1])
368
+ .slice(0, 8);
369
+ if (repos.length) {
370
+ charts.repoTime = new Chart(document.getElementById("repoTimeChart"), {
371
+ type: "bar",
372
+ data: {
373
+ labels: repos.map((r) => shortDir(r[0])),
374
+ datasets: [{ label: "Time", data: repos.map((r) => Math.round(r[1] / 60000)), backgroundColor: "#3fb95088", borderColor: "#3fb950", borderWidth: 1 }],
375
+ },
376
+ options: { indexAxis: "y", responsive: true, plugins: { legend: { display: false }, tooltip: { callbacks: { label: (ctx) => formatDuration(ctx.raw * 60000) } } }, scales: { x: { beginAtZero: true, title: { display: true, text: "minutes", color: tickColor }, ticks: { color: tickColor } }, y: { ticks: { color: tickColor, font: { size: 12 } } } } },
377
+ });
378
+ }
379
+
380
+ // MCP Servers
381
+ const mcpEntries = Object.entries(analytics.mcpServers || {}).sort((a, b) => b[1] - a[1]);
382
+ if (mcpEntries.length) {
383
+ charts.mcp = new Chart(document.getElementById("mcpChart"), {
384
+ type: "doughnut",
385
+ data: {
386
+ labels: mcpEntries.map((m) => m[0]),
387
+ datasets: [{ data: mcpEntries.map((m) => m[1]), backgroundColor: chartColors }],
354
388
  },
355
- options: { indexAxis: "y", responsive: true, plugins: { legend: { display: false } }, scales: { x: { beginAtZero: true, ticks: { color: "#8b949e" } }, y: { ticks: { color: "#8b949e", font: { size: 11 } } } } },
389
+ options: { responsive: true, plugins: { legend: { position: "bottom", labels: { color: legendColor, font: { size: 13 }, padding: 14, boxWidth: 14 } } } },
356
390
  });
391
+ } else {
392
+ document.getElementById("mcpChart").parentElement.innerHTML = '<h3>MCP Servers Used</h3><div style="color:var(--text-dim);padding:40px;text-align:center">No MCP servers detected</div>';
357
393
  }
394
+
395
+ // Model Usage
396
+ const models = Object.entries(analytics.modelUsage || {}).sort((a, b) => b[1] - a[1]);
397
+ if (models.length) {
398
+ charts.model = new Chart(document.getElementById("modelChart"), {
399
+ type: "doughnut",
400
+ data: {
401
+ labels: models.map((m) => m[0]),
402
+ datasets: [{ data: models.map((m) => m[1]), backgroundColor: chartColors }],
403
+ },
404
+ options: { responsive: true, plugins: { legend: { position: "bottom", labels: { color: legendColor, font: { size: 13 }, padding: 14, boxWidth: 14 } } } },
405
+ });
406
+ } else {
407
+ document.getElementById("modelChart").parentElement.innerHTML = '<h3>Model Usage</h3><div style="color:var(--text-dim);padding:40px;text-align:center">No model data detected</div>';
408
+ }
409
+
410
+ // Activity by Hour of Day
411
+ const hours = analytics.hourOfDay || {};
412
+ const allHours = Array.from({ length: 24 }, (_, i) => i.toString().padStart(2, "0") + ":00");
413
+ charts.hour = new Chart(document.getElementById("hourChart"), {
414
+ type: "bar",
415
+ data: {
416
+ labels: allHours.map((h) => h.slice(0, 2)),
417
+ datasets: [{ label: "Sessions", data: allHours.map((h) => hours[h] || 0), backgroundColor: "#56d4dd88", borderColor: "#56d4dd", borderWidth: 1 }],
418
+ },
419
+ options: { responsive: true, maintainAspectRatio: false, plugins: { legend: { display: false } }, scales: { y: { beginAtZero: true, ticks: { color: tickColor } }, x: { ticks: { color: tickColor } } } },
420
+ });
358
421
  }
359
422
 
360
423
  // Data loading
@@ -394,5 +457,24 @@ refreshBtn.addEventListener("click", () => {
394
457
  setTimeout(() => refreshBtn.classList.remove("spinning"), 600);
395
458
  });
396
459
 
460
+ // Theme toggle
461
+ const themeToggle = document.getElementById("themeToggle");
462
+ const savedTheme = localStorage.getItem("copilot-lens-theme");
463
+ if (savedTheme === "light") document.documentElement.setAttribute("data-theme", "light");
464
+ themeToggle.textContent = document.documentElement.getAttribute("data-theme") === "light" ? "🌙" : "☀️";
465
+
466
+ themeToggle.addEventListener("click", () => {
467
+ const isLight = document.documentElement.getAttribute("data-theme") === "light";
468
+ if (isLight) {
469
+ document.documentElement.removeAttribute("data-theme");
470
+ localStorage.setItem("copilot-lens-theme", "dark");
471
+ themeToggle.textContent = "☀️";
472
+ } else {
473
+ document.documentElement.setAttribute("data-theme", "light");
474
+ localStorage.setItem("copilot-lens-theme", "light");
475
+ themeToggle.textContent = "🌙";
476
+ }
477
+ });
478
+
397
479
  // Init
398
480
  loadSessions();
package/public/index.html CHANGED
@@ -15,6 +15,7 @@
15
15
  <button class="nav-btn" data-page="analytics">Analytics</button>
16
16
  </nav>
17
17
  <button class="refresh-btn" id="refreshBtn" title="Refresh data">⟳ Refresh</button>
18
+ <button class="refresh-btn" id="themeToggle" title="Toggle light/dark mode">☀️</button>
18
19
  </header>
19
20
 
20
21
  <main>
@@ -46,22 +47,38 @@
46
47
  <section id="analyticsPage" class="page">
47
48
  <div class="stats-cards" id="statsCards"></div>
48
49
  <div class="charts-grid">
49
- <div class="chart-card">
50
+ <div class="chart-card compact">
50
51
  <h3>Sessions Per Day</h3>
51
52
  <canvas id="sessionsPerDayChart"></canvas>
52
53
  </div>
54
+ <div class="chart-card compact">
55
+ <h3>Activity by Hour of Day</h3>
56
+ <canvas id="hourChart"></canvas>
57
+ </div>
53
58
  <div class="chart-card">
54
59
  <h3>Tool Usage</h3>
55
60
  <canvas id="toolUsageChart"></canvas>
56
61
  </div>
57
62
  <div class="chart-card">
63
+ <h3>Model Usage</h3>
64
+ <canvas id="modelChart"></canvas>
65
+ </div>
66
+ <div class="chart-card full-width">
58
67
  <h3>Top Working Directories</h3>
59
68
  <canvas id="topDirsChart"></canvas>
60
69
  </div>
61
70
  <div class="chart-card">
62
- <h3>Branch Activity</h3>
71
+ <h3>Time Per Branch</h3>
63
72
  <canvas id="branchChart"></canvas>
64
73
  </div>
74
+ <div class="chart-card">
75
+ <h3>Time Per Repo</h3>
76
+ <canvas id="repoTimeChart"></canvas>
77
+ </div>
78
+ <div class="chart-card">
79
+ <h3>MCP Servers Used</h3>
80
+ <canvas id="mcpChart"></canvas>
81
+ </div>
65
82
  </div>
66
83
  </section>
67
84
 
package/public/style.css CHANGED
@@ -14,6 +14,19 @@
14
14
  --radius: 8px;
15
15
  }
16
16
 
17
+ [data-theme="light"] {
18
+ --bg: #ffffff;
19
+ --surface: #f6f8fa;
20
+ --surface2: #eaeef2;
21
+ --border: #d0d7de;
22
+ --text: #1f2328;
23
+ --text-dim: #656d76;
24
+ --accent: #0969da;
25
+ --accent2: #1a7f37;
26
+ --danger: #cf222e;
27
+ --warning: #9a6700;
28
+ }
29
+
17
30
  body {
18
31
  font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
19
32
  background: var(--bg);
@@ -153,10 +166,10 @@ main { max-width: 1200px; margin: 0 auto; padding: 24px; }
153
166
  font-weight: 600;
154
167
  }
155
168
 
156
- .badge-branch { background: #1f3a5f; color: var(--accent); }
157
- .badge-running { background: #1a3a1a; color: var(--accent2); }
158
- .badge-completed { background: #2a2a2a; color: var(--text-dim); }
159
- .badge-error { background: #3a1a1a; color: var(--danger); }
169
+ .badge-branch { background: color-mix(in srgb, var(--accent) 15%, var(--surface)); color: var(--accent); }
170
+ .badge-running { background: color-mix(in srgb, var(--accent2) 15%, var(--surface)); color: var(--accent2); }
171
+ .badge-completed { background: var(--surface2); color: var(--text-dim); }
172
+ .badge-error { background: color-mix(in srgb, var(--danger) 15%, var(--surface)); color: var(--danger); }
160
173
 
161
174
  /* Stats Cards */
162
175
  .stats-cards {
@@ -189,7 +202,7 @@ main { max-width: 1200px; margin: 0 auto; padding: 24px; }
189
202
  /* Charts */
190
203
  .charts-grid {
191
204
  display: grid;
192
- grid-template-columns: repeat(auto-fit, minmax(500px, 1fr));
205
+ grid-template-columns: repeat(2, 1fr);
193
206
  gap: 16px;
194
207
  }
195
208
 
@@ -198,6 +211,29 @@ main { max-width: 1200px; margin: 0 auto; padding: 24px; }
198
211
  border: 1px solid var(--border);
199
212
  border-radius: var(--radius);
200
213
  padding: 20px;
214
+ min-height: 280px;
215
+ display: flex;
216
+ flex-direction: column;
217
+ }
218
+
219
+ .chart-card canvas {
220
+ flex: 1;
221
+ }
222
+
223
+ .chart-card.compact {
224
+ min-height: 200px;
225
+ }
226
+
227
+ .chart-card.compact canvas {
228
+ max-height: 150px;
229
+ }
230
+
231
+ .chart-card.full-width {
232
+ grid-column: 1 / -1;
233
+ }
234
+
235
+ @media (max-width: 768px) {
236
+ .charts-grid { grid-template-columns: 1fr; }
201
237
  }
202
238
 
203
239
  .chart-card h3 {
@@ -293,30 +329,40 @@ main { max-width: 1200px; margin: 0 auto; padding: 24px; }
293
329
  .detail-panel.active { display: block; }
294
330
 
295
331
  /* Conversation */
332
+ .conversation-list {
333
+ display: flex;
334
+ flex-direction: column;
335
+ gap: 16px;
336
+ padding: 8px 0;
337
+ }
338
+
296
339
  .message {
297
- margin-bottom: 12px;
298
- padding: 12px;
299
- border-radius: var(--radius);
340
+ max-width: 75%;
341
+ padding: 12px 16px;
342
+ border-radius: 12px;
300
343
  font-size: 0.85rem;
301
- line-height: 1.5;
344
+ line-height: 1.6;
302
345
  white-space: pre-wrap;
303
346
  word-break: break-word;
304
347
  }
305
348
 
306
349
  .message-user {
307
- background: #1a2332;
308
- border-left: 3px solid var(--accent);
350
+ align-self: flex-end;
351
+ background: color-mix(in srgb, var(--accent) 15%, var(--surface));
352
+ border-bottom-right-radius: 4px;
309
353
  }
310
354
 
311
355
  .message-assistant {
312
- background: #1a2b1a;
313
- border-left: 3px solid var(--accent2);
356
+ align-self: flex-start;
357
+ background: var(--surface2);
358
+ border-bottom-left-radius: 4px;
314
359
  }
315
360
 
316
361
  .message-label {
317
- font-size: 0.75rem;
362
+ font-size: 0.7rem;
318
363
  font-weight: 700;
319
364
  text-transform: uppercase;
365
+ letter-spacing: 0.5px;
320
366
  margin-bottom: 6px;
321
367
  color: var(--text-dim);
322
368
  }