copilot-lens 1.0.8 → 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,121 +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
56
51
 
57
- ### 💬 Conversation View
52
+ > **Example**: Search `"redis connection pool"` and instantly find the session from three weeks ago where you worked through that implementation.
58
53
 
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)
54
+ ### 📋 Session Browser Review your conversations
63
55
 
64
- ![copilot-lens session view](https://raw.githubusercontent.com/pavanvamsi3/copilot-lens/main/assets/copilot-lens-session-view.png)
56
+ Browse the full history of your Copilot sessions in a searchable, filterable list.
65
57
 
66
- ### 📊 Analytics Dashboard
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
67
64
 
68
- Eight interactive charts powered by Chart.js, arranged in a 2-column grid:
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)
69
70
 
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 |
71
+ ![copilot-lens sessions list](https://raw.githubusercontent.com/pavanvamsi3/copilot-lens/main/assets/copilot-lens-sessions-list.png)
80
72
 
81
- Doughnut chart legends are interactive click a label to toggle that segment's visibility.
73
+ ### 📊 AnalyticsUnderstand your usage patterns
82
74
 
83
- #### Glimpse of Analytics Dashboard
75
+ Eight interactive charts that show how and when you use Copilot.
84
76
 
85
- ![copilot-lens demo](https://raw.githubusercontent.com/pavanvamsi3/copilot-lens/main/assets/copilot-lens-demo.png)
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 |
86
87
 
87
- ### 🎨 UI Features
88
+ Dark and light mode. Interactive chart legends. Manual refresh.
88
89
 
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
90
+ ![copilot-lens analytics](https://raw.githubusercontent.com/pavanvamsi3/copilot-lens/main/assets/copilot-lens-demo.png)
93
91
 
94
- ### 🏆 Copilot Effectiveness Score
92
+ ### 🏆 Effectiveness Score — See how well you're using Copilot
95
93
 
96
- Per-repo scoring (0-100) that measures how effectively you use Copilot CLI, with actionable improvement tips.
94
+ A 0100 score per repository (CLI) and globally (VS Code) with actionable improvement tips.
97
95
 
98
96
  | Category | What It Measures |
99
97
  |----------|-----------------|
100
- | **Prompt Quality** | Average prompt length, how often Copilot needs clarification |
101
- | **Tool Utilization** | Diversity of tools used (grep, glob, edit, task, etc.) |
102
- | **Efficiency** | Tool success rate and turns per session |
103
- | **MCP Utilization** | Configured MCP servers vs actually used (reads repo's `mcp.json`) |
104
- | **Engagement** | Session duration sweet spot and usage consistency |
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 |
105
103
 
106
- ![copilot-lens effectiveness score](https://raw.githubusercontent.com/pavanvamsi3/copilot-lens/main/assets/copilot-lens-score.png)
104
+ ![copilot-lens score](https://raw.githubusercontent.com/pavanvamsi3/copilot-lens/main/assets/copilot-lens-score.png)
105
+
106
+ ---
107
107
 
108
108
  ## How It Works
109
109
 
110
- 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.
111
111
 
112
- - **`workspace.yaml`** Session metadata (directory, git branch, timestamps)
113
- - **`events.jsonl`** — Full event log (messages, tool calls, errors)
114
- - **`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
115
117
 
116
- 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
117
121
 
118
- ### 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/` |
119
128
 
120
- 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.
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.
121
130
 
122
- ### Status Detection
131
+ ### Duration Calculation
123
132
 
124
- | Status | How It's Detected |
125
- |--------|-------------------|
126
- | **Running** | `session.db` exists and was modified within 10 min, or `events.jsonl` modified within 5 min |
127
- | **Completed** | Has an `abort` event with "user initiated" reason, or no recent activity |
128
- | **Error** | Has an `abort` event with a non-user-initiated reason |
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.
134
+
135
+ ---
129
136
 
130
137
  ## Tech Stack
131
138
 
132
- - **Backend**: Node.js + Express + TypeScript
133
- - **Frontend**: Vanilla HTML/CSS/JavaScript
134
- - **Charts**: Chart.js
135
- - **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
+ ---
136
148
 
137
149
  ## Development
138
150
 
@@ -140,21 +152,32 @@ Session durations are calculated from **actual event activity**, not wall-clock
140
152
  git clone https://github.com/pavanvamsi3/copilot-lens.git
141
153
  cd copilot-lens
142
154
  npm install
143
- npm run dev # Start with tsx (no build needed)
155
+ npm run dev # Start with tsx (no build step)
144
156
  npm run build # Compile TypeScript
157
+ npm test # Run tests
145
158
  npm start # Run compiled version
146
159
  ```
147
160
 
148
- ## License
149
-
150
- MIT
161
+ ---
151
162
 
152
163
  ## Optional: Custom Local Hostname
153
164
 
154
- 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
+ ```
155
176
 
156
- - **Windows** (run as Admin): `echo 127.0.0.1 copilot.lens >> C:\Windows\System32\drivers\etc\hosts`
157
- - **macOS/Linux**: `echo "127.0.0.1 copilot.lens" | sudo tee -a /etc/hosts`
177
+ Then: `copilot-lens --host copilot.lens`
158
178
 
159
- Then run: `copilot-lens --host copilot.lens`
179
+ ---
160
180
 
181
+ ## License
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 {};