copilot-lens 1.0.6 → 1.0.10

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,16 +1,14 @@
1
1
  # Copilot Lens 👓
2
2
 
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.
3
+ **Your Copilot history has answers. Now you can actually find them.**
4
4
 
5
- ## Why Copilot Lens?
5
+ Copilot Lens is a local memory layer for GitHub Copilot — search and browse everything you've ever discussed with Copilot, across both CLI terminal sessions and VS Code Copilot Chat. All on your machine. No cloud. No sign-in.
6
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:
7
+ ## Why
8
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?
9
+ Copilot sessions are ephemeral by default. You solve a problem, close the terminal, and it's gone. Days later you need that same approach, that regex, that architecture decision — and you have nothing to reference.
12
10
 
13
- Everything runs locally. No data leaves your machine. No cloud. No sign-in.
11
+ Copilot stores all of this locally. Copilot Lens makes it accessible.
14
12
 
15
13
  ## Install
16
14
 
@@ -18,107 +16,135 @@ Everything runs locally. No data leaves your machine. No cloud. No sign-in.
18
16
  npm install -g copilot-lens
19
17
  ```
20
18
 
21
- ## Usage
22
-
23
19
  ```bash
24
- # Start the dashboard
25
- copilot-lens
20
+ # Or without installing
21
+ npx copilot-lens --open
22
+ ```
26
23
 
27
- # Auto-open in browser
28
- copilot-lens --open
24
+ ## Usage
29
25
 
30
- # Custom port
26
+ ```bash
27
+ copilot-lens # Start the dashboard
28
+ copilot-lens --open # Start and open in browser
31
29
  copilot-lens --port 8080
32
-
33
- # Or use npx (no install needed)
34
- npx copilot-lens --open
35
30
  ```
36
31
 
37
- ### CLI Options
38
-
39
32
  | Flag | Default | Description |
40
33
  |------|---------|-------------|
41
34
  | `--port` | `3000` | Port number |
42
35
  | `--host` | `localhost` | Host address |
43
36
  | `--open` | off | Auto-open browser |
44
37
 
38
+ ---
39
+
45
40
  ## Features
46
41
 
47
- ### 📋 Session Browser
42
+ ### 🔍 Search — Find anything in your Copilot history
48
43
 
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
44
+ Search across every conversation you've ever had with Copilot CLI and VS Code in one place.
54
45
 
55
- ![copilot-lens sessions list](https://raw.githubusercontent.com/pavanvamsi3/copilot-lens/main/assets/copilot-lens-sessions-list.png)
46
+ - Full-text search over all session content
47
+ - Ranked results with inline highlights showing context around each match
48
+ - Filter results by source (CLI vs VS Code), date range, or working directory
49
+ - Results update as you type (debounced)
50
+ - Works offline, entirely on your machine
51
+
52
+ > **Example**: Search `"redis connection pool"` and instantly find the session from three weeks ago where you worked through that implementation.
53
+
54
+ ### 📋 Session Browser — Review your conversations
56
55
 
57
- ### 💬 Conversation View
56
+ Browse the full history of your Copilot sessions in a searchable, filterable list.
57
+
58
+ - **Unified view** — CLI terminal and VS Code Copilot Chat sessions side by side
59
+ - **Source badges** — see at a glance whether a session came from CLI or VS Code
60
+ - **Color-coded by directory** — each project gets a distinct accent color
61
+ - **Status detection** — Running, Completed, or Error
62
+ - **Filter by** time range, status, and working directory
63
+ - Click any session to open the full conversation
64
+
65
+ **Conversation view:**
66
+ - Chat-style layout — your prompts on the right, Copilot responses on the left
67
+ - Tool calls made during the session
68
+ - Errors that occurred
69
+ - Session plans (if created)
70
+
71
+ ![copilot-lens sessions list](https://raw.githubusercontent.com/pavanvamsi3/copilot-lens/main/assets/copilot-lens-sessions-list.png)
58
72
 
59
- - Chat-style layout with your prompts on the right and Copilot responses on the left
60
- - View tool calls made during the session
61
- - See any errors that occurred
62
- - Read session plans (if created)
73
+ ### 📊 Analytics Understand your usage patterns
63
74
 
64
- ![copilot-lens session view](https://raw.githubusercontent.com/pavanvamsi3/copilot-lens/main/assets/copilot-lens-session-view.png)
75
+ Eight interactive charts that show how and when you use Copilot.
65
76
 
66
- ### 📊 Analytics Dashboard
77
+ | Chart | What It Shows |
78
+ |-------|---------------|
79
+ | Sessions Per Day | Daily activity over time |
80
+ | Activity by Hour | When during the day you use Copilot most |
81
+ | Tool Usage | Most-used tools (grep, edit, glob, etc.) |
82
+ | Model Usage | Which AI models you've used |
83
+ | Top Working Directories | Which projects you use Copilot in most |
84
+ | Time Per Branch | Active Copilot time per git branch |
85
+ | Time Per Repo | Active Copilot time per repository |
86
+ | MCP Servers Used | Which MCP servers appear across sessions |
67
87
 
68
- Eight interactive charts powered by Chart.js, arranged in a 2-column grid:
88
+ Dark and light mode. Interactive chart legends. Manual refresh.
69
89
 
70
- | Chart | Type | What It Shows |
71
- |-------|------|---------------|
72
- | **Sessions Per Day** | Bar (compact) | Daily session activity over time |
73
- | **Activity by Hour of Day** | Bar (compact) | When during the day you use Copilot most |
74
- | **Tool Usage** | Doughnut | Most-used tools (grep, edit, powershell, etc.) |
75
- | **Model Usage** | Doughnut | Which AI models you've used (Claude, GPT, etc.) |
76
- | **Top Working Directories** | Horizontal bar (full-width) | Which project folders you use Copilot in most |
77
- | **Time Per Branch** | Horizontal bar | Active Copilot time spent on each git branch |
78
- | **Time Per Repo** | Horizontal bar | Active Copilot time per repository |
79
- | **MCP Servers Used** | Doughnut | Which MCP servers are configured across sessions |
90
+ ![copilot-lens analytics](https://raw.githubusercontent.com/pavanvamsi3/copilot-lens/main/assets/copilot-lens-demo.png)
80
91
 
81
- Doughnut chart legends are interactive click a label to toggle that segment's visibility.
92
+ ### 🏆 Effectiveness ScoreSee how well you're using Copilot
82
93
 
83
- #### Glimpse of Analytics Dashboard
94
+ A 0–100 score per repository (CLI) and globally (VS Code) with actionable improvement tips.
84
95
 
85
- ![copilot-lens demo](https://raw.githubusercontent.com/pavanvamsi3/copilot-lens/main/assets/copilot-lens-demo.png)
96
+ | Category | What It Measures |
97
+ |----------|-----------------|
98
+ | Prompt Quality | Average prompt length, clarification rate |
99
+ | Tool Utilization | Diversity of tools used across sessions |
100
+ | Efficiency | Tool success rate, turns per session |
101
+ | MCP Utilization | Configured vs. actually used MCP servers |
102
+ | Engagement | Session duration and usage consistency |
86
103
 
87
- ### 🎨 UI Features
104
+ ![copilot-lens score](https://raw.githubusercontent.com/pavanvamsi3/copilot-lens/main/assets/copilot-lens-score.png)
88
105
 
89
- - **Dark & Light mode** — toggle with one click, preference is saved
90
- - **Manual refresh** — refresh button to reload data on demand
91
- - **Responsive layout** — works on any screen size
92
- - **2-column grid layout** — compact charts with no wasted space
106
+ ---
93
107
 
94
108
  ## How It Works
95
109
 
96
- Copilot Lens reads session data from `~/.copilot/session-state/`, where GitHub Copilot CLI stores:
110
+ Copilot Lens reads session data from two local sources no network requests, no external APIs.
97
111
 
98
- - **`workspace.yaml`** Session metadata (directory, git branch, timestamps)
99
- - **`events.jsonl`** — Full event log (messages, tool calls, errors)
100
- - **`plan.md`**Session plans (if created)
112
+ ### GitHub Copilot CLI Sessions
113
+ - **Location**: `~/.copilot/session-state/`
114
+ - `workspace.yaml`session metadata (directory, git branch, timestamps)
115
+ - `events.jsonl` — full event log (messages, tool calls, errors)
116
+ - `plan.md` — session plans, if created
101
117
 
102
- A local Express server parses these files and serves a static frontend dashboard.
118
+ ### VS Code Copilot Chat Sessions
119
+ - **Index**: `state.vscdb` (SQLite) — session list with titles and timing
120
+ - **Content**: `emptyWindowChatSessions/{id}.json` — full conversation
103
121
 
104
- ### Duration Calculation
122
+ Supported platforms and paths:
123
+ | Platform | Path |
124
+ |----------|------|
125
+ | macOS | `~/Library/Application Support/Code/` |
126
+ | Windows | `%APPDATA%/Code/` |
127
+ | Linux | `~/.config/Code/` |
128
+
129
+ VS Code Insiders is also supported. Sessions with pasted images (which can exceed 100MB) are automatically stripped of image data. Files over 200MB are skipped.
105
130
 
106
- 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.
131
+ ### Duration Calculation
107
132
 
108
- ### Status Detection
133
+ Durations are calculated from actual event activity, not wall-clock time. Gaps longer than 5 minutes between events are excluded — so a session you paused and resumed doesn't show an inflated duration.
109
134
 
110
- | Status | How It's Detected |
111
- |--------|-------------------|
112
- | **Running** | `session.db` exists and was modified within 10 min, or `events.jsonl` modified within 5 min |
113
- | **Completed** | Has an `abort` event with "user initiated" reason, or no recent activity |
114
- | **Error** | Has an `abort` event with a non-user-initiated reason |
135
+ ---
115
136
 
116
137
  ## Tech Stack
117
138
 
118
- - **Backend**: Node.js + Express + TypeScript
119
- - **Frontend**: Vanilla HTML/CSS/JavaScript
120
- - **Charts**: Chart.js
121
- - **Data**: YAML + JSONL parsing (no database required)
139
+ | Layer | Technology |
140
+ |-------|-----------|
141
+ | Backend | Node.js + Express + TypeScript |
142
+ | Frontend | Vanilla HTML/CSS/JavaScript |
143
+ | Charts | Chart.js |
144
+ | Data | YAML, JSONL, SQLite (`better-sqlite3`) |
145
+ | Testing | Vitest (56 tests) |
146
+
147
+ ---
122
148
 
123
149
  ## Development
124
150
 
@@ -126,21 +152,32 @@ Session durations are calculated from **actual event activity**, not wall-clock
126
152
  git clone https://github.com/pavanvamsi3/copilot-lens.git
127
153
  cd copilot-lens
128
154
  npm install
129
- npm run dev # Start with tsx (no build needed)
155
+ npm run dev # Start with tsx (no build step)
130
156
  npm run build # Compile TypeScript
157
+ npm test # Run tests
131
158
  npm start # Run compiled version
132
159
  ```
133
160
 
134
- ## License
135
-
136
- MIT
161
+ ---
137
162
 
138
163
  ## Optional: Custom Local Hostname
139
164
 
140
- If you'd like a prettier URL like `http://copilot.lens:3000`, add this to your hosts file:
165
+ For a cleaner URL like `http://copilot.lens:3000`:
166
+
167
+ **macOS/Linux:**
168
+ ```bash
169
+ echo "127.0.0.1 copilot.lens" | sudo tee -a /etc/hosts
170
+ ```
171
+
172
+ **Windows (run as Admin):**
173
+ ```bash
174
+ echo 127.0.0.1 copilot.lens >> C:\Windows\System32\drivers\etc\hosts
175
+ ```
176
+
177
+ Then: `copilot-lens --host copilot.lens`
141
178
 
142
- - **Windows** (run as Admin): `echo 127.0.0.1 copilot.lens >> C:\Windows\System32\drivers\etc\hosts`
143
- - **macOS/Linux**: `echo "127.0.0.1 copilot.lens" | sudo tee -a /etc/hosts`
179
+ ---
144
180
 
145
- Then run: `copilot-lens --host copilot.lens`
181
+ ## License
146
182
 
183
+ MIT
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,60 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ const vitest_1 = require("vitest");
4
+ const cache_1 = require("../cache");
5
+ (0, vitest_1.beforeEach)(() => {
6
+ (0, cache_1.clearCache)();
7
+ });
8
+ (0, vitest_1.describe)("cachedCall", () => {
9
+ (0, vitest_1.it)("returns the computed value on first call", () => {
10
+ const result = (0, cache_1.cachedCall)("test-key", 1000, () => 42);
11
+ (0, vitest_1.expect)(result).toBe(42);
12
+ });
13
+ (0, vitest_1.it)("returns cached value on subsequent calls", () => {
14
+ let callCount = 0;
15
+ const fn = () => ++callCount;
16
+ const first = (0, cache_1.cachedCall)("counter", 1000, fn);
17
+ const second = (0, cache_1.cachedCall)("counter", 1000, fn);
18
+ (0, vitest_1.expect)(first).toBe(1);
19
+ (0, vitest_1.expect)(second).toBe(1); // cached, fn not called again
20
+ (0, vitest_1.expect)(callCount).toBe(1);
21
+ });
22
+ (0, vitest_1.it)("recomputes after TTL expires", () => {
23
+ let callCount = 0;
24
+ const fn = () => ++callCount;
25
+ (0, cache_1.cachedCall)("expire-test", 1000, fn);
26
+ // Manually expire the entry
27
+ const entry = cache_1._cacheInternals.cache.get("expire-test");
28
+ entry.expiresAt = Date.now() - 1;
29
+ const result = (0, cache_1.cachedCall)("expire-test", 1000, fn);
30
+ (0, vitest_1.expect)(result).toBe(2); // fn called again
31
+ (0, vitest_1.expect)(callCount).toBe(2);
32
+ });
33
+ (0, vitest_1.it)("uses separate keys for different caches", () => {
34
+ const a = (0, cache_1.cachedCall)("key-a", 1000, () => "alpha");
35
+ const b = (0, cache_1.cachedCall)("key-b", 1000, () => "beta");
36
+ (0, vitest_1.expect)(a).toBe("alpha");
37
+ (0, vitest_1.expect)(b).toBe("beta");
38
+ });
39
+ (0, vitest_1.it)("caches complex objects", () => {
40
+ const obj = { items: [1, 2, 3], nested: { x: true } };
41
+ const result = (0, cache_1.cachedCall)("obj", 1000, () => obj);
42
+ (0, vitest_1.expect)(result).toEqual(obj);
43
+ (0, vitest_1.expect)(result).toBe(obj); // same reference
44
+ });
45
+ });
46
+ (0, vitest_1.describe)("clearCache", () => {
47
+ (0, vitest_1.it)("invalidates all cached entries", () => {
48
+ let callCount = 0;
49
+ const fn = () => ++callCount;
50
+ (0, cache_1.cachedCall)("clear-test", 1000, fn);
51
+ (0, vitest_1.expect)(callCount).toBe(1);
52
+ (0, cache_1.clearCache)();
53
+ (0, cache_1.cachedCall)("clear-test", 1000, fn);
54
+ (0, vitest_1.expect)(callCount).toBe(2); // recomputed after clear
55
+ });
56
+ (0, vitest_1.it)("works when cache is empty", () => {
57
+ (0, vitest_1.expect)(() => (0, cache_1.clearCache)()).not.toThrow();
58
+ });
59
+ });
60
+ //# sourceMappingURL=cache.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cache.test.js","sourceRoot":"","sources":["../../src/__tests__/cache.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,203 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ const vitest_1 = require("vitest");
4
+ // Must mock before importing the module under test
5
+ vitest_1.vi.mock("../sessions", async (importOriginal) => {
6
+ const actual = await importOriginal();
7
+ return {
8
+ ...actual,
9
+ getSession: vitest_1.vi.fn(),
10
+ listSessions: vitest_1.vi.fn(),
11
+ };
12
+ });
13
+ const search_1 = require("../search");
14
+ const sessions_1 = require("../sessions");
15
+ const mockGetSession = vitest_1.vi.mocked(sessions_1.getSession);
16
+ // Helpers to build minimal SessionMeta and SessionDetail fixtures
17
+ function makeMeta(overrides = {}) {
18
+ return {
19
+ id: "sess-001",
20
+ cwd: "/home/user/project",
21
+ createdAt: "2024-01-15T10:00:00Z",
22
+ updatedAt: "2024-01-15T11:00:00Z",
23
+ status: "completed",
24
+ source: "cli",
25
+ ...overrides,
26
+ };
27
+ }
28
+ function makeDetail(meta, messages) {
29
+ return {
30
+ ...meta,
31
+ events: messages.map((m, i) => ({
32
+ type: m.type,
33
+ id: `event-${i}`,
34
+ timestamp: meta.createdAt,
35
+ data: { content: m.content },
36
+ })),
37
+ hasSnapshots: false,
38
+ eventCounts: {},
39
+ duration: 0,
40
+ };
41
+ }
42
+ // ─── Test 1: tokenize ────────────────────────────────────────────────────────
43
+ (0, vitest_1.describe)("tokenize", () => {
44
+ (0, vitest_1.it)("strips punctuation, lowercases, and removes tokens under 2 chars", () => {
45
+ const result = (0, search_1.tokenize)("Hello, World! A 42 foo-bar");
46
+ (0, vitest_1.expect)(result).toContain("hello");
47
+ (0, vitest_1.expect)(result).toContain("world");
48
+ (0, vitest_1.expect)(result).toContain("42");
49
+ (0, vitest_1.expect)(result).toContain("foo");
50
+ (0, vitest_1.expect)(result).toContain("bar");
51
+ // Single char 'a' should be removed
52
+ (0, vitest_1.expect)(result).not.toContain("a");
53
+ // Output should be lowercased
54
+ (0, vitest_1.expect)(result).not.toContain("Hello");
55
+ (0, vitest_1.expect)(result).not.toContain("World");
56
+ });
57
+ });
58
+ // ─── Tests 2 & 3: Empty / blank query ───────────────────────────────────────
59
+ (0, vitest_1.describe)("SearchIndex.search — empty/blank query", () => {
60
+ let index;
61
+ (0, vitest_1.beforeEach)(() => {
62
+ index = new search_1.SearchIndex();
63
+ const meta = makeMeta();
64
+ mockGetSession.mockReturnValue(makeDetail(meta, [{ type: "user.message", content: "hello world" }]));
65
+ index.buildIndex([meta]);
66
+ });
67
+ (0, vitest_1.it)("returns [] for empty string", () => {
68
+ (0, vitest_1.expect)(index.search("")).toEqual([]);
69
+ });
70
+ (0, vitest_1.it)("returns [] for blank string (spaces only)", () => {
71
+ (0, vitest_1.expect)(index.search(" ")).toEqual([]);
72
+ });
73
+ });
74
+ // ─── Test 4: Title match scores higher than content-only ─────────────────────
75
+ (0, vitest_1.describe)("SearchIndex.search — scoring", () => {
76
+ (0, vitest_1.it)("session with query token in title scores higher than content-only match", () => {
77
+ const index = new search_1.SearchIndex();
78
+ const metaTitle = makeMeta({ id: "title-sess", title: "typescript refactor", cwd: "/a/b" });
79
+ const metaContent = makeMeta({ id: "content-sess", title: "random session", cwd: "/c/d" });
80
+ mockGetSession.mockImplementation((id) => {
81
+ if (id === "title-sess") {
82
+ return makeDetail(metaTitle, [
83
+ { type: "user.message", content: "please help me with typescript refactor here" },
84
+ ]);
85
+ }
86
+ return makeDetail(metaContent, [
87
+ { type: "user.message", content: "please help me with typescript refactor here" },
88
+ ]);
89
+ });
90
+ index.buildIndex([metaTitle, metaContent]);
91
+ const results = index.search("typescript");
92
+ (0, vitest_1.expect)(results.length).toBeGreaterThanOrEqual(2);
93
+ const titleResult = results.find((r) => r.entry.id === "title-sess");
94
+ const contentResult = results.find((r) => r.entry.id === "content-sess");
95
+ (0, vitest_1.expect)(titleResult.score).toBeGreaterThan(contentResult.score);
96
+ });
97
+ // ─── Test 5: cwd match scores higher than content-only ────────────────────
98
+ (0, vitest_1.it)("cwd match scores higher than content-only match", () => {
99
+ const index = new search_1.SearchIndex();
100
+ const metaCwd = makeMeta({ id: "cwd-sess", cwd: "/home/user/typescript-project", title: "sess1" });
101
+ const metaContent = makeMeta({ id: "plain-sess", cwd: "/home/user/other", title: "sess2" });
102
+ mockGetSession.mockImplementation((id) => {
103
+ if (id === "cwd-sess") {
104
+ return makeDetail(metaCwd, [
105
+ { type: "user.message", content: "help with typescript" },
106
+ ]);
107
+ }
108
+ return makeDetail(metaContent, [
109
+ { type: "user.message", content: "help with typescript" },
110
+ ]);
111
+ });
112
+ index.buildIndex([metaCwd, metaContent]);
113
+ const results = index.search("typescript");
114
+ (0, vitest_1.expect)(results.length).toBeGreaterThanOrEqual(2);
115
+ const cwdResult = results.find((r) => r.entry.id === "cwd-sess");
116
+ const plainResult = results.find((r) => r.entry.id === "plain-sess");
117
+ (0, vitest_1.expect)(cwdResult.score).toBeGreaterThan(plainResult.score);
118
+ });
119
+ });
120
+ // ─── Test 6: Highlight extraction ───────────────────────────────────────────
121
+ (0, vitest_1.describe)("SearchIndex.search — highlights", () => {
122
+ (0, vitest_1.it)("highlight snippet is at most 121 chars (±60 around match)", () => {
123
+ const index = new search_1.SearchIndex();
124
+ const meta = makeMeta();
125
+ // Long content with a searchable word in the middle
126
+ const padding = "word ".repeat(20); // 100 chars before and after
127
+ const content = `${padding}targetword ${padding}`;
128
+ mockGetSession.mockReturnValue(makeDetail(meta, [{ type: "user.message", content }]));
129
+ index.buildIndex([meta]);
130
+ const results = index.search("targetword");
131
+ (0, vitest_1.expect)(results.length).toBe(1);
132
+ (0, vitest_1.expect)(results[0].highlights.length).toBeGreaterThan(0);
133
+ for (const snippet of results[0].highlights) {
134
+ // ±60 chars around a 10-char token = max 121 chars before word-boundary trimming
135
+ (0, vitest_1.expect)(snippet.length).toBeLessThanOrEqual(121);
136
+ }
137
+ });
138
+ });
139
+ // ─── Tests 7 & 8: source filter ─────────────────────────────────────────────
140
+ (0, vitest_1.describe)("SearchIndex.search — source filter", () => {
141
+ let index;
142
+ const metaCli = makeMeta({ id: "cli-sess", source: "cli", cwd: "/cli/proj" });
143
+ const metaVscode = makeMeta({ id: "vscode-sess", source: "vscode", cwd: "/vscode/proj" });
144
+ (0, vitest_1.beforeEach)(() => {
145
+ index = new search_1.SearchIndex();
146
+ mockGetSession.mockImplementation((id) => {
147
+ if (id === "cli-sess") {
148
+ return makeDetail(metaCli, [{ type: "user.message", content: "hello from cli session" }]);
149
+ }
150
+ return makeDetail(metaVscode, [{ type: "user.message", content: "hello from vscode session" }]);
151
+ });
152
+ index.buildIndex([metaCli, metaVscode]);
153
+ });
154
+ (0, vitest_1.it)("source:'cli' excludes vscode entries", () => {
155
+ const results = index.search("hello", { source: "cli" });
156
+ (0, vitest_1.expect)(results.every((r) => r.entry.source === "cli")).toBe(true);
157
+ (0, vitest_1.expect)(results.some((r) => r.entry.id === "cli-sess")).toBe(true);
158
+ (0, vitest_1.expect)(results.some((r) => r.entry.id === "vscode-sess")).toBe(false);
159
+ });
160
+ (0, vitest_1.it)("source:'vscode' excludes cli entries", () => {
161
+ const results = index.search("hello", { source: "vscode" });
162
+ (0, vitest_1.expect)(results.every((r) => r.entry.source === "vscode")).toBe(true);
163
+ (0, vitest_1.expect)(results.some((r) => r.entry.id === "vscode-sess")).toBe(true);
164
+ (0, vitest_1.expect)(results.some((r) => r.entry.id === "cli-sess")).toBe(false);
165
+ });
166
+ });
167
+ // ─── Test 9: clear() causes rebuild ─────────────────────────────────────────
168
+ (0, vitest_1.describe)("SearchIndex.clear()", () => {
169
+ (0, vitest_1.it)("causes next search() call to rebuild the index", () => {
170
+ const index = new search_1.SearchIndex();
171
+ const meta = makeMeta({ id: "rebuild-sess" });
172
+ // First build: content has "apple"
173
+ mockGetSession.mockReturnValue(makeDetail(meta, [{ type: "user.message", content: "I love apple pie" }]));
174
+ index.buildIndex([meta]);
175
+ (0, vitest_1.expect)(index.search("apple")).toHaveLength(1);
176
+ (0, vitest_1.expect)(index.search("mango")).toHaveLength(0);
177
+ // Clear and update the mock to return different content
178
+ index.clear();
179
+ mockGetSession.mockReturnValue(makeDetail(meta, [{ type: "user.message", content: "I love mango juice" }]));
180
+ // Next search() should lazy-rebuild from storedSessions
181
+ const afterClear = index.search("mango");
182
+ (0, vitest_1.expect)(afterClear).toHaveLength(1);
183
+ // "apple" no longer in index
184
+ (0, vitest_1.expect)(index.search("apple")).toHaveLength(0);
185
+ });
186
+ });
187
+ // ─── Test 10: limit option ───────────────────────────────────────────────────
188
+ (0, vitest_1.describe)("SearchIndex.search — limit option", () => {
189
+ (0, vitest_1.it)("respects limit option and does not exceed it", () => {
190
+ const index = new search_1.SearchIndex();
191
+ const metas = Array.from({ length: 10 }, (_, i) => makeMeta({ id: `sess-${i}`, cwd: `/proj/${i}`, title: `session ${i}` }));
192
+ mockGetSession.mockImplementation((id) => {
193
+ const meta = metas.find((m) => m.id === id);
194
+ return makeDetail(meta, [
195
+ { type: "user.message", content: "common keyword everywhere" },
196
+ ]);
197
+ });
198
+ index.buildIndex(metas);
199
+ const results = index.search("common", { limit: 3 });
200
+ (0, vitest_1.expect)(results.length).toBeLessThanOrEqual(3);
201
+ });
202
+ });
203
+ //# sourceMappingURL=search.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"search.test.js","sourceRoot":"","sources":["../../src/__tests__/search.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1 @@
1
+ export {};