codedash-app 1.2.0 → 1.3.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/README.md CHANGED
@@ -1,60 +1,71 @@
1
- # claude-sessions-dash
1
+ # CodeDash
2
2
 
3
- Termius-style browser dashboard for your Claude Code (and Codex) sessions.
3
+ Browser dashboard for Claude Code & Codex sessions. View, search, resume, and manage all your AI coding sessions.
4
4
 
5
- ![Dashboard](https://img.shields.io/badge/UI-Dark%20Theme-1a1d23?style=flat-square) ![Node](https://img.shields.io/badge/node-%3E%3D16-green?style=flat-square) ![License](https://img.shields.io/badge/license-MIT-blue?style=flat-square)
5
+ https://github.com/user-attachments/assets/15c45659-365b-49f8-86a3-9005fa155ca6
6
+
7
+ ![npm](https://img.shields.io/npm/v/codedash-app?style=flat-square) ![Node](https://img.shields.io/badge/node-%3E%3D16-green?style=flat-square) ![License](https://img.shields.io/badge/license-MIT-blue?style=flat-square)
6
8
 
7
9
  ## Quick Start
8
10
 
9
11
  ```bash
10
- npx claude-sessions-dash
12
+ npx codedash-app run
11
13
  ```
12
14
 
13
- Opens `http://localhost:3847` with your sessions dashboard.
14
-
15
- Custom port:
15
+ Opens `http://localhost:3847` in your browser.
16
16
 
17
17
  ```bash
18
- npx claude-sessions-dash 4000
18
+ npx codedash-app run --port=4000 # custom port
19
+ npx codedash-app run --no-browser # don't auto-open
20
+ npx codedash-app list # list sessions in terminal
21
+ npx codedash-app stats # show statistics
19
22
  ```
20
23
 
21
24
  ## Features
22
25
 
23
26
  **Sessions**
24
- - View all Claude Code and Codex sessions in a card grid
25
- - Group by project, view as timeline, or filter by tool
26
- - Full-text search across session names and projects
27
- - Preview conversation history in a side panel
27
+ - Grid and List view with project grouping
28
+ - Trigram fuzzy search across session content and projects
29
+ - Filter by tool (Claude/Codex), tags, date range
30
+ - Star/pin important sessions (always shown first)
31
+ - Tag sessions: bug, feature, research, infra, deploy, review
32
+ - Activity heatmap (GitHub-style)
33
+ - Cost estimation per session
28
34
 
29
35
  **Launch**
30
- - Resume any session directly in your terminal (iTerm2, Terminal.app, Warp, Kitty, Alacritty)
31
- - One-click launch with `--dangerously-skip-permissions` option
36
+ - Resume sessions in iTerm2, Terminal.app, Warp, Kitty, Alacritty
32
37
  - Auto `cd` into the correct project directory
33
38
  - Copy resume command to clipboard
34
39
  - Terminal preference saved between sessions
35
40
 
36
41
  **Manage**
37
42
  - Delete sessions (file + history + env cleanup)
38
- - Confirmation dialog to prevent accidents
39
- - Refresh data without restarting
43
+ - Bulk select and delete
44
+ - Export conversations as Markdown
45
+ - Related git commits shown per session
46
+ - Auto-update notifications
47
+
48
+ **Themes**
49
+ - Dark (default), Light, System
40
50
 
41
51
  **Keyboard Shortcuts**
42
- - `/` Focus search
43
- - `Escape` Close panels
52
+ - `/` focus search, `j/k` navigate, `Enter` open
53
+ - `x` star, `d` delete, `s` select mode, `g` toggle groups
54
+ - `r` refresh, `Escape` close panels
44
55
 
45
56
  ## How It Works
46
57
 
47
- Reads session data from `~/.claude/`:
48
- - `history.jsonl` — session index with timestamps and projects
49
- - `projects/*/\<session-id\>.jsonl` — full conversation data
50
- - `session-env/` — session environment files
58
+ Reads session data from `~/.claude/` and `~/.codex/`:
59
+ - `history.jsonl` — session index
60
+ - `projects/*/<session-id>.jsonl` — conversation data
61
+ - `sessions/` — Codex session files
51
62
 
52
- Zero dependencies. Single Node.js file. Everything runs on `localhost`.
63
+ Zero dependencies. Everything runs on `localhost`.
53
64
 
54
65
  ## Requirements
55
66
 
56
67
  - Node.js >= 16
57
- - Claude Code installed (`~/.claude/` directory exists)
68
+ - Claude Code or Codex CLI installed
58
69
  - macOS / Linux / Windows
59
70
 
60
71
  ## License
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "codedash-app",
3
- "version": "1.2.0",
3
+ "version": "1.3.0",
4
4
  "description": "Termius-style browser dashboard for Claude Code sessions. View, search, resume, and delete sessions with a dark-themed UI.",
5
5
  "bin": {
6
6
  "codedash": "./bin/cli.js"
@@ -186,48 +186,103 @@ function saveTerminalPref(val) {
186
186
  localStorage.setItem('codedash-terminal', val);
187
187
  }
188
188
 
189
+ // ── Trigram search ─────────────────────────────────────────────
190
+
191
+ function trigrams(str) {
192
+ var s = ' ' + str.toLowerCase() + ' ';
193
+ var t = {};
194
+ for (var i = 0; i < s.length - 2; i++) {
195
+ var tri = s.substring(i, i + 3);
196
+ t[tri] = (t[tri] || 0) + 1;
197
+ }
198
+ return t;
199
+ }
200
+
201
+ function trigramScore(query, text) {
202
+ if (!query || !text) return 0;
203
+ var qt = trigrams(query);
204
+ var tt = trigrams(text);
205
+ var matches = 0;
206
+ var total = 0;
207
+ for (var k in qt) {
208
+ total += qt[k];
209
+ if (tt[k]) matches += Math.min(qt[k], tt[k]);
210
+ }
211
+ return total > 0 ? matches / total : 0;
212
+ }
213
+
214
+ function searchScore(query, session) {
215
+ var q = query.toLowerCase();
216
+ var fields = [
217
+ session.first_message || '',
218
+ session.project_short || '',
219
+ session.project || '',
220
+ session.id || '',
221
+ session.tool || ''
222
+ ];
223
+ var haystack = fields.join(' ').toLowerCase();
224
+
225
+ // Exact substring match = highest score
226
+ if (haystack.indexOf(q) >= 0) return 1;
227
+
228
+ // Trigram fuzzy match
229
+ var best = 0;
230
+ for (var i = 0; i < fields.length; i++) {
231
+ var score = trigramScore(q, fields[i]);
232
+ if (score > best) best = score;
233
+ }
234
+ // Also score against full haystack
235
+ var fullScore = trigramScore(q, haystack);
236
+ if (fullScore > best) best = fullScore;
237
+
238
+ return best;
239
+ }
240
+
189
241
  // ── Filtering ──────────────────────────────────────────────────
190
242
 
243
+ var SEARCH_THRESHOLD = 0.3;
244
+
191
245
  function applyFilters() {
192
- filteredSessions = allSessions.filter(function(s) {
193
- // Tool filter
194
- if (toolFilter && s.tool !== toolFilter) return false;
246
+ var scored = [];
247
+ for (var i = 0; i < allSessions.length; i++) {
248
+ var s = allSessions[i];
195
249
 
196
- // Search
197
- if (searchQuery) {
198
- var q = searchQuery.toLowerCase();
199
- var haystack = (
200
- (s.first_message || '') + ' ' +
201
- (s.project || '') + ' ' +
202
- (s.project_short || '') + ' ' +
203
- (s.id || '') + ' ' +
204
- (s.tool || '')
205
- ).toLowerCase();
206
- if (haystack.indexOf(q) === -1) return false;
207
- }
250
+ // Tool filter
251
+ if (toolFilter && s.tool !== toolFilter) continue;
208
252
 
209
253
  // Tag filter
210
254
  if (tagFilter) {
211
255
  var sessionTags = tags[s.id] || [];
212
- if (sessionTags.indexOf(tagFilter) === -1) return false;
256
+ if (sessionTags.indexOf(tagFilter) === -1) continue;
213
257
  }
214
258
 
215
259
  // Date range
216
- if (dateFrom && s.date < dateFrom) return false;
217
- if (dateTo && s.date > dateTo) return false;
260
+ if (dateFrom && s.date < dateFrom) continue;
261
+ if (dateTo && s.date > dateTo) continue;
218
262
 
219
- return true;
220
- });
263
+ // Search with trigram scoring
264
+ var score = 1;
265
+ if (searchQuery) {
266
+ score = searchScore(searchQuery, s);
267
+ if (score < SEARCH_THRESHOLD) continue;
268
+ }
269
+
270
+ scored.push({ session: s, score: score });
271
+ }
221
272
 
222
- // Starred sessions first
223
- filteredSessions.sort(function(a, b) {
224
- var aStarred = stars.indexOf(a.id) >= 0 ? 1 : 0;
225
- var bStarred = stars.indexOf(b.id) >= 0 ? 1 : 0;
273
+ // Sort: starred first, then by search score (if searching), then by time
274
+ scored.sort(function(a, b) {
275
+ var aStarred = stars.indexOf(a.session.id) >= 0 ? 1 : 0;
276
+ var bStarred = stars.indexOf(b.session.id) >= 0 ? 1 : 0;
226
277
  if (aStarred !== bStarred) return bStarred - aStarred;
227
- return b.last_ts - a.last_ts;
278
+ if (searchQuery && a.score !== b.score) return b.score - a.score;
279
+ return b.session.last_ts - a.session.last_ts;
228
280
  });
229
281
 
282
+ filteredSessions = scored.map(function(x) { return x.session; });
283
+
230
284
  render();
285
+
231
286
  }
232
287
 
233
288
  function onSearch(val) {