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.
Files changed (47) hide show
  1. package/README.md +18 -12
  2. package/dist/agent.d.ts +4 -0
  3. package/dist/agent.js +91 -16
  4. package/dist/commands/git.d.ts +2 -0
  5. package/dist/commands/git.js +50 -0
  6. package/dist/commands/ollama.d.ts +27 -0
  7. package/dist/commands/ollama.js +171 -0
  8. package/dist/commands/output.d.ts +2 -0
  9. package/dist/commands/output.js +18 -0
  10. package/dist/commands/registry.d.ts +2 -0
  11. package/dist/commands/registry.js +8 -0
  12. package/dist/commands/skills.d.ts +18 -0
  13. package/dist/commands/skills.js +121 -0
  14. package/dist/commands/types.d.ts +5 -0
  15. package/dist/commands/types.js +1 -0
  16. package/dist/commands/ui.d.ts +16 -0
  17. package/dist/commands/ui.js +79 -0
  18. package/dist/config.d.ts +9 -0
  19. package/dist/config.js +13 -3
  20. package/dist/exec.js +4 -1
  21. package/dist/index.js +75 -401
  22. package/dist/tools/files.js +58 -3
  23. package/dist/utils/context.js +6 -0
  24. package/dist/utils/mcp.d.ts +7 -2
  25. package/dist/utils/mcp.js +34 -6
  26. package/package.json +8 -5
  27. package/src/agent.ts +0 -894
  28. package/src/auth-cli.ts +0 -287
  29. package/src/cli.ts +0 -37
  30. package/src/config.ts +0 -352
  31. package/src/exec.ts +0 -183
  32. package/src/index.tsx +0 -2647
  33. package/src/skills/registry.ts +0 -1436
  34. package/src/themes.ts +0 -335
  35. package/src/tools/files.ts +0 -374
  36. package/src/utils/auth.ts +0 -606
  37. package/src/utils/context.ts +0 -174
  38. package/src/utils/git.ts +0 -117
  39. package/src/utils/hardware.ts +0 -131
  40. package/src/utils/lint.ts +0 -116
  41. package/src/utils/mcp.ts +0 -307
  42. package/src/utils/models.ts +0 -218
  43. package/src/utils/ollama.ts +0 -352
  44. package/src/utils/repomap.ts +0 -220
  45. package/src/utils/sessions.ts +0 -254
  46. package/src/utils/skills.ts +0 -241
  47. package/tsconfig.json +0 -16
package/src/utils/mcp.ts DELETED
@@ -1,307 +0,0 @@
1
- /**
2
- * MCP (Model Context Protocol) client support
3
- * Connects to external MCP servers and exposes their tools to the LLM agent.
4
- */
5
-
6
- import { Client } from "@modelcontextprotocol/sdk/client/index.js";
7
- import { StdioClientTransport } from "@modelcontextprotocol/sdk/client/stdio.js";
8
- import { readFileSync, writeFileSync, existsSync, mkdirSync } from "fs";
9
- import { join } from "path";
10
- import { homedir } from "os";
11
- import type { ChatCompletionTool } from "openai/resources/chat/completions";
12
-
13
- // ── Types ──
14
-
15
- export interface MCPServerConfig {
16
- command: string;
17
- args?: string[];
18
- env?: Record<string, string>;
19
- }
20
-
21
- export interface MCPConfig {
22
- mcpServers: Record<string, MCPServerConfig>;
23
- }
24
-
25
- export interface ConnectedServer {
26
- name: string;
27
- client: Client;
28
- transport: StdioClientTransport;
29
- tools: Array<{ name: string; description?: string; inputSchema: Record<string, unknown> }>;
30
- }
31
-
32
- // ── Config paths ──
33
-
34
- const GLOBAL_CONFIG_DIR = join(homedir(), ".codemaxxing");
35
- const GLOBAL_CONFIG_PATH = join(GLOBAL_CONFIG_DIR, "mcp.json");
36
-
37
- function getProjectConfigPaths(cwd: string): string[] {
38
- return [
39
- join(cwd, ".codemaxxing", "mcp.json"),
40
- join(cwd, ".cursor", "mcp.json"),
41
- join(cwd, "opencode.json"),
42
- ];
43
- }
44
-
45
- // ── Config loading ──
46
-
47
- function loadConfigFile(path: string): MCPConfig | null {
48
- try {
49
- if (!existsSync(path)) return null;
50
- const raw = readFileSync(path, "utf-8");
51
- const parsed = JSON.parse(raw);
52
- if (parsed.mcpServers && typeof parsed.mcpServers === "object") {
53
- return parsed as MCPConfig;
54
- }
55
- return null;
56
- } catch {
57
- return null;
58
- }
59
- }
60
-
61
- export function loadMCPConfig(cwd: string): MCPConfig {
62
- const merged: MCPConfig = { mcpServers: {} };
63
-
64
- // Load global config first (lower priority)
65
- const globalConfig = loadConfigFile(GLOBAL_CONFIG_PATH);
66
- if (globalConfig) {
67
- Object.assign(merged.mcpServers, globalConfig.mcpServers);
68
- }
69
-
70
- // Load project configs (higher priority — later overwrites earlier)
71
- for (const configPath of getProjectConfigPaths(cwd)) {
72
- const config = loadConfigFile(configPath);
73
- if (config) {
74
- Object.assign(merged.mcpServers, config.mcpServers);
75
- }
76
- }
77
-
78
- return merged;
79
- }
80
-
81
- // ── Connection management ──
82
-
83
- const connectedServers: ConnectedServer[] = [];
84
-
85
- export async function connectToServers(
86
- config: MCPConfig,
87
- onStatus?: (name: string, status: string) => void,
88
- ): Promise<ConnectedServer[]> {
89
- const entries = Object.entries(config.mcpServers);
90
- if (entries.length === 0) return [];
91
-
92
- for (const [name, serverConfig] of entries) {
93
- try {
94
- onStatus?.(name, "connecting");
95
-
96
- const transport = new StdioClientTransport({
97
- command: serverConfig.command,
98
- args: serverConfig.args ?? [],
99
- env: { ...process.env, ...(serverConfig.env ?? {}) } as Record<string, string>,
100
- });
101
-
102
- const client = new Client({
103
- name: "codemaxxing",
104
- version: "0.3.0",
105
- });
106
-
107
- await client.connect(transport);
108
-
109
- // Fetch available tools
110
- const toolsResult = await client.listTools();
111
- const tools = (toolsResult.tools ?? []).map((t) => ({
112
- name: t.name,
113
- description: t.description,
114
- inputSchema: (t.inputSchema ?? { type: "object", properties: {} }) as Record<string, unknown>,
115
- }));
116
-
117
- const server: ConnectedServer = { name, client, transport, tools };
118
- connectedServers.push(server);
119
- onStatus?.(name, `connected (${tools.length} tools)`);
120
- } catch (err: any) {
121
- onStatus?.(name, `failed: ${err.message}`);
122
- }
123
- }
124
-
125
- return connectedServers;
126
- }
127
-
128
- export async function disconnectAll(): Promise<void> {
129
- for (const server of connectedServers) {
130
- try {
131
- await server.client.close();
132
- } catch {
133
- // Ignore cleanup errors
134
- }
135
- }
136
- connectedServers.length = 0;
137
- }
138
-
139
- export function getConnectedServers(): ConnectedServer[] {
140
- return connectedServers;
141
- }
142
-
143
- // ── Tool format conversion ──
144
-
145
- export function getAllMCPTools(servers: ConnectedServer[]): ChatCompletionTool[] {
146
- const tools: ChatCompletionTool[] = [];
147
-
148
- for (const server of servers) {
149
- for (const tool of server.tools) {
150
- tools.push({
151
- type: "function",
152
- function: {
153
- name: `mcp_${server.name}_${tool.name}`,
154
- description: `[MCP: ${server.name}] ${tool.description ?? tool.name}`,
155
- parameters: tool.inputSchema as any,
156
- },
157
- });
158
- }
159
- }
160
-
161
- return tools;
162
- }
163
-
164
- /**
165
- * Parse an MCP tool call name to extract server name and tool name.
166
- * Format: mcp_<serverName>_<toolName>
167
- * Server names can contain hyphens but not underscores (by convention).
168
- */
169
- export function parseMCPToolName(fullName: string): { serverName: string; toolName: string } | null {
170
- if (!fullName.startsWith("mcp_")) return null;
171
- const rest = fullName.slice(4); // Remove "mcp_"
172
-
173
- // Find the server by matching known connected server names
174
- for (const server of connectedServers) {
175
- const prefix = server.name + "_";
176
- if (rest.startsWith(prefix)) {
177
- return { serverName: server.name, toolName: rest.slice(prefix.length) };
178
- }
179
- }
180
-
181
- // Fallback: split on first underscore
182
- const idx = rest.indexOf("_");
183
- if (idx === -1) return null;
184
- return { serverName: rest.slice(0, idx), toolName: rest.slice(idx + 1) };
185
- }
186
-
187
- // ── Tool execution ──
188
-
189
- export async function callMCPTool(
190
- serverName: string,
191
- toolName: string,
192
- args: Record<string, unknown>,
193
- ): Promise<string> {
194
- const server = connectedServers.find((s) => s.name === serverName);
195
- if (!server) {
196
- return `Error: MCP server "${serverName}" not found or not connected.`;
197
- }
198
-
199
- try {
200
- const result = await server.client.callTool({ name: toolName, arguments: args });
201
- // MCP tool results have a content array
202
- const content = result.content;
203
- if (Array.isArray(content)) {
204
- return content
205
- .map((c: any) => {
206
- if (c.type === "text") return c.text;
207
- if (c.type === "image") return `[image: ${c.mimeType}]`;
208
- return JSON.stringify(c);
209
- })
210
- .join("\n");
211
- }
212
- return typeof content === "string" ? content : JSON.stringify(content);
213
- } catch (err: any) {
214
- return `Error calling MCP tool "${toolName}" on server "${serverName}": ${err.message}`;
215
- }
216
- }
217
-
218
- // ── Server management ──
219
-
220
- export function addServer(name: string, config: MCPServerConfig): { ok: boolean; message: string } {
221
- try {
222
- if (!existsSync(GLOBAL_CONFIG_DIR)) {
223
- mkdirSync(GLOBAL_CONFIG_DIR, { recursive: true });
224
- }
225
-
226
- let existing: MCPConfig = { mcpServers: {} };
227
- if (existsSync(GLOBAL_CONFIG_PATH)) {
228
- try {
229
- existing = JSON.parse(readFileSync(GLOBAL_CONFIG_PATH, "utf-8"));
230
- if (!existing.mcpServers) existing.mcpServers = {};
231
- } catch {
232
- existing = { mcpServers: {} };
233
- }
234
- }
235
-
236
- existing.mcpServers[name] = config;
237
- writeFileSync(GLOBAL_CONFIG_PATH, JSON.stringify(existing, null, 2) + "\n", "utf-8");
238
- return { ok: true, message: `Added MCP server "${name}" to global config.` };
239
- } catch (err: any) {
240
- return { ok: false, message: `Failed to add server: ${err.message}` };
241
- }
242
- }
243
-
244
- export function removeServer(name: string): { ok: boolean; message: string } {
245
- try {
246
- if (!existsSync(GLOBAL_CONFIG_PATH)) {
247
- return { ok: false, message: `No global MCP config found.` };
248
- }
249
-
250
- const existing: MCPConfig = JSON.parse(readFileSync(GLOBAL_CONFIG_PATH, "utf-8"));
251
- if (!existing.mcpServers || !existing.mcpServers[name]) {
252
- return { ok: false, message: `Server "${name}" not found in global config.` };
253
- }
254
-
255
- delete existing.mcpServers[name];
256
- writeFileSync(GLOBAL_CONFIG_PATH, JSON.stringify(existing, null, 2) + "\n", "utf-8");
257
- return { ok: true, message: `Removed MCP server "${name}" from global config.` };
258
- } catch (err: any) {
259
- return { ok: false, message: `Failed to remove server: ${err.message}` };
260
- }
261
- }
262
-
263
- export function listServers(cwd: string): Array<{ name: string; source: string; command: string; connected: boolean; toolCount: number }> {
264
- const result: Array<{ name: string; source: string; command: string; connected: boolean; toolCount: number }> = [];
265
-
266
- // Gather from global config
267
- const globalConfig = loadConfigFile(GLOBAL_CONFIG_PATH);
268
- if (globalConfig) {
269
- for (const [name, cfg] of Object.entries(globalConfig.mcpServers)) {
270
- const connected = connectedServers.find((s) => s.name === name);
271
- result.push({
272
- name,
273
- source: "global",
274
- command: `${cfg.command} ${(cfg.args ?? []).join(" ")}`.trim(),
275
- connected: !!connected,
276
- toolCount: connected?.tools.length ?? 0,
277
- });
278
- }
279
- }
280
-
281
- // Gather from project configs
282
- for (const configPath of getProjectConfigPaths(cwd)) {
283
- const config = loadConfigFile(configPath);
284
- if (config) {
285
- const source = configPath.includes(".cursor") ? "cursor" : configPath.includes("opencode") ? "opencode" : "project";
286
- for (const [name, cfg] of Object.entries(config.mcpServers)) {
287
- // Skip if already listed from global (project overrides)
288
- const existing = result.find((r) => r.name === name);
289
- if (existing) {
290
- existing.source = source;
291
- existing.command = `${cfg.command} ${(cfg.args ?? []).join(" ")}`.trim();
292
- continue;
293
- }
294
- const connected = connectedServers.find((s) => s.name === name);
295
- result.push({
296
- name,
297
- source,
298
- command: `${cfg.command} ${(cfg.args ?? []).join(" ")}`.trim(),
299
- connected: !!connected,
300
- toolCount: connected?.tools.length ?? 0,
301
- });
302
- }
303
- }
304
- }
305
-
306
- return result;
307
- }
@@ -1,218 +0,0 @@
1
- import { execSync } from "child_process";
2
- import type { HardwareInfo } from "./hardware.js";
3
-
4
- export interface RecommendedModel {
5
- name: string; // Display name
6
- ollamaId: string; // Ollama model ID
7
- size: number; // Download size in GB
8
- ramRequired: number; // Minimum RAM in GB
9
- vramOptimal: number; // Optimal VRAM in GB (0 = CPU fine)
10
- description: string; // One-liner
11
- speed: string; // e.g., "~45 tok/s on M1"
12
- quality: "limited" | "good" | "great" | "best";
13
- }
14
-
15
- export type ModelFit = "perfect" | "good" | "tight" | "skip";
16
-
17
- export interface ScoredModel extends RecommendedModel {
18
- fit: ModelFit;
19
- }
20
-
21
- const MODELS: RecommendedModel[] = [
22
- {
23
- name: "Qwen 2.5 Coder 3B",
24
- ollamaId: "qwen2.5-coder:3b",
25
- size: 2,
26
- ramRequired: 8,
27
- vramOptimal: 4,
28
- description: "\u26A0\uFE0F May not support tool calling well",
29
- speed: "~60 tok/s on M1",
30
- quality: "limited",
31
- },
32
- {
33
- name: "Qwen 2.5 Coder 7B",
34
- ollamaId: "qwen2.5-coder:7b",
35
- size: 5,
36
- ramRequired: 16,
37
- vramOptimal: 8,
38
- description: "Sweet spot for most machines",
39
- speed: "~45 tok/s on M1",
40
- quality: "great",
41
- },
42
- {
43
- name: "Qwen 2.5 Coder 14B",
44
- ollamaId: "qwen2.5-coder:14b",
45
- size: 9,
46
- ramRequired: 32,
47
- vramOptimal: 16,
48
- description: "High quality coding",
49
- speed: "~25 tok/s on M1 Pro",
50
- quality: "best",
51
- },
52
- {
53
- name: "Qwen 2.5 Coder 32B",
54
- ollamaId: "qwen2.5-coder:32b",
55
- size: 20,
56
- ramRequired: 48,
57
- vramOptimal: 32,
58
- description: "Premium quality, needs lots of RAM",
59
- speed: "~12 tok/s on M1 Max",
60
- quality: "best",
61
- },
62
- {
63
- name: "DeepSeek Coder V2 16B",
64
- ollamaId: "deepseek-coder-v2:16b",
65
- size: 9,
66
- ramRequired: 32,
67
- vramOptimal: 16,
68
- description: "Strong alternative for coding",
69
- speed: "~30 tok/s on M1 Pro",
70
- quality: "great",
71
- },
72
- {
73
- name: "CodeLlama 7B",
74
- ollamaId: "codellama:7b",
75
- size: 4,
76
- ramRequired: 16,
77
- vramOptimal: 8,
78
- description: "Meta's coding model",
79
- speed: "~40 tok/s on M1",
80
- quality: "good",
81
- },
82
- {
83
- name: "StarCoder2 7B",
84
- ollamaId: "starcoder2:7b",
85
- size: 4,
86
- ramRequired: 16,
87
- vramOptimal: 8,
88
- description: "Good for code completion",
89
- speed: "~40 tok/s on M1",
90
- quality: "good",
91
- },
92
- ];
93
-
94
- function scoreModel(model: RecommendedModel, ramGB: number, vramGB: number): ModelFit {
95
- if (ramGB < model.ramRequired) return "skip";
96
-
97
- const ramHeadroom = ramGB - model.ramRequired;
98
- const hasGoodVRAM = vramGB >= model.vramOptimal;
99
-
100
- if (hasGoodVRAM && ramHeadroom >= 4) return "perfect";
101
- if (hasGoodVRAM || ramHeadroom >= 8) return "good";
102
- if (ramHeadroom >= 0) return "tight";
103
- return "skip";
104
- }
105
-
106
- const qualityOrder: Record<string, number> = { best: 3, great: 2, good: 1, limited: 0 };
107
- const fitOrder: Record<string, number> = { perfect: 4, good: 3, tight: 2, skip: 1 };
108
-
109
- export function getRecommendations(hardware: HardwareInfo): ScoredModel[] {
110
- const ramGB = hardware.ram / (1024 * 1024 * 1024);
111
- const vramGB = hardware.gpu?.vram ? hardware.gpu.vram / (1024 * 1024 * 1024) : 0;
112
-
113
- // Apple Silicon uses unified memory — VRAM = RAM
114
- const effectiveVRAM = hardware.appleSilicon ? ramGB : vramGB;
115
-
116
- const scored: ScoredModel[] = MODELS.map((m) => ({
117
- ...m,
118
- fit: scoreModel(m, ramGB, effectiveVRAM),
119
- }));
120
-
121
- // Sort: perfect first, then by quality descending
122
- scored.sort((a, b) => {
123
- const fitDiff = (fitOrder[b.fit] ?? 0) - (fitOrder[a.fit] ?? 0);
124
- if (fitDiff !== 0) return fitDiff;
125
- return (qualityOrder[b.quality] ?? 0) - (qualityOrder[a.quality] ?? 0);
126
- });
127
-
128
- return scored;
129
- }
130
-
131
- export function getFitIcon(fit: ModelFit): string {
132
- switch (fit) {
133
- case "perfect": return "\u2B50"; // ⭐
134
- case "good": return "\u2705"; // ✅
135
- case "tight": return "\u26A0\uFE0F"; // ⚠️
136
- case "skip": return "\u274C"; // ❌
137
- }
138
- }
139
-
140
- /** Check if llmfit binary is available */
141
- export function isLlmfitAvailable(): boolean {
142
- try {
143
- const cmd = process.platform === "win32" ? "where llmfit" : "which llmfit";
144
- execSync(cmd, { stdio: ["pipe", "pipe", "pipe"], timeout: 3000 });
145
- return true;
146
- } catch {
147
- return false;
148
- }
149
- }
150
-
151
- interface LlmfitModel {
152
- name: string;
153
- provider: string;
154
- params_b: number;
155
- quant: string;
156
- fit: string;
157
- estimated_tps: number;
158
- vram_gb: number;
159
- ram_gb: number;
160
- }
161
-
162
- function mapLlmfitFit(fit: string): ModelFit {
163
- switch (fit) {
164
- case "perfect": return "perfect";
165
- case "good": return "good";
166
- case "marginal": return "tight";
167
- default: return "skip";
168
- }
169
- }
170
-
171
- function mapLlmfitQuality(params_b: number): "limited" | "good" | "great" | "best" {
172
- if (params_b >= 14) return "best";
173
- if (params_b >= 7) return "great";
174
- if (params_b >= 5) return "good";
175
- return "limited";
176
- }
177
-
178
- /** Get recommendations using llmfit if available, otherwise fall back to hardcoded list */
179
- export function getRecommendationsWithLlmfit(hardware: HardwareInfo): { models: ScoredModel[]; usedLlmfit: boolean } {
180
- if (!isLlmfitAvailable()) {
181
- return { models: getRecommendations(hardware), usedLlmfit: false };
182
- }
183
-
184
- try {
185
- const raw = execSync("llmfit recommend --use-case coding --format json --limit 10", {
186
- encoding: "utf-8",
187
- timeout: 15000,
188
- stdio: ["pipe", "pipe", "pipe"],
189
- });
190
-
191
- const llmfitModels: LlmfitModel[] = JSON.parse(raw.trim());
192
- if (!Array.isArray(llmfitModels) || llmfitModels.length === 0) {
193
- return { models: getRecommendations(hardware), usedLlmfit: false };
194
- }
195
-
196
- const scored: ScoredModel[] = llmfitModels
197
- .filter((m) => m.provider === "ollama")
198
- .map((m) => ({
199
- name: m.name.split(":")[0]?.replace(/-/g, " ").replace(/\b\w/g, c => c.toUpperCase()) + ` ${m.params_b}B`,
200
- ollamaId: m.name,
201
- size: Math.ceil(m.vram_gb),
202
- ramRequired: Math.ceil(m.ram_gb),
203
- vramOptimal: Math.ceil(m.vram_gb),
204
- description: `${m.quant} · ~${m.estimated_tps.toFixed(0)} tok/s`,
205
- speed: `~${m.estimated_tps.toFixed(0)} tok/s`,
206
- quality: mapLlmfitQuality(m.params_b),
207
- fit: mapLlmfitFit(m.fit),
208
- }));
209
-
210
- if (scored.length === 0) {
211
- return { models: getRecommendations(hardware), usedLlmfit: false };
212
- }
213
-
214
- return { models: scored, usedLlmfit: true };
215
- } catch {
216
- return { models: getRecommendations(hardware), usedLlmfit: false };
217
- }
218
- }