@code-insights/cli 3.4.1 → 3.5.1
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/CHANGELOG.md +11 -0
- package/README.md +20 -0
- package/dashboard-dist/assets/index-CFvScypR.css +1 -0
- package/dashboard-dist/assets/index-CyMrlee5.js +592 -0
- package/dashboard-dist/dist/assets/index-CFvScypR.css +1 -0
- package/dashboard-dist/dist/assets/index-CyMrlee5.js +592 -0
- package/dashboard-dist/dist/favicon.svg +18 -0
- package/dashboard-dist/dist/index.html +33 -0
- package/dashboard-dist/index.html +2 -2
- package/dist/commands/telemetry.d.ts.map +1 -1
- package/dist/commands/telemetry.js +14 -4
- package/dist/commands/telemetry.js.map +1 -1
- package/dist/utils/telemetry.d.ts +1 -1
- package/dist/utils/telemetry.d.ts.map +1 -1
- package/dist/utils/telemetry.js.map +1 -1
- package/package.json +2 -2
- package/server-dist/dist/index.d.ts +20 -0
- package/server-dist/dist/index.d.ts.map +1 -0
- package/server-dist/dist/index.js +109 -0
- package/server-dist/dist/index.js.map +1 -0
- package/server-dist/dist/llm/analysis.d.ts +84 -0
- package/server-dist/dist/llm/analysis.d.ts.map +1 -0
- package/server-dist/dist/llm/analysis.js +550 -0
- package/server-dist/dist/llm/analysis.js.map +1 -0
- package/server-dist/dist/llm/client.d.ts +27 -0
- package/server-dist/dist/llm/client.d.ts.map +1 -0
- package/server-dist/dist/llm/client.js +71 -0
- package/server-dist/dist/llm/client.js.map +1 -0
- package/server-dist/dist/llm/index.d.ts +7 -0
- package/server-dist/dist/llm/index.d.ts.map +1 -0
- package/server-dist/dist/llm/index.js +5 -0
- package/server-dist/dist/llm/index.js.map +1 -0
- package/server-dist/dist/llm/prompts.d.ts +109 -0
- package/server-dist/dist/llm/prompts.d.ts.map +1 -0
- package/server-dist/dist/llm/prompts.js +381 -0
- package/server-dist/dist/llm/prompts.js.map +1 -0
- package/server-dist/dist/llm/providers/anthropic.d.ts +3 -0
- package/server-dist/dist/llm/providers/anthropic.d.ts.map +1 -0
- package/server-dist/dist/llm/providers/anthropic.js +45 -0
- package/server-dist/dist/llm/providers/anthropic.js.map +1 -0
- package/server-dist/dist/llm/providers/gemini.d.ts +3 -0
- package/server-dist/dist/llm/providers/gemini.d.ts.map +1 -0
- package/server-dist/dist/llm/providers/gemini.js +51 -0
- package/server-dist/dist/llm/providers/gemini.js.map +1 -0
- package/server-dist/dist/llm/providers/ollama.d.ts +12 -0
- package/server-dist/dist/llm/providers/ollama.d.ts.map +1 -0
- package/server-dist/dist/llm/providers/ollama.js +61 -0
- package/server-dist/dist/llm/providers/ollama.js.map +1 -0
- package/server-dist/dist/llm/providers/openai.d.ts +3 -0
- package/server-dist/dist/llm/providers/openai.d.ts.map +1 -0
- package/server-dist/dist/llm/providers/openai.js +40 -0
- package/server-dist/dist/llm/providers/openai.js.map +1 -0
- package/server-dist/dist/llm/types.d.ts +22 -0
- package/server-dist/dist/llm/types.d.ts.map +1 -0
- package/server-dist/dist/llm/types.js +5 -0
- package/server-dist/dist/llm/types.js.map +1 -0
- package/server-dist/dist/routes/analysis.d.ts +4 -0
- package/server-dist/dist/routes/analysis.d.ts.map +1 -0
- package/server-dist/dist/routes/analysis.js +370 -0
- package/server-dist/dist/routes/analysis.js.map +1 -0
- package/server-dist/dist/routes/analytics.d.ts +4 -0
- package/server-dist/dist/routes/analytics.d.ts.map +1 -0
- package/server-dist/dist/routes/analytics.js +57 -0
- package/server-dist/dist/routes/analytics.js.map +1 -0
- package/server-dist/dist/routes/config.d.ts +4 -0
- package/server-dist/dist/routes/config.d.ts.map +1 -0
- package/server-dist/dist/routes/config.js +108 -0
- package/server-dist/dist/routes/config.js.map +1 -0
- package/server-dist/dist/routes/export.d.ts +4 -0
- package/server-dist/dist/routes/export.d.ts.map +1 -0
- package/server-dist/dist/routes/export.js +52 -0
- package/server-dist/dist/routes/export.js.map +1 -0
- package/server-dist/dist/routes/insights.d.ts +4 -0
- package/server-dist/dist/routes/insights.d.ts.map +1 -0
- package/server-dist/dist/routes/insights.js +80 -0
- package/server-dist/dist/routes/insights.js.map +1 -0
- package/server-dist/dist/routes/messages.d.ts +4 -0
- package/server-dist/dist/routes/messages.d.ts.map +1 -0
- package/server-dist/dist/routes/messages.js +19 -0
- package/server-dist/dist/routes/messages.js.map +1 -0
- package/server-dist/dist/routes/projects.d.ts +4 -0
- package/server-dist/dist/routes/projects.d.ts.map +1 -0
- package/server-dist/dist/routes/projects.js +32 -0
- package/server-dist/dist/routes/projects.js.map +1 -0
- package/server-dist/dist/routes/sessions.d.ts +4 -0
- package/server-dist/dist/routes/sessions.d.ts.map +1 -0
- package/server-dist/dist/routes/sessions.js +65 -0
- package/server-dist/dist/routes/sessions.js.map +1 -0
- package/server-dist/dist/routes/telemetry.d.ts +4 -0
- package/server-dist/dist/routes/telemetry.d.ts.map +1 -0
- package/server-dist/dist/routes/telemetry.js +21 -0
- package/server-dist/dist/routes/telemetry.js.map +1 -0
- package/server-dist/dist/utils.d.ts +6 -0
- package/server-dist/dist/utils.d.ts.map +1 -0
- package/server-dist/dist/utils.js +9 -0
- package/server-dist/dist/utils.js.map +1 -0
- package/dashboard-dist/assets/index-BdoBoNtI.js +0 -567
- package/dashboard-dist/assets/index-QzYeMRSf.css +0 -1
|
@@ -0,0 +1,370 @@
|
|
|
1
|
+
import { Hono } from 'hono';
|
|
2
|
+
import { streamSSE } from 'hono/streaming';
|
|
3
|
+
import { getDb } from '@code-insights/cli/db/client';
|
|
4
|
+
import { trackEvent, captureError } from '@code-insights/cli/utils/telemetry';
|
|
5
|
+
import { parseIntParam } from '../utils.js';
|
|
6
|
+
import { loadLLMConfig, isLLMConfigured } from '../llm/client.js';
|
|
7
|
+
import { analyzeSession, analyzePromptQuality, findRecurringInsights } from '../llm/analysis.js';
|
|
8
|
+
const app = new Hono();
|
|
9
|
+
// POST /api/analysis/session
|
|
10
|
+
// Body: { sessionId: string }
|
|
11
|
+
// Fetches session + messages from SQLite, runs LLM analysis, saves insights, returns results.
|
|
12
|
+
app.post('/session', async (c) => {
|
|
13
|
+
if (!isLLMConfigured()) {
|
|
14
|
+
return c.json({
|
|
15
|
+
success: false,
|
|
16
|
+
error: 'LLM not configured. Run `code-insights config llm` to configure a provider.',
|
|
17
|
+
}, 400);
|
|
18
|
+
}
|
|
19
|
+
const body = await c.req.json();
|
|
20
|
+
if (!body.sessionId || typeof body.sessionId !== 'string') {
|
|
21
|
+
return c.json({ error: 'Missing required field: sessionId' }, 400);
|
|
22
|
+
}
|
|
23
|
+
const db = getDb();
|
|
24
|
+
const session = db.prepare(`
|
|
25
|
+
SELECT id, project_id, project_name, project_path, summary, ended_at
|
|
26
|
+
FROM sessions WHERE id = ?
|
|
27
|
+
`).get(body.sessionId);
|
|
28
|
+
if (!session) {
|
|
29
|
+
return c.json({ error: 'Session not found' }, 404);
|
|
30
|
+
}
|
|
31
|
+
const messages = db.prepare(`
|
|
32
|
+
SELECT id, session_id, type, content, thinking, tool_calls, tool_results, usage, timestamp, parent_id
|
|
33
|
+
FROM messages WHERE session_id = ? ORDER BY timestamp ASC
|
|
34
|
+
`).all(body.sessionId);
|
|
35
|
+
const llmConfig = loadLLMConfig();
|
|
36
|
+
const startTime = Date.now();
|
|
37
|
+
const result = await analyzeSession(session, messages);
|
|
38
|
+
const baseProperties = {
|
|
39
|
+
type: 'session',
|
|
40
|
+
llm_provider: llmConfig?.provider,
|
|
41
|
+
llm_model: llmConfig?.model,
|
|
42
|
+
duration_ms: Date.now() - startTime,
|
|
43
|
+
success: result.success,
|
|
44
|
+
};
|
|
45
|
+
if (!result.success) {
|
|
46
|
+
const errorProperties = {
|
|
47
|
+
...baseProperties,
|
|
48
|
+
error_type: result.error_type,
|
|
49
|
+
error_message: result.error,
|
|
50
|
+
response_preview: result.response_preview,
|
|
51
|
+
};
|
|
52
|
+
trackEvent('analysis_run', errorProperties);
|
|
53
|
+
captureError(new Error(result.error ?? 'analysis_run failed'), errorProperties);
|
|
54
|
+
}
|
|
55
|
+
else {
|
|
56
|
+
trackEvent('analysis_run', baseProperties);
|
|
57
|
+
trackEvent('insight_generated', {
|
|
58
|
+
type: 'session',
|
|
59
|
+
count: result.insights.length,
|
|
60
|
+
});
|
|
61
|
+
}
|
|
62
|
+
return c.json(result, result.success ? 200 : 422);
|
|
63
|
+
});
|
|
64
|
+
// GET /api/analysis/session/stream?sessionId=X
|
|
65
|
+
// SSE endpoint — streams progress events during session analysis.
|
|
66
|
+
// onProgress is non-async because analyzeSession calls it without await;
|
|
67
|
+
// stream.writeSSE is fire-and-forget for progress events (non-fatal if missed).
|
|
68
|
+
app.get('/session/stream', async (c) => {
|
|
69
|
+
if (!isLLMConfigured()) {
|
|
70
|
+
return c.json({
|
|
71
|
+
success: false,
|
|
72
|
+
error: 'LLM not configured. Run `code-insights config llm` to configure a provider.',
|
|
73
|
+
}, 400);
|
|
74
|
+
}
|
|
75
|
+
const sessionId = c.req.query('sessionId');
|
|
76
|
+
if (!sessionId) {
|
|
77
|
+
return c.json({ error: 'Missing required query param: sessionId' }, 400);
|
|
78
|
+
}
|
|
79
|
+
const db = getDb();
|
|
80
|
+
const session = db.prepare(`
|
|
81
|
+
SELECT id, project_id, project_name, project_path, summary, ended_at
|
|
82
|
+
FROM sessions WHERE id = ?
|
|
83
|
+
`).get(sessionId);
|
|
84
|
+
if (!session) {
|
|
85
|
+
return c.json({ error: 'Session not found' }, 404);
|
|
86
|
+
}
|
|
87
|
+
const messages = db.prepare(`
|
|
88
|
+
SELECT id, session_id, type, content, thinking, tool_calls, tool_results, usage, timestamp, parent_id
|
|
89
|
+
FROM messages WHERE session_id = ? ORDER BY timestamp ASC
|
|
90
|
+
`).all(sessionId);
|
|
91
|
+
const llmConfig = loadLLMConfig();
|
|
92
|
+
return streamSSE(c, async (stream) => {
|
|
93
|
+
const streamStart = Date.now();
|
|
94
|
+
try {
|
|
95
|
+
const abortSignal = c.req.raw.signal;
|
|
96
|
+
await stream.writeSSE({
|
|
97
|
+
event: 'progress',
|
|
98
|
+
data: JSON.stringify({ phase: 'loading_messages', message: 'Loading messages...' }),
|
|
99
|
+
});
|
|
100
|
+
const result = await analyzeSession(session, messages, {
|
|
101
|
+
signal: abortSignal,
|
|
102
|
+
onProgress: (progress) => {
|
|
103
|
+
const message = progress.phase === 'saving'
|
|
104
|
+
? 'Saving insights...'
|
|
105
|
+
: progress.currentChunk && progress.totalChunks
|
|
106
|
+
? `Analyzing... (${progress.currentChunk} of ${progress.totalChunks})`
|
|
107
|
+
: 'Analyzing...';
|
|
108
|
+
void stream.writeSSE({
|
|
109
|
+
event: 'progress',
|
|
110
|
+
data: JSON.stringify({ ...progress, message }),
|
|
111
|
+
}).catch(() => { });
|
|
112
|
+
},
|
|
113
|
+
});
|
|
114
|
+
const streamBaseProperties = {
|
|
115
|
+
type: 'session',
|
|
116
|
+
llm_provider: llmConfig?.provider,
|
|
117
|
+
llm_model: llmConfig?.model,
|
|
118
|
+
duration_ms: Date.now() - streamStart,
|
|
119
|
+
success: result.success,
|
|
120
|
+
};
|
|
121
|
+
if (!result.success) {
|
|
122
|
+
const streamErrorProperties = {
|
|
123
|
+
...streamBaseProperties,
|
|
124
|
+
error_type: result.error_type,
|
|
125
|
+
error_message: result.error,
|
|
126
|
+
response_preview: result.response_preview,
|
|
127
|
+
};
|
|
128
|
+
trackEvent('analysis_run', streamErrorProperties);
|
|
129
|
+
captureError(new Error(result.error ?? 'analysis_run stream failed'), streamErrorProperties);
|
|
130
|
+
await stream.writeSSE({
|
|
131
|
+
event: 'error',
|
|
132
|
+
data: JSON.stringify({ error: result.error ?? 'Analysis failed' }),
|
|
133
|
+
});
|
|
134
|
+
}
|
|
135
|
+
else {
|
|
136
|
+
trackEvent('analysis_run', streamBaseProperties);
|
|
137
|
+
trackEvent('insight_generated', {
|
|
138
|
+
type: 'session',
|
|
139
|
+
count: result.insights.length,
|
|
140
|
+
});
|
|
141
|
+
const summaryInsight = result.insights.find(i => i.type === 'summary');
|
|
142
|
+
await stream.writeSSE({
|
|
143
|
+
event: 'complete',
|
|
144
|
+
data: JSON.stringify({
|
|
145
|
+
success: true,
|
|
146
|
+
insightCount: result.insights.length,
|
|
147
|
+
tokenUsage: result.usage,
|
|
148
|
+
suggestedTitle: summaryInsight?.title ?? null,
|
|
149
|
+
}),
|
|
150
|
+
});
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
catch (err) {
|
|
154
|
+
const message = err instanceof Error ? err.message : 'Unknown error';
|
|
155
|
+
captureError(err, { type: 'session_stream', llm_provider: llmConfig?.provider, llm_model: llmConfig?.model });
|
|
156
|
+
await stream.writeSSE({
|
|
157
|
+
event: 'error',
|
|
158
|
+
data: JSON.stringify({ error: message }),
|
|
159
|
+
}).catch(() => { });
|
|
160
|
+
}
|
|
161
|
+
});
|
|
162
|
+
});
|
|
163
|
+
// POST /api/analysis/prompt-quality
|
|
164
|
+
// Body: { sessionId: string }
|
|
165
|
+
// Runs prompt quality analysis on user messages in the session.
|
|
166
|
+
app.post('/prompt-quality', async (c) => {
|
|
167
|
+
if (!isLLMConfigured()) {
|
|
168
|
+
return c.json({
|
|
169
|
+
success: false,
|
|
170
|
+
error: 'LLM not configured. Run `code-insights config llm` to configure a provider.',
|
|
171
|
+
}, 400);
|
|
172
|
+
}
|
|
173
|
+
const body = await c.req.json();
|
|
174
|
+
if (!body.sessionId || typeof body.sessionId !== 'string') {
|
|
175
|
+
return c.json({ error: 'Missing required field: sessionId' }, 400);
|
|
176
|
+
}
|
|
177
|
+
const db = getDb();
|
|
178
|
+
const session = db.prepare(`
|
|
179
|
+
SELECT id, project_id, project_name, project_path, summary, ended_at
|
|
180
|
+
FROM sessions WHERE id = ?
|
|
181
|
+
`).get(body.sessionId);
|
|
182
|
+
if (!session) {
|
|
183
|
+
return c.json({ error: 'Session not found' }, 404);
|
|
184
|
+
}
|
|
185
|
+
const messages = db.prepare(`
|
|
186
|
+
SELECT id, session_id, type, content, thinking, tool_calls, tool_results, usage, timestamp, parent_id
|
|
187
|
+
FROM messages WHERE session_id = ? ORDER BY timestamp ASC
|
|
188
|
+
`).all(body.sessionId);
|
|
189
|
+
const llmConfig = loadLLMConfig();
|
|
190
|
+
const pqStart = Date.now();
|
|
191
|
+
const result = await analyzePromptQuality(session, messages);
|
|
192
|
+
const pqBaseProperties = {
|
|
193
|
+
type: 'prompt-quality',
|
|
194
|
+
llm_provider: llmConfig?.provider,
|
|
195
|
+
llm_model: llmConfig?.model,
|
|
196
|
+
duration_ms: Date.now() - pqStart,
|
|
197
|
+
success: result.success,
|
|
198
|
+
};
|
|
199
|
+
if (!result.success) {
|
|
200
|
+
const pqErrorProperties = {
|
|
201
|
+
...pqBaseProperties,
|
|
202
|
+
error_type: result.error_type,
|
|
203
|
+
error_message: result.error,
|
|
204
|
+
response_preview: result.response_preview,
|
|
205
|
+
};
|
|
206
|
+
trackEvent('analysis_run', pqErrorProperties);
|
|
207
|
+
captureError(new Error(result.error ?? 'prompt_quality analysis failed'), pqErrorProperties);
|
|
208
|
+
}
|
|
209
|
+
else {
|
|
210
|
+
trackEvent('analysis_run', pqBaseProperties);
|
|
211
|
+
trackEvent('insight_generated', {
|
|
212
|
+
type: 'prompt_quality',
|
|
213
|
+
count: result.insights.length,
|
|
214
|
+
});
|
|
215
|
+
}
|
|
216
|
+
return c.json(result, result.success ? 200 : 422);
|
|
217
|
+
});
|
|
218
|
+
// GET /api/analysis/prompt-quality/stream?sessionId=X
|
|
219
|
+
// SSE endpoint — streams progress events during prompt quality analysis.
|
|
220
|
+
// onProgress is non-async because analyzePromptQuality calls it without await;
|
|
221
|
+
// stream.writeSSE is fire-and-forget for progress events (non-fatal if missed).
|
|
222
|
+
app.get('/prompt-quality/stream', async (c) => {
|
|
223
|
+
if (!isLLMConfigured()) {
|
|
224
|
+
return c.json({
|
|
225
|
+
success: false,
|
|
226
|
+
error: 'LLM not configured. Run `code-insights config llm` to configure a provider.',
|
|
227
|
+
}, 400);
|
|
228
|
+
}
|
|
229
|
+
const sessionId = c.req.query('sessionId');
|
|
230
|
+
if (!sessionId) {
|
|
231
|
+
return c.json({ error: 'Missing required query param: sessionId' }, 400);
|
|
232
|
+
}
|
|
233
|
+
const db = getDb();
|
|
234
|
+
const session = db.prepare(`
|
|
235
|
+
SELECT id, project_id, project_name, project_path, summary, ended_at
|
|
236
|
+
FROM sessions WHERE id = ?
|
|
237
|
+
`).get(sessionId);
|
|
238
|
+
if (!session) {
|
|
239
|
+
return c.json({ error: 'Session not found' }, 404);
|
|
240
|
+
}
|
|
241
|
+
const messages = db.prepare(`
|
|
242
|
+
SELECT id, session_id, type, content, thinking, tool_calls, tool_results, usage, timestamp, parent_id
|
|
243
|
+
FROM messages WHERE session_id = ? ORDER BY timestamp ASC
|
|
244
|
+
`).all(sessionId);
|
|
245
|
+
const llmConfig = loadLLMConfig();
|
|
246
|
+
return streamSSE(c, async (stream) => {
|
|
247
|
+
const pqStreamStart = Date.now();
|
|
248
|
+
try {
|
|
249
|
+
const abortSignal = c.req.raw.signal;
|
|
250
|
+
await stream.writeSSE({
|
|
251
|
+
event: 'progress',
|
|
252
|
+
data: JSON.stringify({ phase: 'loading_messages', message: 'Loading messages...' }),
|
|
253
|
+
});
|
|
254
|
+
const result = await analyzePromptQuality(session, messages, {
|
|
255
|
+
signal: abortSignal,
|
|
256
|
+
onProgress: (progress) => {
|
|
257
|
+
const message = progress.phase === 'saving'
|
|
258
|
+
? 'Saving insights...'
|
|
259
|
+
: 'Analyzing prompt quality...';
|
|
260
|
+
void stream.writeSSE({
|
|
261
|
+
event: 'progress',
|
|
262
|
+
data: JSON.stringify({ ...progress, message }),
|
|
263
|
+
}).catch(() => { });
|
|
264
|
+
},
|
|
265
|
+
});
|
|
266
|
+
const pqStreamBaseProperties = {
|
|
267
|
+
type: 'prompt-quality',
|
|
268
|
+
llm_provider: llmConfig?.provider,
|
|
269
|
+
llm_model: llmConfig?.model,
|
|
270
|
+
duration_ms: Date.now() - pqStreamStart,
|
|
271
|
+
success: result.success,
|
|
272
|
+
};
|
|
273
|
+
if (!result.success) {
|
|
274
|
+
const pqStreamErrorProperties = {
|
|
275
|
+
...pqStreamBaseProperties,
|
|
276
|
+
error_type: result.error_type,
|
|
277
|
+
error_message: result.error,
|
|
278
|
+
response_preview: result.response_preview,
|
|
279
|
+
};
|
|
280
|
+
trackEvent('analysis_run', pqStreamErrorProperties);
|
|
281
|
+
captureError(new Error(result.error ?? 'prompt_quality stream failed'), pqStreamErrorProperties);
|
|
282
|
+
await stream.writeSSE({
|
|
283
|
+
event: 'error',
|
|
284
|
+
data: JSON.stringify({ error: result.error ?? 'Prompt quality analysis failed' }),
|
|
285
|
+
});
|
|
286
|
+
}
|
|
287
|
+
else {
|
|
288
|
+
trackEvent('analysis_run', pqStreamBaseProperties);
|
|
289
|
+
trackEvent('insight_generated', {
|
|
290
|
+
type: 'prompt_quality',
|
|
291
|
+
count: result.insights.length,
|
|
292
|
+
});
|
|
293
|
+
await stream.writeSSE({
|
|
294
|
+
event: 'complete',
|
|
295
|
+
data: JSON.stringify({
|
|
296
|
+
success: true,
|
|
297
|
+
insightCount: result.insights.length,
|
|
298
|
+
tokenUsage: result.usage,
|
|
299
|
+
suggestedTitle: null,
|
|
300
|
+
}),
|
|
301
|
+
});
|
|
302
|
+
}
|
|
303
|
+
}
|
|
304
|
+
catch (err) {
|
|
305
|
+
const message = err instanceof Error ? err.message : 'Unknown error';
|
|
306
|
+
captureError(err, { type: 'prompt_quality_stream', llm_provider: llmConfig?.provider, llm_model: llmConfig?.model });
|
|
307
|
+
await stream.writeSSE({
|
|
308
|
+
event: 'error',
|
|
309
|
+
data: JSON.stringify({ error: message }),
|
|
310
|
+
}).catch(() => { });
|
|
311
|
+
}
|
|
312
|
+
});
|
|
313
|
+
});
|
|
314
|
+
// POST /api/analysis/recurring
|
|
315
|
+
// Body: { projectId?: string; limit?: number }
|
|
316
|
+
// Finds recurring insight patterns across sessions.
|
|
317
|
+
app.post('/recurring', async (c) => {
|
|
318
|
+
if (!isLLMConfigured()) {
|
|
319
|
+
return c.json({
|
|
320
|
+
success: false,
|
|
321
|
+
error: 'LLM not configured. Run `code-insights config llm` to configure a provider.',
|
|
322
|
+
}, 400);
|
|
323
|
+
}
|
|
324
|
+
const body = await c.req.json();
|
|
325
|
+
const db = getDb();
|
|
326
|
+
const conditions = [];
|
|
327
|
+
const params = [];
|
|
328
|
+
if (body.projectId) {
|
|
329
|
+
conditions.push('project_id = ?');
|
|
330
|
+
params.push(body.projectId);
|
|
331
|
+
}
|
|
332
|
+
const where = conditions.length > 0 ? `WHERE ${conditions.join(' AND ')}` : '';
|
|
333
|
+
const limit = Math.min(parseIntParam(String(body.limit ?? ''), 200), 200);
|
|
334
|
+
const insights = db.prepare(`
|
|
335
|
+
SELECT id, type, title, summary, project_name, session_id
|
|
336
|
+
FROM insights
|
|
337
|
+
${where}
|
|
338
|
+
ORDER BY timestamp DESC
|
|
339
|
+
LIMIT ?
|
|
340
|
+
`).all(...params, limit);
|
|
341
|
+
const llmConfig = loadLLMConfig();
|
|
342
|
+
const recurringStart = Date.now();
|
|
343
|
+
const result = await findRecurringInsights(insights);
|
|
344
|
+
const recurringBaseProperties = {
|
|
345
|
+
type: 'recurring',
|
|
346
|
+
llm_provider: llmConfig?.provider,
|
|
347
|
+
llm_model: llmConfig?.model,
|
|
348
|
+
duration_ms: Date.now() - recurringStart,
|
|
349
|
+
success: result.success,
|
|
350
|
+
};
|
|
351
|
+
if (!result.success) {
|
|
352
|
+
const recurringErrorProperties = {
|
|
353
|
+
...recurringBaseProperties,
|
|
354
|
+
error_type: 'api_error',
|
|
355
|
+
error_message: result.error,
|
|
356
|
+
};
|
|
357
|
+
trackEvent('analysis_run', recurringErrorProperties);
|
|
358
|
+
captureError(new Error(result.error ?? 'recurring insights failed'), recurringErrorProperties);
|
|
359
|
+
}
|
|
360
|
+
else {
|
|
361
|
+
trackEvent('analysis_run', recurringBaseProperties);
|
|
362
|
+
trackEvent('insight_generated', {
|
|
363
|
+
type: 'recurring',
|
|
364
|
+
count: result.groups.length,
|
|
365
|
+
});
|
|
366
|
+
}
|
|
367
|
+
return c.json(result, result.success ? 200 : 422);
|
|
368
|
+
});
|
|
369
|
+
export default app;
|
|
370
|
+
//# sourceMappingURL=analysis.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"analysis.js","sourceRoot":"","sources":["../../src/routes/analysis.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"analytics.d.ts","sourceRoot":"","sources":["../../src/routes/analytics.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,MAAM,MAAM,CAAC;AAG5B,QAAA,MAAM,GAAG,4EAAa,CAAC;AA6DvB,eAAe,GAAG,CAAC"}
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import { Hono } from 'hono';
|
|
2
|
+
import { getDb } from '@code-insights/cli/db/client';
|
|
3
|
+
const app = new Hono();
|
|
4
|
+
const VALID_RANGES = ['7d', '30d', '90d', 'all'];
|
|
5
|
+
// Dashboard overview stats for a given time range (e.g. ?range=7d|30d|90d|all)
|
|
6
|
+
app.get('/dashboard', (c) => {
|
|
7
|
+
const db = getDb();
|
|
8
|
+
const { range = '7d' } = c.req.query();
|
|
9
|
+
if (!VALID_RANGES.includes(range)) {
|
|
10
|
+
return c.json({ error: `Invalid range. Must be one of: ${VALID_RANGES.join(', ')}` }, 400);
|
|
11
|
+
}
|
|
12
|
+
let periodStart = null;
|
|
13
|
+
const now = new Date();
|
|
14
|
+
if (range === '7d') {
|
|
15
|
+
periodStart = new Date(now.getTime() - 7 * 24 * 60 * 60 * 1000).toISOString();
|
|
16
|
+
}
|
|
17
|
+
else if (range === '30d') {
|
|
18
|
+
periodStart = new Date(now.getTime() - 30 * 24 * 60 * 60 * 1000).toISOString();
|
|
19
|
+
}
|
|
20
|
+
else if (range === '90d') {
|
|
21
|
+
periodStart = new Date(now.getTime() - 90 * 24 * 60 * 60 * 1000).toISOString();
|
|
22
|
+
}
|
|
23
|
+
const where = periodStart ? 'WHERE started_at >= ?' : '';
|
|
24
|
+
const params = periodStart ? [periodStart] : [];
|
|
25
|
+
const stats = db.prepare(`
|
|
26
|
+
SELECT
|
|
27
|
+
COUNT(*) AS session_count,
|
|
28
|
+
COUNT(DISTINCT project_id) AS active_projects,
|
|
29
|
+
SUM(message_count) AS total_messages,
|
|
30
|
+
SUM(tool_call_count) AS total_tool_calls,
|
|
31
|
+
CAST(COALESCE(SUM(
|
|
32
|
+
CASE WHEN ended_at IS NOT NULL AND started_at IS NOT NULL
|
|
33
|
+
THEN (julianday(ended_at) - julianday(started_at)) * 1440
|
|
34
|
+
ELSE 0
|
|
35
|
+
END
|
|
36
|
+
), 0) AS INTEGER) AS total_duration_min,
|
|
37
|
+
SUM(total_input_tokens) AS total_input_tokens,
|
|
38
|
+
SUM(total_output_tokens) AS total_output_tokens,
|
|
39
|
+
SUM(cache_creation_tokens) AS cache_creation_tokens,
|
|
40
|
+
SUM(cache_read_tokens) AS cache_read_tokens,
|
|
41
|
+
SUM(estimated_cost_usd) AS estimated_cost_usd
|
|
42
|
+
FROM sessions ${where}
|
|
43
|
+
`).get(...params);
|
|
44
|
+
return c.json({ range, stats });
|
|
45
|
+
});
|
|
46
|
+
// Global cumulative usage stats
|
|
47
|
+
app.get('/usage', (c) => {
|
|
48
|
+
const db = getDb();
|
|
49
|
+
const stats = db.prepare(`
|
|
50
|
+
SELECT total_input_tokens, total_output_tokens, cache_creation_tokens,
|
|
51
|
+
cache_read_tokens, estimated_cost_usd, sessions_with_usage, last_updated_at
|
|
52
|
+
FROM usage_stats WHERE id = 1
|
|
53
|
+
`).get();
|
|
54
|
+
return c.json({ stats: stats ?? null });
|
|
55
|
+
});
|
|
56
|
+
export default app;
|
|
57
|
+
//# sourceMappingURL=analytics.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"analytics.js","sourceRoot":"","sources":["../../src/routes/analytics.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,MAAM,MAAM,CAAC;AAC5B,OAAO,EAAE,KAAK,EAAE,MAAM,8BAA8B,CAAC;AAErD,MAAM,GAAG,GAAG,IAAI,IAAI,EAAE,CAAC;AAEvB,MAAM,YAAY,GAAG,CAAC,IAAI,EAAE,KAAK,EAAE,KAAK,EAAE,KAAK,CAAU,CAAC;AAG1D,+EAA+E;AAC/E,GAAG,CAAC,GAAG,CAAC,YAAY,EAAE,CAAC,CAAC,EAAE,EAAE;IAC1B,MAAM,EAAE,GAAG,KAAK,EAAE,CAAC;IACnB,MAAM,EAAE,KAAK,GAAG,IAAI,EAAE,GAAG,CAAC,CAAC,GAAG,CAAC,KAAK,EAAE,CAAC;IAEvC,IAAI,CAAC,YAAY,CAAC,QAAQ,CAAC,KAAc,CAAC,EAAE,CAAC;QAC3C,OAAO,CAAC,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,kCAAkC,YAAY,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,EAAE,EAAE,GAAG,CAAC,CAAC;IAC7F,CAAC;IAED,IAAI,WAAW,GAAkB,IAAI,CAAC;IACtC,MAAM,GAAG,GAAG,IAAI,IAAI,EAAE,CAAC;IACvB,IAAI,KAAK,KAAK,IAAI,EAAE,CAAC;QACnB,WAAW,GAAG,IAAI,IAAI,CAAC,GAAG,CAAC,OAAO,EAAE,GAAG,CAAC,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,IAAI,CAAC,CAAC,WAAW,EAAE,CAAC;IAChF,CAAC;SAAM,IAAI,KAAK,KAAK,KAAK,EAAE,CAAC;QAC3B,WAAW,GAAG,IAAI,IAAI,CAAC,GAAG,CAAC,OAAO,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,IAAI,CAAC,CAAC,WAAW,EAAE,CAAC;IACjF,CAAC;SAAM,IAAI,KAAK,KAAK,KAAK,EAAE,CAAC;QAC3B,WAAW,GAAG,IAAI,IAAI,CAAC,GAAG,CAAC,OAAO,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,IAAI,CAAC,CAAC,WAAW,EAAE,CAAC;IACjF,CAAC;IAED,MAAM,KAAK,GAAG,WAAW,CAAC,CAAC,CAAC,uBAAuB,CAAC,CAAC,CAAC,EAAE,CAAC;IACzD,MAAM,MAAM,GAAG,WAAW,CAAC,CAAC,CAAC,CAAC,WAAW,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;IAEhD,MAAM,KAAK,GAAG,EAAE,CAAC,OAAO,CAAC;;;;;;;;;;;;;;;;;oBAiBP,KAAK;GACtB,CAAC,CAAC,GAAG,CAAC,GAAG,MAAM,CAAC,CAAC;IAElB,OAAO,CAAC,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,KAAK,EAAE,CAAC,CAAC;AAClC,CAAC,CAAC,CAAC;AAEH,gCAAgC;AAChC,GAAG,CAAC,GAAG,CAAC,QAAQ,EAAE,CAAC,CAAC,EAAE,EAAE;IACtB,MAAM,EAAE,GAAG,KAAK,EAAE,CAAC;IACnB,MAAM,KAAK,GAAG,EAAE,CAAC,OAAO,CAAC;;;;GAIxB,CAAC,CAAC,GAAG,EAAE,CAAC;IACT,OAAO,CAAC,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,KAAK,IAAI,IAAI,EAAE,CAAC,CAAC;AAC1C,CAAC,CAAC,CAAC;AAEH,eAAe,GAAG,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../../src/routes/config.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,MAAM,MAAM,CAAC;AAM5B,QAAA,MAAM,GAAG,4EAAa,CAAC;AAiIvB,eAAe,GAAG,CAAC"}
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
import { Hono } from 'hono';
|
|
2
|
+
import { loadConfig, saveConfig } from '@code-insights/cli/utils/config';
|
|
3
|
+
import { loadLLMConfig, testLLMConfig } from '../llm/client.js';
|
|
4
|
+
import { discoverOllamaModels } from '../llm/providers/ollama.js';
|
|
5
|
+
const app = new Hono();
|
|
6
|
+
const VALID_PROVIDERS = ['openai', 'anthropic', 'gemini', 'ollama'];
|
|
7
|
+
function maskApiKey(key) {
|
|
8
|
+
if (!key || key.length < 8)
|
|
9
|
+
return key ? '***' : undefined;
|
|
10
|
+
return key.slice(0, 4) + '...' + key.slice(-4);
|
|
11
|
+
}
|
|
12
|
+
// GET /api/config/llm — return full config (API key masked)
|
|
13
|
+
app.get('/llm', (c) => {
|
|
14
|
+
const config = loadConfig();
|
|
15
|
+
const llm = config?.dashboard?.llm;
|
|
16
|
+
return c.json({
|
|
17
|
+
dashboardPort: config?.dashboard?.port ?? 7890,
|
|
18
|
+
provider: llm?.provider,
|
|
19
|
+
model: llm?.model,
|
|
20
|
+
apiKey: maskApiKey(llm?.apiKey),
|
|
21
|
+
baseUrl: llm?.baseUrl,
|
|
22
|
+
});
|
|
23
|
+
});
|
|
24
|
+
// PUT /api/config/llm — update dashboard port and/or LLM config
|
|
25
|
+
app.put('/llm', async (c) => {
|
|
26
|
+
const body = await c.req.json();
|
|
27
|
+
const config = loadConfig() ?? {
|
|
28
|
+
sync: { claudeDir: '', excludeProjects: [] },
|
|
29
|
+
};
|
|
30
|
+
let changed = false;
|
|
31
|
+
// Update dashboard port if provided
|
|
32
|
+
if (body.dashboardPort !== undefined) {
|
|
33
|
+
const port = body.dashboardPort;
|
|
34
|
+
if (typeof port !== 'number' || !Number.isInteger(port) || port < 1 || port > 65535) {
|
|
35
|
+
return c.json({ error: 'dashboardPort must be an integer between 1 and 65535' }, 400);
|
|
36
|
+
}
|
|
37
|
+
config.dashboard = { ...config.dashboard, port };
|
|
38
|
+
changed = true;
|
|
39
|
+
}
|
|
40
|
+
// Update LLM config if any LLM field is provided
|
|
41
|
+
const hasLLMField = body.provider !== undefined || body.model !== undefined ||
|
|
42
|
+
body.apiKey !== undefined || body.baseUrl !== undefined;
|
|
43
|
+
if (hasLLMField) {
|
|
44
|
+
if (body.provider !== undefined && !VALID_PROVIDERS.includes(body.provider)) {
|
|
45
|
+
return c.json({ error: `provider must be one of: ${VALID_PROVIDERS.join(', ')}` }, 400);
|
|
46
|
+
}
|
|
47
|
+
const existingLlm = config.dashboard?.llm ?? {};
|
|
48
|
+
const updatedLlm = {
|
|
49
|
+
provider: body.provider ?? existingLlm.provider ?? 'ollama',
|
|
50
|
+
model: body.model ?? existingLlm.model ?? '',
|
|
51
|
+
// Preserve existing API key if not provided in update
|
|
52
|
+
...(body.apiKey !== undefined
|
|
53
|
+
? { apiKey: body.apiKey || undefined }
|
|
54
|
+
: existingLlm.apiKey !== undefined ? { apiKey: existingLlm.apiKey } : {}),
|
|
55
|
+
...(body.baseUrl !== undefined
|
|
56
|
+
? { baseUrl: body.baseUrl || undefined }
|
|
57
|
+
: existingLlm.baseUrl !== undefined ? { baseUrl: existingLlm.baseUrl } : {}),
|
|
58
|
+
};
|
|
59
|
+
if (!updatedLlm.model) {
|
|
60
|
+
return c.json({ error: 'model is required when setting LLM config' }, 400);
|
|
61
|
+
}
|
|
62
|
+
config.dashboard = { ...config.dashboard, llm: updatedLlm };
|
|
63
|
+
changed = true;
|
|
64
|
+
}
|
|
65
|
+
if (!changed) {
|
|
66
|
+
return c.json({ ok: true });
|
|
67
|
+
}
|
|
68
|
+
saveConfig(config);
|
|
69
|
+
return c.json({ ok: true });
|
|
70
|
+
});
|
|
71
|
+
// POST /api/config/llm/test — validate LLM credentials with a test call
|
|
72
|
+
app.post('/llm/test', async (c) => {
|
|
73
|
+
// Allow testing with body config or existing saved config
|
|
74
|
+
let testConfig = null;
|
|
75
|
+
try {
|
|
76
|
+
const body = await c.req.json();
|
|
77
|
+
if (body.provider && body.model) {
|
|
78
|
+
testConfig = {
|
|
79
|
+
provider: body.provider,
|
|
80
|
+
model: body.model,
|
|
81
|
+
...(body.apiKey ? { apiKey: body.apiKey } : {}),
|
|
82
|
+
...(body.baseUrl ? { baseUrl: body.baseUrl } : {}),
|
|
83
|
+
};
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
catch {
|
|
87
|
+
// No body or invalid JSON — use existing config
|
|
88
|
+
}
|
|
89
|
+
if (!testConfig) {
|
|
90
|
+
testConfig = loadLLMConfig();
|
|
91
|
+
}
|
|
92
|
+
if (!testConfig) {
|
|
93
|
+
return c.json({
|
|
94
|
+
success: false,
|
|
95
|
+
error: 'No LLM config found. Run `code-insights config llm` or provide config in request body.',
|
|
96
|
+
}, 400);
|
|
97
|
+
}
|
|
98
|
+
const result = await testLLMConfig(testConfig);
|
|
99
|
+
return c.json(result, result.success ? 200 : 422);
|
|
100
|
+
});
|
|
101
|
+
// GET /api/config/llm/ollama-models — return locally available Ollama models
|
|
102
|
+
app.get('/llm/ollama-models', async (c) => {
|
|
103
|
+
const baseUrl = c.req.query('baseUrl');
|
|
104
|
+
const models = await discoverOllamaModels(baseUrl);
|
|
105
|
+
return c.json({ models });
|
|
106
|
+
});
|
|
107
|
+
export default app;
|
|
108
|
+
//# sourceMappingURL=config.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"config.js","sourceRoot":"","sources":["../../src/routes/config.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"export.d.ts","sourceRoot":"","sources":["../../src/routes/export.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,MAAM,MAAM,CAAC;AAI5B,QAAA,MAAM,GAAG,4EAAa,CAAC;AAyDvB,eAAe,GAAG,CAAC"}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import { Hono } from 'hono';
|
|
2
|
+
import { getDb } from '@code-insights/cli/db/client';
|
|
3
|
+
import { trackEvent } from '@code-insights/cli/utils/telemetry';
|
|
4
|
+
const app = new Hono();
|
|
5
|
+
// POST /api/export/markdown — export sessions/insights as markdown
|
|
6
|
+
app.post('/markdown', async (c) => {
|
|
7
|
+
const db = getDb();
|
|
8
|
+
const body = await c.req.json();
|
|
9
|
+
const { sessionIds, projectId } = body;
|
|
10
|
+
if (sessionIds !== undefined && !Array.isArray(sessionIds)) {
|
|
11
|
+
return c.json({ error: 'sessionIds must be an array' }, 400);
|
|
12
|
+
}
|
|
13
|
+
if (sessionIds && sessionIds.some((id) => typeof id !== 'string')) {
|
|
14
|
+
return c.json({ error: 'sessionIds must contain only strings' }, 400);
|
|
15
|
+
}
|
|
16
|
+
if (sessionIds && sessionIds.length > 100) {
|
|
17
|
+
return c.json({ error: 'Maximum 100 session IDs per export request' }, 400);
|
|
18
|
+
}
|
|
19
|
+
let sessions;
|
|
20
|
+
if (sessionIds && sessionIds.length > 0) {
|
|
21
|
+
const placeholders = sessionIds.map(() => '?').join(', ');
|
|
22
|
+
sessions = db.prepare(`SELECT id, project_name, generated_title, custom_title, started_at, ended_at,
|
|
23
|
+
message_count, estimated_cost_usd, session_character, source_tool
|
|
24
|
+
FROM sessions WHERE id IN (${placeholders}) ORDER BY started_at DESC`).all(...sessionIds);
|
|
25
|
+
}
|
|
26
|
+
else if (projectId) {
|
|
27
|
+
sessions = db.prepare(`SELECT id, project_name, generated_title, custom_title, started_at, ended_at,
|
|
28
|
+
message_count, estimated_cost_usd, session_character, source_tool
|
|
29
|
+
FROM sessions WHERE project_id = ? ORDER BY started_at DESC`).all(projectId);
|
|
30
|
+
}
|
|
31
|
+
else {
|
|
32
|
+
return c.json({ error: 'sessionIds or projectId required' }, 400);
|
|
33
|
+
}
|
|
34
|
+
const lines = ['# Code Insights Export', ''];
|
|
35
|
+
for (const s of sessions) {
|
|
36
|
+
const title = (s.custom_title ?? s.generated_title ?? s.id);
|
|
37
|
+
lines.push(`## ${title}`);
|
|
38
|
+
lines.push(`- **Started:** ${s.started_at}`);
|
|
39
|
+
lines.push(`- **Ended:** ${s.ended_at}`);
|
|
40
|
+
lines.push(`- **Messages:** ${s.message_count}`);
|
|
41
|
+
if (s.estimated_cost_usd != null) {
|
|
42
|
+
lines.push(`- **Cost:** $${Number(s.estimated_cost_usd).toFixed(4)}`);
|
|
43
|
+
}
|
|
44
|
+
lines.push('');
|
|
45
|
+
}
|
|
46
|
+
const markdown = lines.join('\n');
|
|
47
|
+
trackEvent('export_run', { format: 'markdown', session_count: sessions.length, success: true });
|
|
48
|
+
c.header('Content-Type', 'text/markdown');
|
|
49
|
+
return c.body(markdown);
|
|
50
|
+
});
|
|
51
|
+
export default app;
|
|
52
|
+
//# sourceMappingURL=export.js.map
|