smart-aipi 1.3.0 → 1.4.1

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 CHANGED
@@ -4,6 +4,19 @@ CLI for [Smart AIPI](https://smartaipi.com) — OpenAI-compatible API at 75% off
4
4
 
5
5
  ## Install
6
6
 
7
+ ### Install both packages (recommended)
8
+
9
+ ```bash
10
+ npm install -g smart-aipi @smart-aipi/mcp
11
+ ```
12
+
13
+ What each package provides:
14
+
15
+ - `smart-aipi` (CLI): programmatic control for account, keys, tokens, and usage from terminal.
16
+ - `@smart-aipi/mcp`: easy setup for AI agents through MCP with docs + management tools.
17
+
18
+ ### CLI only
19
+
7
20
  ```bash
8
21
  npm install -g smart-aipi
9
22
  ```
package/bin/cli.js CHANGED
@@ -7,6 +7,7 @@ import inquirer from 'inquirer';
7
7
  import { createRequire } from 'node:module';
8
8
  import { DOCS, TOPIC_LIST, DOC_CATEGORIES } from '../lib/docs.js';
9
9
  import { registerLoginCommand, saveAuthState } from '../lib/login.js';
10
+ import { registerSetupCommands } from '../lib/setup.js';
10
11
  const API_BASE = process.env.SMART_AIPI_URL || 'https://api.smartaipi.com';
11
12
  const WEB_BASE = process.env.SMART_AIPI_WEB_URL || 'https://smartaipi.com';
12
13
  const require = createRequire(import.meta.url);
@@ -22,6 +23,10 @@ registerLoginCommand(program, {
22
23
  apiBase: API_BASE,
23
24
  webBase: WEB_BASE
24
25
  });
26
+ registerSetupCommands(program, {
27
+ config,
28
+ apiBase: API_BASE
29
+ });
25
30
  program
26
31
  .command('signup')
27
32
  .description('Create a new Smart AIPI account')
package/lib/docs.js CHANGED
@@ -7,16 +7,22 @@ export const DOCS = {
7
7
 
8
8
  Smart AIPI is an OpenAI-compatible API. Set two env vars and go.
9
9
 
10
- ${chalk.cyan('1. Install & sign up')}
11
- ${chalk.dim('$ npm install -g smart-aipi')}
10
+ ${chalk.cyan('1. Install both packages')}
11
+ ${chalk.dim('$ npm install -g smart-aipi @smart-aipi/mcp')}
12
+
13
+ ${chalk.dim('- smart-aipi CLI: programmatic account/key/token/usage control')}
14
+ ${chalk.dim('- @smart-aipi/mcp: easy AI-agent setup with MCP tools')}
15
+
16
+ ${chalk.cyan('2. Create account + credentials')}
12
17
  ${chalk.dim('$ smart-aipi signup')}
13
18
  ${chalk.dim('$ smart-aipi keys create')}
19
+ ${chalk.dim('$ smart-aipi tokens create -n "MCP Token"')}
14
20
 
15
- ${chalk.cyan('2. Set environment variables')}
21
+ ${chalk.cyan('3. Set environment variables')}
16
22
  ${chalk.dim('export OPENAI_BASE_URL=https://api.smartaipi.com/v1')}
17
23
  ${chalk.dim('export OPENAI_API_KEY=sk-your-api-key')}
18
24
 
19
- ${chalk.cyan('3. Use with any OpenAI-compatible tool')}
25
+ ${chalk.cyan('4. Use with any OpenAI-compatible tool')}
20
26
  Your existing code, SDKs, and AI tools work automatically.
21
27
 
22
28
  ${chalk.cyan('Available topics:')}
package/lib/setup.js ADDED
@@ -0,0 +1,387 @@
1
+ import chalk from 'chalk';
2
+ import ora from 'ora';
3
+ import inquirer from 'inquirer';
4
+ import { existsSync, mkdirSync, readFileSync, writeFileSync } from 'node:fs';
5
+ import { homedir } from 'node:os';
6
+ import { join } from 'node:path';
7
+ // ─── Shared: get or create an API key ─────────────────────────────────────────
8
+ async function getOrCreateKey(deps) {
9
+ const token = deps.config.get('token');
10
+ if (!token) {
11
+ console.log(chalk.red('Not logged in. Run: smart-aipi login'));
12
+ return null;
13
+ }
14
+ const { source } = await inquirer.prompt([{
15
+ type: 'list',
16
+ name: 'source',
17
+ message: 'API key source:',
18
+ choices: [
19
+ { name: 'Create a new key (recommended)', value: 'create' },
20
+ { name: 'Paste an existing key', value: 'paste' }
21
+ ]
22
+ }]);
23
+ if (source === 'paste') {
24
+ const { key } = await inquirer.prompt([{
25
+ type: 'input',
26
+ name: 'key',
27
+ message: 'API key:',
28
+ validate: (v) => v.startsWith('sk-') ? true : 'Key must start with sk-'
29
+ }]);
30
+ return key;
31
+ }
32
+ const spinner = ora('Creating API key...').start();
33
+ try {
34
+ const res = await fetch(`${deps.apiBase}/api/keys/create`, {
35
+ method: 'POST',
36
+ headers: {
37
+ 'Authorization': `Bearer ${token}`,
38
+ 'Content-Type': 'application/json'
39
+ },
40
+ body: JSON.stringify({ name: 'Auto-setup key' })
41
+ });
42
+ const data = await res.json();
43
+ if (!res.ok || !data.key) {
44
+ spinner.fail(chalk.red(data.error || 'Failed to create key'));
45
+ return null;
46
+ }
47
+ spinner.succeed(chalk.green('API key created'));
48
+ console.log(chalk.yellow(' Save this key — you won\'t see it again:'));
49
+ console.log(` ${chalk.bold.cyan(data.key)}\n`);
50
+ return data.key;
51
+ }
52
+ catch (err) {
53
+ spinner.fail(chalk.red(`Error: ${err.message}`));
54
+ return null;
55
+ }
56
+ }
57
+ function writeJSON(filePath, data) {
58
+ const dir = join(filePath, '..');
59
+ if (!existsSync(dir))
60
+ mkdirSync(dir, { recursive: true });
61
+ writeFileSync(filePath, JSON.stringify(data, null, 2) + '\n');
62
+ }
63
+ function readJSON(filePath) {
64
+ try {
65
+ return JSON.parse(readFileSync(filePath, 'utf-8'));
66
+ }
67
+ catch {
68
+ return {};
69
+ }
70
+ }
71
+ // ─── Claude Code ──────────────────────────────────────────────────────────────
72
+ async function setupClaude(deps) {
73
+ console.log(chalk.bold('\n Setting up Claude Code with Smart AIPI\n'));
74
+ const home = homedir();
75
+ const settingsPath = join(home, '.claude', 'settings.json');
76
+ const claudeJsonPath = join(home, '.claude.json');
77
+ // Check for existing Claude Code installation with OAuth
78
+ const claudeJson = readJSON(claudeJsonPath);
79
+ if (claudeJson.oauthAccount) {
80
+ console.log(chalk.yellow(' Warning: Claude Code is already set up with an Anthropic account.'));
81
+ console.log(chalk.yellow(' Writing Smart AIPI env vars to the global settings will override'));
82
+ console.log(chalk.yellow(' your existing Anthropic connection.\n'));
83
+ const { proceed } = await inquirer.prompt([{
84
+ type: 'list',
85
+ name: 'proceed',
86
+ message: 'How would you like to proceed?',
87
+ choices: [
88
+ { name: 'Show manual setup instructions (safe — no files changed)', value: 'manual' },
89
+ { name: 'Write to global settings anyway (replaces Anthropic connection)', value: 'write' },
90
+ { name: 'Cancel', value: 'cancel' }
91
+ ]
92
+ }]);
93
+ if (proceed === 'cancel')
94
+ return;
95
+ if (proceed === 'manual') {
96
+ const key = await getOrCreateKey(deps);
97
+ if (!key)
98
+ return;
99
+ console.log(chalk.cyan('\n To use Smart AIPI with Claude Code, add this to'));
100
+ console.log(chalk.cyan(' ~/.claude/settings.json (or a project-level .claude/settings.json):\n'));
101
+ console.log(chalk.dim(` {
102
+ "env": {
103
+ "ANTHROPIC_BASE_URL": "https://api.smartaipi.com",
104
+ "ANTHROPIC_API_KEY": "${key}",
105
+ "ANTHROPIC_DEFAULT_HAIKU_MODEL": "claude-haiku-4-5-20251001"
106
+ },
107
+ "model": "claude-opus-4-6"
108
+ }\n`));
109
+ console.log(chalk.dim(' Or export the env vars in your shell:\n'));
110
+ console.log(chalk.dim(` export ANTHROPIC_BASE_URL=https://api.smartaipi.com`));
111
+ console.log(chalk.dim(` export ANTHROPIC_API_KEY=${key}\n`));
112
+ console.log(chalk.dim(' Tip: Use a project-level .claude/settings.json to keep your'));
113
+ console.log(chalk.dim(' global Anthropic connection intact.\n'));
114
+ return;
115
+ }
116
+ // proceed === 'write' — fall through to write the settings
117
+ }
118
+ const key = await getOrCreateKey(deps);
119
+ if (!key)
120
+ return;
121
+ // Write ~/.claude/settings.json
122
+ const settings = readJSON(settingsPath);
123
+ settings.env = {
124
+ ...(settings.env || {}),
125
+ ANTHROPIC_BASE_URL: 'https://api.smartaipi.com',
126
+ ANTHROPIC_API_KEY: key,
127
+ ANTHROPIC_DEFAULT_HAIKU_MODEL: 'claude-haiku-4-5-20251001'
128
+ };
129
+ if (!settings.model)
130
+ settings.model = 'claude-opus-4-6';
131
+ writeJSON(settingsPath, settings);
132
+ // Patch ~/.claude.json to skip setup wizard
133
+ claudeJson.hasCompletedOnboarding = true;
134
+ writeJSON(claudeJsonPath, claudeJson);
135
+ console.log(chalk.green(' Claude Code configured!\n'));
136
+ console.log(chalk.dim(' Written:'));
137
+ console.log(chalk.dim(` ${settingsPath}`));
138
+ console.log(chalk.dim(` ${claudeJsonPath}\n`));
139
+ console.log(chalk.dim(' Model: claude-opus-4-6 (high reasoning)'));
140
+ console.log(chalk.dim(' Haiku: claude-haiku-4-5-20251001 (background tasks)'));
141
+ console.log(chalk.dim(' Backend: gpt-5.3-codex (transparent via x-actual-model header)\n'));
142
+ console.log(` Run ${chalk.cyan('claude')} to start.\n`);
143
+ }
144
+ // ─── Codex CLI ────────────────────────────────────────────────────────────────
145
+ async function setupCodex(deps) {
146
+ console.log(chalk.bold('\n Setting up Codex CLI with Smart AIPI\n'));
147
+ const key = await getOrCreateKey(deps);
148
+ if (!key)
149
+ return;
150
+ const home = homedir();
151
+ const configDir = join(home, '.codex');
152
+ const configPath = join(configDir, 'config.toml');
153
+ if (!existsSync(configDir))
154
+ mkdirSync(configDir, { recursive: true });
155
+ // Write TOML config
156
+ const toml = `# Smart AIPI configuration
157
+ model = "gpt-5.3-codex"
158
+
159
+ [api]
160
+ base_url = "https://api.smartaipi.com/v1"
161
+ api_key = "${key}"
162
+ `;
163
+ writeFileSync(configPath, toml);
164
+ console.log(chalk.green(' Codex CLI configured!\n'));
165
+ console.log(chalk.dim(` Written: ${configPath}\n`));
166
+ console.log(chalk.dim(' Model: gpt-5.3-codex'));
167
+ console.log(chalk.dim(' Base URL: https://api.smartaipi.com/v1\n'));
168
+ console.log(` Run ${chalk.cyan('codex "your prompt"')} to start.\n`);
169
+ }
170
+ // ─── OpenCode ─────────────────────────────────────────────────────────────────
171
+ async function setupOpenCode(deps) {
172
+ console.log(chalk.bold('\n Setting up OpenCode with Smart AIPI\n'));
173
+ const key = await getOrCreateKey(deps);
174
+ if (!key)
175
+ return;
176
+ const home = homedir();
177
+ const configDir = join(home, '.config', 'opencode');
178
+ const configPath = join(configDir, 'config.json');
179
+ const config = readJSON(configPath);
180
+ config.provider = {
181
+ ...(config.provider || {}),
182
+ openai: {
183
+ options: {
184
+ baseURL: 'https://api.smartaipi.com/v1',
185
+ apiKey: key
186
+ },
187
+ models: {
188
+ 'gpt-5.3-codex': {}
189
+ }
190
+ }
191
+ };
192
+ config.model = 'openai/gpt-5.3-codex';
193
+ writeJSON(configPath, config);
194
+ console.log(chalk.green(' OpenCode configured!\n'));
195
+ console.log(chalk.dim(` Written: ${configPath}\n`));
196
+ console.log(chalk.dim(' Model: openai/gpt-5.3-codex'));
197
+ console.log(chalk.dim(' Base URL: https://api.smartaipi.com/v1\n'));
198
+ console.log(` Run ${chalk.cyan('opencode')} to start.\n`);
199
+ }
200
+ // ─── Aider ────────────────────────────────────────────────────────────────────
201
+ async function setupAider(deps) {
202
+ console.log(chalk.bold('\n Setting up Aider with Smart AIPI\n'));
203
+ const key = await getOrCreateKey(deps);
204
+ if (!key)
205
+ return;
206
+ const home = homedir();
207
+ const configPath = join(home, '.aider.conf.yml');
208
+ const yaml = `# Smart AIPI configuration
209
+ openai-api-base: https://api.smartaipi.com/v1
210
+ openai-api-key: ${key}
211
+ model: openai/gpt-5.3-codex
212
+ `;
213
+ writeFileSync(configPath, yaml);
214
+ console.log(chalk.green(' Aider configured!\n'));
215
+ console.log(chalk.dim(` Written: ${configPath}\n`));
216
+ console.log(chalk.dim(' Model: openai/gpt-5.3-codex'));
217
+ console.log(chalk.dim(' Base URL: https://api.smartaipi.com/v1\n'));
218
+ console.log(` Run ${chalk.cyan('aider')} to start.\n`);
219
+ }
220
+ // ─── Cursor (GUI — instructions only) ────────────────────────────────────────
221
+ async function setupCursor(deps) {
222
+ console.log(chalk.bold('\n Setting up Cursor with Smart AIPI\n'));
223
+ const key = await getOrCreateKey(deps);
224
+ if (!key)
225
+ return;
226
+ console.log(chalk.cyan(' Open Cursor and follow these steps:\n'));
227
+ console.log(' 1. Open Settings (Cmd+, or Ctrl+,)');
228
+ console.log(' 2. Go to "Models"');
229
+ console.log(` 3. Enter your API key under "OpenAI API Key":`);
230
+ console.log(` ${chalk.green(key)}`);
231
+ console.log(' 4. Click "Override OpenAI Base URL"');
232
+ console.log(` 5. Enter: ${chalk.green('https://api.smartaipi.com/v1')}`);
233
+ console.log(` 6. Select model: ${chalk.green('gpt-5.3-codex')}\n`);
234
+ }
235
+ // ─── Cline (VS Code extension — instructions only) ───────────────────────────
236
+ async function setupCline(deps) {
237
+ console.log(chalk.bold('\n Setting up Cline with Smart AIPI\n'));
238
+ const key = await getOrCreateKey(deps);
239
+ if (!key)
240
+ return;
241
+ console.log(chalk.cyan(' In Cline extension settings, configure:\n'));
242
+ console.log(` API Provider: ${chalk.green('OpenAI Compatible')}`);
243
+ console.log(` Base URL: ${chalk.green('https://api.smartaipi.com/v1')}`);
244
+ console.log(` API Key: ${chalk.green(key)}`);
245
+ console.log(` Model: ${chalk.green('gpt-5.3-codex')}\n`);
246
+ }
247
+ // ─── Windsurf (GUI — instructions only) ──────────────────────────────────────
248
+ async function setupWindsurf(deps) {
249
+ console.log(chalk.bold('\n Setting up Windsurf with Smart AIPI\n'));
250
+ const key = await getOrCreateKey(deps);
251
+ if (!key)
252
+ return;
253
+ console.log(chalk.cyan(' In Windsurf Settings → AI Provider:\n'));
254
+ console.log(` Provider: ${chalk.green('OpenAI Compatible')}`);
255
+ console.log(` Base URL: ${chalk.green('https://api.smartaipi.com/v1')}`);
256
+ console.log(` API Key: ${chalk.green(key)}`);
257
+ console.log(` Model: ${chalk.green('gpt-5.3-codex')}\n`);
258
+ }
259
+ // ─── Register all setup commands ──────────────────────────────────────────────
260
+ const AGENTS = {
261
+ claude: { desc: 'Set up Claude Code to use Smart AIPI', handler: setupClaude },
262
+ codex: { desc: 'Set up Codex CLI to use Smart AIPI', handler: setupCodex },
263
+ opencode: { desc: 'Set up OpenCode to use Smart AIPI', handler: setupOpenCode },
264
+ aider: { desc: 'Set up Aider to use Smart AIPI', handler: setupAider },
265
+ cursor: { desc: 'Set up Cursor to use Smart AIPI', handler: setupCursor },
266
+ cline: { desc: 'Set up Cline to use Smart AIPI', handler: setupCline },
267
+ windsurf: { desc: 'Set up Windsurf to use Smart AIPI', handler: setupWindsurf },
268
+ };
269
+ export function registerSetupCommands(program, deps) {
270
+ for (const [name, { desc, handler }] of Object.entries(AGENTS)) {
271
+ program
272
+ .command(name)
273
+ .description(desc)
274
+ .option('-k, --key <key>', 'Use an existing API key instead of creating one')
275
+ .action(async (options) => {
276
+ if (options.key) {
277
+ // Bypass the interactive key prompt — inject the key
278
+ const wrappedDeps = {
279
+ ...deps,
280
+ // We pass the key via a custom property; handler uses getOrCreateKey
281
+ };
282
+ // For --key flag, we override getOrCreateKey by calling the handler
283
+ // with an env that pre-sets the key. Simpler: just call the handler
284
+ // after setting the key in a closure.
285
+ await setupWithKey(options.key, name, deps);
286
+ return;
287
+ }
288
+ await handler(deps);
289
+ });
290
+ }
291
+ // Also register a top-level "setup" command that lists available agents
292
+ program
293
+ .command('setup [agent]')
294
+ .description('Set up an AI agent to use Smart AIPI')
295
+ .action((agent) => {
296
+ if (agent && AGENTS[agent]) {
297
+ AGENTS[agent].handler(deps);
298
+ return;
299
+ }
300
+ console.log(chalk.bold('\n Quick Setup — one command to configure any AI agent\n'));
301
+ console.log(chalk.cyan(' Auto-configure (writes config files):'));
302
+ console.log(` ${chalk.green('smart-aipi claude')} Claude Code`);
303
+ console.log(` ${chalk.green('smart-aipi codex')} Codex CLI`);
304
+ console.log(` ${chalk.green('smart-aipi opencode')} OpenCode`);
305
+ console.log(` ${chalk.green('smart-aipi aider')} Aider`);
306
+ console.log();
307
+ console.log(chalk.cyan(' Guided setup (shows instructions + creates key):'));
308
+ console.log(` ${chalk.green('smart-aipi cursor')} Cursor IDE`);
309
+ console.log(` ${chalk.green('smart-aipi cline')} Cline (VS Code)`);
310
+ console.log(` ${chalk.green('smart-aipi windsurf')} Windsurf`);
311
+ console.log();
312
+ console.log(chalk.dim(' Each command creates an API key and configures the tool.\n'));
313
+ console.log(chalk.dim(' Use --key <key> to use an existing key instead.\n'));
314
+ });
315
+ }
316
+ // Handler for --key flag (skips interactive key prompt)
317
+ async function setupWithKey(key, agent, deps) {
318
+ // Temporarily monkey-patch nothing — we just call the specific setup
319
+ // function directly with the key injected. To avoid refactoring,
320
+ // we'll use a simpler approach per agent.
321
+ const home = homedir();
322
+ switch (agent) {
323
+ case 'claude': {
324
+ const settingsPath = join(home, '.claude', 'settings.json');
325
+ const claudeJsonPath = join(home, '.claude.json');
326
+ const settings = readJSON(settingsPath);
327
+ settings.env = {
328
+ ...(settings.env || {}),
329
+ ANTHROPIC_BASE_URL: 'https://api.smartaipi.com',
330
+ ANTHROPIC_API_KEY: key,
331
+ ANTHROPIC_DEFAULT_HAIKU_MODEL: 'claude-haiku-4-5-20251001'
332
+ };
333
+ if (!settings.model)
334
+ settings.model = 'claude-opus-4-6';
335
+ writeJSON(settingsPath, settings);
336
+ const claudeJson = readJSON(claudeJsonPath);
337
+ claudeJson.hasCompletedOnboarding = true;
338
+ writeJSON(claudeJsonPath, claudeJson);
339
+ console.log(chalk.green('\n Claude Code configured!\n'));
340
+ console.log(chalk.dim(` Written: ${settingsPath}`));
341
+ console.log(chalk.dim(` Written: ${claudeJsonPath}\n`));
342
+ console.log(` Run ${chalk.cyan('claude')} to start.\n`);
343
+ break;
344
+ }
345
+ case 'codex': {
346
+ const configDir = join(home, '.codex');
347
+ const configPath = join(configDir, 'config.toml');
348
+ if (!existsSync(configDir))
349
+ mkdirSync(configDir, { recursive: true });
350
+ writeFileSync(configPath, `model = "gpt-5.3-codex"\n\n[api]\nbase_url = "https://api.smartaipi.com/v1"\napi_key = "${key}"\n`);
351
+ console.log(chalk.green('\n Codex CLI configured!\n'));
352
+ console.log(chalk.dim(` Written: ${configPath}\n`));
353
+ console.log(` Run ${chalk.cyan('codex "your prompt"')} to start.\n`);
354
+ break;
355
+ }
356
+ case 'opencode': {
357
+ const configDir = join(home, '.config', 'opencode');
358
+ const configPath = join(configDir, 'config.json');
359
+ const config = readJSON(configPath);
360
+ config.provider = { ...(config.provider || {}), openai: { options: { baseURL: 'https://api.smartaipi.com/v1', apiKey: key }, models: { 'gpt-5.3-codex': {} } } };
361
+ config.model = 'openai/gpt-5.3-codex';
362
+ writeJSON(configPath, config);
363
+ console.log(chalk.green('\n OpenCode configured!\n'));
364
+ console.log(chalk.dim(` Written: ${configPath}\n`));
365
+ console.log(` Run ${chalk.cyan('opencode')} to start.\n`);
366
+ break;
367
+ }
368
+ case 'aider': {
369
+ const configPath = join(home, '.aider.conf.yml');
370
+ writeFileSync(configPath, `openai-api-base: https://api.smartaipi.com/v1\nopenai-api-key: ${key}\nmodel: openai/gpt-5.3-codex\n`);
371
+ console.log(chalk.green('\n Aider configured!\n'));
372
+ console.log(chalk.dim(` Written: ${configPath}\n`));
373
+ console.log(` Run ${chalk.cyan('aider')} to start.\n`);
374
+ break;
375
+ }
376
+ case 'cursor':
377
+ case 'cline':
378
+ case 'windsurf': {
379
+ // GUI tools — just show instructions with the provided key
380
+ console.log(chalk.bold(`\n ${agent.charAt(0).toUpperCase() + agent.slice(1)} Setup\n`));
381
+ console.log(` Base URL: ${chalk.green('https://api.smartaipi.com/v1')}`);
382
+ console.log(` API Key: ${chalk.green(key)}`);
383
+ console.log(` Model: ${chalk.green('gpt-5.3-codex')}\n`);
384
+ break;
385
+ }
386
+ }
387
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "smart-aipi",
3
- "version": "1.3.0",
3
+ "version": "1.4.1",
4
4
  "description": "CLI for Smart AIPI - OpenAI-compatible API gateway",
5
5
  "type": "module",
6
6
  "files": [
@@ -13,16 +13,21 @@
13
13
  },
14
14
  "scripts": {
15
15
  "build": "tsc -p tsconfig.json",
16
- "test:runtime": "node --check bin/cli.js && node --check lib/login.js && node --check lib/docs.js",
16
+ "test:runtime": "node --check bin/cli.js && node --check lib/login.js && node --check lib/docs.js && node --check lib/setup.js",
17
17
  "test": "npm run build && npm run test:runtime"
18
18
  },
19
19
  "keywords": [
20
20
  "openai",
21
+ "anthropic",
21
22
  "api",
22
23
  "cli",
23
24
  "ai",
24
25
  "gpt",
25
- "chatgpt"
26
+ "claude",
27
+ "codex",
28
+ "cursor",
29
+ "aider",
30
+ "cline"
26
31
  ],
27
32
  "author": "",
28
33
  "license": "MIT",