copilot-lens 1.0.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.
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 pavanvamsi3
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,55 @@
1
+ # Copilot Lens 👓
2
+
3
+ A local dashboard to visualize and analyze your GitHub Copilot CLI sessions.
4
+
5
+ ## Install
6
+
7
+ ```bash
8
+ npm install -g copilot-lens
9
+ ```
10
+
11
+ ## Usage
12
+
13
+ ```bash
14
+ # Start the dashboard
15
+ copilot-lens
16
+
17
+ # With options
18
+ copilot-lens --port 8080 --open
19
+
20
+ # Or use npx (no install needed)
21
+ npx copilot-lens
22
+ ```
23
+
24
+ ### Options
25
+
26
+ | Flag | Default | Description |
27
+ |------|---------|-------------|
28
+ | `--port` | `3000` | Port number |
29
+ | `--host` | `localhost` | Host address |
30
+ | `--open` | off | Auto-open browser |
31
+
32
+ ## Features
33
+
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
39
+
40
+ ## Data Source
41
+
42
+ Reads session data from `~/.copilot/session-state/` (the default GitHub Copilot CLI session directory).
43
+
44
+ ## License
45
+
46
+ MIT
47
+
48
+ ## Optional: Custom Local Hostname
49
+
50
+ If you'd like a prettier URL like `http://copilot.lens:3000`, add this to your hosts file:
51
+
52
+ - **Windows** (run as Admin): `echo 127.0.0.1 copilot.lens >> C:\Windows\System32\drivers\etc\hosts`
53
+ - **macOS/Linux**: `echo "127.0.0.1 copilot.lens" | sudo tee -a /etc/hosts`
54
+
55
+ Then run: `copilot-lens --host copilot.lens`
package/dist/cli.d.ts ADDED
@@ -0,0 +1,2 @@
1
+ #!/usr/bin/env node
2
+ export {};
package/dist/cli.js ADDED
@@ -0,0 +1,66 @@
1
+ #!/usr/bin/env node
2
+ "use strict";
3
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
4
+ if (k2 === undefined) k2 = k;
5
+ var desc = Object.getOwnPropertyDescriptor(m, k);
6
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
7
+ desc = { enumerable: true, get: function() { return m[k]; } };
8
+ }
9
+ Object.defineProperty(o, k2, desc);
10
+ }) : (function(o, m, k, k2) {
11
+ if (k2 === undefined) k2 = k;
12
+ o[k2] = m[k];
13
+ }));
14
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
15
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
16
+ }) : function(o, v) {
17
+ o["default"] = v;
18
+ });
19
+ var __importStar = (this && this.__importStar) || (function () {
20
+ var ownKeys = function(o) {
21
+ ownKeys = Object.getOwnPropertyNames || function (o) {
22
+ var ar = [];
23
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
24
+ return ar;
25
+ };
26
+ return ownKeys(o);
27
+ };
28
+ return function (mod) {
29
+ if (mod && mod.__esModule) return mod;
30
+ var result = {};
31
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
32
+ __setModuleDefault(result, mod);
33
+ return result;
34
+ };
35
+ })();
36
+ Object.defineProperty(exports, "__esModule", { value: true });
37
+ const server_1 = require("./server");
38
+ process.on("uncaughtException", (err) => {
39
+ console.error("Uncaught error:", err.message);
40
+ });
41
+ process.on("unhandledRejection", (err) => {
42
+ console.error("Unhandled rejection:", err?.message || err);
43
+ });
44
+ const args = process.argv.slice(2);
45
+ function getArg(name, fallback) {
46
+ const idx = args.indexOf(name);
47
+ return idx !== -1 && args[idx + 1] ? args[idx + 1] : fallback;
48
+ }
49
+ const port = parseInt(getArg("--port", "3000"), 10);
50
+ const host = getArg("--host", "localhost");
51
+ const shouldOpen = args.includes("--open");
52
+ const app = (0, server_1.createApp)();
53
+ app.listen(port, host, async () => {
54
+ const url = `http://${host}:${port}`;
55
+ console.log(`\n 👓 Copilot Lens is running at ${url}\n`);
56
+ if (shouldOpen) {
57
+ const { exec } = await Promise.resolve().then(() => __importStar(require("child_process")));
58
+ const cmd = process.platform === "win32"
59
+ ? `start "" "${url}"`
60
+ : process.platform === "darwin"
61
+ ? `open ${url}`
62
+ : `xdg-open ${url}`;
63
+ exec(cmd);
64
+ }
65
+ });
66
+ //# sourceMappingURL=cli.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAEA,qCAAqC;AAErC,OAAO,CAAC,EAAE,CAAC,mBAAmB,EAAE,CAAC,GAAG,EAAE,EAAE;IACtC,OAAO,CAAC,KAAK,CAAC,iBAAiB,EAAE,GAAG,CAAC,OAAO,CAAC,CAAC;AAChD,CAAC,CAAC,CAAC;AACH,OAAO,CAAC,EAAE,CAAC,oBAAoB,EAAE,CAAC,GAAQ,EAAE,EAAE;IAC5C,OAAO,CAAC,KAAK,CAAC,sBAAsB,EAAE,GAAG,EAAE,OAAO,IAAI,GAAG,CAAC,CAAC;AAC7D,CAAC,CAAC,CAAC;AAEH,MAAM,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;AAEnC,SAAS,MAAM,CAAC,IAAY,EAAE,QAAgB;IAC5C,MAAM,GAAG,GAAG,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC;IAC/B,OAAO,GAAG,KAAK,CAAC,CAAC,IAAI,IAAI,CAAC,GAAG,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC;AAChE,CAAC;AAED,MAAM,IAAI,GAAG,QAAQ,CAAC,MAAM,CAAC,QAAQ,EAAE,MAAM,CAAC,EAAE,EAAE,CAAC,CAAC;AACpD,MAAM,IAAI,GAAG,MAAM,CAAC,QAAQ,EAAE,WAAW,CAAC,CAAC;AAC3C,MAAM,UAAU,GAAG,IAAI,CAAC,QAAQ,CAAC,QAAQ,CAAC,CAAC;AAE3C,MAAM,GAAG,GAAG,IAAA,kBAAS,GAAE,CAAC;AAExB,GAAG,CAAC,MAAM,CAAC,IAAI,EAAE,IAAI,EAAE,KAAK,IAAI,EAAE;IAChC,MAAM,GAAG,GAAG,UAAU,IAAI,IAAI,IAAI,EAAE,CAAC;IACrC,OAAO,CAAC,GAAG,CAAC,qCAAqC,GAAG,IAAI,CAAC,CAAC;IAE1D,IAAI,UAAU,EAAE,CAAC;QACf,MAAM,EAAE,IAAI,EAAE,GAAG,wDAAa,eAAe,GAAC,CAAC;QAC/C,MAAM,GAAG,GACP,OAAO,CAAC,QAAQ,KAAK,OAAO;YAC1B,CAAC,CAAC,aAAa,GAAG,GAAG;YACrB,CAAC,CAAC,OAAO,CAAC,QAAQ,KAAK,QAAQ;gBAC7B,CAAC,CAAC,QAAQ,GAAG,EAAE;gBACf,CAAC,CAAC,YAAY,GAAG,EAAE,CAAC;QAC1B,IAAI,CAAC,GAAG,CAAC,CAAC;IACZ,CAAC;AACH,CAAC,CAAC,CAAC"}
@@ -0,0 +1 @@
1
+ export declare function createApp(): import("express-serve-static-core").Express;
package/dist/server.js ADDED
@@ -0,0 +1,61 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.createApp = createApp;
7
+ const express_1 = __importDefault(require("express"));
8
+ const cors_1 = __importDefault(require("cors"));
9
+ const path_1 = __importDefault(require("path"));
10
+ const sessions_1 = require("./sessions");
11
+ function createApp() {
12
+ const app = (0, express_1.default)();
13
+ app.use((0, cors_1.default)());
14
+ // Serve static frontend files
15
+ app.use(express_1.default.static(path_1.default.join(__dirname, "..", "public")));
16
+ // API: List all sessions
17
+ app.get("/api/sessions", (_req, res) => {
18
+ try {
19
+ const sessions = (0, sessions_1.listSessions)();
20
+ res.json(sessions);
21
+ }
22
+ catch (err) {
23
+ res.status(500).json({ error: err.message });
24
+ }
25
+ });
26
+ // API: Get session detail
27
+ app.get("/api/sessions/:id", (req, res) => {
28
+ try {
29
+ const session = (0, sessions_1.getSession)(req.params.id);
30
+ if (!session) {
31
+ res.status(404).json({ error: "Session not found" });
32
+ return;
33
+ }
34
+ res.json(session);
35
+ }
36
+ catch (err) {
37
+ res.status(500).json({ error: err.message });
38
+ }
39
+ });
40
+ // API: Analytics
41
+ app.get("/api/analytics", (_req, res) => {
42
+ try {
43
+ const analytics = (0, sessions_1.getAnalytics)();
44
+ res.json(analytics);
45
+ }
46
+ catch (err) {
47
+ res.status(500).json({ error: err.message });
48
+ }
49
+ });
50
+ // SPA fallback — only for non-API routes
51
+ app.use((_req, res) => {
52
+ res.sendFile(path_1.default.join(__dirname, "..", "public", "index.html"));
53
+ });
54
+ // Global error handler
55
+ app.use((err, _req, res, _next) => {
56
+ console.error("Server error:", err.message);
57
+ res.status(500).json({ error: err.message });
58
+ });
59
+ return app;
60
+ }
61
+ //# sourceMappingURL=server.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"server.js","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":";;;;;AAKA,8BAqDC;AA1DD,sDAA8B;AAC9B,gDAAwB;AACxB,gDAAwB;AACxB,yCAAoE;AAEpE,SAAgB,SAAS;IACvB,MAAM,GAAG,GAAG,IAAA,iBAAO,GAAE,CAAC;IACtB,GAAG,CAAC,GAAG,CAAC,IAAA,cAAI,GAAE,CAAC,CAAC;IAEhB,8BAA8B;IAC9B,GAAG,CAAC,GAAG,CAAC,iBAAO,CAAC,MAAM,CAAC,cAAI,CAAC,IAAI,CAAC,SAAS,EAAE,IAAI,EAAE,QAAQ,CAAC,CAAC,CAAC,CAAC;IAE9D,yBAAyB;IACzB,GAAG,CAAC,GAAG,CAAC,eAAe,EAAE,CAAC,IAAI,EAAE,GAAG,EAAE,EAAE;QACrC,IAAI,CAAC;YACH,MAAM,QAAQ,GAAG,IAAA,uBAAY,GAAE,CAAC;YAChC,GAAG,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;QACrB,CAAC;QAAC,OAAO,GAAQ,EAAE,CAAC;YAClB,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,GAAG,CAAC,OAAO,EAAE,CAAC,CAAC;QAC/C,CAAC;IACH,CAAC,CAAC,CAAC;IAEH,0BAA0B;IAC1B,GAAG,CAAC,GAAG,CAAC,mBAAmB,EAAE,CAAC,GAAG,EAAE,GAAG,EAAE,EAAE;QACxC,IAAI,CAAC;YACH,MAAM,OAAO,GAAG,IAAA,qBAAU,EAAC,GAAG,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC;YAC1C,IAAI,CAAC,OAAO,EAAE,CAAC;gBACb,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,mBAAmB,EAAE,CAAC,CAAC;gBACrD,OAAO;YACT,CAAC;YACD,GAAG,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;QACpB,CAAC;QAAC,OAAO,GAAQ,EAAE,CAAC;YAClB,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,GAAG,CAAC,OAAO,EAAE,CAAC,CAAC;QAC/C,CAAC;IACH,CAAC,CAAC,CAAC;IAEH,iBAAiB;IACjB,GAAG,CAAC,GAAG,CAAC,gBAAgB,EAAE,CAAC,IAAI,EAAE,GAAG,EAAE,EAAE;QACtC,IAAI,CAAC;YACH,MAAM,SAAS,GAAG,IAAA,uBAAY,GAAE,CAAC;YACjC,GAAG,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC;QACtB,CAAC;QAAC,OAAO,GAAQ,EAAE,CAAC;YAClB,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,GAAG,CAAC,OAAO,EAAE,CAAC,CAAC;QAC/C,CAAC;IACH,CAAC,CAAC,CAAC;IAEH,yCAAyC;IACzC,GAAG,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,GAAG,EAAE,EAAE;QACpB,GAAG,CAAC,QAAQ,CAAC,cAAI,CAAC,IAAI,CAAC,SAAS,EAAE,IAAI,EAAE,QAAQ,EAAE,YAAY,CAAC,CAAC,CAAC;IACnE,CAAC,CAAC,CAAC;IAEH,uBAAuB;IACvB,GAAG,CAAC,GAAG,CAAC,CAAC,GAAQ,EAAE,IAAS,EAAE,GAAQ,EAAE,KAAU,EAAE,EAAE;QACpD,OAAO,CAAC,KAAK,CAAC,eAAe,EAAE,GAAG,CAAC,OAAO,CAAC,CAAC;QAC5C,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,GAAG,CAAC,OAAO,EAAE,CAAC,CAAC;IAC/C,CAAC,CAAC,CAAC;IAEH,OAAO,GAAG,CAAC;AACb,CAAC"}
@@ -0,0 +1,40 @@
1
+ export type SessionStatus = "running" | "completed" | "error";
2
+ export interface SessionMeta {
3
+ id: string;
4
+ cwd: string;
5
+ gitRoot?: string;
6
+ branch?: string;
7
+ createdAt: string;
8
+ updatedAt: string;
9
+ summaryCount?: number;
10
+ status: SessionStatus;
11
+ }
12
+ export interface SessionEvent {
13
+ type: string;
14
+ id: string;
15
+ timestamp: string;
16
+ data: Record<string, any>;
17
+ }
18
+ export interface SessionDetail extends SessionMeta {
19
+ events: SessionEvent[];
20
+ planContent?: string;
21
+ hasSnapshots: boolean;
22
+ copilotVersion?: string;
23
+ eventCounts: Record<string, number>;
24
+ duration: number;
25
+ status: SessionStatus;
26
+ }
27
+ export interface AnalyticsData {
28
+ totalSessions: number;
29
+ sessionsPerDay: Record<string, number>;
30
+ avgDuration: number;
31
+ minDuration: number;
32
+ maxDuration: number;
33
+ totalDuration: number;
34
+ toolUsage: Record<string, number>;
35
+ topDirectories: Record<string, number>;
36
+ branchActivity: Record<string, number>;
37
+ }
38
+ export declare function listSessions(): SessionMeta[];
39
+ export declare function getSession(sessionId: string): SessionDetail | null;
40
+ export declare function getAnalytics(): AnalyticsData;
@@ -0,0 +1,246 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ Object.defineProperty(exports, "__esModule", { value: true });
36
+ exports.listSessions = listSessions;
37
+ exports.getSession = getSession;
38
+ exports.getAnalytics = getAnalytics;
39
+ const fs = __importStar(require("fs"));
40
+ const path = __importStar(require("path"));
41
+ const os = __importStar(require("os"));
42
+ const yaml_1 = require("yaml");
43
+ function getSessionDir() {
44
+ return path.join(os.homedir(), ".copilot", "session-state");
45
+ }
46
+ function detectStatus(sessionDir, _updatedAt) {
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
+ const eventsPath = path.join(sessionDir, "events.jsonl");
52
+ if (!fs.existsSync(eventsPath))
53
+ return "completed";
54
+ // Only read the last 2KB to check for abort events (avoid reading huge files)
55
+ const stat = fs.statSync(eventsPath);
56
+ const readSize = Math.min(stat.size, 2048);
57
+ const buf = Buffer.alloc(readSize);
58
+ const fd = fs.openSync(eventsPath, "r");
59
+ fs.readSync(fd, buf, 0, readSize, Math.max(0, stat.size - readSize));
60
+ fs.closeSync(fd);
61
+ const tail = buf.toString("utf-8");
62
+ const lines = tail.trimEnd().split("\n").filter(Boolean);
63
+ // Check last few lines for abort signals
64
+ for (const line of lines.slice(-5)) {
65
+ try {
66
+ const event = JSON.parse(line);
67
+ if (event.type === "abort") {
68
+ return event.data?.reason === "user initiated" ? "completed" : "error";
69
+ }
70
+ }
71
+ catch { }
72
+ }
73
+ // No abort — check if recently modified
74
+ const age = Date.now() - stat.mtimeMs;
75
+ if (age < 300000)
76
+ return "running";
77
+ }
78
+ catch { }
79
+ return "completed";
80
+ }
81
+ function listSessions() {
82
+ const dir = getSessionDir();
83
+ if (!fs.existsSync(dir))
84
+ return [];
85
+ const entries = fs.readdirSync(dir, { withFileTypes: true });
86
+ const sessions = [];
87
+ for (const entry of entries) {
88
+ if (!entry.isDirectory())
89
+ continue;
90
+ const wsPath = path.join(dir, entry.name, "workspace.yaml");
91
+ if (!fs.existsSync(wsPath))
92
+ continue;
93
+ try {
94
+ const raw = fs.readFileSync(wsPath, "utf-8");
95
+ const ws = (0, yaml_1.parse)(raw);
96
+ const sessionPath = path.join(dir, entry.name);
97
+ const updatedAt = ws.updated_at || "";
98
+ sessions.push({
99
+ id: ws.id || entry.name,
100
+ cwd: ws.cwd || "",
101
+ gitRoot: ws.git_root,
102
+ branch: ws.branch,
103
+ createdAt: ws.created_at || "",
104
+ updatedAt,
105
+ summaryCount: ws.summary_count,
106
+ status: detectStatus(sessionPath, updatedAt),
107
+ });
108
+ }
109
+ catch {
110
+ // skip corrupted files
111
+ }
112
+ }
113
+ sessions.sort((a, b) => new Date(b.createdAt).getTime() - new Date(a.createdAt).getTime());
114
+ return sessions;
115
+ }
116
+ function getSession(sessionId) {
117
+ const dir = path.join(getSessionDir(), sessionId);
118
+ if (!fs.existsSync(dir))
119
+ return null;
120
+ // Parse workspace.yaml
121
+ const wsPath = path.join(dir, "workspace.yaml");
122
+ if (!fs.existsSync(wsPath))
123
+ return null;
124
+ let ws;
125
+ try {
126
+ ws = (0, yaml_1.parse)(fs.readFileSync(wsPath, "utf-8"));
127
+ }
128
+ catch {
129
+ return null;
130
+ }
131
+ // Parse events.jsonl
132
+ const events = [];
133
+ const eventsPath = path.join(dir, "events.jsonl");
134
+ try {
135
+ if (fs.existsSync(eventsPath)) {
136
+ const lines = fs.readFileSync(eventsPath, "utf-8").split("\n");
137
+ for (const line of lines) {
138
+ if (!line.trim())
139
+ continue;
140
+ try {
141
+ events.push(JSON.parse(line));
142
+ }
143
+ catch {
144
+ // skip malformed lines
145
+ }
146
+ }
147
+ }
148
+ }
149
+ catch {
150
+ // file may be locked by active session
151
+ }
152
+ // Read plan.md
153
+ let planContent;
154
+ const planPath = path.join(dir, "plan.md");
155
+ if (fs.existsSync(planPath)) {
156
+ planContent = fs.readFileSync(planPath, "utf-8");
157
+ }
158
+ // Check snapshots
159
+ const hasSnapshots = fs.existsSync(path.join(dir, "rewind-snapshots", "index.json"));
160
+ // Extract copilot version from session.start event
161
+ const startEvent = events.find((e) => e.type === "session.start");
162
+ const copilotVersion = startEvent?.data?.copilotVersion;
163
+ // Count events by type
164
+ const eventCounts = {};
165
+ for (const e of events) {
166
+ eventCounts[e.type] = (eventCounts[e.type] || 0) + 1;
167
+ }
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;
172
+ return {
173
+ id: ws.id || sessionId,
174
+ cwd: ws.cwd || "",
175
+ gitRoot: ws.git_root,
176
+ branch: ws.branch,
177
+ createdAt: ws.created_at || "",
178
+ updatedAt: ws.updated_at || "",
179
+ summaryCount: ws.summary_count,
180
+ events,
181
+ planContent,
182
+ hasSnapshots,
183
+ copilotVersion,
184
+ eventCounts,
185
+ duration,
186
+ status: detectStatus(dir, ws.updated_at || ""),
187
+ };
188
+ }
189
+ function getAnalytics() {
190
+ const sessions = listSessions();
191
+ const sessionsPerDay = {};
192
+ const toolUsage = {};
193
+ const topDirectories = {};
194
+ const branchActivity = {};
195
+ const durations = [];
196
+ const sessionDir = getSessionDir();
197
+ for (const s of sessions) {
198
+ // Sessions per day
199
+ const day = s.createdAt.slice(0, 10);
200
+ if (day)
201
+ 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);
206
+ // Top directories
207
+ const dirName = s.cwd || "unknown";
208
+ topDirectories[dirName] = (topDirectories[dirName] || 0) + 1;
209
+ // Branch activity
210
+ 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
213
+ try {
214
+ const eventsPath = path.join(sessionDir, s.id, "events.jsonl");
215
+ if (fs.existsSync(eventsPath)) {
216
+ const content = fs.readFileSync(eventsPath, "utf-8");
217
+ for (const line of content.split("\n")) {
218
+ if (!line.includes("tool.execution_start"))
219
+ continue;
220
+ try {
221
+ const event = JSON.parse(line);
222
+ if (event.type === "tool.execution_start") {
223
+ const tool = event.data?.tool || event.data?.toolName || "unknown";
224
+ toolUsage[tool] = (toolUsage[tool] || 0) + 1;
225
+ }
226
+ }
227
+ catch { }
228
+ }
229
+ }
230
+ }
231
+ catch { }
232
+ }
233
+ const totalDuration = durations.reduce((a, b) => a + b, 0);
234
+ return {
235
+ totalSessions: sessions.length,
236
+ sessionsPerDay,
237
+ avgDuration: durations.length ? totalDuration / durations.length : 0,
238
+ minDuration: durations.length ? Math.min(...durations) : 0,
239
+ maxDuration: durations.length ? Math.max(...durations) : 0,
240
+ totalDuration,
241
+ toolUsage,
242
+ topDirectories,
243
+ branchActivity,
244
+ };
245
+ }
246
+ //# sourceMappingURL=sessions.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sessions.js","sourceRoot":"","sources":["../src/sessions.ts"],"names":[],"mappings":"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"}
package/package.json ADDED
@@ -0,0 +1,35 @@
1
+ {
2
+ "name": "copilot-lens",
3
+ "version": "1.0.0",
4
+ "description": "A local dashboard to visualize and analyze your GitHub Copilot CLI sessions",
5
+ "main": "dist/server.js",
6
+ "bin": {
7
+ "copilot-lens": "dist/cli.js"
8
+ },
9
+ "scripts": {
10
+ "build": "tsc",
11
+ "dev": "tsx src/cli.ts",
12
+ "start": "node dist/cli.js",
13
+ "prepublishOnly": "npm run build"
14
+ },
15
+ "keywords": ["copilot", "github", "cli", "dashboard", "sessions", "analytics"],
16
+ "author": "pavanvamsi3",
17
+ "license": "MIT",
18
+ "repository": {
19
+ "type": "git",
20
+ "url": "https://github.com/pavanvamsi3/copilot-lens"
21
+ },
22
+ "files": ["dist/", "public/", "README.md"],
23
+ "dependencies": {
24
+ "cors": "^2.8.6",
25
+ "express": "^5.2.1",
26
+ "yaml": "^2.8.2"
27
+ },
28
+ "devDependencies": {
29
+ "@types/cors": "^2.8.19",
30
+ "@types/express": "^5.0.6",
31
+ "@types/node": "^25.2.2",
32
+ "tsx": "^4.21.0",
33
+ "typescript": "^5.9.3"
34
+ }
35
+ }