ownkey 1.3.0 → 1.4.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/README.md +53 -1
- package/dist/ai/anthropic.d.ts +2 -3
- package/dist/ai/anthropic.d.ts.map +1 -1
- package/dist/ai/anthropic.js +5 -15
- package/dist/ai/anthropic.js.map +1 -1
- package/dist/ai/gemini.d.ts +3 -3
- package/dist/ai/gemini.d.ts.map +1 -1
- package/dist/ai/gemini.js +9 -20
- package/dist/ai/gemini.js.map +1 -1
- package/dist/ai/ollama.d.ts +7 -3
- package/dist/ai/ollama.d.ts.map +1 -1
- package/dist/ai/ollama.js +27 -17
- package/dist/ai/ollama.js.map +1 -1
- package/dist/ai/openai.d.ts +2 -3
- package/dist/ai/openai.d.ts.map +1 -1
- package/dist/ai/openai.js +7 -15
- package/dist/ai/openai.js.map +1 -1
- package/dist/ai/provider.d.ts +17 -8
- package/dist/ai/provider.d.ts.map +1 -1
- package/dist/ai/provider.js +16 -0
- package/dist/ai/provider.js.map +1 -1
- package/dist/bench/cases.d.ts +5 -0
- package/dist/bench/cases.d.ts.map +1 -1
- package/dist/bench/cases.js +47 -0
- package/dist/bench/cases.js.map +1 -1
- package/dist/commands/bench.d.ts +2 -1
- package/dist/commands/bench.d.ts.map +1 -1
- package/dist/commands/bench.js +13 -5
- package/dist/commands/bench.js.map +1 -1
- package/dist/commands/review.d.ts.map +1 -1
- package/dist/commands/review.js +25 -3
- package/dist/commands/review.js.map +1 -1
- package/dist/core/review/agent.d.ts +58 -0
- package/dist/core/review/agent.d.ts.map +1 -0
- package/dist/core/review/agent.js +225 -0
- package/dist/core/review/agent.js.map +1 -0
- package/dist/core/review/report.d.ts +2 -2
- package/dist/core/review/report.d.ts.map +1 -1
- package/dist/core/review/report.js +4 -2
- package/dist/core/review/report.js.map +1 -1
- package/dist/core/review/run.d.ts +11 -2
- package/dist/core/review/run.d.ts.map +1 -1
- package/dist/core/review/run.js +12 -4
- package/dist/core/review/run.js.map +1 -1
- package/dist/types/index.d.ts +2 -0
- package/dist/types/index.d.ts.map +1 -1
- package/dist/types/index.js.map +1 -1
- package/dist/ui/App.d.ts +11 -3
- package/dist/ui/App.d.ts.map +1 -1
- package/dist/ui/App.js +20 -6
- package/dist/ui/App.js.map +1 -1
- package/dist/ui/Keeper.d.ts +18 -0
- package/dist/ui/Keeper.d.ts.map +1 -0
- package/dist/ui/Keeper.js +45 -0
- package/dist/ui/Keeper.js.map +1 -0
- package/dist/ui/keeper-term.d.ts +33 -0
- package/dist/ui/keeper-term.d.ts.map +1 -0
- package/dist/ui/keeper-term.js +104 -0
- package/dist/ui/keeper-term.js.map +1 -0
- package/dist/ui/keeper.d.ts +60 -0
- package/dist/ui/keeper.d.ts.map +1 -0
- package/dist/ui/keeper.js +167 -0
- package/dist/ui/keeper.js.map +1 -0
- package/dist/ui/session.d.ts +3 -0
- package/dist/ui/session.d.ts.map +1 -1
- package/dist/ui/session.js +32 -7
- package/dist/ui/session.js.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
<p align="center"><img src="assets/keeper.svg" alt="Keeper, the OwnKey mascot: a living orange key with one inspecting eye, holding a magnifying glass" width="132"></p>
|
|
2
|
+
|
|
1
3
|
<h1 align="center">OwnKey</h1>
|
|
2
4
|
|
|
3
5
|
<h3 align="center">The local-first AI code reviewer that runs on your own keys.</h3>
|
|
@@ -102,6 +104,24 @@ ownkey undo --list # recent applies
|
|
|
102
104
|
|
|
103
105
|
For a whole-project audit of any folder, git or not, use `ownkey suggest .`
|
|
104
106
|
|
|
107
|
+
## How a review works
|
|
108
|
+
|
|
109
|
+
OwnKey sends the model your changed files and the diff, then lets it investigate before it answers, much like a teammate who opens a few files before leaving a comment. With read-only tools, it can:
|
|
110
|
+
|
|
111
|
+
- read any file in the repository, with line numbers
|
|
112
|
+
- search the codebase with a regular expression
|
|
113
|
+
- list a folder
|
|
114
|
+
|
|
115
|
+
Each step is printed as it happens:
|
|
116
|
+
|
|
117
|
+
```text
|
|
118
|
+
Investigating the repository before reporting...
|
|
119
|
+
↳ search /findOrder\(/
|
|
120
|
+
↳ read src/orders.ts
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
That's how it catches bugs that only show up across files, such as a lookup that can return `undefined`, a value in cents compared against dollars, or an async loader used without `await`. It also stays quiet about code that is already guarded elsewhere. Files ignored by `.gitignore` or `.ownkeyignore` are never readable. Use `--max-steps <n>` to cap the investigation (default 6 rounds), or `--quick` for the old single-pass review.
|
|
124
|
+
|
|
105
125
|
## Interactive session
|
|
106
126
|
|
|
107
127
|
Run `ownkey` with no arguments in any project folder and it opens a session for that folder. The header shows the repository, branch, how many files changed, and which model is reviewing. Then type any command without the `ownkey` prefix:
|
|
@@ -115,6 +135,24 @@ Run `ownkey` with no arguments in any project folder and it opens a session for
|
|
|
115
135
|
|
|
116
136
|
Tab completes commands and their flags, the arrow keys walk your history (kept in `~/.ownkey/history`), `help` lists every command, and Ctrl+D leaves. Commands run exactly as they do from the shell, including interactive prompts like the `config` wizard. Ctrl+C stops the running command without closing the session.
|
|
117
137
|
|
|
138
|
+
### Keeper
|
|
139
|
+
|
|
140
|
+
Keeper, the key with one inspecting eye, keeps you company in the terminal and shows what OwnKey is doing:
|
|
141
|
+
|
|
142
|
+
```text
|
|
143
|
+
╭─────╮ !
|
|
144
|
+
│ ◎ │ Found 3 issues
|
|
145
|
+
╰──┬──╯ 1 high or critical
|
|
146
|
+
├┐
|
|
147
|
+
╵
|
|
148
|
+
```
|
|
149
|
+
|
|
150
|
+
- While a review investigates, Keeper's eye darts between files on a live status line that counts its lookups.
|
|
151
|
+
- When a review finishes, Keeper is alarmed if it found issues, smiles if everything is clear, and shows an × if something went wrong. Back at the session prompt, it keeps that look until your next command.
|
|
152
|
+
- Keeper resizes with your terminal: a full figure from 72 columns, a compact one from 44, a one-line `( ◉ )━┳┳╸` below that, and nothing on very narrow screens. Words never wrap into it, and it redraws when you resize the window.
|
|
153
|
+
|
|
154
|
+
Keeper only moves on an interactive terminal. In CI, pipes, and `--format json|sarif|markdown` output it never appears, so logs and reports stay clean. Set `OWNKEY_REDUCED_MOTION=1` to keep Keeper still, or `OWNKEY_NO_MASCOT=1` to hide it.
|
|
155
|
+
|
|
118
156
|
Outside an interactive terminal (pipes, scripts, CI), or with `OWNKEY_NO_UI=1` set, a bare `ownkey` prints help as before. Every subcommand behaves the same with or without the session.
|
|
119
157
|
|
|
120
158
|
## Commands
|
|
@@ -231,7 +269,20 @@ ownkey bench --model claude-opus-5-5 # compare another model on the sam
|
|
|
231
269
|
ownkey bench --format markdown --output bench.md
|
|
232
270
|
```
|
|
233
271
|
|
|
234
|
-
It reports **recall** (planted bugs reported within 3 lines of the defect with a matching type), **precision** (matching findings over all findings at medium or above), and how many controls stayed clean. Low and info findings never count against a model. Bench runs in temporary directories and stores nothing, but it does send every case to your provider
|
|
272
|
+
It reports **recall** (planted bugs reported within 3 lines of the defect with a matching type), **precision** (matching findings over all findings at medium or above), and how many controls stayed clean. Low and info findings never count against a model. Bench runs in temporary directories and stores nothing, but it does send every case to your provider. A full run is 19 cases: one call each with `--quick`, a few each in agentic mode.
|
|
273
|
+
|
|
274
|
+
### Results
|
|
275
|
+
|
|
276
|
+
One run of all 19 cases (15 bugs, 4 controls) on Ollama Cloud, 2026-10-24, OwnKey 1.4.0:
|
|
277
|
+
|
|
278
|
+
| Model | Mode | Recall | Precision | Controls with false alarms |
|
|
279
|
+
|---|---|---|---|---|
|
|
280
|
+
| `gpt-oss:120b` | agentic | 15/15 | 0.60 | 2 of 4 |
|
|
281
|
+
| `gpt-oss:120b` | quick | 14/15 | 0.44 | 4 of 4 |
|
|
282
|
+
| `gemma4:31b` | agentic | 14/15 | 0.88 | 1 of 4 |
|
|
283
|
+
| `gemma4:31b` | quick | 15/15 | 0.76 | 4 of 4 |
|
|
284
|
+
|
|
285
|
+
Both models catch nearly every planted bug either way. Investigating first mostly cuts noise: on correct changes, the agentic reviewer flagged half or fewer of the controls, against all four in a single pass. A single run on 19 small cases is a smoke test, not a leaderboard, so run `ownkey bench` on your own model before you rely on it.
|
|
235
286
|
|
|
236
287
|
## Providers
|
|
237
288
|
|
|
@@ -241,6 +292,7 @@ It reports **recall** (planted bugs reported within 3 lines of the defect with a
|
|
|
241
292
|
| Anthropic | `--provider anthropic --api-key <key>` | `claude-sonnet-5-5` |
|
|
242
293
|
| Google Gemini | `--provider gemini --api-key <key>` | `gemini-3.8-flash` |
|
|
243
294
|
| Ollama | `--provider ollama [--endpoint http://host:11434]` | `qwen2.5-coder:latest` |
|
|
295
|
+
| Ollama Cloud | `--provider ollama --endpoint https://ollama.com --model gpt-oss:120b`, with `OLLAMA_API_KEY` set | Set with `--model` |
|
|
244
296
|
| OpenAI-compatible | `--provider openai --endpoint <base-url> --api-key <key>` | Set with `--model` |
|
|
245
297
|
|
|
246
298
|
Defaults are mid-tier models chosen for review quality per dollar. Override with `--model`, for example `gpt-6-astra` or `claude-opus-5-5` for the deepest review. OwnKey sends each model the request shape it expects, including reasoning-era OpenAI models and Claude's adaptive thinking.
|
package/dist/ai/anthropic.d.ts
CHANGED
|
@@ -1,5 +1,4 @@
|
|
|
1
|
-
import { AIProvider } from './provider.js';
|
|
2
|
-
import type { CodeContext, Suggestion } from '../types/index.js';
|
|
1
|
+
import { AIProvider, type ChatMessage } from './provider.js';
|
|
3
2
|
/**
|
|
4
3
|
* Anthropic Claude AI Provider
|
|
5
4
|
* Supports all Claude models: Opus, Sonnet, and Haiku variants
|
|
@@ -11,7 +10,7 @@ export declare class AnthropicProvider extends AIProvider {
|
|
|
11
10
|
constructor(apiKey?: string, model?: string);
|
|
12
11
|
getDefaultModel(): string;
|
|
13
12
|
testConnection(): Promise<boolean>;
|
|
14
|
-
|
|
13
|
+
chat(system: string, messages: ChatMessage[]): Promise<string>;
|
|
15
14
|
/**
|
|
16
15
|
* Get available Claude models
|
|
17
16
|
* Note: Anthropic doesn't provide a models API endpoint
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"anthropic.d.ts","sourceRoot":"","sources":["../../src/ai/anthropic.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,
|
|
1
|
+
{"version":3,"file":"anthropic.d.ts","sourceRoot":"","sources":["../../src/ai/anthropic.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,KAAK,WAAW,EAAE,MAAM,eAAe,CAAC;AAI7D;;;;GAIG;AACH,qBAAa,iBAAkB,SAAQ,UAAU;IAC7C,OAAO,CAAC,OAAO,CAAkC;IACjD,OAAO,CAAC,UAAU,CAAgB;gBAEtB,MAAM,CAAC,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,MAAM;IAI3C,eAAe,IAAI,MAAM;IAInB,cAAc,IAAI,OAAO,CAAC,OAAO,CAAC;IAkClC,IAAI,CAAC,MAAM,EAAE,MAAM,EAAE,QAAQ,EAAE,WAAW,EAAE,GAAG,OAAO,CAAC,MAAM,CAAC;IA8CpE;;;;OAIG;WACU,kBAAkB,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC;CAUtE"}
|
package/dist/ai/anthropic.js
CHANGED
|
@@ -47,10 +47,9 @@ export class AnthropicProvider extends AIProvider {
|
|
|
47
47
|
return false;
|
|
48
48
|
}
|
|
49
49
|
}
|
|
50
|
-
async
|
|
51
|
-
const prompt = this.buildUserPrompt(context);
|
|
50
|
+
async chat(system, messages) {
|
|
52
51
|
try {
|
|
53
|
-
logger.verbose(`
|
|
52
|
+
logger.verbose(`Calling Anthropic (${this.model})...`);
|
|
54
53
|
const response = await fetch(`${this.baseUrl}/messages`, {
|
|
55
54
|
method: 'POST',
|
|
56
55
|
headers: {
|
|
@@ -63,13 +62,8 @@ export class AnthropicProvider extends AIProvider {
|
|
|
63
62
|
// No temperature: Claude 5.x models reject sampling parameters.
|
|
64
63
|
// Adaptive thinking shares this budget, so leave headroom.
|
|
65
64
|
max_tokens: 32000,
|
|
66
|
-
system
|
|
67
|
-
messages
|
|
68
|
-
{
|
|
69
|
-
role: 'user',
|
|
70
|
-
content: prompt,
|
|
71
|
-
},
|
|
72
|
-
],
|
|
65
|
+
system,
|
|
66
|
+
messages,
|
|
73
67
|
}),
|
|
74
68
|
});
|
|
75
69
|
if (!response.ok) {
|
|
@@ -86,11 +80,7 @@ export class AnthropicProvider extends AIProvider {
|
|
|
86
80
|
if (!content) {
|
|
87
81
|
throw new Error('No content in Anthropic response');
|
|
88
82
|
}
|
|
89
|
-
|
|
90
|
-
const suggestions = this.parseSuggestions(content);
|
|
91
|
-
for (const suggestion of suggestions) {
|
|
92
|
-
yield suggestion;
|
|
93
|
-
}
|
|
83
|
+
return content;
|
|
94
84
|
}
|
|
95
85
|
catch (error) {
|
|
96
86
|
logger.error('Anthropic analysis error:', error);
|
package/dist/ai/anthropic.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"anthropic.js","sourceRoot":"","sources":["../../src/ai/anthropic.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,
|
|
1
|
+
{"version":3,"file":"anthropic.js","sourceRoot":"","sources":["../../src/ai/anthropic.ts"],"names":[],"mappings":"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"}
|
package/dist/ai/gemini.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { AIProvider } from './provider.js';
|
|
2
|
-
import {
|
|
1
|
+
import { AIProvider, type ChatMessage } from './provider.js';
|
|
2
|
+
import { AnalyzeOptions } from '../types/index.js';
|
|
3
3
|
/**
|
|
4
4
|
* Google Gemini AI Provider
|
|
5
5
|
* Supports: gemini-1.5-pro, gemini-1.5-flash, gemini-pro
|
|
@@ -8,7 +8,7 @@ export declare class GeminiProvider extends AIProvider {
|
|
|
8
8
|
private baseUrl;
|
|
9
9
|
getDefaultModel(): string;
|
|
10
10
|
testConnection(): Promise<boolean>;
|
|
11
|
-
|
|
11
|
+
chat(system: string, messages: ChatMessage[], options?: AnalyzeOptions): Promise<string>;
|
|
12
12
|
/**
|
|
13
13
|
* Get available Gemini models
|
|
14
14
|
*/
|
package/dist/ai/gemini.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"gemini.d.ts","sourceRoot":"","sources":["../../src/ai/gemini.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,eAAe,CAAC;
|
|
1
|
+
{"version":3,"file":"gemini.d.ts","sourceRoot":"","sources":["../../src/ai/gemini.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,KAAK,WAAW,EAAE,MAAM,eAAe,CAAC;AAC7D,OAAO,EAAE,cAAc,EAAmB,MAAM,mBAAmB,CAAC;AA2BpE;;;GAGG;AACH,qBAAa,cAAe,SAAQ,UAAU;IAC1C,OAAO,CAAC,OAAO,CAAkD;IAEjE,eAAe,IAAI,MAAM;IAInB,cAAc,IAAI,OAAO,CAAC,OAAO,CAAC;IAelC,IAAI,CAAC,MAAM,EAAE,MAAM,EAAE,QAAQ,EAAE,WAAW,EAAE,EAAE,OAAO,CAAC,EAAE,cAAc,GAAG,OAAO,CAAC,MAAM,CAAC;IA8D9F;;OAEG;WACU,kBAAkB,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC;IAgClE;;OAEG;IACH,MAAM,CAAC,mBAAmB,IAAI,MAAM;CAGvC"}
|
package/dist/ai/gemini.js
CHANGED
|
@@ -26,21 +26,16 @@ export class GeminiProvider extends AIProvider {
|
|
|
26
26
|
return false;
|
|
27
27
|
}
|
|
28
28
|
}
|
|
29
|
-
async
|
|
29
|
+
async chat(system, messages, options) {
|
|
30
30
|
try {
|
|
31
|
-
logger.verbose(`
|
|
32
|
-
|
|
33
|
-
const
|
|
31
|
+
logger.verbose(`Calling Gemini (${this.model})...`);
|
|
32
|
+
// The system prompt rides as the first part of the first user turn, as before
|
|
33
|
+
const contents = messages.map((m, i) => ({
|
|
34
|
+
role: m.role === 'assistant' ? 'model' : 'user',
|
|
35
|
+
parts: i === 0 ? [{ text: system }, { text: m.content }] : [{ text: m.content }],
|
|
36
|
+
}));
|
|
34
37
|
const request = {
|
|
35
|
-
contents
|
|
36
|
-
{
|
|
37
|
-
role: 'user',
|
|
38
|
-
parts: [
|
|
39
|
-
{ text: systemPrompt },
|
|
40
|
-
{ text: userPrompt },
|
|
41
|
-
],
|
|
42
|
-
},
|
|
43
|
-
],
|
|
38
|
+
contents,
|
|
44
39
|
generationConfig: {
|
|
45
40
|
// Gemini 3.x is tuned for its default temperature; thinking shares the output budget.
|
|
46
41
|
...(options?.temperature !== undefined ? { temperature: options.temperature } : {}),
|
|
@@ -72,13 +67,7 @@ export class GeminiProvider extends AIProvider {
|
|
|
72
67
|
.join('');
|
|
73
68
|
logger.verbose('Received response from Gemini');
|
|
74
69
|
logger.debug('Response:', responseText.substring(0, 200) + '...');
|
|
75
|
-
|
|
76
|
-
const suggestions = this.parseSuggestions(responseText);
|
|
77
|
-
logger.success(`Generated ${suggestions.length} suggestions`);
|
|
78
|
-
// Yield suggestions one by one (simulating streaming)
|
|
79
|
-
for (const suggestion of suggestions) {
|
|
80
|
-
yield suggestion;
|
|
81
|
-
}
|
|
70
|
+
return responseText;
|
|
82
71
|
}
|
|
83
72
|
catch (error) {
|
|
84
73
|
if (error instanceof AIProviderError) {
|
package/dist/ai/gemini.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"gemini.js","sourceRoot":"","sources":["../../src/ai/gemini.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,
|
|
1
|
+
{"version":3,"file":"gemini.js","sourceRoot":"","sources":["../../src/ai/gemini.ts"],"names":[],"mappings":"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"}
|
package/dist/ai/ollama.d.ts
CHANGED
|
@@ -1,16 +1,20 @@
|
|
|
1
|
-
import { AIProvider } from './provider.js';
|
|
2
|
-
import type { CodeContext, Suggestion } from '../types/index.js';
|
|
1
|
+
import { AIProvider, type ChatMessage } from './provider.js';
|
|
3
2
|
/**
|
|
4
3
|
* Ollama AI Provider
|
|
5
4
|
* Supports local LLMs via Ollama (DeepSeek Coder, CodeLlama, etc.)
|
|
6
5
|
* No API key required - runs completely offline
|
|
7
6
|
*/
|
|
7
|
+
/**
|
|
8
|
+
* Headers for an Ollama request. Ollama Cloud (https://ollama.com) and authenticated
|
|
9
|
+
* proxies take a bearer key, read from OLLAMA_API_KEY as Ollama's own clients do.
|
|
10
|
+
*/
|
|
11
|
+
export declare function ollamaHeaders(extra?: Record<string, string>): Record<string, string>;
|
|
8
12
|
export declare class OllamaProvider extends AIProvider {
|
|
9
13
|
private baseUrl;
|
|
10
14
|
constructor(apiKey?: string, model?: string, baseUrl?: string);
|
|
11
15
|
getDefaultModel(): string;
|
|
12
16
|
testConnection(): Promise<boolean>;
|
|
13
|
-
|
|
17
|
+
chat(system: string, messages: ChatMessage[]): Promise<string>;
|
|
14
18
|
/**
|
|
15
19
|
* Get available Ollama models
|
|
16
20
|
* Lists models installed on the local Ollama instance
|
package/dist/ai/ollama.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ollama.d.ts","sourceRoot":"","sources":["../../src/ai/ollama.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,eAAe,CAAC;
|
|
1
|
+
{"version":3,"file":"ollama.d.ts","sourceRoot":"","sources":["../../src/ai/ollama.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,KAAK,WAAW,EAAE,MAAM,eAAe,CAAC;AAI7D;;;;GAIG;AACH;;;GAGG;AACH,wBAAgB,aAAa,CAAC,KAAK,GAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAM,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAGxF;AAED,qBAAa,cAAe,SAAQ,UAAU;IAC1C,OAAO,CAAC,OAAO,CAAS;gBAEZ,MAAM,CAAC,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,MAAM;IAK7D,eAAe,IAAI,MAAM;IAInB,cAAc,IAAI,OAAO,CAAC,OAAO,CAAC;IAalC,IAAI,CAAC,MAAM,EAAE,MAAM,EAAE,QAAQ,EAAE,WAAW,EAAE,GAAG,OAAO,CAAC,MAAM,CAAC;IA2CpE;;;OAGG;WACU,kBAAkB,CAAC,OAAO,GAAE,MAAiC,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC;IAe9F;;OAEG;WACU,eAAe,CAAC,OAAO,GAAE,MAAiC,GAAG,OAAO,CAAC,OAAO,CAAC;IAS1F;;OAEG;WACU,gBAAgB,CACzB,KAAK,EAAE,MAAM,EACb,OAAO,GAAE,MAAiC,GAC3C,OAAO,CAAC,OAAO,CAAC;CAItB"}
|
package/dist/ai/ollama.js
CHANGED
|
@@ -6,6 +6,14 @@ import { DEFAULT_MODELS } from '../config/models.js';
|
|
|
6
6
|
* Supports local LLMs via Ollama (DeepSeek Coder, CodeLlama, etc.)
|
|
7
7
|
* No API key required - runs completely offline
|
|
8
8
|
*/
|
|
9
|
+
/**
|
|
10
|
+
* Headers for an Ollama request. Ollama Cloud (https://ollama.com) and authenticated
|
|
11
|
+
* proxies take a bearer key, read from OLLAMA_API_KEY as Ollama's own clients do.
|
|
12
|
+
*/
|
|
13
|
+
export function ollamaHeaders(extra = {}) {
|
|
14
|
+
const key = process.env.OLLAMA_API_KEY?.trim();
|
|
15
|
+
return key ? { ...extra, Authorization: `Bearer ${key}` } : extra;
|
|
16
|
+
}
|
|
9
17
|
export class OllamaProvider extends AIProvider {
|
|
10
18
|
baseUrl;
|
|
11
19
|
constructor(apiKey, model, baseUrl) {
|
|
@@ -19,6 +27,7 @@ export class OllamaProvider extends AIProvider {
|
|
|
19
27
|
try {
|
|
20
28
|
const response = await fetch(`${this.baseUrl}/api/tags`, {
|
|
21
29
|
method: 'GET',
|
|
30
|
+
headers: ollamaHeaders(),
|
|
22
31
|
});
|
|
23
32
|
return response.ok;
|
|
24
33
|
}
|
|
@@ -27,20 +36,19 @@ export class OllamaProvider extends AIProvider {
|
|
|
27
36
|
return false;
|
|
28
37
|
}
|
|
29
38
|
}
|
|
30
|
-
async
|
|
39
|
+
async chat(system, messages) {
|
|
40
|
+
// A single turn uses /api/generate (unchanged from earlier versions); a conversation uses /api/chat
|
|
41
|
+
const single = messages.length === 1 && messages[0].role === 'user';
|
|
42
|
+
const url = `${this.baseUrl}${single ? '/api/generate' : '/api/chat'}`;
|
|
43
|
+
const payload = single
|
|
44
|
+
? { model: this.model, system, prompt: messages[0].content, stream: false, options: { temperature: 0.2 } }
|
|
45
|
+
: { model: this.model, messages: [{ role: 'system', content: system }, ...messages], stream: false, options: { temperature: 0.2 } };
|
|
31
46
|
let response;
|
|
32
47
|
try {
|
|
33
|
-
|
|
34
|
-
response = await fetch(`${this.baseUrl}/api/generate`, {
|
|
48
|
+
response = await fetch(url, {
|
|
35
49
|
method: 'POST',
|
|
36
|
-
headers: { 'Content-Type': 'application/json' },
|
|
37
|
-
body: JSON.stringify(
|
|
38
|
-
model: this.model,
|
|
39
|
-
system: this.getSystemPrompt(),
|
|
40
|
-
prompt: this.buildUserPrompt(context),
|
|
41
|
-
stream: false,
|
|
42
|
-
options: { temperature: 0.2 },
|
|
43
|
-
}),
|
|
50
|
+
headers: ollamaHeaders({ 'Content-Type': 'application/json' }),
|
|
51
|
+
body: JSON.stringify(payload),
|
|
44
52
|
});
|
|
45
53
|
}
|
|
46
54
|
catch (error) {
|
|
@@ -50,17 +58,19 @@ export class OllamaProvider extends AIProvider {
|
|
|
50
58
|
}
|
|
51
59
|
if (!response.ok) {
|
|
52
60
|
const body = await response.text().catch(() => '');
|
|
61
|
+
if (response.status === 401 || response.status === 403) {
|
|
62
|
+
throw new Error(`Ollama at ${this.baseUrl} rejected the request (${response.status}). ` +
|
|
63
|
+
'For Ollama Cloud, set OLLAMA_API_KEY to a key from https://ollama.com/settings/keys');
|
|
64
|
+
}
|
|
53
65
|
if (response.status === 404 && /model/i.test(body)) {
|
|
54
66
|
throw new Error(`Model "${this.model}" is not installed. Run: ollama pull ${this.model}`);
|
|
55
67
|
}
|
|
56
68
|
throw new Error(`Ollama API error: ${response.status} ${response.statusText} ${body}`.trim());
|
|
57
69
|
}
|
|
58
70
|
const data = (await response.json());
|
|
59
|
-
const text = data.response ?? '';
|
|
71
|
+
const text = data.response ?? data.message?.content ?? '';
|
|
60
72
|
logger.debug(`Ollama raw response (${text.length} chars): ${text.slice(0, 500)}`);
|
|
61
|
-
|
|
62
|
-
yield suggestion;
|
|
63
|
-
}
|
|
73
|
+
return text;
|
|
64
74
|
}
|
|
65
75
|
/**
|
|
66
76
|
* Get available Ollama models
|
|
@@ -68,7 +78,7 @@ export class OllamaProvider extends AIProvider {
|
|
|
68
78
|
*/
|
|
69
79
|
static async getAvailableModels(baseUrl = 'http://localhost:11434') {
|
|
70
80
|
try {
|
|
71
|
-
const response = await fetch(`${baseUrl}/api/tags
|
|
81
|
+
const response = await fetch(`${baseUrl}/api/tags`, { headers: ollamaHeaders() });
|
|
72
82
|
if (!response.ok) {
|
|
73
83
|
return [];
|
|
74
84
|
}
|
|
@@ -85,7 +95,7 @@ export class OllamaProvider extends AIProvider {
|
|
|
85
95
|
*/
|
|
86
96
|
static async isOllamaRunning(baseUrl = 'http://localhost:11434') {
|
|
87
97
|
try {
|
|
88
|
-
const response = await fetch(`${baseUrl}/api/tags
|
|
98
|
+
const response = await fetch(`${baseUrl}/api/tags`, { headers: ollamaHeaders() });
|
|
89
99
|
return response.ok;
|
|
90
100
|
}
|
|
91
101
|
catch {
|
package/dist/ai/ollama.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ollama.js","sourceRoot":"","sources":["../../src/ai/ollama.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,
|
|
1
|
+
{"version":3,"file":"ollama.js","sourceRoot":"","sources":["../../src/ai/ollama.ts"],"names":[],"mappings":"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"}
|
package/dist/ai/openai.d.ts
CHANGED
|
@@ -1,5 +1,4 @@
|
|
|
1
|
-
import { AIProvider } from './provider.js';
|
|
2
|
-
import type { CodeContext, Suggestion } from '../types/index.js';
|
|
1
|
+
import { AIProvider, type ChatMessage } from './provider.js';
|
|
3
2
|
export declare class OpenAIProvider extends AIProvider {
|
|
4
3
|
private baseUrl;
|
|
5
4
|
/**
|
|
@@ -10,7 +9,7 @@ export declare class OpenAIProvider extends AIProvider {
|
|
|
10
9
|
testConnection(): Promise<boolean>;
|
|
11
10
|
/** Chat Completions body; reasoning-era models take max_completion_tokens and no temperature. */
|
|
12
11
|
private buildBody;
|
|
13
|
-
|
|
12
|
+
chat(system: string, messages: ChatMessage[]): Promise<string>;
|
|
14
13
|
static getAvailableModels(apiKey: string): Promise<string[]>;
|
|
15
14
|
}
|
|
16
15
|
//# sourceMappingURL=openai.d.ts.map
|
package/dist/ai/openai.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"openai.d.ts","sourceRoot":"","sources":["../../src/ai/openai.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,
|
|
1
|
+
{"version":3,"file":"openai.d.ts","sourceRoot":"","sources":["../../src/ai/openai.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,KAAK,WAAW,EAAE,MAAM,eAAe,CAAC;AAI7D,qBAAa,cAAe,SAAQ,UAAU;IAC1C,OAAO,CAAC,OAAO,CAAS;IAExB;;OAEG;gBACS,MAAM,CAAC,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,MAAM,EAAE,QAAQ,CAAC,EAAE,MAAM;IAK9D,eAAe,IAAI,MAAM;IAInB,cAAc,IAAI,OAAO,CAAC,OAAO,CAAC;IAoBxC,iGAAiG;IACjG,OAAO,CAAC,SAAS;IAeX,IAAI,CAAC,MAAM,EAAE,MAAM,EAAE,QAAQ,EAAE,WAAW,EAAE,GAAG,OAAO,CAAC,MAAM,CAAC;WAiDvD,kBAAkB,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC;CA8BrE"}
|
package/dist/ai/openai.js
CHANGED
|
@@ -32,13 +32,10 @@ export class OpenAIProvider extends AIProvider {
|
|
|
32
32
|
}
|
|
33
33
|
}
|
|
34
34
|
/** Chat Completions body; reasoning-era models take max_completion_tokens and no temperature. */
|
|
35
|
-
buildBody(
|
|
35
|
+
buildBody(system, messages, reasoning) {
|
|
36
36
|
const body = {
|
|
37
37
|
model: this.model,
|
|
38
|
-
messages: [
|
|
39
|
-
{ role: 'system', content: this.getSystemPrompt() },
|
|
40
|
-
{ role: 'user', content: prompt },
|
|
41
|
-
],
|
|
38
|
+
messages: [{ role: 'system', content: system }, ...messages],
|
|
42
39
|
};
|
|
43
40
|
if (reasoning) {
|
|
44
41
|
// Reasoning tokens count toward this cap, so leave room beyond the visible answer.
|
|
@@ -50,17 +47,16 @@ export class OpenAIProvider extends AIProvider {
|
|
|
50
47
|
}
|
|
51
48
|
return body;
|
|
52
49
|
}
|
|
53
|
-
async
|
|
54
|
-
const prompt = this.buildUserPrompt(context);
|
|
50
|
+
async chat(system, messages) {
|
|
55
51
|
try {
|
|
56
|
-
logger.verbose(`
|
|
52
|
+
logger.verbose(`Calling OpenAI (${this.model})...`);
|
|
57
53
|
const response = await fetch(`${this.baseUrl}/chat/completions`, {
|
|
58
54
|
method: 'POST',
|
|
59
55
|
headers: {
|
|
60
56
|
'Content-Type': 'application/json',
|
|
61
57
|
'Authorization': `Bearer ${this.apiKey}`,
|
|
62
58
|
},
|
|
63
|
-
body: JSON.stringify(this.buildBody(
|
|
59
|
+
body: JSON.stringify(this.buildBody(system, messages, isOpenAIReasoningModel(this.model))),
|
|
64
60
|
});
|
|
65
61
|
let res = response;
|
|
66
62
|
if (res.status === 400) {
|
|
@@ -73,7 +69,7 @@ export class OpenAIProvider extends AIProvider {
|
|
|
73
69
|
'Content-Type': 'application/json',
|
|
74
70
|
'Authorization': `Bearer ${this.apiKey}`,
|
|
75
71
|
},
|
|
76
|
-
body: JSON.stringify(this.buildBody(
|
|
72
|
+
body: JSON.stringify(this.buildBody(system, messages, !isOpenAIReasoningModel(this.model))),
|
|
77
73
|
});
|
|
78
74
|
}
|
|
79
75
|
}
|
|
@@ -88,11 +84,7 @@ export class OpenAIProvider extends AIProvider {
|
|
|
88
84
|
if (!content) {
|
|
89
85
|
throw new Error('No content in OpenAI response');
|
|
90
86
|
}
|
|
91
|
-
|
|
92
|
-
const suggestions = this.parseSuggestions(content);
|
|
93
|
-
for (const suggestion of suggestions) {
|
|
94
|
-
yield suggestion;
|
|
95
|
-
}
|
|
87
|
+
return content;
|
|
96
88
|
}
|
|
97
89
|
catch (error) {
|
|
98
90
|
logger.error('OpenAI analysis error:', error);
|
package/dist/ai/openai.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"openai.js","sourceRoot":"","sources":["../../src/ai/openai.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,
|
|
1
|
+
{"version":3,"file":"openai.js","sourceRoot":"","sources":["../../src/ai/openai.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAoB,MAAM,eAAe,CAAC;AAC7D,OAAO,EAAE,MAAM,EAAE,MAAM,oBAAoB,CAAC;AAC5C,OAAO,EAAE,cAAc,EAAE,sBAAsB,EAAE,MAAM,qBAAqB,CAAC;AAE7E,MAAM,OAAO,cAAe,SAAQ,UAAU;IAClC,OAAO,CAAS;IAExB;;OAEG;IACH,YAAY,MAAe,EAAE,KAAc,EAAE,QAAiB;QAC1D,KAAK,CAAC,MAAM,EAAE,KAAK,EAAE,QAAQ,CAAC,CAAC;QAC/B,IAAI,CAAC,OAAO,GAAG,CAAC,QAAQ,IAAI,2BAA2B,CAAC,CAAC,OAAO,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC;IACjF,CAAC;IAED,eAAe;QACX,OAAO,cAAc,CAAC,MAAM,CAAC;IACjC,CAAC;IAED,KAAK,CAAC,cAAc;QAChB,IAAI,CAAC;YACD,MAAM,QAAQ,GAAG,MAAM,KAAK,CAAC,GAAG,IAAI,CAAC,OAAO,SAAS,EAAE;gBACnD,OAAO,EAAE;oBACL,eAAe,EAAE,UAAU,IAAI,CAAC,MAAM,EAAE;iBAC3C;aACJ,CAAC,CAAC;YAEH,IAAI,CAAC,QAAQ,CAAC,EAAE,EAAE,CAAC;gBACf,MAAM,CAAC,KAAK,CAAC,kCAAkC,QAAQ,CAAC,MAAM,EAAE,CAAC,CAAC;gBAClE,OAAO,KAAK,CAAC;YACjB,CAAC;YAED,OAAO,IAAI,CAAC;QAChB,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACb,MAAM,CAAC,KAAK,CAAC,+BAA+B,EAAE,KAAK,CAAC,CAAC;YACrD,OAAO,KAAK,CAAC;QACjB,CAAC;IACL,CAAC;IAED,iGAAiG;IACzF,SAAS,CAAC,MAAc,EAAE,QAAuB,EAAE,SAAkB;QACzE,MAAM,IAAI,GAA4B;YAClC,KAAK,EAAE,IAAI,CAAC,KAAK;YACjB,QAAQ,EAAE,CAAC,EAAE,IAAI,EAAE,QAAQ,EAAE,OAAO,EAAE,MAAM,EAAE,EAAE,GAAG,QAAQ,CAAC;SAC/D,CAAC;QACF,IAAI,SAAS,EAAE,CAAC;YACZ,mFAAmF;YACnF,IAAI,CAAC,qBAAqB,GAAG,KAAK,CAAC;QACvC,CAAC;aAAM,CAAC;YACJ,IAAI,CAAC,WAAW,GAAG,GAAG,CAAC;YACvB,IAAI,CAAC,UAAU,GAAG,IAAI,CAAC;QAC3B,CAAC;QACD,OAAO,IAAI,CAAC;IAChB,CAAC;IAED,KAAK,CAAC,IAAI,CAAC,MAAc,EAAE,QAAuB;QAE9C,IAAI,CAAC;YACD,MAAM,CAAC,OAAO,CAAC,mBAAmB,IAAI,CAAC,KAAK,MAAM,CAAC,CAAC;YAEpD,MAAM,QAAQ,GAAG,MAAM,KAAK,CAAC,GAAG,IAAI,CAAC,OAAO,mBAAmB,EAAE;gBAC7D,MAAM,EAAE,MAAM;gBACd,OAAO,EAAE;oBACL,cAAc,EAAE,kBAAkB;oBAClC,eAAe,EAAE,UAAU,IAAI,CAAC,MAAM,EAAE;iBAC3C;gBACD,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,SAAS,CAAC,MAAM,EAAE,QAAQ,EAAE,sBAAsB,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC;aAC7F,CAAC,CAAC;YACH,IAAI,GAAG,GAAG,QAAQ,CAAC;YACnB,IAAI,GAAG,CAAC,MAAM,KAAK,GAAG,EAAE,CAAC;gBACrB,8FAA8F;gBAC9F,MAAM,IAAI,GAAG,MAAM,GAAG,CAAC,KAAK,EAAE,CAAC,IAAI,EAAE,CAAC;gBACtC,IAAI,2DAA2D,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC;oBACzE,GAAG,GAAG,MAAM,KAAK,CAAC,GAAG,IAAI,CAAC,OAAO,mBAAmB,EAAE;wBAClD,MAAM,EAAE,MAAM;wBACd,OAAO,EAAE;4BACL,cAAc,EAAE,kBAAkB;4BAClC,eAAe,EAAE,UAAU,IAAI,CAAC,MAAM,EAAE;yBAC3C;wBACD,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,SAAS,CAAC,MAAM,EAAE,QAAQ,EAAE,CAAC,sBAAsB,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC;qBAC9F,CAAC,CAAC;gBACP,CAAC;YACL,CAAC;YACD,MAAM,SAAS,GAAG,GAAG,CAAC;YACtB,IAAI,CAAC,SAAS,CAAC,EAAE,EAAE,CAAC;gBAChB,MAAM,SAAS,GAAG,MAAM,SAAS,CAAC,IAAI,EAAE,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,CAAC,EAAE,CAAC,CAAQ,CAAC;gBAClE,MAAM,CAAC,KAAK,CAAC,mBAAmB,EAAE,SAAkB,CAAC,CAAC;gBACtD,MAAM,IAAI,KAAK,CAAC,qBAAqB,SAAS,CAAC,KAAK,EAAE,OAAO,IAAI,SAAS,CAAC,UAAU,EAAE,CAAC,CAAC;YAC7F,CAAC;YAED,MAAM,IAAI,GAAG,MAAM,SAAS,CAAC,IAAI,EAAS,CAAC;YAC3C,MAAM,OAAO,GAAG,IAAI,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,OAAO,EAAE,OAAO,CAAC;YAElD,IAAI,CAAC,OAAO,EAAE,CAAC;gBACX,MAAM,IAAI,KAAK,CAAC,+BAA+B,CAAC,CAAC;YACrD,CAAC;YAED,OAAO,OAAO,CAAC;QACnB,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACb,MAAM,CAAC,KAAK,CAAC,wBAAwB,EAAE,KAAc,CAAC,CAAC;YACvD,MAAM,KAAK,CAAC;QAChB,CAAC;IACL,CAAC;IAED,MAAM,CAAC,KAAK,CAAC,kBAAkB,CAAC,MAAc;QAC1C,IAAI,CAAC;YACD,MAAM,QAAQ,GAAG,MAAM,KAAK,CAAC,kCAAkC,EAAE;gBAC7D,OAAO,EAAE;oBACL,eAAe,EAAE,UAAU,MAAM,EAAE;iBACtC;aACJ,CAAC,CAAC;YAEH,IAAI,CAAC,QAAQ,CAAC,EAAE,EAAE,CAAC;gBACf,MAAM,IAAI,KAAK,CAAC,2BAA2B,QAAQ,CAAC,UAAU,EAAE,CAAC,CAAC;YACtE,CAAC;YAED,MAAM,IAAI,GAAG,MAAM,QAAQ,CAAC,IAAI,EAAS,CAAC;YAE1C,mDAAmD;YACnD,MAAM,MAAM,GAAG,IAAI,CAAC,IAAI;iBACnB,MAAM,CAAC,CAAC,CAAM,EAAE,EAAE,CACf,CAAC,CAAC,EAAE,CAAC,QAAQ,CAAC,KAAK,CAAC;gBACpB,CAAC,CAAC,CAAC,EAAE,CAAC,QAAQ,CAAC,UAAU,CAAC;gBAC1B,CAAC,CAAC,CAAC,EAAE,CAAC,QAAQ,CAAC,QAAQ,CAAC,CAC3B;iBACA,GAAG,CAAC,CAAC,CAAM,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;iBACrB,IAAI,CAAC,CAAC,CAAS,EAAE,CAAS,EAAE,EAAE,CAAC,CAAC,CAAC,aAAa,CAAC,CAAC,EAAE,SAAS,EAAE,EAAE,OAAO,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC;YAEtF,OAAO,MAAM,CAAC;QAClB,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACb,MAAM,CAAC,KAAK,CAAC,gCAAgC,EAAE,KAAc,CAAC,CAAC;YAC/D,OAAO,CAAC,cAAc,CAAC,MAAM,EAAE,aAAa,EAAE,YAAY,CAAC,CAAC,CAAC,WAAW;QAC5E,CAAC;IACL,CAAC;CACJ"}
|
package/dist/ai/provider.d.ts
CHANGED
|
@@ -1,4 +1,9 @@
|
|
|
1
1
|
import { CodeContext, Suggestion, AnalyzeOptions, AIProviderInterface } from '../types/index.js';
|
|
2
|
+
/** One turn of a multi-turn conversation with the model. */
|
|
3
|
+
export interface ChatMessage {
|
|
4
|
+
role: 'user' | 'assistant';
|
|
5
|
+
content: string;
|
|
6
|
+
}
|
|
2
7
|
/**
|
|
3
8
|
* Base abstract class for AI providers
|
|
4
9
|
* All providers (OpenAI, Gemini, Anthropic, Ollama) must implement this interface
|
|
@@ -19,24 +24,28 @@ export declare abstract class AIProvider implements AIProviderInterface {
|
|
|
19
24
|
*/
|
|
20
25
|
abstract testConnection(): Promise<boolean>;
|
|
21
26
|
/**
|
|
22
|
-
*
|
|
23
|
-
*
|
|
24
|
-
|
|
25
|
-
|
|
27
|
+
* Send a conversation to the model and return its text reply.
|
|
28
|
+
* Every provider implements this; one-shot analysis and the review agent both build on it.
|
|
29
|
+
*/
|
|
30
|
+
abstract chat(system: string, messages: ChatMessage[], options?: AnalyzeOptions): Promise<string>;
|
|
31
|
+
/**
|
|
32
|
+
* Analyze code in one request and yield suggestions
|
|
26
33
|
*/
|
|
27
|
-
|
|
34
|
+
analyze(context: CodeContext, options?: AnalyzeOptions): AsyncGenerator<Suggestion>;
|
|
28
35
|
/**
|
|
29
36
|
* Format the system prompt for code analysis
|
|
30
37
|
*/
|
|
31
|
-
|
|
38
|
+
getSystemPrompt(): string;
|
|
32
39
|
/**
|
|
33
40
|
* Build the user prompt with code context
|
|
34
41
|
*/
|
|
35
|
-
|
|
42
|
+
buildUserPrompt(context: CodeContext): string;
|
|
36
43
|
/**
|
|
37
44
|
* Parse AI response and extract suggestions
|
|
38
45
|
*/
|
|
39
|
-
|
|
46
|
+
parseSuggestions(response: string): Suggestion[];
|
|
47
|
+
/** A positive integer line number from a model reply, or undefined. */
|
|
48
|
+
static parseLine(value: unknown): number | undefined;
|
|
40
49
|
/**
|
|
41
50
|
* Pull the first JSON array/object out of a model reply (handles ```json fences and chatter).
|
|
42
51
|
*/
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"provider.d.ts","sourceRoot":"","sources":["../../src/ai/provider.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,UAAU,EAAE,cAAc,EAAE,mBAAmB,EAAE,MAAM,mBAAmB,CAAC;AAEjG;;;GAGG;AACH,8BAAsB,UAAW,YAAW,mBAAmB;IAC3D,SAAS,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC;IAC1B,SAAS,CAAC,KAAK,EAAE,MAAM,CAAC;IACxB,SAAS,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;gBAEhB,MAAM,CAAC,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,MAAM,EAAE,QAAQ,CAAC,EAAE,MAAM;IAM9D,wCAAwC;IACxC,QAAQ,IAAI,MAAM;IAIlB;;OAEG;IACH,QAAQ,CAAC,eAAe,IAAI,MAAM;IAElC;;OAEG;IACH,QAAQ,CAAC,cAAc,IAAI,OAAO,CAAC,OAAO,CAAC;IAE3C
|
|
1
|
+
{"version":3,"file":"provider.d.ts","sourceRoot":"","sources":["../../src/ai/provider.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,UAAU,EAAE,cAAc,EAAE,mBAAmB,EAAE,MAAM,mBAAmB,CAAC;AAEjG,4DAA4D;AAC5D,MAAM,WAAW,WAAW;IACxB,IAAI,EAAE,MAAM,GAAG,WAAW,CAAC;IAC3B,OAAO,EAAE,MAAM,CAAC;CACnB;AAED;;;GAGG;AACH,8BAAsB,UAAW,YAAW,mBAAmB;IAC3D,SAAS,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC;IAC1B,SAAS,CAAC,KAAK,EAAE,MAAM,CAAC;IACxB,SAAS,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;gBAEhB,MAAM,CAAC,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,MAAM,EAAE,QAAQ,CAAC,EAAE,MAAM;IAM9D,wCAAwC;IACxC,QAAQ,IAAI,MAAM;IAIlB;;OAEG;IACH,QAAQ,CAAC,eAAe,IAAI,MAAM;IAElC;;OAEG;IACH,QAAQ,CAAC,cAAc,IAAI,OAAO,CAAC,OAAO,CAAC;IAE3C;;;OAGG;IACH,QAAQ,CAAC,IAAI,CAAC,MAAM,EAAE,MAAM,EAAE,QAAQ,EAAE,WAAW,EAAE,EAAE,OAAO,CAAC,EAAE,cAAc,GAAG,OAAO,CAAC,MAAM,CAAC;IAEjG;;OAEG;IACI,OAAO,CAAC,OAAO,EAAE,WAAW,EAAE,OAAO,CAAC,EAAE,cAAc,GAAG,cAAc,CAAC,UAAU,CAAC;IAK1F;;OAEG;IACH,eAAe,IAAI,MAAM;IAsCzB;;OAEG;IACH,eAAe,CAAC,OAAO,EAAE,WAAW,GAAG,MAAM;IAmC7C;;OAEG;IACH,gBAAgB,CAAC,QAAQ,EAAE,MAAM,GAAG,UAAU,EAAE;IA6BhD,uEAAuE;IACvE,MAAM,CAAC,SAAS,CAAC,KAAK,EAAE,OAAO,GAAG,MAAM,GAAG,SAAS;IAKpD;;OAEG;IACH,MAAM,CAAC,WAAW,CAAC,IAAI,EAAE,MAAM,GAAG,GAAG;CAwBxC"}
|
package/dist/ai/provider.js
CHANGED
|
@@ -15,6 +15,14 @@ export class AIProvider {
|
|
|
15
15
|
getModel() {
|
|
16
16
|
return this.model;
|
|
17
17
|
}
|
|
18
|
+
/**
|
|
19
|
+
* Analyze code in one request and yield suggestions
|
|
20
|
+
*/
|
|
21
|
+
async *analyze(context, options) {
|
|
22
|
+
const text = await this.chat(this.getSystemPrompt(), [{ role: 'user', content: this.buildUserPrompt(context) }], options);
|
|
23
|
+
for (const suggestion of this.parseSuggestions(text))
|
|
24
|
+
yield suggestion;
|
|
25
|
+
}
|
|
18
26
|
/**
|
|
19
27
|
* Format the system prompt for code analysis
|
|
20
28
|
*/
|
|
@@ -36,6 +44,7 @@ Guidelines:
|
|
|
36
44
|
- Use unified diff format for code changes
|
|
37
45
|
- Categorize suggestions by type and severity
|
|
38
46
|
- "filePath" must be the file's path relative to the project root, exactly as written in its "###" heading
|
|
47
|
+
- "line" is the 1-based line number in that file where the problem is (the line a reviewer would comment on)
|
|
39
48
|
- "diff" must be a unified diff for that one file: "--- a/<filePath>", "+++ b/<filePath>", then "@@" hunks
|
|
40
49
|
with 2-3 unchanged context lines copied exactly from the file (same indentation)
|
|
41
50
|
- If you cannot produce an exact diff, omit the suggestion
|
|
@@ -47,6 +56,7 @@ Return a JSON array of suggestions. Each suggestion must have:
|
|
|
47
56
|
"severity": "critical" | "high" | "medium" | "low" | "info",
|
|
48
57
|
"content": "Brief description of the issue",
|
|
49
58
|
"filePath": "path/to/file.ts",
|
|
59
|
+
"line": 42,
|
|
50
60
|
"rationale": "Detailed explanation of why this matters",
|
|
51
61
|
"diff": "Unified diff format showing the change"
|
|
52
62
|
}
|
|
@@ -107,9 +117,15 @@ Be thorough but practical. Focus on high-impact suggestions.`;
|
|
|
107
117
|
filePath: s.filePath || s.file || '',
|
|
108
118
|
rationale: s.rationale || '',
|
|
109
119
|
diff: typeof s.diff === 'string' ? s.diff : '',
|
|
120
|
+
line: AIProvider.parseLine(s.line ?? s.lineNumber ?? s.startLine),
|
|
110
121
|
tags: s.tags,
|
|
111
122
|
}));
|
|
112
123
|
}
|
|
124
|
+
/** A positive integer line number from a model reply, or undefined. */
|
|
125
|
+
static parseLine(value) {
|
|
126
|
+
const n = typeof value === 'number' ? value : typeof value === 'string' ? parseInt(value, 10) : NaN;
|
|
127
|
+
return Number.isInteger(n) && n > 0 ? n : undefined;
|
|
128
|
+
}
|
|
113
129
|
/**
|
|
114
130
|
* Pull the first JSON array/object out of a model reply (handles ```json fences and chatter).
|
|
115
131
|
*/
|