heyiam 0.1.7 → 0.1.8

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.
Files changed (136) hide show
  1. package/dist/analyzer.d.ts +3 -3
  2. package/dist/archive.d.ts +14 -0
  3. package/dist/archive.js +125 -0
  4. package/dist/archive.js.map +1 -0
  5. package/dist/auth.d.ts +0 -6
  6. package/dist/auth.js +2 -4
  7. package/dist/auth.js.map +1 -1
  8. package/dist/autostart.d.ts +19 -0
  9. package/dist/autostart.js +103 -0
  10. package/dist/autostart.js.map +1 -0
  11. package/dist/bridge.d.ts +0 -2
  12. package/dist/bridge.js +33 -4
  13. package/dist/bridge.js.map +1 -1
  14. package/dist/config.d.ts +1 -1
  15. package/dist/config.js +2 -2
  16. package/dist/config.js.map +1 -1
  17. package/dist/context-export.d.ts +22 -0
  18. package/dist/context-export.js +230 -0
  19. package/dist/context-export.js.map +1 -0
  20. package/dist/daemon-install.d.ts +23 -0
  21. package/dist/daemon-install.js +155 -0
  22. package/dist/daemon-install.js.map +1 -0
  23. package/dist/db.d.ts +118 -0
  24. package/dist/db.js +444 -0
  25. package/dist/db.js.map +1 -0
  26. package/dist/export.d.ts +30 -0
  27. package/dist/export.js +377 -0
  28. package/dist/export.js.map +1 -0
  29. package/dist/format-utils.d.ts +6 -0
  30. package/dist/format-utils.js +15 -0
  31. package/dist/format-utils.js.map +1 -0
  32. package/dist/index.js +474 -117
  33. package/dist/index.js.map +1 -1
  34. package/dist/llm/project-enhance.js +1 -1
  35. package/dist/parsers/claude.js +73 -0
  36. package/dist/parsers/claude.js.map +1 -1
  37. package/dist/parsers/codex.js +1 -1
  38. package/dist/parsers/codex.js.map +1 -1
  39. package/dist/parsers/cursor.d.ts +2 -0
  40. package/dist/parsers/cursor.js +14 -26
  41. package/dist/parsers/cursor.js.map +1 -1
  42. package/dist/parsers/gemini.d.ts +3 -2
  43. package/dist/parsers/gemini.js +198 -21
  44. package/dist/parsers/gemini.js.map +1 -1
  45. package/dist/parsers/index.d.ts +1 -1
  46. package/dist/parsers/index.js +23 -7
  47. package/dist/parsers/index.js.map +1 -1
  48. package/dist/parsers/types.d.ts +27 -1
  49. package/dist/render/build-render-data.d.ts +59 -0
  50. package/dist/render/build-render-data.js +101 -0
  51. package/dist/render/build-render-data.js.map +1 -0
  52. package/dist/render/components/PortfolioPage.d.ts +4 -0
  53. package/dist/render/components/PortfolioPage.js +16 -0
  54. package/dist/render/components/PortfolioPage.js.map +1 -0
  55. package/dist/render/components/ProjectPage.d.ts +4 -0
  56. package/dist/render/components/ProjectPage.js +101 -0
  57. package/dist/render/components/ProjectPage.js.map +1 -0
  58. package/dist/render/components/SessionPage.d.ts +4 -0
  59. package/dist/render/components/SessionPage.js +29 -0
  60. package/dist/render/components/SessionPage.js.map +1 -0
  61. package/dist/render/index.d.ts +37 -0
  62. package/dist/render/index.js +140 -0
  63. package/dist/render/index.js.map +1 -0
  64. package/dist/render/types.d.ts +121 -0
  65. package/dist/render/types.js +9 -0
  66. package/dist/render/types.js.map +1 -0
  67. package/dist/routes/archive.d.ts +3 -0
  68. package/dist/routes/archive.js +56 -0
  69. package/dist/routes/archive.js.map +1 -0
  70. package/dist/routes/auth.d.ts +3 -0
  71. package/dist/routes/auth.js +116 -0
  72. package/dist/routes/auth.js.map +1 -0
  73. package/dist/routes/context.d.ts +61 -0
  74. package/dist/routes/context.js +356 -0
  75. package/dist/routes/context.js.map +1 -0
  76. package/dist/routes/dashboard.d.ts +3 -0
  77. package/dist/routes/dashboard.js +103 -0
  78. package/dist/routes/dashboard.js.map +1 -0
  79. package/dist/routes/enhance.d.ts +3 -0
  80. package/dist/routes/enhance.js +305 -0
  81. package/dist/routes/enhance.js.map +1 -0
  82. package/dist/routes/export.d.ts +3 -0
  83. package/dist/routes/export.js +145 -0
  84. package/dist/routes/export.js.map +1 -0
  85. package/dist/routes/index.d.ts +12 -0
  86. package/dist/routes/index.js +13 -0
  87. package/dist/routes/index.js.map +1 -0
  88. package/dist/routes/preview.d.ts +3 -0
  89. package/dist/routes/preview.js +191 -0
  90. package/dist/routes/preview.js.map +1 -0
  91. package/dist/routes/projects.d.ts +3 -0
  92. package/dist/routes/projects.js +356 -0
  93. package/dist/routes/projects.js.map +1 -0
  94. package/dist/routes/publish.d.ts +3 -0
  95. package/dist/routes/publish.js +466 -0
  96. package/dist/routes/publish.js.map +1 -0
  97. package/dist/routes/search.d.ts +3 -0
  98. package/dist/routes/search.js +110 -0
  99. package/dist/routes/search.js.map +1 -0
  100. package/dist/routes/sessions.d.ts +3 -0
  101. package/dist/routes/sessions.js +103 -0
  102. package/dist/routes/sessions.js.map +1 -0
  103. package/dist/routes/settings.d.ts +3 -0
  104. package/dist/routes/settings.js +30 -0
  105. package/dist/routes/settings.js.map +1 -0
  106. package/dist/screenshot.d.ts +5 -2
  107. package/dist/screenshot.js +187 -13
  108. package/dist/screenshot.js.map +1 -1
  109. package/dist/search.d.ts +30 -0
  110. package/dist/search.js +153 -0
  111. package/dist/search.js.map +1 -0
  112. package/dist/server.d.ts +1 -1
  113. package/dist/server.js +55 -1318
  114. package/dist/server.js.map +1 -1
  115. package/dist/settings.d.ts +23 -6
  116. package/dist/settings.js +36 -12
  117. package/dist/settings.js.map +1 -1
  118. package/dist/source-audit.d.ts +29 -0
  119. package/dist/source-audit.js +203 -0
  120. package/dist/source-audit.js.map +1 -0
  121. package/dist/sync.d.ts +74 -0
  122. package/dist/sync.js +358 -0
  123. package/dist/sync.js.map +1 -0
  124. package/dist/transcript.d.ts +68 -0
  125. package/dist/transcript.js +268 -0
  126. package/dist/transcript.js.map +1 -0
  127. package/package.json +5 -1
  128. package/app/dist/assets/html2canvas-Cwn_rrOw.js +0 -5
  129. package/app/dist/assets/index-CEQyTkgN.js +0 -14
  130. package/app/dist/assets/index-DLh5xRE8.css +0 -1
  131. package/app/dist/favicon.svg +0 -5
  132. package/app/dist/icons.svg +0 -24
  133. package/app/dist/index.html +0 -20
  134. package/dist/machine-key.d.ts +0 -10
  135. package/dist/machine-key.js +0 -51
  136. package/dist/machine-key.js.map +0 -1
package/dist/db.js ADDED
@@ -0,0 +1,444 @@
1
+ // SQLite search index for session archive
2
+ // Replaces stats-cache.json with a proper database layer
3
+ import Database from 'better-sqlite3';
4
+ import { mkdirSync, statSync } from 'node:fs';
5
+ import { join } from 'node:path';
6
+ import { homedir } from 'node:os';
7
+ // ── Constants ────────────────────────────────────────────────
8
+ const CONFIG_DIR = join(homedir(), '.config', 'heyiam');
9
+ export const DB_PATH = join(CONFIG_DIR, 'sessions.db');
10
+ const CURRENT_SCHEMA_VERSION = 2;
11
+ // ── Singleton ────────────────────────────────────────────────
12
+ let _db = null;
13
+ export function getDatabase(dbPath = DB_PATH) {
14
+ if (_db)
15
+ return _db;
16
+ _db = openDatabase(dbPath);
17
+ return _db;
18
+ }
19
+ export function closeDatabase() {
20
+ if (_db) {
21
+ _db.close();
22
+ _db = null;
23
+ }
24
+ }
25
+ // ── Open / Migrate ───────────────────────────────────────────
26
+ export function openDatabase(dbPath = DB_PATH) {
27
+ mkdirSync(join(dbPath, '..'), { recursive: true });
28
+ const db = new Database(dbPath);
29
+ db.pragma('journal_mode = WAL');
30
+ db.pragma('foreign_keys = ON');
31
+ runMigrations(db);
32
+ return db;
33
+ }
34
+ function runMigrations(db) {
35
+ // Create schema_version table if it doesn't exist
36
+ db.exec(`CREATE TABLE IF NOT EXISTS schema_version (version INTEGER)`);
37
+ const row = db.prepare('SELECT version FROM schema_version LIMIT 1').get();
38
+ const currentVersion = row?.version ?? 0;
39
+ if (currentVersion < 2) {
40
+ migrateToV2(db);
41
+ }
42
+ }
43
+ function migrateToV2(db) {
44
+ // Drop old tables if upgrading from v1
45
+ db.exec('DROP TABLE IF EXISTS session_files');
46
+ db.exec('DROP TABLE IF EXISTS sessions_fts');
47
+ db.exec('DROP TABLE IF EXISTS sessions');
48
+ const tx = db.transaction(() => {
49
+ // F1: session_files gets composite PK
50
+ // F2: ON DELETE CASCADE on all foreign keys
51
+ // F5: NOT NULL DEFAULT on numeric columns
52
+ // F7: role UNINDEXED in FTS5
53
+ // F21: file_mtime as INTEGER not REAL
54
+ db.exec(`
55
+ CREATE TABLE sessions (
56
+ id TEXT PRIMARY KEY,
57
+ project_dir TEXT NOT NULL,
58
+ source TEXT NOT NULL,
59
+ title TEXT,
60
+ start_time TEXT,
61
+ end_time TEXT,
62
+ duration_minutes REAL NOT NULL DEFAULT 0,
63
+ wall_clock_minutes REAL NOT NULL DEFAULT 0,
64
+ turns INTEGER NOT NULL DEFAULT 0,
65
+ loc_added INTEGER NOT NULL DEFAULT 0,
66
+ loc_removed INTEGER NOT NULL DEFAULT 0,
67
+ loc_net INTEGER NOT NULL DEFAULT 0,
68
+ files_changed INTEGER NOT NULL DEFAULT 0,
69
+ tool_calls INTEGER NOT NULL DEFAULT 0,
70
+ skills TEXT,
71
+ files_touched TEXT,
72
+ models_used TEXT,
73
+ cwd TEXT,
74
+ parent_session_id TEXT,
75
+ agent_role TEXT,
76
+ is_subagent INTEGER NOT NULL DEFAULT 0,
77
+ file_path TEXT,
78
+ file_mtime INTEGER,
79
+ file_size INTEGER,
80
+ indexed_at TEXT,
81
+ context_summary TEXT,
82
+ FOREIGN KEY (parent_session_id) REFERENCES sessions(id) ON DELETE CASCADE
83
+ )
84
+ `);
85
+ db.exec('CREATE INDEX idx_sessions_project ON sessions(project_dir)');
86
+ db.exec('CREATE INDEX idx_sessions_source ON sessions(source)');
87
+ db.exec('CREATE INDEX idx_sessions_start ON sessions(start_time)');
88
+ db.exec('CREATE INDEX idx_sessions_parent ON sessions(parent_session_id)');
89
+ db.exec(`
90
+ CREATE VIRTUAL TABLE sessions_fts USING fts5(
91
+ session_id UNINDEXED,
92
+ role UNINDEXED,
93
+ content,
94
+ tokenize='porter unicode61'
95
+ )
96
+ `);
97
+ db.exec(`
98
+ CREATE TABLE session_files (
99
+ session_id TEXT NOT NULL,
100
+ file_path TEXT NOT NULL,
101
+ additions INTEGER NOT NULL DEFAULT 0,
102
+ deletions INTEGER NOT NULL DEFAULT 0,
103
+ PRIMARY KEY (session_id, file_path),
104
+ FOREIGN KEY (session_id) REFERENCES sessions(id) ON DELETE CASCADE
105
+ )
106
+ `);
107
+ db.exec('CREATE INDEX idx_session_files_path ON session_files(file_path)');
108
+ // Upsert schema version
109
+ const existing = db.prepare('SELECT version FROM schema_version LIMIT 1').get();
110
+ if (existing) {
111
+ db.prepare('UPDATE schema_version SET version = ?').run(CURRENT_SCHEMA_VERSION);
112
+ }
113
+ else {
114
+ db.prepare('INSERT INTO schema_version (version) VALUES (?)').run(CURRENT_SCHEMA_VERSION);
115
+ }
116
+ });
117
+ tx();
118
+ }
119
+ // ── Staleness Check ──────────────────────────────────────────
120
+ export function isSessionStale(db, sessionId, filePath) {
121
+ const row = db.prepare('SELECT file_mtime, file_size FROM sessions WHERE id = ?').get(sessionId);
122
+ if (!row)
123
+ return true; // Not in DB — needs indexing
124
+ // Skip stat for non-filesystem paths (e.g. cursor:// URLs) — F23 fix
125
+ if (filePath.includes('://'))
126
+ return true;
127
+ try {
128
+ const stat = statSync(filePath);
129
+ // F21: Use Math.floor to avoid floating-point comparison issues
130
+ const mtime = Math.floor(stat.mtimeMs);
131
+ const size = stat.size;
132
+ return mtime !== row.file_mtime || size !== row.file_size;
133
+ }
134
+ catch {
135
+ return true; // Can't stat — re-index to be safe
136
+ }
137
+ }
138
+ export function upsertSession(db, input) {
139
+ const { meta, analysis, session, fileMtime, fileSize } = input;
140
+ const stmt = db.prepare(`
141
+ INSERT INTO sessions (
142
+ id, project_dir, source, title, start_time, end_time,
143
+ duration_minutes, wall_clock_minutes, turns, loc_added, loc_removed, loc_net,
144
+ files_changed, tool_calls, skills, files_touched, models_used,
145
+ cwd, parent_session_id, agent_role, is_subagent,
146
+ file_path, file_mtime, file_size, indexed_at, context_summary
147
+ ) VALUES (
148
+ ?, ?, ?, ?, ?, ?,
149
+ ?, ?, ?, ?, ?, ?,
150
+ ?, ?, ?, ?, ?,
151
+ ?, ?, ?, ?,
152
+ ?, ?, ?, ?, ?
153
+ )
154
+ ON CONFLICT(id) DO UPDATE SET
155
+ project_dir = excluded.project_dir,
156
+ source = excluded.source,
157
+ title = excluded.title,
158
+ start_time = excluded.start_time,
159
+ end_time = excluded.end_time,
160
+ duration_minutes = excluded.duration_minutes,
161
+ wall_clock_minutes = excluded.wall_clock_minutes,
162
+ turns = excluded.turns,
163
+ loc_added = excluded.loc_added,
164
+ loc_removed = excluded.loc_removed,
165
+ loc_net = excluded.loc_net,
166
+ files_changed = excluded.files_changed,
167
+ tool_calls = excluded.tool_calls,
168
+ skills = excluded.skills,
169
+ files_touched = excluded.files_touched,
170
+ models_used = excluded.models_used,
171
+ cwd = excluded.cwd,
172
+ parent_session_id = excluded.parent_session_id,
173
+ agent_role = excluded.agent_role,
174
+ is_subagent = excluded.is_subagent,
175
+ file_path = excluded.file_path,
176
+ file_mtime = excluded.file_mtime,
177
+ file_size = excluded.file_size,
178
+ indexed_at = excluded.indexed_at,
179
+ context_summary = excluded.context_summary
180
+ `);
181
+ stmt.run(meta.sessionId, meta.projectDir, analysis.source, session.title ?? null, analysis.start_time ?? null, analysis.end_time ?? null, session.durationMinutes, session.wallClockMinutes ?? 0, session.turns, analysis.loc_stats.loc_added, analysis.loc_stats.loc_removed, analysis.loc_stats.loc_net, session.filesChanged.length, session.toolCalls, JSON.stringify(session.skills), JSON.stringify(analysis.files_touched), JSON.stringify(analysis.models_used ?? []), analysis.cwd ?? null, meta.parentSessionId ?? null, meta.agentRole ?? null, meta.isSubagent ? 1 : 0, meta.path, Math.floor(fileMtime), fileSize, new Date().toISOString(), input.contextSummary ?? null);
182
+ }
183
+ // ── Index Session Content (FTS5) ─────────────────────────────
184
+ export function indexSessionContent(db, sessionId, turns) {
185
+ // Clear existing FTS entries for this session
186
+ db.prepare('DELETE FROM sessions_fts WHERE session_id = ?').run(sessionId);
187
+ const insert = db.prepare('INSERT INTO sessions_fts (session_id, role, content) VALUES (?, ?, ?)');
188
+ // F8: Truncate content per turn to prevent FTS index bloat.
189
+ // Tool outputs (file reads, command output) can be 50KB+ but are low-value for search.
190
+ const MAX_CONTENT_CHARS = 10_000;
191
+ for (const turn of turns) {
192
+ const role = turn.type === 'prompt' ? 'user' : turn.type === 'tool' ? 'tool' : 'assistant';
193
+ const content = turn.content;
194
+ if (content && content.length > 0) {
195
+ const truncated = content.length > MAX_CONTENT_CHARS
196
+ ? content.slice(0, MAX_CONTENT_CHARS)
197
+ : content;
198
+ insert.run(sessionId, role, truncated);
199
+ }
200
+ }
201
+ }
202
+ // ── Index Session Files ──────────────────────────────────────
203
+ export function indexSessionFiles(db, sessionId, files) {
204
+ db.prepare('DELETE FROM session_files WHERE session_id = ?').run(sessionId);
205
+ const insert = db.prepare('INSERT OR REPLACE INTO session_files (session_id, file_path, additions, deletions) VALUES (?, ?, ?, ?)');
206
+ for (const file of files) {
207
+ insert.run(sessionId, file.path, file.additions, file.deletions);
208
+ }
209
+ }
210
+ // ── Full Index Pipeline (transactional) ──────────────────────
211
+ export function indexSession(db, input, turns) {
212
+ const tx = db.transaction(() => {
213
+ upsertSession(db, input);
214
+ indexSessionContent(db, input.meta.sessionId, turns);
215
+ indexSessionFiles(db, input.meta.sessionId, input.session.filesChanged);
216
+ });
217
+ tx();
218
+ }
219
+ // ── Read: Get Session Stats ──────────────────────────────────
220
+ export function getSessionStats(db, sessionId) {
221
+ const row = db.prepare(`
222
+ SELECT
223
+ id, start_time, end_time, duration_minutes, turns,
224
+ loc_added, loc_removed, files_changed, skills
225
+ FROM sessions WHERE id = ?
226
+ `).get(sessionId);
227
+ if (!row)
228
+ return null;
229
+ const loc = (row.loc_added ?? 0) + (row.loc_removed ?? 0);
230
+ const skills = row.skills ? JSON.parse(row.skills) : [];
231
+ return {
232
+ loc,
233
+ duration: row.duration_minutes ?? 0,
234
+ files: row.files_changed ?? 0,
235
+ turns: row.turns ?? 0,
236
+ skills,
237
+ date: row.start_time ?? '',
238
+ endTime: row.end_time ?? undefined,
239
+ };
240
+ }
241
+ // ── Read: Get All Project Stats ──────────────────────────────
242
+ export function getAllProjectStats(db) {
243
+ // F9: Single query — no N+1. Fetch aggregates and skills in one pass.
244
+ const rows = db.prepare(`
245
+ SELECT
246
+ project_dir,
247
+ COUNT(*) as session_count,
248
+ COALESCE(SUM(loc_added + loc_removed), 0) as total_loc,
249
+ COALESCE(SUM(duration_minutes), 0) as total_duration,
250
+ COALESCE(SUM(turns), 0) as total_turns,
251
+ MAX(start_time) as latest_date,
252
+ GROUP_CONCAT(DISTINCT source) as sources
253
+ FROM sessions
254
+ WHERE is_subagent = 0
255
+ GROUP BY project_dir
256
+ ORDER BY latest_date DESC
257
+ `).all();
258
+ // Collect all skills in a single query, grouped by project
259
+ const skillRows = db.prepare('SELECT project_dir, skills FROM sessions WHERE skills IS NOT NULL AND is_subagent = 0').all();
260
+ const skillsByProject = new Map();
261
+ for (const sr of skillRows) {
262
+ const parsed = JSON.parse(sr.skills);
263
+ if (!skillsByProject.has(sr.project_dir)) {
264
+ skillsByProject.set(sr.project_dir, new Set());
265
+ }
266
+ const set = skillsByProject.get(sr.project_dir);
267
+ for (const s of parsed)
268
+ set.add(s);
269
+ }
270
+ return rows.map((row) => {
271
+ const projectSkills = skillsByProject.get(row.project_dir);
272
+ const projectName = row.project_dir.replace(/^-/, '').split('-').pop() ?? row.project_dir;
273
+ return {
274
+ projectDir: row.project_dir,
275
+ projectName,
276
+ sessionCount: row.session_count,
277
+ totalLoc: row.total_loc,
278
+ totalDuration: row.total_duration,
279
+ totalTurns: row.total_turns,
280
+ skills: projectSkills ? [...projectSkills].sort() : [],
281
+ sources: row.sources ? row.sources.split(',') : [],
282
+ latestDate: row.latest_date ?? '',
283
+ };
284
+ });
285
+ }
286
+ // ── Read: Get Session Row ────────────────────────────────────
287
+ export function getSessionRow(db, sessionId) {
288
+ return db.prepare('SELECT * FROM sessions WHERE id = ?').get(sessionId) ?? null;
289
+ }
290
+ // ── Read: Get Context Summary ─────────────────────────────────
291
+ export function getContextSummary(db, sessionId) {
292
+ const row = db.prepare('SELECT context_summary FROM sessions WHERE id = ?').get(sessionId);
293
+ return row?.context_summary ?? null;
294
+ }
295
+ // ── Read: List sessions by project ───────────────────────────
296
+ export function getSessionsByProject(db, projectDir) {
297
+ return db.prepare('SELECT * FROM sessions WHERE project_dir = ? ORDER BY start_time DESC').all(projectDir);
298
+ }
299
+ // ── Delete ───────────────────────────────────────────────────
300
+ export function deleteSession(db, sessionId) {
301
+ const tx = db.transaction(() => {
302
+ db.prepare('DELETE FROM sessions_fts WHERE session_id = ?').run(sessionId);
303
+ db.prepare('DELETE FROM session_files WHERE session_id = ?').run(sessionId);
304
+ db.prepare('DELETE FROM sessions WHERE id = ?').run(sessionId);
305
+ });
306
+ tx();
307
+ }
308
+ // ── Rebuild Index ────────────────────────────────────────────
309
+ export function rebuildIndex(db, onProgress) {
310
+ const tx = db.transaction(() => {
311
+ db.exec('DELETE FROM sessions_fts');
312
+ db.exec('DELETE FROM session_files');
313
+ db.exec('DELETE FROM sessions');
314
+ });
315
+ tx();
316
+ onProgress?.(0, 0);
317
+ }
318
+ /** F17: Merge FTS5 segments for better query performance. Call after bulk indexing. */
319
+ export function optimizeFtsIndex(db) {
320
+ db.exec("INSERT INTO sessions_fts(sessions_fts) VALUES('optimize')");
321
+ }
322
+ // ── Cleanup ──────────────────────────────────────────────────
323
+ //
324
+ // IMPORTANT: We do NOT delete sessions from the DB when the source file
325
+ // is gone. The DB IS the archive — if Claude Code deleted the original
326
+ // after 30 days, the DB row is the preserved copy. That's the whole
327
+ // point of the product.
328
+ //
329
+ // The only valid cleanup is removing sessions that the USER explicitly
330
+ // chose to delete, which would go through deleteSession() directly.
331
+ /** Count sessions whose source file no longer exists (preserved in DB). */
332
+ export function countPreservedSessions(db) {
333
+ const rows = db.prepare("SELECT file_path FROM sessions WHERE file_path IS NOT NULL AND file_path NOT LIKE 'cursor://%'").all();
334
+ let preserved = 0;
335
+ for (const row of rows) {
336
+ try {
337
+ statSync(row.file_path);
338
+ }
339
+ catch {
340
+ preserved++;
341
+ }
342
+ }
343
+ return preserved;
344
+ }
345
+ /**
346
+ * Escape a user query for FTS5. Wraps each term in double quotes
347
+ * to prevent FTS5 syntax injection (*, OR, NOT, NEAR, etc.).
348
+ */
349
+ function escapeFtsQuery(raw) {
350
+ // Split on whitespace, quote each non-empty term
351
+ const terms = raw.trim().split(/\s+/).filter(Boolean);
352
+ if (terms.length === 0)
353
+ return '""';
354
+ return terms.map((t) => `"${t.replace(/"/g, '""')}"`).join(' ');
355
+ }
356
+ export function searchFts(db, query, limit = 50) {
357
+ const safeQuery = escapeFtsQuery(query);
358
+ // Fetch a larger window of raw FTS matches, then deduplicate in JS.
359
+ // We fetch limit*10 rows to ensure we get enough distinct sessions,
360
+ // since a single session can have dozens of matching turns.
361
+ const rawRows = db.prepare(`
362
+ SELECT
363
+ session_id,
364
+ snippet(sessions_fts, 2, '<mark>', '</mark>', '...', 40) as snippet,
365
+ rank
366
+ FROM sessions_fts
367
+ WHERE sessions_fts MATCH ?
368
+ ORDER BY rank
369
+ LIMIT ?
370
+ `).all(safeQuery, limit * 10);
371
+ // Deduplicate: keep best-ranking entry per session
372
+ const bySession = new Map();
373
+ for (const r of rawRows) {
374
+ const existing = bySession.get(r.session_id);
375
+ if (!existing || r.rank < existing.rank) {
376
+ bySession.set(r.session_id, r);
377
+ }
378
+ }
379
+ // Sort by rank and limit
380
+ const rows = [...bySession.values()]
381
+ .sort((a, b) => a.rank - b.rank)
382
+ .slice(0, limit);
383
+ return rows.map((r) => ({
384
+ sessionId: r.session_id,
385
+ snippet: r.snippet,
386
+ rank: r.rank,
387
+ }));
388
+ }
389
+ // ── File Search ──────────────────────────────────────────────
390
+ export function searchByFile(db, filePath) {
391
+ // Escape LIKE wildcards in user input
392
+ const escaped = filePath.replace(/[%_]/g, (c) => `\\${c}`);
393
+ return db.prepare(`
394
+ SELECT session_id as sessionId, additions, deletions
395
+ FROM session_files
396
+ WHERE file_path LIKE ? ESCAPE '\\'
397
+ `).all(`%${escaped}%`);
398
+ }
399
+ export function getAllSessionMetas(db) {
400
+ const rows = db.prepare(`
401
+ SELECT id, file_path, source, project_dir, is_subagent, parent_session_id, agent_role
402
+ FROM sessions
403
+ ORDER BY project_dir, start_time
404
+ `).all();
405
+ return rows.map((r) => ({
406
+ path: r.file_path ?? '',
407
+ source: r.source,
408
+ sessionId: r.id,
409
+ projectDir: r.project_dir,
410
+ isSubagent: r.is_subagent === 1,
411
+ parentSessionId: r.parent_session_id ?? undefined,
412
+ agentRole: r.agent_role ?? undefined,
413
+ }));
414
+ }
415
+ // ── Session Count ────────────────────────────────────────────
416
+ export function getSessionCount(db) {
417
+ const row = db.prepare('SELECT COUNT(*) as count FROM sessions').get();
418
+ return row.count;
419
+ }
420
+ export function getDashboardStats(db) {
421
+ const agg = db.prepare(`
422
+ SELECT
423
+ COUNT(*) as session_count,
424
+ COUNT(DISTINCT project_dir) as project_count,
425
+ COUNT(DISTINCT source) as source_count
426
+ FROM sessions WHERE is_subagent = 0
427
+ `).get();
428
+ const projects = getAllProjectStats(db);
429
+ return {
430
+ sessionCount: agg.session_count,
431
+ projectCount: agg.project_count,
432
+ sourceCount: agg.source_count,
433
+ projects: projects.map((p) => ({
434
+ projectDir: p.projectDir,
435
+ projectName: p.projectName,
436
+ sessionCount: p.sessionCount,
437
+ totalLoc: p.totalLoc,
438
+ totalDuration: p.totalDuration,
439
+ skills: p.skills,
440
+ latestDate: p.latestDate,
441
+ })),
442
+ };
443
+ }
444
+ //# sourceMappingURL=db.js.map
package/dist/db.js.map ADDED
@@ -0,0 +1 @@
1
+ {"version":3,"file":"db.js","sourceRoot":"","sources":["../src/db.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,30 @@
1
+ /**
2
+ * Export module — produces Markdown and HTML exports of refined projects.
3
+ *
4
+ * Both functions write self-contained, offline-viewable output.
5
+ * HTML export uses the same render pipeline as publish (ReactDOMServer),
6
+ * producing JS-free static HTML safe for script-src 'self' CSP.
7
+ */
8
+ import type { ProjectEnhanceCache } from './settings.js';
9
+ import type { Session } from './analyzer.js';
10
+ export interface ExportResult {
11
+ files: string[];
12
+ totalBytes: number;
13
+ outputPath: string;
14
+ }
15
+ export declare function exportMarkdown(dirName: string, cache: ProjectEnhanceCache, sessions: Session[], outputPath: string): Promise<ExportResult>;
16
+ export declare function exportHtml(dirName: string, cache: ProjectEnhanceCache, sessions: Session[], outputPath: string, username?: string): Promise<ExportResult>;
17
+ export interface HtmlFile {
18
+ path: string;
19
+ content: string;
20
+ }
21
+ /**
22
+ * Generate HTML files in memory (no disk writes).
23
+ * Returns an array of {path, content} for zipping.
24
+ */
25
+ export declare function generateHtmlFiles(dirName: string, cache: ProjectEnhanceCache, sessions: Session[], username?: string): HtmlFile[];
26
+ /**
27
+ * Create a ZIP file buffer from an array of {path, content} entries.
28
+ * Uses DEFLATE compression via Node's built-in zlib.
29
+ */
30
+ export declare function createZipBuffer(entries: HtmlFile[]): Buffer;