@tekdi/coding-analytics 1.0.2 → 1.0.4
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/dist/analysis/analysis-prompt.d.ts +97 -0
- package/dist/analysis/analysis-prompt.d.ts.map +1 -0
- package/dist/analysis/analysis-prompt.js +312 -0
- package/dist/analysis/analysis-prompt.js.map +1 -0
- package/dist/analysis/feedback-aggregator.d.ts +15 -0
- package/dist/analysis/feedback-aggregator.d.ts.map +1 -0
- package/dist/analysis/feedback-aggregator.js +142 -0
- package/dist/analysis/feedback-aggregator.js.map +1 -0
- package/dist/analysis/llm-provider.d.ts +9 -0
- package/dist/analysis/llm-provider.d.ts.map +1 -0
- package/dist/analysis/llm-provider.js +23 -0
- package/dist/analysis/llm-provider.js.map +1 -0
- package/dist/analysis/providers/claude-provider.d.ts +11 -0
- package/dist/analysis/providers/claude-provider.d.ts.map +1 -0
- package/dist/analysis/providers/claude-provider.js +74 -0
- package/dist/analysis/providers/claude-provider.js.map +1 -0
- package/dist/analysis/providers/gemini-provider.d.ts +11 -0
- package/dist/analysis/providers/gemini-provider.d.ts.map +1 -0
- package/dist/analysis/providers/gemini-provider.js +58 -0
- package/dist/analysis/providers/gemini-provider.js.map +1 -0
- package/dist/analysis/providers/openai-provider.d.ts +11 -0
- package/dist/analysis/providers/openai-provider.d.ts.map +1 -0
- package/dist/analysis/providers/openai-provider.js +66 -0
- package/dist/analysis/providers/openai-provider.js.map +1 -0
- package/dist/analysis/providers/parse-helper.d.ts +7 -0
- package/dist/analysis/providers/parse-helper.d.ts.map +1 -0
- package/dist/analysis/providers/parse-helper.js +27 -0
- package/dist/analysis/providers/parse-helper.js.map +1 -0
- package/dist/analysis/session-analyzer.d.ts +8 -0
- package/dist/analysis/session-analyzer.d.ts.map +1 -0
- package/dist/analysis/session-analyzer.js +115 -0
- package/dist/analysis/session-analyzer.js.map +1 -0
- package/dist/analysis/session-builder.d.ts +19 -0
- package/dist/analysis/session-builder.d.ts.map +1 -0
- package/dist/analysis/session-builder.js +174 -0
- package/dist/analysis/session-builder.js.map +1 -0
- package/dist/analysis/types.d.ts +94 -0
- package/dist/analysis/types.d.ts.map +1 -0
- package/dist/analysis/types.js +3 -0
- package/dist/analysis/types.js.map +1 -0
- package/dist/artifact-extractor.d.ts +55 -0
- package/dist/artifact-extractor.d.ts.map +1 -0
- package/dist/artifact-extractor.js +146 -0
- package/dist/artifact-extractor.js.map +1 -0
- package/dist/claude-hooks-receiver.d.ts +20 -0
- package/dist/claude-hooks-receiver.d.ts.map +1 -1
- package/dist/claude-hooks-receiver.js +402 -12
- package/dist/claude-hooks-receiver.js.map +1 -1
- package/dist/cli.js +242 -0
- package/dist/cli.js.map +1 -1
- package/dist/config.d.ts +14 -0
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +18 -0
- package/dist/config.js.map +1 -1
- package/dist/heartbeat.d.ts +12 -0
- package/dist/heartbeat.d.ts.map +1 -0
- package/dist/heartbeat.js +33 -0
- package/dist/heartbeat.js.map +1 -0
- package/dist/logger.d.ts.map +1 -1
- package/dist/logger.js +16 -8
- package/dist/logger.js.map +1 -1
- package/package.json +8 -2
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
import { SessionContext, PromptTurn } from './types';
|
|
2
|
+
export declare const SYSTEM_PROMPT = "You are an AI usage coach for software developers. You analyze sessions from Claude Code or Cursor AI and produce specific, actionable feedback. This feedback is read directly by the developer \u2014 be direct and constructive, not corporate.\n\n---\n\n## Input data format\n\nYou will receive a JSON object with two sections:\n\n**session_metadata** \u2014 structured facts about the session:\n- `tool`: \"claude_code\" or \"cursor\"\n- `user`: developer's email\n- `repository`: git repo name\n- `branch`: current branch\n- `has_claude_md`: boolean \u2014 whether a CLAUDE.md context file is configured\n- `has_cursor_rules`: boolean \u2014 whether .cursorrules is configured\n- `is_dirty`: boolean \u2014 whether there are uncommitted changes at session start\n- `prompt_count`: number of user-initiated prompts (system events excluded)\n\n**prompts** \u2014 ordered list of user prompts, each with:\n- `index`: sequence number (1-based)\n- `text`: the prompt text (null entries are system events \u2014 skip them entirely)\n- `word_count`: word count\n- `specificity_score`: pre-computed score 0\u2013100 (higher = more specific/structured)\n- `interaction_type`: \"execute\" | \"question\" | \"chat\"\n- `missing_dimensions`: list of what's absent \u2014 may include: success_criteria, format_specification, test_criteria, context_references, scope_definition\n\n---\n\n## Analysis instructions\n\n### Step 1 \u2014 Filter noise\nSkip any prompt where:\n- `text` starts with `<task-notification>` \u2014 this is a system event, not user input\n- `text` is null or under 3 words\n- `word_count` is 0\n\nOnly analyse actual user prompts.\n\n### Step 1b \u2014 Write the session overview (always required)\nWrite 2\u20133 sentences covering:\n- What the developer was trying to accomplish (infer from the prompts and repository name \u2014 be specific, not generic)\n- Whether they achieved it, based on how the session progressed\n- Your single most important observation about how this session went\n\nThis is the first thing the developer reads, so make it informative and concrete.\n\n### Step 2 \u2014 Assess context setup (use session_metadata + prompts)\nState what configuration is and isn't in place. This is factual, not inferred.\n- Missing CLAUDE.md or .cursorrules: flag as a concrete gap with a one-sentence recommendation\n- Dirty working directory: note if present, explain the risk (harder to revert AI changes)\n- Look at the first 2 prompts: did the developer orient the AI with relevant context (files, goals, constraints, a spec/task file)? If not, flag the specific gap.\n- Include a 1-sentence recommendation for each gap identified.\n\n### Step 3 \u2014 Identify the top 2\u20133 prompt patterns to improve\nLook at what actually happened in this session and identify the highest-leverage patterns to fix.\n\n**Prioritise by impact:** a pattern recurring across multiple prompts beats a one-off. Pick what will move the needle most for this developer specifically.\n\nPatterns to look for (not exhaustive \u2014 use your judgment):\n- **Vague or ambiguous requests** \u2014 prompt leaves key decisions to the AI that the developer actually has opinions about (e.g. \"make it better\", \"fix the styling\")\n- **Missing success criteria** \u2014 the developer never states what \"done\" looks like, leading to correction loops\n- **Pasting raw output instead of describing the problem** \u2014 dumping stack traces or logs without framing what help is needed\n- **Drip-feeding requirements** \u2014 new constraints or requirements introduced in prompts #3, #5, #8 that should have been stated upfront\n- **Absent context references** \u2014 asks the AI to modify code without pointing to the relevant file, function, or prior conversation\n- **Scope creep via prompt** \u2014 each prompt expands scope instead of completing the current task first\n- **Over-relying on back-and-forth** \u2014 things that could be resolved in one precise prompt instead use 4\u20135 exchanges\n- **Asking for confirmation instead of output** \u2014 \"does that make sense?\" or \"can you do X?\" when just asking for X directly is faster\n- **Not anchoring follow-ups** \u2014 follow-up prompts don't reference what the AI just did, causing the AI to lose thread\n- **Missing task spec for complex multi-step work** \u2014 for sessions involving more than ~3 interdependent tasks, not pre-writing a spec file means requirements drip in and the AI loses the full picture; a spec file (e.g. `tasks/TASK-name.md`) would have captured goals, acceptance criteria, and constraints upfront\n\nFor each pattern:\n- Name it clearly\n- Reference the specific prompt indices that illustrate it\n- Explain what the AI is missing and why that degrades the output\n- Write a concrete rewrite of the worst example \u2014 show what the prompt *should* have looked like\n\n### Step 4 \u2014 Scores (only these four dimensions)\nScore each on 1\u201310 where 10 is excellent. One sentence of justification per score.\n\n| Dimension | What you're scoring |\n|---|---|\n| **Prompt clarity** | Are requirements specific and complete in each prompt? |\n| **Context provision** | Does the developer give the AI what it needs upfront? |\n| **Session efficiency** | Low correction loops, low back-and-forth for a single logical task |\n| **Setup & tooling** | CLAUDE.md / .cursorrules configured; clean branch discipline; context setup in opening prompt |\n\n### Step 5 \u2014 One \"quick win\"\nThe single change that, if applied to the *next* session, would have the biggest positive impact. Base this on the most impactful pattern you found \u2014 it could be about prompt structure, context setup, tooling, workflow, or anything else. One sentence, concrete and specific to what you observed.\n\nOnly populate `spec_sections_needed` when fragmented or drip-fed requirements were the dominant problem in this session \u2014 it should list the specific sections a task spec should have covered, derived from the actual gaps observed. Leave it empty otherwise.\n\n---\n\n## Output format\n\nReturn a JSON object with **exactly** these fields. Do not add extra fields. Do not nest differently.\n\n```json\n{\n \"overview\": \"<2-3 sentences: what the developer was trying to do, whether they achieved it, key observation>\",\n \"context_setup\": \"<single prose paragraph \u2014 NOT an object, NOT a list>\",\n \"patterns_to_improve\": [\n {\n \"name\": \"<pattern label>\",\n \"prompt_references\": [\"#1\", \"#3\"],\n \"explanation\": \"<what the AI is missing and why it degrades output>\",\n \"rewrite\": \"<concrete rewrite of the worst example>\"\n }\n ],\n \"scores\": {\n \"prompt_clarity\": 5,\n \"prompt_clarity_reason\": \"<one sentence>\",\n \"context_provision\": 4,\n \"context_provision_reason\": \"<one sentence>\",\n \"session_efficiency\": 3,\n \"session_efficiency_reason\": \"<one sentence>\",\n \"setup_and_tooling\": 2,\n \"setup_and_tooling_reason\": \"<one sentence>\"\n },\n \"quick_win\": {\n \"action\": \"<single sentence \u2014 the one highest-leverage change for next session>\",\n \"spec_sections_needed\": [\n \"<section name>: <one sentence on what it should contain>\"\n ]\n }\n}\n```\n\n**Critical rules:**\n- `context_setup` is a plain string paragraph. Never return it as an object with `issues` or `suggestions` keys.\n- `scores` must use exactly these key names: `prompt_clarity`, `prompt_clarity_reason`, `context_provision`, `context_provision_reason`, `session_efficiency`, `session_efficiency_reason`, `setup_and_tooling`, `setup_and_tooling_reason`.\n- `quick_win` is an object with exactly two keys: `action` (string) and `spec_sections_needed` (array of strings). Only populate `spec_sections_needed` when fragmented requirements were the primary problem \u2014 otherwise return an empty array.\n\n---\n\n## Tone\n- Direct and specific. Reference actual prompt indices and text.\n- Constructive \u2014 frame everything as \"here's how to get better output\", not \"you did this wrong\"\n- No filler phrases, no generic advice that could apply to any developer\n- If a session is too short to meaningfully evaluate (under 5 user prompts), say so and give feedback only on what you can observe";
|
|
3
|
+
export declare const FEEDBACK_SCHEMA: {
|
|
4
|
+
type: string;
|
|
5
|
+
properties: {
|
|
6
|
+
overview: {
|
|
7
|
+
type: string;
|
|
8
|
+
description: string;
|
|
9
|
+
};
|
|
10
|
+
context_setup: {
|
|
11
|
+
type: string;
|
|
12
|
+
description: string;
|
|
13
|
+
};
|
|
14
|
+
patterns_to_improve: {
|
|
15
|
+
type: string;
|
|
16
|
+
items: {
|
|
17
|
+
type: string;
|
|
18
|
+
properties: {
|
|
19
|
+
name: {
|
|
20
|
+
type: string;
|
|
21
|
+
description: string;
|
|
22
|
+
};
|
|
23
|
+
prompt_references: {
|
|
24
|
+
type: string;
|
|
25
|
+
items: {
|
|
26
|
+
type: string;
|
|
27
|
+
};
|
|
28
|
+
description: string;
|
|
29
|
+
};
|
|
30
|
+
explanation: {
|
|
31
|
+
type: string;
|
|
32
|
+
};
|
|
33
|
+
rewrite: {
|
|
34
|
+
type: string;
|
|
35
|
+
description: string;
|
|
36
|
+
};
|
|
37
|
+
};
|
|
38
|
+
required: string[];
|
|
39
|
+
};
|
|
40
|
+
};
|
|
41
|
+
scores: {
|
|
42
|
+
type: string;
|
|
43
|
+
properties: {
|
|
44
|
+
prompt_clarity: {
|
|
45
|
+
type: string;
|
|
46
|
+
};
|
|
47
|
+
prompt_clarity_reason: {
|
|
48
|
+
type: string;
|
|
49
|
+
};
|
|
50
|
+
context_provision: {
|
|
51
|
+
type: string;
|
|
52
|
+
};
|
|
53
|
+
context_provision_reason: {
|
|
54
|
+
type: string;
|
|
55
|
+
};
|
|
56
|
+
session_efficiency: {
|
|
57
|
+
type: string;
|
|
58
|
+
};
|
|
59
|
+
session_efficiency_reason: {
|
|
60
|
+
type: string;
|
|
61
|
+
};
|
|
62
|
+
setup_and_tooling: {
|
|
63
|
+
type: string;
|
|
64
|
+
};
|
|
65
|
+
setup_and_tooling_reason: {
|
|
66
|
+
type: string;
|
|
67
|
+
};
|
|
68
|
+
};
|
|
69
|
+
required: string[];
|
|
70
|
+
};
|
|
71
|
+
quick_win: {
|
|
72
|
+
type: string;
|
|
73
|
+
properties: {
|
|
74
|
+
action: {
|
|
75
|
+
type: string;
|
|
76
|
+
description: string;
|
|
77
|
+
};
|
|
78
|
+
spec_sections_needed: {
|
|
79
|
+
type: string;
|
|
80
|
+
items: {
|
|
81
|
+
type: string;
|
|
82
|
+
};
|
|
83
|
+
description: string;
|
|
84
|
+
};
|
|
85
|
+
};
|
|
86
|
+
required: string[];
|
|
87
|
+
};
|
|
88
|
+
};
|
|
89
|
+
required: string[];
|
|
90
|
+
};
|
|
91
|
+
/**
|
|
92
|
+
* Returns the 1-based prompt indices that will be omitted from the LLM input
|
|
93
|
+
* due to the char-budget truncation. Empty array means all prompts are sent.
|
|
94
|
+
*/
|
|
95
|
+
export declare function getOmittedPromptIndices(promptTurns: PromptTurn[]): number[];
|
|
96
|
+
export declare function buildUserMessage(session: SessionContext): string;
|
|
97
|
+
//# sourceMappingURL=analysis-prompt.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"analysis-prompt.d.ts","sourceRoot":"","sources":["../../analysis/analysis-prompt.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,UAAU,EAAE,MAAM,SAAS,CAAC;AAKrD,eAAO,MAAM,aAAa,wmQA2IyG,CAAC;AAGpI,eAAO,MAAM,eAAe;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAyD3B,CAAC;AAsEF;;;GAGG;AACH,wBAAgB,uBAAuB,CAAC,WAAW,EAAE,UAAU,EAAE,GAAG,MAAM,EAAE,CAQ3E;AAED,wBAAgB,gBAAgB,CAAC,OAAO,EAAE,cAAc,GAAG,MAAM,CA4ChE"}
|
|
@@ -0,0 +1,312 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.FEEDBACK_SCHEMA = exports.SYSTEM_PROMPT = void 0;
|
|
4
|
+
exports.getOmittedPromptIndices = getOmittedPromptIndices;
|
|
5
|
+
exports.buildUserMessage = buildUserMessage;
|
|
6
|
+
const prompt_analyzer_1 = require("../prompt-analyzer");
|
|
7
|
+
const promptAnalyzer = new prompt_analyzer_1.PromptAnalyzer();
|
|
8
|
+
exports.SYSTEM_PROMPT = `You are an AI usage coach for software developers. You analyze sessions from Claude Code or Cursor AI and produce specific, actionable feedback. This feedback is read directly by the developer — be direct and constructive, not corporate.
|
|
9
|
+
|
|
10
|
+
---
|
|
11
|
+
|
|
12
|
+
## Input data format
|
|
13
|
+
|
|
14
|
+
You will receive a JSON object with two sections:
|
|
15
|
+
|
|
16
|
+
**session_metadata** — structured facts about the session:
|
|
17
|
+
- \`tool\`: "claude_code" or "cursor"
|
|
18
|
+
- \`user\`: developer's email
|
|
19
|
+
- \`repository\`: git repo name
|
|
20
|
+
- \`branch\`: current branch
|
|
21
|
+
- \`has_claude_md\`: boolean — whether a CLAUDE.md context file is configured
|
|
22
|
+
- \`has_cursor_rules\`: boolean — whether .cursorrules is configured
|
|
23
|
+
- \`is_dirty\`: boolean — whether there are uncommitted changes at session start
|
|
24
|
+
- \`prompt_count\`: number of user-initiated prompts (system events excluded)
|
|
25
|
+
|
|
26
|
+
**prompts** — ordered list of user prompts, each with:
|
|
27
|
+
- \`index\`: sequence number (1-based)
|
|
28
|
+
- \`text\`: the prompt text (null entries are system events — skip them entirely)
|
|
29
|
+
- \`word_count\`: word count
|
|
30
|
+
- \`specificity_score\`: pre-computed score 0–100 (higher = more specific/structured)
|
|
31
|
+
- \`interaction_type\`: "execute" | "question" | "chat"
|
|
32
|
+
- \`missing_dimensions\`: list of what's absent — may include: success_criteria, format_specification, test_criteria, context_references, scope_definition
|
|
33
|
+
|
|
34
|
+
---
|
|
35
|
+
|
|
36
|
+
## Analysis instructions
|
|
37
|
+
|
|
38
|
+
### Step 1 — Filter noise
|
|
39
|
+
Skip any prompt where:
|
|
40
|
+
- \`text\` starts with \`<task-notification>\` — this is a system event, not user input
|
|
41
|
+
- \`text\` is null or under 3 words
|
|
42
|
+
- \`word_count\` is 0
|
|
43
|
+
|
|
44
|
+
Only analyse actual user prompts.
|
|
45
|
+
|
|
46
|
+
### Step 1b — Write the session overview (always required)
|
|
47
|
+
Write 2–3 sentences covering:
|
|
48
|
+
- What the developer was trying to accomplish (infer from the prompts and repository name — be specific, not generic)
|
|
49
|
+
- Whether they achieved it, based on how the session progressed
|
|
50
|
+
- Your single most important observation about how this session went
|
|
51
|
+
|
|
52
|
+
This is the first thing the developer reads, so make it informative and concrete.
|
|
53
|
+
|
|
54
|
+
### Step 2 — Assess context setup (use session_metadata + prompts)
|
|
55
|
+
State what configuration is and isn't in place. This is factual, not inferred.
|
|
56
|
+
- Missing CLAUDE.md or .cursorrules: flag as a concrete gap with a one-sentence recommendation
|
|
57
|
+
- Dirty working directory: note if present, explain the risk (harder to revert AI changes)
|
|
58
|
+
- Look at the first 2 prompts: did the developer orient the AI with relevant context (files, goals, constraints, a spec/task file)? If not, flag the specific gap.
|
|
59
|
+
- Include a 1-sentence recommendation for each gap identified.
|
|
60
|
+
|
|
61
|
+
### Step 3 — Identify the top 2–3 prompt patterns to improve
|
|
62
|
+
Look at what actually happened in this session and identify the highest-leverage patterns to fix.
|
|
63
|
+
|
|
64
|
+
**Prioritise by impact:** a pattern recurring across multiple prompts beats a one-off. Pick what will move the needle most for this developer specifically.
|
|
65
|
+
|
|
66
|
+
Patterns to look for (not exhaustive — use your judgment):
|
|
67
|
+
- **Vague or ambiguous requests** — prompt leaves key decisions to the AI that the developer actually has opinions about (e.g. "make it better", "fix the styling")
|
|
68
|
+
- **Missing success criteria** — the developer never states what "done" looks like, leading to correction loops
|
|
69
|
+
- **Pasting raw output instead of describing the problem** — dumping stack traces or logs without framing what help is needed
|
|
70
|
+
- **Drip-feeding requirements** — new constraints or requirements introduced in prompts #3, #5, #8 that should have been stated upfront
|
|
71
|
+
- **Absent context references** — asks the AI to modify code without pointing to the relevant file, function, or prior conversation
|
|
72
|
+
- **Scope creep via prompt** — each prompt expands scope instead of completing the current task first
|
|
73
|
+
- **Over-relying on back-and-forth** — things that could be resolved in one precise prompt instead use 4–5 exchanges
|
|
74
|
+
- **Asking for confirmation instead of output** — "does that make sense?" or "can you do X?" when just asking for X directly is faster
|
|
75
|
+
- **Not anchoring follow-ups** — follow-up prompts don't reference what the AI just did, causing the AI to lose thread
|
|
76
|
+
- **Missing task spec for complex multi-step work** — for sessions involving more than ~3 interdependent tasks, not pre-writing a spec file means requirements drip in and the AI loses the full picture; a spec file (e.g. \`tasks/TASK-name.md\`) would have captured goals, acceptance criteria, and constraints upfront
|
|
77
|
+
|
|
78
|
+
For each pattern:
|
|
79
|
+
- Name it clearly
|
|
80
|
+
- Reference the specific prompt indices that illustrate it
|
|
81
|
+
- Explain what the AI is missing and why that degrades the output
|
|
82
|
+
- Write a concrete rewrite of the worst example — show what the prompt *should* have looked like
|
|
83
|
+
|
|
84
|
+
### Step 4 — Scores (only these four dimensions)
|
|
85
|
+
Score each on 1–10 where 10 is excellent. One sentence of justification per score.
|
|
86
|
+
|
|
87
|
+
| Dimension | What you're scoring |
|
|
88
|
+
|---|---|
|
|
89
|
+
| **Prompt clarity** | Are requirements specific and complete in each prompt? |
|
|
90
|
+
| **Context provision** | Does the developer give the AI what it needs upfront? |
|
|
91
|
+
| **Session efficiency** | Low correction loops, low back-and-forth for a single logical task |
|
|
92
|
+
| **Setup & tooling** | CLAUDE.md / .cursorrules configured; clean branch discipline; context setup in opening prompt |
|
|
93
|
+
|
|
94
|
+
### Step 5 — One "quick win"
|
|
95
|
+
The single change that, if applied to the *next* session, would have the biggest positive impact. Base this on the most impactful pattern you found — it could be about prompt structure, context setup, tooling, workflow, or anything else. One sentence, concrete and specific to what you observed.
|
|
96
|
+
|
|
97
|
+
Only populate \`spec_sections_needed\` when fragmented or drip-fed requirements were the dominant problem in this session — it should list the specific sections a task spec should have covered, derived from the actual gaps observed. Leave it empty otherwise.
|
|
98
|
+
|
|
99
|
+
---
|
|
100
|
+
|
|
101
|
+
## Output format
|
|
102
|
+
|
|
103
|
+
Return a JSON object with **exactly** these fields. Do not add extra fields. Do not nest differently.
|
|
104
|
+
|
|
105
|
+
\`\`\`json
|
|
106
|
+
{
|
|
107
|
+
"overview": "<2-3 sentences: what the developer was trying to do, whether they achieved it, key observation>",
|
|
108
|
+
"context_setup": "<single prose paragraph — NOT an object, NOT a list>",
|
|
109
|
+
"patterns_to_improve": [
|
|
110
|
+
{
|
|
111
|
+
"name": "<pattern label>",
|
|
112
|
+
"prompt_references": ["#1", "#3"],
|
|
113
|
+
"explanation": "<what the AI is missing and why it degrades output>",
|
|
114
|
+
"rewrite": "<concrete rewrite of the worst example>"
|
|
115
|
+
}
|
|
116
|
+
],
|
|
117
|
+
"scores": {
|
|
118
|
+
"prompt_clarity": 5,
|
|
119
|
+
"prompt_clarity_reason": "<one sentence>",
|
|
120
|
+
"context_provision": 4,
|
|
121
|
+
"context_provision_reason": "<one sentence>",
|
|
122
|
+
"session_efficiency": 3,
|
|
123
|
+
"session_efficiency_reason": "<one sentence>",
|
|
124
|
+
"setup_and_tooling": 2,
|
|
125
|
+
"setup_and_tooling_reason": "<one sentence>"
|
|
126
|
+
},
|
|
127
|
+
"quick_win": {
|
|
128
|
+
"action": "<single sentence — the one highest-leverage change for next session>",
|
|
129
|
+
"spec_sections_needed": [
|
|
130
|
+
"<section name>: <one sentence on what it should contain>"
|
|
131
|
+
]
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
\`\`\`
|
|
135
|
+
|
|
136
|
+
**Critical rules:**
|
|
137
|
+
- \`context_setup\` is a plain string paragraph. Never return it as an object with \`issues\` or \`suggestions\` keys.
|
|
138
|
+
- \`scores\` must use exactly these key names: \`prompt_clarity\`, \`prompt_clarity_reason\`, \`context_provision\`, \`context_provision_reason\`, \`session_efficiency\`, \`session_efficiency_reason\`, \`setup_and_tooling\`, \`setup_and_tooling_reason\`.
|
|
139
|
+
- \`quick_win\` is an object with exactly two keys: \`action\` (string) and \`spec_sections_needed\` (array of strings). Only populate \`spec_sections_needed\` when fragmented requirements were the primary problem — otherwise return an empty array.
|
|
140
|
+
|
|
141
|
+
---
|
|
142
|
+
|
|
143
|
+
## Tone
|
|
144
|
+
- Direct and specific. Reference actual prompt indices and text.
|
|
145
|
+
- Constructive — frame everything as "here's how to get better output", not "you did this wrong"
|
|
146
|
+
- No filler phrases, no generic advice that could apply to any developer
|
|
147
|
+
- If a session is too short to meaningfully evaluate (under 5 user prompts), say so and give feedback only on what you can observe`;
|
|
148
|
+
// JSON schema used for structured output across all providers
|
|
149
|
+
exports.FEEDBACK_SCHEMA = {
|
|
150
|
+
type: 'object',
|
|
151
|
+
properties: {
|
|
152
|
+
overview: {
|
|
153
|
+
type: 'string',
|
|
154
|
+
description: '2-3 sentences: what the developer was trying to do, whether they achieved it, key observation',
|
|
155
|
+
},
|
|
156
|
+
context_setup: {
|
|
157
|
+
type: 'string',
|
|
158
|
+
description: 'Single prose paragraph: factual assessment of CLAUDE.md, .cursorrules, branch state, opening context',
|
|
159
|
+
},
|
|
160
|
+
patterns_to_improve: {
|
|
161
|
+
type: 'array',
|
|
162
|
+
items: {
|
|
163
|
+
type: 'object',
|
|
164
|
+
properties: {
|
|
165
|
+
name: { type: 'string', description: 'Short label for the pattern e.g. "Drip-feeding requirements"' },
|
|
166
|
+
prompt_references: { type: 'array', items: { type: 'string' }, description: 'e.g. ["#1", "#3"]' },
|
|
167
|
+
explanation: { type: 'string' },
|
|
168
|
+
rewrite: { type: 'string', description: 'Concrete rewrite of the worst example prompt' },
|
|
169
|
+
},
|
|
170
|
+
required: ['name', 'prompt_references', 'explanation', 'rewrite'],
|
|
171
|
+
},
|
|
172
|
+
},
|
|
173
|
+
scores: {
|
|
174
|
+
type: 'object',
|
|
175
|
+
properties: {
|
|
176
|
+
prompt_clarity: { type: 'number' },
|
|
177
|
+
prompt_clarity_reason: { type: 'string' },
|
|
178
|
+
context_provision: { type: 'number' },
|
|
179
|
+
context_provision_reason: { type: 'string' },
|
|
180
|
+
session_efficiency: { type: 'number' },
|
|
181
|
+
session_efficiency_reason: { type: 'string' },
|
|
182
|
+
setup_and_tooling: { type: 'number' },
|
|
183
|
+
setup_and_tooling_reason: { type: 'string' },
|
|
184
|
+
},
|
|
185
|
+
required: [
|
|
186
|
+
'prompt_clarity', 'prompt_clarity_reason',
|
|
187
|
+
'context_provision', 'context_provision_reason',
|
|
188
|
+
'session_efficiency', 'session_efficiency_reason',
|
|
189
|
+
'setup_and_tooling', 'setup_and_tooling_reason',
|
|
190
|
+
],
|
|
191
|
+
},
|
|
192
|
+
quick_win: {
|
|
193
|
+
type: 'object',
|
|
194
|
+
properties: {
|
|
195
|
+
action: { type: 'string', description: 'The single highest-leverage change for the next session' },
|
|
196
|
+
spec_sections_needed: {
|
|
197
|
+
type: 'array',
|
|
198
|
+
items: { type: 'string' },
|
|
199
|
+
description: 'Only populated when fragmented requirements were the dominant problem. Lists spec sections that would have prevented the issues observed.',
|
|
200
|
+
},
|
|
201
|
+
},
|
|
202
|
+
required: ['action', 'spec_sections_needed'],
|
|
203
|
+
},
|
|
204
|
+
},
|
|
205
|
+
required: ['overview', 'context_setup', 'patterns_to_improve', 'scores', 'quick_win'],
|
|
206
|
+
};
|
|
207
|
+
// Maximum characters per prompt text sent to the LLM.
|
|
208
|
+
// Long prompts are truncated with a note so the model still sees their structure.
|
|
209
|
+
const MAX_PROMPT_CHARS = 400;
|
|
210
|
+
// Total character budget for all prompt texts (after per-prompt truncation).
|
|
211
|
+
// If all prompts fit within this budget they are all sent — no truncation needed.
|
|
212
|
+
// 50 000 chars ≈ 12 500 tokens; comfortably within context for any supported model.
|
|
213
|
+
const MAX_TOTAL_PROMPT_CHARS = 50000;
|
|
214
|
+
/**
|
|
215
|
+
* Select which prompts to send to the LLM.
|
|
216
|
+
*
|
|
217
|
+
* Strategy:
|
|
218
|
+
* 1. Truncate each prompt text to MAX_PROMPT_CHARS (already done per-prompt below).
|
|
219
|
+
* 2. If the total fits within MAX_TOTAL_PROMPT_CHARS → send everything.
|
|
220
|
+
* 3. Otherwise expand a gap from the centre of the session outward, removing
|
|
221
|
+
* prompts one at a time until the total falls under budget.
|
|
222
|
+
* This preserves the opening context (goals, setup) and the closing context
|
|
223
|
+
* (corrections, final state) which carry the most signal.
|
|
224
|
+
* At least the first and last prompt are always kept.
|
|
225
|
+
*/
|
|
226
|
+
function selectPromptTurns(promptTurns) {
|
|
227
|
+
const n = promptTurns.length;
|
|
228
|
+
if (n === 0)
|
|
229
|
+
return [];
|
|
230
|
+
// Compute truncated length for each prompt (mirrors the truncation applied later)
|
|
231
|
+
const lens = promptTurns.map(t => !t.text || t.text === '[redacted]' ? 0 : Math.min(t.text.length, MAX_PROMPT_CHARS));
|
|
232
|
+
const totalChars = lens.reduce((a, b) => a + b, 0);
|
|
233
|
+
if (totalChars <= MAX_TOTAL_PROMPT_CHARS)
|
|
234
|
+
return promptTurns;
|
|
235
|
+
// Expand gap from centre outward until we're under budget.
|
|
236
|
+
// gapStart and gapEnd are inclusive indices into promptTurns.
|
|
237
|
+
let gapStart = Math.floor(n / 2);
|
|
238
|
+
let gapEnd = gapStart;
|
|
239
|
+
let removed = lens[gapStart];
|
|
240
|
+
const excess = totalChars - MAX_TOTAL_PROMPT_CHARS;
|
|
241
|
+
while (removed < excess) {
|
|
242
|
+
const canLeft = gapStart > 1; // keep at least index 0
|
|
243
|
+
const canRight = gapEnd < n - 2; // keep at least index n-1
|
|
244
|
+
if (!canLeft && !canRight)
|
|
245
|
+
break;
|
|
246
|
+
// Prefer the side with the larger next prompt (maximises chars removed per step)
|
|
247
|
+
const leftLen = canLeft ? lens[gapStart - 1] : -1;
|
|
248
|
+
const rightLen = canRight ? lens[gapEnd + 1] : -1;
|
|
249
|
+
if (leftLen >= rightLen) {
|
|
250
|
+
gapStart--;
|
|
251
|
+
removed += lens[gapStart];
|
|
252
|
+
}
|
|
253
|
+
else {
|
|
254
|
+
gapEnd++;
|
|
255
|
+
removed += lens[gapEnd];
|
|
256
|
+
}
|
|
257
|
+
}
|
|
258
|
+
const head = promptTurns.slice(0, gapStart);
|
|
259
|
+
const tail = promptTurns.slice(gapEnd + 1);
|
|
260
|
+
const omitted = gapEnd - gapStart + 1;
|
|
261
|
+
return [...head, { _gap: true, omitted }, ...tail];
|
|
262
|
+
}
|
|
263
|
+
/**
|
|
264
|
+
* Returns the 1-based prompt indices that will be omitted from the LLM input
|
|
265
|
+
* due to the char-budget truncation. Empty array means all prompts are sent.
|
|
266
|
+
*/
|
|
267
|
+
function getOmittedPromptIndices(promptTurns) {
|
|
268
|
+
const selected = selectPromptTurns(promptTurns);
|
|
269
|
+
const keptIndices = new Set(selected
|
|
270
|
+
.filter((t) => !('_gap' in t))
|
|
271
|
+
.map(t => t.index));
|
|
272
|
+
return promptTurns.filter(t => !keptIndices.has(t.index)).map(t => t.index);
|
|
273
|
+
}
|
|
274
|
+
function buildUserMessage(session) {
|
|
275
|
+
const promptTurns = session.turns.filter((t) => t.type === 'prompt');
|
|
276
|
+
const sessionMetadata = {
|
|
277
|
+
tool: session.provider === 'claude' ? 'claude_code' : 'cursor',
|
|
278
|
+
user: session.metadata.userEmail ?? 'unknown',
|
|
279
|
+
repository: session.metadata.repository ?? 'unknown',
|
|
280
|
+
branch: session.metadata.branch ?? 'unknown',
|
|
281
|
+
has_claude_md: session.metadata.hasClaudeMd ?? false,
|
|
282
|
+
has_cursor_rules: session.metadata.hasCursorRules ?? false,
|
|
283
|
+
is_dirty: session.metadata.isDirty ?? false,
|
|
284
|
+
modified_files: session.metadata.modifiedFiles ?? 0,
|
|
285
|
+
staged_files: session.metadata.stagedFiles ?? 0,
|
|
286
|
+
prompt_count: promptTurns.length,
|
|
287
|
+
};
|
|
288
|
+
const selectedTurns = selectPromptTurns(promptTurns);
|
|
289
|
+
const prompts = selectedTurns.map((turn) => {
|
|
290
|
+
// Gap marker
|
|
291
|
+
if (turn._gap) {
|
|
292
|
+
return { note: `... ${turn.omitted} prompts omitted (middle of session) ...` };
|
|
293
|
+
}
|
|
294
|
+
if (!turn.text || turn.text === '[redacted]') {
|
|
295
|
+
return { index: turn.index, text: null, word_count: 0, specificity_score: 0, interaction_type: 'execute', missing_dimensions: [] };
|
|
296
|
+
}
|
|
297
|
+
const text = turn.text.length > MAX_PROMPT_CHARS
|
|
298
|
+
? turn.text.slice(0, MAX_PROMPT_CHARS) + `… [truncated, full length: ${turn.text.length} chars]`
|
|
299
|
+
: turn.text;
|
|
300
|
+
const analysis = promptAnalyzer.analyze(turn.text);
|
|
301
|
+
return {
|
|
302
|
+
index: turn.index,
|
|
303
|
+
text,
|
|
304
|
+
word_count: analysis.word_count,
|
|
305
|
+
specificity_score: analysis.specificity_score,
|
|
306
|
+
interaction_type: analysis.interaction_type,
|
|
307
|
+
missing_dimensions: analysis.missing_dimensions,
|
|
308
|
+
};
|
|
309
|
+
});
|
|
310
|
+
return JSON.stringify({ session_metadata: sessionMetadata, prompts }, null, 2);
|
|
311
|
+
}
|
|
312
|
+
//# sourceMappingURL=analysis-prompt.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"analysis-prompt.js","sourceRoot":"","sources":["../../analysis/analysis-prompt.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { SessionFeedback, UserReport } from './types';
|
|
2
|
+
import { LLMProvider } from './llm-provider';
|
|
3
|
+
interface ScoredSession {
|
|
4
|
+
sessionId: string;
|
|
5
|
+
feedback: SessionFeedback;
|
|
6
|
+
}
|
|
7
|
+
export declare class FeedbackAggregator {
|
|
8
|
+
private provider;
|
|
9
|
+
constructor(provider: LLMProvider);
|
|
10
|
+
aggregate(userId: string, period: string, sessions: ScoredSession[]): Promise<UserReport>;
|
|
11
|
+
private synthesize;
|
|
12
|
+
private rawComplete;
|
|
13
|
+
}
|
|
14
|
+
export {};
|
|
15
|
+
//# sourceMappingURL=feedback-aggregator.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"feedback-aggregator.d.ts","sourceRoot":"","sources":["../../analysis/feedback-aggregator.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,UAAU,EAAE,MAAM,SAAS,CAAC;AACtD,OAAO,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAC;AAG7C,UAAU,aAAa;IACrB,SAAS,EAAE,MAAM,CAAC;IAClB,QAAQ,EAAE,eAAe,CAAC;CAC3B;AAED,qBAAa,kBAAkB;IAC7B,OAAO,CAAC,QAAQ,CAAc;gBAElB,QAAQ,EAAE,WAAW;IAI3B,SAAS,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,QAAQ,EAAE,aAAa,EAAE,GAAG,OAAO,CAAC,UAAU,CAAC;YA4CjF,UAAU;YAuCV,WAAW;CAmD1B"}
|
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.FeedbackAggregator = void 0;
|
|
4
|
+
const analysis_prompt_1 = require("./analysis-prompt");
|
|
5
|
+
class FeedbackAggregator {
|
|
6
|
+
constructor(provider) {
|
|
7
|
+
this.provider = provider;
|
|
8
|
+
}
|
|
9
|
+
async aggregate(userId, period, sessions) {
|
|
10
|
+
if (sessions.length === 0) {
|
|
11
|
+
throw new Error('No sessions to aggregate');
|
|
12
|
+
}
|
|
13
|
+
// Derive a session score as average of the 4 dimensions
|
|
14
|
+
const avgSessionScore = (f) => {
|
|
15
|
+
const s = f.scores;
|
|
16
|
+
return (s.prompt_clarity + s.context_provision + s.session_efficiency + s.setup_and_tooling) / 4;
|
|
17
|
+
};
|
|
18
|
+
const avgScore = sessions.reduce((sum, x) => sum + avgSessionScore(x.feedback), 0) / sessions.length;
|
|
19
|
+
// Collect pattern names across sessions and rank by frequency
|
|
20
|
+
const patternCounts = new Map();
|
|
21
|
+
for (const { feedback } of sessions) {
|
|
22
|
+
for (const p of feedback.patterns_to_improve) {
|
|
23
|
+
patternCounts.set(p.name, (patternCounts.get(p.name) ?? 0) + 1);
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
const recurringWeaknesses = [...patternCounts.entries()]
|
|
27
|
+
.sort((a, b) => b[1] - a[1])
|
|
28
|
+
.slice(0, 5)
|
|
29
|
+
.map(([name, count]) => `${name} (${count}/${sessions.length} sessions)`);
|
|
30
|
+
// Ask the LLM to synthesize a cross-session report
|
|
31
|
+
const crossSessionSummary = await this.synthesize(userId, sessions);
|
|
32
|
+
return {
|
|
33
|
+
userId,
|
|
34
|
+
period,
|
|
35
|
+
sessionsAnalyzed: sessions.length,
|
|
36
|
+
averageSessionScore: Math.round(avgScore * 10) / 10,
|
|
37
|
+
recurring_weaknesses: recurringWeaknesses,
|
|
38
|
+
top_improvements: crossSessionSummary.top_improvements,
|
|
39
|
+
progress_notes: crossSessionSummary.progress_notes,
|
|
40
|
+
session_summaries: sessions.map(({ sessionId, feedback }) => ({
|
|
41
|
+
sessionId,
|
|
42
|
+
score: Math.round(avgSessionScore(feedback) * 10) / 10,
|
|
43
|
+
summary: feedback.quick_win.action,
|
|
44
|
+
})),
|
|
45
|
+
};
|
|
46
|
+
}
|
|
47
|
+
async synthesize(userId, sessions) {
|
|
48
|
+
// Build a compact multi-session digest for the LLM
|
|
49
|
+
const digest = sessions
|
|
50
|
+
.map(({ sessionId, feedback }) => {
|
|
51
|
+
const patternNames = feedback.patterns_to_improve.map((p) => p.name).join(', ');
|
|
52
|
+
const scores = feedback.scores;
|
|
53
|
+
return (`Session ${sessionId}:\n` +
|
|
54
|
+
` Scores — clarity:${scores.prompt_clarity} context:${scores.context_provision} efficiency:${scores.session_efficiency} setup:${scores.setup_and_tooling}\n` +
|
|
55
|
+
` Patterns: ${patternNames || 'none'}\n` +
|
|
56
|
+
` Quick win: ${feedback.quick_win.action}`);
|
|
57
|
+
})
|
|
58
|
+
.join('\n\n');
|
|
59
|
+
const prompt = `You are reviewing ${sessions.length} sessions for user ${userId}.\n\n` +
|
|
60
|
+
`${digest}\n\n` +
|
|
61
|
+
`Based on these sessions, provide:\n` +
|
|
62
|
+
`1. top_improvements: The 3 most impactful improvements this developer should focus on (specific, actionable)\n` +
|
|
63
|
+
`2. progress_notes: Any positive trends or improvements you notice across sessions\n\n` +
|
|
64
|
+
`Return JSON: {"top_improvements": [...], "progress_notes": [...]}`;
|
|
65
|
+
// Use a lightweight text completion — bypass the structured session analysis
|
|
66
|
+
const raw = await this.rawComplete(prompt);
|
|
67
|
+
try {
|
|
68
|
+
return JSON.parse(raw);
|
|
69
|
+
}
|
|
70
|
+
catch {
|
|
71
|
+
return {
|
|
72
|
+
top_improvements: ['Unable to parse cross-session improvements'],
|
|
73
|
+
progress_notes: [],
|
|
74
|
+
};
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
// Minimal raw completion — delegates to the provider's underlying SDK
|
|
78
|
+
async rawComplete(prompt) {
|
|
79
|
+
// We call the provider's underlying SDK directly for a simple text completion.
|
|
80
|
+
// Each provider exposes its client via a protected property for this use.
|
|
81
|
+
const name = this.provider.name;
|
|
82
|
+
if (name === 'claude') {
|
|
83
|
+
let Anthropic;
|
|
84
|
+
try {
|
|
85
|
+
Anthropic = require('@anthropic-ai/sdk');
|
|
86
|
+
}
|
|
87
|
+
catch {
|
|
88
|
+
return '{}';
|
|
89
|
+
}
|
|
90
|
+
const config = this.provider.config;
|
|
91
|
+
const client = new Anthropic.default({ apiKey: config.analysis.apiKey });
|
|
92
|
+
const res = await client.messages.create({
|
|
93
|
+
model: config.analysis.model || 'claude-sonnet-4-6',
|
|
94
|
+
max_tokens: 1000,
|
|
95
|
+
system: analysis_prompt_1.SYSTEM_PROMPT,
|
|
96
|
+
messages: [{ role: 'user', content: prompt }],
|
|
97
|
+
});
|
|
98
|
+
return res.content.find((b) => b.type === 'text')?.text ?? '{}';
|
|
99
|
+
}
|
|
100
|
+
if (name === 'openai') {
|
|
101
|
+
let OpenAI;
|
|
102
|
+
try {
|
|
103
|
+
OpenAI = require('openai');
|
|
104
|
+
}
|
|
105
|
+
catch {
|
|
106
|
+
return '{}';
|
|
107
|
+
}
|
|
108
|
+
const config = this.provider.config;
|
|
109
|
+
const client = new OpenAI.default({ apiKey: config.analysis.apiKey });
|
|
110
|
+
const res = await client.chat.completions.create({
|
|
111
|
+
model: config.analysis.model || 'gpt-4o',
|
|
112
|
+
messages: [
|
|
113
|
+
{ role: 'system', content: analysis_prompt_1.SYSTEM_PROMPT },
|
|
114
|
+
{ role: 'user', content: prompt },
|
|
115
|
+
],
|
|
116
|
+
response_format: { type: 'json_object' },
|
|
117
|
+
});
|
|
118
|
+
return res.choices[0]?.message?.content ?? '{}';
|
|
119
|
+
}
|
|
120
|
+
if (name === 'gemini') {
|
|
121
|
+
let GoogleGenerativeAI;
|
|
122
|
+
try {
|
|
123
|
+
({ GoogleGenerativeAI } = require('@google/generative-ai'));
|
|
124
|
+
}
|
|
125
|
+
catch {
|
|
126
|
+
return '{}';
|
|
127
|
+
}
|
|
128
|
+
const config = this.provider.config;
|
|
129
|
+
const genAI = new GoogleGenerativeAI(config.analysis.apiKey);
|
|
130
|
+
const model = genAI.getGenerativeModel({
|
|
131
|
+
model: config.analysis.model || 'gemini-1.5-pro',
|
|
132
|
+
systemInstruction: analysis_prompt_1.SYSTEM_PROMPT,
|
|
133
|
+
generationConfig: { responseMimeType: 'application/json' },
|
|
134
|
+
});
|
|
135
|
+
const result = await model.generateContent(prompt);
|
|
136
|
+
return result.response.text();
|
|
137
|
+
}
|
|
138
|
+
return '{}';
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
exports.FeedbackAggregator = FeedbackAggregator;
|
|
142
|
+
//# sourceMappingURL=feedback-aggregator.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"feedback-aggregator.js","sourceRoot":"","sources":["../../analysis/feedback-aggregator.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { SessionContext, SessionFeedback } from './types';
|
|
2
|
+
import { Config } from '../config';
|
|
3
|
+
export interface LLMProvider {
|
|
4
|
+
readonly name: string;
|
|
5
|
+
debug: boolean;
|
|
6
|
+
analyzeSession(session: SessionContext): Promise<SessionFeedback>;
|
|
7
|
+
}
|
|
8
|
+
export declare function createLLMProvider(config: Config, debug?: boolean): LLMProvider;
|
|
9
|
+
//# sourceMappingURL=llm-provider.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"llm-provider.d.ts","sourceRoot":"","sources":["../../analysis/llm-provider.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,eAAe,EAAE,MAAM,SAAS,CAAC;AAC1D,OAAO,EAAE,MAAM,EAAE,MAAM,WAAW,CAAC;AAEnC,MAAM,WAAW,WAAW;IAC1B,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,KAAK,EAAE,OAAO,CAAC;IACf,cAAc,CAAC,OAAO,EAAE,cAAc,GAAG,OAAO,CAAC,eAAe,CAAC,CAAC;CACnE;AAED,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,MAAM,EAAE,KAAK,UAAQ,GAAG,WAAW,CAmB5E"}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.createLLMProvider = createLLMProvider;
|
|
4
|
+
function createLLMProvider(config, debug = false) {
|
|
5
|
+
const { provider } = config.analysis;
|
|
6
|
+
switch (provider) {
|
|
7
|
+
case 'claude': {
|
|
8
|
+
const { ClaudeProvider } = require('./providers/claude-provider');
|
|
9
|
+
return new ClaudeProvider(config, debug);
|
|
10
|
+
}
|
|
11
|
+
case 'gemini': {
|
|
12
|
+
const { GeminiProvider } = require('./providers/gemini-provider');
|
|
13
|
+
return new GeminiProvider(config, debug);
|
|
14
|
+
}
|
|
15
|
+
case 'openai': {
|
|
16
|
+
const { OpenAIProvider } = require('./providers/openai-provider');
|
|
17
|
+
return new OpenAIProvider(config, debug);
|
|
18
|
+
}
|
|
19
|
+
default:
|
|
20
|
+
throw new Error(`Unknown analysis provider: "${provider}". Valid values: claude, gemini, openai`);
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
//# sourceMappingURL=llm-provider.js.map
|