@stan-chen/simple-cli 0.2.2 → 0.2.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.
Files changed (100) hide show
  1. package/README.md +58 -271
  2. package/dist/anyllm.py +62 -0
  3. package/dist/builtins.d.ts +726 -0
  4. package/dist/builtins.js +481 -0
  5. package/dist/cli.d.ts +0 -4
  6. package/dist/cli.js +37 -279
  7. package/dist/engine.d.ts +33 -0
  8. package/dist/engine.js +138 -0
  9. package/dist/learnings.d.ts +15 -0
  10. package/dist/learnings.js +54 -0
  11. package/dist/llm.d.ts +18 -0
  12. package/dist/llm.js +66 -0
  13. package/dist/mcp.d.ts +132 -0
  14. package/dist/mcp.js +43 -0
  15. package/dist/skills.d.ts +5 -16
  16. package/dist/skills.js +91 -253
  17. package/dist/tui.d.ts +1 -0
  18. package/dist/tui.js +10 -0
  19. package/package.json +88 -78
  20. package/dist/commands/add.d.ts +0 -9
  21. package/dist/commands/add.js +0 -50
  22. package/dist/commands/git/commit.d.ts +0 -12
  23. package/dist/commands/git/commit.js +0 -97
  24. package/dist/commands/git/status.d.ts +0 -6
  25. package/dist/commands/git/status.js +0 -42
  26. package/dist/commands/index.d.ts +0 -16
  27. package/dist/commands/index.js +0 -376
  28. package/dist/commands/mcp/status.d.ts +0 -6
  29. package/dist/commands/mcp/status.js +0 -31
  30. package/dist/commands/swarm.d.ts +0 -36
  31. package/dist/commands/swarm.js +0 -236
  32. package/dist/commands.d.ts +0 -32
  33. package/dist/commands.js +0 -427
  34. package/dist/context.d.ts +0 -116
  35. package/dist/context.js +0 -327
  36. package/dist/index.d.ts +0 -6
  37. package/dist/index.js +0 -109
  38. package/dist/lib/agent.d.ts +0 -98
  39. package/dist/lib/agent.js +0 -281
  40. package/dist/lib/editor.d.ts +0 -74
  41. package/dist/lib/editor.js +0 -441
  42. package/dist/lib/git.d.ts +0 -164
  43. package/dist/lib/git.js +0 -351
  44. package/dist/lib/ui.d.ts +0 -159
  45. package/dist/lib/ui.js +0 -252
  46. package/dist/mcp/client.d.ts +0 -22
  47. package/dist/mcp/client.js +0 -81
  48. package/dist/mcp/manager.d.ts +0 -186
  49. package/dist/mcp/manager.js +0 -446
  50. package/dist/prompts/provider.d.ts +0 -22
  51. package/dist/prompts/provider.js +0 -78
  52. package/dist/providers/index.d.ts +0 -15
  53. package/dist/providers/index.js +0 -82
  54. package/dist/providers/multi.d.ts +0 -11
  55. package/dist/providers/multi.js +0 -28
  56. package/dist/registry.d.ts +0 -24
  57. package/dist/registry.js +0 -379
  58. package/dist/repoMap.d.ts +0 -5
  59. package/dist/repoMap.js +0 -79
  60. package/dist/router.d.ts +0 -41
  61. package/dist/router.js +0 -108
  62. package/dist/swarm/coordinator.d.ts +0 -86
  63. package/dist/swarm/coordinator.js +0 -257
  64. package/dist/swarm/index.d.ts +0 -28
  65. package/dist/swarm/index.js +0 -29
  66. package/dist/swarm/task.d.ts +0 -104
  67. package/dist/swarm/task.js +0 -221
  68. package/dist/swarm/types.d.ts +0 -132
  69. package/dist/swarm/types.js +0 -37
  70. package/dist/swarm/worker.d.ts +0 -107
  71. package/dist/swarm/worker.js +0 -299
  72. package/dist/tools/analyzeFile.d.ts +0 -16
  73. package/dist/tools/analyzeFile.js +0 -43
  74. package/dist/tools/git.d.ts +0 -40
  75. package/dist/tools/git.js +0 -236
  76. package/dist/tools/glob.d.ts +0 -34
  77. package/dist/tools/glob.js +0 -165
  78. package/dist/tools/grep.d.ts +0 -53
  79. package/dist/tools/grep.js +0 -296
  80. package/dist/tools/linter.d.ts +0 -35
  81. package/dist/tools/linter.js +0 -349
  82. package/dist/tools/listDir.d.ts +0 -29
  83. package/dist/tools/listDir.js +0 -50
  84. package/dist/tools/memory.d.ts +0 -34
  85. package/dist/tools/memory.js +0 -215
  86. package/dist/tools/readFiles.d.ts +0 -25
  87. package/dist/tools/readFiles.js +0 -31
  88. package/dist/tools/reloadTools.d.ts +0 -11
  89. package/dist/tools/reloadTools.js +0 -22
  90. package/dist/tools/runCommand.d.ts +0 -32
  91. package/dist/tools/runCommand.js +0 -79
  92. package/dist/tools/scraper.d.ts +0 -31
  93. package/dist/tools/scraper.js +0 -211
  94. package/dist/tools/writeFiles.d.ts +0 -63
  95. package/dist/tools/writeFiles.js +0 -87
  96. package/dist/ui/server.d.ts +0 -5
  97. package/dist/ui/server.js +0 -74
  98. package/dist/watcher.d.ts +0 -35
  99. package/dist/watcher.js +0 -164
  100. /package/{docs/assets → assets}/logo.jpeg +0 -0
@@ -1,446 +0,0 @@
1
- /**
2
- * MCP Client Manager - Manages multiple MCP server connections
3
- * Supports Composio, custom MCP servers, and dynamic tool discovery
4
- * Based on GeminiCLI's mcp-client.ts patterns
5
- */
6
- import { Client } from '@modelcontextprotocol/sdk/client/index.js';
7
- import { StdioClientTransport } from '@modelcontextprotocol/sdk/client/stdio.js';
8
- import { z } from 'zod';
9
- import { existsSync, readFileSync } from 'fs';
10
- import { join } from 'path';
11
- // MCP Server Configuration Schema
12
- export const MCPServerConfigSchema = z.object({
13
- name: z.string(),
14
- command: z.string().optional(),
15
- args: z.array(z.string()).optional(),
16
- env: z.record(z.string()).optional(),
17
- url: z.string().optional(),
18
- type: z.enum(['stdio', 'sse', 'http']).optional(),
19
- timeout: z.number().optional(),
20
- trust: z.enum(['full', 'partial', 'none']).optional(),
21
- enabled: z.boolean().optional().default(true),
22
- });
23
- // Server Status
24
- export var MCPServerStatus;
25
- (function (MCPServerStatus) {
26
- MCPServerStatus["DISCONNECTED"] = "disconnected";
27
- MCPServerStatus["CONNECTING"] = "connecting";
28
- MCPServerStatus["CONNECTED"] = "connected";
29
- MCPServerStatus["ERROR"] = "error";
30
- })(MCPServerStatus || (MCPServerStatus = {}));
31
- /**
32
- * MCP Client Manager
33
- * Manages connections to multiple MCP servers and aggregates their tools
34
- */
35
- export class MCPManager {
36
- servers = new Map();
37
- onStatusChange;
38
- constructor(options) {
39
- this.onStatusChange = options?.onStatusChange;
40
- }
41
- /**
42
- * Load MCP configuration from all available sources
43
- */
44
- async loadConfig(configPath) {
45
- const allConfigs = [];
46
- const sources = [
47
- { path: configPath, type: 'file' },
48
- { path: join(process.cwd(), 'mcp.json'), type: 'file' },
49
- { path: join(process.cwd(), '.mcp.json'), type: 'file' },
50
- { path: join(process.cwd(), 'mcp'), type: 'dir' },
51
- { path: join(process.env.HOME || '', '.config', 'simplecli', 'mcp.json'), type: 'file' },
52
- { path: join(process.env.APPDATA || '', 'simplecli', 'mcp.json'), type: 'file' },
53
- ].filter(s => s.path && existsSync(s.path));
54
- for (const source of sources) {
55
- try {
56
- if (source.type === 'file') {
57
- const content = readFileSync(source.path, 'utf-8');
58
- allConfigs.push(...this.parseConfigContent(content));
59
- }
60
- else if (source.type === 'dir') {
61
- // Scan directory for .json files
62
- const { readdir } = await import('fs/promises');
63
- const files = await readdir(source.path);
64
- for (const file of files) {
65
- if (file.endsWith('.json')) {
66
- const content = readFileSync(join(source.path, file), 'utf-8');
67
- allConfigs.push(...this.parseConfigContent(content));
68
- }
69
- }
70
- }
71
- }
72
- catch (e) {
73
- if (process.env.DEBUG)
74
- console.error(`Failed to load MCP source ${source.path}:`, e);
75
- }
76
- }
77
- // De-duplicate by name, preferring earlier (local) configs
78
- const unique = new Map();
79
- for (const config of allConfigs) {
80
- if (!unique.has(config.name)) {
81
- unique.set(config.name, config);
82
- }
83
- }
84
- return Array.from(unique.values()).filter(s => s.enabled !== false);
85
- }
86
- /**
87
- * Parse MCP config content which can be in multiple formats
88
- */
89
- parseConfigContent(content) {
90
- try {
91
- const config = JSON.parse(content);
92
- // Handle { servers: [...] } format
93
- if (Array.isArray(config.servers)) {
94
- return config.servers;
95
- }
96
- // Handle { mcpServers: { name: config } } format (Claude-style)
97
- if (config.mcpServers) {
98
- return Object.entries(config.mcpServers).map(([name, cfg]) => ({
99
- name,
100
- ...cfg
101
- }));
102
- }
103
- // Handle single server config
104
- if (config.name && (config.command || config.url)) {
105
- return [config];
106
- }
107
- }
108
- catch (e) {
109
- if (process.env.DEBUG)
110
- console.error('Failed to parse MCP content:', e);
111
- }
112
- return [];
113
- }
114
- /**
115
- * Connect to an MCP server
116
- */
117
- async connect(config) {
118
- const serverName = config.name;
119
- if (this.servers.has(serverName)) {
120
- await this.disconnect(serverName);
121
- }
122
- const state = {
123
- client: null,
124
- transport: null,
125
- status: MCPServerStatus.CONNECTING,
126
- config,
127
- tools: [],
128
- resources: [],
129
- prompts: [],
130
- };
131
- this.servers.set(serverName, state);
132
- this.updateStatus(serverName, MCPServerStatus.CONNECTING);
133
- try {
134
- const client = new Client({ name: 'simplecli', version: '0.2.1' }, { capabilities: {} });
135
- let transport;
136
- if (config.command) {
137
- // Stdio transport
138
- transport = new StdioClientTransport({
139
- command: config.command,
140
- args: config.args || [],
141
- env: { ...process.env, ...(config.env || {}) },
142
- });
143
- }
144
- else if (config.url) {
145
- // For URL-based transports, we'd need additional SDK imports
146
- // For now, throw an error suggesting stdio
147
- throw new Error('URL-based MCP transports require additional configuration. Use stdio transport with command.');
148
- }
149
- else {
150
- throw new Error(`Invalid MCP server config for ${serverName}: missing command or url`);
151
- }
152
- // Connect with a 5-second timeout
153
- await Promise.race([
154
- client.connect(transport),
155
- new Promise((_, reject) => setTimeout(() => reject(new Error('Connection timeout')), 5000))
156
- ]);
157
- state.client = client;
158
- state.transport = transport;
159
- state.status = MCPServerStatus.CONNECTED;
160
- // Discover tools, resources, and prompts
161
- await this.discover(serverName);
162
- this.updateStatus(serverName, MCPServerStatus.CONNECTED);
163
- console.log(`✓ Connected to MCP server: ${serverName}`);
164
- }
165
- catch (error) {
166
- state.status = MCPServerStatus.ERROR;
167
- state.error = error instanceof Error ? error.message : String(error);
168
- this.updateStatus(serverName, MCPServerStatus.ERROR);
169
- console.error(`✗ Failed to connect to MCP server ${serverName}:`, state.error);
170
- }
171
- }
172
- /**
173
- * Connect to all configured servers
174
- */
175
- async connectAll(configs) {
176
- const serverConfigs = configs || await this.loadConfig();
177
- await Promise.all(serverConfigs.map(config => this.connect(config).catch(e => {
178
- console.error(`Failed to connect to ${config.name}:`, e);
179
- })));
180
- }
181
- /**
182
- * Disconnect from an MCP server
183
- */
184
- async disconnect(serverName) {
185
- const state = this.servers.get(serverName);
186
- if (!state)
187
- return;
188
- try {
189
- if (state.transport) {
190
- await state.transport.close();
191
- }
192
- if (state.client) {
193
- await state.client.close();
194
- }
195
- }
196
- catch (e) {
197
- // Ignore close errors
198
- }
199
- this.servers.delete(serverName);
200
- this.updateStatus(serverName, MCPServerStatus.DISCONNECTED);
201
- }
202
- /**
203
- * Disconnect from all servers
204
- */
205
- async disconnectAll() {
206
- await Promise.all(Array.from(this.servers.keys()).map(name => this.disconnect(name)));
207
- }
208
- /**
209
- * Discover tools, resources, and prompts from a server
210
- */
211
- async discover(serverName) {
212
- const state = this.servers.get(serverName);
213
- if (!state?.client)
214
- return;
215
- const client = state.client;
216
- // Discover tools
217
- try {
218
- const toolsResult = await client.listTools();
219
- state.tools = toolsResult.tools.map(tool => ({
220
- name: tool.name,
221
- description: tool.description || '',
222
- serverName,
223
- inputSchema: tool.inputSchema || {},
224
- execute: async (args) => {
225
- const result = await client.callTool({ name: tool.name, arguments: args });
226
- return result;
227
- },
228
- }));
229
- }
230
- catch {
231
- // Server may not support tools
232
- }
233
- // Discover resources
234
- try {
235
- const resourcesResult = await client.listResources();
236
- state.resources = resourcesResult.resources.map(resource => ({
237
- uri: resource.uri,
238
- name: resource.name,
239
- description: resource.description,
240
- mimeType: resource.mimeType,
241
- serverName,
242
- }));
243
- }
244
- catch {
245
- // Server may not support resources
246
- }
247
- // Discover prompts
248
- try {
249
- const promptsResult = await client.listPrompts();
250
- state.prompts = promptsResult.prompts.map(prompt => ({
251
- name: prompt.name,
252
- description: prompt.description,
253
- serverName,
254
- arguments: prompt.arguments,
255
- invoke: async (params) => {
256
- const stringParams = {};
257
- for (const [k, v] of Object.entries(params)) {
258
- stringParams[k] = String(v);
259
- }
260
- const result = await client.getPrompt({ name: prompt.name, arguments: stringParams });
261
- return {
262
- messages: result.messages.map(m => ({
263
- role: m.role,
264
- content: typeof m.content === 'string' ? m.content : JSON.stringify(m.content),
265
- })),
266
- };
267
- },
268
- }));
269
- }
270
- catch {
271
- // Server may not support prompts
272
- }
273
- }
274
- /**
275
- * Get all discovered tools from all connected servers
276
- */
277
- getAllTools() {
278
- const tools = [];
279
- for (const state of this.servers.values()) {
280
- if (state.status === MCPServerStatus.CONNECTED) {
281
- tools.push(...state.tools);
282
- }
283
- }
284
- return tools;
285
- }
286
- /**
287
- * Get all discovered resources from all connected servers
288
- */
289
- getAllResources() {
290
- const resources = [];
291
- for (const state of this.servers.values()) {
292
- if (state.status === MCPServerStatus.CONNECTED) {
293
- resources.push(...state.resources);
294
- }
295
- }
296
- return resources;
297
- }
298
- /**
299
- * Get all discovered prompts from all connected servers
300
- */
301
- getAllPrompts() {
302
- const prompts = [];
303
- for (const state of this.servers.values()) {
304
- if (state.status === MCPServerStatus.CONNECTED) {
305
- prompts.push(...state.prompts);
306
- }
307
- }
308
- return prompts;
309
- }
310
- /**
311
- * Get a specific tool by name
312
- */
313
- getTool(name) {
314
- return this.getAllTools().find(t => t.name === name);
315
- }
316
- /**
317
- * Execute an MCP tool
318
- */
319
- async executeTool(name, args) {
320
- const tool = this.getTool(name);
321
- if (!tool) {
322
- throw new Error(`MCP tool not found: ${name}`);
323
- }
324
- return tool.execute(args);
325
- }
326
- /**
327
- * Read an MCP resource
328
- */
329
- async readResource(uri) {
330
- // Find which server has this resource
331
- for (const state of this.servers.values()) {
332
- if (state.status !== MCPServerStatus.CONNECTED || !state.client)
333
- continue;
334
- const resource = state.resources.find(r => r.uri === uri);
335
- if (resource) {
336
- const result = await state.client.readResource({ uri });
337
- return result;
338
- }
339
- }
340
- throw new Error(`Resource not found: ${uri}`);
341
- }
342
- /**
343
- * Invoke an MCP prompt
344
- */
345
- async invokePrompt(name, params) {
346
- const prompt = this.getAllPrompts().find(p => p.name === name);
347
- if (!prompt) {
348
- throw new Error(`MCP prompt not found: ${name}`);
349
- }
350
- return prompt.invoke(params);
351
- }
352
- /**
353
- * Get server status
354
- */
355
- getServerStatus(serverName) {
356
- return this.servers.get(serverName)?.status || MCPServerStatus.DISCONNECTED;
357
- }
358
- /**
359
- * Get all server statuses
360
- */
361
- getAllServerStatuses() {
362
- const statuses = new Map();
363
- for (const [name, state] of this.servers) {
364
- statuses.set(name, state.status);
365
- }
366
- return statuses;
367
- }
368
- updateStatus(serverName, status) {
369
- this.onStatusChange?.(serverName, status);
370
- }
371
- }
372
- /**
373
- * Create Composio MCP configuration
374
- * Composio provides pre-built integrations for 250+ tools
375
- */
376
- export function createComposioConfig(apiKey) {
377
- return {
378
- name: 'composio',
379
- command: 'npx',
380
- args: ['-y', 'composio-core', 'mcp'],
381
- env: apiKey ? { COMPOSIO_API_KEY: apiKey } : {},
382
- trust: 'full',
383
- enabled: true,
384
- };
385
- }
386
- /**
387
- * Create filesystem MCP configuration
388
- */
389
- export function createFilesystemConfig(allowedPaths) {
390
- return {
391
- name: 'filesystem',
392
- command: 'npx',
393
- args: ['-y', '@modelcontextprotocol/server-filesystem', ...allowedPaths],
394
- trust: 'full',
395
- enabled: true,
396
- };
397
- }
398
- /**
399
- * Create GitHub MCP configuration
400
- */
401
- export function createGitHubConfig(token) {
402
- return {
403
- name: 'github',
404
- command: 'npx',
405
- args: ['-y', '@modelcontextprotocol/server-github'],
406
- env: token ? { GITHUB_PERSONAL_ACCESS_TOKEN: token } : {},
407
- trust: 'full',
408
- enabled: true,
409
- };
410
- }
411
- /**
412
- * Create memory/context MCP configuration
413
- */
414
- export function createMemoryConfig() {
415
- return {
416
- name: 'memory',
417
- command: 'npx',
418
- args: ['-y', '@modelcontextprotocol/server-memory'],
419
- trust: 'full',
420
- enabled: true,
421
- };
422
- }
423
- /**
424
- * Create Brave Search MCP configuration
425
- */
426
- export function createBraveSearchConfig(apiKey) {
427
- return {
428
- name: 'brave-search',
429
- command: 'npx',
430
- args: ['-y', '@modelcontextprotocol/server-brave-search'],
431
- env: apiKey ? { BRAVE_API_KEY: apiKey } : {},
432
- trust: 'full',
433
- enabled: true,
434
- };
435
- }
436
- // Singleton instance
437
- let mcpManager = null;
438
- /**
439
- * Get or create the global MCP manager instance
440
- */
441
- export function getMCPManager() {
442
- if (!mcpManager) {
443
- mcpManager = new MCPManager();
444
- }
445
- return mcpManager;
446
- }
@@ -1,22 +0,0 @@
1
- /**
2
- * Prompt Provider - Manages composition of system prompts
3
- * Supports built-in defaults, project-specific overrides, and modular personas.
4
- */
5
- export interface PromptOptions {
6
- cwd: string;
7
- skillPrompt?: string;
8
- }
9
- export declare class PromptProvider {
10
- /**
11
- * Builds the full system prompt by composing multiple layers:
12
- * 1. Built-in system instructions (src/prompts/defaults)
13
- * 2. Global user overrides (~/.simple/prompts)
14
- * 3. Project-specific prompts (.simple/prompts)
15
- * 4. Current skill instructions
16
- * 5. Local project rules (AGENT.md)
17
- */
18
- getSystemPrompt(options: PromptOptions): Promise<string>;
19
- private loadFromDirectory;
20
- private loadProjectRules;
21
- }
22
- export declare function getPromptProvider(): PromptProvider;
@@ -1,78 +0,0 @@
1
- /**
2
- * Prompt Provider - Manages composition of system prompts
3
- * Supports built-in defaults, project-specific overrides, and modular personas.
4
- */
5
- import { readFileSync, existsSync, readdirSync } from 'fs';
6
- import { join, dirname } from 'path';
7
- import { fileURLToPath } from 'url';
8
- import { homedir } from 'os';
9
- const __filename = fileURLToPath(import.meta.url);
10
- const __dirname = dirname(__filename);
11
- export class PromptProvider {
12
- /**
13
- * Builds the full system prompt by composing multiple layers:
14
- * 1. Built-in system instructions (src/prompts/defaults)
15
- * 2. Global user overrides (~/.simple/prompts)
16
- * 3. Project-specific prompts (.simple/prompts)
17
- * 4. Current skill instructions
18
- * 5. Local project rules (AGENT.md)
19
- */
20
- async getSystemPrompt(options) {
21
- const parts = [];
22
- // 1. Built-in defaults (Hardcoded in the package)
23
- const builtInDir = join(__dirname, 'defaults');
24
- if (existsSync(builtInDir)) {
25
- parts.push(...this.loadFromDirectory(builtInDir));
26
- }
27
- // 2. Global user rules (~/.simple/AGENT.md)
28
- const globalRulesPath = join(homedir(), '.simple', 'AGENT.md');
29
- if (existsSync(globalRulesPath)) {
30
- parts.push('\n## Global Rules\n' + readFileSync(globalRulesPath, 'utf-8'));
31
- }
32
- // 3. Project rules (AGENT.md)
33
- const rules = this.loadProjectRules(options.cwd);
34
- if (rules) {
35
- parts.push('\n## Project Rules\n' + rules);
36
- }
37
- // 4. Skill-specific prompt (Dynamic based on @skill)
38
- if (options.skillPrompt) {
39
- parts.push('\n## Skill Context: ' + options.skillPrompt);
40
- }
41
- return parts.join('\n\n').trim();
42
- }
43
- loadFromDirectory(dir) {
44
- try {
45
- return readdirSync(dir)
46
- .filter(f => f.endsWith('.md') || f.endsWith('.mdc'))
47
- .sort() // Ensure deterministic order
48
- .map(f => readFileSync(join(dir, f), 'utf-8'));
49
- }
50
- catch {
51
- return [];
52
- }
53
- }
54
- loadProjectRules(cwd) {
55
- const commonPaths = [
56
- '.simple/AGENT.md',
57
- '.agent/AGENT.md',
58
- 'AGENT.md',
59
- '.agent.md',
60
- '.cursorrules',
61
- '.aider/agent.md'
62
- ];
63
- for (const p of commonPaths) {
64
- const fullPath = join(cwd, p);
65
- if (existsSync(fullPath)) {
66
- return readFileSync(fullPath, 'utf-8');
67
- }
68
- }
69
- return null;
70
- }
71
- }
72
- let provider = null;
73
- export function getPromptProvider() {
74
- if (!provider) {
75
- provider = new PromptProvider();
76
- }
77
- return provider;
78
- }
@@ -1,15 +0,0 @@
1
- /**
2
- * Provider Bridge: Unified LLM interface via OpenAI SDK
3
- * Supports OpenAI, DeepSeek, Groq, and other OpenAI-compatible endpoints.
4
- */
5
- export interface Message {
6
- role: string;
7
- content: string;
8
- }
9
- export interface Provider {
10
- name: string;
11
- model: string;
12
- generateResponse: (systemPrompt: string, messages: Message[]) => Promise<string>;
13
- }
14
- export declare const createProviderForModel: (model: string) => Provider;
15
- export declare const createProvider: () => Provider;
@@ -1,82 +0,0 @@
1
- /**
2
- * Provider Bridge: Unified LLM interface via OpenAI SDK
3
- * Supports OpenAI, DeepSeek, Groq, and other OpenAI-compatible endpoints.
4
- */
5
- import OpenAI from 'openai';
6
- const getProviderConfig = () => {
7
- // 1. OpenAI (Default)
8
- if (process.env.OPENAI_API_KEY) {
9
- return {
10
- apiKey: process.env.OPENAI_API_KEY,
11
- model: process.env.OPENAI_MODEL || 'gpt-5-mini'
12
- };
13
- }
14
- // 2. DeepSeek
15
- if (process.env.DEEPSEEK_API_KEY) {
16
- return {
17
- apiKey: process.env.DEEPSEEK_API_KEY,
18
- baseURL: 'https://api.deepseek.com/v1',
19
- model: process.env.DEEPSEEK_MODEL || 'deepseek-chat'
20
- };
21
- }
22
- // 3. Groq
23
- if (process.env.GROQ_API_KEY) {
24
- return {
25
- apiKey: process.env.GROQ_API_KEY,
26
- baseURL: 'https://api.groq.com/openai/v1',
27
- model: process.env.GROQ_MODEL || 'llama3-70b-8192'
28
- };
29
- }
30
- // 4. Mistral
31
- if (process.env.MISTRAL_API_KEY) {
32
- return {
33
- apiKey: process.env.MISTRAL_API_KEY,
34
- baseURL: 'https://api.mistral.ai/v1',
35
- model: process.env.MISTRAL_MODEL || 'mistral-large-latest'
36
- };
37
- }
38
- throw new Error('No supported API key found (OPENAI_API_KEY, DEEPSEEK_API_KEY, GROQ_API_KEY, MISTRAL_API_KEY)');
39
- };
40
- export const createProviderForModel = (model) => {
41
- // Quick heuristic to determine provider for specific model overrides
42
- // logic can be improved, but this assumes the environment variables set the *default* linkage
43
- // If a specific model is requested (e.g. for MoE), we try to route it.
44
- let config = getProviderConfig();
45
- // Override config if model implies a different provider?
46
- // For the sake of "Simple-CLI", we assume the default connected provider serves the requested model
47
- // or we just use OpenAI SDK's flexibility.
48
- if (model.includes('gpt'))
49
- config = { ...config, apiKey: process.env.OPENAI_API_KEY, baseURL: undefined };
50
- else if (model.includes('deepseek'))
51
- config = { ...config, apiKey: process.env.DEEPSEEK_API_KEY, baseURL: 'https://api.deepseek.com/v1' };
52
- if (!config.apiKey)
53
- throw new Error(`Cannot route for model ${model} - missing API key`);
54
- const client = new OpenAI({
55
- apiKey: config.apiKey,
56
- baseURL: config.baseURL
57
- });
58
- return {
59
- name: 'openai-compatible',
60
- model,
61
- generateResponse: async (systemPrompt, messages) => {
62
- try {
63
- const response = await client.chat.completions.create({
64
- model: model,
65
- messages: [
66
- { role: 'system', content: systemPrompt },
67
- ...messages.map(m => ({ role: m.role, content: m.content }))
68
- ]
69
- });
70
- return response.choices[0]?.message?.content || '';
71
- }
72
- catch (e) {
73
- return `Error calling LLM: ${e instanceof Error ? e.message : e}`; // Fail gracefully
74
- }
75
- }
76
- };
77
- };
78
- export const createProvider = () => {
79
- const config = getProviderConfig();
80
- console.log(`🤖 Using model: ${config.model}`);
81
- return createProviderForModel(config.model);
82
- };
@@ -1,11 +0,0 @@
1
- /**
2
- * Multi-Provider: Manages multiple LLM models for MoE routing via LiteLLM
3
- * Each tier can use a different model from any provider
4
- */
5
- import { type Provider, type Message } from './index.js';
6
- import type { Tier, TierConfig } from '../router.js';
7
- export interface MultiProvider {
8
- getProvider: (tier: Tier) => Provider;
9
- generateWithTier: (tier: Tier, systemPrompt: string, messages: Message[]) => Promise<string>;
10
- }
11
- export declare const createMultiProvider: (tierConfigs: Map<Tier, TierConfig>) => MultiProvider;
@@ -1,28 +0,0 @@
1
- /**
2
- * Multi-Provider: Manages multiple LLM models for MoE routing via LiteLLM
3
- * Each tier can use a different model from any provider
4
- */
5
- import { createProviderForModel } from './index.js';
6
- // Create multi-provider system using LiteLLM
7
- export const createMultiProvider = (tierConfigs) => {
8
- const providerCache = new Map();
9
- const getProvider = (tier) => {
10
- const cached = providerCache.get(tier);
11
- if (cached)
12
- return cached;
13
- const config = tierConfigs.get(tier);
14
- if (!config) {
15
- throw new Error(`No configuration for tier ${tier}`);
16
- }
17
- const provider = createProviderForModel(config.model);
18
- providerCache.set(tier, provider);
19
- return provider;
20
- };
21
- return {
22
- getProvider,
23
- generateWithTier: async (tier, systemPrompt, messages) => {
24
- const provider = getProvider(tier);
25
- return provider.generateResponse(systemPrompt, messages);
26
- }
27
- };
28
- };