@code-insights/cli 3.6.1 → 4.0.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/CHANGELOG.md +46 -0
- package/README.md +40 -4
- package/dashboard-dist/assets/index-D1JDyyu5.js +660 -0
- package/dashboard-dist/assets/index-kwbCW1n2.css +1 -0
- package/dashboard-dist/index.html +2 -2
- package/dist/commands/reflect.d.ts +3 -0
- package/dist/commands/reflect.d.ts.map +1 -0
- package/dist/commands/reflect.js +457 -0
- package/dist/commands/reflect.js.map +1 -0
- package/dist/commands/reset.d.ts.map +1 -1
- package/dist/commands/reset.js +3 -1
- package/dist/commands/reset.js.map +1 -1
- package/dist/commands/stats/actions/patterns.d.ts +3 -0
- package/dist/commands/stats/actions/patterns.d.ts.map +1 -0
- package/dist/commands/stats/actions/patterns.js +140 -0
- package/dist/commands/stats/actions/patterns.js.map +1 -0
- package/dist/commands/stats/data/aggregation-helpers.d.ts +23 -0
- package/dist/commands/stats/data/aggregation-helpers.d.ts.map +1 -0
- package/dist/commands/stats/data/aggregation-helpers.js +128 -0
- package/dist/commands/stats/data/aggregation-helpers.js.map +1 -0
- package/dist/commands/stats/data/aggregation.d.ts +3 -35
- package/dist/commands/stats/data/aggregation.d.ts.map +1 -1
- package/dist/commands/stats/data/aggregation.js +8 -290
- package/dist/commands/stats/data/aggregation.js.map +1 -1
- package/dist/commands/stats/data/time-series.d.ts +24 -0
- package/dist/commands/stats/data/time-series.d.ts.map +1 -0
- package/dist/commands/stats/data/time-series.js +162 -0
- package/dist/commands/stats/data/time-series.js.map +1 -0
- package/dist/commands/stats/index.d.ts.map +1 -1
- package/dist/commands/stats/index.js +7 -1
- package/dist/commands/stats/index.js.map +1 -1
- package/dist/commands/sync.d.ts +19 -0
- package/dist/commands/sync.d.ts.map +1 -1
- package/dist/commands/sync.js +67 -1
- package/dist/commands/sync.js.map +1 -1
- package/dist/constants/llm-providers.js +1 -1
- package/dist/constants/llm-providers.js.map +1 -1
- package/dist/db/client.d.ts +7 -0
- package/dist/db/client.d.ts.map +1 -1
- package/dist/db/client.js +11 -1
- package/dist/db/client.js.map +1 -1
- package/dist/db/migrate.d.ts +10 -1
- package/dist/db/migrate.d.ts.map +1 -1
- package/dist/db/migrate.js +96 -0
- package/dist/db/migrate.js.map +1 -1
- package/dist/db/read.d.ts +5 -0
- package/dist/db/read.d.ts.map +1 -1
- package/dist/db/read.js +20 -3
- package/dist/db/read.js.map +1 -1
- package/dist/db/schema.d.ts +1 -1
- package/dist/db/schema.js +2 -2
- package/dist/db/schema.js.map +1 -1
- package/dist/db/write.d.ts.map +1 -1
- package/dist/db/write.js +8 -2
- package/dist/db/write.js.map +1 -1
- package/dist/index.js +39 -3
- package/dist/index.js.map +1 -1
- package/dist/parser/jsonl.d.ts +19 -1
- package/dist/parser/jsonl.d.ts.map +1 -1
- package/dist/parser/jsonl.js +109 -3
- package/dist/parser/jsonl.js.map +1 -1
- package/dist/providers/codex.js +4 -1
- package/dist/providers/codex.js.map +1 -1
- package/dist/providers/copilot-cli.js +3 -0
- package/dist/providers/copilot-cli.js.map +1 -1
- package/dist/providers/copilot.js +3 -0
- package/dist/providers/copilot.js.map +1 -1
- package/dist/providers/cursor.js +3 -0
- package/dist/providers/cursor.js.map +1 -1
- package/dist/types.d.ts +109 -0
- package/dist/types.d.ts.map +1 -1
- package/dist/utils/date-utils.d.ts +6 -0
- package/dist/utils/date-utils.d.ts.map +1 -0
- package/dist/utils/date-utils.js +26 -0
- package/dist/utils/date-utils.js.map +1 -0
- package/dist/utils/telemetry.d.ts +1 -1
- package/dist/utils/telemetry.d.ts.map +1 -1
- package/dist/utils/telemetry.js +1 -1
- package/dist/utils/telemetry.js.map +1 -1
- package/package.json +2 -1
- package/server-dist/export/agent-rules.d.ts.map +1 -1
- package/server-dist/export/agent-rules.js +15 -4
- package/server-dist/export/agent-rules.js.map +1 -1
- package/server-dist/export/knowledge-base.d.ts.map +1 -1
- package/server-dist/export/knowledge-base.js +30 -4
- package/server-dist/export/knowledge-base.js.map +1 -1
- package/server-dist/index.d.ts.map +1 -1
- package/server-dist/index.js +4 -0
- package/server-dist/index.js.map +1 -1
- package/server-dist/llm/analysis-db.d.ts +51 -0
- package/server-dist/llm/analysis-db.d.ts.map +1 -0
- package/server-dist/llm/analysis-db.js +208 -0
- package/server-dist/llm/analysis-db.js.map +1 -0
- package/server-dist/llm/analysis-internal.d.ts +36 -0
- package/server-dist/llm/analysis-internal.d.ts.map +1 -0
- package/server-dist/llm/analysis-internal.js +23 -0
- package/server-dist/llm/analysis-internal.js.map +1 -0
- package/server-dist/llm/analysis-pricing.d.ts +25 -0
- package/server-dist/llm/analysis-pricing.d.ts.map +1 -0
- package/server-dist/llm/analysis-pricing.js +74 -0
- package/server-dist/llm/analysis-pricing.js.map +1 -0
- package/server-dist/llm/analysis-usage-db.d.ts +45 -0
- package/server-dist/llm/analysis-usage-db.d.ts.map +1 -0
- package/server-dist/llm/analysis-usage-db.js +35 -0
- package/server-dist/llm/analysis-usage-db.js.map +1 -0
- package/server-dist/llm/analysis.d.ts +9 -79
- package/server-dist/llm/analysis.d.ts.map +1 -1
- package/server-dist/llm/analysis.js +119 -375
- package/server-dist/llm/analysis.js.map +1 -1
- package/server-dist/llm/facet-extraction.d.ts +14 -0
- package/server-dist/llm/facet-extraction.d.ts.map +1 -0
- package/server-dist/llm/facet-extraction.js +91 -0
- package/server-dist/llm/facet-extraction.js.map +1 -0
- package/server-dist/llm/friction-normalize.d.ts +16 -0
- package/server-dist/llm/friction-normalize.d.ts.map +1 -0
- package/server-dist/llm/friction-normalize.js +54 -0
- package/server-dist/llm/friction-normalize.js.map +1 -0
- package/server-dist/llm/index.d.ts +3 -2
- package/server-dist/llm/index.d.ts.map +1 -1
- package/server-dist/llm/index.js +1 -1
- package/server-dist/llm/index.js.map +1 -1
- package/server-dist/llm/message-format.d.ts +32 -0
- package/server-dist/llm/message-format.d.ts.map +1 -0
- package/server-dist/llm/message-format.js +129 -0
- package/server-dist/llm/message-format.js.map +1 -0
- package/server-dist/llm/normalize-utils.d.ts +22 -0
- package/server-dist/llm/normalize-utils.d.ts.map +1 -0
- package/server-dist/llm/normalize-utils.js +71 -0
- package/server-dist/llm/normalize-utils.js.map +1 -0
- package/server-dist/llm/pattern-normalize.d.ts +19 -0
- package/server-dist/llm/pattern-normalize.d.ts.map +1 -0
- package/server-dist/llm/pattern-normalize.js +90 -0
- package/server-dist/llm/pattern-normalize.js.map +1 -0
- package/server-dist/llm/prompt-constants.d.ts +9 -0
- package/server-dist/llm/prompt-constants.d.ts.map +1 -0
- package/server-dist/llm/prompt-constants.js +169 -0
- package/server-dist/llm/prompt-constants.js.map +1 -0
- package/server-dist/llm/prompt-quality-analysis.d.ts +8 -0
- package/server-dist/llm/prompt-quality-analysis.d.ts.map +1 -0
- package/server-dist/llm/prompt-quality-analysis.js +133 -0
- package/server-dist/llm/prompt-quality-analysis.js.map +1 -0
- package/server-dist/llm/prompt-quality-normalize.d.ts +26 -0
- package/server-dist/llm/prompt-quality-normalize.d.ts.map +1 -0
- package/server-dist/llm/prompt-quality-normalize.js +116 -0
- package/server-dist/llm/prompt-quality-normalize.js.map +1 -0
- package/server-dist/llm/prompt-types.d.ts +124 -0
- package/server-dist/llm/prompt-types.d.ts.map +1 -0
- package/server-dist/llm/prompt-types.js +4 -0
- package/server-dist/llm/prompt-types.js.map +1 -0
- package/server-dist/llm/prompts.d.ts +57 -100
- package/server-dist/llm/prompts.d.ts.map +1 -1
- package/server-dist/llm/prompts.js +606 -232
- package/server-dist/llm/prompts.js.map +1 -1
- package/server-dist/llm/providers/anthropic.d.ts.map +1 -1
- package/server-dist/llm/providers/anthropic.js +12 -0
- package/server-dist/llm/providers/anthropic.js.map +1 -1
- package/server-dist/llm/providers/gemini.d.ts.map +1 -1
- package/server-dist/llm/providers/gemini.js +10 -2
- package/server-dist/llm/providers/gemini.js.map +1 -1
- package/server-dist/llm/providers/ollama.d.ts.map +1 -1
- package/server-dist/llm/providers/ollama.js +3 -1
- package/server-dist/llm/providers/ollama.js.map +1 -1
- package/server-dist/llm/providers/openai.d.ts.map +1 -1
- package/server-dist/llm/providers/openai.js +4 -1
- package/server-dist/llm/providers/openai.js.map +1 -1
- package/server-dist/llm/recurring-insights.d.ts +26 -0
- package/server-dist/llm/recurring-insights.d.ts.map +1 -0
- package/server-dist/llm/recurring-insights.js +119 -0
- package/server-dist/llm/recurring-insights.js.map +1 -0
- package/server-dist/llm/reflect-prompts.d.ts +55 -0
- package/server-dist/llm/reflect-prompts.d.ts.map +1 -0
- package/server-dist/llm/reflect-prompts.js +151 -0
- package/server-dist/llm/reflect-prompts.js.map +1 -0
- package/server-dist/llm/response-parsers.d.ts +8 -0
- package/server-dist/llm/response-parsers.d.ts.map +1 -0
- package/server-dist/llm/response-parsers.js +151 -0
- package/server-dist/llm/response-parsers.js.map +1 -0
- package/server-dist/llm/types.d.ts +23 -1
- package/server-dist/llm/types.d.ts.map +1 -1
- package/server-dist/llm/types.js +10 -1
- package/server-dist/llm/types.js.map +1 -1
- package/server-dist/routes/analysis.d.ts.map +1 -1
- package/server-dist/routes/analysis.js +107 -282
- package/server-dist/routes/analysis.js.map +1 -1
- package/server-dist/routes/analytics.d.ts.map +1 -1
- package/server-dist/routes/analytics.js +3 -1
- package/server-dist/routes/analytics.js.map +1 -1
- package/server-dist/routes/export.d.ts.map +1 -1
- package/server-dist/routes/export.js +19 -27
- package/server-dist/routes/export.js.map +1 -1
- package/server-dist/routes/facets.d.ts +4 -0
- package/server-dist/routes/facets.d.ts.map +1 -0
- package/server-dist/routes/facets.js +208 -0
- package/server-dist/routes/facets.js.map +1 -0
- package/server-dist/routes/insights.d.ts.map +1 -1
- package/server-dist/routes/insights.js +12 -11
- package/server-dist/routes/insights.js.map +1 -1
- package/server-dist/routes/reflect.d.ts +4 -0
- package/server-dist/routes/reflect.d.ts.map +1 -0
- package/server-dist/routes/reflect.js +332 -0
- package/server-dist/routes/reflect.js.map +1 -0
- package/server-dist/routes/route-helpers.d.ts +124 -0
- package/server-dist/routes/route-helpers.d.ts.map +1 -0
- package/server-dist/routes/route-helpers.js +242 -0
- package/server-dist/routes/route-helpers.js.map +1 -0
- package/server-dist/routes/sessions.d.ts.map +1 -1
- package/server-dist/routes/sessions.js +29 -5
- package/server-dist/routes/sessions.js.map +1 -1
- package/server-dist/routes/shared-aggregation.d.ts +82 -0
- package/server-dist/routes/shared-aggregation.d.ts.map +1 -0
- package/server-dist/routes/shared-aggregation.js +384 -0
- package/server-dist/routes/shared-aggregation.js.map +1 -0
- package/dashboard-dist/assets/index-BaKju1iW.js +0 -607
- package/dashboard-dist/assets/index-_SWpRg6C.css +0 -1
|
@@ -0,0 +1,332 @@
|
|
|
1
|
+
import { Hono } from 'hono';
|
|
2
|
+
import { streamSSE } from 'hono/streaming';
|
|
3
|
+
import { getDb } from '@code-insights/cli/db/client';
|
|
4
|
+
import { jsonrepair } from 'jsonrepair';
|
|
5
|
+
import { createLLMClient } from '../llm/client.js';
|
|
6
|
+
import { requireLLM } from './route-helpers.js';
|
|
7
|
+
import { extractJsonPayload } from '../llm/response-parsers.js';
|
|
8
|
+
import { FRICTION_WINS_SYSTEM_PROMPT, generateFrictionWinsPrompt, RULES_SKILLS_SYSTEM_PROMPT, generateRulesSkillsPrompt, WORKING_STYLE_SYSTEM_PROMPT, generateWorkingStylePrompt, } from '../llm/reflect-prompts.js';
|
|
9
|
+
import { buildWhereClause, buildPeriodFilter, parseIsoWeek, formatIsoWeek, getAggregatedData } from './shared-aggregation.js';
|
|
10
|
+
const app = new Hono();
|
|
11
|
+
const MIN_FACETS_FOR_REFLECT = 8;
|
|
12
|
+
const ALL_SECTIONS = ['friction-wins', 'rules-skills', 'working-style'];
|
|
13
|
+
// Parse LLM JSON response wrapped in <json>...</json> tags with jsonrepair fallback.
|
|
14
|
+
function parseLLMJson(response) {
|
|
15
|
+
const payload = extractJsonPayload(response);
|
|
16
|
+
if (!payload)
|
|
17
|
+
return null;
|
|
18
|
+
try {
|
|
19
|
+
return JSON.parse(payload);
|
|
20
|
+
}
|
|
21
|
+
catch {
|
|
22
|
+
try {
|
|
23
|
+
return JSON.parse(jsonrepair(payload));
|
|
24
|
+
}
|
|
25
|
+
catch {
|
|
26
|
+
return null;
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
// Detect the dominant source tool from the database to target artifact generation.
|
|
31
|
+
function detectTargetTool(db) {
|
|
32
|
+
const row = db.prepare(`SELECT source_tool, COUNT(*) as count FROM sessions WHERE deleted_at IS NULL GROUP BY source_tool ORDER BY count DESC LIMIT 1`).get();
|
|
33
|
+
return row?.source_tool || 'claude-code';
|
|
34
|
+
}
|
|
35
|
+
// POST /api/reflect/generate
|
|
36
|
+
// Body: { sections?: ReflectSection[], period?: string, project?: string, source?: string }
|
|
37
|
+
// SSE endpoint: aggregates facets in code, then calls synthesis prompts for each section.
|
|
38
|
+
// Streams progress events so the UI can show phase-by-phase progress.
|
|
39
|
+
app.post('/generate', requireLLM(), async (c) => {
|
|
40
|
+
const body = await c.req.json();
|
|
41
|
+
const sections = body.sections && body.sections.length > 0 ? body.sections : ALL_SECTIONS;
|
|
42
|
+
const period = body.period || '30d';
|
|
43
|
+
const db = getDb();
|
|
44
|
+
const { where, params } = buildWhereClause(period, body.project, body.source);
|
|
45
|
+
return streamSSE(c, async (stream) => {
|
|
46
|
+
const abortSignal = c.req.raw.signal;
|
|
47
|
+
try {
|
|
48
|
+
await stream.writeSSE({
|
|
49
|
+
event: 'progress',
|
|
50
|
+
data: JSON.stringify({ phase: 'aggregating', message: 'Aggregating facets...' }),
|
|
51
|
+
});
|
|
52
|
+
const aggregated = getAggregatedData(db, where, params, body.project, body.source);
|
|
53
|
+
if (aggregated.totalSessions === 0) {
|
|
54
|
+
await stream.writeSSE({
|
|
55
|
+
event: 'error',
|
|
56
|
+
data: JSON.stringify({ error: 'No sessions with facets found. Run analysis first.' }),
|
|
57
|
+
});
|
|
58
|
+
return;
|
|
59
|
+
}
|
|
60
|
+
if (aggregated.totalSessions < MIN_FACETS_FOR_REFLECT) {
|
|
61
|
+
await stream.writeSSE({
|
|
62
|
+
event: 'error',
|
|
63
|
+
data: JSON.stringify({
|
|
64
|
+
error: `Need at least ${MIN_FACETS_FOR_REFLECT} analyzed sessions for meaningful pattern synthesis. Currently have ${aggregated.totalSessions}. Run session analysis on more sessions first.`,
|
|
65
|
+
code: 'INSUFFICIENT_FACETS',
|
|
66
|
+
current: aggregated.totalSessions,
|
|
67
|
+
required: MIN_FACETS_FOR_REFLECT,
|
|
68
|
+
}),
|
|
69
|
+
});
|
|
70
|
+
return;
|
|
71
|
+
}
|
|
72
|
+
const client = createLLMClient();
|
|
73
|
+
const results = {};
|
|
74
|
+
const targetTool = detectTargetTool(db);
|
|
75
|
+
for (const section of sections) {
|
|
76
|
+
if (abortSignal.aborted)
|
|
77
|
+
break;
|
|
78
|
+
await stream.writeSSE({
|
|
79
|
+
event: 'progress',
|
|
80
|
+
data: JSON.stringify({ phase: 'synthesizing', section, message: `Generating ${section}...` }),
|
|
81
|
+
});
|
|
82
|
+
if (section === 'friction-wins') {
|
|
83
|
+
const prompt = generateFrictionWinsPrompt({
|
|
84
|
+
frictionCategories: aggregated.frictionCategories,
|
|
85
|
+
effectivePatterns: aggregated.effectivePatterns,
|
|
86
|
+
totalSessions: aggregated.totalSessions,
|
|
87
|
+
period,
|
|
88
|
+
pqSignals: (aggregated.pqDeficits.length || aggregated.pqStrengths.length)
|
|
89
|
+
? { deficits: aggregated.pqDeficits, strengths: aggregated.pqStrengths }
|
|
90
|
+
: undefined,
|
|
91
|
+
});
|
|
92
|
+
const response = await client.chat([
|
|
93
|
+
{ role: 'system', content: FRICTION_WINS_SYSTEM_PROMPT },
|
|
94
|
+
{ role: 'user', content: prompt },
|
|
95
|
+
], { signal: abortSignal });
|
|
96
|
+
const parsed = parseLLMJson(response.content);
|
|
97
|
+
results['friction-wins'] = {
|
|
98
|
+
section: 'friction-wins',
|
|
99
|
+
...(parsed ?? {}),
|
|
100
|
+
frictionCategories: aggregated.frictionCategories,
|
|
101
|
+
effectivePatterns: aggregated.effectivePatterns,
|
|
102
|
+
pqDeficits: aggregated.pqDeficits,
|
|
103
|
+
pqStrengths: aggregated.pqStrengths,
|
|
104
|
+
generatedAt: new Date().toISOString(),
|
|
105
|
+
};
|
|
106
|
+
}
|
|
107
|
+
else if (section === 'rules-skills') {
|
|
108
|
+
// Only include patterns with sufficient occurrence counts for actionable artifacts
|
|
109
|
+
const recurringFriction = aggregated.frictionCategories.filter(fc => fc.count >= 3);
|
|
110
|
+
const recurringPatterns = aggregated.effectivePatterns.filter(ep => ep.frequency >= 2);
|
|
111
|
+
const prompt = generateRulesSkillsPrompt({
|
|
112
|
+
recurringFriction,
|
|
113
|
+
effectivePatterns: recurringPatterns,
|
|
114
|
+
targetTool,
|
|
115
|
+
});
|
|
116
|
+
const response = await client.chat([
|
|
117
|
+
{ role: 'system', content: RULES_SKILLS_SYSTEM_PROMPT },
|
|
118
|
+
{ role: 'user', content: prompt },
|
|
119
|
+
], { signal: abortSignal });
|
|
120
|
+
const parsed = parseLLMJson(response.content);
|
|
121
|
+
results['rules-skills'] = {
|
|
122
|
+
section: 'rules-skills',
|
|
123
|
+
...(parsed ?? {}),
|
|
124
|
+
targetTool,
|
|
125
|
+
generatedAt: new Date().toISOString(),
|
|
126
|
+
};
|
|
127
|
+
}
|
|
128
|
+
else if (section === 'working-style') {
|
|
129
|
+
const prompt = generateWorkingStylePrompt({
|
|
130
|
+
workflowDistribution: aggregated.workflowDistribution,
|
|
131
|
+
outcomeDistribution: aggregated.outcomeDistribution,
|
|
132
|
+
characterDistribution: aggregated.characterDistribution,
|
|
133
|
+
totalSessions: aggregated.totalSessions,
|
|
134
|
+
period,
|
|
135
|
+
frictionFrequency: aggregated.frictionTotal,
|
|
136
|
+
});
|
|
137
|
+
const response = await client.chat([
|
|
138
|
+
{ role: 'system', content: WORKING_STYLE_SYSTEM_PROMPT },
|
|
139
|
+
{ role: 'user', content: prompt },
|
|
140
|
+
], { signal: abortSignal });
|
|
141
|
+
const parsed = parseLLMJson(response.content);
|
|
142
|
+
// Sanitize tagline: must be a string ≤40 chars. Non-string LLM outputs
|
|
143
|
+
// (object, array, number) are normalized to undefined so they don't
|
|
144
|
+
// corrupt the snapshot blob stored in SQLite.
|
|
145
|
+
const rawTagline = parsed && parsed['tagline'];
|
|
146
|
+
const tagline = typeof rawTagline === 'string'
|
|
147
|
+
? rawTagline.slice(0, 40)
|
|
148
|
+
: undefined;
|
|
149
|
+
results['working-style'] = {
|
|
150
|
+
section: 'working-style',
|
|
151
|
+
...(parsed ?? {}),
|
|
152
|
+
tagline,
|
|
153
|
+
workflowDistribution: aggregated.workflowDistribution,
|
|
154
|
+
outcomeDistribution: aggregated.outcomeDistribution,
|
|
155
|
+
characterDistribution: aggregated.characterDistribution,
|
|
156
|
+
generatedAt: new Date().toISOString(),
|
|
157
|
+
};
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
// Only save snapshot if the request was not aborted mid-generation.
|
|
161
|
+
// Saving partial results would cause stale/incomplete data to auto-load on next visit.
|
|
162
|
+
if (!c.req.raw.signal.aborted) {
|
|
163
|
+
// For ISO week periods, use the exact week boundaries as window metadata.
|
|
164
|
+
// This ensures the snapshot metadata accurately reflects the week it covers.
|
|
165
|
+
const isoWeekBounds = parseIsoWeek(period);
|
|
166
|
+
const windowStart = isoWeekBounds
|
|
167
|
+
? isoWeekBounds.start.toISOString()
|
|
168
|
+
: buildPeriodFilter(period);
|
|
169
|
+
const windowEnd = isoWeekBounds
|
|
170
|
+
? isoWeekBounds.end.toISOString()
|
|
171
|
+
: new Date().toISOString();
|
|
172
|
+
const projectKey = body.project || '__all__';
|
|
173
|
+
db.prepare(`
|
|
174
|
+
INSERT INTO reflect_snapshots (period, project_id, results_json, generated_at, window_start, window_end, session_count, facet_count)
|
|
175
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?)
|
|
176
|
+
ON CONFLICT(period, project_id) DO UPDATE SET
|
|
177
|
+
results_json = excluded.results_json,
|
|
178
|
+
generated_at = excluded.generated_at,
|
|
179
|
+
window_start = excluded.window_start,
|
|
180
|
+
window_end = excluded.window_end,
|
|
181
|
+
session_count = excluded.session_count,
|
|
182
|
+
facet_count = excluded.facet_count
|
|
183
|
+
`).run(period, projectKey, JSON.stringify(results), windowEnd, windowStart, windowEnd, aggregated.totalSessions, aggregated.frictionTotal);
|
|
184
|
+
}
|
|
185
|
+
await stream.writeSSE({
|
|
186
|
+
event: 'complete',
|
|
187
|
+
data: JSON.stringify({ results }),
|
|
188
|
+
});
|
|
189
|
+
}
|
|
190
|
+
catch (err) {
|
|
191
|
+
const message = err instanceof Error ? err.message : 'Unknown error';
|
|
192
|
+
await stream.writeSSE({
|
|
193
|
+
event: 'error',
|
|
194
|
+
data: JSON.stringify({ error: message }),
|
|
195
|
+
}).catch(() => { });
|
|
196
|
+
}
|
|
197
|
+
});
|
|
198
|
+
});
|
|
199
|
+
// GET /api/reflect/results
|
|
200
|
+
// Returns raw aggregated facet data without LLM synthesis (fast, no cost).
|
|
201
|
+
// The full synthesized view requires POST /api/reflect/generate.
|
|
202
|
+
app.get('/results', (c) => {
|
|
203
|
+
const db = getDb();
|
|
204
|
+
const period = c.req.query('period') || '30d';
|
|
205
|
+
const project = c.req.query('project');
|
|
206
|
+
const source = c.req.query('source');
|
|
207
|
+
const { where, params } = buildWhereClause(period, project, source);
|
|
208
|
+
const aggregated = getAggregatedData(db, where, params, project, source);
|
|
209
|
+
return c.json(aggregated);
|
|
210
|
+
});
|
|
211
|
+
// GET /api/reflect/weeks
|
|
212
|
+
// Returns all ISO weeks from the earliest session through the current week, with snapshot status.
|
|
213
|
+
// Used by the WeekSelector component to show which weeks have reflections.
|
|
214
|
+
// Week list is data-driven (not capped at 8) so users with long history can navigate all weeks.
|
|
215
|
+
app.get('/weeks', (c) => {
|
|
216
|
+
const db = getDb();
|
|
217
|
+
const project = c.req.query('project');
|
|
218
|
+
const projectCondition = project ? 'AND project_id = ?' : '';
|
|
219
|
+
const projectParams = project ? [project] : [];
|
|
220
|
+
// Find the earliest session to determine the full week range.
|
|
221
|
+
// If no sessions exist, return empty array.
|
|
222
|
+
const earliestRow = db.prepare(`
|
|
223
|
+
SELECT MIN(started_at) as earliest
|
|
224
|
+
FROM sessions
|
|
225
|
+
WHERE deleted_at IS NULL
|
|
226
|
+
${projectCondition}
|
|
227
|
+
`).get(...projectParams);
|
|
228
|
+
if (!earliestRow?.earliest) {
|
|
229
|
+
return c.json({ weeks: [] });
|
|
230
|
+
}
|
|
231
|
+
// Compute the UTC midnight of the Monday of the current ISO week.
|
|
232
|
+
// Truncating to midnight ensures the while-loop comparison is stable
|
|
233
|
+
// regardless of the time-of-day component in started_at values.
|
|
234
|
+
const now = new Date();
|
|
235
|
+
const nowDay = now.getUTCDay(); // 0=Sun
|
|
236
|
+
const daysToMonday = nowDay === 0 ? 6 : nowDay - 1;
|
|
237
|
+
const thisMondayMs = Date.UTC(now.getUTCFullYear(), now.getUTCMonth(), now.getUTCDate()) - daysToMonday * 86400000;
|
|
238
|
+
// Compute the UTC midnight of the Monday of the earliest session's ISO week.
|
|
239
|
+
const earliestDate = new Date(earliestRow.earliest);
|
|
240
|
+
const earliestDay = earliestDate.getUTCDay();
|
|
241
|
+
const daysToEarliestMonday = earliestDay === 0 ? 6 : earliestDay - 1;
|
|
242
|
+
const earliestMondayMs = Date.UTC(earliestDate.getUTCFullYear(), earliestDate.getUTCMonth(), earliestDate.getUTCDate()) - daysToEarliestMonday * 86400000;
|
|
243
|
+
// Generate all weeks from earliest through current (most recent first).
|
|
244
|
+
// Cap at 520 weeks (~10 years) to stay under SQLite's 999 bind-variable limit
|
|
245
|
+
// for the snapshot IN (...) query (each week uses 1 placeholder + 1 for project_id).
|
|
246
|
+
const MAX_WEEKS = 520;
|
|
247
|
+
const weekEntries = [];
|
|
248
|
+
let weekMondayMs = thisMondayMs;
|
|
249
|
+
while (weekMondayMs >= earliestMondayMs && weekEntries.length < MAX_WEEKS) {
|
|
250
|
+
const weekMonday = new Date(weekMondayMs);
|
|
251
|
+
const week = formatIsoWeek(weekMonday);
|
|
252
|
+
const bounds = parseIsoWeek(week);
|
|
253
|
+
weekEntries.push({ week, start: bounds.start.toISOString(), end: bounds.end.toISOString() });
|
|
254
|
+
weekMondayMs -= 7 * 86400000;
|
|
255
|
+
}
|
|
256
|
+
const projectKey = project || '__all__';
|
|
257
|
+
// Query 1: session counts per ISO week using GROUP BY.
|
|
258
|
+
// The Thursday trick: 'weekday 4' advances to the ISO week's Thursday (or stays if already
|
|
259
|
+
// Thursday), then '-3 days' gives that week's Monday. This handles all 7 days correctly —
|
|
260
|
+
// unlike 'weekday 1, -7 days' which overshoots by one week for sessions starting on Monday.
|
|
261
|
+
// Returns one row per week that has sessions — O(sessions), not O(weeks).
|
|
262
|
+
const rangeStart = weekEntries[weekEntries.length - 1].start;
|
|
263
|
+
const rangeEnd = weekEntries[0].end;
|
|
264
|
+
const sessionCountRaw = db.prepare(`
|
|
265
|
+
SELECT
|
|
266
|
+
date(s.started_at, 'weekday 4', '-3 days') as week_monday,
|
|
267
|
+
COUNT(*) as cnt
|
|
268
|
+
FROM sessions s
|
|
269
|
+
WHERE s.deleted_at IS NULL
|
|
270
|
+
AND s.started_at >= ?
|
|
271
|
+
AND s.started_at < ?
|
|
272
|
+
${projectCondition}
|
|
273
|
+
GROUP BY week_monday
|
|
274
|
+
`).all(rangeStart, rangeEnd, ...projectParams);
|
|
275
|
+
// Build a map from ISO week string -> session count.
|
|
276
|
+
// week_monday is a YYYY-MM-DD string (SQLite date()); parse it and derive the ISO week string.
|
|
277
|
+
const sessionCountMap = new Map();
|
|
278
|
+
for (const row of sessionCountRaw) {
|
|
279
|
+
const monday = new Date(row.week_monday + 'T00:00:00Z');
|
|
280
|
+
const weekStr = formatIsoWeek(monday);
|
|
281
|
+
// Accumulate in case formatIsoWeek maps two slightly-different Mondays to the same week
|
|
282
|
+
sessionCountMap.set(weekStr, (sessionCountMap.get(weekStr) ?? 0) + row.cnt);
|
|
283
|
+
}
|
|
284
|
+
// Query 2: all snapshots for these weeks in one query
|
|
285
|
+
const weekPlaceholders = weekEntries.map(() => '?').join(', ');
|
|
286
|
+
const snapshotRows = db.prepare(`
|
|
287
|
+
SELECT period, generated_at
|
|
288
|
+
FROM reflect_snapshots
|
|
289
|
+
WHERE period IN (${weekPlaceholders}) AND project_id = ?
|
|
290
|
+
`).all(...weekEntries.map(e => e.week), projectKey);
|
|
291
|
+
const snapshotMap = new Map(snapshotRows.map(r => [r.period, r.generated_at]));
|
|
292
|
+
const weeks = weekEntries.map((entry) => ({
|
|
293
|
+
week: entry.week,
|
|
294
|
+
sessionCount: sessionCountMap.get(entry.week) ?? 0,
|
|
295
|
+
hasSnapshot: snapshotMap.has(entry.week),
|
|
296
|
+
generatedAt: snapshotMap.get(entry.week) ?? null,
|
|
297
|
+
}));
|
|
298
|
+
return c.json({ weeks });
|
|
299
|
+
});
|
|
300
|
+
// GET /api/reflect/snapshot
|
|
301
|
+
// Returns cached reflect results for a given period/project combo.
|
|
302
|
+
app.get('/snapshot', (c) => {
|
|
303
|
+
const db = getDb();
|
|
304
|
+
const period = c.req.query('period') || '30d';
|
|
305
|
+
const project = c.req.query('project') || '__all__';
|
|
306
|
+
const row = db.prepare(`SELECT * FROM reflect_snapshots WHERE period = ? AND project_id = ?`).get(period, project);
|
|
307
|
+
if (!row) {
|
|
308
|
+
return c.json({ snapshot: null });
|
|
309
|
+
}
|
|
310
|
+
let results;
|
|
311
|
+
try {
|
|
312
|
+
results = JSON.parse(row.results_json);
|
|
313
|
+
}
|
|
314
|
+
catch {
|
|
315
|
+
// Corrupted snapshot data — treat as if no snapshot exists
|
|
316
|
+
return c.json({ snapshot: null });
|
|
317
|
+
}
|
|
318
|
+
return c.json({
|
|
319
|
+
snapshot: {
|
|
320
|
+
period: row.period,
|
|
321
|
+
projectId: row.project_id,
|
|
322
|
+
results,
|
|
323
|
+
generatedAt: row.generated_at,
|
|
324
|
+
windowStart: row.window_start,
|
|
325
|
+
windowEnd: row.window_end,
|
|
326
|
+
sessionCount: row.session_count,
|
|
327
|
+
facetCount: row.facet_count,
|
|
328
|
+
},
|
|
329
|
+
});
|
|
330
|
+
});
|
|
331
|
+
export default app;
|
|
332
|
+
//# sourceMappingURL=reflect.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"reflect.js","sourceRoot":"","sources":["../../src/routes/reflect.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
import type { Context, MiddlewareHandler } from 'hono';
|
|
2
|
+
import { streamSSE } from 'hono/streaming';
|
|
3
|
+
import { getDb } from '@code-insights/cli/db/client';
|
|
4
|
+
import type { AnalysisResult, AnalysisOptions } from '../llm/analysis.js';
|
|
5
|
+
import type { SessionData } from '../llm/analysis-db.js';
|
|
6
|
+
import type { SQLiteMessageRow } from '../llm/prompt-types.js';
|
|
7
|
+
/**
|
|
8
|
+
* Load a session row for LLM analysis. Returns undefined if the session doesn't exist
|
|
9
|
+
* or has been soft-deleted. The selected columns match exactly what the analysis engine
|
|
10
|
+
* expects via the SessionData interface.
|
|
11
|
+
*/
|
|
12
|
+
export declare function loadSessionForAnalysis(db: ReturnType<typeof getDb>, sessionId: string): SessionData | undefined;
|
|
13
|
+
/**
|
|
14
|
+
* Load all messages for a session, ordered by timestamp ascending.
|
|
15
|
+
* The selected columns match the SQLiteMessageRow interface consumed by the analysis engine.
|
|
16
|
+
*/
|
|
17
|
+
export declare function loadSessionMessages(db: ReturnType<typeof getDb>, sessionId: string): SQLiteMessageRow[];
|
|
18
|
+
/**
|
|
19
|
+
* Hono middleware factory that short-circuits with a 400 if no LLM provider is configured.
|
|
20
|
+
* Apply per-route: app.post('/route', requireLLM(), async (c) => { ... })
|
|
21
|
+
* The error shape { success: false, error: '...' } matches the analysis endpoint convention.
|
|
22
|
+
*/
|
|
23
|
+
export declare function requireLLM(): MiddlewareHandler;
|
|
24
|
+
/**
|
|
25
|
+
* Options for trackAnalysisResult. Callers may supply an onSuccess callback for
|
|
26
|
+
* any handler-specific events (e.g., insight_generated) that fire only on success.
|
|
27
|
+
*/
|
|
28
|
+
interface TrackAnalysisOptions {
|
|
29
|
+
/** Called after the success-path analysis_run event — emit extra events here. */
|
|
30
|
+
onSuccess?: () => void;
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Emit the 'analysis_run' telemetry event (and captureError on failure) for a
|
|
34
|
+
* completed analysis call. Encapsulates the boilerplate shared across the session,
|
|
35
|
+
* prompt-quality, and recurring handlers in analysis.ts.
|
|
36
|
+
*
|
|
37
|
+
* Usage:
|
|
38
|
+
* trackAnalysisResult('session', result, startTime, {
|
|
39
|
+
* onSuccess: () => trackEvent('insight_generated', { type: 'session', count: result.insights.length }),
|
|
40
|
+
* });
|
|
41
|
+
*/
|
|
42
|
+
export declare function trackAnalysisResult(analysisType: string, result: Pick<AnalysisResult, 'success' | 'error' | 'error_type' | 'response_preview'>, startTime: number, options?: TrackAnalysisOptions): void;
|
|
43
|
+
/**
|
|
44
|
+
* Build the human-readable progress message for a session analysis stream event.
|
|
45
|
+
* The session analysis handler uses chunk-count information; the prompt-quality
|
|
46
|
+
* handler uses a fixed string. Accept a resolver callback so each caller decides.
|
|
47
|
+
*/
|
|
48
|
+
type ProgressMessageFn = (progress: {
|
|
49
|
+
phase: string;
|
|
50
|
+
currentChunk?: number;
|
|
51
|
+
totalChunks?: number;
|
|
52
|
+
}) => string;
|
|
53
|
+
/**
|
|
54
|
+
* Options for streamSessionAnalysis.
|
|
55
|
+
*/
|
|
56
|
+
interface StreamSessionAnalysisOptions {
|
|
57
|
+
/**
|
|
58
|
+
* Human-readable telemetry type string (e.g. 'session', 'prompt-quality').
|
|
59
|
+
* Used as the `type` field in the analysis_run event.
|
|
60
|
+
*/
|
|
61
|
+
analysisType: string;
|
|
62
|
+
/**
|
|
63
|
+
* The analysis function to call. Must match the signature of analyzeSession /
|
|
64
|
+
* analyzePromptQuality — accepts (session, messages, options?) and returns AnalysisResult.
|
|
65
|
+
*/
|
|
66
|
+
analysisFn: (session: SessionData, messages: SQLiteMessageRow[], options?: AnalysisOptions) => Promise<AnalysisResult>;
|
|
67
|
+
/**
|
|
68
|
+
* Resolve the progress event message from the current progress state.
|
|
69
|
+
* Called inside the onProgress callback to build the human-readable 'message' field.
|
|
70
|
+
*/
|
|
71
|
+
progressMessage: ProgressMessageFn;
|
|
72
|
+
/**
|
|
73
|
+
* Called on the success path after tracking telemetry (e.g. to apply a generated title
|
|
74
|
+
* or emit an insight_generated event). Receives the successful AnalysisResult.
|
|
75
|
+
*/
|
|
76
|
+
onSuccess?: (result: AnalysisResult) => void;
|
|
77
|
+
}
|
|
78
|
+
/**
|
|
79
|
+
* Shared SSE lifecycle for single-session analysis stream endpoints.
|
|
80
|
+
* Handles the streamSSE wrapper, abort signal, progress events, result telemetry,
|
|
81
|
+
* and complete/error SSE events. The two analysis stream handlers (session and
|
|
82
|
+
* prompt-quality) differed only in their progress message and analysis function —
|
|
83
|
+
* those are injected via options.
|
|
84
|
+
*
|
|
85
|
+
* Preserves the exact SSE event names and complete/error payload shapes the
|
|
86
|
+
* dashboard expects: progress → { phase, message, ...progress }, complete →
|
|
87
|
+
* { success, insightCount, tokenUsage }, error → { error }.
|
|
88
|
+
*/
|
|
89
|
+
export declare function streamSessionAnalysis(c: Context, session: SessionData, messages: SQLiteMessageRow[], opts: StreamSessionAnalysisOptions): ReturnType<typeof streamSSE>;
|
|
90
|
+
/**
|
|
91
|
+
* Options for streamBatchBackfill. The two backfill handlers (facets and PQ) share
|
|
92
|
+
* the same loop structure but differ in how they check for existing work and which
|
|
93
|
+
* analysis function they call.
|
|
94
|
+
*/
|
|
95
|
+
export interface StreamBatchBackfillOptions {
|
|
96
|
+
/**
|
|
97
|
+
* Returns true if the session should be skipped (work already done).
|
|
98
|
+
* Only called when force=false; callers implement the appropriate SQL check.
|
|
99
|
+
*/
|
|
100
|
+
shouldSkip: (sessionId: string) => boolean;
|
|
101
|
+
/**
|
|
102
|
+
* The analysis function to run for each session. Receives the loaded session,
|
|
103
|
+
* its messages, and an AbortSignal. Returns a result with a success boolean.
|
|
104
|
+
*/
|
|
105
|
+
analysisFn: (session: SessionData, messages: SQLiteMessageRow[], options: {
|
|
106
|
+
signal: AbortSignal;
|
|
107
|
+
}) => Promise<{
|
|
108
|
+
success: boolean;
|
|
109
|
+
error?: string;
|
|
110
|
+
}>;
|
|
111
|
+
}
|
|
112
|
+
/**
|
|
113
|
+
* Shared SSE lifecycle for batch backfill endpoints (facets backfill and PQ backfill).
|
|
114
|
+
* Iterates sessionIds one-by-one, streaming per-session progress events and a final
|
|
115
|
+
* complete event. Preserves the exact SSE payload shapes the dashboard expects:
|
|
116
|
+
* progress → { completed, failed, total, currentSessionId, error? }
|
|
117
|
+
* complete → { completed, failed, total }
|
|
118
|
+
*
|
|
119
|
+
* The caller is responsible for request body validation (sessionIds array present,
|
|
120
|
+
* length ≤ MAX_BACKFILL_SESSIONS) before calling this helper.
|
|
121
|
+
*/
|
|
122
|
+
export declare function streamBatchBackfill(c: Context, sessionIds: string[], force: boolean, opts: StreamBatchBackfillOptions): ReturnType<typeof streamSSE>;
|
|
123
|
+
export {};
|
|
124
|
+
//# sourceMappingURL=route-helpers.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"route-helpers.d.ts","sourceRoot":"","sources":["../../src/routes/route-helpers.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,OAAO,EAAE,iBAAiB,EAAE,MAAM,MAAM,CAAC;AACvD,OAAO,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAC;AAC3C,OAAO,EAAE,KAAK,EAAE,MAAM,8BAA8B,CAAC;AAIrD,OAAO,KAAK,EAAE,cAAc,EAAE,eAAe,EAAE,MAAM,oBAAoB,CAAC;AAC1E,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,uBAAuB,CAAC;AACzD,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,wBAAwB,CAAC;AAE/D;;;;GAIG;AACH,wBAAgB,sBAAsB,CAAC,EAAE,EAAE,UAAU,CAAC,OAAO,KAAK,CAAC,EAAE,SAAS,EAAE,MAAM,GAAG,WAAW,GAAG,SAAS,CAM/G;AAED;;;GAGG;AACH,wBAAgB,mBAAmB,CAAC,EAAE,EAAE,UAAU,CAAC,OAAO,KAAK,CAAC,EAAE,SAAS,EAAE,MAAM,GAAG,gBAAgB,EAAE,CAKvG;AAED;;;;GAIG;AACH,wBAAgB,UAAU,IAAI,iBAAiB,CAU9C;AAID;;;GAGG;AACH,UAAU,oBAAoB;IAC5B,iFAAiF;IACjF,SAAS,CAAC,EAAE,MAAM,IAAI,CAAC;CACxB;AAED;;;;;;;;;GASG;AACH,wBAAgB,mBAAmB,CACjC,YAAY,EAAE,MAAM,EACpB,MAAM,EAAE,IAAI,CAAC,cAAc,EAAE,SAAS,GAAG,OAAO,GAAG,YAAY,GAAG,kBAAkB,CAAC,EACrF,SAAS,EAAE,MAAM,EACjB,OAAO,CAAC,EAAE,oBAAoB,GAC7B,IAAI,CAuBN;AAID;;;;GAIG;AACH,KAAK,iBAAiB,GAAG,CAAC,QAAQ,EAAE;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,YAAY,CAAC,EAAE,MAAM,CAAC;IAAC,WAAW,CAAC,EAAE,MAAM,CAAA;CAAE,KAAK,MAAM,CAAC;AAE9G;;GAEG;AACH,UAAU,4BAA4B;IACpC;;;OAGG;IACH,YAAY,EAAE,MAAM,CAAC;IACrB;;;OAGG;IACH,UAAU,EAAE,CACV,OAAO,EAAE,WAAW,EACpB,QAAQ,EAAE,gBAAgB,EAAE,EAC5B,OAAO,CAAC,EAAE,eAAe,KACtB,OAAO,CAAC,cAAc,CAAC,CAAC;IAC7B;;;OAGG;IACH,eAAe,EAAE,iBAAiB,CAAC;IACnC;;;OAGG;IACH,SAAS,CAAC,EAAE,CAAC,MAAM,EAAE,cAAc,KAAK,IAAI,CAAC;CAC9C;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,qBAAqB,CACnC,CAAC,EAAE,OAAO,EACV,OAAO,EAAE,WAAW,EACpB,QAAQ,EAAE,gBAAgB,EAAE,EAC5B,IAAI,EAAE,4BAA4B,GACjC,UAAU,CAAC,OAAO,SAAS,CAAC,CA0F9B;AAID;;;;GAIG;AACH,MAAM,WAAW,0BAA0B;IACzC;;;OAGG;IACH,UAAU,EAAE,CAAC,SAAS,EAAE,MAAM,KAAK,OAAO,CAAC;IAC3C;;;OAGG;IACH,UAAU,EAAE,CACV,OAAO,EAAE,WAAW,EACpB,QAAQ,EAAE,gBAAgB,EAAE,EAC5B,OAAO,EAAE;QAAE,MAAM,EAAE,WAAW,CAAA;KAAE,KAC7B,OAAO,CAAC;QAAE,OAAO,EAAE,OAAO,CAAC;QAAC,KAAK,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;CACpD;AAED;;;;;;;;;GASG;AACH,wBAAgB,mBAAmB,CACjC,CAAC,EAAE,OAAO,EACV,UAAU,EAAE,MAAM,EAAE,EACpB,KAAK,EAAE,OAAO,EACd,IAAI,EAAE,0BAA0B,GAC/B,UAAU,CAAC,OAAO,SAAS,CAAC,CA2D9B"}
|