codemaxxing 1.0.0 → 1.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/README.md +18 -12
- package/dist/agent.d.ts +4 -0
- package/dist/agent.js +91 -16
- package/dist/commands/git.d.ts +2 -0
- package/dist/commands/git.js +50 -0
- package/dist/commands/ollama.d.ts +27 -0
- package/dist/commands/ollama.js +171 -0
- package/dist/commands/output.d.ts +2 -0
- package/dist/commands/output.js +18 -0
- package/dist/commands/registry.d.ts +2 -0
- package/dist/commands/registry.js +8 -0
- package/dist/commands/skills.d.ts +18 -0
- package/dist/commands/skills.js +121 -0
- package/dist/commands/types.d.ts +5 -0
- package/dist/commands/types.js +1 -0
- package/dist/commands/ui.d.ts +16 -0
- package/dist/commands/ui.js +79 -0
- package/dist/config.d.ts +9 -0
- package/dist/config.js +13 -3
- package/dist/exec.js +4 -1
- package/dist/index.js +75 -401
- package/dist/tools/files.js +58 -3
- package/dist/utils/context.js +6 -0
- package/dist/utils/mcp.d.ts +7 -2
- package/dist/utils/mcp.js +34 -6
- package/package.json +8 -5
- package/src/agent.ts +0 -894
- package/src/auth-cli.ts +0 -287
- package/src/cli.ts +0 -37
- package/src/config.ts +0 -352
- package/src/exec.ts +0 -183
- package/src/index.tsx +0 -2647
- package/src/skills/registry.ts +0 -1436
- package/src/themes.ts +0 -335
- package/src/tools/files.ts +0 -374
- package/src/utils/auth.ts +0 -606
- package/src/utils/context.ts +0 -174
- package/src/utils/git.ts +0 -117
- package/src/utils/hardware.ts +0 -131
- package/src/utils/lint.ts +0 -116
- package/src/utils/mcp.ts +0 -307
- package/src/utils/models.ts +0 -218
- package/src/utils/ollama.ts +0 -352
- package/src/utils/repomap.ts +0 -220
- package/src/utils/sessions.ts +0 -254
- package/src/utils/skills.ts +0 -241
- package/tsconfig.json +0 -16
package/src/auth-cli.ts
DELETED
|
@@ -1,287 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* codemaxxing auth CLI
|
|
5
|
-
*
|
|
6
|
-
* Commands:
|
|
7
|
-
* codemaxxing login — Interactive auth setup
|
|
8
|
-
* codemaxxing auth list — List saved credentials
|
|
9
|
-
* codemaxxing auth remove <name> — Remove a credential
|
|
10
|
-
* codemaxxing auth openrouter — Start OpenRouter OAuth
|
|
11
|
-
* codemaxxing auth anthropic — Get Anthropic via Claude Code
|
|
12
|
-
* codemaxxing auth openai — Import Codex CLI credentials
|
|
13
|
-
* codemaxxing auth qwen — Import Qwen CLI credentials
|
|
14
|
-
* codemaxxing auth copilot — GitHub Copilot device flow
|
|
15
|
-
* codemaxxing auth api-key <name> <key> — Save API key directly
|
|
16
|
-
*/
|
|
17
|
-
|
|
18
|
-
import {
|
|
19
|
-
PROVIDERS,
|
|
20
|
-
getCredentials,
|
|
21
|
-
removeCredential,
|
|
22
|
-
openRouterOAuth,
|
|
23
|
-
anthropicSetupToken,
|
|
24
|
-
importCodexToken,
|
|
25
|
-
importQwenToken,
|
|
26
|
-
copilotDeviceFlow,
|
|
27
|
-
saveApiKey,
|
|
28
|
-
detectAvailableAuth,
|
|
29
|
-
} from "./utils/auth.js";
|
|
30
|
-
|
|
31
|
-
export async function main() {
|
|
32
|
-
const command = process.argv[2] ?? "login";
|
|
33
|
-
|
|
34
|
-
switch (command) {
|
|
35
|
-
case "login": {
|
|
36
|
-
console.log("\n💪 Codemaxxing Authentication\n");
|
|
37
|
-
console.log("Available providers:\n");
|
|
38
|
-
|
|
39
|
-
PROVIDERS.forEach((p, i) => {
|
|
40
|
-
const methods = p.methods.filter((m) => m !== "none").join(", ");
|
|
41
|
-
console.log(` ${i + 1}. ${p.name}`);
|
|
42
|
-
console.log(` ${p.description}`);
|
|
43
|
-
console.log(` Methods: ${methods}\n`);
|
|
44
|
-
});
|
|
45
|
-
|
|
46
|
-
console.log("Detected on this machine:");
|
|
47
|
-
const detected = detectAvailableAuth();
|
|
48
|
-
if (detected.length === 0) {
|
|
49
|
-
console.log(" — No existing CLI credentials found\n");
|
|
50
|
-
} else {
|
|
51
|
-
detected.forEach((d) => {
|
|
52
|
-
console.log(` ⚡ ${d.provider} — ${d.description}`);
|
|
53
|
-
});
|
|
54
|
-
console.log("");
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
const readline = await import("readline");
|
|
58
|
-
const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
|
|
59
|
-
|
|
60
|
-
const ask = (q: string): Promise<string> =>
|
|
61
|
-
new Promise((resolve) => rl.question(q, resolve));
|
|
62
|
-
|
|
63
|
-
const choice = await ask("Select a provider (1-" + PROVIDERS.length + ") or name: ");
|
|
64
|
-
const providerId = PROVIDERS[parseInt(choice) - 1]?.id ?? choice.toLowerCase();
|
|
65
|
-
const provider = PROVIDERS.find((p) => p.id === providerId);
|
|
66
|
-
|
|
67
|
-
if (!provider) {
|
|
68
|
-
console.log(`Unknown provider: ${providerId}`);
|
|
69
|
-
process.exit(1);
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
console.log(`\nSetting up ${provider.name}...\n`);
|
|
73
|
-
|
|
74
|
-
try {
|
|
75
|
-
if (providerId === "openrouter") {
|
|
76
|
-
const cred = await openRouterOAuth((msg) => console.log(` ${msg}`));
|
|
77
|
-
console.log(`\n✅ OpenRouter authenticated! (${cred.label})`);
|
|
78
|
-
} else if (providerId === "anthropic") {
|
|
79
|
-
if (provider.methods.includes("setup-token")) {
|
|
80
|
-
const cred = await anthropicSetupToken((msg) => console.log(` ${msg}`));
|
|
81
|
-
console.log(`\n✅ Anthropic authenticated! (${cred.label})`);
|
|
82
|
-
} else {
|
|
83
|
-
const apiKey = await ask("Enter your Anthropic API key: ");
|
|
84
|
-
const cred = saveApiKey(providerId, apiKey);
|
|
85
|
-
console.log(`\n✅ Saved API key for ${provider.name}`);
|
|
86
|
-
}
|
|
87
|
-
} else if (providerId === "openai") {
|
|
88
|
-
const imported = importCodexToken((msg) => console.log(` ${msg}`));
|
|
89
|
-
if (imported) {
|
|
90
|
-
console.log(`\n✅ Imported Codex credentials! (${imported.label})`);
|
|
91
|
-
} else {
|
|
92
|
-
const apiKey = await ask("Enter your OpenAI API key: ");
|
|
93
|
-
const cred = saveApiKey(providerId, apiKey);
|
|
94
|
-
console.log(`\n✅ Saved API key for ${provider.name}`);
|
|
95
|
-
}
|
|
96
|
-
} else if (providerId === "qwen") {
|
|
97
|
-
const imported = importQwenToken((msg) => console.log(` ${msg}`));
|
|
98
|
-
if (imported) {
|
|
99
|
-
console.log(`\n✅ Imported Qwen credentials! (${imported.label})`);
|
|
100
|
-
} else {
|
|
101
|
-
const apiKey = await ask("Enter your Qwen/DashScope API key: ");
|
|
102
|
-
const cred = saveApiKey(providerId, apiKey);
|
|
103
|
-
console.log(`\n✅ Saved API key for ${provider.name}`);
|
|
104
|
-
}
|
|
105
|
-
} else if (providerId === "copilot") {
|
|
106
|
-
const cred = await copilotDeviceFlow((msg) => console.log(` ${msg}`));
|
|
107
|
-
console.log(`\n✅ GitHub Copilot authenticated!`);
|
|
108
|
-
} else if (providerId === "custom") {
|
|
109
|
-
const baseUrl = await ask("Enter the base URL: ");
|
|
110
|
-
const apiKey = await ask("Enter your API key: ");
|
|
111
|
-
const cred = saveApiKey(providerId, apiKey, baseUrl, "Custom provider");
|
|
112
|
-
console.log(`\n✅ Saved custom provider`);
|
|
113
|
-
} else {
|
|
114
|
-
const apiKey = await ask(`Enter your ${provider.name} API key (${provider.consoleUrl}): `);
|
|
115
|
-
const cred = saveApiKey(providerId, apiKey);
|
|
116
|
-
console.log(`\n✅ Saved API key for ${provider.name}`);
|
|
117
|
-
}
|
|
118
|
-
|
|
119
|
-
console.log("\nRun 'codemaxxing' to start coding!");
|
|
120
|
-
} catch (err: any) {
|
|
121
|
-
console.error(`\n❌ Error: ${err.message}`);
|
|
122
|
-
process.exit(1);
|
|
123
|
-
} finally {
|
|
124
|
-
rl.close();
|
|
125
|
-
}
|
|
126
|
-
break;
|
|
127
|
-
}
|
|
128
|
-
|
|
129
|
-
case "list":
|
|
130
|
-
case "ls": {
|
|
131
|
-
const creds = getCredentials();
|
|
132
|
-
console.log("\n💪 Saved Credentials\n");
|
|
133
|
-
|
|
134
|
-
if (creds.length === 0) {
|
|
135
|
-
console.log(" No credentials saved.\n");
|
|
136
|
-
console.log(" Run 'codemaxxing login' to set up authentication.\n");
|
|
137
|
-
break;
|
|
138
|
-
}
|
|
139
|
-
|
|
140
|
-
creds.forEach((c) => {
|
|
141
|
-
console.log(` ${c.provider}`);
|
|
142
|
-
console.log(` Method: ${c.method}`);
|
|
143
|
-
console.log(` Label: ${c.label ?? "—"}`);
|
|
144
|
-
const maskedKey = c.apiKey.length > 12
|
|
145
|
-
? c.apiKey.slice(0, 4) + "•".repeat(8) + c.apiKey.slice(-4)
|
|
146
|
-
: "••••••••";
|
|
147
|
-
console.log(` Key: ${maskedKey}`);
|
|
148
|
-
console.log(` Base: ${c.baseUrl}`);
|
|
149
|
-
console.log("");
|
|
150
|
-
});
|
|
151
|
-
break;
|
|
152
|
-
}
|
|
153
|
-
|
|
154
|
-
case "remove":
|
|
155
|
-
case "rm":
|
|
156
|
-
case "delete": {
|
|
157
|
-
const target = process.argv[3];
|
|
158
|
-
if (!target) {
|
|
159
|
-
console.log("Usage: codemaxxing auth remove <provider-name>");
|
|
160
|
-
console.log("\nSaved providers:");
|
|
161
|
-
getCredentials().forEach((c) => console.log(` ${c.provider}`));
|
|
162
|
-
process.exit(1);
|
|
163
|
-
}
|
|
164
|
-
|
|
165
|
-
const removed = removeCredential(target);
|
|
166
|
-
if (removed) {
|
|
167
|
-
console.log(`✅ Removed ${target}`);
|
|
168
|
-
} else {
|
|
169
|
-
console.log(`❌ No credential found for: ${target}`);
|
|
170
|
-
console.log("\nSaved providers:");
|
|
171
|
-
getCredentials().forEach((c) => console.log(` ${c.provider}`));
|
|
172
|
-
process.exit(1);
|
|
173
|
-
}
|
|
174
|
-
break;
|
|
175
|
-
}
|
|
176
|
-
|
|
177
|
-
case "openrouter": {
|
|
178
|
-
console.log("Starting OpenRouter OAuth flow...\n");
|
|
179
|
-
try {
|
|
180
|
-
const cred = await openRouterOAuth((msg) => console.log(` ${msg}`));
|
|
181
|
-
console.log(`\n✅ OpenRouter authenticated!`);
|
|
182
|
-
} catch (err: any) {
|
|
183
|
-
console.error(`\n❌ ${err.message}`);
|
|
184
|
-
process.exit(1);
|
|
185
|
-
}
|
|
186
|
-
break;
|
|
187
|
-
}
|
|
188
|
-
|
|
189
|
-
case "anthropic": {
|
|
190
|
-
console.log("Starting Anthropic setup-token flow...\n");
|
|
191
|
-
try {
|
|
192
|
-
const cred = await anthropicSetupToken((msg) => console.log(` ${msg}`));
|
|
193
|
-
console.log(`\n✅ Anthropic authenticated!`);
|
|
194
|
-
} catch (err: any) {
|
|
195
|
-
console.error(`\n❌ ${err.message}`);
|
|
196
|
-
process.exit(1);
|
|
197
|
-
}
|
|
198
|
-
break;
|
|
199
|
-
}
|
|
200
|
-
|
|
201
|
-
case "openai": {
|
|
202
|
-
console.log("Checking for Codex CLI credentials...\n");
|
|
203
|
-
const imported = importCodexToken((msg) => console.log(` ${msg}`));
|
|
204
|
-
if (imported) {
|
|
205
|
-
console.log(`\n✅ Imported Codex credentials!`);
|
|
206
|
-
} else {
|
|
207
|
-
console.log("\n❌ No Codex CLI credentials found.");
|
|
208
|
-
console.log("Make sure Codex CLI is installed and you've logged in.");
|
|
209
|
-
}
|
|
210
|
-
break;
|
|
211
|
-
}
|
|
212
|
-
|
|
213
|
-
case "qwen": {
|
|
214
|
-
console.log("Checking for Qwen CLI credentials...\n");
|
|
215
|
-
const imported = importQwenToken((msg) => console.log(` ${msg}`));
|
|
216
|
-
if (imported) {
|
|
217
|
-
console.log(`\n✅ Imported Qwen credentials!`);
|
|
218
|
-
} else {
|
|
219
|
-
console.log("\n❌ No Qwen CLI credentials found.");
|
|
220
|
-
console.log("Make sure Qwen CLI is installed and you've logged in.");
|
|
221
|
-
}
|
|
222
|
-
break;
|
|
223
|
-
}
|
|
224
|
-
|
|
225
|
-
case "copilot": {
|
|
226
|
-
console.log("Starting GitHub Copilot device flow...\n");
|
|
227
|
-
try {
|
|
228
|
-
const cred = await copilotDeviceFlow((msg) => console.log(` ${msg}`));
|
|
229
|
-
console.log(`\n✅ GitHub Copilot authenticated!`);
|
|
230
|
-
} catch (err: any) {
|
|
231
|
-
console.error(`\n❌ ${err.message}`);
|
|
232
|
-
process.exit(1);
|
|
233
|
-
}
|
|
234
|
-
break;
|
|
235
|
-
}
|
|
236
|
-
|
|
237
|
-
case "api-key": {
|
|
238
|
-
const providerId = process.argv[3];
|
|
239
|
-
const apiKey = process.argv[4];
|
|
240
|
-
if (!providerId || !apiKey) {
|
|
241
|
-
console.log("Usage: codemaxxing auth api-key <provider-id> <api-key>");
|
|
242
|
-
process.exit(1);
|
|
243
|
-
}
|
|
244
|
-
const cred = saveApiKey(providerId, apiKey);
|
|
245
|
-
console.log(`\n✅ Saved API key for ${cred.provider}`);
|
|
246
|
-
break;
|
|
247
|
-
}
|
|
248
|
-
|
|
249
|
-
case "help":
|
|
250
|
-
case "--help":
|
|
251
|
-
case "-h": {
|
|
252
|
-
console.log(`
|
|
253
|
-
💪 Codemaxxing Auth
|
|
254
|
-
|
|
255
|
-
Commands:
|
|
256
|
-
codemaxxing login Interactive authentication setup
|
|
257
|
-
codemaxxing auth list List saved credentials
|
|
258
|
-
codemaxxing auth remove <name> Remove a credential
|
|
259
|
-
codemaxxing auth openrouter Start OpenRouter OAuth flow
|
|
260
|
-
codemaxxing auth anthropic Get Anthropic via Claude Code CLI
|
|
261
|
-
codemaxxing auth openai Import Codex CLI credentials
|
|
262
|
-
codemaxxing auth qwen Import Qwen CLI credentials
|
|
263
|
-
codemaxxing auth copilot GitHub Copilot device flow
|
|
264
|
-
codemaxxing auth api-key <id> <key> Save API key directly
|
|
265
|
-
codemaxxing auth help Show this help
|
|
266
|
-
|
|
267
|
-
Examples:
|
|
268
|
-
codemaxxing login # Interactive provider picker
|
|
269
|
-
codemaxxing auth openrouter # One browser login, access to 200+ models
|
|
270
|
-
codemaxxing auth anthropic # Use your Claude subscription via Claude Code
|
|
271
|
-
codemaxxing auth list # See what's saved
|
|
272
|
-
`);
|
|
273
|
-
break;
|
|
274
|
-
}
|
|
275
|
-
|
|
276
|
-
default:
|
|
277
|
-
console.log(`Unknown command: ${command}`);
|
|
278
|
-
console.log("Run 'codemaxxing auth help' for available commands.");
|
|
279
|
-
process.exit(1);
|
|
280
|
-
}
|
|
281
|
-
}
|
|
282
|
-
|
|
283
|
-
// Always run main — this module is either imported and main() called, or run directly
|
|
284
|
-
main().catch((err) => {
|
|
285
|
-
console.error(`Error: ${err.message}`);
|
|
286
|
-
process.exit(1);
|
|
287
|
-
});
|
package/src/cli.ts
DELETED
|
@@ -1,37 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* Codemaxxing CLI entry point
|
|
5
|
-
* Routes subcommands (login, auth, exec) to handlers, everything else to the TUI
|
|
6
|
-
*/
|
|
7
|
-
|
|
8
|
-
import { spawn } from "node:child_process";
|
|
9
|
-
import { fileURLToPath } from "node:url";
|
|
10
|
-
import { dirname, join } from "node:path";
|
|
11
|
-
|
|
12
|
-
const __filename = fileURLToPath(import.meta.url);
|
|
13
|
-
const __dirname = dirname(__filename);
|
|
14
|
-
|
|
15
|
-
const subcmd = process.argv[2];
|
|
16
|
-
|
|
17
|
-
if (subcmd === "login" || subcmd === "auth") {
|
|
18
|
-
// Route to auth CLI (spawn is fine here — no TUI/raw mode needed)
|
|
19
|
-
const authScript = join(__dirname, "auth-cli.js");
|
|
20
|
-
const args = subcmd === "login"
|
|
21
|
-
? [authScript, "login", ...process.argv.slice(3)]
|
|
22
|
-
: [authScript, ...process.argv.slice(3)];
|
|
23
|
-
|
|
24
|
-
const child = spawn(process.execPath, args, {
|
|
25
|
-
stdio: "inherit",
|
|
26
|
-
cwd: process.cwd(),
|
|
27
|
-
});
|
|
28
|
-
|
|
29
|
-
child.on("exit", (code) => process.exit(code ?? 0));
|
|
30
|
-
} else if (subcmd === "exec") {
|
|
31
|
-
// Headless/CI mode — no TUI
|
|
32
|
-
const { runExec } = await import("./exec.js");
|
|
33
|
-
await runExec(process.argv.slice(3));
|
|
34
|
-
} else {
|
|
35
|
-
// TUI mode — import directly (not spawn) to preserve raw stdin
|
|
36
|
-
await import("./index.js");
|
|
37
|
-
}
|
package/src/config.ts
DELETED
|
@@ -1,352 +0,0 @@
|
|
|
1
|
-
import { readFileSync, existsSync, mkdirSync, writeFileSync } from "fs";
|
|
2
|
-
import { homedir } from "os";
|
|
3
|
-
import { join } from "path";
|
|
4
|
-
import { getCredential, type AuthCredential } from "./utils/auth.js";
|
|
5
|
-
|
|
6
|
-
export interface ProviderConfig {
|
|
7
|
-
baseUrl: string;
|
|
8
|
-
apiKey: string;
|
|
9
|
-
model: string;
|
|
10
|
-
type?: "openai" | "anthropic";
|
|
11
|
-
}
|
|
12
|
-
|
|
13
|
-
export interface ProviderProfile extends ProviderConfig {
|
|
14
|
-
name: string;
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
export interface CodemaxxingConfig {
|
|
18
|
-
provider: ProviderConfig;
|
|
19
|
-
providers?: Record<string, ProviderProfile>;
|
|
20
|
-
defaults: {
|
|
21
|
-
autoApprove: boolean;
|
|
22
|
-
contextFiles: number;
|
|
23
|
-
maxTokens: number;
|
|
24
|
-
contextCompressionThreshold?: number;
|
|
25
|
-
architectModel?: string;
|
|
26
|
-
autoLint?: boolean;
|
|
27
|
-
stopOllamaOnExit?: boolean;
|
|
28
|
-
};
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
export interface CLIArgs {
|
|
32
|
-
model?: string;
|
|
33
|
-
provider?: string;
|
|
34
|
-
apiKey?: string;
|
|
35
|
-
baseUrl?: string;
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
const CONFIG_DIR = join(homedir(), ".codemaxxing");
|
|
39
|
-
const CONFIG_FILE = join(CONFIG_DIR, "settings.json");
|
|
40
|
-
|
|
41
|
-
const DEFAULT_CONFIG: CodemaxxingConfig = {
|
|
42
|
-
provider: {
|
|
43
|
-
baseUrl: "http://localhost:1234/v1",
|
|
44
|
-
apiKey: "not-needed",
|
|
45
|
-
model: "auto",
|
|
46
|
-
},
|
|
47
|
-
providers: {
|
|
48
|
-
local: {
|
|
49
|
-
name: "Local (LM Studio/Ollama)",
|
|
50
|
-
baseUrl: "http://localhost:1234/v1",
|
|
51
|
-
apiKey: "not-needed",
|
|
52
|
-
model: "auto",
|
|
53
|
-
},
|
|
54
|
-
},
|
|
55
|
-
defaults: {
|
|
56
|
-
autoApprove: false,
|
|
57
|
-
contextFiles: 20,
|
|
58
|
-
maxTokens: 8192,
|
|
59
|
-
},
|
|
60
|
-
};
|
|
61
|
-
|
|
62
|
-
/**
|
|
63
|
-
* Parse CLI arguments
|
|
64
|
-
*/
|
|
65
|
-
export function parseCLIArgs(): CLIArgs {
|
|
66
|
-
const args: CLIArgs = {};
|
|
67
|
-
const argv = process.argv.slice(2);
|
|
68
|
-
|
|
69
|
-
for (let i = 0; i < argv.length; i++) {
|
|
70
|
-
const arg = argv[i];
|
|
71
|
-
const next = argv[i + 1];
|
|
72
|
-
|
|
73
|
-
if ((arg === "--model" || arg === "-m") && next) {
|
|
74
|
-
args.model = next;
|
|
75
|
-
i++;
|
|
76
|
-
} else if ((arg === "--provider" || arg === "-p") && next) {
|
|
77
|
-
args.provider = next;
|
|
78
|
-
i++;
|
|
79
|
-
} else if ((arg === "--api-key" || arg === "-k") && next) {
|
|
80
|
-
args.apiKey = next;
|
|
81
|
-
i++;
|
|
82
|
-
} else if ((arg === "--base-url" || arg === "-u") && next) {
|
|
83
|
-
args.baseUrl = next;
|
|
84
|
-
i++;
|
|
85
|
-
} else if (arg === "--help" || arg === "-h") {
|
|
86
|
-
console.log(`
|
|
87
|
-
codemaxxing — your code. your model. no excuses.
|
|
88
|
-
|
|
89
|
-
Usage:
|
|
90
|
-
codemaxxing [options]
|
|
91
|
-
codemaxxing exec "prompt" [exec-options]
|
|
92
|
-
|
|
93
|
-
Options:
|
|
94
|
-
-m, --model <model> Model name to use
|
|
95
|
-
-p, --provider <name> Provider profile from config (e.g. local, openrouter)
|
|
96
|
-
-k, --api-key <key> API key for the provider
|
|
97
|
-
-u, --base-url <url> Base URL for the provider API
|
|
98
|
-
-h, --help Show this help
|
|
99
|
-
|
|
100
|
-
Exec options (headless/CI mode):
|
|
101
|
-
--auto-approve Skip tool approval prompts
|
|
102
|
-
--json Output JSON instead of streaming text
|
|
103
|
-
-m, --model <model> Model to use
|
|
104
|
-
-p, --provider <name> Provider profile
|
|
105
|
-
|
|
106
|
-
Examples:
|
|
107
|
-
codemaxxing # Auto-detect local LLM
|
|
108
|
-
codemaxxing -m gpt-4o -u https://api.openai.com/v1 -k sk-...
|
|
109
|
-
codemaxxing -p openrouter # Use saved provider profile
|
|
110
|
-
codemaxxing -m qwen3.5-35b # Override model only
|
|
111
|
-
codemaxxing exec "fix the failing tests" # Headless mode
|
|
112
|
-
echo "explain this code" | codemaxxing exec # Pipe input
|
|
113
|
-
|
|
114
|
-
Config: ~/.codemaxxing/settings.json
|
|
115
|
-
`);
|
|
116
|
-
process.exit(0);
|
|
117
|
-
}
|
|
118
|
-
}
|
|
119
|
-
|
|
120
|
-
return args;
|
|
121
|
-
}
|
|
122
|
-
|
|
123
|
-
export function loadConfig(): CodemaxxingConfig {
|
|
124
|
-
if (!existsSync(CONFIG_DIR)) {
|
|
125
|
-
mkdirSync(CONFIG_DIR, { recursive: true });
|
|
126
|
-
}
|
|
127
|
-
|
|
128
|
-
if (!existsSync(CONFIG_FILE)) {
|
|
129
|
-
writeFileSync(CONFIG_FILE, JSON.stringify(DEFAULT_CONFIG, null, 2));
|
|
130
|
-
return DEFAULT_CONFIG;
|
|
131
|
-
}
|
|
132
|
-
|
|
133
|
-
try {
|
|
134
|
-
const raw = readFileSync(CONFIG_FILE, "utf-8");
|
|
135
|
-
const parsed = JSON.parse(raw);
|
|
136
|
-
return { ...DEFAULT_CONFIG, ...parsed };
|
|
137
|
-
} catch {
|
|
138
|
-
return DEFAULT_CONFIG;
|
|
139
|
-
}
|
|
140
|
-
}
|
|
141
|
-
|
|
142
|
-
/** Save config to disk (merges with existing) */
|
|
143
|
-
export function saveConfig(updates: Partial<CodemaxxingConfig>): void {
|
|
144
|
-
const current = loadConfig();
|
|
145
|
-
const merged = {
|
|
146
|
-
...current,
|
|
147
|
-
...updates,
|
|
148
|
-
defaults: { ...current.defaults, ...(updates.defaults ?? {}) },
|
|
149
|
-
};
|
|
150
|
-
if (!existsSync(CONFIG_DIR)) {
|
|
151
|
-
mkdirSync(CONFIG_DIR, { recursive: true });
|
|
152
|
-
}
|
|
153
|
-
writeFileSync(CONFIG_FILE, JSON.stringify(merged, null, 2));
|
|
154
|
-
}
|
|
155
|
-
|
|
156
|
-
/**
|
|
157
|
-
* Apply CLI overrides to a provider config
|
|
158
|
-
*/
|
|
159
|
-
export function applyOverrides(config: CodemaxxingConfig, args: CLIArgs): CodemaxxingConfig {
|
|
160
|
-
const result = { ...config, provider: { ...config.provider } };
|
|
161
|
-
|
|
162
|
-
// If a named provider profile is specified, use it as the base
|
|
163
|
-
if (args.provider && config.providers?.[args.provider]) {
|
|
164
|
-
const profile = config.providers[args.provider];
|
|
165
|
-
result.provider = { ...profile };
|
|
166
|
-
// Also check auth store for this provider
|
|
167
|
-
const authCred = getCredential(args.provider);
|
|
168
|
-
if (authCred) {
|
|
169
|
-
result.provider.baseUrl = authCred.baseUrl;
|
|
170
|
-
result.provider.apiKey = authCred.apiKey;
|
|
171
|
-
}
|
|
172
|
-
// Detect provider type
|
|
173
|
-
result.provider.type = detectProviderType(args.provider, result.provider.baseUrl);
|
|
174
|
-
}
|
|
175
|
-
|
|
176
|
-
// CLI flags override everything
|
|
177
|
-
if (args.model) result.provider.model = args.model;
|
|
178
|
-
if (args.apiKey) result.provider.apiKey = args.apiKey;
|
|
179
|
-
if (args.baseUrl) result.provider.baseUrl = args.baseUrl;
|
|
180
|
-
|
|
181
|
-
// Auto-detect type from baseUrl if not set
|
|
182
|
-
if (!result.provider.type && result.provider.baseUrl) {
|
|
183
|
-
result.provider.type = detectProviderType(args.provider || "", result.provider.baseUrl);
|
|
184
|
-
}
|
|
185
|
-
|
|
186
|
-
return result;
|
|
187
|
-
}
|
|
188
|
-
|
|
189
|
-
export function getConfigPath(): string {
|
|
190
|
-
return CONFIG_FILE;
|
|
191
|
-
}
|
|
192
|
-
|
|
193
|
-
/**
|
|
194
|
-
* Auto-detect local LLM servers
|
|
195
|
-
*/
|
|
196
|
-
export type DetectionResult =
|
|
197
|
-
| { status: "connected"; provider: ProviderConfig }
|
|
198
|
-
| { status: "no-models"; serverName: string; baseUrl: string }
|
|
199
|
-
| { status: "no-server" };
|
|
200
|
-
|
|
201
|
-
export async function detectLocalProviderDetailed(): Promise<DetectionResult> {
|
|
202
|
-
const endpoints = [
|
|
203
|
-
{ name: "LM Studio", url: "http://localhost:1234/v1" },
|
|
204
|
-
{ name: "Ollama", url: "http://localhost:11434/v1" },
|
|
205
|
-
{ name: "vLLM", url: "http://localhost:8000/v1" },
|
|
206
|
-
];
|
|
207
|
-
|
|
208
|
-
let serverFound: { name: string; url: string } | null = null;
|
|
209
|
-
|
|
210
|
-
for (const endpoint of endpoints) {
|
|
211
|
-
try {
|
|
212
|
-
const controller = new AbortController();
|
|
213
|
-
const timeout = setTimeout(() => controller.abort(), 2000);
|
|
214
|
-
const res = await fetch(`${endpoint.url}/models`, {
|
|
215
|
-
signal: controller.signal,
|
|
216
|
-
});
|
|
217
|
-
clearTimeout(timeout);
|
|
218
|
-
|
|
219
|
-
if (res.ok) {
|
|
220
|
-
const data = (await res.json()) as { data?: Array<{ id: string }> };
|
|
221
|
-
const models = data.data ?? [];
|
|
222
|
-
if (models.length === 0) {
|
|
223
|
-
// Server is up but no models — remember it but keep looking
|
|
224
|
-
if (!serverFound) serverFound = endpoint;
|
|
225
|
-
continue;
|
|
226
|
-
}
|
|
227
|
-
return {
|
|
228
|
-
status: "connected",
|
|
229
|
-
provider: {
|
|
230
|
-
baseUrl: endpoint.url,
|
|
231
|
-
apiKey: "not-needed",
|
|
232
|
-
model: models[0]!.id,
|
|
233
|
-
},
|
|
234
|
-
};
|
|
235
|
-
}
|
|
236
|
-
} catch {
|
|
237
|
-
// Server not running, try next
|
|
238
|
-
}
|
|
239
|
-
}
|
|
240
|
-
|
|
241
|
-
if (serverFound) {
|
|
242
|
-
return { status: "no-models", serverName: serverFound.name, baseUrl: serverFound.url };
|
|
243
|
-
}
|
|
244
|
-
return { status: "no-server" };
|
|
245
|
-
}
|
|
246
|
-
|
|
247
|
-
export async function detectLocalProvider(): Promise<ProviderConfig | null> {
|
|
248
|
-
const endpoints = [
|
|
249
|
-
{ name: "LM Studio", url: "http://localhost:1234/v1" },
|
|
250
|
-
{ name: "Ollama", url: "http://localhost:11434/v1" },
|
|
251
|
-
{ name: "vLLM", url: "http://localhost:8000/v1" },
|
|
252
|
-
];
|
|
253
|
-
|
|
254
|
-
for (const endpoint of endpoints) {
|
|
255
|
-
try {
|
|
256
|
-
const controller = new AbortController();
|
|
257
|
-
const timeout = setTimeout(() => controller.abort(), 2000);
|
|
258
|
-
const res = await fetch(`${endpoint.url}/models`, {
|
|
259
|
-
signal: controller.signal,
|
|
260
|
-
});
|
|
261
|
-
clearTimeout(timeout);
|
|
262
|
-
|
|
263
|
-
if (res.ok) {
|
|
264
|
-
const data = (await res.json()) as { data?: Array<{ id: string }> };
|
|
265
|
-
const models = data.data ?? [];
|
|
266
|
-
if (models.length === 0) {
|
|
267
|
-
// Server is up but no models available — don't fake a connection
|
|
268
|
-
continue;
|
|
269
|
-
}
|
|
270
|
-
const model = models[0]!.id;
|
|
271
|
-
return {
|
|
272
|
-
baseUrl: endpoint.url,
|
|
273
|
-
apiKey: "not-needed",
|
|
274
|
-
model,
|
|
275
|
-
};
|
|
276
|
-
}
|
|
277
|
-
} catch {
|
|
278
|
-
// Server not running, try next
|
|
279
|
-
}
|
|
280
|
-
}
|
|
281
|
-
|
|
282
|
-
return null;
|
|
283
|
-
}
|
|
284
|
-
|
|
285
|
-
/**
|
|
286
|
-
* List available models from a provider endpoint
|
|
287
|
-
*/
|
|
288
|
-
export async function listModels(baseUrl: string, apiKey: string): Promise<string[]> {
|
|
289
|
-
try {
|
|
290
|
-
const controller = new AbortController();
|
|
291
|
-
const timeout = setTimeout(() => controller.abort(), 5000);
|
|
292
|
-
const headers: Record<string, string> = {};
|
|
293
|
-
if (apiKey && apiKey !== "not-needed") {
|
|
294
|
-
headers["Authorization"] = `Bearer ${apiKey}`;
|
|
295
|
-
}
|
|
296
|
-
const res = await fetch(`${baseUrl}/models`, {
|
|
297
|
-
signal: controller.signal,
|
|
298
|
-
headers,
|
|
299
|
-
});
|
|
300
|
-
clearTimeout(timeout);
|
|
301
|
-
|
|
302
|
-
if (res.ok) {
|
|
303
|
-
const data = (await res.json()) as { data?: Array<{ id: string }> };
|
|
304
|
-
return (data.data ?? []).map(m => m.id);
|
|
305
|
-
}
|
|
306
|
-
} catch { /* ignore */ }
|
|
307
|
-
return [];
|
|
308
|
-
}
|
|
309
|
-
|
|
310
|
-
/**
|
|
311
|
-
* Resolve provider configuration from auth store or config file
|
|
312
|
-
* Priority: CLI args > auth store > config file > auto-detect
|
|
313
|
-
*/
|
|
314
|
-
export function resolveProvider(
|
|
315
|
-
providerId: string,
|
|
316
|
-
cliArgs: CLIArgs
|
|
317
|
-
): ProviderConfig | null {
|
|
318
|
-
// Check auth store first
|
|
319
|
-
const authCred = getCredential(providerId);
|
|
320
|
-
if (authCred) {
|
|
321
|
-
return {
|
|
322
|
-
baseUrl: authCred.baseUrl,
|
|
323
|
-
apiKey: authCred.apiKey,
|
|
324
|
-
model: cliArgs.model || "auto",
|
|
325
|
-
type: detectProviderType(providerId, authCred.baseUrl),
|
|
326
|
-
};
|
|
327
|
-
}
|
|
328
|
-
|
|
329
|
-
// Fall back to config file
|
|
330
|
-
const config = loadConfig();
|
|
331
|
-
const provider = config.providers?.[providerId];
|
|
332
|
-
if (provider) {
|
|
333
|
-
return {
|
|
334
|
-
baseUrl: provider.baseUrl,
|
|
335
|
-
apiKey: cliArgs.apiKey || provider.apiKey,
|
|
336
|
-
model: cliArgs.model || provider.model,
|
|
337
|
-
type: detectProviderType(providerId, provider.baseUrl),
|
|
338
|
-
};
|
|
339
|
-
}
|
|
340
|
-
|
|
341
|
-
return null;
|
|
342
|
-
}
|
|
343
|
-
|
|
344
|
-
/**
|
|
345
|
-
* Detect provider type from ID or base URL
|
|
346
|
-
*/
|
|
347
|
-
function detectProviderType(providerId: string, baseUrl: string): "openai" | "anthropic" {
|
|
348
|
-
if (providerId === "anthropic" || baseUrl.includes("anthropic.com")) {
|
|
349
|
-
return "anthropic";
|
|
350
|
-
}
|
|
351
|
-
return "openai";
|
|
352
|
-
}
|