@ariso-ai/ivan 1.0.25 → 1.0.28

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 (46) hide show
  1. package/dist/config.js +11 -11
  2. package/dist/config.js.map +1 -1
  3. package/dist/database/migrations/016_create_reviews_table.d.ts +3 -0
  4. package/dist/database/migrations/016_create_reviews_table.d.ts.map +1 -0
  5. package/dist/database/migrations/016_create_reviews_table.js +19 -0
  6. package/dist/database/migrations/016_create_reviews_table.js.map +1 -0
  7. package/dist/database/migrations/016_create_session_analyses_table.d.ts +3 -0
  8. package/dist/database/migrations/016_create_session_analyses_table.d.ts.map +1 -0
  9. package/dist/database/migrations/016_create_session_analyses_table.js +19 -0
  10. package/dist/database/migrations/016_create_session_analyses_table.js.map +1 -0
  11. package/dist/database/migrations/index.d.ts.map +1 -1
  12. package/dist/database/migrations/index.js +3 -1
  13. package/dist/database/migrations/index.js.map +1 -1
  14. package/dist/database.d.ts +2 -0
  15. package/dist/database.d.ts.map +1 -1
  16. package/dist/database.js +3 -0
  17. package/dist/database.js.map +1 -1
  18. package/dist/index.js +4 -2
  19. package/dist/index.js.map +1 -1
  20. package/dist/learnings/coding-sessions-command.d.ts +12 -0
  21. package/dist/learnings/coding-sessions-command.d.ts.map +1 -0
  22. package/dist/learnings/coding-sessions-command.js +274 -0
  23. package/dist/learnings/coding-sessions-command.js.map +1 -0
  24. package/dist/learnings/database.js +1 -1
  25. package/dist/learnings/database.js.map +1 -1
  26. package/dist/learnings/embeddings.d.ts.map +1 -1
  27. package/dist/learnings/embeddings.js +15 -2
  28. package/dist/learnings/embeddings.js.map +1 -1
  29. package/dist/learnings/index.d.ts +1 -1
  30. package/dist/learnings/index.d.ts.map +1 -1
  31. package/dist/learnings/index.js +23 -11
  32. package/dist/learnings/index.js.map +1 -1
  33. package/dist/learnings/install-hooks-command.js +2 -2
  34. package/dist/learnings/session-analyzer.d.ts +39 -0
  35. package/dist/learnings/session-analyzer.d.ts.map +1 -0
  36. package/dist/learnings/session-analyzer.js +339 -0
  37. package/dist/learnings/session-analyzer.js.map +1 -0
  38. package/dist/learnings/session-parser.d.ts +46 -0
  39. package/dist/learnings/session-parser.d.ts.map +1 -0
  40. package/dist/learnings/session-parser.js +235 -0
  41. package/dist/learnings/session-parser.js.map +1 -0
  42. package/dist/services/review-executor.d.ts +8 -0
  43. package/dist/services/review-executor.d.ts.map +1 -0
  44. package/dist/services/review-executor.js +159 -0
  45. package/dist/services/review-executor.js.map +1 -0
  46. package/package.json +2 -2
@@ -0,0 +1,339 @@
1
+ // Extracts thinking patterns AND example interactions from parsed session digests
2
+ // using GPT-5.5. Classification and extraction happen in a single LLM pass per session.
3
+ // Follows the same structured-output pattern as extractor.ts.
4
+ import OpenAI from 'openai';
5
+ import { ConfigManager } from '../config.js';
6
+ import { createDeterministicId } from './id.js';
7
+ import { LESSONS_JSONL_RELATIVE_PATH } from './paths.js';
8
+ const MODEL = 'gpt-5.5';
9
+ /**
10
+ * JSON Schema for GPT-5.5 structured output.
11
+ * The model is constrained to emit exactly this shape.
12
+ */
13
+ const ANALYSIS_SCHEMA = {
14
+ type: 'object',
15
+ properties: {
16
+ has_signal: { type: 'boolean' },
17
+ session_topic: { type: 'string' },
18
+ patterns: {
19
+ type: 'array',
20
+ items: {
21
+ type: 'object',
22
+ properties: {
23
+ statement: { type: 'string' },
24
+ kind: {
25
+ type: 'string',
26
+ enum: [
27
+ 'thinking_architecture',
28
+ 'thinking_product',
29
+ 'thinking_quality',
30
+ 'thinking_process'
31
+ ]
32
+ },
33
+ confidence: { type: 'number' },
34
+ rationale: { anyOf: [{ type: 'null' }, { type: 'string' }] },
35
+ applicability: { anyOf: [{ type: 'null' }, { type: 'string' }] }
36
+ },
37
+ required: [
38
+ 'statement',
39
+ 'kind',
40
+ 'confidence',
41
+ 'rationale',
42
+ 'applicability'
43
+ ],
44
+ additionalProperties: false
45
+ }
46
+ },
47
+ example_interactions: {
48
+ type: 'array',
49
+ items: {
50
+ type: 'object',
51
+ properties: {
52
+ context: { type: 'string' },
53
+ user_message: { type: 'string' },
54
+ derived_question: { type: 'string' },
55
+ when_to_ask: { type: 'string' },
56
+ confidence: { type: 'number' }
57
+ },
58
+ required: [
59
+ 'context',
60
+ 'user_message',
61
+ 'derived_question',
62
+ 'when_to_ask',
63
+ 'confidence'
64
+ ],
65
+ additionalProperties: false
66
+ }
67
+ }
68
+ },
69
+ required: ['has_signal', 'session_topic', 'patterns', 'example_interactions'],
70
+ additionalProperties: false
71
+ };
72
+ const SYSTEM_PROMPT = `\
73
+ You are analyzing coding sessions between a CEO/product architect and an AI coding assistant (Claude).
74
+
75
+ You have two goals:
76
+ 1. Extract reusable THINKING PATTERNS — how this person reasons about systems, products, quality, and decisions.
77
+ 2. Extract EXAMPLE INTERACTIONS — key moments where the user asked a question, made a correction, or
78
+ redirected the AI that reveal how a product architect thinks. These will be preserved as example
79
+ questions for the AI to consider asking in future similar tasks.
80
+
81
+ For each session, determine if it contains meaningful signal. If yes, extract both patterns and
82
+ example interactions. If no (routine coding with no decisions or corrections), set has_signal to false
83
+ and return empty arrays.
84
+
85
+ ## Thinking Patterns
86
+
87
+ Extract reusable principles as imperative statements.
88
+
89
+ What to look for:
90
+ - **Architecture decisions**: System design, data flow, separation of concerns, when to use existing
91
+ solutions vs custom code, pipeline design, data modeling choices.
92
+ - **Product reasoning**: How they connect technical choices to user/business value, UX philosophy,
93
+ data presentation choices, feature prioritization.
94
+ - **Quality standards**: What they reject, what "done right" means to them, debugging philosophy
95
+ (root cause vs band-aid), when to use established libraries vs custom implementations.
96
+ - **Process/decision-making**: How they evaluate tradeoffs, when they choose simplicity vs power.
97
+
98
+ Good pattern examples:
99
+ - "When a visualization takes more than 2 iterations, switch to an established library"
100
+ - "Always question whether null/missing data needs its own category"
101
+ - "Separate concerns by data domain, not by technical layer"
102
+
103
+ Classification (kind):
104
+ - thinking_architecture: System design, data modeling, integration, scalability
105
+ - thinking_product: UX, business value, feature priorities, user experience
106
+ - thinking_quality: Quality standards, debugging, testing, technical debt
107
+ - thinking_process: Decision-making style, delegation, workflow
108
+
109
+ ## Example Interactions
110
+
111
+ These are the most valuable part. Find moments where the user:
112
+ - Asked a probing question that reframed the problem
113
+ - Corrected the AI's approach in a way that reveals a deeper standard
114
+ - Redirected from implementation details to business/product concerns
115
+ - Challenged whether the current approach was the right one
116
+
117
+ For each, capture:
118
+ - context: Brief description of what was happening (1-2 sentences)
119
+ - user_message: The actual user message (quote it closely, can be paraphrased for clarity)
120
+ - derived_question: A reusable question to ask in similar future situations, written as an
121
+ imperative prompt (e.g., "Before implementing X, ask: ...")
122
+ - when_to_ask: When this question is relevant (specific trigger conditions)
123
+
124
+ Good example interaction:
125
+ - context: "Building a Sankey chart to visualize meeting productivity scores"
126
+ - user_message: "should we separate score = null into its own category? unrated or something?"
127
+ - derived_question: "Before implementing data visualizations with categorical breakdowns, ask: does
128
+ null/missing data need its own category, or should it be grouped with an existing bucket?"
129
+ - when_to_ask: "When building charts, dashboards, or data visualizations that categorize data into
130
+ groups — especially when the data has optional/nullable fields"
131
+
132
+ Bad examples (too trivial):
133
+ - "Can you fix the typo?" (no thinking signal)
134
+ - "Make it blue" (too specific, no transferable question)
135
+
136
+ ## Confidence
137
+
138
+ - 0.85-0.95: User explicitly stated the principle or the question is directly quotable
139
+ - 0.65-0.80: Pattern/question clearly demonstrated through decisions
140
+ - 0.50-0.60: Inferred from context with some uncertainty
141
+
142
+ ## Conversation dynamics context
143
+
144
+ Each session includes computed dynamics:
145
+ - correctionDensity: Ratio of corrections (higher = more opinionated session)
146
+ - questionCount: Number of probing questions (higher = more exploratory)
147
+ - hasEscalationArc: Whether user escalated from specific issue to systemic concern
148
+ - topicShifts: Number of topic changes (higher = broader thinking)`;
149
+ export class SessionAnalyzer {
150
+ client = null;
151
+ getClient() {
152
+ if (!this.client) {
153
+ // Try OPENAI_API_KEY env var first, then fall back to ivan config
154
+ const envKey = process.env['OPENAI_API_KEY'];
155
+ if (envKey) {
156
+ this.client = new OpenAI();
157
+ }
158
+ else {
159
+ const config = new ConfigManager().getConfig();
160
+ const apiKey = config?.openaiApiKey;
161
+ if (!apiKey) {
162
+ throw new Error('No OpenAI API key found. Set OPENAI_API_KEY or run "ivan reconfigure".');
163
+ }
164
+ this.client = new OpenAI({ apiKey });
165
+ }
166
+ }
167
+ return this.client;
168
+ }
169
+ /**
170
+ * Analyzes a single session digest and extracts thinking patterns + example interactions.
171
+ */
172
+ async analyzeSession(digest) {
173
+ const userContent = buildUserContent(digest);
174
+ const response = await this.getClient().chat.completions.create({
175
+ model: MODEL,
176
+ response_format: {
177
+ type: 'json_schema',
178
+ json_schema: {
179
+ name: 'session_analysis',
180
+ strict: true,
181
+ schema: ANALYSIS_SCHEMA
182
+ }
183
+ },
184
+ messages: [
185
+ { role: 'system', content: SYSTEM_PROMPT },
186
+ { role: 'user', content: userContent }
187
+ ]
188
+ });
189
+ let parsed;
190
+ try {
191
+ parsed = JSON.parse(response.choices[0]?.message?.content ??
192
+ '{"has_signal":false,"session_topic":"","patterns":[],"example_interactions":[]}');
193
+ }
194
+ catch {
195
+ return {
196
+ sessionId: digest.sessionId,
197
+ hasSignal: false,
198
+ sessionTopic: '',
199
+ patterns: [],
200
+ exampleInteractions: []
201
+ };
202
+ }
203
+ return {
204
+ sessionId: digest.sessionId,
205
+ hasSignal: parsed.has_signal,
206
+ sessionTopic: parsed.session_topic,
207
+ patterns: parsed.patterns.filter((p) => p.statement && p.statement.trim().length >= 10),
208
+ exampleInteractions: (parsed.example_interactions ?? []).filter((e) => e.derived_question && e.derived_question.trim().length >= 10)
209
+ };
210
+ }
211
+ /**
212
+ * Converts analysis results into LearningRecord objects ready for storage.
213
+ * Both thinking patterns and example interactions become learning records.
214
+ * Each record is tagged with its source project for relevance filtering.
215
+ */
216
+ analysisToLearningRecords(analysis, digest) {
217
+ if (!analysis.hasSignal)
218
+ return [];
219
+ const now = new Date().toISOString();
220
+ const records = [];
221
+ const project = extractProjectName(digest.projectPath);
222
+ const projectTag = `project:${project}`;
223
+ // Thinking patterns
224
+ for (const pattern of analysis.patterns) {
225
+ const trimmed = pattern.statement.trim();
226
+ const applicability = prependProject(project, pattern.applicability);
227
+ records.push({
228
+ type: 'learning',
229
+ sourcePath: LESSONS_JSONL_RELATIVE_PATH,
230
+ id: createDeterministicId('lrn', digest.sessionId, trimmed),
231
+ kind: pattern.kind,
232
+ source_type: 'coding_session',
233
+ source_url: projectTag,
234
+ statement: trimmed,
235
+ title: trimmed.length <= 72
236
+ ? trimmed
237
+ : `${trimmed.slice(0, 69).trimEnd()}...`,
238
+ rationale: pattern.rationale ?? undefined,
239
+ applicability,
240
+ confidence: Math.max(0.35, Math.min(0.95, pattern.confidence)),
241
+ status: 'active',
242
+ created_at: digest.timestamp ?? now,
243
+ updated_at: now
244
+ });
245
+ }
246
+ // Example interactions → stored as learning records with kind 'example_question'
247
+ // Note: user_message is intentionally excluded from persisted rationale to avoid
248
+ // storing raw transcript text that could contain secrets or sensitive data.
249
+ for (const interaction of analysis.exampleInteractions) {
250
+ const question = interaction.derived_question.trim();
251
+ const statement = question;
252
+ const rationale = `Context: ${interaction.context}`;
253
+ const applicability = prependProject(project, interaction.when_to_ask);
254
+ records.push({
255
+ type: 'learning',
256
+ sourcePath: LESSONS_JSONL_RELATIVE_PATH,
257
+ id: createDeterministicId('lrn', digest.sessionId, question),
258
+ kind: 'example_question',
259
+ source_type: 'coding_session',
260
+ source_url: projectTag,
261
+ statement,
262
+ title: statement.length <= 72
263
+ ? statement
264
+ : `${statement.slice(0, 69).trimEnd()}...`,
265
+ rationale,
266
+ applicability,
267
+ confidence: Math.max(0.35, Math.min(0.95, interaction.confidence)),
268
+ status: 'active',
269
+ created_at: digest.timestamp ?? now,
270
+ updated_at: now
271
+ });
272
+ }
273
+ return records;
274
+ }
275
+ }
276
+ /**
277
+ * Formats a session digest into the user content for the LLM prompt.
278
+ * Uses the ordered transcript to preserve the original message sequence.
279
+ */
280
+ function buildUserContent(digest) {
281
+ const lines = [];
282
+ lines.push(`## Session: ${digest.aiTitle ?? 'Untitled'}`);
283
+ lines.push(`Project: ${digest.projectPath}`);
284
+ lines.push(`Timestamp: ${digest.timestamp}`);
285
+ lines.push(`Entry point: ${digest.entrypoint}`);
286
+ lines.push('');
287
+ lines.push('### Conversation Dynamics');
288
+ lines.push(`- Turns: ${digest.dynamics.turnCount}`);
289
+ lines.push(`- Correction density: ${(digest.dynamics.correctionDensity * 100).toFixed(0)}%`);
290
+ lines.push(`- Questions asked: ${digest.dynamics.questionCount}`);
291
+ lines.push(`- Avg user message length: ${Math.round(digest.dynamics.avgUserMsgLength)} chars`);
292
+ lines.push(`- Has escalation arc: ${digest.dynamics.hasEscalationArc}`);
293
+ lines.push(`- Topic shifts: ${digest.dynamics.topicShifts}`);
294
+ lines.push('');
295
+ lines.push('### Conversation Transcript');
296
+ lines.push('');
297
+ const maxMessages = 60; // Cap to stay within token budget
298
+ const transcript = digest.transcript.slice(0, maxMessages);
299
+ for (const msg of transcript) {
300
+ const label = msg.role === 'user' ? 'User' : 'Assistant';
301
+ const text = msg.role === 'user' && msg.text.length > 1000
302
+ ? `${msg.text.slice(0, 1000)}...`
303
+ : msg.text;
304
+ lines.push(`**${label}:** ${text}`);
305
+ lines.push('');
306
+ }
307
+ if (digest.transcript.length > maxMessages) {
308
+ lines.push(`[${digest.transcript.length - maxMessages} additional messages truncated]`);
309
+ }
310
+ return lines.join('\n');
311
+ }
312
+ /**
313
+ * Extracts a clean project name from a Claude Code project path.
314
+ * e.g., "-Users-erkang-Repos-ariso-agents" → "ariso-agents"
315
+ * e.g., "-Users-erkang-Repos-my-cool-project" → "my-cool-project"
316
+ */
317
+ function extractProjectName(projectPath) {
318
+ const reposIdx = projectPath.indexOf('-Repos-');
319
+ if (reposIdx !== -1) {
320
+ const afterRepos = projectPath.slice(reposIdx + '-Repos-'.length);
321
+ const trimmed = afterRepos.replace(/^-+|-+$/g, '');
322
+ if (trimmed)
323
+ return trimmed;
324
+ }
325
+ // Fallback: last segment
326
+ const parts = projectPath.split('-').filter(Boolean);
327
+ return parts[parts.length - 1] ?? projectPath;
328
+ }
329
+ /**
330
+ * Prepends "Learned from project: X. " to the applicability text so the
331
+ * project context gets baked into the vector embedding for relevance ranking.
332
+ */
333
+ function prependProject(project, applicability) {
334
+ const prefix = `Learned from project: ${project}.`;
335
+ if (!applicability)
336
+ return prefix;
337
+ return `${prefix} ${applicability}`;
338
+ }
339
+ //# sourceMappingURL=session-analyzer.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"session-analyzer.js","sourceRoot":"","sources":["../../src/learnings/session-analyzer.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,46 @@
1
+ /** A single message in the ordered conversation transcript. */
2
+ export interface TranscriptMessage {
3
+ role: 'user' | 'assistant';
4
+ text: string;
5
+ }
6
+ /** Clean digest of a single Claude Code session. */
7
+ export interface SessionDigest {
8
+ sessionId: string;
9
+ projectPath: string;
10
+ filePath: string;
11
+ aiTitle: string | null;
12
+ timestamp: string;
13
+ entrypoint: string;
14
+ /** Ordered conversation transcript preserving original message sequence. */
15
+ transcript: TranscriptMessage[];
16
+ /** Convenience: just user text messages (derived from transcript). */
17
+ userMessages: string[];
18
+ dynamics: {
19
+ turnCount: number;
20
+ correctionDensity: number;
21
+ questionCount: number;
22
+ avgUserMsgLength: number;
23
+ hasEscalationArc: boolean;
24
+ topicShifts: number;
25
+ };
26
+ fileSize: number;
27
+ fileModifiedAt: string;
28
+ }
29
+ /**
30
+ * Discovers all main session JSONL files across all Claude Code projects.
31
+ * Skips subagent sessions (inside UUID subdirectories).
32
+ */
33
+ export declare function discoverSessionFiles(options?: {
34
+ project?: string;
35
+ recentDays?: number;
36
+ }): Array<{
37
+ filePath: string;
38
+ projectPath: string;
39
+ sessionId: string;
40
+ }>;
41
+ /**
42
+ * Parses a single JSONL session file into a SessionDigest.
43
+ * Streams line-by-line to handle large files efficiently.
44
+ */
45
+ export declare function parseSessionFile(filePath: string, projectPath: string, sessionId: string): Promise<SessionDigest | null>;
46
+ //# sourceMappingURL=session-parser.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"session-parser.d.ts","sourceRoot":"","sources":["../../src/learnings/session-parser.ts"],"names":[],"mappings":"AASA,+DAA+D;AAC/D,MAAM,WAAW,iBAAiB;IAChC,IAAI,EAAE,MAAM,GAAG,WAAW,CAAC;IAC3B,IAAI,EAAE,MAAM,CAAC;CACd;AAED,oDAAoD;AACpD,MAAM,WAAW,aAAa;IAC5B,SAAS,EAAE,MAAM,CAAC;IAClB,WAAW,EAAE,MAAM,CAAC;IACpB,QAAQ,EAAE,MAAM,CAAC;IACjB,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;IACvB,SAAS,EAAE,MAAM,CAAC;IAClB,UAAU,EAAE,MAAM,CAAC;IACnB,4EAA4E;IAC5E,UAAU,EAAE,iBAAiB,EAAE,CAAC;IAChC,sEAAsE;IACtE,YAAY,EAAE,MAAM,EAAE,CAAC;IACvB,QAAQ,EAAE;QACR,SAAS,EAAE,MAAM,CAAC;QAClB,iBAAiB,EAAE,MAAM,CAAC;QAC1B,aAAa,EAAE,MAAM,CAAC;QACtB,gBAAgB,EAAE,MAAM,CAAC;QACzB,gBAAgB,EAAE,OAAO,CAAC;QAC1B,WAAW,EAAE,MAAM,CAAC;KACrB,CAAC;IACF,QAAQ,EAAE,MAAM,CAAC;IACjB,cAAc,EAAE,MAAM,CAAC;CACxB;AAQD;;;GAGG;AACH,wBAAgB,oBAAoB,CAAC,OAAO,CAAC,EAAE;IAC7C,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB,GAAG,KAAK,CAAC;IAAE,QAAQ,EAAE,MAAM,CAAC;IAAC,WAAW,EAAE,MAAM,CAAC;IAAC,SAAS,EAAE,MAAM,CAAA;CAAE,CAAC,CA8CtE;AAED;;;GAGG;AACH,wBAAsB,gBAAgB,CACpC,QAAQ,EAAE,MAAM,EAChB,WAAW,EAAE,MAAM,EACnB,SAAS,EAAE,MAAM,GAChB,OAAO,CAAC,aAAa,GAAG,IAAI,CAAC,CA6E/B"}
@@ -0,0 +1,235 @@
1
+ // Parses Claude Code JSONL session files from ~/.claude/projects/ into clean
2
+ // conversation digests with computed conversation dynamics. Streams line-by-line
3
+ // to handle large files (up to 44MB) without loading into memory.
4
+ import fs from 'fs';
5
+ import path from 'path';
6
+ import readline from 'readline';
7
+ import os from 'os';
8
+ /**
9
+ * Discovers all main session JSONL files across all Claude Code projects.
10
+ * Skips subagent sessions (inside UUID subdirectories).
11
+ */
12
+ export function discoverSessionFiles(options) {
13
+ const projectsDir = path.join(os.homedir(), '.claude', 'projects');
14
+ if (!fs.existsSync(projectsDir))
15
+ return [];
16
+ const entries = fs.readdirSync(projectsDir);
17
+ const results = [];
18
+ const cutoff = options?.recentDays
19
+ ? Date.now() - options.recentDays * 86_400_000
20
+ : 0;
21
+ for (const dirName of entries) {
22
+ const dirPath = path.join(projectsDir, dirName);
23
+ if (!fs.statSync(dirPath).isDirectory())
24
+ continue;
25
+ // Filter by project name if specified
26
+ if (options?.project) {
27
+ const projectSlug = dirName.toLowerCase();
28
+ if (!projectSlug.includes(options.project.toLowerCase()))
29
+ continue;
30
+ }
31
+ // Find JSONL files directly in the project directory (not in subdirs/subagents)
32
+ const files = fs.readdirSync(dirPath);
33
+ for (const file of files) {
34
+ if (!file.endsWith('.jsonl'))
35
+ continue;
36
+ const filePath = path.join(dirPath, file);
37
+ const stat = fs.statSync(filePath);
38
+ if (!stat.isFile())
39
+ continue;
40
+ // Filter by recency
41
+ if (cutoff > 0 && stat.mtimeMs < cutoff)
42
+ continue;
43
+ const sessionId = file.replace('.jsonl', '');
44
+ results.push({
45
+ filePath,
46
+ projectPath: dirName,
47
+ sessionId
48
+ });
49
+ }
50
+ }
51
+ return results;
52
+ }
53
+ /**
54
+ * Parses a single JSONL session file into a SessionDigest.
55
+ * Streams line-by-line to handle large files efficiently.
56
+ */
57
+ export async function parseSessionFile(filePath, projectPath, sessionId) {
58
+ const stat = fs.statSync(filePath);
59
+ const messages = [];
60
+ let aiTitle = null;
61
+ let firstTimestamp = null;
62
+ let entrypoint = 'unknown';
63
+ const stream = fs.createReadStream(filePath, { encoding: 'utf8' });
64
+ const rl = readline.createInterface({ input: stream, crlfDelay: Infinity });
65
+ for await (const line of rl) {
66
+ const trimmed = line.trim();
67
+ if (!trimmed)
68
+ continue;
69
+ let obj;
70
+ try {
71
+ obj = JSON.parse(trimmed);
72
+ }
73
+ catch {
74
+ continue;
75
+ }
76
+ const type = obj['type'];
77
+ if (type === 'ai-title') {
78
+ aiTitle = obj['aiTitle'] ?? null;
79
+ continue;
80
+ }
81
+ if (type !== 'user' && type !== 'assistant')
82
+ continue;
83
+ const message = obj['message'];
84
+ if (!message)
85
+ continue;
86
+ const role = message['role'];
87
+ if (role !== 'user' && role !== 'assistant')
88
+ continue;
89
+ const timestamp = obj['timestamp'];
90
+ if (!firstTimestamp && timestamp) {
91
+ firstTimestamp = timestamp;
92
+ entrypoint = obj['entrypoint'] ?? 'unknown';
93
+ }
94
+ const text = extractTextContent(message, role);
95
+ if (!text)
96
+ continue;
97
+ messages.push({ role, text, timestamp });
98
+ }
99
+ const transcript = messages.map((m) => ({
100
+ role: m.role,
101
+ text: m.role === 'assistant' ? m.text.slice(0, 500) : m.text
102
+ }));
103
+ const userMessages = messages
104
+ .filter((m) => m.role === 'user')
105
+ .map((m) => m.text);
106
+ // Pre-filter: need at least 3 genuine user text messages
107
+ if (userMessages.length < 3)
108
+ return null;
109
+ const dynamics = computeDynamics(messages);
110
+ // Pre-filter: need at least 4 turns
111
+ if (dynamics.turnCount < 4)
112
+ return null;
113
+ return {
114
+ sessionId,
115
+ projectPath,
116
+ filePath,
117
+ aiTitle,
118
+ timestamp: firstTimestamp ?? stat.birthtime.toISOString(),
119
+ entrypoint,
120
+ transcript,
121
+ userMessages,
122
+ dynamics,
123
+ fileSize: stat.size,
124
+ fileModifiedAt: stat.mtime.toISOString()
125
+ };
126
+ }
127
+ /**
128
+ * Extracts plain text content from a message, skipping tool_use, tool_result,
129
+ * base64 images, thinking blocks, and IDE metadata messages.
130
+ */
131
+ function extractTextContent(message, role) {
132
+ const content = message['content'];
133
+ if (typeof content === 'string') {
134
+ return filterText(content, role);
135
+ }
136
+ if (!Array.isArray(content))
137
+ return null;
138
+ const textParts = [];
139
+ for (const block of content) {
140
+ if (typeof block !== 'object' || block === null)
141
+ continue;
142
+ const b = block;
143
+ // Only extract text blocks
144
+ if (b['type'] !== 'text')
145
+ continue;
146
+ const text = b['text'];
147
+ if (!text)
148
+ continue;
149
+ textParts.push(text);
150
+ }
151
+ const combined = textParts.join('\n').trim();
152
+ return filterText(combined, role);
153
+ }
154
+ /**
155
+ * Filters out low-signal messages: IDE metadata, tool results, system reminders,
156
+ * and very short messages.
157
+ */
158
+ function filterText(text, role) {
159
+ if (!text || text.length < 10)
160
+ return null;
161
+ // Skip IDE-only messages (opened file, selection without user text)
162
+ if (role === 'user' &&
163
+ (text.startsWith('<ide_opened_file>') ||
164
+ text.startsWith('<ide_selection>')) &&
165
+ !text.includes('\n')) {
166
+ return null;
167
+ }
168
+ // Skip system reminders
169
+ if (text.startsWith('<system-reminder>'))
170
+ return null;
171
+ // Skip tool result messages
172
+ if (text.startsWith('[Request interrupted'))
173
+ return null;
174
+ return text;
175
+ }
176
+ /**
177
+ * Computes conversation dynamics from the message sequence.
178
+ * These are structural signals — not keyword matching.
179
+ */
180
+ function computeDynamics(messages) {
181
+ const userMsgs = messages.filter((m) => m.role === 'user');
182
+ // Turn count: number of user↔assistant exchanges
183
+ let turnCount = 0;
184
+ let lastRole = '';
185
+ for (const m of messages) {
186
+ if (m.role !== lastRole) {
187
+ turnCount++;
188
+ lastRole = m.role;
189
+ }
190
+ }
191
+ // Correction density: ratio of short user messages following long assistant messages
192
+ // Short user msg after long assistant msg = likely a correction/redirect
193
+ let correctionLikeCount = 0;
194
+ for (let i = 1; i < messages.length; i++) {
195
+ const prev = messages[i - 1];
196
+ const curr = messages[i];
197
+ if (curr.role === 'user' &&
198
+ prev.role === 'assistant' &&
199
+ curr.text.length < 100 &&
200
+ prev.text.length > 200) {
201
+ correctionLikeCount++;
202
+ }
203
+ }
204
+ const correctionDensity = userMsgs.length > 0 ? correctionLikeCount / userMsgs.length : 0;
205
+ // Question count
206
+ const questionCount = userMsgs.filter((m) => m.text.includes('?')).length;
207
+ // Average user message length
208
+ const avgUserMsgLength = userMsgs.length > 0
209
+ ? userMsgs.reduce((sum, m) => sum + m.text.length, 0) / userMsgs.length
210
+ : 0;
211
+ // Escalation arc: frustration signals followed by explicit quality statements
212
+ const frustrationPatterns = /\b(come on|this can't be|shouldn't be this hard|let's not hack|the right way|is this a solved|we got burned|stop doing)\b/i;
213
+ const hasEscalationArc = userMsgs.some((m) => frustrationPatterns.test(m.text));
214
+ // Topic shifts: simple proxy — count times consecutive user messages are very different
215
+ let topicShifts = 0;
216
+ for (let i = 1; i < userMsgs.length; i++) {
217
+ const prev = userMsgs[i - 1].text.toLowerCase();
218
+ const curr = userMsgs[i].text.toLowerCase();
219
+ // Very rough: if fewer than 2 shared words (of 4+ chars), it's a topic shift
220
+ const prevWords = new Set(prev.split(/\s+/).filter((w) => w.length >= 4));
221
+ const currWords = curr.split(/\s+/).filter((w) => w.length >= 4);
222
+ const shared = currWords.filter((w) => prevWords.has(w)).length;
223
+ if (shared < 2 && prevWords.size > 2)
224
+ topicShifts++;
225
+ }
226
+ return {
227
+ turnCount,
228
+ correctionDensity,
229
+ questionCount,
230
+ avgUserMsgLength,
231
+ hasEscalationArc,
232
+ topicShifts
233
+ };
234
+ }
235
+ //# sourceMappingURL=session-parser.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"session-parser.js","sourceRoot":"","sources":["../../src/learnings/session-parser.ts"],"names":[],"mappings":"AAAA,6EAA6E;AAC7E,iFAAiF;AACjF,kEAAkE;AAElE,OAAO,EAAE,MAAM,IAAI,CAAC;AACpB,OAAO,IAAI,MAAM,MAAM,CAAC;AACxB,OAAO,QAAQ,MAAM,UAAU,CAAC;AAChC,OAAO,EAAE,MAAM,IAAI,CAAC;AAsCpB;;;GAGG;AACH,MAAM,UAAU,oBAAoB,CAAC,OAGpC;IACC,MAAM,WAAW,GAAG,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,OAAO,EAAE,EAAE,SAAS,EAAE,UAAU,CAAC,CAAC;IACnE,IAAI,CAAC,EAAE,CAAC,UAAU,CAAC,WAAW,CAAC;QAAE,OAAO,EAAE,CAAC;IAE3C,MAAM,OAAO,GAAG,EAAE,CAAC,WAAW,CAAC,WAAW,CAAC,CAAC;IAC5C,MAAM,OAAO,GAIR,EAAE,CAAC;IAER,MAAM,MAAM,GAAG,OAAO,EAAE,UAAU;QAChC,CAAC,CAAC,IAAI,CAAC,GAAG,EAAE,GAAG,OAAO,CAAC,UAAU,GAAG,UAAU;QAC9C,CAAC,CAAC,CAAC,CAAC;IAEN,KAAK,MAAM,OAAO,IAAI,OAAO,EAAE,CAAC;QAC9B,MAAM,OAAO,GAAG,IAAI,CAAC,IAAI,CAAC,WAAW,EAAE,OAAO,CAAC,CAAC;QAChD,IAAI,CAAC,EAAE,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC,WAAW,EAAE;YAAE,SAAS;QAElD,sCAAsC;QACtC,IAAI,OAAO,EAAE,OAAO,EAAE,CAAC;YACrB,MAAM,WAAW,GAAG,OAAO,CAAC,WAAW,EAAE,CAAC;YAC1C,IAAI,CAAC,WAAW,CAAC,QAAQ,CAAC,OAAO,CAAC,OAAO,CAAC,WAAW,EAAE,CAAC;gBAAE,SAAS;QACrE,CAAC;QAED,gFAAgF;QAChF,MAAM,KAAK,GAAG,EAAE,CAAC,WAAW,CAAC,OAAO,CAAC,CAAC;QACtC,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;YACzB,IAAI,CAAC,IAAI,CAAC,QAAQ,CAAC,QAAQ,CAAC;gBAAE,SAAS;YACvC,MAAM,QAAQ,GAAG,IAAI,CAAC,IAAI,CAAC,OAAO,EAAE,IAAI,CAAC,CAAC;YAC1C,MAAM,IAAI,GAAG,EAAE,CAAC,QAAQ,CAAC,QAAQ,CAAC,CAAC;YACnC,IAAI,CAAC,IAAI,CAAC,MAAM,EAAE;gBAAE,SAAS;YAE7B,oBAAoB;YACpB,IAAI,MAAM,GAAG,CAAC,IAAI,IAAI,CAAC,OAAO,GAAG,MAAM;gBAAE,SAAS;YAElD,MAAM,SAAS,GAAG,IAAI,CAAC,OAAO,CAAC,QAAQ,EAAE,EAAE,CAAC,CAAC;YAC7C,OAAO,CAAC,IAAI,CAAC;gBACX,QAAQ;gBACR,WAAW,EAAE,OAAO;gBACpB,SAAS;aACV,CAAC,CAAC;QACL,CAAC;IACH,CAAC;IAED,OAAO,OAAO,CAAC;AACjB,CAAC;AAED;;;GAGG;AACH,MAAM,CAAC,KAAK,UAAU,gBAAgB,CACpC,QAAgB,EAChB,WAAmB,EACnB,SAAiB;IAEjB,MAAM,IAAI,GAAG,EAAE,CAAC,QAAQ,CAAC,QAAQ,CAAC,CAAC;IACnC,MAAM,QAAQ,GAAoB,EAAE,CAAC;IACrC,IAAI,OAAO,GAAkB,IAAI,CAAC;IAClC,IAAI,cAAc,GAAkB,IAAI,CAAC;IACzC,IAAI,UAAU,GAAG,SAAS,CAAC;IAE3B,MAAM,MAAM,GAAG,EAAE,CAAC,gBAAgB,CAAC,QAAQ,EAAE,EAAE,QAAQ,EAAE,MAAM,EAAE,CAAC,CAAC;IACnE,MAAM,EAAE,GAAG,QAAQ,CAAC,eAAe,CAAC,EAAE,KAAK,EAAE,MAAM,EAAE,SAAS,EAAE,QAAQ,EAAE,CAAC,CAAC;IAE5E,IAAI,KAAK,EAAE,MAAM,IAAI,IAAI,EAAE,EAAE,CAAC;QAC5B,MAAM,OAAO,GAAG,IAAI,CAAC,IAAI,EAAE,CAAC;QAC5B,IAAI,CAAC,OAAO;YAAE,SAAS;QAEvB,IAAI,GAA4B,CAAC;QACjC,IAAI,CAAC;YACH,GAAG,GAAG,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;QAC5B,CAAC;QAAC,MAAM,CAAC;YACP,SAAS;QACX,CAAC;QAED,MAAM,IAAI,GAAG,GAAG,CAAC,MAAM,CAAW,CAAC;QAEnC,IAAI,IAAI,KAAK,UAAU,EAAE,CAAC;YACxB,OAAO,GAAI,GAAG,CAAC,SAAS,CAAY,IAAI,IAAI,CAAC;YAC7C,SAAS;QACX,CAAC;QAED,IAAI,IAAI,KAAK,MAAM,IAAI,IAAI,KAAK,WAAW;YAAE,SAAS;QAEtD,MAAM,OAAO,GAAG,GAAG,CAAC,SAAS,CAAwC,CAAC;QACtE,IAAI,CAAC,OAAO;YAAE,SAAS;QAEvB,MAAM,IAAI,GAAG,OAAO,CAAC,MAAM,CAAW,CAAC;QACvC,IAAI,IAAI,KAAK,MAAM,IAAI,IAAI,KAAK,WAAW;YAAE,SAAS;QAEtD,MAAM,SAAS,GAAG,GAAG,CAAC,WAAW,CAAW,CAAC;QAC7C,IAAI,CAAC,cAAc,IAAI,SAAS,EAAE,CAAC;YACjC,cAAc,GAAG,SAAS,CAAC;YAC3B,UAAU,GAAI,GAAG,CAAC,YAAY,CAAY,IAAI,SAAS,CAAC;QAC1D,CAAC;QAED,MAAM,IAAI,GAAG,kBAAkB,CAAC,OAAO,EAAE,IAAI,CAAC,CAAC;QAC/C,IAAI,CAAC,IAAI;YAAE,SAAS;QAEpB,QAAQ,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,IAAI,EAAE,SAAS,EAAE,CAAC,CAAC;IAC3C,CAAC;IAED,MAAM,UAAU,GAAwB,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;QAC3D,IAAI,EAAE,CAAC,CAAC,IAAI;QACZ,IAAI,EAAE,CAAC,CAAC,IAAI,KAAK,WAAW,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI;KAC7D,CAAC,CAAC,CAAC;IACJ,MAAM,YAAY,GAAG,QAAQ;SAC1B,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,KAAK,MAAM,CAAC;SAChC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC;IAEtB,yDAAyD;IACzD,IAAI,YAAY,CAAC,MAAM,GAAG,CAAC;QAAE,OAAO,IAAI,CAAC;IAEzC,MAAM,QAAQ,GAAG,eAAe,CAAC,QAAQ,CAAC,CAAC;IAE3C,oCAAoC;IACpC,IAAI,QAAQ,CAAC,SAAS,GAAG,CAAC;QAAE,OAAO,IAAI,CAAC;IAExC,OAAO;QACL,SAAS;QACT,WAAW;QACX,QAAQ;QACR,OAAO;QACP,SAAS,EAAE,cAAc,IAAI,IAAI,CAAC,SAAS,CAAC,WAAW,EAAE;QACzD,UAAU;QACV,UAAU;QACV,YAAY;QACZ,QAAQ;QACR,QAAQ,EAAE,IAAI,CAAC,IAAI;QACnB,cAAc,EAAE,IAAI,CAAC,KAAK,CAAC,WAAW,EAAE;KACzC,CAAC;AACJ,CAAC;AAED;;;GAGG;AACH,SAAS,kBAAkB,CACzB,OAAgC,EAChC,IAAY;IAEZ,MAAM,OAAO,GAAG,OAAO,CAAC,SAAS,CAAC,CAAC;IACnC,IAAI,OAAO,OAAO,KAAK,QAAQ,EAAE,CAAC;QAChC,OAAO,UAAU,CAAC,OAAO,EAAE,IAAI,CAAC,CAAC;IACnC,CAAC;IAED,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,OAAO,CAAC;QAAE,OAAO,IAAI,CAAC;IAEzC,MAAM,SAAS,GAAa,EAAE,CAAC;IAC/B,KAAK,MAAM,KAAK,IAAI,OAAO,EAAE,CAAC;QAC5B,IAAI,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,KAAK,IAAI;YAAE,SAAS;QAC1D,MAAM,CAAC,GAAG,KAAgC,CAAC;QAE3C,2BAA2B;QAC3B,IAAI,CAAC,CAAC,MAAM,CAAC,KAAK,MAAM;YAAE,SAAS;QACnC,MAAM,IAAI,GAAG,CAAC,CAAC,MAAM,CAAW,CAAC;QACjC,IAAI,CAAC,IAAI;YAAE,SAAS;QAEpB,SAAS,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IACvB,CAAC;IAED,MAAM,QAAQ,GAAG,SAAS,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,IAAI,EAAE,CAAC;IAC7C,OAAO,UAAU,CAAC,QAAQ,EAAE,IAAI,CAAC,CAAC;AACpC,CAAC;AAED;;;GAGG;AACH,SAAS,UAAU,CAAC,IAAY,EAAE,IAAY;IAC5C,IAAI,CAAC,IAAI,IAAI,IAAI,CAAC,MAAM,GAAG,EAAE;QAAE,OAAO,IAAI,CAAC;IAE3C,oEAAoE;IACpE,IACE,IAAI,KAAK,MAAM;QACf,CAAC,IAAI,CAAC,UAAU,CAAC,mBAAmB,CAAC;YACnC,IAAI,CAAC,UAAU,CAAC,iBAAiB,CAAC,CAAC;QACrC,CAAC,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,EACpB,CAAC;QACD,OAAO,IAAI,CAAC;IACd,CAAC;IAED,wBAAwB;IACxB,IAAI,IAAI,CAAC,UAAU,CAAC,mBAAmB,CAAC;QAAE,OAAO,IAAI,CAAC;IAEtD,4BAA4B;IAC5B,IAAI,IAAI,CAAC,UAAU,CAAC,sBAAsB,CAAC;QAAE,OAAO,IAAI,CAAC;IAEzD,OAAO,IAAI,CAAC;AACd,CAAC;AAED;;;GAGG;AACH,SAAS,eAAe,CAAC,QAAyB;IAChD,MAAM,QAAQ,GAAG,QAAQ,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,KAAK,MAAM,CAAC,CAAC;IAE3D,iDAAiD;IACjD,IAAI,SAAS,GAAG,CAAC,CAAC;IAClB,IAAI,QAAQ,GAAG,EAAE,CAAC;IAClB,KAAK,MAAM,CAAC,IAAI,QAAQ,EAAE,CAAC;QACzB,IAAI,CAAC,CAAC,IAAI,KAAK,QAAQ,EAAE,CAAC;YACxB,SAAS,EAAE,CAAC;YACZ,QAAQ,GAAG,CAAC,CAAC,IAAI,CAAC;QACpB,CAAC;IACH,CAAC;IAED,qFAAqF;IACrF,yEAAyE;IACzE,IAAI,mBAAmB,GAAG,CAAC,CAAC;IAC5B,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,QAAQ,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;QACzC,MAAM,IAAI,GAAG,QAAQ,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC;QAC7B,MAAM,IAAI,GAAG,QAAQ,CAAC,CAAC,CAAC,CAAC;QACzB,IACE,IAAI,CAAC,IAAI,KAAK,MAAM;YACpB,IAAI,CAAC,IAAI,KAAK,WAAW;YACzB,IAAI,CAAC,IAAI,CAAC,MAAM,GAAG,GAAG;YACtB,IAAI,CAAC,IAAI,CAAC,MAAM,GAAG,GAAG,EACtB,CAAC;YACD,mBAAmB,EAAE,CAAC;QACxB,CAAC;IACH,CAAC;IACD,MAAM,iBAAiB,GACrB,QAAQ,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,mBAAmB,GAAG,QAAQ,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC;IAElE,iBAAiB;IACjB,MAAM,aAAa,GAAG,QAAQ,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,MAAM,CAAC;IAE1E,8BAA8B;IAC9B,MAAM,gBAAgB,GACpB,QAAQ,CAAC,MAAM,GAAG,CAAC;QACjB,CAAC,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC,GAAG,EAAE,CAAC,EAAE,EAAE,CAAC,GAAG,GAAG,CAAC,CAAC,IAAI,CAAC,MAAM,EAAE,CAAC,CAAC,GAAG,QAAQ,CAAC,MAAM;QACvE,CAAC,CAAC,CAAC,CAAC;IAER,8EAA8E;IAC9E,MAAM,mBAAmB,GACvB,4HAA4H,CAAC;IAC/H,MAAM,gBAAgB,GAAG,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAC3C,mBAAmB,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,CACjC,CAAC;IAEF,wFAAwF;IACxF,IAAI,WAAW,GAAG,CAAC,CAAC;IACpB,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,QAAQ,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;QACzC,MAAM,IAAI,GAAG,QAAQ,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,IAAI,CAAC,WAAW,EAAE,CAAC;QAChD,MAAM,IAAI,GAAG,QAAQ,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,WAAW,EAAE,CAAC;QAC5C,6EAA6E;QAC7E,MAAM,SAAS,GAAG,IAAI,GAAG,CAAC,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,MAAM,IAAI,CAAC,CAAC,CAAC,CAAC;QAC1E,MAAM,SAAS,GAAG,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,MAAM,IAAI,CAAC,CAAC,CAAC;QACjE,MAAM,MAAM,GAAG,SAAS,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,SAAS,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC;QAChE,IAAI,MAAM,GAAG,CAAC,IAAI,SAAS,CAAC,IAAI,GAAG,CAAC;YAAE,WAAW,EAAE,CAAC;IACtD,CAAC;IAED,OAAO;QACL,SAAS;QACT,iBAAiB;QACjB,aAAa;QACb,gBAAgB;QAChB,gBAAgB;QAChB,WAAW;KACZ,CAAC;AACJ,CAAC"}
@@ -0,0 +1,8 @@
1
+ export declare class ReviewExecutor {
2
+ private dbManager;
3
+ constructor();
4
+ executeReviews(prNumbers: number[]): Promise<void>;
5
+ private reviewPR;
6
+ private extractReviewContent;
7
+ }
8
+ //# sourceMappingURL=review-executor.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"review-executor.d.ts","sourceRoot":"","sources":["../../src/services/review-executor.ts"],"names":[],"mappings":"AAMA,qBAAa,cAAc;IACzB,OAAO,CAAC,SAAS,CAAkB;;IAM7B,cAAc,CAAC,SAAS,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC;YAgB1C,QAAQ;IAyItB,OAAO,CAAC,oBAAoB;CAc7B"}