@involvex/prompt-enhancer 0.0.2
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/.github/FUNDING.yml +9 -0
- package/LICENSE +21 -0
- package/dist/app.d.ts +5 -0
- package/dist/app.js +39 -0
- package/dist/cli.d.ts +2 -0
- package/dist/cli.js +53 -0
- package/dist/commands/about.d.ts +1 -0
- package/dist/commands/about.js +6 -0
- package/dist/commands/direct-enhance.d.ts +5 -0
- package/dist/commands/direct-enhance.js +52 -0
- package/dist/commands/enhanceprompt.d.ts +1 -0
- package/dist/commands/enhanceprompt.js +6 -0
- package/dist/commands/help.d.ts +1 -0
- package/dist/commands/help.js +6 -0
- package/dist/commands/settings.d.ts +1 -0
- package/dist/commands/settings.js +1 -0
- package/dist/commands/version.d.ts +1 -0
- package/dist/commands/version.js +6 -0
- package/dist/components/enhance-prompt.d.ts +5 -0
- package/dist/components/enhance-prompt.js +82 -0
- package/dist/components/history-viewer.d.ts +5 -0
- package/dist/components/history-viewer.js +64 -0
- package/dist/components/menu.d.ts +11 -0
- package/dist/components/menu.js +6 -0
- package/dist/components/select-input.d.ts +19 -0
- package/dist/components/select-input.js +51 -0
- package/dist/components/settings.d.ts +5 -0
- package/dist/components/settings.js +246 -0
- package/dist/lib/config/manager.d.ts +49 -0
- package/dist/lib/config/manager.js +152 -0
- package/dist/lib/config/schema.d.ts +69 -0
- package/dist/lib/config/schema.js +37 -0
- package/dist/lib/config/types.d.ts +34 -0
- package/dist/lib/config/types.js +4 -0
- package/dist/lib/enhancement/engine.d.ts +41 -0
- package/dist/lib/enhancement/engine.js +163 -0
- package/dist/lib/history/manager.d.ts +45 -0
- package/dist/lib/history/manager.js +120 -0
- package/dist/lib/providers/base.d.ts +47 -0
- package/dist/lib/providers/base.js +30 -0
- package/dist/lib/providers/copilot.d.ts +18 -0
- package/dist/lib/providers/copilot.js +112 -0
- package/dist/lib/providers/gemini.d.ts +13 -0
- package/dist/lib/providers/gemini.js +86 -0
- package/dist/lib/providers/index.d.ts +26 -0
- package/dist/lib/providers/index.js +86 -0
- package/dist/lib/providers/kilo.d.ts +22 -0
- package/dist/lib/providers/kilo.js +174 -0
- package/dist/lib/providers/opencode.d.ts +22 -0
- package/dist/lib/providers/opencode.js +174 -0
- package/dist/lib/types/index.d.ts +26 -0
- package/dist/lib/types/index.js +4 -0
- package/dist/lib/utils/copilot-auth.d.ts +14 -0
- package/dist/lib/utils/copilot-auth.js +84 -0
- package/dist/lib/utils/models-cache.d.ts +12 -0
- package/dist/lib/utils/models-cache.js +139 -0
- package/dist/lib/utils/paths.d.ts +10 -0
- package/dist/lib/utils/paths.js +18 -0
- package/package.json +101 -0
- package/readme.md +271 -0
|
@@ -0,0 +1,163 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Enhancement engine - orchestrates prompt enhancement across providers
|
|
3
|
+
*/
|
|
4
|
+
import { getProvider } from '../providers/index.js';
|
|
5
|
+
export class EnhancementEngine {
|
|
6
|
+
configManager;
|
|
7
|
+
historyManager;
|
|
8
|
+
constructor(configManager, historyManager) {
|
|
9
|
+
this.configManager = configManager;
|
|
10
|
+
this.historyManager = historyManager;
|
|
11
|
+
}
|
|
12
|
+
/**
|
|
13
|
+
* Enhance a prompt using the specified (or default) provider
|
|
14
|
+
*/
|
|
15
|
+
async enhance(request) {
|
|
16
|
+
const startTime = Date.now();
|
|
17
|
+
const config = this.configManager.getConfig();
|
|
18
|
+
// Determine which provider to use
|
|
19
|
+
const providerName = request.provider || config.defaultProvider;
|
|
20
|
+
const providerConfig = config.providers[providerName];
|
|
21
|
+
if (!providerConfig?.enabled) {
|
|
22
|
+
throw new Error(`Provider '${providerName}' is not enabled. Please configure it in settings.`);
|
|
23
|
+
}
|
|
24
|
+
// Create credentials from config
|
|
25
|
+
const credentials = {
|
|
26
|
+
apiKey: providerConfig.apiKey || '',
|
|
27
|
+
endpoint: providerConfig.endpoint,
|
|
28
|
+
};
|
|
29
|
+
const provider = getProvider(providerName, credentials);
|
|
30
|
+
if (!provider) {
|
|
31
|
+
throw new Error(`Provider '${providerName}' not found. Available providers: ${Object.keys(config.providers).join(', ')}`);
|
|
32
|
+
}
|
|
33
|
+
// Create enhancement options
|
|
34
|
+
const options = {
|
|
35
|
+
model: request.model || config.defaultModel,
|
|
36
|
+
temperature: request.temperature ?? config.temperature,
|
|
37
|
+
maxTokens: request.maxTokens ?? config.maxTokens,
|
|
38
|
+
};
|
|
39
|
+
// Enhance the prompt
|
|
40
|
+
let enhanced;
|
|
41
|
+
try {
|
|
42
|
+
enhanced = await provider.enhance(request.prompt, options);
|
|
43
|
+
}
|
|
44
|
+
catch (error) {
|
|
45
|
+
throw new Error(`Enhancement failed with ${providerName}: ${error instanceof Error ? error.message : String(error)}`);
|
|
46
|
+
}
|
|
47
|
+
const duration = Date.now() - startTime;
|
|
48
|
+
// Save to history if requested
|
|
49
|
+
if (request.saveToHistory !== false && config.saveHistory) {
|
|
50
|
+
await this.historyManager.addEntry({
|
|
51
|
+
originalPrompt: request.prompt,
|
|
52
|
+
enhancedPrompt: enhanced,
|
|
53
|
+
provider: providerName,
|
|
54
|
+
model: options.model || 'default',
|
|
55
|
+
temperature: options.temperature,
|
|
56
|
+
tokens: options.maxTokens,
|
|
57
|
+
});
|
|
58
|
+
}
|
|
59
|
+
return {
|
|
60
|
+
originalPrompt: request.prompt,
|
|
61
|
+
enhancedPrompt: enhanced,
|
|
62
|
+
provider: providerName,
|
|
63
|
+
model: options.model || 'default',
|
|
64
|
+
duration,
|
|
65
|
+
};
|
|
66
|
+
}
|
|
67
|
+
/**
|
|
68
|
+
* Enhance a prompt with streaming output
|
|
69
|
+
*/
|
|
70
|
+
async *enhanceStream(request) {
|
|
71
|
+
const startTime = Date.now();
|
|
72
|
+
const config = this.configManager.getConfig();
|
|
73
|
+
// Determine which provider to use
|
|
74
|
+
const providerName = request.provider || config.defaultProvider;
|
|
75
|
+
const providerConfig = config.providers[providerName];
|
|
76
|
+
if (!providerConfig?.enabled) {
|
|
77
|
+
throw new Error(`Provider '${providerName}' is not enabled. Please configure it in settings.`);
|
|
78
|
+
}
|
|
79
|
+
// Create credentials from config
|
|
80
|
+
const credentials = {
|
|
81
|
+
apiKey: providerConfig.apiKey || '',
|
|
82
|
+
endpoint: providerConfig.endpoint,
|
|
83
|
+
};
|
|
84
|
+
const provider = getProvider(providerName, credentials);
|
|
85
|
+
if (!provider) {
|
|
86
|
+
throw new Error(`Provider '${providerName}' not found. Available providers: ${Object.keys(config.providers).join(', ')}`);
|
|
87
|
+
}
|
|
88
|
+
// Create enhancement options
|
|
89
|
+
const options = {
|
|
90
|
+
model: request.model || config.defaultModel,
|
|
91
|
+
temperature: request.temperature ?? config.temperature,
|
|
92
|
+
maxTokens: request.maxTokens ?? config.maxTokens,
|
|
93
|
+
};
|
|
94
|
+
// Stream enhancement
|
|
95
|
+
let enhancedText = '';
|
|
96
|
+
try {
|
|
97
|
+
for await (const chunk of provider.enhanceStream(request.prompt, options)) {
|
|
98
|
+
enhancedText += chunk;
|
|
99
|
+
yield chunk;
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
catch (error) {
|
|
103
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
104
|
+
throw new Error(`Enhancement failed with ${providerName}: ${errorMessage}`);
|
|
105
|
+
}
|
|
106
|
+
const duration = Date.now() - startTime;
|
|
107
|
+
// Save to history if requested
|
|
108
|
+
if (request.saveToHistory !== false && config.saveHistory) {
|
|
109
|
+
await this.historyManager.addEntry({
|
|
110
|
+
originalPrompt: request.prompt,
|
|
111
|
+
enhancedPrompt: enhancedText,
|
|
112
|
+
provider: providerName,
|
|
113
|
+
model: options.model || 'default',
|
|
114
|
+
temperature: options.temperature,
|
|
115
|
+
tokens: options.maxTokens,
|
|
116
|
+
});
|
|
117
|
+
}
|
|
118
|
+
return {
|
|
119
|
+
originalPrompt: request.prompt,
|
|
120
|
+
enhancedPrompt: enhancedText,
|
|
121
|
+
provider: providerName,
|
|
122
|
+
model: options.model || 'default',
|
|
123
|
+
duration,
|
|
124
|
+
};
|
|
125
|
+
}
|
|
126
|
+
/**
|
|
127
|
+
* Get available models for a provider
|
|
128
|
+
*/
|
|
129
|
+
async getAvailableModels(providerName) {
|
|
130
|
+
const config = this.configManager.getConfig();
|
|
131
|
+
const providers = providerName
|
|
132
|
+
? [providerName]
|
|
133
|
+
: Object.keys(config.providers);
|
|
134
|
+
const result = {};
|
|
135
|
+
for (const pName of providers) {
|
|
136
|
+
const provider = getProvider(pName);
|
|
137
|
+
if (provider) {
|
|
138
|
+
try {
|
|
139
|
+
result[pName] = await provider.getAvailableModels();
|
|
140
|
+
}
|
|
141
|
+
catch {
|
|
142
|
+
result[pName] = []; // Return empty list on error
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
return result;
|
|
147
|
+
}
|
|
148
|
+
/**
|
|
149
|
+
* Validate provider credentials
|
|
150
|
+
*/
|
|
151
|
+
async validateProvider(providerName) {
|
|
152
|
+
const provider = getProvider(providerName);
|
|
153
|
+
if (!provider) {
|
|
154
|
+
return false;
|
|
155
|
+
}
|
|
156
|
+
try {
|
|
157
|
+
return await provider.validateCredentials();
|
|
158
|
+
}
|
|
159
|
+
catch {
|
|
160
|
+
return false;
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* History manager for persisting enhanced prompts
|
|
3
|
+
*/
|
|
4
|
+
import type { HistoryEntry, HistoryData } from '../config/schema.js';
|
|
5
|
+
export declare class HistoryManager {
|
|
6
|
+
private history;
|
|
7
|
+
private historyPath;
|
|
8
|
+
constructor(historyPath?: string);
|
|
9
|
+
/**
|
|
10
|
+
* Load history from file
|
|
11
|
+
*/
|
|
12
|
+
load(): Promise<HistoryData>;
|
|
13
|
+
/**
|
|
14
|
+
* Save current history to file
|
|
15
|
+
*/
|
|
16
|
+
save(): Promise<void>;
|
|
17
|
+
/**
|
|
18
|
+
* Add an entry to history
|
|
19
|
+
*/
|
|
20
|
+
addEntry(entry: Omit<HistoryEntry, 'id' | 'timestamp'>): Promise<HistoryEntry>;
|
|
21
|
+
/**
|
|
22
|
+
* Get all history entries
|
|
23
|
+
*/
|
|
24
|
+
getEntries(): HistoryEntry[];
|
|
25
|
+
/**
|
|
26
|
+
* Get entries by provider
|
|
27
|
+
*/
|
|
28
|
+
getEntriesByProvider(provider: string): HistoryEntry[];
|
|
29
|
+
/**
|
|
30
|
+
* Get recent entries (last N)
|
|
31
|
+
*/
|
|
32
|
+
getRecent(count?: number): HistoryEntry[];
|
|
33
|
+
/**
|
|
34
|
+
* Clear all history
|
|
35
|
+
*/
|
|
36
|
+
clear(): Promise<void>;
|
|
37
|
+
/**
|
|
38
|
+
* Delete a specific entry
|
|
39
|
+
*/
|
|
40
|
+
deleteEntry(id: string): Promise<boolean>;
|
|
41
|
+
/**
|
|
42
|
+
* Export history as JSON
|
|
43
|
+
*/
|
|
44
|
+
export(): HistoryData;
|
|
45
|
+
}
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* History manager for persisting enhanced prompts
|
|
3
|
+
*/
|
|
4
|
+
import { readFile, writeFile, mkdir } from 'fs/promises';
|
|
5
|
+
import { existsSync } from 'fs';
|
|
6
|
+
import { randomUUID } from 'crypto';
|
|
7
|
+
import { HistoryDataSchema } from '../config/schema.js';
|
|
8
|
+
import { CONFIG_DIR, HISTORY_FILE } from '../utils/paths.js';
|
|
9
|
+
const DEFAULT_HISTORY = {
|
|
10
|
+
entries: [],
|
|
11
|
+
version: '1.0.0',
|
|
12
|
+
};
|
|
13
|
+
export class HistoryManager {
|
|
14
|
+
history;
|
|
15
|
+
historyPath;
|
|
16
|
+
constructor(historyPath) {
|
|
17
|
+
this.historyPath = historyPath || HISTORY_FILE;
|
|
18
|
+
this.history = structuredClone(DEFAULT_HISTORY);
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* Load history from file
|
|
22
|
+
*/
|
|
23
|
+
async load() {
|
|
24
|
+
try {
|
|
25
|
+
// Ensure directory exists
|
|
26
|
+
if (!existsSync(CONFIG_DIR)) {
|
|
27
|
+
await mkdir(CONFIG_DIR, { recursive: true });
|
|
28
|
+
}
|
|
29
|
+
// If history file doesn't exist, create it with defaults
|
|
30
|
+
if (!existsSync(this.historyPath)) {
|
|
31
|
+
await this.save();
|
|
32
|
+
return this.history;
|
|
33
|
+
}
|
|
34
|
+
// Read and parse history file
|
|
35
|
+
const content = await readFile(this.historyPath, 'utf-8');
|
|
36
|
+
const parsed = JSON.parse(content);
|
|
37
|
+
// Validate using schema
|
|
38
|
+
const validated = HistoryDataSchema.parse(parsed);
|
|
39
|
+
this.history = validated;
|
|
40
|
+
return this.history;
|
|
41
|
+
}
|
|
42
|
+
catch (error) {
|
|
43
|
+
console.error('Failed to load history:', error);
|
|
44
|
+
throw error;
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* Save current history to file
|
|
49
|
+
*/
|
|
50
|
+
async save() {
|
|
51
|
+
try {
|
|
52
|
+
// Ensure directory exists
|
|
53
|
+
if (!existsSync(CONFIG_DIR)) {
|
|
54
|
+
await mkdir(CONFIG_DIR, { recursive: true });
|
|
55
|
+
}
|
|
56
|
+
// Write history file
|
|
57
|
+
await writeFile(this.historyPath, JSON.stringify(this.history, null, 2), 'utf-8');
|
|
58
|
+
}
|
|
59
|
+
catch (error) {
|
|
60
|
+
console.error('Failed to save history:', error);
|
|
61
|
+
throw error;
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
/**
|
|
65
|
+
* Add an entry to history
|
|
66
|
+
*/
|
|
67
|
+
async addEntry(entry) {
|
|
68
|
+
const historyEntry = {
|
|
69
|
+
...entry,
|
|
70
|
+
id: randomUUID(),
|
|
71
|
+
timestamp: Date.now(),
|
|
72
|
+
};
|
|
73
|
+
this.history.entries.push(historyEntry);
|
|
74
|
+
await this.save();
|
|
75
|
+
return historyEntry;
|
|
76
|
+
}
|
|
77
|
+
/**
|
|
78
|
+
* Get all history entries
|
|
79
|
+
*/
|
|
80
|
+
getEntries() {
|
|
81
|
+
return this.history.entries;
|
|
82
|
+
}
|
|
83
|
+
/**
|
|
84
|
+
* Get entries by provider
|
|
85
|
+
*/
|
|
86
|
+
getEntriesByProvider(provider) {
|
|
87
|
+
return this.history.entries.filter(entry => entry.provider === provider);
|
|
88
|
+
}
|
|
89
|
+
/**
|
|
90
|
+
* Get recent entries (last N)
|
|
91
|
+
*/
|
|
92
|
+
getRecent(count = 10) {
|
|
93
|
+
return this.history.entries.slice(-count).reverse();
|
|
94
|
+
}
|
|
95
|
+
/**
|
|
96
|
+
* Clear all history
|
|
97
|
+
*/
|
|
98
|
+
async clear() {
|
|
99
|
+
this.history.entries = [];
|
|
100
|
+
await this.save();
|
|
101
|
+
}
|
|
102
|
+
/**
|
|
103
|
+
* Delete a specific entry
|
|
104
|
+
*/
|
|
105
|
+
async deleteEntry(id) {
|
|
106
|
+
const index = this.history.entries.findIndex(e => e.id === id);
|
|
107
|
+
if (index !== -1) {
|
|
108
|
+
this.history.entries.splice(index, 1);
|
|
109
|
+
await this.save();
|
|
110
|
+
return true;
|
|
111
|
+
}
|
|
112
|
+
return false;
|
|
113
|
+
}
|
|
114
|
+
/**
|
|
115
|
+
* Export history as JSON
|
|
116
|
+
*/
|
|
117
|
+
export() {
|
|
118
|
+
return structuredClone(this.history);
|
|
119
|
+
}
|
|
120
|
+
}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Abstract base class for all LLM providers
|
|
3
|
+
* Defines the interface that all provider implementations must follow
|
|
4
|
+
*/
|
|
5
|
+
import type { EnhancementOptions, ProviderCredentials } from '../types/index.js';
|
|
6
|
+
export declare abstract class Provider {
|
|
7
|
+
protected name: string;
|
|
8
|
+
protected credentials: ProviderCredentials;
|
|
9
|
+
protected defaultModel: string;
|
|
10
|
+
constructor(name: string, credentials: ProviderCredentials, defaultModel: string);
|
|
11
|
+
/**
|
|
12
|
+
* Get the provider name
|
|
13
|
+
*/
|
|
14
|
+
getName(): string;
|
|
15
|
+
/**
|
|
16
|
+
* Enhance a prompt using this provider
|
|
17
|
+
* @param prompt The prompt to enhance
|
|
18
|
+
* @param options Enhancement options (model, temperature, etc.)
|
|
19
|
+
* @returns The enhanced prompt as a string
|
|
20
|
+
*/
|
|
21
|
+
abstract enhance(prompt: string, options?: EnhancementOptions): Promise<string>;
|
|
22
|
+
/**
|
|
23
|
+
* Enhance a prompt with streaming output
|
|
24
|
+
* @param prompt The prompt to enhance
|
|
25
|
+
* @param options Enhancement options
|
|
26
|
+
* @returns AsyncGenerator that yields string chunks as they arrive
|
|
27
|
+
*/
|
|
28
|
+
abstract enhanceStream(prompt: string, options?: EnhancementOptions): AsyncGenerator<string, void, unknown>;
|
|
29
|
+
/**
|
|
30
|
+
* Get available models for this provider
|
|
31
|
+
* @returns Array of available model IDs
|
|
32
|
+
*/
|
|
33
|
+
abstract getAvailableModels(): Promise<string[]>;
|
|
34
|
+
/**
|
|
35
|
+
* Validate that the provider has valid credentials
|
|
36
|
+
* @returns true if credentials are valid, false otherwise
|
|
37
|
+
*/
|
|
38
|
+
abstract validateCredentials(): Promise<boolean>;
|
|
39
|
+
/**
|
|
40
|
+
* Get provider-specific metadata
|
|
41
|
+
*/
|
|
42
|
+
getMetadata(): {
|
|
43
|
+
name: string;
|
|
44
|
+
defaultModel: string;
|
|
45
|
+
supportsStreaming: boolean;
|
|
46
|
+
};
|
|
47
|
+
}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Abstract base class for all LLM providers
|
|
3
|
+
* Defines the interface that all provider implementations must follow
|
|
4
|
+
*/
|
|
5
|
+
export class Provider {
|
|
6
|
+
name;
|
|
7
|
+
credentials;
|
|
8
|
+
defaultModel;
|
|
9
|
+
constructor(name, credentials, defaultModel) {
|
|
10
|
+
this.name = name;
|
|
11
|
+
this.credentials = credentials;
|
|
12
|
+
this.defaultModel = defaultModel;
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* Get the provider name
|
|
16
|
+
*/
|
|
17
|
+
getName() {
|
|
18
|
+
return this.name;
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* Get provider-specific metadata
|
|
22
|
+
*/
|
|
23
|
+
getMetadata() {
|
|
24
|
+
return {
|
|
25
|
+
name: this.name,
|
|
26
|
+
defaultModel: this.defaultModel,
|
|
27
|
+
supportsStreaming: true,
|
|
28
|
+
};
|
|
29
|
+
}
|
|
30
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* GitHub Copilot provider implementation
|
|
3
|
+
* Uses OpenAI-compatible API.
|
|
4
|
+
* Auto-detects OAuth token from ~/.copilot/ and other well-known paths;
|
|
5
|
+
* falls back to manually-configured apiKey.
|
|
6
|
+
*/
|
|
7
|
+
import { Provider } from './base.js';
|
|
8
|
+
import type { EnhancementOptions, ProviderCredentials } from '../types/index.js';
|
|
9
|
+
export declare class CopilotProvider extends Provider {
|
|
10
|
+
private client;
|
|
11
|
+
private initialized;
|
|
12
|
+
constructor(credentials: ProviderCredentials);
|
|
13
|
+
private ensureInitialized;
|
|
14
|
+
enhance(prompt: string, options?: EnhancementOptions): Promise<string>;
|
|
15
|
+
enhanceStream(prompt: string, options?: EnhancementOptions): AsyncGenerator<string, void, unknown>;
|
|
16
|
+
getAvailableModels(): Promise<string[]>;
|
|
17
|
+
validateCredentials(): Promise<boolean>;
|
|
18
|
+
}
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* GitHub Copilot provider implementation
|
|
3
|
+
* Uses OpenAI-compatible API.
|
|
4
|
+
* Auto-detects OAuth token from ~/.copilot/ and other well-known paths;
|
|
5
|
+
* falls back to manually-configured apiKey.
|
|
6
|
+
*/
|
|
7
|
+
import { OpenAI } from 'openai';
|
|
8
|
+
import { Provider } from './base.js';
|
|
9
|
+
import { getCopilotToken } from '../utils/copilot-auth.js';
|
|
10
|
+
export class CopilotProvider extends Provider {
|
|
11
|
+
client;
|
|
12
|
+
initialized = false;
|
|
13
|
+
constructor(credentials) {
|
|
14
|
+
super('copilot', credentials, 'gpt-4o');
|
|
15
|
+
// Async init happens in ensureInitialized() before first use
|
|
16
|
+
}
|
|
17
|
+
async ensureInitialized() {
|
|
18
|
+
if (this.initialized)
|
|
19
|
+
return;
|
|
20
|
+
let apiKey = this.credentials.apiKey;
|
|
21
|
+
// Try auto-detection if no key was manually provided
|
|
22
|
+
if (!apiKey || this.credentials.useOAuth) {
|
|
23
|
+
const autoToken = await getCopilotToken();
|
|
24
|
+
if (autoToken) {
|
|
25
|
+
apiKey = autoToken;
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
if (!apiKey) {
|
|
29
|
+
throw new Error('Copilot provider: no API key found. Set an API key in settings or install GitHub Copilot / GitHub CLI so a token can be auto-detected.');
|
|
30
|
+
}
|
|
31
|
+
const config = { apiKey };
|
|
32
|
+
if (this.credentials.endpoint) {
|
|
33
|
+
config.baseURL = this.credentials.endpoint;
|
|
34
|
+
}
|
|
35
|
+
this.client = new OpenAI(config);
|
|
36
|
+
this.initialized = true;
|
|
37
|
+
}
|
|
38
|
+
async enhance(prompt, options) {
|
|
39
|
+
await this.ensureInitialized();
|
|
40
|
+
const systemPrompt = options?.systemPrompt ||
|
|
41
|
+
'You are an expert at enhancing and improving user prompts for LLMs. Analyze the given prompt and return an improved version that is clearer, more specific, and more likely to produce better results. Return ONLY the enhanced prompt, no explanations.';
|
|
42
|
+
const response = await this.client.chat.completions.create({
|
|
43
|
+
messages: [
|
|
44
|
+
{
|
|
45
|
+
role: 'system',
|
|
46
|
+
content: systemPrompt,
|
|
47
|
+
},
|
|
48
|
+
{
|
|
49
|
+
role: 'user',
|
|
50
|
+
content: `Original prompt:\n${prompt}`,
|
|
51
|
+
},
|
|
52
|
+
],
|
|
53
|
+
model: options?.model || this.defaultModel,
|
|
54
|
+
temperature: options?.temperature || 0.7,
|
|
55
|
+
max_tokens: options?.maxTokens || 1000,
|
|
56
|
+
});
|
|
57
|
+
const content = response.choices[0]?.message?.content;
|
|
58
|
+
if (!content) {
|
|
59
|
+
throw new Error('No response received from Copilot');
|
|
60
|
+
}
|
|
61
|
+
return content.trim();
|
|
62
|
+
}
|
|
63
|
+
async *enhanceStream(prompt, options) {
|
|
64
|
+
await this.ensureInitialized();
|
|
65
|
+
const systemPrompt = options?.systemPrompt ||
|
|
66
|
+
'You are an expert at enhancing and improving user prompts for LLMs. Analyze the given prompt and return an improved version that is clearer, more specific, and more likely to produce better results. Return ONLY the enhanced prompt, no explanations.';
|
|
67
|
+
const stream = await this.client.chat.completions.create({
|
|
68
|
+
messages: [
|
|
69
|
+
{
|
|
70
|
+
role: 'system',
|
|
71
|
+
content: systemPrompt,
|
|
72
|
+
},
|
|
73
|
+
{
|
|
74
|
+
role: 'user',
|
|
75
|
+
content: `Original prompt:\n${prompt}`,
|
|
76
|
+
},
|
|
77
|
+
],
|
|
78
|
+
model: options?.model || this.defaultModel,
|
|
79
|
+
temperature: options?.temperature || 0.7,
|
|
80
|
+
max_tokens: options?.maxTokens || 1000,
|
|
81
|
+
stream: true,
|
|
82
|
+
});
|
|
83
|
+
for await (const event of stream) {
|
|
84
|
+
const content = event.choices[0]?.delta?.content;
|
|
85
|
+
if (content) {
|
|
86
|
+
yield content;
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
async getAvailableModels() {
|
|
91
|
+
return ['gpt-4o', 'gpt-4o-mini', 'gpt-4.1', 'claude-sonnet-4', 'o3'];
|
|
92
|
+
}
|
|
93
|
+
async validateCredentials() {
|
|
94
|
+
try {
|
|
95
|
+
await this.ensureInitialized();
|
|
96
|
+
await this.client.chat.completions.create({
|
|
97
|
+
messages: [
|
|
98
|
+
{
|
|
99
|
+
role: 'user',
|
|
100
|
+
content: 'test',
|
|
101
|
+
},
|
|
102
|
+
],
|
|
103
|
+
model: this.defaultModel,
|
|
104
|
+
max_tokens: 1,
|
|
105
|
+
});
|
|
106
|
+
return true;
|
|
107
|
+
}
|
|
108
|
+
catch {
|
|
109
|
+
return false;
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Google Gemini provider implementation
|
|
3
|
+
*/
|
|
4
|
+
import { Provider } from './base.js';
|
|
5
|
+
import type { EnhancementOptions, ProviderCredentials } from '../types/index.js';
|
|
6
|
+
export declare class GeminiProvider extends Provider {
|
|
7
|
+
private client;
|
|
8
|
+
constructor(credentials: ProviderCredentials);
|
|
9
|
+
enhance(prompt: string, options?: EnhancementOptions): Promise<string>;
|
|
10
|
+
enhanceStream(prompt: string, options?: EnhancementOptions): AsyncGenerator<string, void, unknown>;
|
|
11
|
+
getAvailableModels(): Promise<string[]>;
|
|
12
|
+
validateCredentials(): Promise<boolean>;
|
|
13
|
+
}
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Google Gemini provider implementation
|
|
3
|
+
*/
|
|
4
|
+
import { GoogleGenerativeAI } from '@google/generative-ai';
|
|
5
|
+
import { Provider } from './base.js';
|
|
6
|
+
export class GeminiProvider extends Provider {
|
|
7
|
+
client;
|
|
8
|
+
constructor(credentials) {
|
|
9
|
+
super('gemini', credentials, 'gemini-2.5-flash');
|
|
10
|
+
if (!credentials.apiKey) {
|
|
11
|
+
throw new Error('Gemini provider requires an API key');
|
|
12
|
+
}
|
|
13
|
+
this.client = new GoogleGenerativeAI(credentials.apiKey);
|
|
14
|
+
}
|
|
15
|
+
async enhance(prompt, options) {
|
|
16
|
+
const model = this.client.getGenerativeModel({
|
|
17
|
+
model: options?.model || this.defaultModel,
|
|
18
|
+
});
|
|
19
|
+
const systemPrompt = options?.systemPrompt ||
|
|
20
|
+
'You are an expert at enhancing and improving user prompts for LLMs. Analyze the given prompt and return an improved version that is clearer, more specific, and more likely to produce better results. Return ONLY the enhanced prompt, no explanations.';
|
|
21
|
+
const result = await model.generateContent({
|
|
22
|
+
contents: [
|
|
23
|
+
{
|
|
24
|
+
role: 'user',
|
|
25
|
+
parts: [
|
|
26
|
+
{
|
|
27
|
+
text: `${systemPrompt}\n\nOriginal prompt:\n${prompt}`,
|
|
28
|
+
},
|
|
29
|
+
],
|
|
30
|
+
},
|
|
31
|
+
],
|
|
32
|
+
});
|
|
33
|
+
const response = result.response;
|
|
34
|
+
const text = response.text();
|
|
35
|
+
if (!text) {
|
|
36
|
+
throw new Error('No response received from Gemini');
|
|
37
|
+
}
|
|
38
|
+
return text.trim();
|
|
39
|
+
}
|
|
40
|
+
async *enhanceStream(prompt, options) {
|
|
41
|
+
const model = this.client.getGenerativeModel({
|
|
42
|
+
model: options?.model || this.defaultModel,
|
|
43
|
+
});
|
|
44
|
+
const systemPrompt = options?.systemPrompt ||
|
|
45
|
+
'You are an expert at enhancing and improving user prompts for LLMs. Analyze the given prompt and return an improved version that is clearer, more specific, and more likely to produce better results. Return ONLY the enhanced prompt, no explanations.';
|
|
46
|
+
const result = await model.generateContentStream({
|
|
47
|
+
contents: [
|
|
48
|
+
{
|
|
49
|
+
role: 'user',
|
|
50
|
+
parts: [
|
|
51
|
+
{
|
|
52
|
+
text: `${systemPrompt}\n\nOriginal prompt:\n${prompt}`,
|
|
53
|
+
},
|
|
54
|
+
],
|
|
55
|
+
},
|
|
56
|
+
],
|
|
57
|
+
});
|
|
58
|
+
for await (const chunk of result.stream) {
|
|
59
|
+
const text = chunk.text();
|
|
60
|
+
if (text) {
|
|
61
|
+
yield text;
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
async getAvailableModels() {
|
|
66
|
+
// Gemini models available as of 2025
|
|
67
|
+
return [
|
|
68
|
+
'gemini-2.5-flash',
|
|
69
|
+
'gemini-2.0-flash',
|
|
70
|
+
'gemini-1.5-pro',
|
|
71
|
+
'gemini-1.5-flash',
|
|
72
|
+
];
|
|
73
|
+
}
|
|
74
|
+
async validateCredentials() {
|
|
75
|
+
try {
|
|
76
|
+
const model = this.client.getGenerativeModel({
|
|
77
|
+
model: this.defaultModel,
|
|
78
|
+
});
|
|
79
|
+
await model.countTokens('test prompt');
|
|
80
|
+
return true;
|
|
81
|
+
}
|
|
82
|
+
catch {
|
|
83
|
+
return false;
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Provider factory and registry
|
|
3
|
+
* Manages creation and registration of provider instances
|
|
4
|
+
*/
|
|
5
|
+
import { Provider } from './base.js';
|
|
6
|
+
import type { ProviderCredentials } from '../types/index.js';
|
|
7
|
+
export type ProviderType = 'gemini' | 'copilot' | 'kilo' | 'opencode';
|
|
8
|
+
export declare class ProviderFactory {
|
|
9
|
+
private registry;
|
|
10
|
+
createProvider(type: ProviderType, credentials: ProviderCredentials): Provider;
|
|
11
|
+
registerProvider(name: string, provider: Provider): void;
|
|
12
|
+
getProvider(name: string): Provider | undefined;
|
|
13
|
+
getAllProviders(): Provider[];
|
|
14
|
+
hasProvider(name: string): boolean;
|
|
15
|
+
listProviders(): string[];
|
|
16
|
+
clearProviders(): void;
|
|
17
|
+
}
|
|
18
|
+
export declare const providerFactory: ProviderFactory;
|
|
19
|
+
/**
|
|
20
|
+
* Get or create a provider by type with credentials
|
|
21
|
+
*/
|
|
22
|
+
export declare function getProvider(type: ProviderType, credentials?: ProviderCredentials): Provider;
|
|
23
|
+
/**
|
|
24
|
+
* Get list of supported provider types
|
|
25
|
+
*/
|
|
26
|
+
export declare function getSupportedProviders(): ProviderType[];
|